From 3b19ba1790d6035a9e246f5d4ae627bf68778606 Mon Sep 17 00:00:00 2001 From: damithc Date: Mon, 25 May 2020 00:58:18 +0800 Subject: [PATCH 01/95] Add Gradle support --- build.gradle | 46 ++++++ gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 58695 bytes gradle/wrapper/gradle-wrapper.properties | 5 + gradlew | 183 +++++++++++++++++++++++ gradlew.bat | 103 +++++++++++++ text-ui-test/runtest.sh | 0 6 files changed, 337 insertions(+) create mode 100644 build.gradle create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat mode change 100644 => 100755 text-ui-test/runtest.sh diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000000..b0c5528fb5 --- /dev/null +++ b/build.gradle @@ -0,0 +1,46 @@ +plugins { + id 'java' + id 'application' + id 'checkstyle' + id 'com.github.johnrengelman.shadow' version '5.1.0' +} + +repositories { + mavenCentral() +} + +dependencies { + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.5.0' + testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.5.0' +} + +test { + useJUnitPlatform() + + testLogging { + events "passed", "skipped", "failed" + + showExceptions true + exceptionFormat "full" + showCauses true + showStackTraces true + showStandardStreams = false + } +} + +application { + mainClassName = "seedu.duke.Duke" +} + +shadowJar { + archiveBaseName = "duke" + archiveClassifier = null +} + +checkstyle { + toolVersion = '8.23' +} + +run{ + standardInput = System.in +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..f3d88b1c2faf2fc91d853cd5d4242b5547257070 GIT binary patch literal 58695 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 literal 0 HcmV?d00001 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000..b7c8c5dbf5 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 0000000000..2fe81a7d95 --- /dev/null +++ b/gradlew @@ -0,0 +1,183 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000000..62bd9b9cce --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,103 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/text-ui-test/runtest.sh b/text-ui-test/runtest.sh old mode 100644 new mode 100755 From 77511f4a87d3d0cb3751ec08c9b2b9f26d3c6a30 Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Thu, 20 Aug 2020 15:00:13 +0800 Subject: [PATCH 02/95] Add Message.java with constructor, reply() method, and enum for command types --- src/main/java/Message.java | 45 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 src/main/java/Message.java diff --git a/src/main/java/Message.java b/src/main/java/Message.java new file mode 100644 index 0000000000..a8d028144e --- /dev/null +++ b/src/main/java/Message.java @@ -0,0 +1,45 @@ +enum Command { + START, + BYE, + ECHO +} + +public class Message { + private final String message; + private Command cmd; + + // public constructor + public Message(String message) { + this.message = message; + String[] words = message.split(" "); + String cmdString = words[0]; + + for (Command c : Command.values()) { + if (c.toString().equalsIgnoreCase(cmdString)) { + this.cmd = c; + break; + } + } + + if (this.cmd == null) { // if not a recognised command + this.cmd = Command.ECHO; + } + } + + public void reply() { + System.out.println("____________________________________________________________"); + switch (cmd) { + case START: + System.out.println("Eh what's up! I'm Meimei" + + "\nWhat you want ah?"); + break; + case BYE: + System.out.println("Ok bye bye! C u again :P"); + break; + case ECHO: + System.out.println(message); + break; + } + System.out.println("____________________________________________________________"); + } +} From 6c96bd9884102a5a42ef578506a9a81e93eadf69 Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Thu, 20 Aug 2020 15:06:23 +0800 Subject: [PATCH 03/95] Added getCmd() method to return command --- src/main/java/Message.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/Message.java b/src/main/java/Message.java index a8d028144e..269f5278e9 100644 --- a/src/main/java/Message.java +++ b/src/main/java/Message.java @@ -42,4 +42,8 @@ public void reply() { } System.out.println("____________________________________________________________"); } + + public Command getCmd() { + return this.cmd; + } } From 2777c68324092792e923c1d863a177ce7ae49440 Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Thu, 20 Aug 2020 15:09:18 +0800 Subject: [PATCH 04/95] Created user input feature and respond to user inputs, deleted default output --- src/main/java/Duke.java | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 5d313334cc..30a0e9ef53 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -1,10 +1,18 @@ +import java.util.Scanner; + public class Duke { public static void main(String[] args) { - String logo = " ____ _ \n" - + "| _ \\ _ _| | _____ \n" - + "| | | | | | | |/ / _ \\\n" - + "| |_| | |_| | < __/\n" - + "|____/ \\__,_|_|\\_\\___|\n"; - System.out.println("Hello from\n" + logo); + Scanner sc = new Scanner(System.in); + + Message start = new Message("start"); + start.reply(); + + while (sc.hasNext()) { + Message msg = new Message(sc.next()); + msg.reply(); + if (msg.getCmd() == Command.BYE) { + break; + } + } } } From 09e2eb0887253d2a0a028795b0d382294107a87a Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Thu, 20 Aug 2020 15:43:59 +0800 Subject: [PATCH 05/95] Changed ECHO command type to ADD with appropriate ouput --- src/main/java/Message.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/Message.java b/src/main/java/Message.java index 269f5278e9..e18b99fafa 100644 --- a/src/main/java/Message.java +++ b/src/main/java/Message.java @@ -1,7 +1,7 @@ enum Command { START, BYE, - ECHO + ADD, } public class Message { @@ -22,7 +22,7 @@ public Message(String message) { } if (this.cmd == null) { // if not a recognised command - this.cmd = Command.ECHO; + this.cmd = Command.ADD; } } @@ -36,8 +36,8 @@ public void reply() { case BYE: System.out.println("Ok bye bye! C u again :P"); break; - case ECHO: - System.out.println(message); + case ADD: + System.out.println("added: " + message); break; } System.out.println("____________________________________________________________"); From 52872bf0c6cda935a7829d4c146064fdc11b7ac8 Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Thu, 20 Aug 2020 15:49:53 +0800 Subject: [PATCH 06/95] Added a HashSet "list" to keep all added items --- src/main/java/Duke.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 30a0e9ef53..04d494bdba 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -1,8 +1,10 @@ +import java.util.HashSet; import java.util.Scanner; public class Duke { public static void main(String[] args) { Scanner sc = new Scanner(System.in); + HashSet list = new HashSet<>(); Message start = new Message("start"); start.reply(); From e8d0b4967768f73bbe6e5142677154796b00168a Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Thu, 20 Aug 2020 15:52:08 +0800 Subject: [PATCH 07/95] Added HashSet "list" as variable in Message and to the constructor for Message objects --- src/main/java/Duke.java | 4 ++-- src/main/java/Message.java | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 04d494bdba..510b11e501 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -6,11 +6,11 @@ public static void main(String[] args) { Scanner sc = new Scanner(System.in); HashSet list = new HashSet<>(); - Message start = new Message("start"); + Message start = new Message("start", list); start.reply(); while (sc.hasNext()) { - Message msg = new Message(sc.next()); + Message msg = new Message(sc.next(), list); msg.reply(); if (msg.getCmd() == Command.BYE) { break; diff --git a/src/main/java/Message.java b/src/main/java/Message.java index e18b99fafa..f315264376 100644 --- a/src/main/java/Message.java +++ b/src/main/java/Message.java @@ -1,3 +1,5 @@ +import java.util.HashSet; + enum Command { START, BYE, @@ -7,9 +9,10 @@ enum Command { public class Message { private final String message; private Command cmd; + private HashSet list; // public constructor - public Message(String message) { + public Message(String message, HashSet list) { this.message = message; String[] words = message.split(" "); String cmdString = words[0]; @@ -24,6 +27,8 @@ public Message(String message) { if (this.cmd == null) { // if not a recognised command this.cmd = Command.ADD; } + + this.list = list; } public void reply() { From 8cd1a6a797f377af61a22493f288989c33c319ba Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Thu, 20 Aug 2020 15:55:11 +0800 Subject: [PATCH 08/95] Change HashSet instances to List implemented with ArrayList to preserve order --- src/main/java/Duke.java | 5 +++-- src/main/java/Message.java | 6 +++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 510b11e501..e7880860d2 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -1,10 +1,11 @@ -import java.util.HashSet; +import java.util.ArrayList; +import java.util.List; import java.util.Scanner; public class Duke { public static void main(String[] args) { Scanner sc = new Scanner(System.in); - HashSet list = new HashSet<>(); + List list = new ArrayList<>(); Message start = new Message("start", list); start.reply(); diff --git a/src/main/java/Message.java b/src/main/java/Message.java index f315264376..47ee82b046 100644 --- a/src/main/java/Message.java +++ b/src/main/java/Message.java @@ -1,4 +1,4 @@ -import java.util.HashSet; +import java.util.List; enum Command { START, @@ -9,10 +9,10 @@ enum Command { public class Message { private final String message; private Command cmd; - private HashSet list; + private List list; // public constructor - public Message(String message, HashSet list) { + public Message(String message, List list) { this.message = message; String[] words = message.split(" "); String cmdString = words[0]; From 8e532e71be243879d2cb9e1879f6b966521b2144 Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Thu, 20 Aug 2020 16:06:51 +0800 Subject: [PATCH 09/95] Updated reply() method for ADD command to add to list --- src/main/java/Message.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/Message.java b/src/main/java/Message.java index 47ee82b046..d2c215cbcf 100644 --- a/src/main/java/Message.java +++ b/src/main/java/Message.java @@ -42,6 +42,7 @@ public void reply() { System.out.println("Ok bye bye! C u again :P"); break; case ADD: + this.list.add(message); System.out.println("added: " + message); break; } From 2f575a1b11e90fd87cc1dbdbd7c0f4d6edc73288 Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Thu, 20 Aug 2020 16:12:51 +0800 Subject: [PATCH 10/95] Change sc.next() to sc.nextLine() so that new Message objects and initialised with the whole of the next line of user input and not with each word of the line as separate objects --- src/main/java/Duke.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index e7880860d2..4a12bab384 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -11,7 +11,7 @@ public static void main(String[] args) { start.reply(); while (sc.hasNext()) { - Message msg = new Message(sc.next(), list); + Message msg = new Message(sc.nextLine(), list); msg.reply(); if (msg.getCmd() == Command.BYE) { break; From 3d27ce2c078f58419c421b018abcd4f17bf4f2bd Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Thu, 20 Aug 2020 16:14:58 +0800 Subject: [PATCH 11/95] Added Command type LIST and reply() case for this type --- src/main/java/Message.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/Message.java b/src/main/java/Message.java index d2c215cbcf..129936a9ec 100644 --- a/src/main/java/Message.java +++ b/src/main/java/Message.java @@ -2,6 +2,7 @@ enum Command { START, + LIST, BYE, ADD, } @@ -38,6 +39,13 @@ public void reply() { System.out.println("Eh what's up! I'm Meimei" + "\nWhat you want ah?"); break; + case LIST: + int counter = 0; + for (String item : list) { + counter++; + System.out.println(counter + ". " + item); + } + break; case BYE: System.out.println("Ok bye bye! C u again :P"); break; From bfad9486580eb154dd92ce1adaec4d9fdf213eab Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Thu, 20 Aug 2020 16:17:12 +0800 Subject: [PATCH 12/95] Updated chatbot's tone in replies --- src/main/java/Message.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/Message.java b/src/main/java/Message.java index 129936a9ec..71385f4312 100644 --- a/src/main/java/Message.java +++ b/src/main/java/Message.java @@ -40,6 +40,7 @@ public void reply() { "\nWhat you want ah?"); break; case LIST: + System.out.println("Na, here is your list lah:"); int counter = 0; for (String item : list) { counter++; @@ -51,7 +52,7 @@ public void reply() { break; case ADD: this.list.add(message); - System.out.println("added: " + message); + System.out.println("I added: " + message); break; } System.out.println("____________________________________________________________"); From a12d24794af7dca737392bd4e80a3a23a90a175d Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Thu, 20 Aug 2020 16:35:39 +0800 Subject: [PATCH 13/95] Added Task.java class, adapting from example given --- src/main/java/Task.java | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/main/java/Task.java diff --git a/src/main/java/Task.java b/src/main/java/Task.java new file mode 100644 index 0000000000..60cc3ee175 --- /dev/null +++ b/src/main/java/Task.java @@ -0,0 +1,22 @@ +//Adapted from https://nus-cs2103-ay2021s1.github.io/website/schedule/week2/project.html +public class Task { + private final String taskName; + private boolean isDone; + + public Task(String taskName) { + this.taskName = taskName; + this.isDone = false; + } + + public String getTaskName() { + return taskName; + } + + public String getStatusIcon() { + return isDone ? "\u2713" : "\u2718"; //return tick or cross symbols + } + + public void markDone() { + this.isDone = true; + } +} From 33bf0dfae02487552be02e93f353a31a37bbbce8 Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Thu, 20 Aug 2020 16:36:54 +0800 Subject: [PATCH 14/95] Changed list from list of String to list of Task, and added status icons to replies to LIST command --- src/main/java/Duke.java | 2 +- src/main/java/Message.java | 14 +++++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 4a12bab384..646c79f219 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -5,7 +5,7 @@ public class Duke { public static void main(String[] args) { Scanner sc = new Scanner(System.in); - List list = new ArrayList<>(); + List list = new ArrayList<>(); Message start = new Message("start", list); start.reply(); diff --git a/src/main/java/Message.java b/src/main/java/Message.java index 71385f4312..14d3be4102 100644 --- a/src/main/java/Message.java +++ b/src/main/java/Message.java @@ -10,10 +10,10 @@ enum Command { public class Message { private final String message; private Command cmd; - private List list; + private List list; // public constructor - public Message(String message, List list) { + public Message(String message, List list) { this.message = message; String[] words = message.split(" "); String cmdString = words[0]; @@ -42,16 +42,20 @@ public void reply() { case LIST: System.out.println("Na, here is your list lah:"); int counter = 0; - for (String item : list) { + for (Task item : list) { counter++; - System.out.println(counter + ". " + item); + System.out.println(counter + + ".[" + + item.getStatusIcon() + + "] " + + item.getTaskName()); } break; case BYE: System.out.println("Ok bye bye! C u again :P"); break; case ADD: - this.list.add(message); + this.list.add(new Task(message)); System.out.println("I added: " + message); break; } From c7910a9407d006e3d47dbd0ef7b8030fbf0f174f Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Thu, 20 Aug 2020 16:51:20 +0800 Subject: [PATCH 15/95] Added Command type DONE and updated reply() method for this case --- src/main/java/Message.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/main/java/Message.java b/src/main/java/Message.java index 14d3be4102..f1708286de 100644 --- a/src/main/java/Message.java +++ b/src/main/java/Message.java @@ -3,6 +3,7 @@ enum Command { START, LIST, + DONE, BYE, ADD, } @@ -51,6 +52,21 @@ public void reply() { item.getTaskName()); } break; + case DONE: + String[] words = message.split(" "); + try { + int index = Integer.parseInt(words[1]); + Task item = list.get(index - 1); + item.markDone(); + System.out.println("Can, I help you mark this as done liao:" + + "\n [" + + item.getStatusIcon() + + "] " + + item.getTaskName()); + } catch (NumberFormatException e) { + System.out.println("Cannot find leh. Try typing \"done {index of list item}\"."); + } + break; case BYE: System.out.println("Ok bye bye! C u again :P"); break; From bfb4cbd43e62829dbeb7f5fcc589b3b92ff1d418 Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Thu, 20 Aug 2020 17:03:05 +0800 Subject: [PATCH 16/95] Overrode toString method for Task objects such that status icon is part of Task's string representation and not added in Message's reply() method --- src/main/java/Message.java | 12 ++++-------- src/main/java/Task.java | 7 ++++++- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/main/java/Message.java b/src/main/java/Message.java index f1708286de..17eefaa5dc 100644 --- a/src/main/java/Message.java +++ b/src/main/java/Message.java @@ -46,10 +46,8 @@ public void reply() { for (Task item : list) { counter++; System.out.println(counter + - ".[" + - item.getStatusIcon() + - "] " + - item.getTaskName()); + "." + + item.toString()); } break; case DONE: @@ -59,10 +57,8 @@ public void reply() { Task item = list.get(index - 1); item.markDone(); System.out.println("Can, I help you mark this as done liao:" + - "\n [" + - item.getStatusIcon() + - "] " + - item.getTaskName()); + "\n " + + item.toString()); } catch (NumberFormatException e) { System.out.println("Cannot find leh. Try typing \"done {index of list item}\"."); } diff --git a/src/main/java/Task.java b/src/main/java/Task.java index 60cc3ee175..1acf93d799 100644 --- a/src/main/java/Task.java +++ b/src/main/java/Task.java @@ -12,11 +12,16 @@ public String getTaskName() { return taskName; } - public String getStatusIcon() { + private String getStatusIcon() { return isDone ? "\u2713" : "\u2718"; //return tick or cross symbols } public void markDone() { this.isDone = true; } + + @Override + public String toString() { + return "[" + this.getStatusIcon() + "] " + taskName; + } } From d68bc56589297e7b308b333803e4d49da7441bf8 Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Thu, 20 Aug 2020 17:11:07 +0800 Subject: [PATCH 17/95] Added Deadline, Event and Todo classes extending Task, adapted from example given --- src/main/java/Deadline.java | 17 +++++++++++++++++ src/main/java/Event.java | 13 +++++++++++++ src/main/java/Todo.java | 11 +++++++++++ 3 files changed, 41 insertions(+) create mode 100644 src/main/java/Deadline.java create mode 100644 src/main/java/Event.java create mode 100644 src/main/java/Todo.java diff --git a/src/main/java/Deadline.java b/src/main/java/Deadline.java new file mode 100644 index 0000000000..17b31ead8e --- /dev/null +++ b/src/main/java/Deadline.java @@ -0,0 +1,17 @@ +//@@author {FooJingYi}-reused +//Reused from https://nus-cs2103-ay2021s1.github.io/website/schedule/week2/project.html with minor modifications + +public class Deadline extends Task { + + protected String dl; //deadline given + + public Deadline(String taskName, String dl) { + super(taskName); + this.dl = dl; + } + + @Override + public String toString() { + return "[D]" + super.toString() + " (by: " + dl + ")"; + } +} \ No newline at end of file diff --git a/src/main/java/Event.java b/src/main/java/Event.java new file mode 100644 index 0000000000..8addfeedca --- /dev/null +++ b/src/main/java/Event.java @@ -0,0 +1,13 @@ +public class Event extends Task { + protected String time; //deadline given + + public Event(String taskName, String dl) { + super(taskName); + this.time = time; + } + + @Override + public String toString() { + return "[E]" + super.toString() + " (at: " + time + ")"; + } +} diff --git a/src/main/java/Todo.java b/src/main/java/Todo.java new file mode 100644 index 0000000000..a4c4c9ab2c --- /dev/null +++ b/src/main/java/Todo.java @@ -0,0 +1,11 @@ +public class Todo extends Task { + + public Todo(String taskName) { + super(taskName); + } + + @Override + public String toString() { + return "[T]" + super.toString(); + } +} From 6139e1e3fe173e71e3ef1213f7e4b8c492361389 Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Thu, 20 Aug 2020 17:12:45 +0800 Subject: [PATCH 18/95] Changed accessibility for "private" variables to "protected" --- src/main/java/Task.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/Task.java b/src/main/java/Task.java index 1acf93d799..d847d9948b 100644 --- a/src/main/java/Task.java +++ b/src/main/java/Task.java @@ -1,7 +1,7 @@ //Adapted from https://nus-cs2103-ay2021s1.github.io/website/schedule/week2/project.html public class Task { - private final String taskName; - private boolean isDone; + protected final String taskName; + protected boolean isDone; public Task(String taskName) { this.taskName = taskName; @@ -12,7 +12,7 @@ public String getTaskName() { return taskName; } - private String getStatusIcon() { + protected String getStatusIcon() { return isDone ? "\u2713" : "\u2718"; //return tick or cross symbols } From 86f01bb43cdc14a478eb9b9c7788c0fc3d90b2f6 Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Thu, 20 Aug 2020 17:37:29 +0800 Subject: [PATCH 19/95] Changed ADD Command type to NOT_FOUND for inputs that don't match any commands, added TODO, DEADLINE and EVENT to Command types --- src/main/java/Message.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/java/Message.java b/src/main/java/Message.java index 17eefaa5dc..b1dffa8fa8 100644 --- a/src/main/java/Message.java +++ b/src/main/java/Message.java @@ -5,7 +5,10 @@ enum Command { LIST, DONE, BYE, - ADD, + TODO, + DEADLINE, + EVENT, + NOT_FOUND } public class Message { @@ -27,7 +30,7 @@ public Message(String message, List list) { } if (this.cmd == null) { // if not a recognised command - this.cmd = Command.ADD; + this.cmd = Command.NOT_FOUND; } this.list = list; @@ -66,9 +69,9 @@ public void reply() { case BYE: System.out.println("Ok bye bye! C u again :P"); break; - case ADD: + case NOT_FOUND: this.list.add(new Task(message)); - System.out.println("I added: " + message); + System.out.println("I cannot find this command leh. Try sth else?"); break; } System.out.println("____________________________________________________________"); From ebd9ad335eece2482663d614eabeda11c3ee9394 Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Thu, 20 Aug 2020 17:38:50 +0800 Subject: [PATCH 20/95] Changed switch statements to if statements in Message.java, updated reply() for NOT_FOUND command --- src/main/java/Message.java | 63 +++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 35 deletions(-) diff --git a/src/main/java/Message.java b/src/main/java/Message.java index b1dffa8fa8..d3298aec10 100644 --- a/src/main/java/Message.java +++ b/src/main/java/Message.java @@ -38,41 +38,34 @@ public Message(String message, List list) { public void reply() { System.out.println("____________________________________________________________"); - switch (cmd) { - case START: - System.out.println("Eh what's up! I'm Meimei" + - "\nWhat you want ah?"); - break; - case LIST: - System.out.println("Na, here is your list lah:"); - int counter = 0; - for (Task item : list) { - counter++; - System.out.println(counter + - "." + - item.toString()); - } - break; - case DONE: - String[] words = message.split(" "); - try { - int index = Integer.parseInt(words[1]); - Task item = list.get(index - 1); - item.markDone(); - System.out.println("Can, I help you mark this as done liao:" + - "\n " + - item.toString()); - } catch (NumberFormatException e) { - System.out.println("Cannot find leh. Try typing \"done {index of list item}\"."); - } - break; - case BYE: - System.out.println("Ok bye bye! C u again :P"); - break; - case NOT_FOUND: - this.list.add(new Task(message)); - System.out.println("I cannot find this command leh. Try sth else?"); - break; + if (cmd == Command.START) { + System.out.println("Eh what's up! I'm Meimei" + + "\nWhat you want ah?"); + } else if (cmd == Command.LIST) { + System.out.println("Na, here is your list lah:"); + int counter = 0; + for (Task item : list) { + counter++; + System.out.println(counter + + "." + + item.toString()); + } + } else if (cmd == Command.DONE) { + String[] words = message.split(" "); + try { + int index = Integer.parseInt(words[1]); + Task item = list.get(index - 1); + item.markDone(); + System.out.println("Can, I help you mark this as done liao:" + + "\n " + + item.toString()); + } catch (NumberFormatException e) { + System.out.println("Cannot find leh. Try typing \"done {index of list item}\"."); + } + } else if (cmd == Command.BYE) { + System.out.println("Ok bye bye! C u again :P"); + } else if (cmd == Command.NOT_FOUND) { + System.out.println("I cannot find this command leh. Try sth else?"); } System.out.println("____________________________________________________________"); } From 0a72af0177cda083ecd936f44174b43db0ed7472 Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Thu, 20 Aug 2020 18:04:17 +0800 Subject: [PATCH 21/95] Fixed bug caused by typo in Event class, changed "dl" to "time" --- src/main/java/Event.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/Event.java b/src/main/java/Event.java index 8addfeedca..92b11ace7f 100644 --- a/src/main/java/Event.java +++ b/src/main/java/Event.java @@ -1,7 +1,7 @@ public class Event extends Task { protected String time; //deadline given - public Event(String taskName, String dl) { + public Event(String taskName, String time) { super(taskName); this.time = time; } From 3381f40482c2c77081a022655fdd5a2a26f2195f Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Thu, 20 Aug 2020 18:05:03 +0800 Subject: [PATCH 22/95] Added reply() method for cases of Command types TODO, DEADLINE and EVENT --- src/main/java/Message.java | 48 ++++++++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/src/main/java/Message.java b/src/main/java/Message.java index d3298aec10..a1e49e4f1a 100644 --- a/src/main/java/Message.java +++ b/src/main/java/Message.java @@ -38,10 +38,10 @@ public Message(String message, List list) { public void reply() { System.out.println("____________________________________________________________"); - if (cmd == Command.START) { + if (this.cmd == Command.START) { System.out.println("Eh what's up! I'm Meimei" + "\nWhat you want ah?"); - } else if (cmd == Command.LIST) { + } else if (this.cmd == Command.LIST) { System.out.println("Na, here is your list lah:"); int counter = 0; for (Task item : list) { @@ -50,7 +50,7 @@ public void reply() { "." + item.toString()); } - } else if (cmd == Command.DONE) { + } else if (this.cmd == Command.DONE) { String[] words = message.split(" "); try { int index = Integer.parseInt(words[1]); @@ -62,9 +62,47 @@ public void reply() { } catch (NumberFormatException e) { System.out.println("Cannot find leh. Try typing \"done {index of list item}\"."); } - } else if (cmd == Command.BYE) { + } else if (this.cmd == Command.BYE) { System.out.println("Ok bye bye! C u again :P"); - } else if (cmd == Command.NOT_FOUND) { + } else if (this.cmd == Command.TODO || + this.cmd == Command.DEADLINE || + this.cmd == Command.EVENT) { + String[] words = message.split(" ", 2); + String taskString = words[1]; + + Task t = null; + if (this.cmd == Command.TODO) { + t = new Todo(taskString); + } else if (this.cmd == Command.DEADLINE) { + String[] temp = taskString.split(" /by "); + if (temp.length == 2) { + t = new Deadline(temp[0], temp[1]); + } else { + System.out.println("Eh you type wrong lah!" + + "\nTry \"deadline {description of task} /by {date/time}\""); + } + } else { // should be event + String[] temp = taskString.split(" /at "); + if (temp.length == 2) { + t = new Event(temp[0], temp[1]); + } else { + System.out.println("Eh you type wrong lah!" + + "\nTry \"event {description of task} /at {date/time}\""); + } + } + + if (t != null) { + list.add(t); + + System.out.println("Orh. I added:" + + "\n " + + t.toString() + + "\nNow you got " + + list.size() + + " things in the list."); + } + + } else if (this.cmd == Command.NOT_FOUND) { System.out.println("I cannot find this command leh. Try sth else?"); } System.out.println("____________________________________________________________"); From 839111a41be27bde9ef874d9c0d91e5140ecce05 Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Thu, 20 Aug 2020 21:53:21 +0800 Subject: [PATCH 23/95] Updated files in text-ui-test to use the I/O redirection technique to test --- text-ui-test/EXPECTED.TXT | 84 +++++++++++++++++++++++++++++++++++---- text-ui-test/input.txt | 14 +++++++ text-ui-test/runtest.sh | 3 +- 3 files changed, 93 insertions(+), 8 deletions(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 657e74f6e7..71679996b6 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,7 +1,77 @@ -Hello from - ____ _ -| _ \ _ _| | _____ -| | | | | | | |/ / _ \ -| |_| | |_| | < __/ -|____/ \__,_|_|\_\___| - +____________________________________________________________ +Eh what's up! I'm Meimei +What you want ah? +____________________________________________________________ +____________________________________________________________ +Orh. I added: + [T][✘] read book +Now you got 1 things in the list. +____________________________________________________________ +____________________________________________________________ +Orh. I added: + [D][✘] return book (by: June 6th) +Now you got 2 things in the list. +____________________________________________________________ +____________________________________________________________ +Orh. I added: + [E][✘] project meeting (at: Aug 6th 2-4pm) +Now you got 3 things in the list. +____________________________________________________________ +____________________________________________________________ +Orh. I added: + [T][✘] join sports club +Now you got 4 things in the list. +____________________________________________________________ +____________________________________________________________ +Na, here is your list lah: +1.[T][✘] read book +2.[D][✘] return book (by: June 6th) +3.[E][✘] project meeting (at: Aug 6th 2-4pm) +4.[T][✘] join sports club +____________________________________________________________ +____________________________________________________________ +Can, I help you mark this as done liao: + [T][✓] read book +____________________________________________________________ +____________________________________________________________ +Can, I help you mark this as done liao: + [T][✓] join sports club +____________________________________________________________ +____________________________________________________________ +Na, here is your list lah: +1.[T][✓] read book +2.[D][✘] return book (by: June 6th) +3.[E][✘] project meeting (at: Aug 6th 2-4pm) +4.[T][✓] join sports club +____________________________________________________________ +____________________________________________________________ +Orh. I added: + [T][✘] borrow book +Now you got 5 things in the list. +____________________________________________________________ +____________________________________________________________ +Na, here is your list lah: +1.[T][✓] read book +2.[D][✘] return book (by: June 6th) +3.[E][✘] project meeting (at: Aug 6th 2-4pm) +4.[T][✓] join sports club +5.[T][✘] borrow book +____________________________________________________________ +____________________________________________________________ +Orh. I added: + [D][✘] return book (by: Sunday) +Now you got 6 things in the list. +____________________________________________________________ +____________________________________________________________ +Orh. I added: + [E][✘] project meeting (at: Mon 2-4pm) +Now you got 7 things in the list. +____________________________________________________________ +____________________________________________________________ +Orh. I added: + [D][✘] do homework (by: no idea :-p) +Now you got 8 things in the list. +____________________________________________________________ +____________________________________________________________ +Ok bye bye! C u again :P +____________________________________________________________ diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index e69de29bb2..3cd166bf4e 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -0,0 +1,14 @@ +todo read book +deadline return book /by June 6th +event project meeting /at Aug 6th 2-4pm +todo join sports club +list +done 1 +done 4 +list +todo borrow book +list +deadline return book /by Sunday +event project meeting /at Mon 2-4pm +deadline do homework /by no idea :-p +bye \ No newline at end of file diff --git a/text-ui-test/runtest.sh b/text-ui-test/runtest.sh index e169618a34..d139263328 100644 --- a/text-ui-test/runtest.sh +++ b/text-ui-test/runtest.sh @@ -1,4 +1,5 @@ #!/usr/bin/env bash +export LC_ALL=en_GB.UTF-8 # create bin directory if it doesn't exist if [ ! -d "../bin" ] @@ -13,7 +14,7 @@ then fi # compile the code into the bin folder, terminates if error occurred -if ! javac -cp ../src -Xlint:none -d ../bin ../src/main/java/Duke.java +if ! javac -cp ../src -Xlint:none -d ../bin ../src/main/java/*.java then echo "********** BUILD FAILURE **********" exit 1 From de3933093784e9df9cec698bee4ef7acbf789989 Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Thu, 20 Aug 2020 22:18:08 +0800 Subject: [PATCH 24/95] Added DukeException.java to handle exceptions from the chatbot. --- src/main/java/DukeException.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/main/java/DukeException.java diff --git a/src/main/java/DukeException.java b/src/main/java/DukeException.java new file mode 100644 index 0000000000..9a08fe7419 --- /dev/null +++ b/src/main/java/DukeException.java @@ -0,0 +1,12 @@ +public class DukeException extends Exception { + + public DukeException(String message) { + super(message); + } + + @Override + public String getMessage() { + return "Aiyo! " + super.getMessage() + + "\n____________________________________________________________"; + } +} From 758a8e4fed8e18031792eca6c7640c87044f9c7b Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Thu, 20 Aug 2020 22:19:14 +0800 Subject: [PATCH 25/95] Added NoCommandException.java and NoDescriptionException.java to handle exceptions as named in the file names --- src/main/java/NoCommandException.java | 11 +++++++++++ src/main/java/NoDescriptionException.java | 11 +++++++++++ 2 files changed, 22 insertions(+) create mode 100644 src/main/java/NoCommandException.java create mode 100644 src/main/java/NoDescriptionException.java diff --git a/src/main/java/NoCommandException.java b/src/main/java/NoCommandException.java new file mode 100644 index 0000000000..7e96182fec --- /dev/null +++ b/src/main/java/NoCommandException.java @@ -0,0 +1,11 @@ +public class NoCommandException extends DukeException { + + public NoCommandException() { + super("I cannot find this command leh. Try sth else?"); + } + + @Override + public String getMessage() { + return super.getMessage(); + } +} diff --git a/src/main/java/NoDescriptionException.java b/src/main/java/NoDescriptionException.java new file mode 100644 index 0000000000..0ccf9517b9 --- /dev/null +++ b/src/main/java/NoDescriptionException.java @@ -0,0 +1,11 @@ +public class NoDescriptionException extends DukeException { + + public NoDescriptionException(String cmd) { + super("The description of " + cmd + " cannot be empty lah. Try again!"); + } + + @Override + public String getMessage() { + return super.getMessage(); + } +} From 2a5ce271b99ad637038e783c1afa1a533d04302e Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Thu, 20 Aug 2020 22:21:23 +0800 Subject: [PATCH 26/95] Updated Duke.java and Message.java such that incorrect inputs of 1. inputs with no commands and 2. todo/deadline/event commands with no description, are handled as exceptions --- src/main/java/Duke.java | 12 ++++++++++-- src/main/java/Message.java | 12 +++++++++--- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 646c79f219..3480674682 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -8,11 +8,19 @@ public static void main(String[] args) { List list = new ArrayList<>(); Message start = new Message("start", list); - start.reply(); + try { + start.reply(); + } catch (Exception e) { + System.out.println(e.getMessage()); + } while (sc.hasNext()) { Message msg = new Message(sc.nextLine(), list); - msg.reply(); + try { + msg.reply(); + } catch (Exception e) { + System.out.println(e.getMessage()); + } if (msg.getCmd() == Command.BYE) { break; } diff --git a/src/main/java/Message.java b/src/main/java/Message.java index a1e49e4f1a..0e2ee9afea 100644 --- a/src/main/java/Message.java +++ b/src/main/java/Message.java @@ -36,7 +36,7 @@ public Message(String message, List list) { this.list = list; } - public void reply() { + public void reply() throws NoDescriptionException, NoCommandException { System.out.println("____________________________________________________________"); if (this.cmd == Command.START) { System.out.println("Eh what's up! I'm Meimei" + @@ -68,7 +68,13 @@ public void reply() { this.cmd == Command.DEADLINE || this.cmd == Command.EVENT) { String[] words = message.split(" ", 2); - String taskString = words[1]; + String taskString; + + try { + taskString = words[1]; + } catch (Exception e) { + throw new NoDescriptionException(cmd.toString().toLowerCase()); + } Task t = null; if (this.cmd == Command.TODO) { @@ -103,7 +109,7 @@ public void reply() { } } else if (this.cmd == Command.NOT_FOUND) { - System.out.println("I cannot find this command leh. Try sth else?"); + throw new NoCommandException(); } System.out.println("____________________________________________________________"); } From 04e0bbe5cb7bbe3cc27846a4060179ee538e5b17 Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Thu, 20 Aug 2020 22:31:40 +0800 Subject: [PATCH 27/95] Added WrongItemIndexException.java to deal with user input errors from "done" and "delete" commands --- src/main/java/WrongItemIndexException.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/main/java/WrongItemIndexException.java diff --git a/src/main/java/WrongItemIndexException.java b/src/main/java/WrongItemIndexException.java new file mode 100644 index 0000000000..930c8c909d --- /dev/null +++ b/src/main/java/WrongItemIndexException.java @@ -0,0 +1,15 @@ +// for delete and done commands +public class WrongItemIndexException extends DukeException { + + public WrongItemIndexException(String cmd, int listLength) { + super("Cannot find leh. Try typing \"" + cmd + " {index of list item}\"." + + "\nYour list only got " + + listLength + + " things."); + } + + @Override + public String getMessage() { + return super.getMessage(); + } +} From 086487cfee5f6e37df19bed1d98b72a90ad27b69 Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Thu, 20 Aug 2020 22:32:32 +0800 Subject: [PATCH 28/95] Changed exceptions caught in main class to specifically DukeExceptions --- src/main/java/Duke.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 3480674682..073c5a1831 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -10,7 +10,7 @@ public static void main(String[] args) { Message start = new Message("start", list); try { start.reply(); - } catch (Exception e) { + } catch (DukeException e) { System.out.println(e.getMessage()); } @@ -18,7 +18,7 @@ public static void main(String[] args) { Message msg = new Message(sc.nextLine(), list); try { msg.reply(); - } catch (Exception e) { + } catch (DukeException e) { System.out.println(e.getMessage()); } if (msg.getCmd() == Command.BYE) { From f6777985e34919335bced4258d4e0d7220f4c57c Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Thu, 20 Aug 2020 22:37:05 +0800 Subject: [PATCH 29/95] Changed errors arising from using "done" commands to be handled with WrongItemIndexException --- src/main/java/Message.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/Message.java b/src/main/java/Message.java index 0e2ee9afea..e972de9c38 100644 --- a/src/main/java/Message.java +++ b/src/main/java/Message.java @@ -36,7 +36,7 @@ public Message(String message, List list) { this.list = list; } - public void reply() throws NoDescriptionException, NoCommandException { + public void reply() throws NoDescriptionException, NoCommandException, WrongItemIndexException { System.out.println("____________________________________________________________"); if (this.cmd == Command.START) { System.out.println("Eh what's up! I'm Meimei" + @@ -51,16 +51,16 @@ public void reply() throws NoDescriptionException, NoCommandException { item.toString()); } } else if (this.cmd == Command.DONE) { - String[] words = message.split(" "); try { + String[] words = message.split(" ", 2); int index = Integer.parseInt(words[1]); Task item = list.get(index - 1); item.markDone(); System.out.println("Can, I help you mark this as done liao:" + "\n " + item.toString()); - } catch (NumberFormatException e) { - System.out.println("Cannot find leh. Try typing \"done {index of list item}\"."); + } catch (Exception e) { + throw new WrongItemIndexException("done", list.size()); } } else if (this.cmd == Command.BYE) { System.out.println("Ok bye bye! C u again :P"); From b285f63fd3ba61277a5786880e5f301766861532 Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Thu, 20 Aug 2020 22:42:34 +0800 Subject: [PATCH 30/95] Added DELETE command type and its corresponding reply() method case --- src/main/java/Message.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/main/java/Message.java b/src/main/java/Message.java index e972de9c38..1d5ff74434 100644 --- a/src/main/java/Message.java +++ b/src/main/java/Message.java @@ -4,6 +4,7 @@ enum Command { START, LIST, DONE, + DELETE, BYE, TODO, DEADLINE, @@ -62,6 +63,21 @@ public void reply() throws NoDescriptionException, NoCommandException, WrongItem } catch (Exception e) { throw new WrongItemIndexException("done", list.size()); } + } else if (this.cmd == Command.DELETE) { + try { + String[] words = message.split(" ", 2); + int index = Integer.parseInt(words[1]); + Task item = list.get(index - 1); + list.remove(index - 1); + System.out.println("Okay, I deleted this liao:" + + "\n " + + item.toString() + + "\nNow left " + + list.size() + + " things in the list."); + } catch (Exception e) { + throw new WrongItemIndexException("done", list.size()); + } } else if (this.cmd == Command.BYE) { System.out.println("Ok bye bye! C u again :P"); } else if (this.cmd == Command.TODO || From 729fbcfb9317322bb4ca7d9faa7a89724fbd049a Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Thu, 27 Aug 2020 23:27:25 +0800 Subject: [PATCH 31/95] Ignore "data/duke.txt" when it is created. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index f69985ef1f..af6bf95f15 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ bin/ /text-ui-test/ACTUAL.txt text-ui-test/EXPECTED-UNIX.TXT +duke.txt From 9cc342eb4c7084985725c45d4cae03e71a36cf42 Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Thu, 27 Aug 2020 23:28:28 +0800 Subject: [PATCH 32/95] Read file "data/duke.txt" from hard disk on start up and pass it to each Message object --- src/main/java/Duke.java | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 073c5a1831..91a5fecda9 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -1,3 +1,6 @@ +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Scanner; @@ -7,7 +10,28 @@ public static void main(String[] args) { Scanner sc = new Scanner(System.in); List list = new ArrayList<>(); - Message start = new Message("start", list); + // Load the data from the hard disk when Duke starts up. + File taskFile = new File("data/duke.txt"); + if (!taskFile.exists()) { + File dataFolder = new File("data"); + if (!dataFolder.exists()) { + dataFolder.mkdirs(); + } + try { + taskFile.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + try { + FileToListReader fileReader = new FileToListReader(taskFile); + list = fileReader.getList(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + + Message start = new Message("start", list, taskFile); try { start.reply(); } catch (DukeException e) { @@ -15,7 +39,7 @@ public static void main(String[] args) { } while (sc.hasNext()) { - Message msg = new Message(sc.nextLine(), list); + Message msg = new Message(sc.nextLine(), list, taskFile); try { msg.reply(); } catch (DukeException e) { From 4435cacd5836c09c20337ce736c8f40f76417061 Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Thu, 27 Aug 2020 23:29:28 +0800 Subject: [PATCH 33/95] Change format of toString for Deadline and Event so that it is easier to separate date/time from task description --- src/main/java/Deadline.java | 2 +- src/main/java/Event.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/Deadline.java b/src/main/java/Deadline.java index 17b31ead8e..db91b4f182 100644 --- a/src/main/java/Deadline.java +++ b/src/main/java/Deadline.java @@ -12,6 +12,6 @@ public Deadline(String taskName, String dl) { @Override public String toString() { - return "[D]" + super.toString() + " (by: " + dl + ")"; + return "[D]" + super.toString() + " by: " + dl; } } \ No newline at end of file diff --git a/src/main/java/Event.java b/src/main/java/Event.java index 92b11ace7f..22a8912a51 100644 --- a/src/main/java/Event.java +++ b/src/main/java/Event.java @@ -8,6 +8,6 @@ public Event(String taskName, String time) { @Override public String toString() { - return "[E]" + super.toString() + " (at: " + time + ")"; + return "[E]" + super.toString() + " at: " + time; } } From e72475593d0d019d03302b93f601366d01948f42 Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Thu, 27 Aug 2020 23:29:56 +0800 Subject: [PATCH 34/95] Add FileToListReader.java to convert a file from hard disk to list of Task objects --- src/main/java/FileToListReader.java | 33 +++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 src/main/java/FileToListReader.java diff --git a/src/main/java/FileToListReader.java b/src/main/java/FileToListReader.java new file mode 100644 index 0000000000..ee0d19da76 --- /dev/null +++ b/src/main/java/FileToListReader.java @@ -0,0 +1,33 @@ +import java.io.File; +import java.io.FileNotFoundException; +import java.util.ArrayList; +import java.util.List; +import java.util.Scanner; + +public class FileToListReader { + private final List list = new ArrayList<>(); + + public FileToListReader(File taskfile) throws FileNotFoundException { + Scanner sc = new Scanner(taskfile); + while (sc.hasNext()) { + String listItem = sc.nextLine(); + String[] words = listItem.split(" ", 2); + + if (listItem.charAt(1) == 'T') { + list.add(new Todo(words[1])); + } else if (listItem.charAt(1) == 'D') { + String taskString = words[1]; + String[] temp = taskString.split(" by: "); + list.add(new Deadline(temp[0], temp[1])); + } else if (listItem.charAt(1) == 'E') { + String taskString = words[1]; + String[] temp = taskString.split(" at: "); + list.add(new Event(temp[0], temp[1])); + } + } + } + + public List getList() { + return this.list; + } +} From 1e93d9635e167685524bd5f4e0f7e85b5d384f91 Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Thu, 27 Aug 2020 23:31:01 +0800 Subject: [PATCH 35/95] Write to file "data/duke.txt" when changes are made to list of tasks --- src/main/java/Message.java | 47 ++++++++++++++++++++++++++++++++------ 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/src/main/java/Message.java b/src/main/java/Message.java index 1d5ff74434..f5f0aa40c2 100644 --- a/src/main/java/Message.java +++ b/src/main/java/Message.java @@ -1,3 +1,6 @@ +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; import java.util.List; enum Command { @@ -15,10 +18,11 @@ enum Command { public class Message { private final String message; private Command cmd; - private List list; + private final List list; + private final File taskFile; // public constructor - public Message(String message, List list) { + public Message(String message, List list, File taskFile) { this.message = message; String[] words = message.split(" "); String cmdString = words[0]; @@ -35,6 +39,7 @@ public Message(String message, List list) { } this.list = list; + this.taskFile = taskFile; } public void reply() throws NoDescriptionException, NoCommandException, WrongItemIndexException { @@ -45,7 +50,7 @@ public void reply() throws NoDescriptionException, NoCommandException, WrongItem } else if (this.cmd == Command.LIST) { System.out.println("Na, here is your list lah:"); int counter = 0; - for (Task item : list) { + for (Task item : this.list) { counter++; System.out.println(counter + "." + @@ -55,13 +60,23 @@ public void reply() throws NoDescriptionException, NoCommandException, WrongItem try { String[] words = message.split(" ", 2); int index = Integer.parseInt(words[1]); - Task item = list.get(index - 1); + Task item = this.list.get(index - 1); item.markDone(); + FileWriter fileWriter = new FileWriter(this.taskFile); + String fileContents = this.list.get(0).toString(); + + for (int i = 1; i < this.list.size(); i++) { + fileContents += "\n" + this.list.get(i).toString(); + } + + fileWriter.write(fileContents); + fileWriter.close(); + System.out.println("Can, I help you mark this as done liao:" + "\n " + item.toString()); } catch (Exception e) { - throw new WrongItemIndexException("done", list.size()); + throw new WrongItemIndexException("done", this.list.size()); } } else if (this.cmd == Command.DELETE) { try { @@ -69,14 +84,25 @@ public void reply() throws NoDescriptionException, NoCommandException, WrongItem int index = Integer.parseInt(words[1]); Task item = list.get(index - 1); list.remove(index - 1); + System.out.println("Okay, I deleted this liao:" + "\n " + item.toString() + "\nNow left " + list.size() + " things in the list."); + + FileWriter fileWriter = new FileWriter(this.taskFile); + String fileContents = this.list.get(0).toString(); + + for (int i = 1; i < this.list.size(); i++) { + fileContents += "\n" + this.list.get(i).toString(); + } + + fileWriter.write(fileContents); + fileWriter.close(); } catch (Exception e) { - throw new WrongItemIndexException("done", list.size()); + throw new WrongItemIndexException("done", this.list.size()); } } else if (this.cmd == Command.BYE) { System.out.println("Ok bye bye! C u again :P"); @@ -114,7 +140,14 @@ public void reply() throws NoDescriptionException, NoCommandException, WrongItem } if (t != null) { - list.add(t); + this.list.add(t); + try { + FileWriter fileWriter = new FileWriter(this.taskFile, true); + fileWriter.write(t.toString()); + fileWriter.close(); + } catch (IOException e) { + e.printStackTrace(); + } System.out.println("Orh. I added:" + "\n " + From a1711a07ab2e9cb14659bb222425d11af96c08ee Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Fri, 28 Aug 2020 00:08:01 +0800 Subject: [PATCH 36/95] Teach duke to parse date and times inputted as "yyyy-MM-dd HH:mm" and output as "d MMM yyyy h.m a" --- src/main/java/Deadline.java | 22 ++++++++++++++++++-- src/main/java/Event.java | 25 +++++++++++++++++++---- src/main/java/Message.java | 8 +++----- src/main/java/WrongDeadlineException.java | 11 ++++++++++ 4 files changed, 55 insertions(+), 11 deletions(-) create mode 100644 src/main/java/WrongDeadlineException.java diff --git a/src/main/java/Deadline.java b/src/main/java/Deadline.java index 17b31ead8e..8dac10f6ba 100644 --- a/src/main/java/Deadline.java +++ b/src/main/java/Deadline.java @@ -1,17 +1,35 @@ //@@author {FooJingYi}-reused //Reused from https://nus-cs2103-ay2021s1.github.io/website/schedule/week2/project.html with minor modifications +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; + public class Deadline extends Task { protected String dl; //deadline given + protected LocalDate date; + protected LocalTime time; - public Deadline(String taskName, String dl) { + public Deadline(String taskName, String dl) throws WrongDeadlineException { super(taskName); this.dl = dl; + String[] temp = dl.split(" ", 2); + try { + this.date = LocalDate.parse(temp[0]); + this.time = LocalTime.parse(temp[1]); + } catch (DateTimeParseException e) { + throw new WrongDeadlineException("deadline", "/by"); + } } @Override public String toString() { - return "[D]" + super.toString() + " (by: " + dl + ")"; + return "[D]" + super.toString() + " (by: " + + this.date.format(DateTimeFormatter.ofPattern("d MMM yyyy")) + + ", " + + this.time.format(DateTimeFormatter.ofPattern("h.m a")) + + ")"; } } \ No newline at end of file diff --git a/src/main/java/Event.java b/src/main/java/Event.java index 92b11ace7f..14fa9e6320 100644 --- a/src/main/java/Event.java +++ b/src/main/java/Event.java @@ -1,13 +1,30 @@ +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; + public class Event extends Task { - protected String time; //deadline given + protected String dl; //deadline given + protected LocalDate date; + protected LocalTime time; - public Event(String taskName, String time) { + public Event(String taskName, String dl) throws WrongDeadlineException { super(taskName); - this.time = time; + this.dl = dl; + String[] temp = dl.split(" ", 2); + try { + this.date = LocalDate.parse(temp[0]); + this.time = LocalTime.parse(temp[1]); + } catch (DateTimeParseException e) { + throw new WrongDeadlineException("event", "/at"); + } } @Override public String toString() { - return "[E]" + super.toString() + " (at: " + time + ")"; + return "[E]" + super.toString() + " (at: " + + this.date.format(DateTimeFormatter.ofPattern("d MMM yyyy")) + + ", " + + this.time.format(DateTimeFormatter.ofPattern("h.m a")) + ")"; } } diff --git a/src/main/java/Message.java b/src/main/java/Message.java index 1d5ff74434..0baea1ca20 100644 --- a/src/main/java/Message.java +++ b/src/main/java/Message.java @@ -37,7 +37,7 @@ public Message(String message, List list) { this.list = list; } - public void reply() throws NoDescriptionException, NoCommandException, WrongItemIndexException { + public void reply() throws NoDescriptionException, NoCommandException, WrongItemIndexException, WrongDeadlineException { System.out.println("____________________________________________________________"); if (this.cmd == Command.START) { System.out.println("Eh what's up! I'm Meimei" + @@ -100,16 +100,14 @@ public void reply() throws NoDescriptionException, NoCommandException, WrongItem if (temp.length == 2) { t = new Deadline(temp[0], temp[1]); } else { - System.out.println("Eh you type wrong lah!" + - "\nTry \"deadline {description of task} /by {date/time}\""); + throw new WrongDeadlineException("deadline", "/by"); } } else { // should be event String[] temp = taskString.split(" /at "); if (temp.length == 2) { t = new Event(temp[0], temp[1]); } else { - System.out.println("Eh you type wrong lah!" + - "\nTry \"event {description of task} /at {date/time}\""); + throw new WrongDeadlineException("event", "/at"); } } diff --git a/src/main/java/WrongDeadlineException.java b/src/main/java/WrongDeadlineException.java new file mode 100644 index 0000000000..6283942483 --- /dev/null +++ b/src/main/java/WrongDeadlineException.java @@ -0,0 +1,11 @@ +// for deadline or exception +public class WrongDeadlineException extends DukeException { + public WrongDeadlineException(String cmd, String separator) { + super("You type wrong lah!" + + "\nTry \"" + + cmd + + " {description of task} " + + separator + + " {date and time in this format: yyyy-MM-dd HH:mm}\""); + } +} From 400181a7840f7bc59dbfc7d87935faa1863f6d01 Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Fri, 28 Aug 2020 00:59:32 +0800 Subject: [PATCH 37/95] Change Deadline and Event to work with LocalDateTime objects rather than Strings to represent the deadlines and change format of dates and times as inputs --- src/main/java/Deadline.java | 25 +++++---------------- src/main/java/Duke.java | 2 ++ src/main/java/Event.java | 23 +++++-------------- src/main/java/FileToListReader.java | 12 +++++++--- src/main/java/Message.java | 27 ++++++++++++++--------- src/main/java/WrongDeadlineException.java | 11 +++++++++ 6 files changed, 48 insertions(+), 52 deletions(-) create mode 100644 src/main/java/WrongDeadlineException.java diff --git a/src/main/java/Deadline.java b/src/main/java/Deadline.java index ae986eb583..28cc8724cf 100644 --- a/src/main/java/Deadline.java +++ b/src/main/java/Deadline.java @@ -1,34 +1,19 @@ //@@author {FooJingYi}-reused //Reused from https://nus-cs2103-ay2021s1.github.io/website/schedule/week2/project.html with minor modifications -import java.time.LocalDate; -import java.time.LocalTime; +import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; -import java.time.format.DateTimeParseException; public class Deadline extends Task { - - protected String dl; //deadline given - protected LocalDate date; - protected LocalTime time; - - public Deadline(String taskName, String dl) throws WrongDeadlineException { + protected LocalDateTime dateTime; + public Deadline(String taskName, LocalDateTime dateTime) { super(taskName); - this.dl = dl; - String[] temp = dl.split(" ", 2); - try { - this.date = LocalDate.parse(temp[0]); - this.time = LocalTime.parse(temp[1]); - } catch (DateTimeParseException e) { - throw new WrongDeadlineException("deadline", "/by"); - } + this.dateTime = dateTime; } @Override public String toString() { return "[D]" + super.toString() + " by: " + - this.date.format(DateTimeFormatter.ofPattern("d MMM yyyy")) + - ", " + - this.time.format(DateTimeFormatter.ofPattern("h.m a")); + this.dateTime.format(DateTimeFormatter.ofPattern("d MMM yyyy, h.m a")); } } \ No newline at end of file diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 91a5fecda9..9523500537 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -29,6 +29,8 @@ public static void main(String[] args) { list = fileReader.getList(); } catch (FileNotFoundException e) { e.printStackTrace(); + } catch (WrongDeadlineException e) { + e.printStackTrace(); } Message start = new Message("start", list, taskFile); diff --git a/src/main/java/Event.java b/src/main/java/Event.java index aaab880e5d..d199ae3ad1 100644 --- a/src/main/java/Event.java +++ b/src/main/java/Event.java @@ -1,30 +1,17 @@ -import java.time.LocalDate; -import java.time.LocalTime; +import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; -import java.time.format.DateTimeParseException; public class Event extends Task { - protected String dl; //deadline given - protected LocalDate date; - protected LocalTime time; + protected LocalDateTime dateTime; - public Event(String taskName, String dl) throws WrongDeadlineException { + public Event(String taskName, LocalDateTime dateTime) { super(taskName); - this.dl = dl; - String[] temp = dl.split(" ", 2); - try { - this.date = LocalDate.parse(temp[0]); - this.time = LocalTime.parse(temp[1]); - } catch (DateTimeParseException e) { - throw new WrongDeadlineException("event", "/at"); - } + this.dateTime = dateTime; } @Override public String toString() { return "[E]" + super.toString() + " at: " + - this.date.format(DateTimeFormatter.ofPattern("d MMM yyyy")) + - ", " + - this.time.format(DateTimeFormatter.ofPattern("h.m a")); + this.dateTime.format(DateTimeFormatter.ofPattern("d MMM yyyy, h.m a")); } } diff --git a/src/main/java/FileToListReader.java b/src/main/java/FileToListReader.java index ee0d19da76..63852f6450 100644 --- a/src/main/java/FileToListReader.java +++ b/src/main/java/FileToListReader.java @@ -1,5 +1,7 @@ import java.io.File; import java.io.FileNotFoundException; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.List; import java.util.Scanner; @@ -7,7 +9,7 @@ public class FileToListReader { private final List list = new ArrayList<>(); - public FileToListReader(File taskfile) throws FileNotFoundException { + public FileToListReader(File taskfile) throws FileNotFoundException, WrongDeadlineException { Scanner sc = new Scanner(taskfile); while (sc.hasNext()) { String listItem = sc.nextLine(); @@ -18,11 +20,15 @@ public FileToListReader(File taskfile) throws FileNotFoundException { } else if (listItem.charAt(1) == 'D') { String taskString = words[1]; String[] temp = taskString.split(" by: "); - list.add(new Deadline(temp[0], temp[1])); + LocalDateTime dateTime = LocalDateTime.parse(temp[1], + DateTimeFormatter.ofPattern("d MMM yyyy, h.m a")); + list.add(new Deadline(temp[0], dateTime)); } else if (listItem.charAt(1) == 'E') { String taskString = words[1]; String[] temp = taskString.split(" at: "); - list.add(new Event(temp[0], temp[1])); + LocalDateTime dateTime = LocalDateTime.parse(temp[1], + DateTimeFormatter.ofPattern("d MMM yyyy, h.m a")); + list.add(new Deadline(temp[0], dateTime)); } } } diff --git a/src/main/java/Message.java b/src/main/java/Message.java index f5f0aa40c2..4233d6e1d0 100644 --- a/src/main/java/Message.java +++ b/src/main/java/Message.java @@ -1,6 +1,9 @@ import java.io.File; import java.io.FileWriter; import java.io.IOException; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; import java.util.List; enum Command { @@ -42,7 +45,7 @@ public Message(String message, List list, File taskFile) { this.taskFile = taskFile; } - public void reply() throws NoDescriptionException, NoCommandException, WrongItemIndexException { + public void reply() throws NoDescriptionException, NoCommandException, WrongItemIndexException, WrongDeadlineException { System.out.println("____________________________________________________________"); if (this.cmd == Command.START) { System.out.println("Eh what's up! I'm Meimei" + @@ -123,19 +126,21 @@ public void reply() throws NoDescriptionException, NoCommandException, WrongItem t = new Todo(taskString); } else if (this.cmd == Command.DEADLINE) { String[] temp = taskString.split(" /by "); - if (temp.length == 2) { - t = new Deadline(temp[0], temp[1]); - } else { - System.out.println("Eh you type wrong lah!" + - "\nTry \"deadline {description of task} /by {date/time}\""); + try { + LocalDateTime dateTime = LocalDateTime.parse(temp[1], + DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm")); + t = new Deadline(temp[0], dateTime); + } catch (Exception e) { + throw new WrongDeadlineException("deadline", "/by"); } } else { // should be event String[] temp = taskString.split(" /at "); - if (temp.length == 2) { - t = new Event(temp[0], temp[1]); - } else { - System.out.println("Eh you type wrong lah!" + - "\nTry \"event {description of task} /at {date/time}\""); + try { + LocalDateTime dateTime = LocalDateTime.parse(temp[1], + DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm")); + t = new Event(temp[0], dateTime); + } catch (Exception e) { + throw new WrongDeadlineException("event", "/at"); } } diff --git a/src/main/java/WrongDeadlineException.java b/src/main/java/WrongDeadlineException.java new file mode 100644 index 0000000000..67bb60450a --- /dev/null +++ b/src/main/java/WrongDeadlineException.java @@ -0,0 +1,11 @@ +// for deadline or exception +public class WrongDeadlineException extends DukeException { + public WrongDeadlineException(String cmd, String separator) { + super("You type wrong lah!" + + "\nTry \"" + + cmd + + " {description of task} " + + separator + + " {date and time in this format: dd/MM/yyyy HH:mm}\""); + } +} From 46414706e86458c59f68600d3a628c2e798d12a9 Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Fri, 28 Aug 2020 12:33:22 +0800 Subject: [PATCH 38/95] Change FileToListReader.java to Storage.java, and added functionality to update file in hard disk Have not implemented relevant errors --- src/main/java/FileToListReader.java | 39 ------------ src/main/java/Storage.java | 92 +++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 39 deletions(-) delete mode 100644 src/main/java/FileToListReader.java create mode 100644 src/main/java/Storage.java diff --git a/src/main/java/FileToListReader.java b/src/main/java/FileToListReader.java deleted file mode 100644 index 63852f6450..0000000000 --- a/src/main/java/FileToListReader.java +++ /dev/null @@ -1,39 +0,0 @@ -import java.io.File; -import java.io.FileNotFoundException; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.List; -import java.util.Scanner; - -public class FileToListReader { - private final List list = new ArrayList<>(); - - public FileToListReader(File taskfile) throws FileNotFoundException, WrongDeadlineException { - Scanner sc = new Scanner(taskfile); - while (sc.hasNext()) { - String listItem = sc.nextLine(); - String[] words = listItem.split(" ", 2); - - if (listItem.charAt(1) == 'T') { - list.add(new Todo(words[1])); - } else if (listItem.charAt(1) == 'D') { - String taskString = words[1]; - String[] temp = taskString.split(" by: "); - LocalDateTime dateTime = LocalDateTime.parse(temp[1], - DateTimeFormatter.ofPattern("d MMM yyyy, h.m a")); - list.add(new Deadline(temp[0], dateTime)); - } else if (listItem.charAt(1) == 'E') { - String taskString = words[1]; - String[] temp = taskString.split(" at: "); - LocalDateTime dateTime = LocalDateTime.parse(temp[1], - DateTimeFormatter.ofPattern("d MMM yyyy, h.m a")); - list.add(new Deadline(temp[0], dateTime)); - } - } - } - - public List getList() { - return this.list; - } -} diff --git a/src/main/java/Storage.java b/src/main/java/Storage.java new file mode 100644 index 0000000000..5615af649d --- /dev/null +++ b/src/main/java/Storage.java @@ -0,0 +1,92 @@ +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileWriter; +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; +import java.util.Scanner; + +public class Storage { + private final File taskFile; + + public Storage(String filePath) { + this.taskFile = new File(filePath); + } + + public List load() { + if (!this.taskFile.exists()) { + File dir = this.taskFile.getParentFile(); + if (dir != null && !dir.exists()) { + dir.mkdirs(); + } + + try { + this.taskFile.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); // todo + } + } + + Scanner sc = null; + try { + sc = new Scanner(this.taskFile); + } catch (FileNotFoundException e) { + e.printStackTrace(); // todo + } + + if (sc != null) { + List list = new ArrayList<>(); + while (sc.hasNext()) { + String listItem = sc.nextLine(); + String[] words = listItem.split(" ", 2); + + if (listItem.charAt(1) == 'T') { + list.add(new Todo(words[1])); + } else if (listItem.charAt(1) == 'D') { + String taskString = words[1]; + String[] temp = taskString.split(" by: "); + LocalDateTime dateTime = LocalDateTime.parse(temp[1], + DateTimeFormatter.ofPattern("d MMM yyyy, h.m a")); + list.add(new Deadline(temp[0], dateTime)); + } else if (listItem.charAt(1) == 'E') { + String taskString = words[1]; + String[] temp = taskString.split(" at: "); + LocalDateTime dateTime = LocalDateTime.parse(temp[1], + DateTimeFormatter.ofPattern("d MMM yyyy, h.m a")); + list.add(new Deadline(temp[0], dateTime)); + } + } + return list; + } else { + return new ArrayList(); //todo + } + } + + public void update(Task task) { + try { + FileWriter fileWriter = new FileWriter(this.taskFile, true); + fileWriter.write(task.toString()); + fileWriter.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public void update(List list) { + try { + FileWriter fileWriter = new FileWriter(this.taskFile); + String fileContents = list.get(0).toString(); + + for (int i = 1; i < list.size(); i++) { + fileContents += "\n" + list.get(i).toString(); + } + + fileWriter.write(fileContents); + fileWriter.close(); + } catch (IOException e) { + e.printStackTrace(); // todo + } + } +} From 0e2d60cfda44a00f3f9d51c2e3a0b29aba6e7d73 Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Fri, 28 Aug 2020 12:33:53 +0800 Subject: [PATCH 39/95] Add Ui.java to deal with interactions with user --- src/main/java/Ui.java | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/main/java/Ui.java diff --git a/src/main/java/Ui.java b/src/main/java/Ui.java new file mode 100644 index 0000000000..9138dc3640 --- /dev/null +++ b/src/main/java/Ui.java @@ -0,0 +1,22 @@ +import java.util.Scanner; + +public class Ui { + private final Scanner sc = new Scanner(System.in); + + public void showWelcomeMsg() { + System.out.println("Eh what's up! I'm Meimei" + + "\nWhat you want ah?"); + } + + public String readCommand() { + return sc.nextLine(); + } + + public void showLine() { + System.out.println("____________________________________________________________"); + } + + public void showError(String message) { + System.out.println(message); + } +} From 02060a6f6f07cf490abe1df61303f8f4e131a943 Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Fri, 28 Aug 2020 18:21:41 +0800 Subject: [PATCH 40/95] Abstract content of load() and update() methods to new methods parseToStorage() and parseFromStorage() --- src/main/java/Storage.java | 98 ++++++++++++++++++++++++++++---------- 1 file changed, 74 insertions(+), 24 deletions(-) diff --git a/src/main/java/Storage.java b/src/main/java/Storage.java index 5615af649d..baed10f065 100644 --- a/src/main/java/Storage.java +++ b/src/main/java/Storage.java @@ -15,7 +15,7 @@ public Storage(String filePath) { this.taskFile = new File(filePath); } - public List load() { + public List load() throws DukeException { if (!this.taskFile.exists()) { File dir = this.taskFile.getParentFile(); if (dir != null && !dir.exists()) { @@ -39,24 +39,9 @@ public List load() { if (sc != null) { List list = new ArrayList<>(); while (sc.hasNext()) { - String listItem = sc.nextLine(); - String[] words = listItem.split(" ", 2); - - if (listItem.charAt(1) == 'T') { - list.add(new Todo(words[1])); - } else if (listItem.charAt(1) == 'D') { - String taskString = words[1]; - String[] temp = taskString.split(" by: "); - LocalDateTime dateTime = LocalDateTime.parse(temp[1], - DateTimeFormatter.ofPattern("d MMM yyyy, h.m a")); - list.add(new Deadline(temp[0], dateTime)); - } else if (listItem.charAt(1) == 'E') { - String taskString = words[1]; - String[] temp = taskString.split(" at: "); - LocalDateTime dateTime = LocalDateTime.parse(temp[1], - DateTimeFormatter.ofPattern("d MMM yyyy, h.m a")); - list.add(new Deadline(temp[0], dateTime)); - } + String storedTask = sc.nextLine(); + Task task = parseFromStorage(storedTask); + list.add(task); } return list; } else { @@ -64,23 +49,23 @@ public List load() { } } - public void update(Task task) { + public void update(Task task) throws DukeException { try { FileWriter fileWriter = new FileWriter(this.taskFile, true); - fileWriter.write(task.toString()); + fileWriter.write("\n" + parseToStorage(task)); fileWriter.close(); } catch (IOException e) { e.printStackTrace(); } } - public void update(List list) { + public void update(List list) throws DukeException { try { FileWriter fileWriter = new FileWriter(this.taskFile); - String fileContents = list.get(0).toString(); + String fileContents = parseToStorage(list.get(0)); for (int i = 1; i < list.size(); i++) { - fileContents += "\n" + list.get(i).toString(); + fileContents += "\n" + parseToStorage(list.get(i)); } fileWriter.write(fileContents); @@ -89,4 +74,69 @@ public void update(List list) { e.printStackTrace(); // todo } } + + public String parseToStorage(Task task) throws DukeException { + String taskType = ""; + String status = task.isDone() ? "1" : "0"; + String taskDescription = ""; + + if (task instanceof Todo) { + taskType = "T"; + taskDescription = task.getTaskName(); + } else if (task instanceof Deadline) { + taskType = "D"; // + date + taskDescription = task.getTaskName() + + " | " + + ((Deadline) task).getDateTime().format( + DateTimeFormatter.ofPattern("d MMM yyyy, h.m a")); + } else if (task instanceof Event) { + taskType = "E"; + taskDescription = task.getTaskName() + + " | " + + ((Event) task).getDateTime().format( + DateTimeFormatter.ofPattern("d MMM yyyy, h.m a")); + } else { + throw new DukeException("Cannot recognise type"); + } + + return taskType + + " | " + + status + + " | " + + taskDescription; + } + + public Task parseFromStorage(String storedTask) throws DukeException { + String[] taskElements = storedTask.split(" \\| ", 4); + + try { + Task task = null; + if (storedTask.charAt(0) == 'T') { + task = new Todo(taskElements[2]); + } else if (storedTask.charAt(0) == 'D') { + String taskName = taskElements[2]; + LocalDateTime dateTime = LocalDateTime.parse(taskElements[3], + DateTimeFormatter.ofPattern("d MMM yyyy, h.m a")); + task = new Deadline(taskName, dateTime); + } else if (storedTask.charAt(0) == 'E') { + String taskName = taskElements[2]; + LocalDateTime dateTime = LocalDateTime.parse(taskElements[3], + DateTimeFormatter.ofPattern("d MMM yyyy, h.m a")); + task = new Event(taskName, dateTime); + } + + if (taskElements[1].equals("1")) { + task.markDone(); + } + + if (task != null) { + return task; + } else { + throw new DukeException("Cannot read tasks from file."); + } + } catch (Exception e) { + System.out.println(e); + throw new DukeException("Cannot read tasks from file."); + } + } } From c217a62aa9587ea3642a247c40e6c1ddbc1abff9 Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Fri, 28 Aug 2020 18:22:21 +0800 Subject: [PATCH 41/95] Ignore tasks.txt created through using the chatbot --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index af6bf95f15..6b779900da 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ bin/ /text-ui-test/ACTUAL.txt text-ui-test/EXPECTED-UNIX.TXT duke.txt +tasks.txt From 08625153def367a1f2974703e14e8807b2e7bc0b Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Fri, 28 Aug 2020 18:22:48 +0800 Subject: [PATCH 42/95] Update welcome message in Ui.java --- src/main/java/Ui.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/Ui.java b/src/main/java/Ui.java index 9138dc3640..c524a2dddf 100644 --- a/src/main/java/Ui.java +++ b/src/main/java/Ui.java @@ -4,8 +4,10 @@ public class Ui { private final Scanner sc = new Scanner(System.in); public void showWelcomeMsg() { + System.out.println("____________________________________________________________"); System.out.println("Eh what's up! I'm Meimei" + "\nWhat you want ah?"); + System.out.println("____________________________________________________________"); } public String readCommand() { From 7abd6e10e5af60940f7f72f0d01713bfb568e779 Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Fri, 28 Aug 2020 18:23:37 +0800 Subject: [PATCH 43/95] Add TaskList.java to contain the list of task and methods for manipulating this list --- src/main/java/TaskList.java | 49 +++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 src/main/java/TaskList.java diff --git a/src/main/java/TaskList.java b/src/main/java/TaskList.java new file mode 100644 index 0000000000..fe5c6711ce --- /dev/null +++ b/src/main/java/TaskList.java @@ -0,0 +1,49 @@ +import java.util.ArrayList; +import java.util.List; + +public class TaskList { + private final List list; + + public TaskList() { + this.list = new ArrayList<>(); + } + + public TaskList(List list) { + this.list = list; + } + + public void add(Task task, Storage storage) throws DukeException { + this.list.add(task); + storage.update(task); + } + + public void delete(int taskNum, Storage storage) throws DukeException { + this.list.remove(taskNum - 1); + storage.update(this.list); + } + + public void markDone(int taskNum, Storage storage) throws DukeException { + this.list.get(taskNum - 1).markDone(); + storage.update(this.list); + } + + public Task getTask(int taskNum) { + return this.list.get(taskNum - 1); + } + + public int getListLength() { + return this.list.size(); + } + + @Override + public String toString() { + String finalString = ""; + int counter = 0; + for (Task task : this.list) { + counter++; + finalString += "\n" + counter + "." + task.toString(); + } + + return finalString; + } +} From 5865350cd0feee655af3cf087f1a097c80328894 Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Fri, 28 Aug 2020 18:24:33 +0800 Subject: [PATCH 44/95] Add methods to Task.java and its children to support functionality of Storage and TaskList --- src/main/java/Deadline.java | 5 +++++ src/main/java/Event.java | 4 ++++ src/main/java/Task.java | 4 ++++ 3 files changed, 13 insertions(+) diff --git a/src/main/java/Deadline.java b/src/main/java/Deadline.java index 28cc8724cf..b50ece16e7 100644 --- a/src/main/java/Deadline.java +++ b/src/main/java/Deadline.java @@ -6,11 +6,16 @@ public class Deadline extends Task { protected LocalDateTime dateTime; + public Deadline(String taskName, LocalDateTime dateTime) { super(taskName); this.dateTime = dateTime; } + public LocalDateTime getDateTime() { + return dateTime; + } + @Override public String toString() { return "[D]" + super.toString() + " by: " + diff --git a/src/main/java/Event.java b/src/main/java/Event.java index d199ae3ad1..be4716f00a 100644 --- a/src/main/java/Event.java +++ b/src/main/java/Event.java @@ -9,6 +9,10 @@ public Event(String taskName, LocalDateTime dateTime) { this.dateTime = dateTime; } + public LocalDateTime getDateTime() { + return dateTime; + } + @Override public String toString() { return "[E]" + super.toString() + " at: " + diff --git a/src/main/java/Task.java b/src/main/java/Task.java index d847d9948b..b735bd140d 100644 --- a/src/main/java/Task.java +++ b/src/main/java/Task.java @@ -16,6 +16,10 @@ protected String getStatusIcon() { return isDone ? "\u2713" : "\u2718"; //return tick or cross symbols } + public boolean isDone() { + return this.isDone; + } + public void markDone() { this.isDone = true; } From 9c00f727350edee6a08515ca5c3545f0b7d29c21 Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Fri, 28 Aug 2020 18:25:18 +0800 Subject: [PATCH 45/95] Separate Message.java functionality into Parser.java and Command.java and its children --- src/main/java/AddCommand.java | 56 ++++++++++ src/main/java/Command.java | 6 ++ src/main/java/DeleteCommand.java | 31 ++++++ src/main/java/DoneCommand.java | 28 +++++ src/main/java/ExitCommand.java | 11 ++ src/main/java/ListCommand.java | 12 +++ src/main/java/Message.java | 174 ------------------------------- src/main/java/Parser.java | 50 +++++++++ 8 files changed, 194 insertions(+), 174 deletions(-) create mode 100644 src/main/java/AddCommand.java create mode 100644 src/main/java/Command.java create mode 100644 src/main/java/DeleteCommand.java create mode 100644 src/main/java/DoneCommand.java create mode 100644 src/main/java/ExitCommand.java create mode 100644 src/main/java/ListCommand.java delete mode 100644 src/main/java/Message.java create mode 100644 src/main/java/Parser.java diff --git a/src/main/java/AddCommand.java b/src/main/java/AddCommand.java new file mode 100644 index 0000000000..51c14e0ea4 --- /dev/null +++ b/src/main/java/AddCommand.java @@ -0,0 +1,56 @@ +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +public class AddCommand extends Command { + private CommandType commandType; + private final String description; + + public AddCommand(CommandType commandType, String description) { + this.commandType = commandType; + this.description = description; + } + + @Override + public void execute(TaskList tasks, Storage storage) throws DukeException { + Task task = null; + if (this.commandType == CommandType.TODO) { + task = new Todo(this.description); + } else if (this.commandType == CommandType.DEADLINE) { + String[] descElements = this.description.split(" /by "); + try { + LocalDateTime dateTime = LocalDateTime.parse(descElements[1], + DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm")); + String taskName = descElements[0]; + task = new Deadline(taskName, dateTime); + } catch (Exception e) { + throw new WrongDeadlineException("deadline", "/by"); + } + } else { // should be event + String[] descElements = this.description.split(" /at "); + try { + LocalDateTime dateTime = LocalDateTime.parse(descElements[1], + DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm")); + String taskName = descElements[0]; + task = new Event(taskName, dateTime); + } catch (Exception e) { + throw new WrongDeadlineException("event", "/at"); + } + } + + if (task != null) { + tasks.add(task, storage); + + System.out.println("Orh. I added:" + + "\n " + + task.toString() + + "\nNow you got " + + tasks.getListLength() + + " things in the list."); + } + } + + @Override + public boolean isExit() { + return false; + } +} diff --git a/src/main/java/Command.java b/src/main/java/Command.java new file mode 100644 index 0000000000..8ac976231d --- /dev/null +++ b/src/main/java/Command.java @@ -0,0 +1,6 @@ +public abstract class Command { + + public abstract void execute(TaskList tasks, Storage storage) throws DukeException; + + public abstract boolean isExit(); +} diff --git a/src/main/java/DeleteCommand.java b/src/main/java/DeleteCommand.java new file mode 100644 index 0000000000..70bdb54cda --- /dev/null +++ b/src/main/java/DeleteCommand.java @@ -0,0 +1,31 @@ +public class DeleteCommand extends Command { + private final String description; + + public DeleteCommand(String description) { + this.description = description; + } + + @Override + public void execute(TaskList tasks, Storage storage) throws DukeException { + try { + int taskNum = Integer.parseInt(this.description); + Task task = tasks.getTask(taskNum); + tasks.delete(taskNum, storage); + + System.out.println("Okay, I deleted this liao:" + + "\n " + + task.toString() + + "\nNow left " + + tasks.getListLength() + + " things in the list."); + } catch (Exception e) { + throw new WrongItemIndexException(CommandType.DELETE.toString().toLowerCase(), + tasks.getListLength()); + } + } + + @Override + public boolean isExit() { + return false; + } +} diff --git a/src/main/java/DoneCommand.java b/src/main/java/DoneCommand.java new file mode 100644 index 0000000000..8781872d1f --- /dev/null +++ b/src/main/java/DoneCommand.java @@ -0,0 +1,28 @@ +public class DoneCommand extends Command { + private final String description; + + public DoneCommand(String description) { + this.description = description; + } + + @Override + public void execute(TaskList tasks, Storage storage) throws DukeException { + try { + int taskNum = Integer.parseInt(this.description); + Task task = tasks.getTask(taskNum); + tasks.markDone(taskNum, storage); + + System.out.println("Can, I help you mark this as done liao:" + + "\n " + + task.toString()); + } catch (NumberFormatException e) { + throw new WrongItemIndexException(CommandType.DONE.toString().toLowerCase(), + tasks.getListLength()); + } + } + + @Override + public boolean isExit() { + return false; + } +} diff --git a/src/main/java/ExitCommand.java b/src/main/java/ExitCommand.java new file mode 100644 index 0000000000..96aca4cac8 --- /dev/null +++ b/src/main/java/ExitCommand.java @@ -0,0 +1,11 @@ +public class ExitCommand extends Command { + @Override + public void execute(TaskList tasks, Storage storage) { + System.out.println("Ok bye bye! C u again :P"); + } + + @Override + public boolean isExit() { + return true; + } +} diff --git a/src/main/java/ListCommand.java b/src/main/java/ListCommand.java new file mode 100644 index 0000000000..33c2a8f2ef --- /dev/null +++ b/src/main/java/ListCommand.java @@ -0,0 +1,12 @@ +public class ListCommand extends Command { + + @Override + public void execute(TaskList tasks, Storage storage) { + System.out.println("Na, here is your list lah:" + tasks.toString()); + } + + @Override + public boolean isExit() { + return false; + } +} diff --git a/src/main/java/Message.java b/src/main/java/Message.java deleted file mode 100644 index 4233d6e1d0..0000000000 --- a/src/main/java/Message.java +++ /dev/null @@ -1,174 +0,0 @@ -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.time.format.DateTimeParseException; -import java.util.List; - -enum Command { - START, - LIST, - DONE, - DELETE, - BYE, - TODO, - DEADLINE, - EVENT, - NOT_FOUND -} - -public class Message { - private final String message; - private Command cmd; - private final List list; - private final File taskFile; - - // public constructor - public Message(String message, List list, File taskFile) { - this.message = message; - String[] words = message.split(" "); - String cmdString = words[0]; - - for (Command c : Command.values()) { - if (c.toString().equalsIgnoreCase(cmdString)) { - this.cmd = c; - break; - } - } - - if (this.cmd == null) { // if not a recognised command - this.cmd = Command.NOT_FOUND; - } - - this.list = list; - this.taskFile = taskFile; - } - - public void reply() throws NoDescriptionException, NoCommandException, WrongItemIndexException, WrongDeadlineException { - System.out.println("____________________________________________________________"); - if (this.cmd == Command.START) { - System.out.println("Eh what's up! I'm Meimei" + - "\nWhat you want ah?"); - } else if (this.cmd == Command.LIST) { - System.out.println("Na, here is your list lah:"); - int counter = 0; - for (Task item : this.list) { - counter++; - System.out.println(counter + - "." + - item.toString()); - } - } else if (this.cmd == Command.DONE) { - try { - String[] words = message.split(" ", 2); - int index = Integer.parseInt(words[1]); - Task item = this.list.get(index - 1); - item.markDone(); - FileWriter fileWriter = new FileWriter(this.taskFile); - String fileContents = this.list.get(0).toString(); - - for (int i = 1; i < this.list.size(); i++) { - fileContents += "\n" + this.list.get(i).toString(); - } - - fileWriter.write(fileContents); - fileWriter.close(); - - System.out.println("Can, I help you mark this as done liao:" + - "\n " + - item.toString()); - } catch (Exception e) { - throw new WrongItemIndexException("done", this.list.size()); - } - } else if (this.cmd == Command.DELETE) { - try { - String[] words = message.split(" ", 2); - int index = Integer.parseInt(words[1]); - Task item = list.get(index - 1); - list.remove(index - 1); - - System.out.println("Okay, I deleted this liao:" + - "\n " + - item.toString() + - "\nNow left " + - list.size() + - " things in the list."); - - FileWriter fileWriter = new FileWriter(this.taskFile); - String fileContents = this.list.get(0).toString(); - - for (int i = 1; i < this.list.size(); i++) { - fileContents += "\n" + this.list.get(i).toString(); - } - - fileWriter.write(fileContents); - fileWriter.close(); - } catch (Exception e) { - throw new WrongItemIndexException("done", this.list.size()); - } - } else if (this.cmd == Command.BYE) { - System.out.println("Ok bye bye! C u again :P"); - } else if (this.cmd == Command.TODO || - this.cmd == Command.DEADLINE || - this.cmd == Command.EVENT) { - String[] words = message.split(" ", 2); - String taskString; - - try { - taskString = words[1]; - } catch (Exception e) { - throw new NoDescriptionException(cmd.toString().toLowerCase()); - } - - Task t = null; - if (this.cmd == Command.TODO) { - t = new Todo(taskString); - } else if (this.cmd == Command.DEADLINE) { - String[] temp = taskString.split(" /by "); - try { - LocalDateTime dateTime = LocalDateTime.parse(temp[1], - DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm")); - t = new Deadline(temp[0], dateTime); - } catch (Exception e) { - throw new WrongDeadlineException("deadline", "/by"); - } - } else { // should be event - String[] temp = taskString.split(" /at "); - try { - LocalDateTime dateTime = LocalDateTime.parse(temp[1], - DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm")); - t = new Event(temp[0], dateTime); - } catch (Exception e) { - throw new WrongDeadlineException("event", "/at"); - } - } - - if (t != null) { - this.list.add(t); - try { - FileWriter fileWriter = new FileWriter(this.taskFile, true); - fileWriter.write(t.toString()); - fileWriter.close(); - } catch (IOException e) { - e.printStackTrace(); - } - - System.out.println("Orh. I added:" + - "\n " + - t.toString() + - "\nNow you got " + - list.size() + - " things in the list."); - } - - } else if (this.cmd == Command.NOT_FOUND) { - throw new NoCommandException(); - } - System.out.println("____________________________________________________________"); - } - - public Command getCmd() { - return this.cmd; - } -} diff --git a/src/main/java/Parser.java b/src/main/java/Parser.java new file mode 100644 index 0000000000..0e0d657668 --- /dev/null +++ b/src/main/java/Parser.java @@ -0,0 +1,50 @@ +enum CommandType { + LIST, + DONE, + DELETE, + BYE, + TODO, + DEADLINE, + EVENT +} + +public class Parser { + + public static Command parse(String fullCommand) throws DukeException { + String[] commandElements = fullCommand.split(" ", 2); + String commandString = commandElements[0]; + CommandType commandType = null; + + for (CommandType type : CommandType.values()) { + if (type.toString().equalsIgnoreCase(commandString)) { + commandType = type; + break; + } + } + + if (commandType == CommandType.LIST) { + return new ListCommand(); + } else if (commandType == CommandType.BYE) { + return new ExitCommand(); + } else if (commandType == CommandType.DELETE || + commandType == CommandType.DONE || + commandType == CommandType.TODO || + commandType == CommandType.DEADLINE || + commandType == CommandType.EVENT) { // Commands that have a description + try { + String description = commandElements[1]; + if (commandType == CommandType.DELETE) { + return new DeleteCommand(description); + } else if (commandType == CommandType.DONE) { + return new DoneCommand(description); + } else { + return new AddCommand(commandType, description); + } + } catch (Exception e) { + throw new NoDescriptionException(commandType.toString().toLowerCase()); + } + } else { + throw new NoCommandException(); + } + } +} From 3ebac0e811ed2293d865dbeca2f348392ce59072 Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Fri, 28 Aug 2020 18:25:34 +0800 Subject: [PATCH 46/95] Remove bottom line from error message --- src/main/java/DukeException.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/DukeException.java b/src/main/java/DukeException.java index 9a08fe7419..2d10a49fcb 100644 --- a/src/main/java/DukeException.java +++ b/src/main/java/DukeException.java @@ -6,7 +6,6 @@ public DukeException(String message) { @Override public String getMessage() { - return "Aiyo! " + super.getMessage() + - "\n____________________________________________________________"; + return "Aiyo! " + super.getMessage(); } } From 7377d8f6360e6d0ad0fc78c0ba0481f9676ff4ff Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Fri, 28 Aug 2020 18:26:02 +0800 Subject: [PATCH 47/95] Update Duke.java with more OOP structure using the new classes --- src/main/java/Duke.java | 62 +++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 36 deletions(-) diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 9523500537..ceb074b593 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -6,50 +6,40 @@ import java.util.Scanner; public class Duke { - public static void main(String[] args) { - Scanner sc = new Scanner(System.in); - List list = new ArrayList<>(); - - // Load the data from the hard disk when Duke starts up. - File taskFile = new File("data/duke.txt"); - if (!taskFile.exists()) { - File dataFolder = new File("data"); - if (!dataFolder.exists()) { - dataFolder.mkdirs(); - } - try { - taskFile.createNewFile(); - } catch (IOException e) { - e.printStackTrace(); - } - } - - try { - FileToListReader fileReader = new FileToListReader(taskFile); - list = fileReader.getList(); - } catch (FileNotFoundException e) { - e.printStackTrace(); - } catch (WrongDeadlineException e) { - e.printStackTrace(); - } + private Storage storage; + private TaskList tasks; + private Ui ui; - Message start = new Message("start", list, taskFile); + public Duke(String pathFile) { + this.ui = new Ui(); + this.storage = new Storage(pathFile); try { - start.reply(); + this.tasks = new TaskList(storage.load()); } catch (DukeException e) { - System.out.println(e.getMessage()); + // todo + this.tasks = new TaskList(); } + } - while (sc.hasNext()) { - Message msg = new Message(sc.nextLine(), list, taskFile); + public void run() { + ui.showWelcomeMsg(); + boolean isExit = false; + while (!isExit) { try { - msg.reply(); + String fullCommand = this.ui.readCommand(); + this.ui.showLine(); + Command command = Parser.parse(fullCommand); + command.execute(tasks, storage); + isExit = command.isExit(); } catch (DukeException e) { - System.out.println(e.getMessage()); - } - if (msg.getCmd() == Command.BYE) { - break; + ui.showError(e.getMessage()); + } finally { + this.ui.showLine(); } } } + + public static void main(String[] args) { + new Duke("data/tasks.txt").run(); + } } From 674b42c85f895b9a666ee741cf458c13390b687f Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Tue, 1 Sep 2020 02:29:21 +0800 Subject: [PATCH 48/95] Divide classes into packages and refactored accordingly --- README.md | 4 ++-- src/main/java/{ => duke}/Duke.java | 11 +++++------ src/main/java/{ => duke}/Storage.java | 8 ++++++++ src/main/java/{ => duke}/TaskList.java | 5 +++++ src/main/java/{ => duke}/Ui.java | 2 ++ src/main/java/{ => duke/command}/AddCommand.java | 11 +++++++++++ src/main/java/{ => duke/command}/Command.java | 6 ++++++ src/main/java/{ => duke/command}/DeleteCommand.java | 8 ++++++++ src/main/java/{ => duke/command}/DoneCommand.java | 8 ++++++++ src/main/java/{ => duke/command}/ExitCommand.java | 5 +++++ src/main/java/{ => duke/command}/ListCommand.java | 5 +++++ src/main/java/{ => duke/command}/Parser.java | 6 ++++++ .../java/{ => duke/dukeexception}/DukeException.java | 2 ++ .../{ => duke/dukeexception}/NoCommandException.java | 2 ++ .../dukeexception}/NoDescriptionException.java | 2 ++ .../dukeexception}/WrongDeadlineException.java | 2 ++ .../dukeexception}/WrongItemIndexException.java | 2 ++ src/main/java/{ => duke/task}/Deadline.java | 2 +- src/main/java/{ => duke/task}/Event.java | 2 ++ src/main/java/{ => duke/task}/Task.java | 2 ++ src/main/java/{ => duke/task}/Todo.java | 2 ++ text-ui-test/runtest.bat | 4 ++-- 22 files changed, 90 insertions(+), 11 deletions(-) rename src/main/java/{ => duke}/Duke.java (86%) rename src/main/java/{ => duke}/Storage.java (96%) rename src/main/java/{ => duke}/TaskList.java (93%) rename src/main/java/{ => duke}/Ui.java (97%) rename src/main/java/{ => duke/command}/AddCommand.java (88%) rename src/main/java/{ => duke/command}/Command.java (60%) rename src/main/java/{ => duke/command}/DeleteCommand.java (83%) rename src/main/java/{ => duke/command}/DoneCommand.java (82%) rename src/main/java/{ => duke/command}/ExitCommand.java (78%) rename src/main/java/{ => duke/command}/ListCommand.java (80%) rename src/main/java/{ => duke/command}/Parser.java (91%) rename src/main/java/{ => duke/dukeexception}/DukeException.java (88%) rename src/main/java/{ => duke/dukeexception}/NoCommandException.java (89%) rename src/main/java/{ => duke/dukeexception}/NoDescriptionException.java (90%) rename src/main/java/{ => duke/dukeexception}/WrongDeadlineException.java (93%) rename src/main/java/{ => duke/dukeexception}/WrongItemIndexException.java (93%) rename src/main/java/{ => duke/task}/Deadline.java (93%) rename src/main/java/{ => duke/task}/Event.java (96%) rename src/main/java/{ => duke/task}/Task.java (97%) rename src/main/java/{ => duke/task}/Todo.java (90%) diff --git a/README.md b/README.md index 9d95025bce..b9e11953ef 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Duke project template +# duke.Duke project template This is a project template for a greenfield Java project. It's named after the Java mascot _Duke_. Given below are instructions on how to use it. @@ -15,7 +15,7 @@ Prerequisites: JDK 11, update Intellij to the most recent version. 1. Click `Open or Import`. 1. Select the project directory, and click `OK` 1. If there are any further prompts, accept the defaults. -1. After the importing is complete, locate the `src/main/java/Duke.java` file, right-click it, and choose `Run Duke.main()`. If the setup is correct, you should see something like the below: +1. After the importing is complete, locate the `src/main/java/duke.Duke.java` file, right-click it, and choose `Run duke.Duke.main()`. If the setup is correct, you should see something like the below: ``` Hello from ____ _ diff --git a/src/main/java/Duke.java b/src/main/java/duke/Duke.java similarity index 86% rename from src/main/java/Duke.java rename to src/main/java/duke/Duke.java index ceb074b593..226557a8aa 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/duke/Duke.java @@ -1,9 +1,8 @@ -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Scanner; +package duke; + +import duke.command.Command; +import duke.command.Parser; +import duke.dukeexception.DukeException; public class Duke { private Storage storage; diff --git a/src/main/java/Storage.java b/src/main/java/duke/Storage.java similarity index 96% rename from src/main/java/Storage.java rename to src/main/java/duke/Storage.java index baed10f065..ea4698dbca 100644 --- a/src/main/java/Storage.java +++ b/src/main/java/duke/Storage.java @@ -1,3 +1,11 @@ +package duke; + +import duke.dukeexception.DukeException; +import duke.task.Deadline; +import duke.task.Event; +import duke.task.Task; +import duke.task.Todo; + import java.io.File; import java.io.FileNotFoundException; import java.io.FileWriter; diff --git a/src/main/java/TaskList.java b/src/main/java/duke/TaskList.java similarity index 93% rename from src/main/java/TaskList.java rename to src/main/java/duke/TaskList.java index fe5c6711ce..024c656b93 100644 --- a/src/main/java/TaskList.java +++ b/src/main/java/duke/TaskList.java @@ -1,3 +1,8 @@ +package duke; + +import duke.dukeexception.DukeException; +import duke.task.Task; + import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/Ui.java b/src/main/java/duke/Ui.java similarity index 97% rename from src/main/java/Ui.java rename to src/main/java/duke/Ui.java index c524a2dddf..d13499bd8b 100644 --- a/src/main/java/Ui.java +++ b/src/main/java/duke/Ui.java @@ -1,3 +1,5 @@ +package duke; + import java.util.Scanner; public class Ui { diff --git a/src/main/java/AddCommand.java b/src/main/java/duke/command/AddCommand.java similarity index 88% rename from src/main/java/AddCommand.java rename to src/main/java/duke/command/AddCommand.java index 51c14e0ea4..282cf722b0 100644 --- a/src/main/java/AddCommand.java +++ b/src/main/java/duke/command/AddCommand.java @@ -1,3 +1,14 @@ +package duke.command; + +import duke.dukeexception.DukeException; +import duke.Storage; +import duke.TaskList; +import duke.dukeexception.WrongDeadlineException; +import duke.task.Deadline; +import duke.task.Event; +import duke.task.Task; +import duke.task.Todo; + import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; diff --git a/src/main/java/Command.java b/src/main/java/duke/command/Command.java similarity index 60% rename from src/main/java/Command.java rename to src/main/java/duke/command/Command.java index 8ac976231d..56f81e6070 100644 --- a/src/main/java/Command.java +++ b/src/main/java/duke/command/Command.java @@ -1,3 +1,9 @@ +package duke.command; + +import duke.dukeexception.DukeException; +import duke.Storage; +import duke.TaskList; + public abstract class Command { public abstract void execute(TaskList tasks, Storage storage) throws DukeException; diff --git a/src/main/java/DeleteCommand.java b/src/main/java/duke/command/DeleteCommand.java similarity index 83% rename from src/main/java/DeleteCommand.java rename to src/main/java/duke/command/DeleteCommand.java index 70bdb54cda..7a1113d342 100644 --- a/src/main/java/DeleteCommand.java +++ b/src/main/java/duke/command/DeleteCommand.java @@ -1,3 +1,11 @@ +package duke.command; + +import duke.dukeexception.DukeException; +import duke.Storage; +import duke.TaskList; +import duke.dukeexception.WrongItemIndexException; +import duke.task.Task; + public class DeleteCommand extends Command { private final String description; diff --git a/src/main/java/DoneCommand.java b/src/main/java/duke/command/DoneCommand.java similarity index 82% rename from src/main/java/DoneCommand.java rename to src/main/java/duke/command/DoneCommand.java index 8781872d1f..5673574589 100644 --- a/src/main/java/DoneCommand.java +++ b/src/main/java/duke/command/DoneCommand.java @@ -1,3 +1,11 @@ +package duke.command; + +import duke.dukeexception.DukeException; +import duke.Storage; +import duke.TaskList; +import duke.dukeexception.WrongItemIndexException; +import duke.task.Task; + public class DoneCommand extends Command { private final String description; diff --git a/src/main/java/ExitCommand.java b/src/main/java/duke/command/ExitCommand.java similarity index 78% rename from src/main/java/ExitCommand.java rename to src/main/java/duke/command/ExitCommand.java index 96aca4cac8..72feacd99b 100644 --- a/src/main/java/ExitCommand.java +++ b/src/main/java/duke/command/ExitCommand.java @@ -1,3 +1,8 @@ +package duke.command; + +import duke.Storage; +import duke.TaskList; + public class ExitCommand extends Command { @Override public void execute(TaskList tasks, Storage storage) { diff --git a/src/main/java/ListCommand.java b/src/main/java/duke/command/ListCommand.java similarity index 80% rename from src/main/java/ListCommand.java rename to src/main/java/duke/command/ListCommand.java index 33c2a8f2ef..c6bfc86649 100644 --- a/src/main/java/ListCommand.java +++ b/src/main/java/duke/command/ListCommand.java @@ -1,3 +1,8 @@ +package duke.command; + +import duke.Storage; +import duke.TaskList; + public class ListCommand extends Command { @Override diff --git a/src/main/java/Parser.java b/src/main/java/duke/command/Parser.java similarity index 91% rename from src/main/java/Parser.java rename to src/main/java/duke/command/Parser.java index 0e0d657668..f711235875 100644 --- a/src/main/java/Parser.java +++ b/src/main/java/duke/command/Parser.java @@ -1,3 +1,9 @@ +package duke.command; + +import duke.dukeexception.DukeException; +import duke.dukeexception.NoCommandException; +import duke.dukeexception.NoDescriptionException; + enum CommandType { LIST, DONE, diff --git a/src/main/java/DukeException.java b/src/main/java/duke/dukeexception/DukeException.java similarity index 88% rename from src/main/java/DukeException.java rename to src/main/java/duke/dukeexception/DukeException.java index 2d10a49fcb..84a267b8c5 100644 --- a/src/main/java/DukeException.java +++ b/src/main/java/duke/dukeexception/DukeException.java @@ -1,3 +1,5 @@ +package duke.dukeexception; + public class DukeException extends Exception { public DukeException(String message) { diff --git a/src/main/java/NoCommandException.java b/src/main/java/duke/dukeexception/NoCommandException.java similarity index 89% rename from src/main/java/NoCommandException.java rename to src/main/java/duke/dukeexception/NoCommandException.java index 7e96182fec..27c927035a 100644 --- a/src/main/java/NoCommandException.java +++ b/src/main/java/duke/dukeexception/NoCommandException.java @@ -1,3 +1,5 @@ +package duke.dukeexception; + public class NoCommandException extends DukeException { public NoCommandException() { diff --git a/src/main/java/NoDescriptionException.java b/src/main/java/duke/dukeexception/NoDescriptionException.java similarity index 90% rename from src/main/java/NoDescriptionException.java rename to src/main/java/duke/dukeexception/NoDescriptionException.java index 0ccf9517b9..6d49137e42 100644 --- a/src/main/java/NoDescriptionException.java +++ b/src/main/java/duke/dukeexception/NoDescriptionException.java @@ -1,3 +1,5 @@ +package duke.dukeexception; + public class NoDescriptionException extends DukeException { public NoDescriptionException(String cmd) { diff --git a/src/main/java/WrongDeadlineException.java b/src/main/java/duke/dukeexception/WrongDeadlineException.java similarity index 93% rename from src/main/java/WrongDeadlineException.java rename to src/main/java/duke/dukeexception/WrongDeadlineException.java index 67bb60450a..b829e3c8f2 100644 --- a/src/main/java/WrongDeadlineException.java +++ b/src/main/java/duke/dukeexception/WrongDeadlineException.java @@ -1,3 +1,5 @@ +package duke.dukeexception; + // for deadline or exception public class WrongDeadlineException extends DukeException { public WrongDeadlineException(String cmd, String separator) { diff --git a/src/main/java/WrongItemIndexException.java b/src/main/java/duke/dukeexception/WrongItemIndexException.java similarity index 93% rename from src/main/java/WrongItemIndexException.java rename to src/main/java/duke/dukeexception/WrongItemIndexException.java index 930c8c909d..caa9c04e20 100644 --- a/src/main/java/WrongItemIndexException.java +++ b/src/main/java/duke/dukeexception/WrongItemIndexException.java @@ -1,3 +1,5 @@ +package duke.dukeexception; + // for delete and done commands public class WrongItemIndexException extends DukeException { diff --git a/src/main/java/Deadline.java b/src/main/java/duke/task/Deadline.java similarity index 93% rename from src/main/java/Deadline.java rename to src/main/java/duke/task/Deadline.java index b50ece16e7..ff5e67f974 100644 --- a/src/main/java/Deadline.java +++ b/src/main/java/duke/task/Deadline.java @@ -1,4 +1,4 @@ -//@@author {FooJingYi}-reused +package duke.task;//@@author {FooJingYi}-reused //Reused from https://nus-cs2103-ay2021s1.github.io/website/schedule/week2/project.html with minor modifications import java.time.LocalDateTime; diff --git a/src/main/java/Event.java b/src/main/java/duke/task/Event.java similarity index 96% rename from src/main/java/Event.java rename to src/main/java/duke/task/Event.java index be4716f00a..3e963ecad0 100644 --- a/src/main/java/Event.java +++ b/src/main/java/duke/task/Event.java @@ -1,3 +1,5 @@ +package duke.task; + import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; diff --git a/src/main/java/Task.java b/src/main/java/duke/task/Task.java similarity index 97% rename from src/main/java/Task.java rename to src/main/java/duke/task/Task.java index b735bd140d..57fdb6a3fd 100644 --- a/src/main/java/Task.java +++ b/src/main/java/duke/task/Task.java @@ -1,3 +1,5 @@ +package duke.task; + //Adapted from https://nus-cs2103-ay2021s1.github.io/website/schedule/week2/project.html public class Task { protected final String taskName; diff --git a/src/main/java/Todo.java b/src/main/java/duke/task/Todo.java similarity index 90% rename from src/main/java/Todo.java rename to src/main/java/duke/task/Todo.java index a4c4c9ab2c..28b9ec86da 100644 --- a/src/main/java/Todo.java +++ b/src/main/java/duke/task/Todo.java @@ -1,3 +1,5 @@ +package duke.task; + public class Todo extends Task { public Todo(String taskName) { diff --git a/text-ui-test/runtest.bat b/text-ui-test/runtest.bat index d0facc6310..00ed6a04cd 100644 --- a/text-ui-test/runtest.bat +++ b/text-ui-test/runtest.bat @@ -7,7 +7,7 @@ REM delete output from previous run del ACTUAL.TXT REM compile the code into the bin folder -javac -cp ..\src -Xlint:none -d ..\bin ..\src\main\java\Duke.java +javac -cp ..\src -Xlint:none -d ..\bin ..\src\main\java\duke.Duke.java IF ERRORLEVEL 1 ( echo ********** BUILD FAILURE ********** exit /b 1 @@ -15,7 +15,7 @@ IF ERRORLEVEL 1 ( REM no error here, errorlevel == 0 REM run the program, feed commands from input.txt file and redirect the output to the ACTUAL.TXT -java -classpath ..\bin Duke < input.txt > ACTUAL.TXT +java -classpath ..\bin duke.Duke < input.txt > ACTUAL.TXT REM compare the output to the expected output FC ACTUAL.TXT EXPECTED.TXT From 5cd15c7cc80867c0b219aec0314c6afded15dc0c Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Tue, 1 Sep 2020 13:09:58 +0800 Subject: [PATCH 49/95] Added JUnit tests including dummyTest() for Duke and tests for Task methods --- src/test/java/duke/DukeTest.java | 13 ++++++++ src/test/java/duke/task/DeadlineTest.java | 20 ++++++++++++ src/test/java/duke/task/EventTest.java | 20 ++++++++++++ src/test/java/duke/task/TaskTest.java | 40 +++++++++++++++++++++++ src/test/java/duke/task/TodoTest.java | 13 ++++++++ 5 files changed, 106 insertions(+) create mode 100644 src/test/java/duke/DukeTest.java create mode 100644 src/test/java/duke/task/DeadlineTest.java create mode 100644 src/test/java/duke/task/EventTest.java create mode 100644 src/test/java/duke/task/TaskTest.java create mode 100644 src/test/java/duke/task/TodoTest.java diff --git a/src/test/java/duke/DukeTest.java b/src/test/java/duke/DukeTest.java new file mode 100644 index 0000000000..9d60fd1c9a --- /dev/null +++ b/src/test/java/duke/DukeTest.java @@ -0,0 +1,13 @@ +package duke; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class DukeTest { + + @Test + public void dummyTest(){ + assertEquals(2, 2); + } +} diff --git a/src/test/java/duke/task/DeadlineTest.java b/src/test/java/duke/task/DeadlineTest.java new file mode 100644 index 0000000000..083507b0b8 --- /dev/null +++ b/src/test/java/duke/task/DeadlineTest.java @@ -0,0 +1,20 @@ +package duke.task; + +import org.junit.jupiter.api.Test; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class DeadlineTest { + + @Test + public void testStringConversion() { + assertEquals("[D][\u2718] Assignment by: 6 Nov 2020, 11.59 pm", + new Deadline("Assignment", + LocalDateTime.parse("06/11/2020 23:59", + DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm"))) + .toString()); + } +} diff --git a/src/test/java/duke/task/EventTest.java b/src/test/java/duke/task/EventTest.java new file mode 100644 index 0000000000..14ab0e2d51 --- /dev/null +++ b/src/test/java/duke/task/EventTest.java @@ -0,0 +1,20 @@ +package duke.task; + +import org.junit.jupiter.api.Test; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class EventTest { + + @Test + public void testStringConversion() { + assertEquals("[E][\u2718] Dance Class at: 3 Sep 2020, 8.30 pm", + new Event("Dance Class", + LocalDateTime.parse("03/09/2020 20:30", + DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm"))) + .toString()); + } +} diff --git a/src/test/java/duke/task/TaskTest.java b/src/test/java/duke/task/TaskTest.java new file mode 100644 index 0000000000..3081e96157 --- /dev/null +++ b/src/test/java/duke/task/TaskTest.java @@ -0,0 +1,40 @@ +package duke.task; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class TaskTest { + + @Test + public void testGetTaskName() { + assertEquals("Laundry", new Task("Laundry").getTaskName()); + } + + @Test + public void isDone_isDoneAssignedTrue_true() { + Task testTask = new Task("Housework"); + testTask.isDone = true; + assertTrue(testTask.isDone()); + } + + @Test + public void isDone_defaultTask_false() { + Task testTask = new Task("Housework"); + assertFalse(testTask.isDone()); + } + + @Test + public void markDone_taskMarkedDone_isDoneVariableIsTrue() { + Task testTask = new Task("Housework"); + testTask.markDone(); + assertTrue(testTask.isDone); + } + + @Test + public void testStringConversionForDoneTask() { + Task testTask = new Task("Dishes"); + testTask.markDone(); + assertEquals("[\u2713] Dishes", testTask.toString()); + } +} diff --git a/src/test/java/duke/task/TodoTest.java b/src/test/java/duke/task/TodoTest.java new file mode 100644 index 0000000000..b900382a75 --- /dev/null +++ b/src/test/java/duke/task/TodoTest.java @@ -0,0 +1,13 @@ +package duke.task; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class TodoTest { + + @Test + public void testStringConversion() { + assertEquals("[T][\u2718] Homework", new Todo("Homework").toString()); + } +} From f641c4d539ffaa90a0ed6816fa204269f7fc93ed Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Tue, 1 Sep 2020 14:47:07 +0800 Subject: [PATCH 50/95] Ignore Manifest file created when jar is built --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 6b779900da..c2993bd8e3 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ bin/ text-ui-test/EXPECTED-UNIX.TXT duke.txt tasks.txt +MANIFEST.MF From 517e09c2de820988aaed30eddb1b75cadbb1c9f2 Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Tue, 1 Sep 2020 17:00:33 +0800 Subject: [PATCH 51/95] Add JavaDoc comments to all public classes and non-overridden methods --- src/main/java/duke/Duke.java | 21 +++++++ src/main/java/duke/Storage.java | 57 ++++++++++++++++++- src/main/java/duke/TaskList.java | 52 +++++++++++++++++ src/main/java/duke/Ui.java | 26 +++++++++ src/main/java/duke/command/AddCommand.java | 17 +++++- src/main/java/duke/command/Command.java | 15 +++++ src/main/java/duke/command/DeleteCommand.java | 13 ++++- src/main/java/duke/command/DoneCommand.java | 13 ++++- src/main/java/duke/command/ExitCommand.java | 3 + src/main/java/duke/command/ListCommand.java | 3 + src/main/java/duke/command/Parser.java | 17 ++++++ .../duke/dukeexception/DukeException.java | 8 +++ .../dukeexception/NoCommandException.java | 3 + .../dukeexception/NoDescriptionException.java | 5 ++ .../dukeexception/WrongDeadlineException.java | 6 +- .../WrongItemIndexException.java | 7 ++- src/main/java/duke/task/Deadline.java | 20 ++++++- src/main/java/duke/task/Event.java | 15 +++++ src/main/java/duke/task/Task.java | 27 ++++++++- 19 files changed, 317 insertions(+), 11 deletions(-) diff --git a/src/main/java/duke/Duke.java b/src/main/java/duke/Duke.java index 226557a8aa..2875c0787a 100644 --- a/src/main/java/duke/Duke.java +++ b/src/main/java/duke/Duke.java @@ -4,11 +4,29 @@ import duke.command.Parser; import duke.dukeexception.DukeException; +/** + * The Duke program is an interactive bot that offers commands to help the + * user keep track of a mutable list of tasks that can be of 3 types: + * Todo, Deadline or Event. + * These tasks can also be marked as done and will be saved in the hard disk. + * + * @author Foo Jing Yi + */ public class Duke { + /** Storage object used by Duke to load from and write to hard disk */ private Storage storage; + /** TaskList object that contains the list of tasks */ private TaskList tasks; + /** Ui object that deals with interactions with the user */ private Ui ui; + /** + * Public class constructor that takes in the location of a file as a string + * indicating the relative file path. + * The list of tasks will be loaded from and saved to this file. + * + * @param pathFile Relative file path. + */ public Duke(String pathFile) { this.ui = new Ui(); this.storage = new Storage(pathFile); @@ -20,6 +38,9 @@ public Duke(String pathFile) { } } + /** + * Runs the bot. + */ public void run() { ui.showWelcomeMsg(); boolean isExit = false; diff --git a/src/main/java/duke/Storage.java b/src/main/java/duke/Storage.java index ea4698dbca..d7279a77c0 100644 --- a/src/main/java/duke/Storage.java +++ b/src/main/java/duke/Storage.java @@ -10,19 +10,39 @@ import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.IOException; + import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; + import java.util.ArrayList; import java.util.List; import java.util.Scanner; +/** + * Represents the hard disk storage used by the bot. + * While this class does not store the data within the class objects, + * each Storage object keeps a link to an actual file where + * data is stored, and handles loading and writing to this file. + */ public class Storage { + /** File where data is stored */ private final File taskFile; + /** + * Public constructor. + * + * @param filePath Relative file path. + */ public Storage(String filePath) { this.taskFile = new File(filePath); } + /** + * Loads list of tasks from taskFile. + * + * @return List of tasks to be passed to a TaskList object + * @throws DukeException If file cannot be created, read or parsed. + */ public List load() throws DukeException { if (!this.taskFile.exists()) { File dir = this.taskFile.getParentFile(); @@ -57,6 +77,12 @@ public List load() throws DukeException { } } + /** + * Updates file by added a new task. + * + * @param task Task to be added to file. + * @throws DukeException If task cannot be parsed or file cannot be written to. + */ public void update(Task task) throws DukeException { try { FileWriter fileWriter = new FileWriter(this.taskFile, true); @@ -67,6 +93,12 @@ public void update(Task task) throws DukeException { } } + /** + * Updates file with list when tasks are marked or deleted. + * + * @param list Updated list given by TaskList object. + * @throws DukeException If tasks cannot be parsed or file cannot be written to. + */ public void update(List list) throws DukeException { try { FileWriter fileWriter = new FileWriter(this.taskFile); @@ -83,9 +115,20 @@ public void update(List list) throws DukeException { } } - public String parseToStorage(Task task) throws DukeException { + /** + * Parses Task objects into strings to be written to the + * file on the hard disk. + * + * @param task Task object to be parsed + * @return String representation of the task. Note that this string is + * different from the string returned by task.toString(). + * e.g. "[T][✓] Homework" from task.toString() will be + * represented as "T | 1 | Homework". + * @throws DukeException If the type of the task cannot be recognised. + */ + protected String parseToStorage(Task task) throws DukeException { String taskType = ""; - String status = task.isDone() ? "1" : "0"; + String status = task.isDone() ? "1" : "0"; // 1 for done and 0 for not done String taskDescription = ""; if (task instanceof Todo) { @@ -114,7 +157,15 @@ public String parseToStorage(Task task) throws DukeException { taskDescription; } - public Task parseFromStorage(String storedTask) throws DukeException { + /** + * Parses string representation of task stored in the file on the hard disk + * to create a Task object. + * + * @param storedTask String representation of the task + * @return Task represented by the string input. + * @throws DukeException If the string is in the wrong format. + */ + protected Task parseFromStorage(String storedTask) throws DukeException { String[] taskElements = storedTask.split(" \\| ", 4); try { diff --git a/src/main/java/duke/TaskList.java b/src/main/java/duke/TaskList.java index 024c656b93..fc26be4fb4 100644 --- a/src/main/java/duke/TaskList.java +++ b/src/main/java/duke/TaskList.java @@ -6,36 +6,88 @@ import java.util.ArrayList; import java.util.List; +/** + * Contains the list of tasks based on user inputs + * and handles operations on this list. + */ public class TaskList { + /** List object containing tasks represented by Task objects */ private final List list; + /** + * Public constructor that creates a new (empty) list + */ public TaskList() { this.list = new ArrayList<>(); } + /** + * Public constructor taking in an existing list loaded from + * the hard disk. + * + * @param list List loaded from the hard disk. + */ public TaskList(List list) { this.list = list; } + /** + * Adds a task to the list and updates the hard disk with this new task. + * + * @param task Task to be added. + * @param storage Storage object that updates the relevant file on the hard disk. + * @throws DukeException If exception is thrown by the update method of the Storage class. + */ public void add(Task task, Storage storage) throws DukeException { this.list.add(task); storage.update(task); } + /** + * Deletes the task represented by a taskNum from the numbered list + * displayed to the user. The task is the "taskNum"th task in the list when numbering + * the tasks chronologically starting from 1 (not zero). + * + * @param taskNum Number that corresponds to the task to be deleted. + * @param storage Storage object that updates the relevant file on the hard disk. + * @throws DukeException If exception is thrown by the update method of the Storage class. + */ public void delete(int taskNum, Storage storage) throws DukeException { this.list.remove(taskNum - 1); storage.update(this.list); } + /** + * Marks the task represented by a taskNum as done. + * The task is the "taskNum"th task in the list when numbering + * the tasks chronologically starting from 1 (not zero). + * + * @param taskNum Number that corresponds to the task to be marked. + * @param storage Storage object that updates the relevant file on the hard disk. + * @throws DukeException If exception is thrown by the update method of the Storage class. + */ public void markDone(int taskNum, Storage storage) throws DukeException { this.list.get(taskNum - 1).markDone(); storage.update(this.list); } + /** + * Returns the task represented by a taskNum. + * The task is the "taskNum"th task in the list when numbering + * the tasks chronologically starting from 1 (not zero). + * + * @param taskNum Number that corresponds to the task to be returned. + * @return Task represented by the taskNum. + */ public Task getTask(int taskNum) { return this.list.get(taskNum - 1); } + /** + * Returns the number of tasks in the list. + * + * @return Current size of list. + */ public int getListLength() { return this.list.size(); } diff --git a/src/main/java/duke/Ui.java b/src/main/java/duke/Ui.java index d13499bd8b..841ed6e07c 100644 --- a/src/main/java/duke/Ui.java +++ b/src/main/java/duke/Ui.java @@ -2,9 +2,21 @@ import java.util.Scanner; +/** + * Represents the user interface that deals with interactions with the user. + */ public class Ui { + /** Scanner to take in user input */ private final Scanner sc = new Scanner(System.in); + /** + * Public constructor. + */ + public Ui() { } + + /** + * Displays the welcome message to the user. + */ public void showWelcomeMsg() { System.out.println("____________________________________________________________"); System.out.println("Eh what's up! I'm Meimei" + @@ -12,14 +24,28 @@ public void showWelcomeMsg() { System.out.println("____________________________________________________________"); } + /** + * Reads user inputs which should contain commands to the bot. + * + * @return the line inputted by the user + */ public String readCommand() { return sc.nextLine(); } + /** + * Prints lines to frame bot responses. + */ public void showLine() { System.out.println("____________________________________________________________"); } + /** + * Prints error messages from DukeExceptions thrown during + * the bot execution. + * + * @param message message from the getMessage() method of a DukeException + */ public void showError(String message) { System.out.println(message); } diff --git a/src/main/java/duke/command/AddCommand.java b/src/main/java/duke/command/AddCommand.java index 282cf722b0..13cd238df8 100644 --- a/src/main/java/duke/command/AddCommand.java +++ b/src/main/java/duke/command/AddCommand.java @@ -1,9 +1,11 @@ package duke.command; import duke.dukeexception.DukeException; +import duke.dukeexception.WrongDeadlineException; + import duke.Storage; import duke.TaskList; -import duke.dukeexception.WrongDeadlineException; + import duke.task.Deadline; import duke.task.Event; import duke.task.Task; @@ -12,10 +14,23 @@ import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; +/** + * Command that adds a task to the user's list when executed. + */ public class AddCommand extends Command { + /** Represents the type of the command used by the user */ private CommandType commandType; + /** Additional information needed for executing the command */ private final String description; + /** + * Public constructor. + * + * @param commandType Either TODO, Deadline, + * or Event. + * @param description Name of the task (and date and time if + * Deadline or Event. + */ public AddCommand(CommandType commandType, String description) { this.commandType = commandType; this.description = description; diff --git a/src/main/java/duke/command/Command.java b/src/main/java/duke/command/Command.java index 56f81e6070..2bfd25a2e0 100644 --- a/src/main/java/duke/command/Command.java +++ b/src/main/java/duke/command/Command.java @@ -4,9 +4,24 @@ import duke.Storage; import duke.TaskList; +/** + * Represents a (bot) command that can be executed by the bot. + */ public abstract class Command { + /** + * Executes the command as derived from the user's input. + * + * @param tasks The user's tasks. + * @param storage Handles updating the hard disk accordingly. + * @throws DukeException If a DukeException is thrown in executing the command. + */ public abstract void execute(TaskList tasks, Storage storage) throws DukeException; + /** + * Returns true when the command is an exit command and false otherwise. + * + * @return A boolean indicating if the command is an exit command. + */ public abstract boolean isExit(); } diff --git a/src/main/java/duke/command/DeleteCommand.java b/src/main/java/duke/command/DeleteCommand.java index 7a1113d342..19fd29b9ea 100644 --- a/src/main/java/duke/command/DeleteCommand.java +++ b/src/main/java/duke/command/DeleteCommand.java @@ -1,14 +1,25 @@ package duke.command; import duke.dukeexception.DukeException; +import duke.dukeexception.WrongItemIndexException; + import duke.Storage; import duke.TaskList; -import duke.dukeexception.WrongItemIndexException; + import duke.task.Task; +/** + * Command that deletes a task from the user's list when executed. + */ public class DeleteCommand extends Command { + /** A string representation of the number corresponding to the task to be deleted */ private final String description; + /** + * Public constructor. + * @param description String representation of the number + * corresponding to the task to be deleted. + */ public DeleteCommand(String description) { this.description = description; } diff --git a/src/main/java/duke/command/DoneCommand.java b/src/main/java/duke/command/DoneCommand.java index 5673574589..fb011bc21b 100644 --- a/src/main/java/duke/command/DoneCommand.java +++ b/src/main/java/duke/command/DoneCommand.java @@ -1,14 +1,25 @@ package duke.command; import duke.dukeexception.DukeException; +import duke.dukeexception.WrongItemIndexException; + import duke.Storage; import duke.TaskList; -import duke.dukeexception.WrongItemIndexException; + import duke.task.Task; +/** + * Command that marks a task from the user's list as completed("done") when executed. + */ public class DoneCommand extends Command { + /** A string representation of the number corresponding to the task to be marked */ private final String description; + /** + * Public constructor. + * @param description String representation of the number + * corresponding to the task to be marked. + */ public DoneCommand(String description) { this.description = description; } diff --git a/src/main/java/duke/command/ExitCommand.java b/src/main/java/duke/command/ExitCommand.java index 72feacd99b..a95c2310d6 100644 --- a/src/main/java/duke/command/ExitCommand.java +++ b/src/main/java/duke/command/ExitCommand.java @@ -3,6 +3,9 @@ import duke.Storage; import duke.TaskList; +/** + * Command that displays a goodbye message and exits(quits) the bot when executed. + */ public class ExitCommand extends Command { @Override public void execute(TaskList tasks, Storage storage) { diff --git a/src/main/java/duke/command/ListCommand.java b/src/main/java/duke/command/ListCommand.java index c6bfc86649..09a145bdfa 100644 --- a/src/main/java/duke/command/ListCommand.java +++ b/src/main/java/duke/command/ListCommand.java @@ -3,6 +3,9 @@ import duke.Storage; import duke.TaskList; +/** + * Command that displays the user's list of tasks when executed. + */ public class ListCommand extends Command { @Override diff --git a/src/main/java/duke/command/Parser.java b/src/main/java/duke/command/Parser.java index f711235875..58cefbe31f 100644 --- a/src/main/java/duke/command/Parser.java +++ b/src/main/java/duke/command/Parser.java @@ -4,6 +4,11 @@ import duke.dukeexception.NoCommandException; import duke.dukeexception.NoDescriptionException; +/** + * Types of user commands. + * User has to type these commands in each line of input + * to command the bot. + */ enum CommandType { LIST, DONE, @@ -14,8 +19,20 @@ enum CommandType { EVENT } +/** + * Static class that parses user commands into executable bot commands represented + * by Command objects. + */ public class Parser { + /** + * + * @param fullCommand User command (the full line of user input). + * @return A bot command to be executed. + * @throws DukeException If description is missing for AddCommand, + * DeleteCommand or DoneCommand, + * or if user command does not match any CommandType. + */ public static Command parse(String fullCommand) throws DukeException { String[] commandElements = fullCommand.split(" ", 2); String commandString = commandElements[0]; diff --git a/src/main/java/duke/dukeexception/DukeException.java b/src/main/java/duke/dukeexception/DukeException.java index 84a267b8c5..6d86516a4a 100644 --- a/src/main/java/duke/dukeexception/DukeException.java +++ b/src/main/java/duke/dukeexception/DukeException.java @@ -1,7 +1,15 @@ package duke.dukeexception; +/** + * Represents exceptions unique to the bot. + */ public class DukeException extends Exception { + /** + * Public constructor. + * + * @param message Error message. + */ public DukeException(String message) { super(message); } diff --git a/src/main/java/duke/dukeexception/NoCommandException.java b/src/main/java/duke/dukeexception/NoCommandException.java index 27c927035a..0009d503ef 100644 --- a/src/main/java/duke/dukeexception/NoCommandException.java +++ b/src/main/java/duke/dukeexception/NoCommandException.java @@ -1,5 +1,8 @@ package duke.dukeexception; +/** + * Exception thrown when a non-valid user command is inputted. + */ public class NoCommandException extends DukeException { public NoCommandException() { diff --git a/src/main/java/duke/dukeexception/NoDescriptionException.java b/src/main/java/duke/dukeexception/NoDescriptionException.java index 6d49137e42..e83b025cad 100644 --- a/src/main/java/duke/dukeexception/NoDescriptionException.java +++ b/src/main/java/duke/dukeexception/NoDescriptionException.java @@ -1,5 +1,10 @@ package duke.dukeexception; +/** + * Exception thrown when the description is missing from is missing for the + * AddCommand, DeleteCommand or DoneCommand + * bot commands. + */ public class NoDescriptionException extends DukeException { public NoDescriptionException(String cmd) { diff --git a/src/main/java/duke/dukeexception/WrongDeadlineException.java b/src/main/java/duke/dukeexception/WrongDeadlineException.java index b829e3c8f2..9da6a4b012 100644 --- a/src/main/java/duke/dukeexception/WrongDeadlineException.java +++ b/src/main/java/duke/dukeexception/WrongDeadlineException.java @@ -1,6 +1,10 @@ package duke.dukeexception; -// for deadline or exception +/** + * Exception thrown when date and time is not given in the correct format + * for DEADLINE and EVENT user commands from + * CommandType. + */ public class WrongDeadlineException extends DukeException { public WrongDeadlineException(String cmd, String separator) { super("You type wrong lah!" + diff --git a/src/main/java/duke/dukeexception/WrongItemIndexException.java b/src/main/java/duke/dukeexception/WrongItemIndexException.java index caa9c04e20..61e190582c 100644 --- a/src/main/java/duke/dukeexception/WrongItemIndexException.java +++ b/src/main/java/duke/dukeexception/WrongItemIndexException.java @@ -1,6 +1,11 @@ package duke.dukeexception; -// for delete and done commands +/** + * Exception thrown when description of DELETE or DONE + * user commands are not accompanied with a valid task number that is an integer + * and within the range 1 to the size of the list of tasks (no tasks can be marked + * or deleted if there are no task in the list). + */ public class WrongItemIndexException extends DukeException { public WrongItemIndexException(String cmd, int listLength) { diff --git a/src/main/java/duke/task/Deadline.java b/src/main/java/duke/task/Deadline.java index ff5e67f974..1fd0002057 100644 --- a/src/main/java/duke/task/Deadline.java +++ b/src/main/java/duke/task/Deadline.java @@ -1,17 +1,33 @@ -package duke.task;//@@author {FooJingYi}-reused -//Reused from https://nus-cs2103-ay2021s1.github.io/website/schedule/week2/project.html with minor modifications +package duke.task; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; +/** + * Represents a task to be completed by a given deadline. + * + * Adapted from https://nus-cs2103-ay2021s1.github.io/website/schedule/week2/project.html . + */ public class Deadline extends Task { + /** Represents the date and time at which to complete the task by */ protected LocalDateTime dateTime; + /** + * Public constructor. + * + * @param taskName Name of the task as given by user. + * @param dateTime LocalDateTime object representing the deadline of the task. + */ public Deadline(String taskName, LocalDateTime dateTime) { super(taskName); this.dateTime = dateTime; } + /** + * Returns the date and time at which to complete the task by. + * + * @return LocalDateTime object representing the deadline of the task. + */ public LocalDateTime getDateTime() { return dateTime; } diff --git a/src/main/java/duke/task/Event.java b/src/main/java/duke/task/Event.java index 3e963ecad0..ba409eb062 100644 --- a/src/main/java/duke/task/Event.java +++ b/src/main/java/duke/task/Event.java @@ -3,14 +3,29 @@ import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; +/** + * Represents an event as a task at a particular date and time. + */ public class Event extends Task { + /** Represents the date and time of the event */ protected LocalDateTime dateTime; + /** + * Public constructor. + * + * @param taskName Name of the task as given by user. + * @param dateTime LocalDateTime object representing the date and time of the event. + */ public Event(String taskName, LocalDateTime dateTime) { super(taskName); this.dateTime = dateTime; } + /** + * Returns the date and time when the event is held. + * + * @return LocalDateTime object representing the date and time of the event. + */ public LocalDateTime getDateTime() { return dateTime; } diff --git a/src/main/java/duke/task/Task.java b/src/main/java/duke/task/Task.java index 57fdb6a3fd..daacbf37eb 100644 --- a/src/main/java/duke/task/Task.java +++ b/src/main/java/duke/task/Task.java @@ -1,15 +1,31 @@ package duke.task; -//Adapted from https://nus-cs2103-ay2021s1.github.io/website/schedule/week2/project.html +/** + * Represents a task that has a name taskName and a status isDone. + * Adapted from https://nus-cs2103-ay2021s1.github.io/website/schedule/week2/project.html + */ + public class Task { + /** Name of the task */ protected final String taskName; + /** Status of the task */ protected boolean isDone; + /** + * Public constructor. A task is not done by default when first added. + * + * @param taskName Name of the task as given by user. + */ public Task(String taskName) { this.taskName = taskName; this.isDone = false; } + /** + * Returns the name of the task given by the user. + * + * @return Name of the task. + */ public String getTaskName() { return taskName; } @@ -18,10 +34,19 @@ protected String getStatusIcon() { return isDone ? "\u2713" : "\u2718"; //return tick or cross symbols } + /** + * Returns a boolean value representing whether the task has been + * completed or not. A true value indicates that the task has been completed. + * + * @return Boolean indicating whether task has been done. + */ public boolean isDone() { return this.isDone; } + /** + * Marks a task as having been completed. + */ public void markDone() { this.isDone = true; } From d084edec1e0078bdfb9c006756d0d2fc617dc823 Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Tue, 1 Sep 2020 17:13:05 +0800 Subject: [PATCH 52/95] Change indentation and variable names to comply with coding standards --- src/main/java/duke/Storage.java | 20 +++++++++---------- src/main/java/duke/Ui.java | 2 +- src/main/java/duke/command/AddCommand.java | 14 ++++++------- src/main/java/duke/command/DeleteCommand.java | 12 +++++------ src/main/java/duke/command/DoneCommand.java | 6 +++--- src/main/java/duke/command/Parser.java | 8 ++++---- .../dukeexception/NoDescriptionException.java | 4 ++-- .../dukeexception/WrongDeadlineException.java | 12 +++++------ .../WrongItemIndexException.java | 10 +++++----- src/main/java/duke/task/Deadline.java | 2 +- src/main/java/duke/task/Event.java | 2 +- 11 files changed, 46 insertions(+), 46 deletions(-) diff --git a/src/main/java/duke/Storage.java b/src/main/java/duke/Storage.java index ea4698dbca..af048fdbf1 100644 --- a/src/main/java/duke/Storage.java +++ b/src/main/java/duke/Storage.java @@ -96,22 +96,22 @@ public String parseToStorage(Task task) throws DukeException { taskDescription = task.getTaskName() + " | " + ((Deadline) task).getDateTime().format( - DateTimeFormatter.ofPattern("d MMM yyyy, h.m a")); + DateTimeFormatter.ofPattern("d MMM yyyy, h.m a")); } else if (task instanceof Event) { taskType = "E"; taskDescription = task.getTaskName() + - " | " + - ((Event) task).getDateTime().format( - DateTimeFormatter.ofPattern("d MMM yyyy, h.m a")); + " | " + + ((Event) task).getDateTime().format( + DateTimeFormatter.ofPattern("d MMM yyyy, h.m a")); } else { throw new DukeException("Cannot recognise type"); } return taskType + - " | " + - status + - " | " + - taskDescription; + " | " + + status + + " | " + + taskDescription; } public Task parseFromStorage(String storedTask) throws DukeException { @@ -124,12 +124,12 @@ public Task parseFromStorage(String storedTask) throws DukeException { } else if (storedTask.charAt(0) == 'D') { String taskName = taskElements[2]; LocalDateTime dateTime = LocalDateTime.parse(taskElements[3], - DateTimeFormatter.ofPattern("d MMM yyyy, h.m a")); + DateTimeFormatter.ofPattern("d MMM yyyy, h.m a")); task = new Deadline(taskName, dateTime); } else if (storedTask.charAt(0) == 'E') { String taskName = taskElements[2]; LocalDateTime dateTime = LocalDateTime.parse(taskElements[3], - DateTimeFormatter.ofPattern("d MMM yyyy, h.m a")); + DateTimeFormatter.ofPattern("d MMM yyyy, h.m a")); task = new Event(taskName, dateTime); } diff --git a/src/main/java/duke/Ui.java b/src/main/java/duke/Ui.java index d13499bd8b..67d2788965 100644 --- a/src/main/java/duke/Ui.java +++ b/src/main/java/duke/Ui.java @@ -8,7 +8,7 @@ public class Ui { public void showWelcomeMsg() { System.out.println("____________________________________________________________"); System.out.println("Eh what's up! I'm Meimei" + - "\nWhat you want ah?"); + "\nWhat you want ah?"); System.out.println("____________________________________________________________"); } diff --git a/src/main/java/duke/command/AddCommand.java b/src/main/java/duke/command/AddCommand.java index 282cf722b0..c085c5b5b9 100644 --- a/src/main/java/duke/command/AddCommand.java +++ b/src/main/java/duke/command/AddCommand.java @@ -30,7 +30,7 @@ public void execute(TaskList tasks, Storage storage) throws DukeException { String[] descElements = this.description.split(" /by "); try { LocalDateTime dateTime = LocalDateTime.parse(descElements[1], - DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm")); + DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm")); String taskName = descElements[0]; task = new Deadline(taskName, dateTime); } catch (Exception e) { @@ -40,7 +40,7 @@ public void execute(TaskList tasks, Storage storage) throws DukeException { String[] descElements = this.description.split(" /at "); try { LocalDateTime dateTime = LocalDateTime.parse(descElements[1], - DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm")); + DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm")); String taskName = descElements[0]; task = new Event(taskName, dateTime); } catch (Exception e) { @@ -52,11 +52,11 @@ public void execute(TaskList tasks, Storage storage) throws DukeException { tasks.add(task, storage); System.out.println("Orh. I added:" + - "\n " + - task.toString() + - "\nNow you got " + - tasks.getListLength() + - " things in the list."); + "\n " + + task.toString() + + "\nNow you got " + + tasks.getListLength() + + " things in the list."); } } diff --git a/src/main/java/duke/command/DeleteCommand.java b/src/main/java/duke/command/DeleteCommand.java index 7a1113d342..d6c6d97f5f 100644 --- a/src/main/java/duke/command/DeleteCommand.java +++ b/src/main/java/duke/command/DeleteCommand.java @@ -21,14 +21,14 @@ public void execute(TaskList tasks, Storage storage) throws DukeException { tasks.delete(taskNum, storage); System.out.println("Okay, I deleted this liao:" + - "\n " + - task.toString() + - "\nNow left " + - tasks.getListLength() + - " things in the list."); + "\n " + + task.toString() + + "\nNow left " + + tasks.getListLength() + + " things in the list."); } catch (Exception e) { throw new WrongItemIndexException(CommandType.DELETE.toString().toLowerCase(), - tasks.getListLength()); + tasks.getListLength()); } } diff --git a/src/main/java/duke/command/DoneCommand.java b/src/main/java/duke/command/DoneCommand.java index 5673574589..85be188653 100644 --- a/src/main/java/duke/command/DoneCommand.java +++ b/src/main/java/duke/command/DoneCommand.java @@ -21,11 +21,11 @@ public void execute(TaskList tasks, Storage storage) throws DukeException { tasks.markDone(taskNum, storage); System.out.println("Can, I help you mark this as done liao:" + - "\n " + - task.toString()); + "\n " + + task.toString()); } catch (NumberFormatException e) { throw new WrongItemIndexException(CommandType.DONE.toString().toLowerCase(), - tasks.getListLength()); + tasks.getListLength()); } } diff --git a/src/main/java/duke/command/Parser.java b/src/main/java/duke/command/Parser.java index f711235875..9156d07b3c 100644 --- a/src/main/java/duke/command/Parser.java +++ b/src/main/java/duke/command/Parser.java @@ -33,10 +33,10 @@ public static Command parse(String fullCommand) throws DukeException { } else if (commandType == CommandType.BYE) { return new ExitCommand(); } else if (commandType == CommandType.DELETE || - commandType == CommandType.DONE || - commandType == CommandType.TODO || - commandType == CommandType.DEADLINE || - commandType == CommandType.EVENT) { // Commands that have a description + commandType == CommandType.DONE || + commandType == CommandType.TODO || + commandType == CommandType.DEADLINE || + commandType == CommandType.EVENT) { // Commands that have a description try { String description = commandElements[1]; if (commandType == CommandType.DELETE) { diff --git a/src/main/java/duke/dukeexception/NoDescriptionException.java b/src/main/java/duke/dukeexception/NoDescriptionException.java index 6d49137e42..6d85fc3d19 100644 --- a/src/main/java/duke/dukeexception/NoDescriptionException.java +++ b/src/main/java/duke/dukeexception/NoDescriptionException.java @@ -2,8 +2,8 @@ public class NoDescriptionException extends DukeException { - public NoDescriptionException(String cmd) { - super("The description of " + cmd + " cannot be empty lah. Try again!"); + public NoDescriptionException(String commandName) { + super("The description of " + commandName + " cannot be empty lah. Try again!"); } @Override diff --git a/src/main/java/duke/dukeexception/WrongDeadlineException.java b/src/main/java/duke/dukeexception/WrongDeadlineException.java index b829e3c8f2..90ebaca699 100644 --- a/src/main/java/duke/dukeexception/WrongDeadlineException.java +++ b/src/main/java/duke/dukeexception/WrongDeadlineException.java @@ -2,12 +2,12 @@ // for deadline or exception public class WrongDeadlineException extends DukeException { - public WrongDeadlineException(String cmd, String separator) { + public WrongDeadlineException(String commandName, String separator) { super("You type wrong lah!" + - "\nTry \"" + - cmd + - " {description of task} " + - separator + - " {date and time in this format: dd/MM/yyyy HH:mm}\""); + "\nTry \"" + + commandName + + " {description of task} " + + separator + + " {date and time in this format: dd/MM/yyyy HH:mm}\""); } } diff --git a/src/main/java/duke/dukeexception/WrongItemIndexException.java b/src/main/java/duke/dukeexception/WrongItemIndexException.java index caa9c04e20..3a4a9c0813 100644 --- a/src/main/java/duke/dukeexception/WrongItemIndexException.java +++ b/src/main/java/duke/dukeexception/WrongItemIndexException.java @@ -3,11 +3,11 @@ // for delete and done commands public class WrongItemIndexException extends DukeException { - public WrongItemIndexException(String cmd, int listLength) { - super("Cannot find leh. Try typing \"" + cmd + " {index of list item}\"." + - "\nYour list only got " + - listLength + - " things."); + public WrongItemIndexException(String commandName, int listLength) { + super("Cannot find leh. Try typing \"" + commandName + " {index of list item}\"." + + "\nYour list only got " + + listLength + + " things."); } @Override diff --git a/src/main/java/duke/task/Deadline.java b/src/main/java/duke/task/Deadline.java index ff5e67f974..e8f2d2edd8 100644 --- a/src/main/java/duke/task/Deadline.java +++ b/src/main/java/duke/task/Deadline.java @@ -19,6 +19,6 @@ public LocalDateTime getDateTime() { @Override public String toString() { return "[D]" + super.toString() + " by: " + - this.dateTime.format(DateTimeFormatter.ofPattern("d MMM yyyy, h.m a")); + this.dateTime.format(DateTimeFormatter.ofPattern("d MMM yyyy, h.m a")); } } \ No newline at end of file diff --git a/src/main/java/duke/task/Event.java b/src/main/java/duke/task/Event.java index 3e963ecad0..b9d3831e3b 100644 --- a/src/main/java/duke/task/Event.java +++ b/src/main/java/duke/task/Event.java @@ -18,6 +18,6 @@ public LocalDateTime getDateTime() { @Override public String toString() { return "[E]" + super.toString() + " at: " + - this.dateTime.format(DateTimeFormatter.ofPattern("d MMM yyyy, h.m a")); + this.dateTime.format(DateTimeFormatter.ofPattern("d MMM yyyy, h.m a")); } } From 78ed73204e03a7e9328d244e59171e89f7dd536d Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Tue, 1 Sep 2020 17:29:01 +0800 Subject: [PATCH 53/95] Add find command --- src/main/java/duke/command/FindCommand.java | 55 +++++++++++++++++++++ src/main/java/duke/command/Parser.java | 8 ++- 2 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 src/main/java/duke/command/FindCommand.java diff --git a/src/main/java/duke/command/FindCommand.java b/src/main/java/duke/command/FindCommand.java new file mode 100644 index 0000000000..83ea8e30d4 --- /dev/null +++ b/src/main/java/duke/command/FindCommand.java @@ -0,0 +1,55 @@ +package duke.command; + +import duke.Storage; +import duke.TaskList; +import duke.task.Task; + +import duke.dukeexception.DukeException; + +import java.util.ArrayList; +import java.util.List; + + +/** + * Finds tasks that contain a given keyword when executed. + */ +public class FindCommand extends Command{ + /** Keyword to be searched for */ + private final String description; + + /** + * Public constructor. + * + * @param description Keyword that user wants to search for amongst tasks. + */ + public FindCommand(String description) { + this.description = description; + } + + @Override + public void execute(TaskList tasks, Storage storage) throws DukeException { + List resultList = new ArrayList<>(); + + for (int i = 1; i <= tasks.getListLength(); i++) { + Task task = tasks.getTask(i); + System.out.println(task.getTaskName()); + if (task.getTaskName().contains(this.description)) { + resultList.add(task); + } + } + + String finalString = ""; + int counter = 0; + for (Task task : resultList) { + counter++; + finalString += "\n" + counter + "." + task.toString(); + } + + System.out.println("Na, I found this:" + finalString); + } + + @Override + public boolean isExit() { + return false; + } +} diff --git a/src/main/java/duke/command/Parser.java b/src/main/java/duke/command/Parser.java index f711235875..3d0e657d02 100644 --- a/src/main/java/duke/command/Parser.java +++ b/src/main/java/duke/command/Parser.java @@ -11,7 +11,8 @@ enum CommandType { BYE, TODO, DEADLINE, - EVENT + EVENT, + FIND } public class Parser { @@ -36,13 +37,16 @@ public static Command parse(String fullCommand) throws DukeException { commandType == CommandType.DONE || commandType == CommandType.TODO || commandType == CommandType.DEADLINE || - commandType == CommandType.EVENT) { // Commands that have a description + commandType == CommandType.EVENT || + commandType == CommandType.FIND) { // Commands that have a description try { String description = commandElements[1]; if (commandType == CommandType.DELETE) { return new DeleteCommand(description); } else if (commandType == CommandType.DONE) { return new DoneCommand(description); + } else if (commandType == CommandType.FIND) { + return new FindCommand(description); } else { return new AddCommand(commandType, description); } From 3d4e0dd04982457ae22775574f156e46c4824e90 Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Tue, 1 Sep 2020 18:02:03 +0800 Subject: [PATCH 54/95] Simplify finding keywords by removing whitespaces and comparing in lowercase --- src/main/java/duke/command/FindCommand.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main/java/duke/command/FindCommand.java b/src/main/java/duke/command/FindCommand.java index 83ea8e30d4..d1c0de8b11 100644 --- a/src/main/java/duke/command/FindCommand.java +++ b/src/main/java/duke/command/FindCommand.java @@ -11,19 +11,23 @@ /** - * Finds tasks that contain a given keyword when executed. + * Command that finds tasks that contain a given keyword when executed. */ public class FindCommand extends Command{ /** Keyword to be searched for */ private final String description; /** - * Public constructor. + * Public constructor. Removes whitespaces of description + * when assigning to class variable, and converts keyword to lower case + * for simplicity. * * @param description Keyword that user wants to search for amongst tasks. */ public FindCommand(String description) { - this.description = description; + this.description = description. + replaceAll("\\s",""). + toLowerCase(); } @Override @@ -32,8 +36,7 @@ public void execute(TaskList tasks, Storage storage) throws DukeException { for (int i = 1; i <= tasks.getListLength(); i++) { Task task = tasks.getTask(i); - System.out.println(task.getTaskName()); - if (task.getTaskName().contains(this.description)) { + if (task.getTaskName().toLowerCase().contains(this.description)) { resultList.add(task); } } From d7fad1cfd29ecb3ee4e2088a8ff12fd067ffd93d Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Thu, 3 Sep 2020 17:15:06 +0800 Subject: [PATCH 55/95] Change error handling for development purposes --- src/main/java/duke/Storage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/duke/Storage.java b/src/main/java/duke/Storage.java index 1aeab89373..027c477e9d 100644 --- a/src/main/java/duke/Storage.java +++ b/src/main/java/duke/Storage.java @@ -194,7 +194,7 @@ protected Task parseFromStorage(String storedTask) throws DukeException { throw new DukeException("Cannot read tasks from file."); } } catch (Exception e) { - System.out.println(e); + e.printStackTrace(); throw new DukeException("Cannot read tasks from file."); } } From 1cfde5886b03ce975bd22da8d75187153fcd8df3 Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Thu, 3 Sep 2020 17:34:50 +0800 Subject: [PATCH 56/95] Review formatting and moved CommandType enum to its own file --- src/main/java/duke/Storage.java | 6 +--- src/main/java/duke/TaskList.java | 6 ++-- src/main/java/duke/Ui.java | 2 +- src/main/java/duke/command/AddCommand.java | 10 ++----- src/main/java/duke/command/CommandType.java | 17 +++++++++++ src/main/java/duke/command/DeleteCommand.java | 12 +++----- src/main/java/duke/command/DoneCommand.java | 7 ++--- src/main/java/duke/command/Parser.java | 29 +++++-------------- src/main/java/duke/task/Deadline.java | 5 ++-- src/main/java/duke/task/Event.java | 2 +- src/main/java/duke/task/Todo.java | 8 +++++ 11 files changed, 51 insertions(+), 53 deletions(-) create mode 100644 src/main/java/duke/command/CommandType.java diff --git a/src/main/java/duke/Storage.java b/src/main/java/duke/Storage.java index 027c477e9d..70e378d297 100644 --- a/src/main/java/duke/Storage.java +++ b/src/main/java/duke/Storage.java @@ -150,11 +150,7 @@ protected String parseToStorage(Task task) throws DukeException { throw new DukeException("Cannot recognise type"); } - return taskType + - " | " + - status + - " | " + - taskDescription; + return taskType + " | " + status + " | " + taskDescription; } /** diff --git a/src/main/java/duke/TaskList.java b/src/main/java/duke/TaskList.java index fc26be4fb4..7935cbfd6f 100644 --- a/src/main/java/duke/TaskList.java +++ b/src/main/java/duke/TaskList.java @@ -15,7 +15,7 @@ public class TaskList { private final List list; /** - * Public constructor that creates a new (empty) list + * Public constructor that creates a new (empty) list. */ public TaskList() { this.list = new ArrayList<>(); @@ -38,7 +38,7 @@ public TaskList(List list) { * @param storage Storage object that updates the relevant file on the hard disk. * @throws DukeException If exception is thrown by the update method of the Storage class. */ - public void add(Task task, Storage storage) throws DukeException { + public void addTask(Task task, Storage storage) throws DukeException { this.list.add(task); storage.update(task); } @@ -52,7 +52,7 @@ public void add(Task task, Storage storage) throws DukeException { * @param storage Storage object that updates the relevant file on the hard disk. * @throws DukeException If exception is thrown by the update method of the Storage class. */ - public void delete(int taskNum, Storage storage) throws DukeException { + public void deleteTask(int taskNum, Storage storage) throws DukeException { this.list.remove(taskNum - 1); storage.update(this.list); } diff --git a/src/main/java/duke/Ui.java b/src/main/java/duke/Ui.java index 28554077a8..4bfd352862 100644 --- a/src/main/java/duke/Ui.java +++ b/src/main/java/duke/Ui.java @@ -10,7 +10,7 @@ public class Ui { private final Scanner sc = new Scanner(System.in); /** - * Public constructor. + * Public constructor. Should only be used by bot. */ public Ui() { } diff --git a/src/main/java/duke/command/AddCommand.java b/src/main/java/duke/command/AddCommand.java index 70cdcf0a96..64dd889844 100644 --- a/src/main/java/duke/command/AddCommand.java +++ b/src/main/java/duke/command/AddCommand.java @@ -64,14 +64,10 @@ public void execute(TaskList tasks, Storage storage) throws DukeException { } if (task != null) { - tasks.add(task, storage); + tasks.addTask(task, storage); - System.out.println("Orh. I added:" + - "\n " + - task.toString() + - "\nNow you got " + - tasks.getListLength() + - " things in the list."); + System.out.println("Orh. I added:" + "\n " + task.toString() + + "\nNow you got " + tasks.getListLength() + " things in the list."); } } diff --git a/src/main/java/duke/command/CommandType.java b/src/main/java/duke/command/CommandType.java new file mode 100644 index 0000000000..a8c29b4c30 --- /dev/null +++ b/src/main/java/duke/command/CommandType.java @@ -0,0 +1,17 @@ +package duke.command; + +/** + * Types of user commands. + * User has to type these commands in each line of input + * to command the bot. + */ +public enum CommandType { + LIST, + DONE, + DELETE, + BYE, + TODO, + DEADLINE, + EVENT, + FIND +} diff --git a/src/main/java/duke/command/DeleteCommand.java b/src/main/java/duke/command/DeleteCommand.java index 4d6154b3f4..f258b55284 100644 --- a/src/main/java/duke/command/DeleteCommand.java +++ b/src/main/java/duke/command/DeleteCommand.java @@ -29,14 +29,10 @@ public void execute(TaskList tasks, Storage storage) throws DukeException { try { int taskNum = Integer.parseInt(this.description); Task task = tasks.getTask(taskNum); - tasks.delete(taskNum, storage); - - System.out.println("Okay, I deleted this liao:" + - "\n " + - task.toString() + - "\nNow left " + - tasks.getListLength() + - " things in the list."); + tasks.deleteTask(taskNum, storage); + + System.out.println("Okay, I deleted this liao:" + "\n " + task.toString() + + "\nNow left " + tasks.getListLength() + " things in the list."); } catch (Exception e) { throw new WrongItemIndexException(CommandType.DELETE.toString().toLowerCase(), tasks.getListLength()); diff --git a/src/main/java/duke/command/DoneCommand.java b/src/main/java/duke/command/DoneCommand.java index 3916fd49d9..e35d83082b 100644 --- a/src/main/java/duke/command/DoneCommand.java +++ b/src/main/java/duke/command/DoneCommand.java @@ -31,12 +31,11 @@ public void execute(TaskList tasks, Storage storage) throws DukeException { Task task = tasks.getTask(taskNum); tasks.markDone(taskNum, storage); - System.out.println("Can, I help you mark this as done liao:" + - "\n " + - task.toString()); + System.out.println("Can, I help you mark this as done liao:" + + "\n " + task.toString()); } catch (NumberFormatException e) { throw new WrongItemIndexException(CommandType.DONE.toString().toLowerCase(), - tasks.getListLength()); + tasks.getListLength()); } } diff --git a/src/main/java/duke/command/Parser.java b/src/main/java/duke/command/Parser.java index af73e81eda..040053acd6 100644 --- a/src/main/java/duke/command/Parser.java +++ b/src/main/java/duke/command/Parser.java @@ -4,22 +4,6 @@ import duke.dukeexception.NoCommandException; import duke.dukeexception.NoDescriptionException; -/** - * Types of user commands. - * User has to type these commands in each line of input - * to command the bot. - */ -enum CommandType { - LIST, - DONE, - DELETE, - BYE, - TODO, - DEADLINE, - EVENT, - FIND -} - /** * Static class that parses user commands into executable bot commands represented * by Command objects. @@ -27,6 +11,7 @@ enum CommandType { public class Parser { /** + * Parse user commands into executable bot commands. * * @param fullCommand User command (the full line of user input). * @return A bot command to be executed. @@ -50,12 +35,12 @@ public static Command parse(String fullCommand) throws DukeException { return new ListCommand(); } else if (commandType == CommandType.BYE) { return new ExitCommand(); - } else if (commandType == CommandType.DELETE || - commandType == CommandType.DONE || - commandType == CommandType.TODO || - commandType == CommandType.DEADLINE || - commandType == CommandType.EVENT || - commandType == CommandType.FIND) { // Commands that have a description + } else if (commandType == CommandType.DELETE + || commandType == CommandType.DONE + || commandType == CommandType.TODO + || commandType == CommandType.DEADLINE + || commandType == CommandType.EVENT + || commandType == CommandType.FIND) { // Commands that have a description try { String description = commandElements[1]; if (commandType == CommandType.DELETE) { diff --git a/src/main/java/duke/task/Deadline.java b/src/main/java/duke/task/Deadline.java index d49187c6b2..b4d923dfd1 100644 --- a/src/main/java/duke/task/Deadline.java +++ b/src/main/java/duke/task/Deadline.java @@ -4,9 +4,10 @@ import java.time.format.DateTimeFormatter; /** - * Represents a task to be completed by a given deadline. + * Represents a task to be completed by a given deadline. Inherits from Task. * - * Adapted from https://nus-cs2103-ay2021s1.github.io/website/schedule/week2/project.html . + * Adapted from + * this page . */ public class Deadline extends Task { /** Represents the date and time at which to complete the task by */ diff --git a/src/main/java/duke/task/Event.java b/src/main/java/duke/task/Event.java index f135f4952c..1530777400 100644 --- a/src/main/java/duke/task/Event.java +++ b/src/main/java/duke/task/Event.java @@ -4,7 +4,7 @@ import java.time.format.DateTimeFormatter; /** - * Represents an event as a task at a particular date and time. + * Represents an event at a particular date and time. Inherits from Task. */ public class Event extends Task { /** Represents the date and time of the event */ diff --git a/src/main/java/duke/task/Todo.java b/src/main/java/duke/task/Todo.java index 28b9ec86da..87c8fb0fe1 100644 --- a/src/main/java/duke/task/Todo.java +++ b/src/main/java/duke/task/Todo.java @@ -1,7 +1,15 @@ package duke.task; +/** + * Represents a task to be done. Inherits from Task. + */ public class Todo extends Task { + /** + * Public constructor. + * + * @param taskName Name of the task as given by user. + */ public Todo(String taskName) { super(taskName); } From 51168940cc278d47c5649e215e58d8598280aea7 Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Thu, 3 Sep 2020 23:05:38 +0800 Subject: [PATCH 57/95] Add checkstyle files and change checkstyle version --- build.gradle | 2 +- config/checkstyle/checkstyle.xml | 403 +++++++++++++++++++++++++++++ config/checkstyle/suppressions.xml | 10 + 3 files changed, 414 insertions(+), 1 deletion(-) create mode 100644 config/checkstyle/checkstyle.xml create mode 100644 config/checkstyle/suppressions.xml diff --git a/build.gradle b/build.gradle index b0c5528fb5..20c0521cc7 100644 --- a/build.gradle +++ b/build.gradle @@ -38,7 +38,7 @@ shadowJar { } checkstyle { - toolVersion = '8.23' + toolVersion = '8.29' } run{ diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml new file mode 100644 index 0000000000..4c001417ae --- /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 0000000000..39efb6e4ac --- /dev/null +++ b/config/checkstyle/suppressions.xml @@ -0,0 +1,10 @@ + + + + + + + + From cef283923ba833748afbb026e5be385b664ee3c0 Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Thu, 3 Sep 2020 23:36:56 +0800 Subject: [PATCH 58/95] Add dependencies for JavaFX in build.gradle --- build.gradle | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/build.gradle b/build.gradle index 20c0521cc7..8cf2a65b39 100644 --- a/build.gradle +++ b/build.gradle @@ -12,6 +12,21 @@ repositories { dependencies { testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.5.0' testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.5.0' + + String javaFxVersion = '11' + + implementation group: 'org.openjfx', name: 'javafx-base', version: javaFxVersion, classifier: 'win' + implementation group: 'org.openjfx', name: 'javafx-base', version: javaFxVersion, classifier: 'mac' + implementation group: 'org.openjfx', name: 'javafx-base', version: javaFxVersion, classifier: 'linux' + implementation group: 'org.openjfx', name: 'javafx-controls', version: javaFxVersion, classifier: 'win' + implementation group: 'org.openjfx', name: 'javafx-controls', version: javaFxVersion, classifier: 'mac' + implementation group: 'org.openjfx', name: 'javafx-controls', version: javaFxVersion, classifier: 'linux' + implementation group: 'org.openjfx', name: 'javafx-fxml', version: javaFxVersion, classifier: 'win' + implementation group: 'org.openjfx', name: 'javafx-fxml', version: javaFxVersion, classifier: 'mac' + implementation group: 'org.openjfx', name: 'javafx-fxml', version: javaFxVersion, classifier: 'linux' + implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'win' + implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'mac' + implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'linux' } test { From 568289ce5e1ac6a9f3be6135c3ed46c15a49c8fd Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Thu, 3 Sep 2020 23:48:53 +0800 Subject: [PATCH 59/95] Try HelloWorld GUI application --- src/main/java/duke/Duke.java | 17 ++++++++++++++++- src/main/java/duke/Launcher.java | 14 ++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 src/main/java/duke/Launcher.java diff --git a/src/main/java/duke/Duke.java b/src/main/java/duke/Duke.java index 2875c0787a..c4b72f0ba9 100644 --- a/src/main/java/duke/Duke.java +++ b/src/main/java/duke/Duke.java @@ -3,6 +3,10 @@ import duke.command.Command; import duke.command.Parser; import duke.dukeexception.DukeException; +import javafx.application.Application; +import javafx.scene.Scene; +import javafx.scene.control.Label; +import javafx.stage.Stage; /** * The Duke program is an interactive bot that offers commands to help the @@ -12,7 +16,7 @@ * * @author Foo Jing Yi */ -public class Duke { +public class Duke extends Application { /** Storage object used by Duke to load from and write to hard disk */ private Storage storage; /** TaskList object that contains the list of tasks */ @@ -20,6 +24,8 @@ public class Duke { /** Ui object that deals with interactions with the user */ private Ui ui; + public Duke() {} + /** * Public class constructor that takes in the location of a file as a string * indicating the relative file path. @@ -62,4 +68,13 @@ public void run() { public static void main(String[] args) { new Duke("data/tasks.txt").run(); } + + @Override + public void start(Stage stage) { + Label helloWorld = new Label("Hello World!"); // Creating a new Label control + Scene scene = new Scene(helloWorld); // Setting the scene to be our Label + + stage.setScene(scene); // Setting the stage to show our screen + stage.show(); // Render the stage. + } } diff --git a/src/main/java/duke/Launcher.java b/src/main/java/duke/Launcher.java new file mode 100644 index 0000000000..ae283bd8b4 --- /dev/null +++ b/src/main/java/duke/Launcher.java @@ -0,0 +1,14 @@ +package duke; + +import javafx.application.Application; + +/** + * A launcher class to workaround classpath issues. + * Reused from + * this guide. + */ +public class Launcher { + public static void main(String[] args) { + Application.launch(Duke.class, args); + } +} From 2b9c4308a3c336843c1d27c47605bdf1eed3e74b Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Thu, 3 Sep 2020 23:53:30 +0800 Subject: [PATCH 60/95] Add according to JavaFX tutorial 2 --- src/main/java/duke/Duke.java | 64 +++++++++++++++++++++++++++++++++--- 1 file changed, 60 insertions(+), 4 deletions(-) diff --git a/src/main/java/duke/Duke.java b/src/main/java/duke/Duke.java index c4b72f0ba9..c8a74cc7b9 100644 --- a/src/main/java/duke/Duke.java +++ b/src/main/java/duke/Duke.java @@ -5,7 +5,13 @@ import duke.dukeexception.DukeException; import javafx.application.Application; import javafx.scene.Scene; +import javafx.scene.control.Button; import javafx.scene.control.Label; +import javafx.scene.control.ScrollPane; +import javafx.scene.control.TextField; +import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.Region; +import javafx.scene.layout.VBox; import javafx.stage.Stage; /** @@ -24,6 +30,12 @@ public class Duke extends Application { /** Ui object that deals with interactions with the user */ private Ui ui; + private ScrollPane scrollPane; + private VBox dialogContainer; + private TextField userInput; + private Button sendButton; + private Scene scene; + public Duke() {} /** @@ -71,10 +83,54 @@ public static void main(String[] args) { @Override public void start(Stage stage) { - Label helloWorld = new Label("Hello World!"); // Creating a new Label control - Scene scene = new Scene(helloWorld); // Setting the scene to be our Label + //Step 1. Setting up required components + + //The container for the content of the chat to scroll. + scrollPane = new ScrollPane(); + dialogContainer = new VBox(); + scrollPane.setContent(dialogContainer); + + userInput = new TextField(); + sendButton = new Button("Send"); + + AnchorPane mainLayout = new AnchorPane(); + mainLayout.getChildren().addAll(scrollPane, userInput, sendButton); + + scene = new Scene(mainLayout); + + stage.setScene(scene); + stage.show(); + + //Step 2. Formatting the window to look as expected + stage.setTitle("Duke"); + stage.setResizable(false); + stage.setMinHeight(600.0); + stage.setMinWidth(400.0); + + mainLayout.setPrefSize(400.0, 600.0); + + scrollPane.setPrefSize(385, 535); + scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER); + scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.ALWAYS); + + scrollPane.setVvalue(1.0); + scrollPane.setFitToWidth(true); + + // You will need to import `javafx.scene.layout.Region` for this. + dialogContainer.setPrefHeight(Region.USE_COMPUTED_SIZE); + + userInput.setPrefWidth(325.0); + + sendButton.setPrefWidth(55.0); + + AnchorPane.setTopAnchor(scrollPane, 1.0); + + AnchorPane.setBottomAnchor(sendButton, 1.0); + AnchorPane.setRightAnchor(sendButton, 1.0); + + AnchorPane.setLeftAnchor(userInput , 1.0); + AnchorPane.setBottomAnchor(userInput, 1.0); - stage.setScene(scene); // Setting the stage to show our screen - stage.show(); // Render the stage. + // more code to be added here later } } From 01d7af9fd3e2954fdec92f983c783a3e40e9465a Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Fri, 4 Sep 2020 00:06:19 +0800 Subject: [PATCH 61/95] Add according to JavaFX tutorial 3 --- src/main/java/duke/DialogBox.java | 47 +++++++++++++++++++++++ src/main/java/duke/Duke.java | 54 ++++++++++++++++++++++++++- src/main/resources/images/DaDuke.png | Bin 0 -> 32657 bytes src/main/resources/images/DaUser.png | Bin 0 -> 37794 bytes 4 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 src/main/java/duke/DialogBox.java create mode 100644 src/main/resources/images/DaDuke.png create mode 100644 src/main/resources/images/DaUser.png diff --git a/src/main/java/duke/DialogBox.java b/src/main/java/duke/DialogBox.java new file mode 100644 index 0000000000..e399dd149c --- /dev/null +++ b/src/main/java/duke/DialogBox.java @@ -0,0 +1,47 @@ +package duke; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.geometry.Pos; +import javafx.scene.Node; +import javafx.scene.control.Label; +import javafx.scene.image.ImageView; +import javafx.scene.layout.HBox; + +public class DialogBox extends HBox { + + private Label text; + private ImageView displayPicture; + + public DialogBox(Label l, ImageView iv) { + text = l; + displayPicture = iv; + + text.setWrapText(true); + displayPicture.setFitWidth(100.0); + displayPicture.setFitHeight(100.0); + + this.setAlignment(Pos.TOP_RIGHT); + this.getChildren().addAll(text, displayPicture); + } + + /** + * Flips the dialog box such that the ImageView is on the left and text on the right. + */ + private void flip() { + this.setAlignment(Pos.TOP_LEFT); + ObservableList tmp = FXCollections.observableArrayList(this.getChildren()); + FXCollections.reverse(tmp); + this.getChildren().setAll(tmp); + } + + public static DialogBox getUserDialog(Label l, ImageView iv) { + return new DialogBox(l, iv); + } + + public static DialogBox getDukeDialog(Label l, ImageView iv) { + var db = new DialogBox(l, iv); + db.flip(); + return db; + } +} \ No newline at end of file diff --git a/src/main/java/duke/Duke.java b/src/main/java/duke/Duke.java index c8a74cc7b9..d40bbcece5 100644 --- a/src/main/java/duke/Duke.java +++ b/src/main/java/duke/Duke.java @@ -9,6 +9,8 @@ import javafx.scene.control.Label; import javafx.scene.control.ScrollPane; import javafx.scene.control.TextField; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.Region; import javafx.scene.layout.VBox; @@ -36,6 +38,9 @@ public class Duke extends Application { private Button sendButton; private Scene scene; + private Image user = new Image(this.getClass().getResourceAsStream("/images/DaUser.png")); + private Image duke = new Image(this.getClass().getResourceAsStream("/images/DaDuke.png")); + public Duke() {} /** @@ -131,6 +136,53 @@ public void start(Stage stage) { AnchorPane.setLeftAnchor(userInput , 1.0); AnchorPane.setBottomAnchor(userInput, 1.0); - // more code to be added here later + //Part 3. Add functionality to handle user input. + sendButton.setOnMouseClicked((event) -> { + handleUserInput(); + }); + + userInput.setOnAction((event) -> { + handleUserInput(); + }); + + //Scroll down to the end every time dialogContainer's height changes. + dialogContainer.heightProperty().addListener((observable) -> scrollPane.setVvalue(1.0)); + } + + /** + * Iteration 1: + * Creates a label with the specified text and adds it to the dialog container. + * @param text String containing text to add + * @return a label with the specified text that has word wrap enabled. + */ + private Label getDialogLabel(String text) { + // You will need to import `javafx.scene.control.Label`. + Label textToAdd = new Label(text); + textToAdd.setWrapText(true); + + return textToAdd; + } + + /** + * Iteration 2: + * Creates two dialog boxes, one echoing user input and the other containing Duke's reply and then appends them to + * the dialog container. Clears the user input after processing. + */ + private void handleUserInput() { + Label userText = new Label(userInput.getText()); + Label dukeText = new Label(getResponse(userInput.getText())); + dialogContainer.getChildren().addAll( + DialogBox.getUserDialog(userText, new ImageView(user)), + DialogBox.getDukeDialog(dukeText, new ImageView(duke)) + ); + userInput.clear(); + } + + /** + * You should have your own function to generate a response to user input. + * Replace this stub with your completed method. + */ + private String getResponse(String input) { + return "Duke heard: " + input; } } diff --git a/src/main/resources/images/DaDuke.png b/src/main/resources/images/DaDuke.png new file mode 100644 index 0000000000000000000000000000000000000000..d893658717e29b50b4ceedada235d9f75835a118 GIT binary patch literal 32657 zcmV)5K*_&}P)Zf>Tg`t%N;{PE9Y2%-o7`CdniQG#9szpJX+ zcrVK`zK7pG^!n{1T{qo3os(XNkTqW0IXIfGihv^wpO4cHg6YjrzS7VYZJZVWev% z!fK9RcD(ntf zCxMCmC7oRZecuOe6J~p&f5@?I;U2zE;O2T?Vy_8UZ1S4i=b>l%q0jeYAI+i(>K zsIun-!&|SPodfa+5u7Z5yhtd`XRjxuF+Ww~1H4xg)(?GXYLo{q2VPiixh(3*`0iyFHj`V{p-_+Q4jWKC=o6ohr zW)q;Nb7IxtY}qD4RyBpAMv`rR!YytU)sgEIa8F)wX(O<=RwjYY+sLdy0`5o0>=a zd(GePvuhIc}2|`2Y7OgN%4u~XV>q*3e$65qJ;}bK0Li-Wh%~iQn_FhV_*O6|& z$E%S!>BN;~l+|jbw5B2<783HDuPYE-T1Z)&EZTFhZGIj)nseO*|7}7`Vz4%vMl&l& z!c*D(>n3|ulhD`D1FzG)Tik79GgrpJ#HE0O)y{gM#c%L1=xtu`A<9oj(;}Q6cpHb= zIjOSNTO3vD|G|N_tH+^nj?I`D4+@;j;9`s4!*lY>X2_|khHWO LGkWs%c&z?S( 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 z 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 KMNUMnLSTa5`eeEQ literal 0 HcmV?d00001 From 43c1fa380ea058102e14ec8aaed8e63e72f55bca Mon Sep 17 00:00:00 2001 From: FooJingYi Date: Fri, 4 Sep 2020 12:58:26 +0800 Subject: [PATCH 62/95] Add according to JavaFX tutorial 4 --- src/main/java/duke/DialogBox.java | 52 +++++++---- src/main/java/duke/Duke.java | 117 +----------------------- src/main/java/duke/Launcher.java | 2 +- src/main/java/duke/Main.java | 31 +++++++ src/main/java/duke/MainWindow.java | 53 +++++++++++ src/main/resources/view/DialogBox.fxml | 16 ++++ src/main/resources/view/MainWindow.fxml | 20 ++++ 7 files changed, 156 insertions(+), 135 deletions(-) create mode 100644 src/main/java/duke/Main.java create mode 100644 src/main/java/duke/MainWindow.java create mode 100644 src/main/resources/view/DialogBox.fxml create mode 100644 src/main/resources/view/MainWindow.fxml diff --git a/src/main/java/duke/DialogBox.java b/src/main/java/duke/DialogBox.java index e399dd149c..d183f8252b 100644 --- a/src/main/java/duke/DialogBox.java +++ b/src/main/java/duke/DialogBox.java @@ -1,46 +1,60 @@ package duke; +import java.io.IOException; +import java.util.Collections; + import javafx.collections.FXCollections; import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; import javafx.geometry.Pos; import javafx.scene.Node; import javafx.scene.control.Label; +import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.HBox; +/** + * An example of a custom control using FXML. + * This control represents a dialog box consisting of an ImageView to represent the speaker's face and a label + * containing text from the speaker. + */ public class DialogBox extends HBox { - - private Label text; + @FXML + private Label dialog; + @FXML private ImageView displayPicture; - public DialogBox(Label l, ImageView iv) { - text = l; - displayPicture = iv; - - text.setWrapText(true); - displayPicture.setFitWidth(100.0); - displayPicture.setFitHeight(100.0); - - this.setAlignment(Pos.TOP_RIGHT); - this.getChildren().addAll(text, displayPicture); + private DialogBox(String text, Image img) { + try { + FXMLLoader fxmlLoader = new FXMLLoader(MainWindow.class.getResource("/view/DialogBox.fxml")); + fxmlLoader.setController(this); + fxmlLoader.setRoot(this); + fxmlLoader.load(); + } catch (IOException e) { + e.printStackTrace(); + } + + dialog.setText(text); + displayPicture.setImage(img); } /** * Flips the dialog box such that the ImageView is on the left and text on the right. */ private void flip() { - this.setAlignment(Pos.TOP_LEFT); ObservableList tmp = FXCollections.observableArrayList(this.getChildren()); - FXCollections.reverse(tmp); - this.getChildren().setAll(tmp); + Collections.reverse(tmp); + getChildren().setAll(tmp); + setAlignment(Pos.TOP_LEFT); } - public static DialogBox getUserDialog(Label l, ImageView iv) { - return new DialogBox(l, iv); + public static DialogBox getUserDialog(String text, Image img) { + return new DialogBox(text, img); } - public static DialogBox getDukeDialog(Label l, ImageView iv) { - var db = new DialogBox(l, iv); + public static DialogBox getDukeDialog(String text, Image img) { + var db = new DialogBox(text, img); db.flip(); return db; } diff --git a/src/main/java/duke/Duke.java b/src/main/java/duke/Duke.java index d40bbcece5..54adb65111 100644 --- a/src/main/java/duke/Duke.java +++ b/src/main/java/duke/Duke.java @@ -3,18 +3,6 @@ import duke.command.Command; import duke.command.Parser; import duke.dukeexception.DukeException; -import javafx.application.Application; -import javafx.scene.Scene; -import javafx.scene.control.Button; -import javafx.scene.control.Label; -import javafx.scene.control.ScrollPane; -import javafx.scene.control.TextField; -import javafx.scene.image.Image; -import javafx.scene.image.ImageView; -import javafx.scene.layout.AnchorPane; -import javafx.scene.layout.Region; -import javafx.scene.layout.VBox; -import javafx.stage.Stage; /** * The Duke program is an interactive bot that offers commands to help the @@ -24,7 +12,7 @@ * * @author Foo Jing Yi */ -public class Duke extends Application { +public class Duke { /** Storage object used by Duke to load from and write to hard disk */ private Storage storage; /** TaskList object that contains the list of tasks */ @@ -32,15 +20,6 @@ public class Duke extends Application { /** Ui object that deals with interactions with the user */ private Ui ui; - private ScrollPane scrollPane; - private VBox dialogContainer; - private TextField userInput; - private Button sendButton; - private Scene scene; - - private Image user = new Image(this.getClass().getResourceAsStream("/images/DaUser.png")); - private Image duke = new Image(this.getClass().getResourceAsStream("/images/DaDuke.png")); - public Duke() {} /** @@ -86,103 +65,11 @@ public static void main(String[] args) { new Duke("data/tasks.txt").run(); } - @Override - public void start(Stage stage) { - //Step 1. Setting up required components - - //The container for the content of the chat to scroll. - scrollPane = new ScrollPane(); - dialogContainer = new VBox(); - scrollPane.setContent(dialogContainer); - - userInput = new TextField(); - sendButton = new Button("Send"); - - AnchorPane mainLayout = new AnchorPane(); - mainLayout.getChildren().addAll(scrollPane, userInput, sendButton); - - scene = new Scene(mainLayout); - - stage.setScene(scene); - stage.show(); - - //Step 2. Formatting the window to look as expected - stage.setTitle("Duke"); - stage.setResizable(false); - stage.setMinHeight(600.0); - stage.setMinWidth(400.0); - - mainLayout.setPrefSize(400.0, 600.0); - - scrollPane.setPrefSize(385, 535); - scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER); - scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.ALWAYS); - - scrollPane.setVvalue(1.0); - scrollPane.setFitToWidth(true); - - // You will need to import `javafx.scene.layout.Region` for this. - dialogContainer.setPrefHeight(Region.USE_COMPUTED_SIZE); - - userInput.setPrefWidth(325.0); - - sendButton.setPrefWidth(55.0); - - AnchorPane.setTopAnchor(scrollPane, 1.0); - - AnchorPane.setBottomAnchor(sendButton, 1.0); - AnchorPane.setRightAnchor(sendButton, 1.0); - - AnchorPane.setLeftAnchor(userInput , 1.0); - AnchorPane.setBottomAnchor(userInput, 1.0); - - //Part 3. Add functionality to handle user input. - sendButton.setOnMouseClicked((event) -> { - handleUserInput(); - }); - - userInput.setOnAction((event) -> { - handleUserInput(); - }); - - //Scroll down to the end every time dialogContainer's height changes. - dialogContainer.heightProperty().addListener((observable) -> scrollPane.setVvalue(1.0)); - } - - /** - * Iteration 1: - * Creates a label with the specified text and adds it to the dialog container. - * @param text String containing text to add - * @return a label with the specified text that has word wrap enabled. - */ - private Label getDialogLabel(String text) { - // You will need to import `javafx.scene.control.Label`. - Label textToAdd = new Label(text); - textToAdd.setWrapText(true); - - return textToAdd; - } - - /** - * Iteration 2: - * Creates two dialog boxes, one echoing user input and the other containing Duke's reply and then appends them to - * the dialog container. Clears the user input after processing. - */ - private void handleUserInput() { - Label userText = new Label(userInput.getText()); - Label dukeText = new Label(getResponse(userInput.getText())); - dialogContainer.getChildren().addAll( - DialogBox.getUserDialog(userText, new ImageView(user)), - DialogBox.getDukeDialog(dukeText, new ImageView(duke)) - ); - userInput.clear(); - } - /** * You should have your own function to generate a response to user input. * Replace this stub with your completed method. */ - private String getResponse(String input) { + public String getResponse(String input) { return "Duke heard: " + input; } } diff --git a/src/main/java/duke/Launcher.java b/src/main/java/duke/Launcher.java index ae283bd8b4..2e68aff077 100644 --- a/src/main/java/duke/Launcher.java +++ b/src/main/java/duke/Launcher.java @@ -9,6 +9,6 @@ */ public class Launcher { public static void main(String[] args) { - Application.launch(Duke.class, args); + Application.launch(Main.class, args); } } diff --git a/src/main/java/duke/Main.java b/src/main/java/duke/Main.java new file mode 100644 index 0000000000..db593707b5 --- /dev/null +++ b/src/main/java/duke/Main.java @@ -0,0 +1,31 @@ +package duke; + +import java.io.IOException; + +import javafx.application.Application; +import javafx.fxml.FXMLLoader; +import javafx.scene.Scene; +import javafx.scene.layout.AnchorPane; +import javafx.stage.Stage; + +/** + * A GUI for Duke using FXML. + */ +public class Main extends Application { + + private Duke duke = new Duke(); + + @Override + public void start(Stage stage) { + try { + FXMLLoader fxmlLoader = new FXMLLoader(Main.class.getResource("/view/MainWindow.fxml")); + AnchorPane ap = fxmlLoader.load(); + Scene scene = new Scene(ap); + stage.setScene(scene); + fxmlLoader.getController().setDuke(duke); + stage.show(); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/duke/MainWindow.java b/src/main/java/duke/MainWindow.java new file mode 100644 index 0000000000..ba7337571d --- /dev/null +++ b/src/main/java/duke/MainWindow.java @@ -0,0 +1,53 @@ +package duke; + +import javafx.fxml.FXML; +import javafx.scene.control.Button; +import javafx.scene.control.ScrollPane; +import javafx.scene.control.TextField; +import javafx.scene.image.Image; +import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.VBox; + +/** + * Controller for duke.MainWindow. Provides the layout for the other controls. + * Reused from this guide. + */ +public class MainWindow extends AnchorPane { + @FXML + private ScrollPane scrollPane; + @FXML + private VBox dialogContainer; + @FXML + private TextField userInput; + @FXML + private Button sendButton; + + private Duke duke; + + private Image userImage = new Image(this.getClass().getResourceAsStream("/images/DaUser.png")); + private Image dukeImage = new Image(this.getClass().getResourceAsStream("/images/DaDuke.png")); + + @FXML + public void initialize() { + scrollPane.vvalueProperty().bind(dialogContainer.heightProperty()); + } + + public void setDuke(Duke d) { + duke = d; + } + + /** + * Creates two dialog boxes, one echoing user input and the other containing Duke's reply and then appends them to + * the dialog container. Clears the user input after processing. + */ + @FXML + private void handleUserInput() { + String input = userInput.getText(); + String response = duke.getResponse(input); + dialogContainer.getChildren().addAll( + DialogBox.getUserDialog(input, userImage), + DialogBox.getDukeDialog(response, dukeImage) + ); + userInput.clear(); + } +} \ No newline at end of file diff --git a/src/main/resources/view/DialogBox.fxml b/src/main/resources/view/DialogBox.fxml new file mode 100644 index 0000000000..ede775d4f9 --- /dev/null +++ b/src/main/resources/view/DialogBox.fxml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml new file mode 100644 index 0000000000..1518f901b2 --- /dev/null +++ b/src/main/resources/view/MainWindow.fxml @@ -0,0 +1,20 @@ + + + + + + + + + + + +