From 556af3f47a96b32898ab4cdbd65b16486a4871e8 Mon Sep 17 00:00:00 2001 From: damithc Date: Mon, 25 May 2020 00:58:18 +0800 Subject: [PATCH 01/44] Add Gradle support --- build.gradle | 41 +++++ 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, 332 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..885198fcfa --- /dev/null +++ b/build.gradle @@ -0,0 +1,41 @@ +plugins { + id 'java' + id 'application' + id 'com.github.johnrengelman.shadow' version '5.1.0' +} + +repositories { + mavenCentral() +} + +dependencies { + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.5.0' + testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.5.0' +} + +test { + useJUnitPlatform() + + testLogging { + events "passed", "skipped", "failed" + + showExceptions true + exceptionFormat "full" + showCauses true + showStackTraces true + showStandardStreams = false + } +} + +application { + mainClassName = "seedu.duke.Duke" +} + +shadowJar { + archiveBaseName = "duke" + archiveClassifier = null +} + +run{ + standardInput = System.in +} 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 c79efa683672642a009d3a7db350b3f87956fb3c Mon Sep 17 00:00:00 2001 From: Sampy147 Date: Thu, 18 Aug 2022 21:28:31 +0800 Subject: [PATCH 02/44] .gitignore: included .class files to be omitted from being committed --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index f69985ef1f..5c3663723f 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,9 @@ /out/ /*.iml +# .class files +/.class/ + # Gradle build files /.gradle/ /build/ From 327c5699fe2d35ea2d32ffaab3ce4bf3e5cebfc5 Mon Sep 17 00:00:00 2001 From: Sampy147 Date: Thu, 18 Aug 2022 23:05:42 +0800 Subject: [PATCH 03/44] Duke.java: Level-1 completed --- src/main/java/Duke.java | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 5d313334cc..9ad25df220 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -1,10 +1,26 @@ +import java.util.Scanner; + public class Duke { + private static String horizontalBorder = "_________________________________\n"; + + public static String welcomeMessage(){ + return horizontalBorder + "Hello! I'm Duke \nWhat can I do for you? \n" + horizontalBorder; + } + + public static String level1(){ + System.out.println(welcomeMessage()); + while(true) { + Scanner scan = new Scanner(System.in); + String s = scan.next(); + if (s.equals("bye")) { + return horizontalBorder + "Bye. Hope to see you again soon! \n" + horizontalBorder; + } + System.out.println(horizontalBorder + s + "\n" + horizontalBorder); + } + } + + public static void main(String[] args) { - String logo = " ____ _ \n" - + "| _ \\ _ _| | _____ \n" - + "| | | | | | | |/ / _ \\\n" - + "| |_| | |_| | < __/\n" - + "|____/ \\__,_|_|\\_\\___|\n"; - System.out.println("Hello from\n" + logo); + System.out.println(level1()); } } From a4346b2a67f7b0b685bdf4bd8fa30666387ddaef Mon Sep 17 00:00:00 2001 From: Sampy147 Date: Thu, 18 Aug 2022 23:37:47 +0800 Subject: [PATCH 04/44] Duke.java: Level-2 completed --- src/main/java/Duke.java | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 9ad25df220..f5cb009c9d 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -2,25 +2,42 @@ public class Duke { private static String horizontalBorder = "_________________________________\n"; + private String[] toDoList = new String[100]; + private int count = 0; public static String welcomeMessage(){ return horizontalBorder + "Hello! I'm Duke \nWhat can I do for you? \n" + horizontalBorder; } - public static String level1(){ + public String list(){ + String list = ""; + int localCount = 1; + while (toDoList[localCount - 1] != null){ + list += localCount + ". " + toDoList[localCount - 1] + "\n"; + localCount += 1; + } + return horizontalBorder + list + horizontalBorder; + } + + public String level2(){ System.out.println(welcomeMessage()); while(true) { Scanner scan = new Scanner(System.in); String s = scan.next(); if (s.equals("bye")) { return horizontalBorder + "Bye. Hope to see you again soon! \n" + horizontalBorder; + } else if (s.equals("list")) { + System.out.println(list()); + } else { + toDoList[count] = s; + count += 1; + System.out.println(horizontalBorder + "added: " + s + "\n" + horizontalBorder); } - System.out.println(horizontalBorder + s + "\n" + horizontalBorder); } } - public static void main(String[] args) { - System.out.println(level1()); + Duke sampleDuke = new Duke(); + System.out.println(sampleDuke.level2()); } } From df16435104ead567c234ef0778618becb2044c49 Mon Sep 17 00:00:00 2001 From: Sampy147 Date: Fri, 19 Aug 2022 13:09:07 +0800 Subject: [PATCH 05/44] Duke.java: Level-1 v1.1 improved --- src/main/java/Duke.java | 30 ++++++------------------------ 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index f5cb009c9d..5ad7565689 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -2,42 +2,24 @@ public class Duke { private static String horizontalBorder = "_________________________________\n"; - private String[] toDoList = new String[100]; - private int count = 0; public static String welcomeMessage(){ return horizontalBorder + "Hello! I'm Duke \nWhat can I do for you? \n" + horizontalBorder; } - public String list(){ - String list = ""; - int localCount = 1; - while (toDoList[localCount - 1] != null){ - list += localCount + ". " + toDoList[localCount - 1] + "\n"; - localCount += 1; - } - return horizontalBorder + list + horizontalBorder; - } - - public String level2(){ + public static String level1(){ System.out.println(welcomeMessage()); while(true) { Scanner scan = new Scanner(System.in); - String s = scan.next(); - if (s.equals("bye")) { + String s = scan.nextLine(); + if (s.toLowerCase().equals("bye")) { return horizontalBorder + "Bye. Hope to see you again soon! \n" + horizontalBorder; - } else if (s.equals("list")) { - System.out.println(list()); - } else { - toDoList[count] = s; - count += 1; - System.out.println(horizontalBorder + "added: " + s + "\n" + horizontalBorder); } + System.out.println(horizontalBorder + s + "\n" + horizontalBorder); } } public static void main(String[] args) { - Duke sampleDuke = new Duke(); - System.out.println(sampleDuke.level2()); + System.out.println(level1()); } -} +} \ No newline at end of file From fd542b81c2e6820a5d9845a0b12354b16e9006d3 Mon Sep 17 00:00:00 2001 From: Sampy147 Date: Fri, 19 Aug 2022 13:34:41 +0800 Subject: [PATCH 06/44] Duke.java: Level-2 v1.1 improved --- src/main/java/Duke.java | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 5ad7565689..1b40e1cea8 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -2,24 +2,42 @@ public class Duke { private static String horizontalBorder = "_________________________________\n"; + private String[] toDoList = new String[100]; + private int count = 0; public static String welcomeMessage(){ return horizontalBorder + "Hello! I'm Duke \nWhat can I do for you? \n" + horizontalBorder; } - public static String level1(){ + public String list(){ + String list = ""; + int localCount = 1; + while (toDoList[localCount - 1] != null){ + list += localCount + ". " + toDoList[localCount - 1] + "\n"; + localCount += 1; + } + return horizontalBorder + list + horizontalBorder; + } + + public String level2(){ System.out.println(welcomeMessage()); while(true) { Scanner scan = new Scanner(System.in); String s = scan.nextLine(); if (s.toLowerCase().equals("bye")) { return horizontalBorder + "Bye. Hope to see you again soon! \n" + horizontalBorder; + } else if (s.equals("list")) { + System.out.println(list()); + } else { + toDoList[count] = s; + count += 1; + System.out.println(horizontalBorder + "added: " + s + "\n" + horizontalBorder); } - System.out.println(horizontalBorder + s + "\n" + horizontalBorder); } } public static void main(String[] args) { - System.out.println(level1()); + Duke sampleDuke = new Duke(); + System.out.println(sampleDuke.level2()); } -} \ No newline at end of file +} From 5d410b8c074ac09ad32a47c4154423bbf30ed9f1 Mon Sep 17 00:00:00 2001 From: Sampy147 Date: Fri, 19 Aug 2022 14:39:39 +0800 Subject: [PATCH 07/44] Duke.java: Level-2 v1.2 improved --- src/main/java/Duke.java | 35 ++++++++++++++++------------------- src/main/java/Task.java | 12 ++++++++++++ src/main/java/Tasklist.java | 23 +++++++++++++++++++++++ 3 files changed, 51 insertions(+), 19 deletions(-) create mode 100644 src/main/java/Task.java create mode 100644 src/main/java/Tasklist.java diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 1b40e1cea8..e87e82484f 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -1,43 +1,40 @@ import java.util.Scanner; public class Duke { - private static String horizontalBorder = "_________________________________\n"; - private String[] toDoList = new String[100]; - private int count = 0; + private static final String horizontalBorder = "_________________________________\n"; + private Tasklist tasklist; + + private Duke(){ + this.tasklist = new Tasklist(10); + } + + private Duke(Tasklist tasklist){ + this.tasklist = tasklist; + } public static String welcomeMessage(){ return horizontalBorder + "Hello! I'm Duke \nWhat can I do for you? \n" + horizontalBorder; } - public String list(){ - String list = ""; - int localCount = 1; - while (toDoList[localCount - 1] != null){ - list += localCount + ". " + toDoList[localCount - 1] + "\n"; - localCount += 1; - } - return horizontalBorder + list + horizontalBorder; - } - public String level2(){ + public String run(){ System.out.println(welcomeMessage()); while(true) { Scanner scan = new Scanner(System.in); String s = scan.nextLine(); if (s.toLowerCase().equals("bye")) { return horizontalBorder + "Bye. Hope to see you again soon! \n" + horizontalBorder; - } else if (s.equals("list")) { - System.out.println(list()); + } else if (s.toLowerCase().equals("list")) { + System.out.println(horizontalBorder + tasklist + horizontalBorder); } else { - toDoList[count] = s; - count += 1; + tasklist.add(new Task(s)); System.out.println(horizontalBorder + "added: " + s + "\n" + horizontalBorder); } } } public static void main(String[] args) { - Duke sampleDuke = new Duke(); - System.out.println(sampleDuke.level2()); + Duke sampleDuke = new Duke(new Tasklist(100)); + System.out.println(sampleDuke.run()); } } diff --git a/src/main/java/Task.java b/src/main/java/Task.java new file mode 100644 index 0000000000..6f160304a9 --- /dev/null +++ b/src/main/java/Task.java @@ -0,0 +1,12 @@ +public class Task { + private String taskDesc; + + public Task(String taskDesc){ + this.taskDesc = taskDesc; + } + + @Override + public String toString(){ + return this.taskDesc; + } +} diff --git a/src/main/java/Tasklist.java b/src/main/java/Tasklist.java new file mode 100644 index 0000000000..63724ccbfb --- /dev/null +++ b/src/main/java/Tasklist.java @@ -0,0 +1,23 @@ +public class Tasklist { + private String[] taskArray; + private int count = 0; + + public Tasklist(int size){ + this.taskArray = new String[size]; + } + + public void add(Task task){ + this.taskArray[this.count] = task.toString(); + this.count += 1; + } + + @Override + public String toString(){ + String stringedList = ""; + for (int i = 0; i < this.count; i++) { + stringedList += (i + 1) + ". " + this.taskArray[i] + "\n"; + } + return stringedList; + } + +} From 53a12aef8890e4b2ad94cdbea20ccb6b44bce089 Mon Sep 17 00:00:00 2001 From: Sampy147 Date: Sun, 21 Aug 2022 13:40:49 +0800 Subject: [PATCH 08/44] Duke.java, Task.java, Tasklist.java: Updated with level 3 requirements --- src/main/java/Duke.java | 61 ++++++++++++++++++++++++++++++++----- src/main/java/Task.java | 23 +++++++++++--- src/main/java/Tasklist.java | 37 ++++++++++++++++++---- 3 files changed, 104 insertions(+), 17 deletions(-) diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index e87e82484f..15053eca6a 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -12,25 +12,72 @@ private Duke(Tasklist tasklist){ this.tasklist = tasklist; } - public static String welcomeMessage(){ + private static String welcomeMessage(){ return horizontalBorder + "Hello! I'm Duke \nWhat can I do for you? \n" + horizontalBorder; } + private static String byeMessage(){ + return horizontalBorder + "Bye. Hope to see you again soon! \n" + horizontalBorder; + } + + private String listContents(){ + return horizontalBorder + this.tasklist + horizontalBorder; + } + + public String addTaskMessage(String taskString) { + return horizontalBorder + "added: " + taskString + "\n" + horizontalBorder; + } + + private String markDoneMessage(int position){ + Task currentTask = this.tasklist.getTask(position); + return horizontalBorder + "Nice! I've marked this task as done: \n" + currentTask + "\n" + horizontalBorder; + } + + private String unmarkDoneMessage(int position){ + Task currentTask = this.tasklist.getTask(position); + return horizontalBorder + "OK, I've marked this task as not done yet: \n" + currentTask + "\n" + horizontalBorder; + } + + private boolean isInteger(String value) { + try { + Integer.parseInt(value); + return true; + } catch (NumberFormatException e) { + return false; + } + } public String run(){ System.out.println(welcomeMessage()); while(true) { Scanner scan = new Scanner(System.in); String s = scan.nextLine(); - if (s.toLowerCase().equals("bye")) { - return horizontalBorder + "Bye. Hope to see you again soon! \n" + horizontalBorder; - } else if (s.toLowerCase().equals("list")) { - System.out.println(horizontalBorder + tasklist + horizontalBorder); - } else { + String[] commandList = s.strip().split(" "); + String command = commandList[0]; + if (command.toLowerCase().equals("bye") && commandList.length == 1) { + System.out.println(byeMessage()); + } else if (command.toLowerCase().equals("list") && commandList.length == 1) { + System.out.println(listContents()); + } else if ((command.toLowerCase().equals("mark") || command.toLowerCase().equals("unmark")) && commandList.length > 1 && isInteger(commandList[1])) { + String taskIndex = commandList[1]; + int taskIndexNum = Integer.parseInt(taskIndex); + if (command.toLowerCase().equals("mark")) { + boolean isTaskMarked = this.tasklist.markTaskAtPos(taskIndexNum); + if (isTaskMarked){ + System.out.println(markDoneMessage(taskIndexNum)); + } + } else { + boolean isTaskUnmarked = this.tasklist.unmarkTaskAtPos(taskIndexNum); + if (isTaskUnmarked) { + System.out.println(unmarkDoneMessage(taskIndexNum)); + } + } + } else if (!s.strip().equals("")) { tasklist.add(new Task(s)); - System.out.println(horizontalBorder + "added: " + s + "\n" + horizontalBorder); + System.out.println(addTaskMessage(s)); } } + } public static void main(String[] args) { diff --git a/src/main/java/Task.java b/src/main/java/Task.java index 6f160304a9..1351c88480 100644 --- a/src/main/java/Task.java +++ b/src/main/java/Task.java @@ -1,12 +1,27 @@ + public class Task { - private String taskDesc; + protected String description; + protected boolean isDone; + + public Task(String description) { + this.description = description; + this.isDone = false; + } + + public String getStatusIcon() { + return (this.isDone ? "X" : " "); + } + + public void markAsDone() { + this.isDone = true; + } - public Task(String taskDesc){ - this.taskDesc = taskDesc; + public void unmark() { + this.isDone = false; } @Override public String toString(){ - return this.taskDesc; + return "[" + getStatusIcon() + "] " + this.description; } } diff --git a/src/main/java/Tasklist.java b/src/main/java/Tasklist.java index 63724ccbfb..d0a087abf2 100644 --- a/src/main/java/Tasklist.java +++ b/src/main/java/Tasklist.java @@ -1,23 +1,48 @@ public class Tasklist { - private String[] taskArray; + private Task[] taskArray; private int count = 0; public Tasklist(int size){ - this.taskArray = new String[size]; + this.taskArray = new Task[size]; } public void add(Task task){ - this.taskArray[this.count] = task.toString(); + this.taskArray[this.count] = task; this.count += 1; } + public Task getTask(int position) throws ArrayIndexOutOfBoundsException{ + return taskArray[position - 1]; + } + + public boolean markTaskAtPos(int position){ + try { + Task currTask = getTask(position); + currTask.markAsDone(); + return true; + } catch (ArrayIndexOutOfBoundsException | NullPointerException e) { + System.out.println("Task does not exist. Try another number between 0 and " + count + "."); + return false; + } + } + + public boolean unmarkTaskAtPos(int position){ + try { + Task currTask = getTask(position); + currTask.unmark(); + return true; + } catch (ArrayIndexOutOfBoundsException | NullPointerException e) { + System.out.println("Task does not exist. Try another number between 0 and " + count + "."); + return false; + } + } + @Override public String toString(){ String stringedList = ""; for (int i = 0; i < this.count; i++) { - stringedList += (i + 1) + ". " + this.taskArray[i] + "\n"; + stringedList += (i + 1) + ". " + this.taskArray[i].toString() + "\n"; } - return stringedList; + return "Here are the tasks in your list:\n" + stringedList; } - } From a003efa6dcb68de41cd52aa6b045f979e2c204aa Mon Sep 17 00:00:00 2001 From: Sampy147 Date: Sun, 21 Aug 2022 21:33:32 +0800 Subject: [PATCH 09/44] Duke.java,Tasklist.java modified. Deadline.java, Event.java, ToDo.java added --- src/main/java/Deadline.java | 14 ++++++ src/main/java/Duke.java | 92 ++++++++++++++++++++++++++----------- src/main/java/Event.java | 14 ++++++ src/main/java/Tasklist.java | 6 ++- src/main/java/ToDo.java | 11 +++++ 5 files changed, 109 insertions(+), 28 deletions(-) create mode 100644 src/main/java/Deadline.java create mode 100644 src/main/java/Event.java create mode 100644 src/main/java/ToDo.java diff --git a/src/main/java/Deadline.java b/src/main/java/Deadline.java new file mode 100644 index 0000000000..00e135daf6 --- /dev/null +++ b/src/main/java/Deadline.java @@ -0,0 +1,14 @@ +public class Deadline extends Task { + + protected String by; + + public Deadline(String description, String by) { + super(description); + this.by = by; + } + + @Override + public String toString() { + return "[D]" + super.toString() + " (by: " + this.by + ")"; + } +} diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 15053eca6a..f5fcd5d48b 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -1,3 +1,4 @@ +import java.util.Arrays; import java.util.Scanner; public class Duke { @@ -25,17 +26,27 @@ private String listContents(){ } public String addTaskMessage(String taskString) { - return horizontalBorder + "added: " + taskString + "\n" + horizontalBorder; + return horizontalBorder + "Got it. I've added this task:\n" + taskString + "\n" + "Now you have " + this.tasklist.getCount() + " tasks in the list.\n" + horizontalBorder; } private String markDoneMessage(int position){ - Task currentTask = this.tasklist.getTask(position); - return horizontalBorder + "Nice! I've marked this task as done: \n" + currentTask + "\n" + horizontalBorder; + boolean isTaskMarked = this.tasklist.markTaskAtPos(position); + if (isTaskMarked) { + Task currentTask = this.tasklist.getTask(position); + return horizontalBorder + "Nice! I've marked this task as done: \n" + currentTask + "\n" + horizontalBorder; + } else { + return horizontalBorder + "Task does not exist. Try another number between 1 and " + this.tasklist.getCount() + " or initialise a task first.\n" + horizontalBorder ; + } } private String unmarkDoneMessage(int position){ - Task currentTask = this.tasklist.getTask(position); - return horizontalBorder + "OK, I've marked this task as not done yet: \n" + currentTask + "\n" + horizontalBorder; + boolean isTaskUnmarked = this.tasklist.unmarkTaskAtPos(position); + if (isTaskUnmarked) { + Task currentTask = this.tasklist.getTask(position); + return horizontalBorder + "OK, I've marked this task as not done yet: \n" + currentTask + "\n" + horizontalBorder; + } else { + return horizontalBorder + "Task does not exist. Try another number between 1 and " + this.tasklist.getCount() + " or initialise a task first.\n" + horizontalBorder; + } } private boolean isInteger(String value) { @@ -47,37 +58,66 @@ private boolean isInteger(String value) { } } + private String makeToDoFromInput(String input){ + String description = input.substring("todo".length()).strip(); + if (!description.equals("")){ + ToDo newToDo = new ToDo(description); + this.tasklist.add(newToDo); + return addTaskMessage(newToDo.toString()); + } else { + return horizontalBorder + "Please use proper todo formatting: todo {task}\n" + horizontalBorder; + } + } + + private String makeDeadlineFromInput(String input){ + String[] stringArray = input.substring("deadline".length()).strip().split("/by"); + if (stringArray.length > 1) { + Deadline newDeadline = new Deadline(stringArray[0].strip(), stringArray[1].strip()); + this.tasklist.add(newDeadline); + return addTaskMessage(newDeadline.toString()); + } else { + return horizontalBorder + "Please use proper deadline formatting: deadline {task} /by {time}\n" + horizontalBorder; + } + } + + private String makeEventFromInput(String input){ + String[] stringArray = input.substring("event".length()).strip().split("/at"); + if (stringArray.length > 1) { + Event newEvent = new Event(stringArray[0].strip(), stringArray[1].strip()); + this.tasklist.add(newEvent); + return addTaskMessage(newEvent.toString()); + } else { + return horizontalBorder + "Please use proper event formatting: event {task} /at {time}\n" + horizontalBorder; + } + } + public String run(){ System.out.println(welcomeMessage()); while(true) { Scanner scan = new Scanner(System.in); String s = scan.nextLine(); String[] commandList = s.strip().split(" "); - String command = commandList[0]; - if (command.toLowerCase().equals("bye") && commandList.length == 1) { - System.out.println(byeMessage()); - } else if (command.toLowerCase().equals("list") && commandList.length == 1) { + String command = commandList[0].toLowerCase(); + if (command.equals("bye") && commandList.length == 1) { + return byeMessage(); + } else if (command.equals("list") && commandList.length == 1) { System.out.println(listContents()); - } else if ((command.toLowerCase().equals("mark") || command.toLowerCase().equals("unmark")) && commandList.length > 1 && isInteger(commandList[1])) { - String taskIndex = commandList[1]; - int taskIndexNum = Integer.parseInt(taskIndex); - if (command.toLowerCase().equals("mark")) { - boolean isTaskMarked = this.tasklist.markTaskAtPos(taskIndexNum); - if (isTaskMarked){ - System.out.println(markDoneMessage(taskIndexNum)); - } - } else { - boolean isTaskUnmarked = this.tasklist.unmarkTaskAtPos(taskIndexNum); - if (isTaskUnmarked) { - System.out.println(unmarkDoneMessage(taskIndexNum)); - } - } + } else if (command.equals("mark") && commandList.length > 1 && isInteger(commandList[1])) { + int taskIndexNum = Integer.parseInt(commandList[1]); + System.out.println(markDoneMessage(taskIndexNum)); + } else if (command.equals("unmark") && commandList.length > 1 && isInteger(commandList[1])) { + int taskIndexNum = Integer.parseInt(commandList[1]); + System.out.println(unmarkDoneMessage(taskIndexNum)); + } else if (command.equals("deadline")) { + System.out.println(makeDeadlineFromInput(s)); + } else if (command.equals("event")) { + System.out.println(makeEventFromInput(s)); + } else if (command.equals("todo")) { + System.out.println(makeToDoFromInput(s)); } else if (!s.strip().equals("")) { - tasklist.add(new Task(s)); - System.out.println(addTaskMessage(s)); + System.out.println(horizontalBorder + "Please use one of these keywords: {deadline, event, todo} followed by \\\"by\\\" and \\\"at\\\" for deadline and event tasks respectively.\n" + horizontalBorder); } } - } public static void main(String[] args) { diff --git a/src/main/java/Event.java b/src/main/java/Event.java new file mode 100644 index 0000000000..0f94de0859 --- /dev/null +++ b/src/main/java/Event.java @@ -0,0 +1,14 @@ +public class Event extends Task{ + + protected String at; + + public Event(String description, String at){ + super(description); + this.at = at; + } + + @Override + public String toString(){ + return "[E]" + super.toString() + " (at: " + this.at + " )"; + } +} diff --git a/src/main/java/Tasklist.java b/src/main/java/Tasklist.java index d0a087abf2..0a74a15739 100644 --- a/src/main/java/Tasklist.java +++ b/src/main/java/Tasklist.java @@ -15,13 +15,16 @@ public Task getTask(int position) throws ArrayIndexOutOfBoundsException{ return taskArray[position - 1]; } + public int getCount(){ + return this.count; + } + public boolean markTaskAtPos(int position){ try { Task currTask = getTask(position); currTask.markAsDone(); return true; } catch (ArrayIndexOutOfBoundsException | NullPointerException e) { - System.out.println("Task does not exist. Try another number between 0 and " + count + "."); return false; } } @@ -32,7 +35,6 @@ public boolean unmarkTaskAtPos(int position){ currTask.unmark(); return true; } catch (ArrayIndexOutOfBoundsException | NullPointerException e) { - System.out.println("Task does not exist. Try another number between 0 and " + count + "."); return false; } } diff --git a/src/main/java/ToDo.java b/src/main/java/ToDo.java new file mode 100644 index 0000000000..82a571316c --- /dev/null +++ b/src/main/java/ToDo.java @@ -0,0 +1,11 @@ +public class ToDo extends Task{ + + public ToDo(String description){ + super(description); + } + + @Override + public String toString(){ + return "[T]" + super.toString(); + } +} From 738c9a80ecbd3baf580e6b7688447a6eabecb888 Mon Sep 17 00:00:00 2001 From: Sampy147 Date: Mon, 22 Aug 2022 22:55:14 +0800 Subject: [PATCH 10/44] Automation testing done --- text-ui-test/EXPECTED.TXT | 48 ++++++++++++++++++++++++++++++++++----- text-ui-test/input.txt | 7 ++++++ 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 657e74f6e7..3beafa787c 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,7 +1,43 @@ -Hello from - ____ _ -| _ \ _ _| | _____ -| | | | | | | |/ / _ \ -| |_| | |_| | < __/ -|____/ \__,_|_|\_\___| +_________________________________ +Hello! I'm Duke +What can I do for you? +_________________________________ + +_________________________________ +Got it. I've added this task: +[E][ ] Tuition (at: Mon 2-3pm ) +Now you have 1 tasks in the list. +_________________________________ + +_________________________________ +Got it. I've added this task: +[T][ ] Eat dinner +Now you have 2 tasks in the list. +_________________________________ + +_________________________________ +Got it. I've added this task: +[D][ ] Math assignment submission (by: Friday) +Now you have 3 tasks in the list. +_________________________________ + +_________________________________ +Nice! I've marked this task as done: +[T][X] Eat dinner +_________________________________ + +_________________________________ +Task does not exist. Try another number between 1 and 3 or initialise a task first. +_________________________________ + +_________________________________ +Here are the tasks in your list: +1. [E][ ] Tuition (at: Mon 2-3pm ) +2. [T][X] Eat dinner +3. [D][ ] Math assignment submission (by: Friday) +_________________________________ + +_________________________________ +Bye. Hope to see you again soon! +_________________________________ diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index e69de29bb2..ec12397023 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -0,0 +1,7 @@ +event Tuition /at Mon 2-3pm +todo Eat dinner +deadline Math assignment submission /by Friday +mark 2 +unmark 0 +list +bye \ No newline at end of file From 17bb08ce86249275bc7e25f6200bde83ebcf16da Mon Sep 17 00:00:00 2001 From: Sampy147 Date: Mon, 22 Aug 2022 23:57:31 +0800 Subject: [PATCH 11/44] Level-5 handle errors done --- src/main/java/DukeException.java | 2 ++ text-ui-test/EXPECTED.TXT | 10 +++++++++- text-ui-test/input.txt | 2 ++ 3 files changed, 13 insertions(+), 1 deletion(-) 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..882a052746 --- /dev/null +++ b/src/main/java/DukeException.java @@ -0,0 +1,2 @@ +package PACKAGE_NAME;public class DukeException { +} diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 3beafa787c..b195e136b5 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -21,13 +21,17 @@ Got it. I've added this task: Now you have 3 tasks in the list. _________________________________ +_________________________________ +☹ OOPS!!! Please use one of these keywords: {deadline, event, todo} followed by \"by\" and \"at\" for deadline and event tasks respectively. +_________________________________ + _________________________________ Nice! I've marked this task as done: [T][X] Eat dinner _________________________________ _________________________________ -Task does not exist. Try another number between 1 and 3 or initialise a task first. +☹ OOPS!!! Task does not exist. Try another number between 1 and 3 _________________________________ _________________________________ @@ -37,6 +41,10 @@ Here are the tasks in your list: 3. [D][ ] Math assignment submission (by: Friday) _________________________________ +_________________________________ +☹ OOPS!!! Please use proper deadline formatting: deadline {task} /by {time} +_________________________________ + _________________________________ Bye. Hope to see you again soon! _________________________________ diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index ec12397023..ebe82d6bb4 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -1,7 +1,9 @@ event Tuition /at Mon 2-3pm todo Eat dinner deadline Math assignment submission /by Friday +make up bed mark 2 unmark 0 list +deadline Math assignment 2 bye \ No newline at end of file From 951291978af177775808c460be7b28c26abea8d2 Mon Sep 17 00:00:00 2001 From: Sampy147 Date: Tue, 23 Aug 2022 01:26:49 +0800 Subject: [PATCH 12/44] Level-6 done --- src/main/java/Duke.java | 136 +++++++++++++++++++++---------- src/main/java/DukeException.java | 5 +- src/main/java/Tasklist.java | 27 ++++-- 3 files changed, 115 insertions(+), 53 deletions(-) diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index f5fcd5d48b..29874c79b8 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -1,12 +1,18 @@ -import java.util.Arrays; +import java.util.ArrayList; import java.util.Scanner; public class Duke { private static final String horizontalBorder = "_________________________________\n"; + private static final String INVALID_TODO_INPUT = " ☹ OOPS!!! The description of a todo cannot be empty.\n"; + private static final String INVALID_DEADLINE_INPUT = "☹ OOPS!!! Please use proper deadline formatting: deadline {task} /by {time}\n"; + private static final String INVALID_EVENT_INPUT = "☹ OOPS!!! Please use proper event formatting: event {task} /at {time}\n"; + private static final String INVALID_ACCESS_EMPTY_TASKLIST = "☹ OOPS!!! Task does not exist. Initialise a task first, then try again\n"; + private static final String INVALID_USER_INPUT = "☹ OOPS!!! Please use one of these keywords: {deadline, event, todo} followed by \\\"by\\\" and \\\"at\\\" for deadline and event tasks respectively.\n"; + private Tasklist tasklist; private Duke(){ - this.tasklist = new Tasklist(10); + this.tasklist = new Tasklist(); } private Duke(Tasklist tasklist){ @@ -14,11 +20,11 @@ private Duke(Tasklist tasklist){ } private static String welcomeMessage(){ - return horizontalBorder + "Hello! I'm Duke \nWhat can I do for you? \n" + horizontalBorder; + return horizontalBorder + "Hello! I'm Duke\nWhat can I do for you?\n" + horizontalBorder; } private static String byeMessage(){ - return horizontalBorder + "Bye. Hope to see you again soon! \n" + horizontalBorder; + return horizontalBorder + "Bye. Hope to see you again soon!\n" + horizontalBorder; } private String listContents(){ @@ -29,23 +35,44 @@ public String addTaskMessage(String taskString) { return horizontalBorder + "Got it. I've added this task:\n" + taskString + "\n" + "Now you have " + this.tasklist.getCount() + " tasks in the list.\n" + horizontalBorder; } - private String markDoneMessage(int position){ + public String taskNotFoundMessage(){ + return "☹ OOPS!!! Task does not exist. Try another number between 1 and " + this.tasklist.getCount() + "\n"; + } + + private String markDoneMessage(int position) throws DukeException{ boolean isTaskMarked = this.tasklist.markTaskAtPos(position); if (isTaskMarked) { Task currentTask = this.tasklist.getTask(position); - return horizontalBorder + "Nice! I've marked this task as done: \n" + currentTask + "\n" + horizontalBorder; + return horizontalBorder + "Nice! I've marked this task as done:\n" + currentTask + "\n" + horizontalBorder; + } else if (this.tasklist.getCount() == 0) { + throw new DukeException(INVALID_ACCESS_EMPTY_TASKLIST); } else { - return horizontalBorder + "Task does not exist. Try another number between 1 and " + this.tasklist.getCount() + " or initialise a task first.\n" + horizontalBorder ; + throw new DukeException(taskNotFoundMessage()); } } - private String unmarkDoneMessage(int position){ + private String unmarkDoneMessage(int position) throws DukeException { boolean isTaskUnmarked = this.tasklist.unmarkTaskAtPos(position); if (isTaskUnmarked) { Task currentTask = this.tasklist.getTask(position); - return horizontalBorder + "OK, I've marked this task as not done yet: \n" + currentTask + "\n" + horizontalBorder; + return horizontalBorder + "OK, I've marked this task as not done yet:\n" + currentTask + "\n" + horizontalBorder; + } else if (this.tasklist.getCount() == 0) { + throw new DukeException(INVALID_ACCESS_EMPTY_TASKLIST); } else { - return horizontalBorder + "Task does not exist. Try another number between 1 and " + this.tasklist.getCount() + " or initialise a task first.\n" + horizontalBorder; + throw new DukeException(taskNotFoundMessage()); + } + } + + private String deleteTaskMessage(int position) throws DukeException { + try { + Task deletedTask = this.tasklist.deleteTaskAtPos(position); + return horizontalBorder + "Noted. I've removed this task:\n" + deletedTask + "\n" + "" + "Now you have " + this.tasklist.getCount() + " tasks in the list.\n" + horizontalBorder; + } catch (IndexOutOfBoundsException e){ + if (this.tasklist.getCount() == 0){ + throw new DukeException(INVALID_ACCESS_EMPTY_TASKLIST); + } else { + throw new DukeException(taskNotFoundMessage()); + } } } @@ -58,70 +85,93 @@ private boolean isInteger(String value) { } } - private String makeToDoFromInput(String input){ + private String makeToDoFromInput(String input) throws DukeException { String description = input.substring("todo".length()).strip(); - if (!description.equals("")){ + if (!description.equals("")) { ToDo newToDo = new ToDo(description); this.tasklist.add(newToDo); return addTaskMessage(newToDo.toString()); } else { - return horizontalBorder + "Please use proper todo formatting: todo {task}\n" + horizontalBorder; + throw new DukeException(INVALID_TODO_INPUT); } } - private String makeDeadlineFromInput(String input){ + private String makeDeadlineFromInput(String input) throws DukeException { String[] stringArray = input.substring("deadline".length()).strip().split("/by"); if (stringArray.length > 1) { Deadline newDeadline = new Deadline(stringArray[0].strip(), stringArray[1].strip()); this.tasklist.add(newDeadline); return addTaskMessage(newDeadline.toString()); } else { - return horizontalBorder + "Please use proper deadline formatting: deadline {task} /by {time}\n" + horizontalBorder; + throw new DukeException(INVALID_DEADLINE_INPUT); } } - private String makeEventFromInput(String input){ + private String makeEventFromInput(String input) throws DukeException{ String[] stringArray = input.substring("event".length()).strip().split("/at"); if (stringArray.length > 1) { Event newEvent = new Event(stringArray[0].strip(), stringArray[1].strip()); this.tasklist.add(newEvent); return addTaskMessage(newEvent.toString()); } else { - return horizontalBorder + "Please use proper event formatting: event {task} /at {time}\n" + horizontalBorder; + throw new DukeException(INVALID_EVENT_INPUT); } } - public String run(){ + public void run(){ System.out.println(welcomeMessage()); - while(true) { - Scanner scan = new Scanner(System.in); - String s = scan.nextLine(); - String[] commandList = s.strip().split(" "); - String command = commandList[0].toLowerCase(); - if (command.equals("bye") && commandList.length == 1) { - return byeMessage(); - } else if (command.equals("list") && commandList.length == 1) { - System.out.println(listContents()); - } else if (command.equals("mark") && commandList.length > 1 && isInteger(commandList[1])) { - int taskIndexNum = Integer.parseInt(commandList[1]); - System.out.println(markDoneMessage(taskIndexNum)); - } else if (command.equals("unmark") && commandList.length > 1 && isInteger(commandList[1])) { - int taskIndexNum = Integer.parseInt(commandList[1]); - System.out.println(unmarkDoneMessage(taskIndexNum)); - } else if (command.equals("deadline")) { - System.out.println(makeDeadlineFromInput(s)); - } else if (command.equals("event")) { - System.out.println(makeEventFromInput(s)); - } else if (command.equals("todo")) { - System.out.println(makeToDoFromInput(s)); - } else if (!s.strip().equals("")) { - System.out.println(horizontalBorder + "Please use one of these keywords: {deadline, event, todo} followed by \\\"by\\\" and \\\"at\\\" for deadline and event tasks respectively.\n" + horizontalBorder); + Scanner scan = new Scanner(System.in); + String s = scan.nextLine(); + boolean exitNow = false; + while(!exitNow) { + try { + String[] commandList = s.strip().split(" "); + String command = commandList[0].toLowerCase(); + if (command.equals("bye") && commandList.length == 1) { + exitNow = true; + System.out.println(byeMessage()); + } else if (command.equals("list") && commandList.length == 1) { + System.out.println(listContents()); + } else if (command.equals("mark") && commandList.length > 1 && isInteger(commandList[1])) { + int taskIndexNum = Integer.parseInt(commandList[1]); + System.out.println(markDoneMessage(taskIndexNum)); + } else if (command.equals("unmark") && commandList.length > 1 && isInteger(commandList[1])) { + int taskIndexNum = Integer.parseInt(commandList[1]); + System.out.println(unmarkDoneMessage(taskIndexNum)); + } else if (command.equals("deadline")) { + System.out.println(makeDeadlineFromInput(s)); + } else if (command.equals("event")) { + System.out.println(makeEventFromInput(s)); + } else if (command.equals("todo")) { + System.out.println(makeToDoFromInput(s)); + } else if (command.equals("delete") && commandList.length > 1 && isInteger(commandList[1])){ + int taskIndexNum = Integer.parseInt(commandList[1]); + System.out.println(deleteTaskMessage(taskIndexNum)); + } else if (!s.strip().equals("")) { + System.out.println(horizontalBorder + INVALID_USER_INPUT + horizontalBorder); + } + } catch (DukeException e) { + System.out.println(horizontalBorder + e.getMessage() + horizontalBorder); + } finally { + if (!exitNow) { + s = scan.nextLine(); + } } } + scan.close(); } + public static void main(String[] args) { - Duke sampleDuke = new Duke(new Tasklist(100)); - System.out.println(sampleDuke.run()); + Duke sampleDuke = new Duke(new Tasklist()); + sampleDuke.run(); +// ArrayList a = new ArrayList<>(); +// a.add("lol"); +// a.add("gan"); +// a.add("lasso"); +// a.remove(1); +// for (int i = 0; i < a.size(); i++){ +// System.out.println(a.get(i)); +// } } } diff --git a/src/main/java/DukeException.java b/src/main/java/DukeException.java index 882a052746..adbe7ebd0a 100644 --- a/src/main/java/DukeException.java +++ b/src/main/java/DukeException.java @@ -1,2 +1,5 @@ -package PACKAGE_NAME;public class DukeException { +public class DukeException extends Exception{ + public DukeException(String message){ + super(message); + } } diff --git a/src/main/java/Tasklist.java b/src/main/java/Tasklist.java index 0a74a15739..f6ecd5d6b9 100644 --- a/src/main/java/Tasklist.java +++ b/src/main/java/Tasklist.java @@ -1,18 +1,20 @@ +import java.util.ArrayList; + public class Tasklist { - private Task[] taskArray; + private ArrayList taskArray; private int count = 0; - public Tasklist(int size){ - this.taskArray = new Task[size]; + public Tasklist(){ + this.taskArray = new ArrayList<>(); } public void add(Task task){ - this.taskArray[this.count] = task; + this.taskArray.add(task); this.count += 1; } - public Task getTask(int position) throws ArrayIndexOutOfBoundsException{ - return taskArray[position - 1]; + public Task getTask(int position) throws IndexOutOfBoundsException{ + return taskArray.get(position - 1); } public int getCount(){ @@ -24,7 +26,7 @@ public boolean markTaskAtPos(int position){ Task currTask = getTask(position); currTask.markAsDone(); return true; - } catch (ArrayIndexOutOfBoundsException | NullPointerException e) { + } catch (IndexOutOfBoundsException e) { return false; } } @@ -34,16 +36,23 @@ public boolean unmarkTaskAtPos(int position){ Task currTask = getTask(position); currTask.unmark(); return true; - } catch (ArrayIndexOutOfBoundsException | NullPointerException e) { + } catch (IndexOutOfBoundsException e) { return false; } } + public Task deleteTaskAtPos(int position) throws IndexOutOfBoundsException { + Task deletedTask = getTask(position); + this.taskArray.remove(position - 1); + this.count -= 1; + return deletedTask; + } + @Override public String toString(){ String stringedList = ""; for (int i = 0; i < this.count; i++) { - stringedList += (i + 1) + ". " + this.taskArray[i].toString() + "\n"; + stringedList += (i + 1) + ". " + getTask(i + 1).toString() + "\n"; } return "Here are the tasks in your list:\n" + stringedList; } From 83446a2ce2e35ee5b3498b989b5ed75283a1ea48 Mon Sep 17 00:00:00 2001 From: Sampy147 Date: Sat, 3 Sep 2022 17:50:46 +0800 Subject: [PATCH 13/44] Level-7 completed --- src/main/java/{Tasklist.java => TaskList.java} | 0 src/main/java/simpleDatabase.java | 2 ++ 2 files changed, 2 insertions(+) rename src/main/java/{Tasklist.java => TaskList.java} (100%) create mode 100644 src/main/java/simpleDatabase.java diff --git a/src/main/java/Tasklist.java b/src/main/java/TaskList.java similarity index 100% rename from src/main/java/Tasklist.java rename to src/main/java/TaskList.java diff --git a/src/main/java/simpleDatabase.java b/src/main/java/simpleDatabase.java new file mode 100644 index 0000000000..c600885eb7 --- /dev/null +++ b/src/main/java/simpleDatabase.java @@ -0,0 +1,2 @@ +package PACKAGE_NAME;public class simpleDatabase { +} From d777be2660bd2685b4f4b3dfad7dd464a86278e2 Mon Sep 17 00:00:00 2001 From: Sampy147 Date: Sat, 3 Sep 2022 17:53:45 +0800 Subject: [PATCH 14/44] level-7 completed --- duke.txt | 0 src/main/java/Deadline.java | 5 ++ src/main/java/Duke.java | 80 ++++++++++--------- src/main/java/Event.java | 7 +- src/main/java/Task.java | 5 +- src/main/java/TaskList.java | 16 +++- src/main/java/ToDo.java | 5 ++ src/main/java/simpleDatabase.java | 125 +++++++++++++++++++++++++++++- 8 files changed, 200 insertions(+), 43 deletions(-) create mode 100644 duke.txt diff --git a/duke.txt b/duke.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/main/java/Deadline.java b/src/main/java/Deadline.java index 00e135daf6..e0717035f5 100644 --- a/src/main/java/Deadline.java +++ b/src/main/java/Deadline.java @@ -11,4 +11,9 @@ public Deadline(String description, String by) { public String toString() { return "[D]" + super.toString() + " (by: " + this.by + ")"; } + + @Override + public String toSimpleString() { + return "D | " + super.toSimpleString() + " | " + this.by; + } } diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 29874c79b8..d596769e76 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -1,50 +1,58 @@ -import java.util.ArrayList; import java.util.Scanner; public class Duke { - private static final String horizontalBorder = "_________________________________\n"; + private static final String SAVED_PATH = "data/duke.txt"; + private static final String HORIZONTAL_BORDER = "_________________________________\n"; private static final String INVALID_TODO_INPUT = " ☹ OOPS!!! The description of a todo cannot be empty.\n"; private static final String INVALID_DEADLINE_INPUT = "☹ OOPS!!! Please use proper deadline formatting: deadline {task} /by {time}\n"; private static final String INVALID_EVENT_INPUT = "☹ OOPS!!! Please use proper event formatting: event {task} /at {time}\n"; private static final String INVALID_ACCESS_EMPTY_TASKLIST = "☹ OOPS!!! Task does not exist. Initialise a task first, then try again\n"; private static final String INVALID_USER_INPUT = "☹ OOPS!!! Please use one of these keywords: {deadline, event, todo} followed by \\\"by\\\" and \\\"at\\\" for deadline and event tasks respectively.\n"; - private Tasklist tasklist; + private simpleDatabase database; + private TaskList taskList; private Duke(){ - this.tasklist = new Tasklist(); + this.database = new simpleDatabase(SAVED_PATH); + try { + this.taskList = this.database.getMemory(); + } catch (DukeException e) { + System.out.println(errorMessage(e.getMessage() + "\n")); + System.exit(0); + } } - private Duke(Tasklist tasklist){ - this.tasklist = tasklist; + private static String errorMessage(String errorMessage) { + return HORIZONTAL_BORDER + errorMessage + HORIZONTAL_BORDER; } private static String welcomeMessage(){ - return horizontalBorder + "Hello! I'm Duke\nWhat can I do for you?\n" + horizontalBorder; + return HORIZONTAL_BORDER + "Hello! I'm Duke\nWhat can I do for you?\n" + HORIZONTAL_BORDER; } private static String byeMessage(){ - return horizontalBorder + "Bye. Hope to see you again soon!\n" + horizontalBorder; + return HORIZONTAL_BORDER + "Bye. Hope to see you again soon!\n" + HORIZONTAL_BORDER; } private String listContents(){ - return horizontalBorder + this.tasklist + horizontalBorder; + return HORIZONTAL_BORDER + this.taskList + HORIZONTAL_BORDER; } public String addTaskMessage(String taskString) { - return horizontalBorder + "Got it. I've added this task:\n" + taskString + "\n" + "Now you have " + this.tasklist.getCount() + " tasks in the list.\n" + horizontalBorder; + return HORIZONTAL_BORDER + "Got it. I've added this task:\n" + taskString + "\n" + "Now you have " + this.taskList.getCount() + " tasks in the list.\n" + HORIZONTAL_BORDER; } public String taskNotFoundMessage(){ - return "☹ OOPS!!! Task does not exist. Try another number between 1 and " + this.tasklist.getCount() + "\n"; + return "☹ OOPS!!! Task does not exist. Try another number between 1 and " + this.taskList.getCount() + "\n"; } private String markDoneMessage(int position) throws DukeException{ - boolean isTaskMarked = this.tasklist.markTaskAtPos(position); + boolean isTaskMarked = this.taskList.markTaskAtPos(position); if (isTaskMarked) { - Task currentTask = this.tasklist.getTask(position); - return horizontalBorder + "Nice! I've marked this task as done:\n" + currentTask + "\n" + horizontalBorder; - } else if (this.tasklist.getCount() == 0) { + Task currentTask = this.taskList.getTask(position); + database.save(this.taskList); + return HORIZONTAL_BORDER + "Nice! I've marked this task as done:\n" + currentTask + "\n" + HORIZONTAL_BORDER; + } else if (this.taskList.getCount() == 0) { throw new DukeException(INVALID_ACCESS_EMPTY_TASKLIST); } else { throw new DukeException(taskNotFoundMessage()); @@ -52,11 +60,12 @@ private String markDoneMessage(int position) throws DukeException{ } private String unmarkDoneMessage(int position) throws DukeException { - boolean isTaskUnmarked = this.tasklist.unmarkTaskAtPos(position); + boolean isTaskUnmarked = this.taskList.unmarkTaskAtPos(position); if (isTaskUnmarked) { - Task currentTask = this.tasklist.getTask(position); - return horizontalBorder + "OK, I've marked this task as not done yet:\n" + currentTask + "\n" + horizontalBorder; - } else if (this.tasklist.getCount() == 0) { + Task currentTask = this.taskList.getTask(position); + database.save(this.taskList); + return HORIZONTAL_BORDER + "OK, I've marked this task as not done yet:\n" + currentTask + "\n" + HORIZONTAL_BORDER; + } else if (this.taskList.getCount() == 0) { throw new DukeException(INVALID_ACCESS_EMPTY_TASKLIST); } else { throw new DukeException(taskNotFoundMessage()); @@ -65,10 +74,11 @@ private String unmarkDoneMessage(int position) throws DukeException { private String deleteTaskMessage(int position) throws DukeException { try { - Task deletedTask = this.tasklist.deleteTaskAtPos(position); - return horizontalBorder + "Noted. I've removed this task:\n" + deletedTask + "\n" + "" + "Now you have " + this.tasklist.getCount() + " tasks in the list.\n" + horizontalBorder; + Task deletedTask = this.taskList.deleteTaskAtPos(position); + database.save(this.taskList); + return HORIZONTAL_BORDER + "Noted. I've removed this task:\n" + deletedTask + "\n" + "" + "Now you have " + this.taskList.getCount() + " tasks in the list.\n" + HORIZONTAL_BORDER; } catch (IndexOutOfBoundsException e){ - if (this.tasklist.getCount() == 0){ + if (this.taskList.getCount() == 0){ throw new DukeException(INVALID_ACCESS_EMPTY_TASKLIST); } else { throw new DukeException(taskNotFoundMessage()); @@ -89,7 +99,8 @@ private String makeToDoFromInput(String input) throws DukeException { String description = input.substring("todo".length()).strip(); if (!description.equals("")) { ToDo newToDo = new ToDo(description); - this.tasklist.add(newToDo); + this.taskList.add(newToDo); + database.save(newToDo.toSimpleString()); return addTaskMessage(newToDo.toString()); } else { throw new DukeException(INVALID_TODO_INPUT); @@ -100,18 +111,20 @@ private String makeDeadlineFromInput(String input) throws DukeException { String[] stringArray = input.substring("deadline".length()).strip().split("/by"); if (stringArray.length > 1) { Deadline newDeadline = new Deadline(stringArray[0].strip(), stringArray[1].strip()); - this.tasklist.add(newDeadline); + this.taskList.add(newDeadline); + database.save(newDeadline.toSimpleString()); return addTaskMessage(newDeadline.toString()); } else { throw new DukeException(INVALID_DEADLINE_INPUT); } } - private String makeEventFromInput(String input) throws DukeException{ + private String makeEventFromInput(String input) throws DukeException { String[] stringArray = input.substring("event".length()).strip().split("/at"); if (stringArray.length > 1) { Event newEvent = new Event(stringArray[0].strip(), stringArray[1].strip()); - this.tasklist.add(newEvent); + this.taskList.add(newEvent); + database.save(newEvent.toSimpleString()); return addTaskMessage(newEvent.toString()); } else { throw new DukeException(INVALID_EVENT_INPUT); @@ -148,10 +161,10 @@ public void run(){ int taskIndexNum = Integer.parseInt(commandList[1]); System.out.println(deleteTaskMessage(taskIndexNum)); } else if (!s.strip().equals("")) { - System.out.println(horizontalBorder + INVALID_USER_INPUT + horizontalBorder); + System.out.println(HORIZONTAL_BORDER + INVALID_USER_INPUT + HORIZONTAL_BORDER); } } catch (DukeException e) { - System.out.println(horizontalBorder + e.getMessage() + horizontalBorder); + System.out.println(HORIZONTAL_BORDER + e.getMessage() + HORIZONTAL_BORDER); } finally { if (!exitNow) { s = scan.nextLine(); @@ -161,17 +174,8 @@ public void run(){ scan.close(); } - public static void main(String[] args) { - Duke sampleDuke = new Duke(new Tasklist()); + Duke sampleDuke = new Duke(); sampleDuke.run(); -// ArrayList a = new ArrayList<>(); -// a.add("lol"); -// a.add("gan"); -// a.add("lasso"); -// a.remove(1); -// for (int i = 0; i < a.size(); i++){ -// System.out.println(a.get(i)); -// } } } diff --git a/src/main/java/Event.java b/src/main/java/Event.java index 0f94de0859..283e1b79be 100644 --- a/src/main/java/Event.java +++ b/src/main/java/Event.java @@ -9,6 +9,11 @@ public Event(String description, String at){ @Override public String toString(){ - return "[E]" + super.toString() + " (at: " + this.at + " )"; + return "[E]" + super.toString() + " (at: " + this.at + ")"; + } + + @Override + public String toSimpleString() { + return "E | " + super.toSimpleString() + " | " + this.at; } } diff --git a/src/main/java/Task.java b/src/main/java/Task.java index 1351c88480..4be6604cb7 100644 --- a/src/main/java/Task.java +++ b/src/main/java/Task.java @@ -1,4 +1,3 @@ - public class Task { protected String description; protected boolean isDone; @@ -24,4 +23,8 @@ public void unmark() { public String toString(){ return "[" + getStatusIcon() + "] " + this.description; } + + public String toSimpleString() { + return (getStatusIcon().equals("X") ? "1" : "0") + " | " + this.description; + } } diff --git a/src/main/java/TaskList.java b/src/main/java/TaskList.java index f6ecd5d6b9..d6cf3e750e 100644 --- a/src/main/java/TaskList.java +++ b/src/main/java/TaskList.java @@ -1,13 +1,17 @@ import java.util.ArrayList; -public class Tasklist { +public class TaskList { private ArrayList taskArray; private int count = 0; - public Tasklist(){ + public TaskList(){ this.taskArray = new ArrayList<>(); } + public TaskList(ArrayList taskArray) { + this.taskArray = taskArray; + } + public void add(Task task){ this.taskArray.add(task); this.count += 1; @@ -48,6 +52,14 @@ public Task deleteTaskAtPos(int position) throws IndexOutOfBoundsException { return deletedTask; } + public String toSimpleStrings() { + String stringedList = ""; + for (int i = 0; i < this.count; i++) { + stringedList += getTask(i + 1).toSimpleString() + "\n"; + } + return stringedList; + } + @Override public String toString(){ String stringedList = ""; diff --git a/src/main/java/ToDo.java b/src/main/java/ToDo.java index 82a571316c..9efe8310fd 100644 --- a/src/main/java/ToDo.java +++ b/src/main/java/ToDo.java @@ -8,4 +8,9 @@ public ToDo(String description){ public String toString(){ return "[T]" + super.toString(); } + + @Override + public String toSimpleString() { + return "T | " + super.toSimpleString(); + } } diff --git a/src/main/java/simpleDatabase.java b/src/main/java/simpleDatabase.java index c600885eb7..e0e1c59d6d 100644 --- a/src/main/java/simpleDatabase.java +++ b/src/main/java/simpleDatabase.java @@ -1,2 +1,125 @@ -package PACKAGE_NAME;public class simpleDatabase { +import java.io.FileWriter; +import java.io.IOException; +import java.io.File; +import java.io.FileNotFoundException; +import java.util.Arrays; +import java.util.Scanner; + + +public class simpleDatabase { + private static final String FILE_NOT_FOUND = "☹ Sorry , the file cannot be found.\n"; + private static final String FILE_READ_ERROR = "☹ Sorry , there is an error when reading the file.\n"; + + private final String filePath; + + public simpleDatabase(String filePath){ + this.filePath = filePath; + File f = new File(filePath); + } + + public TaskList getMemory() throws DukeException{ + TaskList taskList = new TaskList(); + try { + File localFile = new File(this.filePath); + Scanner s = new Scanner(localFile); + while (s.hasNext()) { + String taskString = s.nextLine(); + if (taskString.strip().equals("")) { + continue; + } + taskList.add(makeTask(taskString)); + } + } catch (FileNotFoundException e) { + makeNewFile(this.filePath); + } + return taskList; + } + + private void makeNewFile(String filePath) throws DukeException { + String[] pathArray = filePath.split("/"); + String fileName = pathArray[pathArray.length - 1]; + String[] directories = Arrays.copyOfRange(pathArray, 0, pathArray.length - 1); + String directoryPath = String.join("/", directories); + File directory = new File(directoryPath); + File newFile = new File(fileName); + try { + directory.mkdirs(); + newFile.createNewFile(); + } catch (Exception e) { + throw new DukeException("Error in creating memory space"); + } + } + + + private Event makeEvent(String markIndex, String description, String at) { + Event newEvent = new Event(description, at); + if (markIndex.equals("1")) { + newEvent.markAsDone(); + return newEvent; + } + return newEvent; + } + + private Deadline makeDeadline(String markIndex, String description, String by) { + Deadline newDeadline = new Deadline(description, by); + if (markIndex.strip().equals("1")) { + newDeadline.markAsDone(); + return newDeadline; + } + return newDeadline; + } + + private ToDo makeToDo(String markIndex, String description){ + ToDo newToDo = new ToDo(description); + if (markIndex.strip().equals("1")) { + newToDo.markAsDone(); + return newToDo; + } + return newToDo; + } + + public Task makeTask(String taskString) throws DukeException { + Task newTask; + try { + String[] taskSegments = taskString.split("\\|"); + switch (taskSegments[0].strip()) { + case "E": + newTask = makeEvent(taskSegments[1].strip(), taskSegments[2].strip(), taskSegments[3].strip()); + break; + case "D": + newTask = makeDeadline(taskSegments[1].strip(), taskSegments[2].strip(), taskSegments[3].strip()); + break; + case "T": + newTask = makeToDo(taskSegments[1].strip(), taskSegments[2].strip()); + break; + default: + throw new DukeException(FILE_READ_ERROR); + } + } catch (ArrayIndexOutOfBoundsException e) { + throw new DukeException(FILE_READ_ERROR); + } + return newTask; + } + + public void save(String taskString) throws DukeException { + try { + FileWriter fw = new FileWriter(this.filePath, true); + fw.write(taskString + "\n"); + fw.close(); + } catch (IOException e) { + throw new DukeException(FILE_NOT_FOUND); + } + } + + public void save(TaskList taskList) throws DukeException { + try { + FileWriter fw = new FileWriter(this.filePath); + for (int i = 1; i <= taskList.getCount(); i++) { + fw.write(taskList.getTask(i).toSimpleString() + "\n"); + } + fw.close(); + } catch (IOException e) { + throw new DukeException(FILE_NOT_FOUND); + } + } } From 1a2c9976451810360c608ece0886b45bfdc57f6c Mon Sep 17 00:00:00 2001 From: Sampy147 Date: Sat, 3 Sep 2022 19:32:25 +0800 Subject: [PATCH 15/44] Level-7 completed --- src/main/java/{TaskList.java => Tasklist.java} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/main/java/{TaskList.java => Tasklist.java} (100%) diff --git a/src/main/java/TaskList.java b/src/main/java/Tasklist.java similarity index 100% rename from src/main/java/TaskList.java rename to src/main/java/Tasklist.java From 0b16da0d0cf47973c081d5392740713b4741d480 Mon Sep 17 00:00:00 2001 From: Sampy147 Date: Sat, 3 Sep 2022 19:33:31 +0800 Subject: [PATCH 16/44] Level-7 completed --- src/main/java/{Tasklist.java => TaskList.java} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/main/java/{Tasklist.java => TaskList.java} (100%) diff --git a/src/main/java/Tasklist.java b/src/main/java/TaskList.java similarity index 100% rename from src/main/java/Tasklist.java rename to src/main/java/TaskList.java From 32500d19d4eec2235e3b47619403a8826da1829a Mon Sep 17 00:00:00 2001 From: Sampy147 Date: Tue, 6 Sep 2022 14:20:22 +0800 Subject: [PATCH 17/44] Level-8 completed --- src/main/java/{Tasklist.java => TaskList.java} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/main/java/{Tasklist.java => TaskList.java} (100%) diff --git a/src/main/java/Tasklist.java b/src/main/java/TaskList.java similarity index 100% rename from src/main/java/Tasklist.java rename to src/main/java/TaskList.java From 70bb7e5ecccf9b94a6b4dac7d3f864429657898e Mon Sep 17 00:00:00 2001 From: Sampy147 Date: Tue, 6 Sep 2022 14:20:55 +0800 Subject: [PATCH 18/44] Level-8 completed --- src/main/java/Deadline.java | 8 +++-- src/main/java/Duke.java | 70 ++++++++++++++++++++----------------- src/main/java/Task.java | 2 +- src/main/java/TaskList.java | 4 +-- 4 files changed, 45 insertions(+), 39 deletions(-) diff --git a/src/main/java/Deadline.java b/src/main/java/Deadline.java index 00e135daf6..6c83bd1c3b 100644 --- a/src/main/java/Deadline.java +++ b/src/main/java/Deadline.java @@ -1,14 +1,16 @@ +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; public class Deadline extends Task { - protected String by; + protected LocalDate by; - public Deadline(String description, String by) { + public Deadline(String description, LocalDate by) { super(description); this.by = by; } @Override public String toString() { - return "[D]" + super.toString() + " (by: " + this.by + ")"; + return "[D]" + super.toString() + " (by: " + this.by.format(DateTimeFormatter.ofPattern("MMM dd yyyy")) + ")"; } } diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 29874c79b8..add11133f8 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -1,63 +1,64 @@ -import java.util.ArrayList; import java.util.Scanner; +import java.time.LocalDate; public class Duke { private static final String horizontalBorder = "_________________________________\n"; private static final String INVALID_TODO_INPUT = " ☹ OOPS!!! The description of a todo cannot be empty.\n"; private static final String INVALID_DEADLINE_INPUT = "☹ OOPS!!! Please use proper deadline formatting: deadline {task} /by {time}\n"; + private static final String INVALID_DATE_FORMAT = "☹ OOPS!!! Please indicate your date as YYYY-MM-DD (e.g 2019-12-09)\n"; private static final String INVALID_EVENT_INPUT = "☹ OOPS!!! Please use proper event formatting: event {task} /at {time}\n"; - private static final String INVALID_ACCESS_EMPTY_TASKLIST = "☹ OOPS!!! Task does not exist. Initialise a task first, then try again\n"; + private static final String INVALID_ACCESS_EMPTY_TaskList = "☹ OOPS!!! Task does not exist. Initialise a task first, then try again\n"; private static final String INVALID_USER_INPUT = "☹ OOPS!!! Please use one of these keywords: {deadline, event, todo} followed by \\\"by\\\" and \\\"at\\\" for deadline and event tasks respectively.\n"; - private Tasklist tasklist; + private TaskList TaskList; private Duke(){ - this.tasklist = new Tasklist(); + this.TaskList = new TaskList(); } - private Duke(Tasklist tasklist){ - this.tasklist = tasklist; + private Duke(TaskList TaskList){ + this.TaskList = TaskList; } - private static String welcomeMessage(){ + private static String welcomeMessage() { return horizontalBorder + "Hello! I'm Duke\nWhat can I do for you?\n" + horizontalBorder; } - private static String byeMessage(){ + private static String byeMessage() { return horizontalBorder + "Bye. Hope to see you again soon!\n" + horizontalBorder; } private String listContents(){ - return horizontalBorder + this.tasklist + horizontalBorder; + return horizontalBorder + this.TaskList + horizontalBorder; } public String addTaskMessage(String taskString) { - return horizontalBorder + "Got it. I've added this task:\n" + taskString + "\n" + "Now you have " + this.tasklist.getCount() + " tasks in the list.\n" + horizontalBorder; + return horizontalBorder + "Got it. I've added this task:\n" + taskString + "\n" + "Now you have " + this.TaskList.getCount() + " tasks in the list.\n" + horizontalBorder; } - public String taskNotFoundMessage(){ - return "☹ OOPS!!! Task does not exist. Try another number between 1 and " + this.tasklist.getCount() + "\n"; + public String taskNotFoundMessage() { + return "☹ OOPS!!! Task does not exist. Try another number between 1 and " + this.TaskList.getCount() + "\n"; } - private String markDoneMessage(int position) throws DukeException{ - boolean isTaskMarked = this.tasklist.markTaskAtPos(position); + private String markDoneMessage(int position) throws DukeException { + boolean isTaskMarked = this.TaskList.markTaskAtPos(position); if (isTaskMarked) { - Task currentTask = this.tasklist.getTask(position); + Task currentTask = this.TaskList.getTask(position); return horizontalBorder + "Nice! I've marked this task as done:\n" + currentTask + "\n" + horizontalBorder; - } else if (this.tasklist.getCount() == 0) { - throw new DukeException(INVALID_ACCESS_EMPTY_TASKLIST); + } else if (this.TaskList.getCount() == 0) { + throw new DukeException(INVALID_ACCESS_EMPTY_TaskList); } else { throw new DukeException(taskNotFoundMessage()); } } private String unmarkDoneMessage(int position) throws DukeException { - boolean isTaskUnmarked = this.tasklist.unmarkTaskAtPos(position); + boolean isTaskUnmarked = this.TaskList.unmarkTaskAtPos(position); if (isTaskUnmarked) { - Task currentTask = this.tasklist.getTask(position); + Task currentTask = this.TaskList.getTask(position); return horizontalBorder + "OK, I've marked this task as not done yet:\n" + currentTask + "\n" + horizontalBorder; - } else if (this.tasklist.getCount() == 0) { - throw new DukeException(INVALID_ACCESS_EMPTY_TASKLIST); + } else if (this.TaskList.getCount() == 0) { + throw new DukeException(INVALID_ACCESS_EMPTY_TaskList); } else { throw new DukeException(taskNotFoundMessage()); } @@ -65,11 +66,11 @@ private String unmarkDoneMessage(int position) throws DukeException { private String deleteTaskMessage(int position) throws DukeException { try { - Task deletedTask = this.tasklist.deleteTaskAtPos(position); - return horizontalBorder + "Noted. I've removed this task:\n" + deletedTask + "\n" + "" + "Now you have " + this.tasklist.getCount() + " tasks in the list.\n" + horizontalBorder; - } catch (IndexOutOfBoundsException e){ - if (this.tasklist.getCount() == 0){ - throw new DukeException(INVALID_ACCESS_EMPTY_TASKLIST); + Task deletedTask = this.TaskList.deleteTaskAtPos(position); + return horizontalBorder + "Noted. I've removed this task:\n" + deletedTask + "\n" + "" + "Now you have " + this.TaskList.getCount() + " tasks in the list.\n" + horizontalBorder; + } catch (IndexOutOfBoundsException e) { + if (this.TaskList.getCount() == 0) { + throw new DukeException(INVALID_ACCESS_EMPTY_TaskList); } else { throw new DukeException(taskNotFoundMessage()); } @@ -89,7 +90,7 @@ private String makeToDoFromInput(String input) throws DukeException { String description = input.substring("todo".length()).strip(); if (!description.equals("")) { ToDo newToDo = new ToDo(description); - this.tasklist.add(newToDo); + this.TaskList.add(newToDo); return addTaskMessage(newToDo.toString()); } else { throw new DukeException(INVALID_TODO_INPUT); @@ -98,12 +99,15 @@ private String makeToDoFromInput(String input) throws DukeException { private String makeDeadlineFromInput(String input) throws DukeException { String[] stringArray = input.substring("deadline".length()).strip().split("/by"); - if (stringArray.length > 1) { - Deadline newDeadline = new Deadline(stringArray[0].strip(), stringArray[1].strip()); - this.tasklist.add(newDeadline); + try { + LocalDate deadlineDate = LocalDate.parse(stringArray[1].strip()); + Deadline newDeadline = new Deadline(stringArray[0].strip(), deadlineDate); + this.TaskList.add(newDeadline); return addTaskMessage(newDeadline.toString()); - } else { + } catch (ArrayIndexOutOfBoundsException e) { throw new DukeException(INVALID_DEADLINE_INPUT); + } catch (java.time.format.DateTimeParseException e) { + throw new DukeException(INVALID_DATE_FORMAT); } } @@ -111,7 +115,7 @@ private String makeEventFromInput(String input) throws DukeException{ String[] stringArray = input.substring("event".length()).strip().split("/at"); if (stringArray.length > 1) { Event newEvent = new Event(stringArray[0].strip(), stringArray[1].strip()); - this.tasklist.add(newEvent); + this.TaskList.add(newEvent); return addTaskMessage(newEvent.toString()); } else { throw new DukeException(INVALID_EVENT_INPUT); @@ -163,7 +167,7 @@ public void run(){ public static void main(String[] args) { - Duke sampleDuke = new Duke(new Tasklist()); + Duke sampleDuke = new Duke(new TaskList()); sampleDuke.run(); // ArrayList a = new ArrayList<>(); // a.add("lol"); diff --git a/src/main/java/Task.java b/src/main/java/Task.java index 1351c88480..3535db811d 100644 --- a/src/main/java/Task.java +++ b/src/main/java/Task.java @@ -21,7 +21,7 @@ public void unmark() { } @Override - public String toString(){ + public String toString() { return "[" + getStatusIcon() + "] " + this.description; } } diff --git a/src/main/java/TaskList.java b/src/main/java/TaskList.java index f6ecd5d6b9..63b2fe0d3f 100644 --- a/src/main/java/TaskList.java +++ b/src/main/java/TaskList.java @@ -1,10 +1,10 @@ import java.util.ArrayList; -public class Tasklist { +public class TaskList { private ArrayList taskArray; private int count = 0; - public Tasklist(){ + public TaskList(){ this.taskArray = new ArrayList<>(); } From f110900250406601e564b794e402f69d5bc38009 Mon Sep 17 00:00:00 2001 From: Sampy147 Date: Thu, 8 Sep 2022 13:27:20 +0800 Subject: [PATCH 19/44] More-OOP done with prev commit code as comments --- data/duke.txt | 5 +- src/main/java/AddCommand.java | 20 ++ src/main/java/ByeCommand.java | 12 + src/main/java/Command.java | 6 + src/main/java/DeleteCommand.java | 28 ++ src/main/java/Duke.java | 262 ++++++++++-------- src/main/java/ListCommand.java | 12 + src/main/java/MarkCommand.java | 29 ++ src/main/java/Message.java | 23 ++ src/main/java/Parser.java | 133 +++++++++ .../{simpleDatabase.java => Storage.java} | 17 +- src/main/java/TaskList.java | 13 +- src/main/java/Ui.java | 60 ++++ src/main/java/UnmarkCommand.java | 41 +++ 14 files changed, 523 insertions(+), 138 deletions(-) create mode 100644 src/main/java/AddCommand.java create mode 100644 src/main/java/ByeCommand.java create mode 100644 src/main/java/Command.java create mode 100644 src/main/java/DeleteCommand.java create mode 100644 src/main/java/ListCommand.java create mode 100644 src/main/java/MarkCommand.java create mode 100644 src/main/java/Message.java create mode 100644 src/main/java/Parser.java rename src/main/java/{simpleDatabase.java => Storage.java} (87%) create mode 100644 src/main/java/Ui.java create mode 100644 src/main/java/UnmarkCommand.java diff --git a/data/duke.txt b/data/duke.txt index 91b0804199..39736f42b1 100644 --- a/data/duke.txt +++ b/data/duke.txt @@ -1,2 +1,3 @@ -E | 0 | lol | lol -D | 0 | assignemtn | 2020-01-01 +T | 1 | euiorurvbw +D | 0 | be2iuv | 2022-11-11 +D | 0 | cbiv | 2222-11-11 diff --git a/src/main/java/AddCommand.java b/src/main/java/AddCommand.java new file mode 100644 index 0000000000..dd720b6092 --- /dev/null +++ b/src/main/java/AddCommand.java @@ -0,0 +1,20 @@ +public class AddCommand extends Command { + + private final Task task; + + public AddCommand(Task task) { + this.task = task; + } + + @Override + public boolean isExit() { + return false; + } + + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { + tasks.add(this.task); + storage.save(this.task.toSimpleString()); + ui.showAddition(this.task, tasks.getCount()); + } +} diff --git a/src/main/java/ByeCommand.java b/src/main/java/ByeCommand.java new file mode 100644 index 0000000000..a901c42a28 --- /dev/null +++ b/src/main/java/ByeCommand.java @@ -0,0 +1,12 @@ +public class ByeCommand extends Command { + + @Override + public boolean isExit() { + return true; + } + + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { + ui.showBye(); + } +} diff --git a/src/main/java/Command.java b/src/main/java/Command.java new file mode 100644 index 0000000000..57747eef6a --- /dev/null +++ b/src/main/java/Command.java @@ -0,0 +1,6 @@ +public abstract class Command { + + public abstract boolean isExit(); + + public abstract void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException; +} diff --git a/src/main/java/DeleteCommand.java b/src/main/java/DeleteCommand.java new file mode 100644 index 0000000000..e22c5d5c97 --- /dev/null +++ b/src/main/java/DeleteCommand.java @@ -0,0 +1,28 @@ +public class DeleteCommand extends Command { + + private final int index; + + public DeleteCommand(int index) { + this.index = index; + } + + @Override + public boolean isExit() { + return false; + } + + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { + try { + Task deletedTask = tasks.deleteTaskAtPos(this.index); + storage.save(tasks); + ui.showDeleted(deletedTask); + } catch (IndexOutOfBoundsException e) { + if (tasks.getCount() == 0){ + throw new DukeException(Message.INVALID_ACCESS_EMPTY_TASKLIST); + } else { + throw new DukeException(Message.returnTaskNotFound(tasks)); + } + } + } +} diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index fc878c1da2..ab1eec36be 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -2,185 +2,209 @@ import java.time.LocalDate; public class Duke { - private static final String SAVED_PATH = "data/duke.txt"; - private static final String HORIZONTAL_BORDER = "_________________________________\n"; - private static final String INVALID_TODO_INPUT = " ☹ OOPS!!! The description of a todo cannot be empty.\n"; - private static final String INVALID_DEADLINE_INPUT = "☹ OOPS!!! Please use proper deadline formatting: deadline {task} /by {time}\n"; - private static final String INVALID_DATE_FORMAT = "☹ OOPS!!! Please indicate your date as YYYY-MM-DD (e.g 2019-12-09)\n"; - private static final String INVALID_EVENT_INPUT = "☹ OOPS!!! Please use proper event formatting: event {task} /at {time}\n"; - private static final String INVALID_ACCESS_EMPTY_TASKLIST = "☹ OOPS!!! Task does not exist. Initialise a task first, then try again\n"; - private static final String INVALID_USER_INPUT = "☹ OOPS!!! Please use one of these keywords: {deadline, event, todo} followed by \\\"by\\\" and \\\"at\\\" for deadline and event tasks respectively.\n"; - private static final String INVALID_DATE_INPUT = "☹ OOPS!!! The date given should not be before today's date\n"; - - private simpleDatabase database; + + private final String SAVED_PATH = "data/duke.txt"; + private Storage storage; private TaskList taskList; + private Ui ui; - private Duke(){ - this.database = new simpleDatabase(SAVED_PATH); + private Duke() { + this.storage = new Storage(SAVED_PATH); try { - this.taskList = this.database.getMemory(); + this.taskList = this.storage.load(); } catch (DukeException e) { - System.out.println(errorMessage(e.getMessage() + "\n")); + ui.showError(e.getMessage() + "\n"); System.exit(0); } + this.ui = new Ui(this.taskList); } - private static String errorMessage(String errorMessage) { - return HORIZONTAL_BORDER + errorMessage + HORIZONTAL_BORDER; - } +// private static String errorMessage(String errorMessage) { +// return HORIZONTAL_BORDER + errorMessage + HORIZONTAL_BORDER; +// } - private static String welcomeMessage(){ - return HORIZONTAL_BORDER + "Hello! I'm Duke\nWhat can I do for you?\n" + HORIZONTAL_BORDER; - } +// private static String welcomeMessage(){ +// return HORIZONTAL_BORDER + "Hello! I'm Duke\nWhat can I do for you?\n" + HORIZONTAL_BORDER; +// } - private static String byeMessage(){ - return HORIZONTAL_BORDER + "Bye. Hope to see you again soon!\n" + HORIZONTAL_BORDER; - } +// private static String byeMessage(){ +// return HORIZONTAL_BORDER + "Bye. Hope to see you again soon!\n" + HORIZONTAL_BORDER; +// } - private String listContents(){ - return HORIZONTAL_BORDER + this.taskList + HORIZONTAL_BORDER; - } +// private String listContents(){ +// return HORIZONTAL_BORDER + this.taskList + HORIZONTAL_BORDER; +// } - public String addTaskMessage(String taskString) { - return HORIZONTAL_BORDER + "Got it. I've added this task:\n" + taskString + "\n" + "Now you have " + this.taskList.getCount() + " tasks in the list.\n" + HORIZONTAL_BORDER; - } +// public String addTaskMessage(String taskString) { +// return HORIZONTAL_BORDER + "Got it. I've added this task:\n" + taskString + "\n" + "Now you have " + this.taskList.getCount() + " tasks in the list.\n" + HORIZONTAL_BORDER; +// } - public String taskNotFoundMessage(){ - return "☹ OOPS!!! Task does not exist. Try another number between 1 and " + this.taskList.getCount() + "\n"; - } - - private String markDoneMessage(int position) throws DukeException{ - boolean isTaskMarked = this.taskList.markTaskAtPos(position); - if (isTaskMarked) { - Task currentTask = this.taskList.getTask(position); - database.save(this.taskList); - return HORIZONTAL_BORDER + "Nice! I've marked this task as done:\n" + currentTask + "\n" + HORIZONTAL_BORDER; - } else if (this.taskList.getCount() == 0) { - throw new DukeException(INVALID_ACCESS_EMPTY_TASKLIST); - } else { - throw new DukeException(taskNotFoundMessage()); - } - } +// public String taskNotFoundMessage(){ +// return "Task does not exist. Try another number between 1 and " + this.taskList.getCount(); +// } - private String unmarkDoneMessage(int position) throws DukeException { - boolean isTaskUnmarked = this.taskList.unmarkTaskAtPos(position); - if (isTaskUnmarked) { - Task currentTask = this.taskList.getTask(position); - database.save(this.taskList); - return HORIZONTAL_BORDER + "OK, I've marked this task as not done yet:\n" + currentTask + "\n" + HORIZONTAL_BORDER; - } else if (this.taskList.getCount() == 0) { - throw new DukeException(INVALID_ACCESS_EMPTY_TASKLIST); - } else { - throw new DukeException(taskNotFoundMessage()); + private void markDoneMessage(String command) throws DukeException{ + String[] commandList = command.strip().split(" "); + try { + int taskIndexNum = Integer.parseInt(commandList[1]); + this.taskList.markTaskAtPos(taskIndexNum); + Task currentTask = this.taskList.getTask(taskIndexNum); + storage.save(this.taskList); + ui.showMarked(currentTask); + } catch (NumberFormatException e) { + throw new DukeException(Message.INVALID_MARK_TASK_FORMAT); + } catch (IndexOutOfBoundsException e) { + if (commandList.length <= 1) { + throw new DukeException(Message.INVALID_MARK_TASK_FORMAT); + } else if (this.taskList.getCount() == 0) { + throw new DukeException(Message.INVALID_ACCESS_EMPTY_TASKLIST); + } else { + throw new DukeException("mark done lo"); + } } } - private String deleteTaskMessage(int position) throws DukeException { + private void unmarkDoneMessage(String command) throws DukeException { + String[] commandList = command.strip().split(" "); try { - Task deletedTask = this.taskList.deleteTaskAtPos(position); - database.save(this.taskList); - return HORIZONTAL_BORDER + "Noted. I've removed this task:\n" + deletedTask + "\n" + "" + "Now you have " + this.taskList.getCount() + " tasks in the list.\n" + HORIZONTAL_BORDER; - } catch (IndexOutOfBoundsException e){ - if (this.taskList.getCount() == 0){ - throw new DukeException(INVALID_ACCESS_EMPTY_TASKLIST); + int taskIndexNum = Integer.parseInt(commandList[1]); + this.taskList.unmarkTaskAtPos(taskIndexNum); + Task currentTask = this.taskList.getTask(taskIndexNum); + storage.save(this.taskList); + ui.showUnmarked(currentTask); + } catch (NumberFormatException e) { + throw new DukeException(Message.INVALID_UNMARK_TASK_FORMAT); + } catch (IndexOutOfBoundsException e) { + if (commandList.length <= 1) { + throw new DukeException(Message.INVALID_UNMARK_TASK_FORMAT); + } else if (this.taskList.getCount() == 0) { + throw new DukeException(Message.INVALID_ACCESS_EMPTY_TASKLIST); } else { - throw new DukeException(taskNotFoundMessage()); + throw new DukeException("unmark done lo"); } } } - private boolean isInteger(String value) { + private void deleteTaskMessage(String command) throws DukeException { + String[] commandList = command.strip().split(" "); try { - Integer.parseInt(value); - return true; + int taskIndexNum = Integer.parseInt(commandList[1]); + Task deletedTask = this.taskList.deleteTaskAtPos(taskIndexNum); + storage.save(this.taskList); + ui.showDeleted(deletedTask); } catch (NumberFormatException e) { - return false; + throw new DukeException(Message.INVALID_DELETE_TASK_FORMAT); + } catch (IndexOutOfBoundsException e){ + if (commandList.length <= 1) { + throw new DukeException(Message.INVALID_DELETE_TASK_FORMAT); + } else if (this.taskList.getCount() == 0){ + throw new DukeException(Message.INVALID_ACCESS_EMPTY_TASKLIST); + } else { + throw new DukeException("Hola amigos"); + } } } - private String makeToDoFromInput(String input) throws DukeException { +// private boolean isInteger(String value) { +// try { +// Integer.parseInt(value); +// return true; +// } catch (NumberFormatException e) { +// return false; +// } +// } + + private void makeToDoFromInput(String input) throws DukeException { String description = input.substring("todo".length()).strip(); if (!description.equals("")) { ToDo newToDo = new ToDo(description); this.taskList.add(newToDo); - database.save(newToDo.toSimpleString()); - return addTaskMessage(newToDo.toString()); + storage.save(newToDo.toSimpleString()); + ui.showAddition(newToDo, taskList.getCount()); } else { - throw new DukeException(INVALID_TODO_INPUT); + throw new DukeException(Message.INVALID_TODO_INPUT); } } - private String makeDeadlineFromInput(String input) throws DukeException { + private void makeDeadlineFromInput(String input) throws DukeException { String[] stringArray = input.substring("deadline".length()).strip().split("/by"); try { LocalDate deadlineDate = LocalDate.parse(stringArray[1].strip()); if (deadlineDate.isBefore(LocalDate.now())){ - throw new DukeException(INVALID_DATE_INPUT); + throw new DukeException(Message.INVALID_DATE_INPUT); } Deadline newDeadline = new Deadline(stringArray[0].strip(), deadlineDate); this.taskList.add(newDeadline); - database.save(newDeadline.toSimpleString()); - return addTaskMessage(newDeadline.toString()); + storage.save(newDeadline.toSimpleString()); + ui.showAddition(newDeadline, taskList.getCount()); } catch (ArrayIndexOutOfBoundsException e) { - throw new DukeException(INVALID_DEADLINE_INPUT); + throw new DukeException(Message.INVALID_DEADLINE_INPUT); } catch (java.time.format.DateTimeParseException e) { - throw new DukeException(INVALID_DATE_FORMAT); + throw new DukeException(Message.INVALID_DATE_FORMAT); } } - private String makeEventFromInput(String input) throws DukeException { + private void makeEventFromInput(String input) throws DukeException { String[] stringArray = input.substring("event".length()).strip().split("/at"); if (stringArray.length > 1) { Event newEvent = new Event(stringArray[0].strip(), stringArray[1].strip()); this.taskList.add(newEvent); - database.save(newEvent.toSimpleString()); - return addTaskMessage(newEvent.toString()); + storage.save(newEvent.toSimpleString()); + ui.showAddition(newEvent, taskList.getCount()); } else { - throw new DukeException(INVALID_EVENT_INPUT); + throw new DukeException(Message.INVALID_EVENT_INPUT); } } - public void run(){ - System.out.println(welcomeMessage()); - Scanner scan = new Scanner(System.in); - String s = scan.nextLine(); - boolean exitNow = false; - while(!exitNow) { + public void run() { + ui.showWelcome(); + boolean isExit = false; + while (!isExit) { try { - String[] commandList = s.strip().split(" "); - String command = commandList[0].toLowerCase(); - if (command.equals("bye") && commandList.length == 1) { - exitNow = true; - System.out.println(byeMessage()); - } else if (command.equals("list") && commandList.length == 1) { - System.out.println(listContents()); - } else if (command.equals("mark") && commandList.length > 1 && isInteger(commandList[1])) { - int taskIndexNum = Integer.parseInt(commandList[1]); - System.out.println(markDoneMessage(taskIndexNum)); - } else if (command.equals("unmark") && commandList.length > 1 && isInteger(commandList[1])) { - int taskIndexNum = Integer.parseInt(commandList[1]); - System.out.println(unmarkDoneMessage(taskIndexNum)); - } else if (command.equals("deadline")) { - System.out.println(makeDeadlineFromInput(s)); - } else if (command.equals("event")) { - System.out.println(makeEventFromInput(s)); - } else if (command.equals("todo")) { - System.out.println(makeToDoFromInput(s)); - } else if (command.equals("delete") && commandList.length > 1 && isInteger(commandList[1])){ - int taskIndexNum = Integer.parseInt(commandList[1]); - System.out.println(deleteTaskMessage(taskIndexNum)); - } else if (!s.strip().equals("")) { - System.out.println(HORIZONTAL_BORDER + INVALID_USER_INPUT + HORIZONTAL_BORDER); - } + String fullCommand = ui.readCommand(); + Command c = Parser.parse(fullCommand); + c.execute(taskList, ui, storage); + isExit = c.isExit(); } catch (DukeException e) { - System.out.println(HORIZONTAL_BORDER + e.getMessage() + HORIZONTAL_BORDER); - } finally { - if (!exitNow) { - s = scan.nextLine(); - } + ui.showError(e.getMessage()); } +// Scanner scan = new Scanner(System.in); +// String s = scan.nextLine(); +// boolean exitNow = false; +// while(!exitNow) { +// try { +// String[] commandList = s.strip().split(" "); +// String command = commandList[0].toLowerCase(); +// if (command.equals("bye") && commandList.length == 1) { +// exitNow = true; +// ui.showBye(); +// } else if (command.equals("list") && commandList.length == 1) { +// ui.showList(); +// } else if (command.equals("mark")) { +// markDoneMessage(s); +// } else if (command.equals("unmark")) { +// unmarkDoneMessage(s); +// } else if (command.equals("deadline")) { +// makeDeadlineFromInput(s); +// } else if (command.equals("event")) { +// makeEventFromInput(s); +// } else if (command.equals("todo")) { +// makeToDoFromInput(s); +// } else if (command.equals("delete")){ +// deleteTaskMessage(s); +// } else { +// System.out.println(Message.INVALID_USER_INPUT); +// } +// } catch (DukeException e) { +// ui.showError(e.getMessage()); +// } finally { +// if (!exitNow) { +// s = scan.nextLine(); +// } +// } +// } +// scan.close(); } - scan.close(); } public static void main(String[] args) { diff --git a/src/main/java/ListCommand.java b/src/main/java/ListCommand.java new file mode 100644 index 0000000000..17f84430d1 --- /dev/null +++ b/src/main/java/ListCommand.java @@ -0,0 +1,12 @@ +public class ListCommand extends Command { + + @Override + public boolean isExit() { + return false; + } + + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { + ui.showList(); + } +} diff --git a/src/main/java/MarkCommand.java b/src/main/java/MarkCommand.java new file mode 100644 index 0000000000..e1b9aa7067 --- /dev/null +++ b/src/main/java/MarkCommand.java @@ -0,0 +1,29 @@ +public class MarkCommand extends Command { + + private final int index; + + public MarkCommand(int index) { + this.index = index; + } + + @Override + public boolean isExit() { + return false; + } + + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { + try { + tasks.markTaskAtPos(this.index); + Task currentTask = tasks.getTask(this.index); + storage.save(tasks); + ui.showMarked(currentTask); + } catch (IndexOutOfBoundsException e) { + if (tasks.getCount() == 0) { + throw new DukeException(Message.INVALID_ACCESS_EMPTY_TASKLIST); + } else { + throw new DukeException(Message.returnTaskNotFound(tasks)); + } + } + } +} diff --git a/src/main/java/Message.java b/src/main/java/Message.java new file mode 100644 index 0000000000..63c9d863e2 --- /dev/null +++ b/src/main/java/Message.java @@ -0,0 +1,23 @@ +public class Message { + public static final String SAVED_PATH = "data/duke.txt"; + public static final String HORIZONTAL_BORDER = "________________________________________________________"; + public static final String WELCOME_MESSAGE = "Hello! I'm Duke\nWhat can I do for you?"; + public static final String BYE_MESSAGE = "Bye. Hope to see you again soon!"; + public static final String INVALID_TODO_INPUT = "The description of a todo cannot be empty."; + public static final String INVALID_DEADLINE_INPUT = "Please use proper deadline formatting: deadline {task} /by {time}"; + public static final String INVALID_DATE_FORMAT = "Please indicate your date after {/bye} as YYYY-MM-DD (e.g 2019-12-09)"; + public static final String INVALID_EVENT_INPUT = "Please use proper event formatting: event {task} /at {time}"; + public static final String INVALID_ACCESS_EMPTY_TASKLIST = "Task does not exist. Initialise a task first, then try again"; + public static final String INVALID_MARK_TASK_FORMAT = "To mark a task, please input this format: mark {task number}"; + public static final String INVALID_UNMARK_TASK_FORMAT = "To unmark a task, please input this format: unmark {task number}"; + public static final String INVALID_DELETE_TASK_FORMAT = "To delete a task, please input this format: delete {task number}"; + public static final String INVALID_USER_INPUT = "Please use one of these keywords: {deadline, event, todo} followed by \\\"by\\\" and \\\"at\\\" for deadline and event tasks respectively."; + public static final String INVALID_DATE_INPUT = "The date given should not be before today's date"; + public static final String FILE_NOT_FOUND = "The file cannot be found.\n"; + public static final String FILE_READ_ERROR = "There is an error when reading the file.\n"; + + + public static String returnTaskNotFound(TaskList tasks) { + return "Task does not exist. Try another number between 1 and " + tasks.getCount(); + } +} diff --git a/src/main/java/Parser.java b/src/main/java/Parser.java new file mode 100644 index 0000000000..c7f97507c0 --- /dev/null +++ b/src/main/java/Parser.java @@ -0,0 +1,133 @@ +import java.time.LocalDate; + +public class Parser { + + public static Command parse(String fullCommand) throws DukeException { + String[] commandList = fullCommand.strip().split(" "); + String command = commandList[0].toLowerCase(); + if (command.equals("bye") && commandList.length == 1) { +// exitNow = true; +// ui.showBye(); + return new ByeCommand(); + } else if (command.equals("list") && commandList.length == 1) { +// ui.showList(); + return new ListCommand(); + } else if (command.equals("mark")) { + return markTask(fullCommand); + } else if (command.equals("unmark")) { + return unmarkTask(fullCommand); + } else if (command.equals("deadline")) { + return addDeadline(fullCommand); + } else if (command.equals("event")) { + return addEvent(fullCommand); + } else if (command.equals("todo")) { + return addToDo(fullCommand); + } else if (command.equals("delete")){ + return deleteTask(fullCommand); + } +// else if (!fullCommand.strip().equals("")) { +// System.out.println(Message.INVALID_USER_INPUT); +// } +// return deleteTask(fullCommand); + throw new DukeException(Message.INVALID_USER_INPUT); + } + + + private static AddCommand addToDo(String input) throws DukeException { + String description = input.substring("todo".length()).strip(); + if (!description.equals("")) { + ToDo newToDo = new ToDo(description); + return new AddCommand(newToDo); + } else { + throw new DukeException(Message.INVALID_TODO_INPUT); + } + } + + private static AddCommand addDeadline(String input) throws DukeException { + String[] stringArray = input.substring("deadline".length()).strip().split("/by"); + try { + LocalDate deadlineDate = LocalDate.parse(stringArray[1].strip()); + if (deadlineDate.isBefore(LocalDate.now())){ + throw new DukeException(Message.INVALID_DATE_INPUT); + } + Deadline newDeadline = new Deadline(stringArray[0].strip(), deadlineDate); + return new AddCommand(newDeadline); + } catch (ArrayIndexOutOfBoundsException e) { + throw new DukeException(Message.INVALID_DEADLINE_INPUT); + } catch (java.time.format.DateTimeParseException e) { + throw new DukeException(Message.INVALID_DATE_FORMAT); + } + } + + private static AddCommand addEvent(String input) throws DukeException { + String[] stringArray = input.substring("event".length()).strip().split("/at"); + if (stringArray.length > 1) { + Event newEvent = new Event(stringArray[0].strip(), stringArray[1].strip()); + return new AddCommand(newEvent); + } else { + throw new DukeException(Message.INVALID_EVENT_INPUT); + } + } + + private static MarkCommand markTask(String input) throws DukeException{ + String[] commandList = input.strip().split(" "); + try { + int taskIndexNum = Integer.parseInt(commandList[1]); + return new MarkCommand(taskIndexNum); + +// this.taskList.markTaskAtPos(taskIndexNum); +// Task currentTask = this.taskList.getTask(taskIndexNum); +// storage.save(this.taskList); +// ui.showMarked(currentTask); + + } catch (NumberFormatException | IndexOutOfBoundsException e) { + throw new DukeException(Message.INVALID_MARK_TASK_FORMAT); + } + } + + private static UnmarkCommand unmarkTask(String input) throws DukeException { + String[] commandList = input.strip().split(" "); + try { + int taskIndexNum = Integer.parseInt(commandList[1]); +// this.taskList.unmarkTaskAtPos(taskIndexNum); +// Task currentTask = this.taskList.getTask(taskIndexNum); +// storage.save(this.taskList); +// ui.showUnmarked(currentTask); + return new UnmarkCommand(taskIndexNum); + } catch (NumberFormatException | IndexOutOfBoundsException e) { + throw new DukeException(Message.INVALID_UNMARK_TASK_FORMAT); + } +// catch (IndexOutOfBoundsException e) { +// if (commandList.length <= 1) { +// throw new DukeException(Message.INVALID_UNMARK_TASK_FORMAT); +// } else if (this.taskList.getCount() == 0) { +// throw new DukeException(Message.INVALID_ACCESS_EMPTY_TASKLIST); +// } else { +// throw new DukeException(taskNotFoundMessage()); +// } +// } + } + + private static DeleteCommand deleteTask(String command) throws DukeException { + String[] commandList = command.strip().split(" "); + try { + int taskIndexNum = Integer.parseInt(commandList[1]); +// Task deletedTask = this.taskList.deleteTaskAtPos(taskIndexNum); +// storage.save(this.taskList); +// ui.showDeleted(deletedTask); + return new DeleteCommand(taskIndexNum); + } catch (NumberFormatException | IndexOutOfBoundsException e) { + throw new DukeException(Message.INVALID_DELETE_TASK_FORMAT); + } +// } catch (IndexOutOfBoundsException e){ +// if (commandList.length <= 1) { +// throw new DukeException(Message.INVALID_DELETE_TASK_FORMAT); +// } else if (this.taskList.getCount() == 0){ +// throw new DukeException(Message.INVALID_ACCESS_EMPTY_TASKLIST); +// } else { +// throw new DukeException(taskNotFoundMessage()); +// } +// } + } + +} diff --git a/src/main/java/simpleDatabase.java b/src/main/java/Storage.java similarity index 87% rename from src/main/java/simpleDatabase.java rename to src/main/java/Storage.java index e90a74b489..e4a7d7a277 100644 --- a/src/main/java/simpleDatabase.java +++ b/src/main/java/Storage.java @@ -7,18 +7,15 @@ import java.time.LocalDate; -public class simpleDatabase { - private static final String FILE_NOT_FOUND = "☹ Sorry , the file cannot be found.\n"; - private static final String FILE_READ_ERROR = "☹ Sorry , there is an error when reading the file.\n"; - +public class Storage { private final String filePath; - public simpleDatabase(String filePath){ + public Storage (String filePath) { this.filePath = filePath; File f = new File(filePath); } - public TaskList getMemory() throws DukeException{ + public TaskList load() throws DukeException{ TaskList taskList = new TaskList(); try { File localFile = new File(this.filePath); @@ -94,10 +91,10 @@ public Task makeTask(String taskString) throws DukeException { newTask = makeToDo(taskSegments[1].strip(), taskSegments[2].strip()); break; default: - throw new DukeException(FILE_READ_ERROR); + throw new DukeException(Message.FILE_READ_ERROR); } } catch (ArrayIndexOutOfBoundsException e) { - throw new DukeException(FILE_READ_ERROR); + throw new DukeException(Message.FILE_READ_ERROR); } return newTask; } @@ -108,7 +105,7 @@ public void save(String taskString) throws DukeException { fw.write(taskString + "\n"); fw.close(); } catch (IOException e) { - throw new DukeException(FILE_NOT_FOUND); + throw new DukeException(Message.FILE_NOT_FOUND); } } @@ -120,7 +117,7 @@ public void save(TaskList taskList) throws DukeException { } fw.close(); } catch (IOException e) { - throw new DukeException(FILE_NOT_FOUND); + throw new DukeException(Message.FILE_NOT_FOUND); } } } diff --git a/src/main/java/TaskList.java b/src/main/java/TaskList.java index d6cf3e750e..9db802a843 100644 --- a/src/main/java/TaskList.java +++ b/src/main/java/TaskList.java @@ -25,14 +25,9 @@ public int getCount(){ return this.count; } - public boolean markTaskAtPos(int position){ - try { + public void markTaskAtPos(int position) throws IndexOutOfBoundsException{ Task currTask = getTask(position); currTask.markAsDone(); - return true; - } catch (IndexOutOfBoundsException e) { - return false; - } } public boolean unmarkTaskAtPos(int position){ @@ -64,7 +59,11 @@ public String toSimpleStrings() { public String toString(){ String stringedList = ""; for (int i = 0; i < this.count; i++) { - stringedList += (i + 1) + ". " + getTask(i + 1).toString() + "\n"; + if (i == this.count -1) { + stringedList += (i + 1) + ". " + getTask(i + 1).toString(); + } else { + stringedList += (i + 1) + ". " + getTask(i + 1).toString() + "\n"; + } } return "Here are the tasks in your list:\n" + stringedList; } diff --git a/src/main/java/Ui.java b/src/main/java/Ui.java new file mode 100644 index 0000000000..98399942a2 --- /dev/null +++ b/src/main/java/Ui.java @@ -0,0 +1,60 @@ +import java.util.Scanner; + +public class Ui { + + private static final Scanner SCANNER = new Scanner(System.in); + private final TaskList taskList; + + public Ui(TaskList taskList) { + this.taskList = taskList; + } + + public void showLine() { + System.out.println(Message.HORIZONTAL_BORDER); + } + + public void showFullMessage(String mainMessage) { + showLine(); + System.out.println(mainMessage); + showLine(); + } + + public void showWelcome() { + showFullMessage(Message.WELCOME_MESSAGE); + } + + public void showBye() { + showFullMessage(Message.BYE_MESSAGE); + } + + public String readCommand() { + return SCANNER.nextLine().strip(); + } + + public void showError(String errorMessage) { + showFullMessage("☹ OOPS!!! " + errorMessage); + } + + public void showList() { + showFullMessage(this.taskList.toString()); + } + + public void showAddition(Task task, int totalTasks) { + showFullMessage("Got it. I've added this task:\n" + task + "\nNow you have " + totalTasks + " tasks in the list."); + } + + public void showMarked(Task task) { + showFullMessage("Nice! I've marked this task as done:\n" + task); + } + + public void showUnmarked(Task task) { + showFullMessage("OK, I've marked this task as not done yet:\n" + task); + } + + public void showDeleted(Task task) { + showFullMessage("Noted. I've removed this task:\n" + task + "\nNow you have " + this.taskList.getCount() + " tasks in the list."); + } + + + +} diff --git a/src/main/java/UnmarkCommand.java b/src/main/java/UnmarkCommand.java new file mode 100644 index 0000000000..32c376dfdd --- /dev/null +++ b/src/main/java/UnmarkCommand.java @@ -0,0 +1,41 @@ + +public class UnmarkCommand extends Command { + + private final int index; + + public UnmarkCommand(int index) { + this.index = index; + } + + @Override + public boolean isExit() { + return false; + } + + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { + try { + tasks.unmarkTaskAtPos(this.index); + Task currentTask = tasks.getTask(this.index); + storage.save(tasks); + ui.showUnmarked(currentTask); + } catch (IndexOutOfBoundsException e) { + if (tasks.getCount() == 0) { + throw new DukeException(Message.INVALID_ACCESS_EMPTY_TASKLIST); + } else { + throw new DukeException(Message.returnTaskNotFound(tasks)); + } + } + } +// catch (IndexOutOfBoundsException e) { +// if (commandList.length <= 1) { +// throw new DukeException(Message.INVALID_UNMARK_TASK_FORMAT); +// } else if (this.taskList.getCount() == 0) { +// throw new DukeException(Message.INVALID_ACCESS_EMPTY_TASKLIST); +// } else { +// throw new DukeException(taskNotFoundMessage()); +// } +// } + } + + From 507e8b85d8b7be67c11a70369f77c412c8b6a83e Mon Sep 17 00:00:00 2001 From: Sampy147 Date: Thu, 8 Sep 2022 14:13:38 +0800 Subject: [PATCH 20/44] A-MoreOOP completed --- data/duke.txt | 3 - src/main/java/Duke.java | 184 +------------------------------ src/main/java/Event.java | 2 +- src/main/java/Message.java | 7 +- src/main/java/Parser.java | 38 ------- src/main/java/Storage.java | 28 +++-- src/main/java/Ui.java | 12 +- src/main/java/UnmarkCommand.java | 9 -- 8 files changed, 31 insertions(+), 252 deletions(-) delete mode 100644 data/duke.txt diff --git a/data/duke.txt b/data/duke.txt deleted file mode 100644 index 39736f42b1..0000000000 --- a/data/duke.txt +++ /dev/null @@ -1,3 +0,0 @@ -T | 1 | euiorurvbw -D | 0 | be2iuv | 2022-11-11 -D | 0 | cbiv | 2222-11-11 diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index ab1eec36be..8c0531b9d0 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -5,155 +5,19 @@ public class Duke { private final String SAVED_PATH = "data/duke.txt"; private Storage storage; - private TaskList taskList; + private TaskList tasks; private Ui ui; private Duke() { this.storage = new Storage(SAVED_PATH); try { - this.taskList = this.storage.load(); + this.tasks = this.storage.load(); } catch (DukeException e) { - ui.showError(e.getMessage() + "\n"); + this.ui = new Ui(new TaskList()); + ui.showError(e.getMessage()); System.exit(0); } - this.ui = new Ui(this.taskList); - } - -// private static String errorMessage(String errorMessage) { -// return HORIZONTAL_BORDER + errorMessage + HORIZONTAL_BORDER; -// } - -// private static String welcomeMessage(){ -// return HORIZONTAL_BORDER + "Hello! I'm Duke\nWhat can I do for you?\n" + HORIZONTAL_BORDER; -// } - -// private static String byeMessage(){ -// return HORIZONTAL_BORDER + "Bye. Hope to see you again soon!\n" + HORIZONTAL_BORDER; -// } - -// private String listContents(){ -// return HORIZONTAL_BORDER + this.taskList + HORIZONTAL_BORDER; -// } - -// public String addTaskMessage(String taskString) { -// return HORIZONTAL_BORDER + "Got it. I've added this task:\n" + taskString + "\n" + "Now you have " + this.taskList.getCount() + " tasks in the list.\n" + HORIZONTAL_BORDER; -// } - -// public String taskNotFoundMessage(){ -// return "Task does not exist. Try another number between 1 and " + this.taskList.getCount(); -// } - - private void markDoneMessage(String command) throws DukeException{ - String[] commandList = command.strip().split(" "); - try { - int taskIndexNum = Integer.parseInt(commandList[1]); - this.taskList.markTaskAtPos(taskIndexNum); - Task currentTask = this.taskList.getTask(taskIndexNum); - storage.save(this.taskList); - ui.showMarked(currentTask); - } catch (NumberFormatException e) { - throw new DukeException(Message.INVALID_MARK_TASK_FORMAT); - } catch (IndexOutOfBoundsException e) { - if (commandList.length <= 1) { - throw new DukeException(Message.INVALID_MARK_TASK_FORMAT); - } else if (this.taskList.getCount() == 0) { - throw new DukeException(Message.INVALID_ACCESS_EMPTY_TASKLIST); - } else { - throw new DukeException("mark done lo"); - } - } - } - - private void unmarkDoneMessage(String command) throws DukeException { - String[] commandList = command.strip().split(" "); - try { - int taskIndexNum = Integer.parseInt(commandList[1]); - this.taskList.unmarkTaskAtPos(taskIndexNum); - Task currentTask = this.taskList.getTask(taskIndexNum); - storage.save(this.taskList); - ui.showUnmarked(currentTask); - } catch (NumberFormatException e) { - throw new DukeException(Message.INVALID_UNMARK_TASK_FORMAT); - } catch (IndexOutOfBoundsException e) { - if (commandList.length <= 1) { - throw new DukeException(Message.INVALID_UNMARK_TASK_FORMAT); - } else if (this.taskList.getCount() == 0) { - throw new DukeException(Message.INVALID_ACCESS_EMPTY_TASKLIST); - } else { - throw new DukeException("unmark done lo"); - } - } - } - - private void deleteTaskMessage(String command) throws DukeException { - String[] commandList = command.strip().split(" "); - try { - int taskIndexNum = Integer.parseInt(commandList[1]); - Task deletedTask = this.taskList.deleteTaskAtPos(taskIndexNum); - storage.save(this.taskList); - ui.showDeleted(deletedTask); - } catch (NumberFormatException e) { - throw new DukeException(Message.INVALID_DELETE_TASK_FORMAT); - } catch (IndexOutOfBoundsException e){ - if (commandList.length <= 1) { - throw new DukeException(Message.INVALID_DELETE_TASK_FORMAT); - } else if (this.taskList.getCount() == 0){ - throw new DukeException(Message.INVALID_ACCESS_EMPTY_TASKLIST); - } else { - throw new DukeException("Hola amigos"); - } - } - } - -// private boolean isInteger(String value) { -// try { -// Integer.parseInt(value); -// return true; -// } catch (NumberFormatException e) { -// return false; -// } -// } - - private void makeToDoFromInput(String input) throws DukeException { - String description = input.substring("todo".length()).strip(); - if (!description.equals("")) { - ToDo newToDo = new ToDo(description); - this.taskList.add(newToDo); - storage.save(newToDo.toSimpleString()); - ui.showAddition(newToDo, taskList.getCount()); - } else { - throw new DukeException(Message.INVALID_TODO_INPUT); - } - } - - private void makeDeadlineFromInput(String input) throws DukeException { - String[] stringArray = input.substring("deadline".length()).strip().split("/by"); - try { - LocalDate deadlineDate = LocalDate.parse(stringArray[1].strip()); - if (deadlineDate.isBefore(LocalDate.now())){ - throw new DukeException(Message.INVALID_DATE_INPUT); - } - Deadline newDeadline = new Deadline(stringArray[0].strip(), deadlineDate); - this.taskList.add(newDeadline); - storage.save(newDeadline.toSimpleString()); - ui.showAddition(newDeadline, taskList.getCount()); - } catch (ArrayIndexOutOfBoundsException e) { - throw new DukeException(Message.INVALID_DEADLINE_INPUT); - } catch (java.time.format.DateTimeParseException e) { - throw new DukeException(Message.INVALID_DATE_FORMAT); - } - } - - private void makeEventFromInput(String input) throws DukeException { - String[] stringArray = input.substring("event".length()).strip().split("/at"); - if (stringArray.length > 1) { - Event newEvent = new Event(stringArray[0].strip(), stringArray[1].strip()); - this.taskList.add(newEvent); - storage.save(newEvent.toSimpleString()); - ui.showAddition(newEvent, taskList.getCount()); - } else { - throw new DukeException(Message.INVALID_EVENT_INPUT); - } + this.ui = new Ui(this.tasks); } public void run() { @@ -163,47 +27,11 @@ public void run() { try { String fullCommand = ui.readCommand(); Command c = Parser.parse(fullCommand); - c.execute(taskList, ui, storage); + c.execute(tasks, ui, storage); isExit = c.isExit(); } catch (DukeException e) { ui.showError(e.getMessage()); } -// Scanner scan = new Scanner(System.in); -// String s = scan.nextLine(); -// boolean exitNow = false; -// while(!exitNow) { -// try { -// String[] commandList = s.strip().split(" "); -// String command = commandList[0].toLowerCase(); -// if (command.equals("bye") && commandList.length == 1) { -// exitNow = true; -// ui.showBye(); -// } else if (command.equals("list") && commandList.length == 1) { -// ui.showList(); -// } else if (command.equals("mark")) { -// markDoneMessage(s); -// } else if (command.equals("unmark")) { -// unmarkDoneMessage(s); -// } else if (command.equals("deadline")) { -// makeDeadlineFromInput(s); -// } else if (command.equals("event")) { -// makeEventFromInput(s); -// } else if (command.equals("todo")) { -// makeToDoFromInput(s); -// } else if (command.equals("delete")){ -// deleteTaskMessage(s); -// } else { -// System.out.println(Message.INVALID_USER_INPUT); -// } -// } catch (DukeException e) { -// ui.showError(e.getMessage()); -// } finally { -// if (!exitNow) { -// s = scan.nextLine(); -// } -// } -// } -// scan.close(); } } diff --git a/src/main/java/Event.java b/src/main/java/Event.java index 283e1b79be..53bad9a654 100644 --- a/src/main/java/Event.java +++ b/src/main/java/Event.java @@ -1,4 +1,4 @@ -public class Event extends Task{ +public class Event extends Task { protected String at; diff --git a/src/main/java/Message.java b/src/main/java/Message.java index 63c9d863e2..8b552e6941 100644 --- a/src/main/java/Message.java +++ b/src/main/java/Message.java @@ -1,5 +1,4 @@ public class Message { - public static final String SAVED_PATH = "data/duke.txt"; public static final String HORIZONTAL_BORDER = "________________________________________________________"; public static final String WELCOME_MESSAGE = "Hello! I'm Duke\nWhat can I do for you?"; public static final String BYE_MESSAGE = "Bye. Hope to see you again soon!"; @@ -13,9 +12,9 @@ public class Message { public static final String INVALID_DELETE_TASK_FORMAT = "To delete a task, please input this format: delete {task number}"; public static final String INVALID_USER_INPUT = "Please use one of these keywords: {deadline, event, todo} followed by \\\"by\\\" and \\\"at\\\" for deadline and event tasks respectively."; public static final String INVALID_DATE_INPUT = "The date given should not be before today's date"; - public static final String FILE_NOT_FOUND = "The file cannot be found.\n"; - public static final String FILE_READ_ERROR = "There is an error when reading the file.\n"; - + public static final String FILE_NOT_FOUND = "The memory file cannot be found."; + public static final String FILE_READ_ERROR = "There is an error when reading the memory file."; + public static final String FILE_CREATE_ERROR = "There is an error when creating the memory file"; public static String returnTaskNotFound(TaskList tasks) { return "Task does not exist. Try another number between 1 and " + tasks.getCount(); diff --git a/src/main/java/Parser.java b/src/main/java/Parser.java index c7f97507c0..cf895e0ccd 100644 --- a/src/main/java/Parser.java +++ b/src/main/java/Parser.java @@ -6,11 +6,8 @@ public static Command parse(String fullCommand) throws DukeException { String[] commandList = fullCommand.strip().split(" "); String command = commandList[0].toLowerCase(); if (command.equals("bye") && commandList.length == 1) { -// exitNow = true; -// ui.showBye(); return new ByeCommand(); } else if (command.equals("list") && commandList.length == 1) { -// ui.showList(); return new ListCommand(); } else if (command.equals("mark")) { return markTask(fullCommand); @@ -25,10 +22,6 @@ public static Command parse(String fullCommand) throws DukeException { } else if (command.equals("delete")){ return deleteTask(fullCommand); } -// else if (!fullCommand.strip().equals("")) { -// System.out.println(Message.INVALID_USER_INPUT); -// } -// return deleteTask(fullCommand); throw new DukeException(Message.INVALID_USER_INPUT); } @@ -74,12 +67,6 @@ private static MarkCommand markTask(String input) throws DukeException{ try { int taskIndexNum = Integer.parseInt(commandList[1]); return new MarkCommand(taskIndexNum); - -// this.taskList.markTaskAtPos(taskIndexNum); -// Task currentTask = this.taskList.getTask(taskIndexNum); -// storage.save(this.taskList); -// ui.showMarked(currentTask); - } catch (NumberFormatException | IndexOutOfBoundsException e) { throw new DukeException(Message.INVALID_MARK_TASK_FORMAT); } @@ -89,45 +76,20 @@ private static UnmarkCommand unmarkTask(String input) throws DukeException { String[] commandList = input.strip().split(" "); try { int taskIndexNum = Integer.parseInt(commandList[1]); -// this.taskList.unmarkTaskAtPos(taskIndexNum); -// Task currentTask = this.taskList.getTask(taskIndexNum); -// storage.save(this.taskList); -// ui.showUnmarked(currentTask); return new UnmarkCommand(taskIndexNum); } catch (NumberFormatException | IndexOutOfBoundsException e) { throw new DukeException(Message.INVALID_UNMARK_TASK_FORMAT); } -// catch (IndexOutOfBoundsException e) { -// if (commandList.length <= 1) { -// throw new DukeException(Message.INVALID_UNMARK_TASK_FORMAT); -// } else if (this.taskList.getCount() == 0) { -// throw new DukeException(Message.INVALID_ACCESS_EMPTY_TASKLIST); -// } else { -// throw new DukeException(taskNotFoundMessage()); -// } -// } } private static DeleteCommand deleteTask(String command) throws DukeException { String[] commandList = command.strip().split(" "); try { int taskIndexNum = Integer.parseInt(commandList[1]); -// Task deletedTask = this.taskList.deleteTaskAtPos(taskIndexNum); -// storage.save(this.taskList); -// ui.showDeleted(deletedTask); return new DeleteCommand(taskIndexNum); } catch (NumberFormatException | IndexOutOfBoundsException e) { throw new DukeException(Message.INVALID_DELETE_TASK_FORMAT); } -// } catch (IndexOutOfBoundsException e){ -// if (commandList.length <= 1) { -// throw new DukeException(Message.INVALID_DELETE_TASK_FORMAT); -// } else if (this.taskList.getCount() == 0){ -// throw new DukeException(Message.INVALID_ACCESS_EMPTY_TASKLIST); -// } else { -// throw new DukeException(taskNotFoundMessage()); -// } -// } } } diff --git a/src/main/java/Storage.java b/src/main/java/Storage.java index e4a7d7a277..059899576d 100644 --- a/src/main/java/Storage.java +++ b/src/main/java/Storage.java @@ -16,7 +16,7 @@ public Storage (String filePath) { } public TaskList load() throws DukeException{ - TaskList taskList = new TaskList(); + TaskList tasks = new TaskList(); try { File localFile = new File(this.filePath); Scanner s = new Scanner(localFile); @@ -25,12 +25,12 @@ public TaskList load() throws DukeException{ if (taskString.strip().equals("")) { continue; } - taskList.add(makeTask(taskString)); + tasks.add(makeTask(taskString)); } } catch (FileNotFoundException e) { makeNewFile(this.filePath); } - return taskList; + return tasks; } private void makeNewFile(String filePath) throws DukeException { @@ -44,7 +44,7 @@ private void makeNewFile(String filePath) throws DukeException { directory.mkdirs(); newFile.createNewFile(); } catch (Exception e) { - throw new DukeException("Error in creating memory space"); + throw new DukeException(Message.FILE_CREATE_ERROR); } } @@ -58,13 +58,17 @@ private Event makeEvent(String markIndex, String description, String at) { return newEvent; } - private Deadline makeDeadline(String markIndex, String description, String by) { - Deadline newDeadline = new Deadline(description, LocalDate.parse(by)); - if (markIndex.strip().equals("1")) { - newDeadline.markAsDone(); + private Deadline makeDeadline(String markIndex, String description, String by) throws DukeException{ + try { + Deadline newDeadline = new Deadline(description, LocalDate.parse(by)); + if (markIndex.strip().equals("1")) { + newDeadline.markAsDone(); + return newDeadline; + } return newDeadline; + } catch (java.time.format.DateTimeParseException e) { + throw new DukeException(Message.FILE_READ_ERROR); } - return newDeadline; } private ToDo makeToDo(String markIndex, String description){ @@ -109,11 +113,11 @@ public void save(String taskString) throws DukeException { } } - public void save(TaskList taskList) throws DukeException { + public void save(TaskList tasks) throws DukeException { try { FileWriter fw = new FileWriter(this.filePath); - for (int i = 1; i <= taskList.getCount(); i++) { - fw.write(taskList.getTask(i).toSimpleString() + "\n"); + for (int i = 1; i <= tasks.getCount(); i++) { + fw.write(tasks.getTask(i).toSimpleString() + "\n"); } fw.close(); } catch (IOException e) { diff --git a/src/main/java/Ui.java b/src/main/java/Ui.java index 98399942a2..dcf9c9e1bf 100644 --- a/src/main/java/Ui.java +++ b/src/main/java/Ui.java @@ -3,10 +3,10 @@ public class Ui { private static final Scanner SCANNER = new Scanner(System.in); - private final TaskList taskList; + private final TaskList tasks; - public Ui(TaskList taskList) { - this.taskList = taskList; + public Ui(TaskList tasks) { + this.tasks = tasks; } public void showLine() { @@ -36,7 +36,7 @@ public void showError(String errorMessage) { } public void showList() { - showFullMessage(this.taskList.toString()); + showFullMessage(this.tasks.toString()); } public void showAddition(Task task, int totalTasks) { @@ -52,9 +52,7 @@ public void showUnmarked(Task task) { } public void showDeleted(Task task) { - showFullMessage("Noted. I've removed this task:\n" + task + "\nNow you have " + this.taskList.getCount() + " tasks in the list."); + showFullMessage("Noted. I've removed this task:\n" + task + "\nNow you have " + this.tasks.getCount() + " tasks in the list."); } - - } diff --git a/src/main/java/UnmarkCommand.java b/src/main/java/UnmarkCommand.java index 32c376dfdd..bb90871648 100644 --- a/src/main/java/UnmarkCommand.java +++ b/src/main/java/UnmarkCommand.java @@ -27,15 +27,6 @@ public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException } } } -// catch (IndexOutOfBoundsException e) { -// if (commandList.length <= 1) { -// throw new DukeException(Message.INVALID_UNMARK_TASK_FORMAT); -// } else if (this.taskList.getCount() == 0) { -// throw new DukeException(Message.INVALID_ACCESS_EMPTY_TASKLIST); -// } else { -// throw new DukeException(taskNotFoundMessage()); -// } -// } } From d99e1ff2b066bf66e527127890a8bea5477d7f2f Mon Sep 17 00:00:00 2001 From: Sampy147 Date: Thu, 8 Sep 2022 17:12:44 +0800 Subject: [PATCH 21/44] Divided class into packages --- src/main/java/{ => duke}/Duke.java | 9 +++++++-- src/main/java/{ => duke}/DukeException.java | 2 ++ src/main/java/{ => duke}/Message.java | 4 ++++ src/main/java/{ => duke/command}/AddCommand.java | 8 ++++++++ src/main/java/{ => duke/command}/ByeCommand.java | 7 +++++++ src/main/java/{ => duke/command}/Command.java | 7 +++++++ src/main/java/{ => duke/command}/DeleteCommand.java | 9 +++++++++ src/main/java/{ => duke/command}/ListCommand.java | 9 ++++++++- src/main/java/{ => duke/command}/MarkCommand.java | 9 +++++++++ src/main/java/{ => duke/command}/UnmarkCommand.java | 8 ++++++++ src/main/java/{ => duke/parser}/Parser.java | 10 ++++++++++ src/main/java/{ => duke/storage}/Storage.java | 6 ++++++ 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}/TaskList.java | 4 ++++ src/main/java/{ => duke/task}/ToDo.java | 2 ++ src/main/java/{ => duke/ui}/Ui.java | 6 ++++++ 18 files changed, 103 insertions(+), 3 deletions(-) rename src/main/java/{ => duke}/Duke.java (87%) rename src/main/java/{ => duke}/DukeException.java (88%) rename src/main/java/{ => duke}/Message.java (97%) rename src/main/java/{ => duke/command}/AddCommand.java (75%) rename src/main/java/{ => duke/command}/ByeCommand.java (66%) rename src/main/java/{ => duke/command}/Command.java (57%) rename src/main/java/{ => duke/command}/DeleteCommand.java (81%) rename src/main/java/{ => duke/command}/ListCommand.java (63%) rename src/main/java/{ => duke/command}/MarkCommand.java (82%) rename src/main/java/{ => duke/command}/UnmarkCommand.java (84%) rename src/main/java/{ => duke/parser}/Parser.java (95%) rename src/main/java/{ => duke/storage}/Storage.java (97%) rename src/main/java/{ => duke/task}/Deadline.java (96%) rename src/main/java/{ => duke/task}/Event.java (95%) rename src/main/java/{ => duke/task}/Task.java (97%) rename src/main/java/{ => duke/task}/TaskList.java (97%) rename src/main/java/{ => duke/task}/ToDo.java (93%) rename src/main/java/{ => duke/ui}/Ui.java (94%) diff --git a/src/main/java/Duke.java b/src/main/java/duke/Duke.java similarity index 87% rename from src/main/java/Duke.java rename to src/main/java/duke/Duke.java index 8c0531b9d0..1e10db7c2d 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/duke/Duke.java @@ -1,5 +1,10 @@ -import java.util.Scanner; -import java.time.LocalDate; +package duke; + +import duke.command.Command; +import duke.parser.Parser; +import duke.storage.Storage; +import duke.task.TaskList; +import duke.ui.Ui; public class Duke { diff --git a/src/main/java/DukeException.java b/src/main/java/duke/DukeException.java similarity index 88% rename from src/main/java/DukeException.java rename to src/main/java/duke/DukeException.java index adbe7ebd0a..043a1ca799 100644 --- a/src/main/java/DukeException.java +++ b/src/main/java/duke/DukeException.java @@ -1,3 +1,5 @@ +package duke; + public class DukeException extends Exception{ public DukeException(String message){ super(message); diff --git a/src/main/java/Message.java b/src/main/java/duke/Message.java similarity index 97% rename from src/main/java/Message.java rename to src/main/java/duke/Message.java index 8b552e6941..c2c6a1392f 100644 --- a/src/main/java/Message.java +++ b/src/main/java/duke/Message.java @@ -1,3 +1,7 @@ +package duke; + +import duke.task.TaskList; + public class Message { public static final String HORIZONTAL_BORDER = "________________________________________________________"; public static final String WELCOME_MESSAGE = "Hello! I'm Duke\nWhat can I do for you?"; diff --git a/src/main/java/AddCommand.java b/src/main/java/duke/command/AddCommand.java similarity index 75% rename from src/main/java/AddCommand.java rename to src/main/java/duke/command/AddCommand.java index dd720b6092..737e09396f 100644 --- a/src/main/java/AddCommand.java +++ b/src/main/java/duke/command/AddCommand.java @@ -1,3 +1,11 @@ +package duke.command; + +import duke.DukeException; +import duke.storage.Storage; +import duke.task.Task; +import duke.task.TaskList; +import duke.ui.Ui; + public class AddCommand extends Command { private final Task task; diff --git a/src/main/java/ByeCommand.java b/src/main/java/duke/command/ByeCommand.java similarity index 66% rename from src/main/java/ByeCommand.java rename to src/main/java/duke/command/ByeCommand.java index a901c42a28..90ca7a62fd 100644 --- a/src/main/java/ByeCommand.java +++ b/src/main/java/duke/command/ByeCommand.java @@ -1,3 +1,10 @@ +package duke.command; + +import duke.DukeException; +import duke.storage.Storage; +import duke.task.TaskList; +import duke.ui.Ui; + public class ByeCommand extends Command { @Override diff --git a/src/main/java/Command.java b/src/main/java/duke/command/Command.java similarity index 57% rename from src/main/java/Command.java rename to src/main/java/duke/command/Command.java index 57747eef6a..f09fd835ba 100644 --- a/src/main/java/Command.java +++ b/src/main/java/duke/command/Command.java @@ -1,3 +1,10 @@ +package duke.command; + +import duke.DukeException; +import duke.storage.Storage; +import duke.task.TaskList; +import duke.ui.Ui; + public abstract class Command { public abstract boolean isExit(); diff --git a/src/main/java/DeleteCommand.java b/src/main/java/duke/command/DeleteCommand.java similarity index 81% rename from src/main/java/DeleteCommand.java rename to src/main/java/duke/command/DeleteCommand.java index e22c5d5c97..1f80f01dcf 100644 --- a/src/main/java/DeleteCommand.java +++ b/src/main/java/duke/command/DeleteCommand.java @@ -1,3 +1,12 @@ +package duke.command; + +import duke.Message; +import duke.DukeException; +import duke.storage.Storage; +import duke.task.Task; +import duke.task.TaskList; +import duke.ui.Ui; + public class DeleteCommand extends Command { private final int index; diff --git a/src/main/java/ListCommand.java b/src/main/java/duke/command/ListCommand.java similarity index 63% rename from src/main/java/ListCommand.java rename to src/main/java/duke/command/ListCommand.java index 17f84430d1..694a31a719 100644 --- a/src/main/java/ListCommand.java +++ b/src/main/java/duke/command/ListCommand.java @@ -1,3 +1,10 @@ +package duke.command; + +import duke.DukeException; +import duke.storage.Storage; +import duke.task.TaskList; +import duke.ui.Ui; + public class ListCommand extends Command { @Override @@ -6,7 +13,7 @@ public boolean isExit() { } @Override - public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { + public void execute(TaskList tasks, Ui ui, Storage storage) { ui.showList(); } } diff --git a/src/main/java/MarkCommand.java b/src/main/java/duke/command/MarkCommand.java similarity index 82% rename from src/main/java/MarkCommand.java rename to src/main/java/duke/command/MarkCommand.java index e1b9aa7067..298c38af66 100644 --- a/src/main/java/MarkCommand.java +++ b/src/main/java/duke/command/MarkCommand.java @@ -1,3 +1,12 @@ +package duke.command; + +import duke.Message; +import duke.DukeException; +import duke.storage.Storage; +import duke.task.Task; +import duke.task.TaskList; +import duke.ui.Ui; + public class MarkCommand extends Command { private final int index; diff --git a/src/main/java/UnmarkCommand.java b/src/main/java/duke/command/UnmarkCommand.java similarity index 84% rename from src/main/java/UnmarkCommand.java rename to src/main/java/duke/command/UnmarkCommand.java index bb90871648..3b25ef790b 100644 --- a/src/main/java/UnmarkCommand.java +++ b/src/main/java/duke/command/UnmarkCommand.java @@ -1,3 +1,11 @@ +package duke.command; + +import duke.DukeException; +import duke.Message; +import duke.storage.Storage; +import duke.task.Task; +import duke.task.TaskList; +import duke.ui.Ui; public class UnmarkCommand extends Command { diff --git a/src/main/java/Parser.java b/src/main/java/duke/parser/Parser.java similarity index 95% rename from src/main/java/Parser.java rename to src/main/java/duke/parser/Parser.java index cf895e0ccd..79283af522 100644 --- a/src/main/java/Parser.java +++ b/src/main/java/duke/parser/Parser.java @@ -1,3 +1,13 @@ +package duke.parser; + +import duke.DukeException; +import duke.Message; +import duke.command.*; +import duke.task.Deadline; +import duke.task.Event; +import duke.task.Task; +import duke.task.ToDo; + import java.time.LocalDate; public class Parser { diff --git a/src/main/java/Storage.java b/src/main/java/duke/storage/Storage.java similarity index 97% rename from src/main/java/Storage.java rename to src/main/java/duke/storage/Storage.java index 059899576d..1a300e5dde 100644 --- a/src/main/java/Storage.java +++ b/src/main/java/duke/storage/Storage.java @@ -1,3 +1,9 @@ +package duke.storage; + +import duke.DukeException; +import duke.Message; +import duke.task.*; + import java.io.FileWriter; import java.io.IOException; import java.io.File; diff --git a/src/main/java/Deadline.java b/src/main/java/duke/task/Deadline.java similarity index 96% rename from src/main/java/Deadline.java rename to src/main/java/duke/task/Deadline.java index 163fda375e..7eb2f82033 100644 --- a/src/main/java/Deadline.java +++ b/src/main/java/duke/task/Deadline.java @@ -1,3 +1,5 @@ +package duke.task; + import java.time.LocalDate; import java.time.format.DateTimeFormatter; public class Deadline extends Task { diff --git a/src/main/java/Event.java b/src/main/java/duke/task/Event.java similarity index 95% rename from src/main/java/Event.java rename to src/main/java/duke/task/Event.java index 53bad9a654..bef1f22d97 100644 --- a/src/main/java/Event.java +++ b/src/main/java/duke/task/Event.java @@ -1,3 +1,5 @@ +package duke.task; + public class Event extends Task { protected String at; 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 d7cebe6396..9561bad442 100644 --- a/src/main/java/Task.java +++ b/src/main/java/duke/task/Task.java @@ -1,3 +1,5 @@ +package duke.task; + public class Task { protected String description; protected boolean isDone; diff --git a/src/main/java/TaskList.java b/src/main/java/duke/task/TaskList.java similarity index 97% rename from src/main/java/TaskList.java rename to src/main/java/duke/task/TaskList.java index 9db802a843..9d2a797edc 100644 --- a/src/main/java/TaskList.java +++ b/src/main/java/duke/task/TaskList.java @@ -1,3 +1,7 @@ +package duke.task; + +import duke.task.Task; + import java.util.ArrayList; public class TaskList { diff --git a/src/main/java/ToDo.java b/src/main/java/duke/task/ToDo.java similarity index 93% rename from src/main/java/ToDo.java rename to src/main/java/duke/task/ToDo.java index 9efe8310fd..0d712747e8 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 description){ diff --git a/src/main/java/Ui.java b/src/main/java/duke/ui/Ui.java similarity index 94% rename from src/main/java/Ui.java rename to src/main/java/duke/ui/Ui.java index dcf9c9e1bf..6a571a2a57 100644 --- a/src/main/java/Ui.java +++ b/src/main/java/duke/ui/Ui.java @@ -1,3 +1,9 @@ +package duke.ui; + +import duke.Message; +import duke.task.Task; +import duke.task.TaskList; + import java.util.Scanner; public class Ui { From 27252f0d7729890c7d280d0eec517ba85ac96af2 Mon Sep 17 00:00:00 2001 From: Sampy147 Date: Thu, 8 Sep 2022 21:29:07 +0800 Subject: [PATCH 22/44] Build automation with Gradle added --- duke.txt | 0 src/main/java/duke/parser/Parser.java | 1 - 2 files changed, 1 deletion(-) delete mode 100644 duke.txt diff --git a/duke.txt b/duke.txt deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/main/java/duke/parser/Parser.java b/src/main/java/duke/parser/Parser.java index 79283af522..9f62e8d50b 100644 --- a/src/main/java/duke/parser/Parser.java +++ b/src/main/java/duke/parser/Parser.java @@ -5,7 +5,6 @@ import duke.command.*; import duke.task.Deadline; import duke.task.Event; -import duke.task.Task; import duke.task.ToDo; import java.time.LocalDate; From 371855c0276b2fd6d196b440f2a86a66f109b68d Mon Sep 17 00:00:00 2001 From: Sampy147 Date: Thu, 8 Sep 2022 21:29:07 +0800 Subject: [PATCH 23/44] Build automation with Gradle added Added Gradle support. --- duke.txt | 0 src/main/java/duke/parser/Parser.java | 1 - 2 files changed, 1 deletion(-) delete mode 100644 duke.txt diff --git a/duke.txt b/duke.txt deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/main/java/duke/parser/Parser.java b/src/main/java/duke/parser/Parser.java index 79283af522..9f62e8d50b 100644 --- a/src/main/java/duke/parser/Parser.java +++ b/src/main/java/duke/parser/Parser.java @@ -5,7 +5,6 @@ import duke.command.*; import duke.task.Deadline; import duke.task.Event; -import duke.task.Task; import duke.task.ToDo; import java.time.LocalDate; From 6027556a377b95708dbaa4c6ba7eb347089c8930 Mon Sep 17 00:00:00 2001 From: Sampy147 Date: Sat, 10 Sep 2022 00:38:57 +0800 Subject: [PATCH 24/44] added JUnit tests to the project Added DeadlineTest and ParserTest classes to conduct testing on the methods of Deadline.java and Parser.java respectively --- build.gradle | 2 +- duke.txt | 0 src/main/java/duke/parser/Parser.java | 21 ++++++++++------ src/test/java/duke/parser/ParserTest.java | 30 +++++++++++++++++++++++ src/test/java/duke/task/DeadlineTest.java | 23 +++++++++++++++++ 5 files changed, 68 insertions(+), 8 deletions(-) create mode 100644 duke.txt create mode 100644 src/test/java/duke/parser/ParserTest.java create mode 100644 src/test/java/duke/task/DeadlineTest.java diff --git a/build.gradle b/build.gradle index 885198fcfa..0bdc254568 100644 --- a/build.gradle +++ b/build.gradle @@ -28,7 +28,7 @@ test { } application { - mainClassName = "seedu.duke.Duke" + mainClassName = "duke.Duke" } shadowJar { diff --git a/duke.txt b/duke.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/main/java/duke/parser/Parser.java b/src/main/java/duke/parser/Parser.java index 9f62e8d50b..85e0952adb 100644 --- a/src/main/java/duke/parser/Parser.java +++ b/src/main/java/duke/parser/Parser.java @@ -48,11 +48,13 @@ private static AddCommand addToDo(String input) throws DukeException { private static AddCommand addDeadline(String input) throws DukeException { String[] stringArray = input.substring("deadline".length()).strip().split("/by"); try { - LocalDate deadlineDate = LocalDate.parse(stringArray[1].strip()); - if (deadlineDate.isBefore(LocalDate.now())){ + String description = stringArray[0].strip(); + String by = stringArray[1].strip(); + LocalDate deadlineDate = LocalDate.parse(by); + if (deadlineDate.isBefore(LocalDate.now()) || description.equals("")) { throw new DukeException(Message.INVALID_DATE_INPUT); } - Deadline newDeadline = new Deadline(stringArray[0].strip(), deadlineDate); + Deadline newDeadline = new Deadline(description, deadlineDate); return new AddCommand(newDeadline); } catch (ArrayIndexOutOfBoundsException e) { throw new DukeException(Message.INVALID_DEADLINE_INPUT); @@ -62,11 +64,16 @@ private static AddCommand addDeadline(String input) throws DukeException { } private static AddCommand addEvent(String input) throws DukeException { - String[] stringArray = input.substring("event".length()).strip().split("/at"); - if (stringArray.length > 1) { - Event newEvent = new Event(stringArray[0].strip(), stringArray[1].strip()); + try { + String[] stringArray = input.substring("event".length()).strip().split("/at"); + String description = stringArray[0].strip(); + String at = stringArray[1].strip(); + if (description.equals("") || at.equals("")) { + throw new DukeException(Message.INVALID_EVENT_INPUT); + } + Event newEvent = new Event(description, at); return new AddCommand(newEvent); - } else { + } catch (IndexOutOfBoundsException e) { throw new DukeException(Message.INVALID_EVENT_INPUT); } } diff --git a/src/test/java/duke/parser/ParserTest.java b/src/test/java/duke/parser/ParserTest.java new file mode 100644 index 0000000000..07eb892480 --- /dev/null +++ b/src/test/java/duke/parser/ParserTest.java @@ -0,0 +1,30 @@ +package duke.parser; + +import duke.DukeException; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class ParserTest { + + @Test + public void parse_incorrectInput_exceptionThrown() { + assertThrows(DukeException.class, () -> Parser.parse("bye bye")); + assertThrows(DukeException.class, () -> Parser.parse("")); + assertThrows(DukeException.class, () -> Parser.parse(" ")); + assertThrows(DukeException.class, () -> Parser.parse("list list")); + assertThrows(DukeException.class, () -> Parser.parse("")); + assertThrows(DukeException.class, () -> Parser.parse("bye bye")); + assertThrows(DukeException.class, () -> Parser.parse("event /at ")); + assertThrows(DukeException.class, () -> Parser.parse("event /at school")); + assertThrows(DukeException.class, () -> Parser.parse("deadline assignment /by tomorrow")); + assertThrows(DukeException.class, () -> Parser.parse("deadline assignment /by 11-11-2022")); + assertThrows(DukeException.class, () -> Parser.parse("deadline /by 2022-11-11")); + assertThrows(DukeException.class, () -> Parser.parse("todo ")); + assertThrows(DukeException.class, () -> Parser.parse("mark this")); + assertThrows(DukeException.class, () -> Parser.parse("mark ")); + assertThrows(DukeException.class, () -> Parser.parse("unmark this")); + assertThrows(DukeException.class, () -> Parser.parse("unmark ")); + assertThrows(DukeException.class, () -> Parser.parse("delete this")); + assertThrows(DukeException.class, () -> Parser.parse("delete ")); + } +} diff --git a/src/test/java/duke/task/DeadlineTest.java b/src/test/java/duke/task/DeadlineTest.java new file mode 100644 index 0000000000..747e399b12 --- /dev/null +++ b/src/test/java/duke/task/DeadlineTest.java @@ -0,0 +1,23 @@ +package duke.task; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import java.time.LocalDate; + +public class DeadlineTest { + + @Test + public void toStringTest() { + LocalDate by = LocalDate.parse("2022-11-11"); + Deadline newDeadline = new Deadline("Deadline", by); + assertEquals("[D][ ] Deadline (by: Nov 11 2022)", newDeadline.toString()); + } + + @Test + public void toSimpleStringTest() { + LocalDate by = LocalDate.parse("2022-11-11"); + Deadline newDeadline = new Deadline("Deadline", by); + assertEquals("D | 0 | Deadline | 2022-11-11", newDeadline.toSimpleString()); + } + +} From 0524628a6630f9b62e7ffcb74cb799a204ca68e7 Mon Sep 17 00:00:00 2001 From: Sampy147 Date: Sat, 10 Sep 2022 00:48:08 +0800 Subject: [PATCH 25/44] Add JUnit to current project --- duke.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 duke.txt diff --git a/duke.txt b/duke.txt deleted file mode 100644 index e69de29bb2..0000000000 From ef6a4b791cd4b94ca1359f89a15814b773f25289 Mon Sep 17 00:00:00 2001 From: Sampy147 Date: Sun, 11 Sep 2022 13:25:58 +0800 Subject: [PATCH 26/44] Attach project as JAR file in new release --- src/main/java/duke/Message.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/duke/Message.java b/src/main/java/duke/Message.java index c2c6a1392f..5e53a05219 100644 --- a/src/main/java/duke/Message.java +++ b/src/main/java/duke/Message.java @@ -8,7 +8,7 @@ public class Message { public static final String BYE_MESSAGE = "Bye. Hope to see you again soon!"; public static final String INVALID_TODO_INPUT = "The description of a todo cannot be empty."; public static final String INVALID_DEADLINE_INPUT = "Please use proper deadline formatting: deadline {task} /by {time}"; - public static final String INVALID_DATE_FORMAT = "Please indicate your date after {/bye} as YYYY-MM-DD (e.g 2019-12-09)"; + public static final String INVALID_DATE_FORMAT = "Please indicate your date after {/by} as YYYY-MM-DD (e.g 2019-12-09)"; public static final String INVALID_EVENT_INPUT = "Please use proper event formatting: event {task} /at {time}"; public static final String INVALID_ACCESS_EMPTY_TASKLIST = "Task does not exist. Initialise a task first, then try again"; public static final String INVALID_MARK_TASK_FORMAT = "To mark a task, please input this format: mark {task number}"; From 7a9cd026e4e961551689a9bc7d150266a1fc3ff8 Mon Sep 17 00:00:00 2001 From: Sampy147 Date: Sun, 11 Sep 2022 16:37:03 +0800 Subject: [PATCH 27/44] Add javadocs Added Javadocs for classes in the duke.command package, duke.task package, duke.ui package --- data/duke.txt | 4 ++ duke.txt | 0 src/main/java/duke/Message.java | 8 +++ src/main/java/duke/command/AddCommand.java | 21 ++++++ src/main/java/duke/command/ByeCommand.java | 15 +++++ src/main/java/duke/command/Command.java | 16 +++++ src/main/java/duke/command/DeleteCommand.java | 21 ++++++ src/main/java/duke/command/ListCommand.java | 17 ++++- src/main/java/duke/command/MarkCommand.java | 21 ++++++ src/main/java/duke/command/UnmarkCommand.java | 63 ++++++++++++------ src/main/java/duke/storage/Storage.java | 4 +- src/main/java/duke/task/Deadline.java | 20 ++++++ src/main/java/duke/task/Event.java | 19 ++++++ src/main/java/duke/task/Task.java | 30 +++++++++ src/main/java/duke/task/TaskList.java | 66 +++++++++++++++++-- src/main/java/duke/task/ToDo.java | 18 +++++ src/main/java/duke/ui/Ui.java | 58 +++++++++++++++- 17 files changed, 368 insertions(+), 33 deletions(-) create mode 100644 data/duke.txt create mode 100644 duke.txt diff --git a/data/duke.txt b/data/duke.txt new file mode 100644 index 0000000000..5056126574 --- /dev/null +++ b/data/duke.txt @@ -0,0 +1,4 @@ +E | 0 | supernova | utown +D | 1 | oops | 2022-11-11 +T | 0 | desc +E | 0 | u | at diff --git a/duke.txt b/duke.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/main/java/duke/Message.java b/src/main/java/duke/Message.java index 5e53a05219..87219f4b5e 100644 --- a/src/main/java/duke/Message.java +++ b/src/main/java/duke/Message.java @@ -20,6 +20,14 @@ public class Message { public static final String FILE_READ_ERROR = "There is an error when reading the memory file."; public static final String FILE_CREATE_ERROR = "There is an error when creating the memory file"; + /** + * Returns a String that describes that the task does not exist within the specified tasklist + * and suggests potential numbers for the user to try to locate the task. This method is called + * when the task is not found in the tasklist specified + * + * @param tasks the tasklist of type TaskList + * @return a String message describing the task is not found + */ public static String returnTaskNotFound(TaskList tasks) { return "Task does not exist. Try another number between 1 and " + tasks.getCount(); } diff --git a/src/main/java/duke/command/AddCommand.java b/src/main/java/duke/command/AddCommand.java index 737e09396f..e3ca05d8fb 100644 --- a/src/main/java/duke/command/AddCommand.java +++ b/src/main/java/duke/command/AddCommand.java @@ -6,19 +6,40 @@ import duke.task.TaskList; import duke.ui.Ui; +/** + * Adds a task to the existing storage + */ public class AddCommand extends Command { private final Task task; + /** + * Creates a command to add a task to the existing storage + * + * @param task task to be added + */ public AddCommand(Task task) { this.task = task; } + /** + * Determines if the command should end the program for the user + * + * @return false by default + */ @Override public boolean isExit() { return false; } + /** + * Executes the addition of a task from the storage + * + * @param tasks the list of tasks to be modified in execution + * @param ui the ui used to display messages to the user upon successful addition + * @param storage the storage to be modified in execution + * @throws DukeException if the change cannot be saved in storage successfully + */ @Override public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { tasks.add(this.task); diff --git a/src/main/java/duke/command/ByeCommand.java b/src/main/java/duke/command/ByeCommand.java index 90ca7a62fd..9986e6975b 100644 --- a/src/main/java/duke/command/ByeCommand.java +++ b/src/main/java/duke/command/ByeCommand.java @@ -5,13 +5,28 @@ import duke.task.TaskList; import duke.ui.Ui; +/** + * Exits the program + */ public class ByeCommand extends Command { + /** + * Determines if the command should end the program for the user + * + * @return true by default + */ @Override public boolean isExit() { return true; } + /** + * Exits the program + * + * @param tasks the list of tasks + * @param ui the ui used to display the bye message + * @param storage the local storage + */ @Override public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { ui.showBye(); diff --git a/src/main/java/duke/command/Command.java b/src/main/java/duke/command/Command.java index f09fd835ba..ee3176ea09 100644 --- a/src/main/java/duke/command/Command.java +++ b/src/main/java/duke/command/Command.java @@ -5,9 +5,25 @@ import duke.task.TaskList; import duke.ui.Ui; +/** + * Represents a command that can be executed + */ public abstract class Command { + /** + * Determines if the command should end the program for the user + * + * @return boolean that determines whether program should end + */ public abstract boolean isExit(); + /** + * Executes the command + * + * @param tasks the list of tasks + * @param ui the ui used to display messages after executing the command + * @param storage the local storage + * @throws DukeException if an exception is thrown when the command is executed + */ public abstract void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException; } diff --git a/src/main/java/duke/command/DeleteCommand.java b/src/main/java/duke/command/DeleteCommand.java index 1f80f01dcf..49b0226012 100644 --- a/src/main/java/duke/command/DeleteCommand.java +++ b/src/main/java/duke/command/DeleteCommand.java @@ -7,19 +7,40 @@ import duke.task.TaskList; import duke.ui.Ui; +/** + * Deletes a task from the existing storage + */ public class DeleteCommand extends Command { private final int index; + /** + * Creates a command to delete a task from the existing storage + * + * @param index the index of the task to be deleted + */ public DeleteCommand(int index) { this.index = index; } + /** + * Determines if the command should end the program for the user + * + * @return false by default + */ @Override public boolean isExit() { return false; } + /** + * Executes the deletion of a task from the storage + * + * @param tasks the list of tasks to be modified in execution + * @param ui the ui used to display messages to the user upon successful deletion + * @param storage the storage to be modified in execution + * @throws DukeException if the change cannot be saved in storage successfully or task cannot be deleted + */ @Override public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { try { diff --git a/src/main/java/duke/command/ListCommand.java b/src/main/java/duke/command/ListCommand.java index 694a31a719..6c6be92203 100644 --- a/src/main/java/duke/command/ListCommand.java +++ b/src/main/java/duke/command/ListCommand.java @@ -5,15 +5,30 @@ import duke.task.TaskList; import duke.ui.Ui; +/** + * Displays the list of tasks in the local storage + */ public class ListCommand extends Command { + /** + * Determines if the command should end the program for the user + * + * @return false by default + */ @Override public boolean isExit() { return false; } + /** + * Displays the list of tasks in the local storage using the ui + * + * @param tasks the list of tasks + * @param ui the ui used to display messages to the user + * @param storage the local storage + */ @Override - public void execute(TaskList tasks, Ui ui, Storage storage) { + public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { ui.showList(); } } diff --git a/src/main/java/duke/command/MarkCommand.java b/src/main/java/duke/command/MarkCommand.java index 298c38af66..61779186f2 100644 --- a/src/main/java/duke/command/MarkCommand.java +++ b/src/main/java/duke/command/MarkCommand.java @@ -7,19 +7,40 @@ import duke.task.TaskList; import duke.ui.Ui; +/** + * Marks a task in the existing storage + */ public class MarkCommand extends Command { private final int index; + /** + * Creates a command to mark a task in the existing storage + * + * @param index the index of the task to be marked + */ public MarkCommand(int index) { this.index = index; } + /** + * Determines if the command should end the program for the user + * + * @return false by default + */ @Override public boolean isExit() { return false; } + /** + * Executes the marking of a task in the local storage + * + * @param tasks the list of tasks to be modified in execution + * @param ui the ui used to display messages to the user once the task is successfully marked + * @param storage the storage to be modified in execution + * @throws DukeException if the change cannot be saved in storage successfully or task cannot be marked + */ @Override public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { try { diff --git a/src/main/java/duke/command/UnmarkCommand.java b/src/main/java/duke/command/UnmarkCommand.java index 3b25ef790b..b5c93f04b2 100644 --- a/src/main/java/duke/command/UnmarkCommand.java +++ b/src/main/java/duke/command/UnmarkCommand.java @@ -7,34 +7,55 @@ import duke.task.TaskList; import duke.ui.Ui; +/** + * Unmarks a task in the existing storage + */ public class UnmarkCommand extends Command { - private final int index; + private final int index; - public UnmarkCommand(int index) { - this.index = index; - } + /** + * Creates a command to unmark a task in the existing storage + * + * @param index the index of the task to be unmarked + */ + public UnmarkCommand(int index) { + this.index = index; + } - @Override - public boolean isExit() { - return false; - } + /** + * Determines if the command should end the program for the user + * + * @return false by default + */ + @Override + public boolean isExit() { + return false; + } - @Override - public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { - try { - tasks.unmarkTaskAtPos(this.index); - Task currentTask = tasks.getTask(this.index); - storage.save(tasks); - ui.showUnmarked(currentTask); - } catch (IndexOutOfBoundsException e) { - if (tasks.getCount() == 0) { - throw new DukeException(Message.INVALID_ACCESS_EMPTY_TASKLIST); - } else { - throw new DukeException(Message.returnTaskNotFound(tasks)); - } + /** + * Executes the unmarking of a task in the local storage + * + * @param tasks the list of tasks to be modified in execution + * @param ui the ui used to display messages to the user once the task is successfully unmarked + * @param storage the storage to be modified in execution + * @throws DukeException if the change cannot be saved in storage successfully or task cannot be unmarked + */ + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { + try { + tasks.unmarkTaskAtPos(this.index); + Task currentTask = tasks.getTask(this.index); + storage.save(tasks); + ui.showUnmarked(currentTask); + } catch (IndexOutOfBoundsException e) { + if (tasks.getCount() == 0) { + throw new DukeException(Message.INVALID_ACCESS_EMPTY_TASKLIST); + } else { + throw new DukeException(Message.returnTaskNotFound(tasks)); } } } +} diff --git a/src/main/java/duke/storage/Storage.java b/src/main/java/duke/storage/Storage.java index 1a300e5dde..56666b130b 100644 --- a/src/main/java/duke/storage/Storage.java +++ b/src/main/java/duke/storage/Storage.java @@ -122,9 +122,7 @@ public void save(String taskString) throws DukeException { public void save(TaskList tasks) throws DukeException { try { FileWriter fw = new FileWriter(this.filePath); - for (int i = 1; i <= tasks.getCount(); i++) { - fw.write(tasks.getTask(i).toSimpleString() + "\n"); - } + fw.write(tasks.toSimpleStrings()); fw.close(); } catch (IOException e) { throw new DukeException(Message.FILE_NOT_FOUND); diff --git a/src/main/java/duke/task/Deadline.java b/src/main/java/duke/task/Deadline.java index 7eb2f82033..1fd965bffd 100644 --- a/src/main/java/duke/task/Deadline.java +++ b/src/main/java/duke/task/Deadline.java @@ -2,20 +2,40 @@ import java.time.LocalDate; import java.time.format.DateTimeFormatter; + +/** + * Represents a deadline task + */ public class Deadline extends Task { protected LocalDate by; + /** + * Creates a new deadline + * + * @param description the description of the deadline + * @param by the date of the deadline + */ public Deadline(String description, LocalDate by) { super(description); this.by = by; } + /** + * Returns a string representation of this deadline + * + * @return string representation + */ @Override public String toString() { return "[D]" + super.toString() + " (by: " + this.by.format(DateTimeFormatter.ofPattern("MMM dd yyyy")) + ")"; } + /** + * Returns a simplified string representation of this deadline + * + * @return simplified string representation + */ @Override public String toSimpleString() { return "D | " + super.toSimpleString() + " | " + this.by; diff --git a/src/main/java/duke/task/Event.java b/src/main/java/duke/task/Event.java index bef1f22d97..9332d9bfba 100644 --- a/src/main/java/duke/task/Event.java +++ b/src/main/java/duke/task/Event.java @@ -1,19 +1,38 @@ package duke.task; +/** + * Represents a event task + */ public class Event extends Task { protected String at; + /** + * Creates a new event task + * + * @param description the description of the event + * @param at the place or time of the event + */ public Event(String description, String at){ super(description); this.at = at; } + /** + * Returns a string representation of this event + * + * @return string representation + */ @Override public String toString(){ return "[E]" + super.toString() + " (at: " + this.at + ")"; } + /** + * Returns a simplified string representation of this event + * + * @return simplified string representation + */ @Override public String toSimpleString() { return "E | " + super.toSimpleString() + " | " + this.at; diff --git a/src/main/java/duke/task/Task.java b/src/main/java/duke/task/Task.java index 9561bad442..66dff60d0a 100644 --- a/src/main/java/duke/task/Task.java +++ b/src/main/java/duke/task/Task.java @@ -1,31 +1,61 @@ package duke.task; +/** + * Represents a task unit. + * A task unit can be an event, deadline or to-do + */ public class Task { protected String description; protected boolean isDone; + /** + * Creates a new task that is not done yet + * + * @param description the description of the task + */ public Task(String description) { this.description = description; this.isDone = false; } + /** + * Returns whether a task is completed + * + * @return whether task is done + */ public String getStatusIcon() { return (this.isDone ? "X" : " "); } + /** + * Marks the task as completed + */ public void markAsDone() { this.isDone = true; } + /** + * Unmarks the task which suggests task has yet to be completed + */ public void unmark() { this.isDone = false; } + /** + * Returns a string representation of this task + * + * @return string representation + */ @Override public String toString() { return "[" + getStatusIcon() + "] " + this.description; } + /** + * Returns a simple string representation of this task + * + * @return simple string representation + */ public String toSimpleString() { return (getStatusIcon().equals("X") ? "1" : "0") + " | " + this.description; } diff --git a/src/main/java/duke/task/TaskList.java b/src/main/java/duke/task/TaskList.java index 9d2a797edc..0a22cd822d 100644 --- a/src/main/java/duke/task/TaskList.java +++ b/src/main/java/duke/task/TaskList.java @@ -4,46 +4,88 @@ import java.util.ArrayList; +/** + * Represents a list of tasks + */ public class TaskList { private ArrayList taskArray; private int count = 0; - public TaskList(){ + /** + * Creates a new list of tasks + */ + public TaskList() { this.taskArray = new ArrayList<>(); } + /** + * Creates a new list of tasks using an existing ArrayList of tasks + * + * @param taskArray the list of tasks represented as an ArrayList + */ public TaskList(ArrayList taskArray) { this.taskArray = taskArray; } + /** + * Adds a task into the list of tasks + * + * @param task the task to be added + */ public void add(Task task){ this.taskArray.add(task); this.count += 1; } + /** + * Returns the task at a particular index in the list of tasks + * + * @param position the index of the task in the list of tasks + * @return the task at the given position + * @throws IndexOutOfBoundsException if given position > length of list of tasks or given position < 1 + */ public Task getTask(int position) throws IndexOutOfBoundsException{ return taskArray.get(position - 1); } + /** + * Returns the total number of tasks in the list of tasks + * + * @return number of tasks in list + */ public int getCount(){ return this.count; } + /** + * Marks a task at a particular position in the list of tasks as completed + * + * @param position the index of the task in the list of tasks + * @throws IndexOutOfBoundsException if given position > length of list of tasks or given position < 1 + */ public void markTaskAtPos(int position) throws IndexOutOfBoundsException{ Task currTask = getTask(position); currTask.markAsDone(); } - public boolean unmarkTaskAtPos(int position){ - try { + /** + * Unmarks a task at a particular position in the list of tasks, representing the task is incomplete + * + * @param position the index of the task in the list of tasks + * @throws IndexOutOfBoundsException if given position > length of list of tasks or given position < 1 + */ + public void unmarkTaskAtPos(int position) throws IndexOutOfBoundsException{ Task currTask = getTask(position); currTask.unmark(); - return true; - } catch (IndexOutOfBoundsException e) { - return false; - } } + /** + * Deletes a task at a particular position in the list of tasks + * + * @param position the index of the task in the list of tasks + * @return the deleted task + * @throws IndexOutOfBoundsException the deleted task if given position > length of list of tasks or given position < 1 + */ public Task deleteTaskAtPos(int position) throws IndexOutOfBoundsException { Task deletedTask = getTask(position); this.taskArray.remove(position - 1); @@ -51,6 +93,11 @@ public Task deleteTaskAtPos(int position) throws IndexOutOfBoundsException { return deletedTask; } + /** + * Returns the simple string representation of the list of tasks + * + * @return simple string representation + */ public String toSimpleStrings() { String stringedList = ""; for (int i = 0; i < this.count; i++) { @@ -59,6 +106,11 @@ public String toSimpleStrings() { return stringedList; } + /** + * Returns the string representation of the list of tasks + * + * @return string representation + */ @Override public String toString(){ String stringedList = ""; diff --git a/src/main/java/duke/task/ToDo.java b/src/main/java/duke/task/ToDo.java index 0d712747e8..61d3bff800 100644 --- a/src/main/java/duke/task/ToDo.java +++ b/src/main/java/duke/task/ToDo.java @@ -1,16 +1,34 @@ package duke.task; +/** + * Represents a to-do task + */ public class ToDo extends Task{ + /** + * Creates a new to-do task + * + * @param description the description of this to-do + */ public ToDo(String description){ super(description); } + /** + * Returns a string representation of this to-do + * + * @return string representation + */ @Override public String toString(){ return "[T]" + super.toString(); } + /** + * Returns a simplified string representation of this to-do + * + * @return string representation + */ @Override public String toSimpleString() { return "T | " + super.toSimpleString(); diff --git a/src/main/java/duke/ui/Ui.java b/src/main/java/duke/ui/Ui.java index 6a571a2a57..9b326b8c0a 100644 --- a/src/main/java/duke/ui/Ui.java +++ b/src/main/java/duke/ui/Ui.java @@ -6,57 +6,113 @@ import java.util.Scanner; +/** + * Represents a user interface to display messages to user + */ public class Ui { private static final Scanner SCANNER = new Scanner(System.in); private final TaskList tasks; + /** + * Creates a user interface to display messages to user + * + * @param tasks the list of tasks + */ public Ui(TaskList tasks) { this.tasks = tasks; } + /** + * Displays a horizontal divider line + */ public void showLine() { System.out.println(Message.HORIZONTAL_BORDER); } + /** + * Displays the message with horizontal dividers + * + * @param mainMessage the message to be displayed + */ public void showFullMessage(String mainMessage) { showLine(); System.out.println(mainMessage); showLine(); } + /** + * Displays the full welcome message + */ public void showWelcome() { showFullMessage(Message.WELCOME_MESSAGE); } + /** + * Displays the full bye message + */ public void showBye() { showFullMessage(Message.BYE_MESSAGE); } + /** + * Reads the user input + * + * @return the user input String + */ public String readCommand() { return SCANNER.nextLine().strip(); } + /** + * Displays the formatted full error message + * + * @param errorMessage the error message + */ public void showError(String errorMessage) { showFullMessage("☹ OOPS!!! " + errorMessage); } + /** + * Displays the list of tasks + */ public void showList() { showFullMessage(this.tasks.toString()); } + /** + * Displays the addition message indicating a task has been successfully added + * + * @param task the task to be added + * @param totalTasks the total number of tasks + */ public void showAddition(Task task, int totalTasks) { showFullMessage("Got it. I've added this task:\n" + task + "\nNow you have " + totalTasks + " tasks in the list."); } + /** + * Displays the marked task message indicating a task has been successfully marked + * + * @param task the task to be marked + */ public void showMarked(Task task) { showFullMessage("Nice! I've marked this task as done:\n" + task); } + /** + * Displays the unmarked task message indicating a task has been successfully unmarked + * + * @param task the task to be unmarked + */ public void showUnmarked(Task task) { showFullMessage("OK, I've marked this task as not done yet:\n" + task); } - + + /** + * Displays the deleted task message indicating a task has been successfully deleted + * + * @param task the task to be deleted + */ public void showDeleted(Task task) { showFullMessage("Noted. I've removed this task:\n" + task + "\nNow you have " + this.tasks.getCount() + " tasks in the list."); } From 91ff3bbdb130fc6f12c2118ed39e3b263d77a95d Mon Sep 17 00:00:00 2001 From: Sampy147 Date: Sun, 11 Sep 2022 16:44:23 +0800 Subject: [PATCH 28/44] Remove unnecessary files duke.txt and data/duke.txt --- data/duke.txt | 4 ---- duke.txt | 0 2 files changed, 4 deletions(-) delete mode 100644 data/duke.txt delete mode 100644 duke.txt diff --git a/data/duke.txt b/data/duke.txt deleted file mode 100644 index 5056126574..0000000000 --- a/data/duke.txt +++ /dev/null @@ -1,4 +0,0 @@ -E | 0 | supernova | utown -D | 1 | oops | 2022-11-11 -T | 0 | desc -E | 0 | u | at diff --git a/duke.txt b/duke.txt deleted file mode 100644 index e69de29bb2..0000000000 From 9f1df79d697438d8ca697070b093fba26b8c45e6 Mon Sep 17 00:00:00 2001 From: Sampy147 Date: Mon, 12 Sep 2022 03:02:13 +0800 Subject: [PATCH 29/44] Modify files to comply to Java's Coding Standard Modified the files to ensure switch case statements now have no indentation and Java's conventions with regards to wrapping, newline, whitespace are adhered to. --- src/main/java/duke/Duke.java | 1 + src/main/java/duke/DukeException.java | 6 ++- src/main/java/duke/Message.java | 24 ++++++++--- src/main/java/duke/command/MarkCommand.java | 1 + src/main/java/duke/command/UnmarkCommand.java | 43 ++++++++++--------- src/main/java/duke/parser/Parser.java | 2 +- src/main/java/duke/storage/Storage.java | 31 +++++++------ src/main/java/duke/task/Deadline.java | 5 ++- src/main/java/duke/task/Event.java | 5 ++- src/main/java/duke/task/Task.java | 2 + src/main/java/duke/task/TaskList.java | 16 ++++--- src/main/java/duke/task/ToDo.java | 3 +- src/main/java/duke/ui/Ui.java | 6 ++- 13 files changed, 85 insertions(+), 60 deletions(-) diff --git a/src/main/java/duke/Duke.java b/src/main/java/duke/Duke.java index 1e10db7c2d..e56cbe752c 100644 --- a/src/main/java/duke/Duke.java +++ b/src/main/java/duke/Duke.java @@ -44,4 +44,5 @@ public static void main(String[] args) { Duke sampleDuke = new Duke(); sampleDuke.run(); } + } diff --git a/src/main/java/duke/DukeException.java b/src/main/java/duke/DukeException.java index 043a1ca799..73c1a8d300 100644 --- a/src/main/java/duke/DukeException.java +++ b/src/main/java/duke/DukeException.java @@ -1,7 +1,9 @@ package duke; -public class DukeException extends Exception{ - public DukeException(String message){ +public class DukeException extends Exception { + + public DukeException(String message) { super(message); } + } diff --git a/src/main/java/duke/Message.java b/src/main/java/duke/Message.java index c2c6a1392f..a41e796fb5 100644 --- a/src/main/java/duke/Message.java +++ b/src/main/java/duke/Message.java @@ -3,18 +3,27 @@ import duke.task.TaskList; public class Message { + public static final String HORIZONTAL_BORDER = "________________________________________________________"; public static final String WELCOME_MESSAGE = "Hello! I'm Duke\nWhat can I do for you?"; public static final String BYE_MESSAGE = "Bye. Hope to see you again soon!"; public static final String INVALID_TODO_INPUT = "The description of a todo cannot be empty."; - public static final String INVALID_DEADLINE_INPUT = "Please use proper deadline formatting: deadline {task} /by {time}"; - public static final String INVALID_DATE_FORMAT = "Please indicate your date after {/bye} as YYYY-MM-DD (e.g 2019-12-09)"; + public static final String INVALID_DEADLINE_INPUT = + "Please use proper deadline formatting: deadline {task} /by {time}"; + public static final String INVALID_DATE_FORMAT = + "Please indicate your date after {/bye} as YYYY-MM-DD (e.g 2019-12-09)"; public static final String INVALID_EVENT_INPUT = "Please use proper event formatting: event {task} /at {time}"; - public static final String INVALID_ACCESS_EMPTY_TASKLIST = "Task does not exist. Initialise a task first, then try again"; - public static final String INVALID_MARK_TASK_FORMAT = "To mark a task, please input this format: mark {task number}"; - public static final String INVALID_UNMARK_TASK_FORMAT = "To unmark a task, please input this format: unmark {task number}"; - public static final String INVALID_DELETE_TASK_FORMAT = "To delete a task, please input this format: delete {task number}"; - public static final String INVALID_USER_INPUT = "Please use one of these keywords: {deadline, event, todo} followed by \\\"by\\\" and \\\"at\\\" for deadline and event tasks respectively."; + public static final String INVALID_ACCESS_EMPTY_TASKLIST = + "Task does not exist. Initialise a task first, then try again"; + public static final String INVALID_MARK_TASK_FORMAT = + "To mark a task, please input this format: mark {task number}"; + public static final String INVALID_UNMARK_TASK_FORMAT = + "To unmark a task, please input this format: unmark {task number}"; + public static final String INVALID_DELETE_TASK_FORMAT = + "To delete a task, please input this format: delete {task number}"; + public static final String INVALID_USER_INPUT = + "Please use one of these keywords: {deadline, event, todo} followed by \\\"by\\\" and \\\"at\\\" " + + "for deadline and event tasks respectively."; public static final String INVALID_DATE_INPUT = "The date given should not be before today's date"; public static final String FILE_NOT_FOUND = "The memory file cannot be found."; public static final String FILE_READ_ERROR = "There is an error when reading the memory file."; @@ -23,4 +32,5 @@ public class Message { public static String returnTaskNotFound(TaskList tasks) { return "Task does not exist. Try another number between 1 and " + tasks.getCount(); } + } diff --git a/src/main/java/duke/command/MarkCommand.java b/src/main/java/duke/command/MarkCommand.java index 298c38af66..644e327d4c 100644 --- a/src/main/java/duke/command/MarkCommand.java +++ b/src/main/java/duke/command/MarkCommand.java @@ -35,4 +35,5 @@ public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException } } } + } diff --git a/src/main/java/duke/command/UnmarkCommand.java b/src/main/java/duke/command/UnmarkCommand.java index 3b25ef790b..ca042b8435 100644 --- a/src/main/java/duke/command/UnmarkCommand.java +++ b/src/main/java/duke/command/UnmarkCommand.java @@ -9,32 +9,33 @@ public class UnmarkCommand extends Command { - private final int index; + private final int index; - public UnmarkCommand(int index) { - this.index = index; - } + public UnmarkCommand(int index) { + this.index = index; + } - @Override - public boolean isExit() { - return false; - } + @Override + public boolean isExit() { + return false; + } - @Override - public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { - try { - tasks.unmarkTaskAtPos(this.index); - Task currentTask = tasks.getTask(this.index); - storage.save(tasks); - ui.showUnmarked(currentTask); - } catch (IndexOutOfBoundsException e) { - if (tasks.getCount() == 0) { - throw new DukeException(Message.INVALID_ACCESS_EMPTY_TASKLIST); - } else { - throw new DukeException(Message.returnTaskNotFound(tasks)); - } + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { + try { + tasks.unmarkTaskAtPos(this.index); + Task currentTask = tasks.getTask(this.index); + storage.save(tasks); + ui.showUnmarked(currentTask); + } catch (IndexOutOfBoundsException e) { + if (tasks.getCount() == 0) { + throw new DukeException(Message.INVALID_ACCESS_EMPTY_TASKLIST); + } else { + throw new DukeException(Message.returnTaskNotFound(tasks)); } } } +} + diff --git a/src/main/java/duke/parser/Parser.java b/src/main/java/duke/parser/Parser.java index 85e0952adb..17a8f991b6 100644 --- a/src/main/java/duke/parser/Parser.java +++ b/src/main/java/duke/parser/Parser.java @@ -78,7 +78,7 @@ private static AddCommand addEvent(String input) throws DukeException { } } - private static MarkCommand markTask(String input) throws DukeException{ + private static MarkCommand markTask(String input) throws DukeException { String[] commandList = input.strip().split(" "); try { int taskIndexNum = Integer.parseInt(commandList[1]); diff --git a/src/main/java/duke/storage/Storage.java b/src/main/java/duke/storage/Storage.java index 1a300e5dde..fd0fe8d3dd 100644 --- a/src/main/java/duke/storage/Storage.java +++ b/src/main/java/duke/storage/Storage.java @@ -12,7 +12,6 @@ import java.util.Scanner; import java.time.LocalDate; - public class Storage { private final String filePath; @@ -21,7 +20,7 @@ public Storage (String filePath) { File f = new File(filePath); } - public TaskList load() throws DukeException{ + public TaskList load() throws DukeException { TaskList tasks = new TaskList(); try { File localFile = new File(this.filePath); @@ -54,7 +53,6 @@ private void makeNewFile(String filePath) throws DukeException { } } - private Event makeEvent(String markIndex, String description, String at) { Event newEvent = new Event(description, at); if (markIndex.equals("1")) { @@ -64,7 +62,7 @@ private Event makeEvent(String markIndex, String description, String at) { return newEvent; } - private Deadline makeDeadline(String markIndex, String description, String by) throws DukeException{ + private Deadline makeDeadline(String markIndex, String description, String by) throws DukeException { try { Deadline newDeadline = new Deadline(description, LocalDate.parse(by)); if (markIndex.strip().equals("1")) { @@ -77,7 +75,7 @@ private Deadline makeDeadline(String markIndex, String description, String by) t } } - private ToDo makeToDo(String markIndex, String description){ + private ToDo makeToDo(String markIndex, String description) { ToDo newToDo = new ToDo(description); if (markIndex.strip().equals("1")) { newToDo.markAsDone(); @@ -91,17 +89,17 @@ public Task makeTask(String taskString) throws DukeException { try { String[] taskSegments = taskString.split("\\|"); switch (taskSegments[0].strip()) { - case "E": - newTask = makeEvent(taskSegments[1].strip(), taskSegments[2].strip(), taskSegments[3].strip()); - break; - case "D": - newTask = makeDeadline(taskSegments[1].strip(), taskSegments[2].strip(), taskSegments[3].strip()); - break; - case "T": - newTask = makeToDo(taskSegments[1].strip(), taskSegments[2].strip()); - break; - default: - throw new DukeException(Message.FILE_READ_ERROR); + case "E": + newTask = makeEvent(taskSegments[1].strip(), taskSegments[2].strip(), taskSegments[3].strip()); + break; + case "D": + newTask = makeDeadline(taskSegments[1].strip(), taskSegments[2].strip(), taskSegments[3].strip()); + break; + case "T": + newTask = makeToDo(taskSegments[1].strip(), taskSegments[2].strip()); + break; + default: + throw new DukeException(Message.FILE_READ_ERROR); } } catch (ArrayIndexOutOfBoundsException e) { throw new DukeException(Message.FILE_READ_ERROR); @@ -130,4 +128,5 @@ public void save(TaskList tasks) throws DukeException { throw new DukeException(Message.FILE_NOT_FOUND); } } + } diff --git a/src/main/java/duke/task/Deadline.java b/src/main/java/duke/task/Deadline.java index 7eb2f82033..67d5c2f111 100644 --- a/src/main/java/duke/task/Deadline.java +++ b/src/main/java/duke/task/Deadline.java @@ -2,6 +2,7 @@ import java.time.LocalDate; import java.time.format.DateTimeFormatter; + public class Deadline extends Task { protected LocalDate by; @@ -13,11 +14,13 @@ public Deadline(String description, LocalDate by) { @Override public String toString() { - return "[D]" + super.toString() + " (by: " + this.by.format(DateTimeFormatter.ofPattern("MMM dd yyyy")) + ")"; + return "[D]" + super.toString() + + " (by: " + this.by.format(DateTimeFormatter.ofPattern("MMM dd yyyy")) + ")"; } @Override public String toSimpleString() { return "D | " + super.toSimpleString() + " | " + this.by; } + } diff --git a/src/main/java/duke/task/Event.java b/src/main/java/duke/task/Event.java index bef1f22d97..8113076f16 100644 --- a/src/main/java/duke/task/Event.java +++ b/src/main/java/duke/task/Event.java @@ -4,13 +4,13 @@ public class Event extends Task { protected String at; - public Event(String description, String at){ + public Event(String description, String at) { super(description); this.at = at; } @Override - public String toString(){ + public String toString() { return "[E]" + super.toString() + " (at: " + this.at + ")"; } @@ -18,4 +18,5 @@ public String toString(){ public String toSimpleString() { return "E | " + super.toSimpleString() + " | " + this.at; } + } diff --git a/src/main/java/duke/task/Task.java b/src/main/java/duke/task/Task.java index 9561bad442..d5b82142e1 100644 --- a/src/main/java/duke/task/Task.java +++ b/src/main/java/duke/task/Task.java @@ -1,6 +1,7 @@ package duke.task; public class Task { + protected String description; protected boolean isDone; @@ -29,4 +30,5 @@ public String toString() { public String toSimpleString() { return (getStatusIcon().equals("X") ? "1" : "0") + " | " + this.description; } + } diff --git a/src/main/java/duke/task/TaskList.java b/src/main/java/duke/task/TaskList.java index 9d2a797edc..5652759f25 100644 --- a/src/main/java/duke/task/TaskList.java +++ b/src/main/java/duke/task/TaskList.java @@ -5,10 +5,11 @@ import java.util.ArrayList; public class TaskList { + private ArrayList taskArray; private int count = 0; - public TaskList(){ + public TaskList() { this.taskArray = new ArrayList<>(); } @@ -16,25 +17,25 @@ public TaskList(ArrayList taskArray) { this.taskArray = taskArray; } - public void add(Task task){ + public void add(Task task) { this.taskArray.add(task); this.count += 1; } - public Task getTask(int position) throws IndexOutOfBoundsException{ + public Task getTask(int position) throws IndexOutOfBoundsException { return taskArray.get(position - 1); } - public int getCount(){ + public int getCount() { return this.count; } - public void markTaskAtPos(int position) throws IndexOutOfBoundsException{ + public void markTaskAtPos(int position) throws IndexOutOfBoundsException { Task currTask = getTask(position); currTask.markAsDone(); } - public boolean unmarkTaskAtPos(int position){ + public boolean unmarkTaskAtPos(int position) { try { Task currTask = getTask(position); currTask.unmark(); @@ -60,7 +61,7 @@ public String toSimpleStrings() { } @Override - public String toString(){ + public String toString() { String stringedList = ""; for (int i = 0; i < this.count; i++) { if (i == this.count -1) { @@ -71,4 +72,5 @@ public String toString(){ } return "Here are the tasks in your list:\n" + stringedList; } + } diff --git a/src/main/java/duke/task/ToDo.java b/src/main/java/duke/task/ToDo.java index 0d712747e8..fcbe7325ea 100644 --- a/src/main/java/duke/task/ToDo.java +++ b/src/main/java/duke/task/ToDo.java @@ -2,7 +2,7 @@ public class ToDo extends Task{ - public ToDo(String description){ + public ToDo(String description) { super(description); } @@ -15,4 +15,5 @@ public String toString(){ public String toSimpleString() { return "T | " + super.toSimpleString(); } + } diff --git a/src/main/java/duke/ui/Ui.java b/src/main/java/duke/ui/Ui.java index 6a571a2a57..be95387c02 100644 --- a/src/main/java/duke/ui/Ui.java +++ b/src/main/java/duke/ui/Ui.java @@ -46,7 +46,8 @@ public void showList() { } public void showAddition(Task task, int totalTasks) { - showFullMessage("Got it. I've added this task:\n" + task + "\nNow you have " + totalTasks + " tasks in the list."); + showFullMessage("Got it. I've added this task:\n" + task + + "\nNow you have " + totalTasks + " tasks in the list."); } public void showMarked(Task task) { @@ -58,7 +59,8 @@ public void showUnmarked(Task task) { } public void showDeleted(Task task) { - showFullMessage("Noted. I've removed this task:\n" + task + "\nNow you have " + this.tasks.getCount() + " tasks in the list."); + showFullMessage("Noted. I've removed this task:\n" + task + + "\nNow you have " + this.tasks.getCount() + " tasks in the list."); } } From 52505dc72ed649cfbc7be9ecb3503e2c6e9444ef Mon Sep 17 00:00:00 2001 From: Sampy147 Date: Mon, 12 Sep 2022 18:22:54 +0800 Subject: [PATCH 30/44] Add Level-9 find feature Added the find feature which allows user to find matching tasks using a keyword --- src/main/java/duke/Message.java | 1 + src/main/java/duke/command/FindCommand.java | 27 +++++++++++++++++++++ src/main/java/duke/parser/Parser.java | 13 +++++++++- src/main/java/duke/task/Task.java | 4 +++ src/main/java/duke/task/TaskList.java | 20 +++++++++++++++ src/main/java/duke/ui/Ui.java | 4 +++ 6 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 src/main/java/duke/command/FindCommand.java diff --git a/src/main/java/duke/Message.java b/src/main/java/duke/Message.java index c2c6a1392f..882f109197 100644 --- a/src/main/java/duke/Message.java +++ b/src/main/java/duke/Message.java @@ -19,6 +19,7 @@ public class Message { public static final String FILE_NOT_FOUND = "The memory file cannot be found."; public static final String FILE_READ_ERROR = "There is an error when reading the memory file."; public static final String FILE_CREATE_ERROR = "There is an error when creating the memory file"; + public static final String INVALID_FIND_TASK_FORMAT = "To find a task, please input this format: find {Keyword}"; public static String returnTaskNotFound(TaskList tasks) { return "Task does not exist. Try another number between 1 and " + tasks.getCount(); diff --git a/src/main/java/duke/command/FindCommand.java b/src/main/java/duke/command/FindCommand.java new file mode 100644 index 0000000000..17521b236a --- /dev/null +++ b/src/main/java/duke/command/FindCommand.java @@ -0,0 +1,27 @@ +package duke.command; + +import duke.DukeException; +import duke.storage.Storage; +import duke.task.TaskList; +import duke.ui.Ui; + +public class FindCommand extends Command { + + private String keyword; + + public FindCommand(String keyword) { + this.keyword = keyword; + } + + @Override + public boolean isExit() { + return false; + } + + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { + String foundTasks = tasks.findTasks(this.keyword); + ui.showFound(foundTasks); + } + +} diff --git a/src/main/java/duke/parser/Parser.java b/src/main/java/duke/parser/Parser.java index 85e0952adb..1de5564f0f 100644 --- a/src/main/java/duke/parser/Parser.java +++ b/src/main/java/duke/parser/Parser.java @@ -28,8 +28,10 @@ public static Command parse(String fullCommand) throws DukeException { return addEvent(fullCommand); } else if (command.equals("todo")) { return addToDo(fullCommand); - } else if (command.equals("delete")){ + } else if (command.equals("delete")) { return deleteTask(fullCommand); + } else if (command.equals("find")) { + return findTask(fullCommand); } throw new DukeException(Message.INVALID_USER_INPUT); } @@ -108,4 +110,13 @@ private static DeleteCommand deleteTask(String command) throws DukeException { } } + private static FindCommand findTask(String command) throws DukeException { + String[] commandList = command.strip().split(" "); + try { + return new FindCommand(commandList[1]); + } catch (IndexOutOfBoundsException e) { + throw new DukeException(Message.INVALID_FIND_TASK_FORMAT); + } + } + } diff --git a/src/main/java/duke/task/Task.java b/src/main/java/duke/task/Task.java index 9561bad442..d2dd9419a5 100644 --- a/src/main/java/duke/task/Task.java +++ b/src/main/java/duke/task/Task.java @@ -21,6 +21,10 @@ public void unmark() { this.isDone = false; } + public String getDescription() { + return this.description; + } + @Override public String toString() { return "[" + getStatusIcon() + "] " + this.description; diff --git a/src/main/java/duke/task/TaskList.java b/src/main/java/duke/task/TaskList.java index 9d2a797edc..ae1a1cd6c5 100644 --- a/src/main/java/duke/task/TaskList.java +++ b/src/main/java/duke/task/TaskList.java @@ -51,6 +51,26 @@ public Task deleteTaskAtPos(int position) throws IndexOutOfBoundsException { return deletedTask; } + public String findTasks(String keyword) { + String foundTasks = ""; + int taskCount = 1; + for (Task task : this.taskArray) { + String description = task.getDescription(); + String[] words = description.split(" "); + for (String word : words) { + if (word.equals(keyword)) { + if (taskCount == 1) { + foundTasks += taskCount + ". " + task; + } else { + foundTasks += "\n" + taskCount + ". " + task; + } + taskCount++; + } + } + } + return foundTasks; + } + public String toSimpleStrings() { String stringedList = ""; for (int i = 0; i < this.count; i++) { diff --git a/src/main/java/duke/ui/Ui.java b/src/main/java/duke/ui/Ui.java index 6a571a2a57..d578119042 100644 --- a/src/main/java/duke/ui/Ui.java +++ b/src/main/java/duke/ui/Ui.java @@ -61,4 +61,8 @@ public void showDeleted(Task task) { showFullMessage("Noted. I've removed this task:\n" + task + "\nNow you have " + this.tasks.getCount() + " tasks in the list."); } + public void showFound(String foundTasks) { + showFullMessage("Here are the matching tasks in your list:\n" + foundTasks); + } + } From ed9edd339eca5d34fc8b382f2e59148a07ddf964 Mon Sep 17 00:00:00 2001 From: Sampy147 Date: Wed, 14 Sep 2022 13:43:00 +0800 Subject: [PATCH 31/44] Configure javaFx --- build.gradle | 27 +++++++++-- duke.txt | 0 src/main/java/duke/DialogBox.java | 61 ++++++++++++++++++++++++ src/main/java/duke/Duke.java | 10 +++- src/main/java/duke/Launcher.java | 12 +++++ src/main/java/duke/Main.java | 31 ++++++++++++ src/main/java/duke/MainWindow.java | 51 ++++++++++++++++++++ src/main/resources/images/DaDuke.png | Bin 0 -> 32657 bytes src/main/resources/images/DaUser.png | Bin 0 -> 37794 bytes src/main/resources/view/DialogBox.fxml | 16 +++++++ src/main/resources/view/MainWindow.fxml | 19 ++++++++ 11 files changed, 223 insertions(+), 4 deletions(-) create mode 100644 duke.txt create mode 100644 src/main/java/duke/DialogBox.java create mode 100644 src/main/java/duke/Launcher.java create mode 100644 src/main/java/duke/Main.java create mode 100644 src/main/java/duke/MainWindow.java create mode 100644 src/main/resources/images/DaDuke.png create mode 100644 src/main/resources/images/DaUser.png create mode 100644 src/main/resources/view/DialogBox.fxml create mode 100644 src/main/resources/view/MainWindow.fxml diff --git a/build.gradle b/build.gradle index 0bdc254568..04d76f4a14 100644 --- a/build.gradle +++ b/build.gradle @@ -4,6 +4,9 @@ plugins { id 'com.github.johnrengelman.shadow' version '5.1.0' } +group 'org.example' +version '1.0-SNAPSHOT' + repositories { mavenCentral() } @@ -11,8 +14,24 @@ 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 { useJUnitPlatform() @@ -28,7 +47,8 @@ test { } application { - mainClassName = "duke.Duke" + // Change this to your main class. + mainClassName = "duke.Launcher" } shadowJar { @@ -36,6 +56,7 @@ shadowJar { archiveClassifier = null } -run{ +run { + enableAssertions = true standardInput = System.in -} +} \ No newline at end of file diff --git a/duke.txt b/duke.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/main/java/duke/DialogBox.java b/src/main/java/duke/DialogBox.java new file mode 100644 index 0000000000..1286339296 --- /dev/null +++ b/src/main/java/duke/DialogBox.java @@ -0,0 +1,61 @@ +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 { + @FXML + private Label dialog; + @FXML + private ImageView displayPicture; + + private DialogBox(String text, Image img) { + try { + FXMLLoader fxmlLoader = new FXMLLoader(MainWindow.class.getResource("/view/DialogBox.fxml")); + fxmlLoader.setController(this); + fxmlLoader.setRoot(this); + fxmlLoader.load(); + } catch (IOException e) { + e.printStackTrace(); + } + + dialog.setText(text); + displayPicture.setImage(img); + } + + /** + * Flips the dialog box such that the ImageView is on the left and text on the right. + */ + private void flip() { + ObservableList tmp = FXCollections.observableArrayList(this.getChildren()); + Collections.reverse(tmp); + getChildren().setAll(tmp); + setAlignment(Pos.TOP_LEFT); + } + + public static DialogBox getUserDialog(String text, Image img) { + return new DialogBox(text, img); + } + + public static DialogBox getDukeDialog(String text, Image img) { + var db = new DialogBox(text, img); + db.flip(); + return db; + } +} diff --git a/src/main/java/duke/Duke.java b/src/main/java/duke/Duke.java index e56cbe752c..7fd56b6ed7 100644 --- a/src/main/java/duke/Duke.java +++ b/src/main/java/duke/Duke.java @@ -13,7 +13,7 @@ public class Duke { private TaskList tasks; private Ui ui; - private Duke() { + public Duke() { this.storage = new Storage(SAVED_PATH); try { this.tasks = this.storage.load(); @@ -40,6 +40,14 @@ public void run() { } } + /** + * You should have your own function to generate a response to user input. + * Replace this stub with your completed method. + */ + public String getResponse(String input) { + return "Duke heard: " + input; + } + public static void main(String[] args) { Duke sampleDuke = new Duke(); sampleDuke.run(); diff --git a/src/main/java/duke/Launcher.java b/src/main/java/duke/Launcher.java new file mode 100644 index 0000000000..5483e63637 --- /dev/null +++ b/src/main/java/duke/Launcher.java @@ -0,0 +1,12 @@ +package duke; + +import javafx.application.Application; + +/** + * A launcher class to workaround classpath issues. + */ +public class Launcher { + public static void main(String[] args) { + Application.launch(Main.class, args); + } +} \ No newline at end of file diff --git a/src/main/java/duke/Main.java b/src/main/java/duke/Main.java new file mode 100644 index 0000000000..59ae50a45c --- /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(); + } + } +} \ No newline at end of file diff --git a/src/main/java/duke/MainWindow.java b/src/main/java/duke/MainWindow.java new file mode 100644 index 0000000000..dc08a7342e --- /dev/null +++ b/src/main/java/duke/MainWindow.java @@ -0,0 +1,51 @@ +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 MainWindow. Provides the layout for the other controls. + */ +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/images/DaDuke.png b/src/main/resources/images/DaDuke.png new file mode 100644 index 0000000000000000000000000000000000000000..d893658717e29b50b4ceedada235d9f75835a118 GIT binary patch literal 32657 zcmV)5K*_&}P)Zf>Tg`t%N;{PE9Y2%-o7`CdniQG#9szpJX+ zcrVK`zK7pG^!n{1T{qo3os(XNkTqW0IXIfGihv^wpO4cHg6YjrzS7VYZJZVWev% z!fK9RcD(ntf zCxMCmC7oRZecuOe6J~p&f5@?I;U2zE;O2T?Vy_8UZ1S4i=b>l%q0jeYAI+i(>K zsIun-!&|SPodfa+5u7Z5yhtd`XRjxuF+Ww~1H4xg)(?GXYLo{q2VPiixh(3*`0iyFHj`V{p-_+Q4jWKC=o6ohr zW)q;Nb7IxtY}qD4RyBpAMv`rR!YytU)sgEIa8F)wX(O<=RwjYY+sLdy0`5o0>=a zd(GePvuhIc}2|`2Y7OgN%4u~XV>q*3e$65qJ;}bK0Li-Wh%~iQn_FhV_*O6|& z$E%S!>BN;~l+|jbw5B2<783HDuPYE-T1Z)&EZTFhZGIj)nseO*|7}7`Vz4%vMl&l& z!c*D(>n3|ulhD`D1FzG)Tik79GgrpJ#HE0O)y{gM#c%L1=xtu`A<9oj(;}Q6cpHb= zIjOSNTO3vD|G|N_tH+^nj?I`D4+@;j;9`s4!*lY>X2_|khHWO LGkWs%c&z?S( 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 z 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 KMNUMnLSTa5`eeEQ literal 0 HcmV?d00001 diff --git a/src/main/resources/view/DialogBox.fxml b/src/main/resources/view/DialogBox.fxml new file mode 100644 index 0000000000..e433809947 --- /dev/null +++ b/src/main/resources/view/DialogBox.fxml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml new file mode 100644 index 0000000000..832f84382e --- /dev/null +++ b/src/main/resources/view/MainWindow.fxml @@ -0,0 +1,19 @@ + + + + + + + + + + + +