From d8398594b7bc43da5eb865321c5a50cec4b3923d Mon Sep 17 00:00:00 2001 From: damithc Date: Mon, 25 May 2020 00:58:18 +0800 Subject: [PATCH 01/26] Add Gradle support --- build.gradle | 46 ++++++ gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 58695 bytes gradle/wrapper/gradle-wrapper.properties | 5 + gradlew | 183 +++++++++++++++++++++++ gradlew.bat | 103 +++++++++++++ text-ui-test/runtest.sh | 0 6 files changed, 337 insertions(+) create mode 100644 build.gradle create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat mode change 100644 => 100755 text-ui-test/runtest.sh diff --git a/build.gradle b/build.gradle new file mode 100644 index 00000000..20c0521c --- /dev/null +++ b/build.gradle @@ -0,0 +1,46 @@ +plugins { + id 'java' + id 'application' + id 'checkstyle' + id 'com.github.johnrengelman.shadow' version '5.1.0' +} + +repositories { + mavenCentral() +} + +dependencies { + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.5.0' + testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.5.0' +} + +test { + useJUnitPlatform() + + testLogging { + events "passed", "skipped", "failed" + + showExceptions true + exceptionFormat "full" + showCauses true + showStackTraces true + showStandardStreams = false + } +} + +application { + mainClassName = "seedu.duke.Duke" +} + +shadowJar { + archiveBaseName = "duke" + archiveClassifier = null +} + +checkstyle { + toolVersion = '8.29' +} + +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 00000000..b7c8c5db --- /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 00000000..2fe81a7d --- /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 00000000..62bd9b9c --- /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 eb488b3200056db955e597e416deb8c38da6e276 Mon Sep 17 00:00:00 2001 From: Yan Ru Date: Sun, 29 Aug 2021 18:03:51 +0800 Subject: [PATCH 02/26] Level 1 - Create lines, greet, echo, exit. --- src/main/java/Border.java | 11 +++++++++++ src/main/java/Duke.java | 19 +++++++++++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 src/main/java/Border.java diff --git a/src/main/java/Border.java b/src/main/java/Border.java new file mode 100644 index 00000000..0ba4b706 --- /dev/null +++ b/src/main/java/Border.java @@ -0,0 +1,11 @@ +public class Border { + public String createLine() + { + String lines = ""; + for (int a = 0; a < 50; a ++) + { + lines += "-"; + } + return lines; + } +} diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 5d313334..1e1543b5 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -1,10 +1,25 @@ +import java.util.Scanner; + public class Duke { public static void main(String[] args) { - String logo = " ____ _ \n" + /*String logo = " ____ _ \n" + "| _ \\ _ _| | _____ \n" + "| | | | | | | |/ / _ \\\n" + "| |_| | |_| | < __/\n" + "|____/ \\__,_|_|\\_\\___|\n"; - System.out.println("Hello from\n" + logo); + System.out.println("Hello from\n" + logo);*/ + + Scanner sc = new Scanner(System.in); + Border lines = new Border(); + System.out.println(lines.createLine() + "\n"); + String input = "Hello! I'm Duke\nWhat can I do for you?"; + while (!input.equals("bye")){ + System.out.println(String.format("%45s",input)); + System.out.println(lines.createLine() + "\n"); + input = sc.nextLine(); + System.out.println(lines.createLine() + "\n"); + } + System.out.println(String.format("%50s","Bye. Hope to see you again soon!")); } } + From b3c05ae4818aedaa210b622a9639671e0c264ada Mon Sep 17 00:00:00 2001 From: Yan Ru Date: Sun, 29 Aug 2021 19:45:38 +0800 Subject: [PATCH 03/26] Level 2 - Add, List, Align right for Duke's msg --- src/main/java/Duke.java | 18 +++++++++++------- src/main/java/TaskList.java | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 7 deletions(-) create mode 100644 src/main/java/TaskList.java diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 1e1543b5..0ce6f21b 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -8,18 +8,22 @@ public static void main(String[] args) { + "| |_| | |_| | < __/\n" + "|____/ \\__,_|_|\\_\\___|\n"; System.out.println("Hello from\n" + logo);*/ - + TaskList taskList = new TaskList(); Scanner sc = new Scanner(System.in); Border lines = new Border(); - System.out.println(lines.createLine() + "\n"); - String input = "Hello! I'm Duke\nWhat can I do for you?"; + System.out.println(lines.createLine() + "\n" + "Hello! I'm Duke\nWhat can I do for you?" + "\n" + lines.createLine()); + String input = sc.nextLine(); while (!input.equals("bye")){ - System.out.println(String.format("%45s",input)); - System.out.println(lines.createLine() + "\n"); + if (input.equals("list")) + { + System.out.println(lines.createLine()); + System.out.println(taskList.showTaskList()); + } + taskList.addTask(input); input = sc.nextLine(); - System.out.println(lines.createLine() + "\n"); } - System.out.println(String.format("%50s","Bye. Hope to see you again soon!")); + System.out.println(lines.createLine()); + System.out.println(String.format("%50s","Bye. Hope to see you again soon!") + "\n" + lines.createLine()); } } diff --git a/src/main/java/TaskList.java b/src/main/java/TaskList.java new file mode 100644 index 00000000..92267b75 --- /dev/null +++ b/src/main/java/TaskList.java @@ -0,0 +1,34 @@ +public class TaskList { + //Assume there will be no more than 100 tasks + String [] taskList = new String[100]; + int taskNum = 0; + Border lines = new Border(); + public void addTask(String task) + { + if (!task.equals("list")) + { + taskList[taskNum++] = task; + System.out.println(lines.createLine()); + System.out.println(String.format("%45s","added: " + task) + "\n" + lines.createLine()); + } + } + + public String showTaskList() + { + String output = ""; + for (int idx = 0; idx <= taskNum; idx ++) + { + String task = taskList[idx]; + if (task==null) + { + break; + } + else + { + output += String.format("%45s",((idx+1) + ". " + taskList[idx]) + "\n"); + } + } + System.out.println(String.format("%44s","Your Task List")); + return output + lines.createLine(); + } +} From daa229eb003b6a05c3dc299426156cb6efe17bc6 Mon Sep 17 00:00:00 2001 From: Yan Ru Date: Sun, 5 Sep 2021 19:58:09 +0800 Subject: [PATCH 04/26] Level 3 - Mark task as done (with tick), else show as cross. --- src/main/java/Duke.java | 16 +++++++++------- src/main/java/Task.java | 27 +++++++++++++++++++++++++++ src/main/java/TaskList.java | 35 +++++++++++++++++++++++++++++------ 3 files changed, 65 insertions(+), 13 deletions(-) create mode 100644 src/main/java/Task.java diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 0ce6f21b..28770869 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -13,15 +13,17 @@ public static void main(String[] args) { Border lines = new Border(); System.out.println(lines.createLine() + "\n" + "Hello! I'm Duke\nWhat can I do for you?" + "\n" + lines.createLine()); String input = sc.nextLine(); - while (!input.equals("bye")){ - if (input.equals("list")) + while (!input.equals("bye")) + /*if (input.equals("list"))*/ { - System.out.println(lines.createLine()); - System.out.println(taskList.showTaskList()); + /* System.out.println(lines.createLine()); + System.out.println(taskList.showTaskList());*/ + taskList.addTask(input); + input = sc.nextLine(); } - taskList.addTask(input); - input = sc.nextLine(); - } + /* taskList.addTask(input); + input = sc.nextLine();*/ + System.out.println(lines.createLine()); System.out.println(String.format("%50s","Bye. Hope to see you again soon!") + "\n" + lines.createLine()); } diff --git a/src/main/java/Task.java b/src/main/java/Task.java new file mode 100644 index 00000000..7fc5b073 --- /dev/null +++ b/src/main/java/Task.java @@ -0,0 +1,27 @@ +public class Task { + protected String taskDesc; + protected boolean isDone; + + public Task(String taskDesc) + { + this.taskDesc = taskDesc; + this.isDone = false; + } + + public String getStatus() + { + //tick if done, else cross + return (isDone ? "\u2713" : "\u2718"); + } + + public void markAsDone() + { + this.isDone = true; + } + + public String toString() + { + String results = "[" + this.getStatus() + "]" + " " + this.taskDesc; + return results; + } +} diff --git a/src/main/java/TaskList.java b/src/main/java/TaskList.java index 92267b75..128ee690 100644 --- a/src/main/java/TaskList.java +++ b/src/main/java/TaskList.java @@ -1,34 +1,57 @@ public class TaskList { //Assume there will be no more than 100 tasks - String [] taskList = new String[100]; + Task [] taskList = new Task[100]; int taskNum = 0; Border lines = new Border(); public void addTask(String task) { - if (!task.equals("list")) + /*if (!task.equals("list")) { taskList[taskNum++] = task; System.out.println(lines.createLine()); System.out.println(String.format("%45s","added: " + task) + "\n" + lines.createLine()); + }*/ + String [] arr = task.split(" "); + switch(arr[0]) + { + case "list": + System.out.println(showTaskList()); + break; + + case "done": + taskList[Integer.parseInt(arr[1]) - 1].markAsDone(); + System.out.println(lines.createLine()); + System.out.println(String.format("%45s","Nice! I've marked this task as done:")); + System.out.println(String.format("%45s",taskList[Integer.parseInt(arr[1]) - 1].toString())); + System.out.println(lines.createLine()); + break; + + default: + taskList[taskNum++] = new Task(task); + System.out.println(lines.createLine()); + System.out.println(String.format("%45s","added: " + task) + "\n" + lines.createLine()); + break; } } public String showTaskList() { - String output = ""; + //String output = ""; + String output = lines.createLine() + "\n" + String.format("%45s","Here are the tasks in your list:" + "\n"); for (int idx = 0; idx <= taskNum; idx ++) { - String task = taskList[idx]; + Task task = taskList[idx]; if (task==null) { break; } else { - output += String.format("%45s",((idx+1) + ". " + taskList[idx]) + "\n"); + //output += String.format("%45s",((idx+1) + ". " + taskList[idx]) + "\n"); + output += String.format("%45s",((idx+1) + ". " + task.toString()) + "\n"); } } - System.out.println(String.format("%44s","Your Task List")); + //System.out.println(String.format("%44s","Your Task List")); return output + lines.createLine(); } } From c25e3425ea92d1817046f24b24d13b31d9502f79 Mon Sep 17 00:00:00 2001 From: Yan Ru Date: Sat, 11 Sep 2021 19:43:32 +0800 Subject: [PATCH 05/26] Level 4 - ToDos, Events, Deadlines and Level 5 - Handle Errors --- src/main/java/Deadline.java | 24 +++++++ src/main/java/Duke.java | 16 +---- src/main/java/DukeException.java | 11 +++ src/main/java/Event.java | 24 +++++++ src/main/java/Task.java | 9 +-- src/main/java/TaskList.java | 120 ++++++++++++++++++++++++------- src/main/java/Todo.java | 14 ++++ 7 files changed, 174 insertions(+), 44 deletions(-) create mode 100644 src/main/java/Deadline.java create mode 100644 src/main/java/DukeException.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 00000000..ca595099 --- /dev/null +++ b/src/main/java/Deadline.java @@ -0,0 +1,24 @@ +public class Deadline extends Task +{ + String dateTime; + Border lines = new Border(); + + public Deadline(String description) throws DukeException + { + super(description); + int divider = description.indexOf("/by"); + if (divider == -1 || (divider == description.length() -3)) + { + throw new DukeException((lines.createLine()) + "\n The date/time cannot be empty." + (lines.createLine())); + } + dateTime = description.substring(divider + 4, description.length()); + super.taskDesc = super.taskDesc.substring(0,divider); + } + + @Override + public String toString() + { + String output = "[D][" + super.getStatus() + "]" + " " + super.taskDesc + "(by: " + dateTime + ")"; + return output; + } +} diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 28770869..1e626227 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -1,31 +1,21 @@ import java.util.Scanner; public class Duke { - public static void main(String[] args) { - /*String logo = " ____ _ \n" - + "| _ \\ _ _| | _____ \n" - + "| | | | | | | |/ / _ \\\n" - + "| |_| | |_| | < __/\n" - + "|____/ \\__,_|_|\\_\\___|\n"; - System.out.println("Hello from\n" + logo);*/ + public static void main(String[] args) + { TaskList taskList = new TaskList(); Scanner sc = new Scanner(System.in); Border lines = new Border(); System.out.println(lines.createLine() + "\n" + "Hello! I'm Duke\nWhat can I do for you?" + "\n" + lines.createLine()); String input = sc.nextLine(); while (!input.equals("bye")) - /*if (input.equals("list"))*/ { - /* System.out.println(lines.createLine()); - System.out.println(taskList.showTaskList());*/ taskList.addTask(input); input = sc.nextLine(); } - /* taskList.addTask(input); - input = sc.nextLine();*/ System.out.println(lines.createLine()); - System.out.println(String.format("%50s","Bye. Hope to see you again soon!") + "\n" + lines.createLine()); + System.out.println("Bye. Hope to see you again soon!" + "\n" + lines.createLine()); } } diff --git a/src/main/java/DukeException.java b/src/main/java/DukeException.java new file mode 100644 index 00000000..117c9c3b --- /dev/null +++ b/src/main/java/DukeException.java @@ -0,0 +1,11 @@ +public class DukeException extends Exception +{ + public DukeException(String message) + { + super(message); + } + public DukeException(String message, Throwable cause) + { + super(message,cause); + } +} diff --git a/src/main/java/Event.java b/src/main/java/Event.java new file mode 100644 index 00000000..ea1c0174 --- /dev/null +++ b/src/main/java/Event.java @@ -0,0 +1,24 @@ +public class Event extends Task +{ + String dateTime; + Border lines = new Border(); + + public Event(String description) throws DukeException + { + super(description); + int divider = description.indexOf("/at"); + if (divider == -1 || (divider == description.length() - 3)) + { + throw new DukeException((lines.createLine()) + "\n The date/time cannot be empty." + (lines.createLine())); + } + dateTime = description.substring(divider + 4, description.length()); + super.taskDesc = super.taskDesc.substring(0,divider); + } + + @Override + public String toString() + { + String output = "[E][" + super.getStatus() + "]" + " " + super.taskDesc + "(at: " + dateTime + ")"; + return output; + } +} diff --git a/src/main/java/Task.java b/src/main/java/Task.java index 7fc5b073..f2c5c1ba 100644 --- a/src/main/java/Task.java +++ b/src/main/java/Task.java @@ -1,4 +1,5 @@ -public class Task { +public abstract class Task +{ protected String taskDesc; protected boolean isDone; @@ -19,9 +20,5 @@ public void markAsDone() this.isDone = true; } - public String toString() - { - String results = "[" + this.getStatus() + "]" + " " + this.taskDesc; - return results; - } + public abstract String toString(); } diff --git a/src/main/java/TaskList.java b/src/main/java/TaskList.java index 128ee690..aee62a0c 100644 --- a/src/main/java/TaskList.java +++ b/src/main/java/TaskList.java @@ -1,43 +1,97 @@ -public class TaskList { +public class TaskList +{ //Assume there will be no more than 100 tasks - Task [] taskList = new Task[100]; + Task[] taskList = new Task[100]; int taskNum = 0; Border lines = new Border(); + public void addTask(String task) { - /*if (!task.equals("list")) - { - taskList[taskNum++] = task; - System.out.println(lines.createLine()); - System.out.println(String.format("%45s","added: " + task) + "\n" + lines.createLine()); - }*/ - String [] arr = task.split(" "); - switch(arr[0]) + String[] arr = task.split(" ", 2); + switch (arr[0]) { case "list": System.out.println(showTaskList()); break; case "done": - taskList[Integer.parseInt(arr[1]) - 1].markAsDone(); - System.out.println(lines.createLine()); - System.out.println(String.format("%45s","Nice! I've marked this task as done:")); - System.out.println(String.format("%45s",taskList[Integer.parseInt(arr[1]) - 1].toString())); - System.out.println(lines.createLine()); - break; + try + { + try + { + if (arr.length < 2) + throw new DukeException((lines.createLine()) + "\n Which tasks have u completed? \n" + (lines.createLine())); + taskList[Integer.parseInt(arr[1]) - 1].markAsDone(); + System.out.println(lines.createLine()); + System.out.println("Nice! I've marked this task as done:"); + System.out.println(taskList[Integer.parseInt(arr[1]) - 1].toString()); + System.out.println(lines.createLine()); + } catch (NullPointerException | IndexOutOfBoundsException e) + { + throw new DukeException((lines.createLine()) + "\n Which tasks have u completed? \n" + (lines.createLine())); + } + } catch (DukeException e) + { + System.out.println(e.getMessage()); + } finally + { + break; + } default: - taskList[taskNum++] = new Task(task); - System.out.println(lines.createLine()); - System.out.println(String.format("%45s","added: " + task) + "\n" + lines.createLine()); - break; + try { + Task newTask = track(arr); + taskList[taskNum++] = newTask; + System.out.println(lines.createLine()); + System.out.println("Got it. I've added this task: " + "\n" + newTask.toString()); + System.out.println("Now you have " + taskNum + " task(s) in the list." + "\n" + lines.createLine()); + break; + } + catch (DukeException e) + { + System.out.println(e.getMessage()); + } + } + } + + private Task track(String[] arr) throws DukeException + { + try + { + if (arr[0].equals("todo")) + { + checkDescription(arr, "todo"); + return new Todo(arr[1]); + } else if (arr[0].equals("deadline")) + { + checkDescription(arr, "deadline"); + return new Deadline(arr[1]); + } else if (arr[0].equals("event")) + { + checkDescription(arr, "event"); + return new Event(arr[1]); + } else + { + throw new DukeException((lines.createLine()) + "\nI'm sorry, but I don't know what that means.\n" + (lines.createLine()), new IllegalArgumentException()); + } + } + catch(DukeException e) + { + throw e; + } + } + + private void checkDescription (String[] arr, String taskType) throws DukeException + { + if (arr.length < 2) + { + throw new DukeException((lines.createLine()) + "\n The description of a " + taskType + " cannot be empty.\n" + (lines.createLine())); } } public String showTaskList() { - //String output = ""; - String output = lines.createLine() + "\n" + String.format("%45s","Here are the tasks in your list:" + "\n"); + String output = lines.createLine() + "\n" + "Here are the tasks in your list:" + "\n"; for (int idx = 0; idx <= taskNum; idx ++) { Task task = taskList[idx]; @@ -47,11 +101,27 @@ public String showTaskList() } else { - //output += String.format("%45s",((idx+1) + ". " + taskList[idx]) + "\n"); - output += String.format("%45s",((idx+1) + ". " + task.toString()) + "\n"); + output += ((idx + 1) + "." + task.toString() + "\n"); } } - //System.out.println(String.format("%44s","Your Task List")); return output + lines.createLine(); } + + public String toString() + { + String output = lines.createLine() + "\n" + "Here are the tasks in your list: \n"; + for (int index = 0; index <= taskNum; index ++) + { + Task task = taskList[index]; + if (task == null) + { + break; + } else + { + output += ((index + 1) + "." + task.toString() + "\n"); + } + } + return output + lines.createLine() + "\n"; + } } + diff --git a/src/main/java/Todo.java b/src/main/java/Todo.java new file mode 100644 index 00000000..4f1851ac --- /dev/null +++ b/src/main/java/Todo.java @@ -0,0 +1,14 @@ +public class Todo extends Task +{ + public Todo(String description) + { + super(description); + } + + @Override + public String toString() + { + String output = "[T][" + super.getStatus()+ "]" + " " + super.taskDesc; + return output; + } +} \ No newline at end of file From 60073783a4a762b85f38aae1cab575586064df93 Mon Sep 17 00:00:00 2001 From: Yan Ru Date: Thu, 7 Oct 2021 16:36:05 +0800 Subject: [PATCH 06/26] Level 6 - Delete and use Java Collections classes --- src/main/java/Deadline.java | 2 +- src/main/java/Event.java | 2 +- src/main/java/Task.java | 2 + src/main/java/TaskList.java | 83 +++++++++++++++++++++---------------- 4 files changed, 51 insertions(+), 38 deletions(-) diff --git a/src/main/java/Deadline.java b/src/main/java/Deadline.java index ca595099..24c81dfa 100644 --- a/src/main/java/Deadline.java +++ b/src/main/java/Deadline.java @@ -9,7 +9,7 @@ public Deadline(String description) throws DukeException int divider = description.indexOf("/by"); if (divider == -1 || (divider == description.length() -3)) { - throw new DukeException((lines.createLine()) + "\n The date/time cannot be empty." + (lines.createLine())); + throw new DukeException((lines.createLine()) + "\n The date/time cannot be empty. \n" + (lines.createLine())); } dateTime = description.substring(divider + 4, description.length()); super.taskDesc = super.taskDesc.substring(0,divider); diff --git a/src/main/java/Event.java b/src/main/java/Event.java index ea1c0174..e9ca9437 100644 --- a/src/main/java/Event.java +++ b/src/main/java/Event.java @@ -9,7 +9,7 @@ public Event(String description) throws DukeException int divider = description.indexOf("/at"); if (divider == -1 || (divider == description.length() - 3)) { - throw new DukeException((lines.createLine()) + "\n The date/time cannot be empty." + (lines.createLine())); + throw new DukeException((lines.createLine()) + "\n The date/time cannot be empty.\n" + (lines.createLine())); } dateTime = description.substring(divider + 4, description.length()); super.taskDesc = super.taskDesc.substring(0,divider); diff --git a/src/main/java/Task.java b/src/main/java/Task.java index f2c5c1ba..807b262b 100644 --- a/src/main/java/Task.java +++ b/src/main/java/Task.java @@ -2,11 +2,13 @@ public abstract class Task { protected String taskDesc; protected boolean isDone; + protected boolean deleted; public Task(String taskDesc) { this.taskDesc = taskDesc; this.isDone = false; + this.deleted = false; } public String getStatus() diff --git a/src/main/java/TaskList.java b/src/main/java/TaskList.java index aee62a0c..9c806c87 100644 --- a/src/main/java/TaskList.java +++ b/src/main/java/TaskList.java @@ -1,7 +1,9 @@ +import java.util.ArrayList; + public class TaskList { - //Assume there will be no more than 100 tasks - Task[] taskList = new Task[100]; + ArrayList taskList = new ArrayList<> (); + int taskNum = 0; Border lines = new Border(); @@ -11,7 +13,7 @@ public void addTask(String task) switch (arr[0]) { case "list": - System.out.println(showTaskList()); + System.out.println(toString()); break; case "done": @@ -21,14 +23,18 @@ public void addTask(String task) { if (arr.length < 2) throw new DukeException((lines.createLine()) + "\n Which tasks have u completed? \n" + (lines.createLine())); - taskList[Integer.parseInt(arr[1]) - 1].markAsDone(); + taskList.get(Integer.parseInt(arr[1]) - 1).markAsDone(); System.out.println(lines.createLine()); System.out.println("Nice! I've marked this task as done:"); - System.out.println(taskList[Integer.parseInt(arr[1]) - 1].toString()); + System.out.println(taskList.get(Integer.parseInt(arr[1]) - 1).toString()); System.out.println(lines.createLine()); } catch (NullPointerException | IndexOutOfBoundsException e) { - throw new DukeException((lines.createLine()) + "\n Which tasks have u completed? \n" + (lines.createLine())); + throw new DukeException((lines.createLine()) + "\n Index out of bounds. \n" + (lines.createLine())); + } + catch(NumberFormatException e) + { + throw new DukeException((lines.createLine()) + "\n Please enter the task number that you would like to delete. \n" + (lines.createLine())); } } catch (DukeException e) { @@ -38,10 +44,39 @@ public void addTask(String task) break; } - default: + case "delete": + try { + try{ + if (arr.length < 2) + throw new DukeException((lines.createLine()) + "\n Which tasks have u completed? \n" + (lines.createLine())); + Task removeTask = taskList.get(Integer.parseInt(arr[1])-1); + taskList.remove(removeTask); + taskNum --; + System.out.println(lines.createLine()); + System.out.println("Noted. I've removed this task: \n" + removeTask.toString()); + System.out.println("Now you have " + taskNum + " in the list."); + System.out.println(lines.createLine()); + } catch (NullPointerException | IndexOutOfBoundsException e) + { + throw new DukeException (lines.createLine() + "\n Index out of bounds. \n" + (lines.createLine())); + } + catch(NumberFormatException e) + { + throw new DukeException((lines.createLine()) + "\n Please enter the task number that you would like to delete. \n" + (lines.createLine())); + } + } catch (DukeException e) + { + System.out.println(e.getMessage()); + } finally + { + break; + } + + default: try { Task newTask = track(arr); - taskList[taskNum++] = newTask; + taskList.add(newTask); + taskNum ++; System.out.println(lines.createLine()); System.out.println("Got it. I've added this task: " + "\n" + newTask.toString()); System.out.println("Now you have " + taskNum + " task(s) in the list." + "\n" + lines.createLine()); @@ -89,39 +124,15 @@ private void checkDescription (String[] arr, String taskType) throws DukeExcepti } } - public String showTaskList() + public String toString() { String output = lines.createLine() + "\n" + "Here are the tasks in your list:" + "\n"; - for (int idx = 0; idx <= taskNum; idx ++) + for (int idx = 0; idx < taskNum; idx ++) { - Task task = taskList[idx]; - if (task==null) - { - break; - } - else - { - output += ((idx + 1) + "." + task.toString() + "\n"); - } + Task task = taskList.get(idx); + output += ((idx + 1) + "." + task.toString() + "\n"); } return output + lines.createLine(); } - - public String toString() - { - String output = lines.createLine() + "\n" + "Here are the tasks in your list: \n"; - for (int index = 0; index <= taskNum; index ++) - { - Task task = taskList[index]; - if (task == null) - { - break; - } else - { - output += ((index + 1) + "." + task.toString() + "\n"); - } - } - return output + lines.createLine() + "\n"; - } } From 6f2e0b9d1e5792b7dd500ed04017697ecb3a9f60 Mon Sep 17 00:00:00 2001 From: Yan Ru Date: Thu, 7 Oct 2021 18:01:16 +0800 Subject: [PATCH 07/26] Level 6 - Use Enums --- src/main/java/Activity.java | 28 +++++++ src/main/java/TaskList.java | 157 ++++++++++++++++-------------------- 2 files changed, 99 insertions(+), 86 deletions(-) create mode 100644 src/main/java/Activity.java diff --git a/src/main/java/Activity.java b/src/main/java/Activity.java new file mode 100644 index 00000000..e9fe5ba8 --- /dev/null +++ b/src/main/java/Activity.java @@ -0,0 +1,28 @@ +public enum Activity { + LIST("list"), TODO ("todo"), EVENT ("event"), DEADLINE ("deadline"), DONE ("done"), DELETE ("delete"); + + private String activityName; + + Activity (String activityName) + { + this.activityName = activityName; + } + + public String getActivityName() + { + return activityName; + } + + public static Activity getByAction (String keyword) throws DukeException + { + for (Activity activity : values()) + { + if (activity.getActivityName().equals(keyword)) + { + return activity; + } + } + Border lines = new Border(); + throw new DukeException((lines.createLine()) + "\nI'm sorry, but I don't know what that means.\n" + (lines.createLine())); + } +} diff --git a/src/main/java/TaskList.java b/src/main/java/TaskList.java index 9c806c87..0d444ec4 100644 --- a/src/main/java/TaskList.java +++ b/src/main/java/TaskList.java @@ -7,107 +7,92 @@ public class TaskList int taskNum = 0; Border lines = new Border(); - public void addTask(String task) - { + public void addTask(String task) { String[] arr = task.split(" ", 2); - switch (arr[0]) - { - case "list": - System.out.println(toString()); - break; - case "done": - try - { - try - { - if (arr.length < 2) - throw new DukeException((lines.createLine()) + "\n Which tasks have u completed? \n" + (lines.createLine())); - taskList.get(Integer.parseInt(arr[1]) - 1).markAsDone(); - System.out.println(lines.createLine()); - System.out.println("Nice! I've marked this task as done:"); - System.out.println(taskList.get(Integer.parseInt(arr[1]) - 1).toString()); - System.out.println(lines.createLine()); - } catch (NullPointerException | IndexOutOfBoundsException e) - { - throw new DukeException((lines.createLine()) + "\n Index out of bounds. \n" + (lines.createLine())); - } - catch(NumberFormatException e) - { - throw new DukeException((lines.createLine()) + "\n Please enter the task number that you would like to delete. \n" + (lines.createLine())); - } - } catch (DukeException e) - { - System.out.println(e.getMessage()); - } finally - { + try { + Activity activity = Activity.getByAction(arr[0]); + switch (activity) { + case LIST: + System.out.println(this); break; - } - case "delete": - try { - try{ - if (arr.length < 2) - throw new DukeException((lines.createLine()) + "\n Which tasks have u completed? \n" + (lines.createLine())); - Task removeTask = taskList.get(Integer.parseInt(arr[1])-1); - taskList.remove(removeTask); - taskNum --; - System.out.println(lines.createLine()); - System.out.println("Noted. I've removed this task: \n" + removeTask.toString()); - System.out.println("Now you have " + taskNum + " in the list."); - System.out.println(lines.createLine()); - } catch (NullPointerException | IndexOutOfBoundsException e) - { - throw new DukeException (lines.createLine() + "\n Index out of bounds. \n" + (lines.createLine())); + case DONE: + try { + try { + if (arr.length < 2) + throw new DukeException((lines.createLine()) + "\n Which tasks have u completed? \n" + (lines.createLine())); + taskList.get(Integer.parseInt(arr[1]) - 1).markAsDone(); + System.out.println(lines.createLine()); + System.out.println("Nice! I've marked this task as done:"); + System.out.println(taskList.get(Integer.parseInt(arr[1]) - 1).toString()); + System.out.println(lines.createLine()); + } catch (NullPointerException | IndexOutOfBoundsException e) { + throw new DukeException((lines.createLine()) + "\n Index out of bounds. \n" + (lines.createLine())); + } catch (NumberFormatException e) { + throw new DukeException((lines.createLine()) + "\n Please enter the task number that you have completed. \n" + (lines.createLine())); + } + } catch (DukeException e) { + System.out.println(e.getMessage()); + } finally { + break; } - catch(NumberFormatException e) - { - throw new DukeException((lines.createLine()) + "\n Please enter the task number that you would like to delete. \n" + (lines.createLine())); + case DELETE: + try { + try { + if (arr.length < 2) + throw new DukeException((lines.createLine()) + "\n Which tasks have u completed? \n" + (lines.createLine())); + Task removeTask = taskList.get(Integer.parseInt(arr[1]) - 1); + taskList.remove(removeTask); + taskNum--; + System.out.println(lines.createLine()); + System.out.println("Noted. I've removed this task: \n" + removeTask.toString()); + System.out.println("Now you have " + taskNum + " in the list."); + System.out.println(lines.createLine()); + } catch (NullPointerException | IndexOutOfBoundsException e) { + throw new DukeException(lines.createLine() + "\n Index out of bounds. \n" + (lines.createLine())); + } catch (NumberFormatException e) { + throw new DukeException((lines.createLine()) + "\n Please enter the task number that you would like to delete. \n" + (lines.createLine())); + } + } catch (DukeException e) { + System.out.println(e.getMessage()); + } finally { + break; } - } catch (DukeException e) - { - System.out.println(e.getMessage()); - } finally - { - break; - } - default: - try { - Task newTask = track(arr); - taskList.add(newTask); - taskNum ++; - System.out.println(lines.createLine()); - System.out.println("Got it. I've added this task: " + "\n" + newTask.toString()); - System.out.println("Now you have " + taskNum + " task(s) in the list." + "\n" + lines.createLine()); - break; + default: + try { + Task newTask = track(activity, arr); + taskList.add(newTask); + taskNum++; + System.out.println(lines.createLine()); + System.out.println("Got it. I've added this task: " + "\n" + newTask.toString()); + System.out.println("Now you have " + taskNum + " task(s) in the list." + "\n" + lines.createLine()); + break; + } catch (DukeException e) { + System.out.println(e.getMessage()); } - catch (DukeException e) - { - System.out.println(e.getMessage()); } + } catch (DukeException e) { + System.out.println(e.getMessage()); } } - private Task track(String[] arr) throws DukeException + + private Task track(Activity activity, String[] arr) throws DukeException { try { - if (arr[0].equals("todo")) - { - checkDescription(arr, "todo"); - return new Todo(arr[1]); - } else if (arr[0].equals("deadline")) - { - checkDescription(arr, "deadline"); - return new Deadline(arr[1]); - } else if (arr[0].equals("event")) - { - checkDescription(arr, "event"); - return new Event(arr[1]); - } else - { - throw new DukeException((lines.createLine()) + "\nI'm sorry, but I don't know what that means.\n" + (lines.createLine()), new IllegalArgumentException()); + switch (activity) { + case TODO: + checkDescription(arr, "todo"); + return new Todo(arr[1]); + case DEADLINE: + checkDescription(arr, "deadline"); + return new Deadline(arr[1]); + default: + checkDescription(arr, "event"); + return new Event(arr[1]); } } catch(DukeException e) From 478ef8860161ec2c4697c5ab4c59643fccc4bc9c Mon Sep 17 00:00:00 2001 From: Yan Ru Date: Tue, 12 Oct 2021 16:32:38 +0800 Subject: [PATCH 08/26] Level 7 - Save File --- data/duke.txt | 1 + src/main/java/FileWriterClass.java | 25 ++++++++++++++++++++ src/main/java/TaskList.java | 4 ++++ text-ui-test/EXPECTED.TXT | 37 ++++++++++++++++++++++++------ text-ui-test/input.txt | 8 +++++++ text-ui-test/runtest.bat | 4 +++- 6 files changed, 71 insertions(+), 8 deletions(-) create mode 100644 data/duke.txt create mode 100644 src/main/java/FileWriterClass.java diff --git a/data/duke.txt b/data/duke.txt new file mode 100644 index 00000000..44780d32 --- /dev/null +++ b/data/duke.txt @@ -0,0 +1 @@ +[[T][✘] borrow book, [D][✓] return book (by: Sunday), [E][✘] project meeting (at: Mon 2-4pm), [T][✘] return book] \ No newline at end of file diff --git a/src/main/java/FileWriterClass.java b/src/main/java/FileWriterClass.java new file mode 100644 index 00000000..ff5317a7 --- /dev/null +++ b/src/main/java/FileWriterClass.java @@ -0,0 +1,25 @@ +import java.io.FileWriter; +import java.io.IOException; + +public class FileWriterClass { + public static void writeFile(String filePath, String addText) + { + Border lines = new Border(); + try + { + try + { + FileWriter fw = new FileWriter(filePath); + System.out.println("Written in " + filePath); + fw.write(addText); + fw.close(); + } catch (IOException e) + { + throw new DukeException((lines.createLine()) + "\n Please check. Error message: " + e.getMessage() + "\n" + (lines.createLine())); + } + } catch (DukeException e) + { + System.out.println(e.getMessage()); + } + } +} diff --git a/src/main/java/TaskList.java b/src/main/java/TaskList.java index 0d444ec4..1936c056 100644 --- a/src/main/java/TaskList.java +++ b/src/main/java/TaskList.java @@ -6,6 +6,7 @@ public class TaskList int taskNum = 0; Border lines = new Border(); + String filePath = "data/duke.txt"; public void addTask(String task) { String[] arr = task.split(" ", 2); @@ -23,6 +24,7 @@ public void addTask(String task) { if (arr.length < 2) throw new DukeException((lines.createLine()) + "\n Which tasks have u completed? \n" + (lines.createLine())); taskList.get(Integer.parseInt(arr[1]) - 1).markAsDone(); + FileWriterClass.writeFile(filePath,taskList.toString()); System.out.println(lines.createLine()); System.out.println("Nice! I've marked this task as done:"); System.out.println(taskList.get(Integer.parseInt(arr[1]) - 1).toString()); @@ -44,6 +46,7 @@ public void addTask(String task) { throw new DukeException((lines.createLine()) + "\n Which tasks have u completed? \n" + (lines.createLine())); Task removeTask = taskList.get(Integer.parseInt(arr[1]) - 1); taskList.remove(removeTask); + FileWriterClass.writeFile(filePath, taskList.toString()); taskNum--; System.out.println(lines.createLine()); System.out.println("Noted. I've removed this task: \n" + removeTask.toString()); @@ -64,6 +67,7 @@ public void addTask(String task) { try { Task newTask = track(activity, arr); taskList.add(newTask); + FileWriterClass.writeFile(filePath, taskList.toString()); taskNum++; System.out.println(lines.createLine()); System.out.println("Got it. I've added this task: " + "\n" + newTask.toString()); diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 657e74f6..d054f24a 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,7 +1,30 @@ -Hello from - ____ _ -| _ \ _ _| | _____ -| | | | | | | |/ / _ \ -| |_| | |_| | < __/ -|____/ \__,_|_|\_\___| - + ____________________________________________________________ + Hello! I'm Duke + What can I do for you? + ____________________________________________________________ + ____________________________________________________________ + Got it. I've added this task: + [T][✗] borrow book + Now you have 5 tasks in the list. + ____________________________________________________________ + ____________________________________________________________ + Here are the tasks in your list: + 1.[T][✓] read book + 2.[D][✗] return book (by: June 6th) + 3.[E][✗] project meeting (at: Aug 6th 2-4pm) + 4.[T][✓] join sports club + 5.[T][✗] borrow book + ____________________________________________________________ + ____________________________________________________________ + Got it. I've added this task: + [D][✗] return book (by: Sunday) + Now you have 6 tasks in the list. + ____________________________________________________________ + ____________________________________________________________ + Got it. I've added this task: + [E][✗] project meeting (at: Mon 2-4pm) + Now you have 7 tasks in the list. + ____________________________________________________________ + ____________________________________________________________ + Bye. Hope to see you again soon! + ____________________________________________________________ \ No newline at end of file diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index e69de29b..9a435588 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -0,0 +1,8 @@ +todo borrow book +list +deadline return book /by Sunday +event project meeting /at Mon 2-4pm +done 2 +todo return book +list +bye \ No newline at end of file diff --git a/text-ui-test/runtest.bat b/text-ui-test/runtest.bat index 08737446..69b9e008 100644 --- a/text-ui-test/runtest.bat +++ b/text-ui-test/runtest.bat @@ -7,7 +7,7 @@ REM delete output from previous run if exist ACTUAL.TXT del ACTUAL.TXT REM compile the code into the bin folder -javac -cp ..\src\main\java -Xlint:none -d ..\bin ..\src\main\java\*.java +javac -cp ..\src\main\java -d ..\bin ..\src\main\java\*.java IF ERRORLEVEL 1 ( echo ********** BUILD FAILURE ********** exit /b 1 @@ -19,3 +19,5 @@ java -classpath ..\bin Duke < input.txt > ACTUAL.TXT REM compare the output to the expected output FC ACTUAL.TXT EXPECTED.TXT + +PAUSE \ No newline at end of file From 67f5ab00b7b66fe7ebd1dafd7d69c29f4ce8e33e Mon Sep 17 00:00:00 2001 From: Yan Ru Date: Mon, 25 Oct 2021 15:58:17 +0800 Subject: [PATCH 09/26] Level 8 - Dates and Times --- data/duke.txt | 2 +- src/main/java/DateTime.java | 59 +++++++++++++++++++++++++++++++++++++ src/main/java/Deadline.java | 8 ++--- src/main/java/Event.java | 8 ++--- 4 files changed, 68 insertions(+), 9 deletions(-) create mode 100644 src/main/java/DateTime.java diff --git a/data/duke.txt b/data/duke.txt index 44780d32..e98a2e3b 100644 --- a/data/duke.txt +++ b/data/duke.txt @@ -1 +1 @@ -[[T][✘] borrow book, [D][✓] return book (by: Sunday), [E][✘] project meeting (at: Mon 2-4pm), [T][✘] return book] \ No newline at end of file +[[D][✘] return book (by: 2nd of Dec, 18:00)] \ No newline at end of file diff --git a/src/main/java/DateTime.java b/src/main/java/DateTime.java new file mode 100644 index 00000000..ca56c5f3 --- /dev/null +++ b/src/main/java/DateTime.java @@ -0,0 +1,59 @@ +import java.time.format.DateTimeFormatter; +import java.time.LocalTime; +import java.time.LocalDateTime; + +public class DateTime { + private LocalTime endDateTime; + private LocalDateTime startDateTime; + private String displayDateTime; + private static DateTimeFormatter getDay = DateTimeFormatter.ofPattern("dd"); + private static DateTimeFormatter getTime = DateTimeFormatter.ofPattern("HH:mm"); + private static DateTimeFormatter getMonth= DateTimeFormatter.ofPattern("MMM"); + + public static DateTime setDeadline(String dateTime) { + DateTime deadlineDateTime = new DateTime(); + DateTimeFormatter toFormat = DateTimeFormatter.ofPattern("d/MM/yyyy HHmm"); + deadlineDateTime.startDateTime = LocalDateTime.parse(dateTime, toFormat); + deadlineDateTime.displayDateTime = getDayOfMonth(deadlineDateTime.startDateTime.format(getDay)) + " of " + deadlineDateTime.startDateTime.format(getMonth) + + ", " + deadlineDateTime.startDateTime.format(getTime); + + return deadlineDateTime; + } + + public static DateTime setEventTime (String dateTime) throws DukeException { + Border lines = new Border(); + DateTime eventDateTime = new DateTime(); + int divider = dateTime.indexOf("-"); + if (divider == -1 || (divider == dateTime.length() - 1)) { + throw new DukeException((lines.createLine()) + "\n Need to have end time. \n" + (lines.createLine())); + } + + String end = dateTime.substring(divider + 1, dateTime.length()); + DateTimeFormatter toFormat = DateTimeFormatter.ofPattern("d/MM/yyyy HHmm"); + eventDateTime.startDateTime = LocalDateTime.parse(dateTime, toFormat); + eventDateTime.endDateTime = LocalTime.parse(end, DateTimeFormatter.ofPattern("HHmm")); + eventDateTime.displayDateTime = getDayOfMonth(eventDateTime.startDateTime.format(getDay)) + " " + eventDateTime.startDateTime.format(getMonth) + + " " + eventDateTime.startDateTime.format(getTime) + "-" + eventDateTime.endDateTime.format(getTime); + return eventDateTime; + } + + private static String getDayOfMonth(String dd) { + if (dd.substring(0,1).equals("0")){ + dd = dd.substring(1,dd.length()); + } + int day = Integer.parseInt(dd); + if (day >= 11 && day <= 31) { + return day + "th"; + } + switch (day & 10) { + case 1: return day + "st"; + case 2: return day + "nd"; + case 3: return day + "rd"; + default: return day + "th"; + } + } + + public String toString() { + return displayDateTime; + } +} diff --git a/src/main/java/Deadline.java b/src/main/java/Deadline.java index 24c81dfa..17fea766 100644 --- a/src/main/java/Deadline.java +++ b/src/main/java/Deadline.java @@ -1,6 +1,6 @@ public class Deadline extends Task { - String dateTime; + DateTime dateTime; Border lines = new Border(); public Deadline(String description) throws DukeException @@ -9,16 +9,16 @@ public Deadline(String description) throws DukeException int divider = description.indexOf("/by"); if (divider == -1 || (divider == description.length() -3)) { - throw new DukeException((lines.createLine()) + "\n The date/time cannot be empty. \n" + (lines.createLine())); + throw new DukeException((lines.createLine()) + "\n Incorrect deadline format. \n Please key in deadline (task) /by d/mm/yyyy HHmm \n " + (lines.createLine())); } - dateTime = description.substring(divider + 4, description.length()); + dateTime = DateTime.setDeadline(description.substring(divider + 4, description.length())); super.taskDesc = super.taskDesc.substring(0,divider); } @Override public String toString() { - String output = "[D][" + super.getStatus() + "]" + " " + super.taskDesc + "(by: " + dateTime + ")"; + String output = "[D][" + super.getStatus() + "]" + " " + super.taskDesc + "(by: " + dateTime.toString() + ")"; return output; } } diff --git a/src/main/java/Event.java b/src/main/java/Event.java index e9ca9437..5618b711 100644 --- a/src/main/java/Event.java +++ b/src/main/java/Event.java @@ -1,6 +1,6 @@ public class Event extends Task { - String dateTime; + DateTime dateTime; Border lines = new Border(); public Event(String description) throws DukeException @@ -9,16 +9,16 @@ public Event(String description) throws DukeException int divider = description.indexOf("/at"); if (divider == -1 || (divider == description.length() - 3)) { - throw new DukeException((lines.createLine()) + "\n The date/time cannot be empty.\n" + (lines.createLine())); + throw new DukeException((lines.createLine()) + "\n Incorrect deadline format. \n Please key in deadline (task) /by d/mm/yyyy HHmm \n " + (lines.createLine())); } - dateTime = description.substring(divider + 4, description.length()); + dateTime = DateTime.setEventTime(description.substring(divider + 4, description.length())); super.taskDesc = super.taskDesc.substring(0,divider); } @Override public String toString() { - String output = "[E][" + super.getStatus() + "]" + " " + super.taskDesc + "(at: " + dateTime + ")"; + String output = "[E][" + super.getStatus() + "]" + " " + super.taskDesc + "(at: " + dateTime.toString() + ")"; return output; } } From 0947be9d3db0aa1d2eb2e48f644ae655bc9a534b Mon Sep 17 00:00:00 2001 From: Yan Ru Date: Thu, 28 Oct 2021 09:35:42 +0800 Subject: [PATCH 10/26] Level 7.1 - Implement more oop classes, fix bugs, code refactoring and follow coding standard --- src/main/java/Activity.java | 28 ------- src/main/java/AddCommand.java | 34 +++++++++ src/main/java/Border.java | 11 --- src/main/java/Command.java | 9 +++ src/main/java/DateTime.java | 38 ++++++---- src/main/java/Deadline.java | 19 ++--- src/main/java/DeleteCommand.java | 23 ++++++ src/main/java/Duke.java | 47 ++++++++---- src/main/java/DukeException.java | 6 +- src/main/java/EditCommand.java | 26 +++++++ src/main/java/Event.java | 10 +-- src/main/java/ExitCommand.java | 9 +++ src/main/java/FileWriterClass.java | 25 ------ src/main/java/FullCommand.java | 29 +++++++ src/main/java/ListCommand.java | 18 +++++ src/main/java/Parser.java | 27 +++++++ src/main/java/Storage.java | 117 +++++++++++++++++++++++++++++ src/main/java/Task.java | 9 +-- src/main/java/TaskList.java | 84 +++++++++++++++++---- src/main/java/Todo.java | 6 +- src/main/java/Ui.java | 67 +++++++++++++++++ 21 files changed, 506 insertions(+), 136 deletions(-) delete mode 100644 src/main/java/Activity.java create mode 100644 src/main/java/AddCommand.java delete mode 100644 src/main/java/Border.java create mode 100644 src/main/java/Command.java create mode 100644 src/main/java/DeleteCommand.java create mode 100644 src/main/java/EditCommand.java create mode 100644 src/main/java/ExitCommand.java delete mode 100644 src/main/java/FileWriterClass.java create mode 100644 src/main/java/FullCommand.java create mode 100644 src/main/java/ListCommand.java create mode 100644 src/main/java/Parser.java create mode 100644 src/main/java/Storage.java create mode 100644 src/main/java/Ui.java diff --git a/src/main/java/Activity.java b/src/main/java/Activity.java deleted file mode 100644 index e9fe5ba8..00000000 --- a/src/main/java/Activity.java +++ /dev/null @@ -1,28 +0,0 @@ -public enum Activity { - LIST("list"), TODO ("todo"), EVENT ("event"), DEADLINE ("deadline"), DONE ("done"), DELETE ("delete"); - - private String activityName; - - Activity (String activityName) - { - this.activityName = activityName; - } - - public String getActivityName() - { - return activityName; - } - - public static Activity getByAction (String keyword) throws DukeException - { - for (Activity activity : values()) - { - if (activity.getActivityName().equals(keyword)) - { - return activity; - } - } - Border lines = new Border(); - throw new DukeException((lines.createLine()) + "\nI'm sorry, but I don't know what that means.\n" + (lines.createLine())); - } -} diff --git a/src/main/java/AddCommand.java b/src/main/java/AddCommand.java new file mode 100644 index 00000000..d71ac32a --- /dev/null +++ b/src/main/java/AddCommand.java @@ -0,0 +1,34 @@ +public class AddCommand extends Command { + String specifics; + public AddCommand(FullCommand taskType, String specifics){ + super.type = taskType; + this.specifics = specifics; + } + + public boolean isExit(){ + return false; + } + + public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException{ + Task newTask; + switch(type){ + case TODO: + newTask = new Todo(specifics); + break; + case DEADLINE: + newTask = new Deadline(specifics); + break; + default: + newTask = new Event(specifics); + break; + } + tasks.addTask(newTask); + ui.readTask(newTask, tasks.taskNum); + if (tasks.isFirst){ + tasks.isFirst = !tasks.isFirst; + } else { + storage.writeToFile(System.lineSeparator()); + } + storage.writeToFile(newTask.toString()); + }; +} \ No newline at end of file diff --git a/src/main/java/Border.java b/src/main/java/Border.java deleted file mode 100644 index 0ba4b706..00000000 --- a/src/main/java/Border.java +++ /dev/null @@ -1,11 +0,0 @@ -public class Border { - public String createLine() - { - String lines = ""; - for (int a = 0; a < 50; a ++) - { - lines += "-"; - } - return lines; - } -} diff --git a/src/main/java/Command.java b/src/main/java/Command.java new file mode 100644 index 00000000..56fb5394 --- /dev/null +++ b/src/main/java/Command.java @@ -0,0 +1,9 @@ +public abstract class Command { + FullCommand type; + + public boolean isExit(){ + return type.getActivityName().equals("bye"); + } + + public abstract void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException; +} \ No newline at end of file diff --git a/src/main/java/DateTime.java b/src/main/java/DateTime.java index ca56c5f3..8f707902 100644 --- a/src/main/java/DateTime.java +++ b/src/main/java/DateTime.java @@ -6,34 +6,46 @@ public class DateTime { private LocalTime endDateTime; private LocalDateTime startDateTime; private String displayDateTime; - private static DateTimeFormatter getDay = DateTimeFormatter.ofPattern("dd"); - private static DateTimeFormatter getTime = DateTimeFormatter.ofPattern("HH:mm"); - private static DateTimeFormatter getMonth= DateTimeFormatter.ofPattern("MMM"); + private static DateTimeFormatter getTime = DateTimeFormatter.ofPattern("HHmm"); + private static DateTimeFormatter getStartDateTime = DateTimeFormatter.ofPattern("d/MM/yyyy HHmm"); public static DateTime setDeadline(String dateTime) { DateTime deadlineDateTime = new DateTime(); - DateTimeFormatter toFormat = DateTimeFormatter.ofPattern("d/MM/yyyy HHmm"); - deadlineDateTime.startDateTime = LocalDateTime.parse(dateTime, toFormat); - deadlineDateTime.displayDateTime = getDayOfMonth(deadlineDateTime.startDateTime.format(getDay)) + " of " + deadlineDateTime.startDateTime.format(getMonth) - + ", " + deadlineDateTime.startDateTime.format(getTime); + deadlineDateTime.startDateTime = LocalDateTime.parse(dateTime, getStartDateTime); + deadlineDateTime.displayDateTime = deadlineDateTime.startDateTime.format(getStartDateTime); + return deadlineDateTime; + } + public static DateTime readDeadLine(String dateTime){ + DateTime deadlineDateTime = new DateTime(); + deadlineDateTime.startDateTime = LocalDateTime.parse(dateTime, getStartDateTime); + deadlineDateTime.displayDateTime = deadlineDateTime.startDateTime.format(getStartDateTime); return deadlineDateTime; } public static DateTime setEventTime (String dateTime) throws DukeException { - Border lines = new Border(); DateTime eventDateTime = new DateTime(); int divider = dateTime.indexOf("-"); if (divider == -1 || (divider == dateTime.length() - 1)) { - throw new DukeException((lines.createLine()) + "\n Need to have end time. \n" + (lines.createLine())); + throw new DukeException("\n Please key in the end time."); } + String end = dateTime.substring(divider + 1, dateTime.length()); + eventDateTime.startDateTime = LocalDateTime.parse(dateTime.substring(0,divider), getStartDateTime); + eventDateTime.endDateTime = LocalTime.parse(end, DateTimeFormatter.ofPattern("HHmm")); + eventDateTime.displayDateTime = eventDateTime.startDateTime.format(getStartDateTime) + "-" + eventDateTime.endDateTime.format(getTime); + return eventDateTime; + } + public static DateTime readEventTime (String dateTime) throws DukeException { + DateTime eventDateTime = new DateTime(); + int divider = dateTime.indexOf("-"); + if (divider == -1 || (divider == dateTime.length() - 1)) { + throw new DukeException("\n Please key in the end time."); + } String end = dateTime.substring(divider + 1, dateTime.length()); - DateTimeFormatter toFormat = DateTimeFormatter.ofPattern("d/MM/yyyy HHmm"); - eventDateTime.startDateTime = LocalDateTime.parse(dateTime, toFormat); + eventDateTime.startDateTime = LocalDateTime.parse(dateTime.substring(0,divider), getStartDateTime); eventDateTime.endDateTime = LocalTime.parse(end, DateTimeFormatter.ofPattern("HHmm")); - eventDateTime.displayDateTime = getDayOfMonth(eventDateTime.startDateTime.format(getDay)) + " " + eventDateTime.startDateTime.format(getMonth) - + " " + eventDateTime.startDateTime.format(getTime) + "-" + eventDateTime.endDateTime.format(getTime); + eventDateTime.displayDateTime = eventDateTime.startDateTime.format(getStartDateTime) + "-" + eventDateTime.endDateTime.format(getTime); return eventDateTime; } diff --git a/src/main/java/Deadline.java b/src/main/java/Deadline.java index 17fea766..e36b9e0e 100644 --- a/src/main/java/Deadline.java +++ b/src/main/java/Deadline.java @@ -1,23 +1,24 @@ -public class Deadline extends Task -{ +public class Deadline extends Task { DateTime dateTime; - Border lines = new Border(); - - public Deadline(String description) throws DukeException - { + public Deadline(String description) throws DukeException { super(description); int divider = description.indexOf("/by"); if (divider == -1 || (divider == description.length() -3)) { - throw new DukeException((lines.createLine()) + "\n Incorrect deadline format. \n Please key in deadline (task) /by d/mm/yyyy HHmm \n " + (lines.createLine())); + throw new DukeException("\nIncorrect deadline format. \nPlease key in deadline (task) /by d/mm/yyyy HHmm"); } dateTime = DateTime.setDeadline(description.substring(divider + 4, description.length())); super.taskDesc = super.taskDesc.substring(0,divider); } + public Deadline(String description, DateTime dateTime){ + super(description); + System.out.println(dateTime); + this.dateTime = dateTime; + } + @Override - public String toString() - { + public String toString() { String output = "[D][" + super.getStatus() + "]" + " " + super.taskDesc + "(by: " + dateTime.toString() + ")"; return output; } diff --git a/src/main/java/DeleteCommand.java b/src/main/java/DeleteCommand.java new file mode 100644 index 00000000..c5c2ae37 --- /dev/null +++ b/src/main/java/DeleteCommand.java @@ -0,0 +1,23 @@ +public class DeleteCommand extends Command { + int index; + + public DeleteCommand(int index){ + this.index = index; + } + + public boolean isExit(){ + return false; + } + + public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException{ + try { + Task removeTask = tasks.remove(index); + ui.readDelete(removeTask, tasks.taskNum); + storage.editFile(tasks); + } catch (NullPointerException | IndexOutOfBoundsException e) { + throw new DukeException("Index out of bounds."); + } catch (NumberFormatException e) { + throw new DukeException("Please enter the task number that you would like to delete."); + } + } +} \ No newline at end of file diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 1e626227..79dc443e 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -1,21 +1,42 @@ import java.util.Scanner; public class Duke { - public static void main(String[] args) - { - TaskList taskList = new TaskList(); - Scanner sc = new Scanner(System.in); - Border lines = new Border(); - System.out.println(lines.createLine() + "\n" + "Hello! I'm Duke\nWhat can I do for you?" + "\n" + lines.createLine()); - String input = sc.nextLine(); - while (!input.equals("bye")) - { - taskList.addTask(input); - input = sc.nextLine(); + + private Storage storage; + private TaskList tasks; + private Ui ui; + + public Duke(String filePath) { + ui = new Ui(); + try { + storage = new Storage(filePath); + tasks = new TaskList(storage.load()); + } catch (DukeException e) { + ui.showLoadingError(); + tasks = new TaskList(); + } + } + + public void run() { + ui.showWelcome(); + boolean isExit = false; + while (!isExit) { + try { + String fullCommand = ui.readCommand(); + ui.showLine(); // show the divider line ("_______") + Command c = Parser.parse(fullCommand); + c.execute(tasks, ui, storage); + isExit = c.isExit(); + } catch (DukeException e) { + ui.showError(e.getMessage()); } + } + ui.showGoodbye(); + } - System.out.println(lines.createLine()); - System.out.println("Bye. Hope to see you again soon!" + "\n" + lines.createLine()); + public static void main(String[] args) + { + new Duke("data/tasks.txt").run(); } } diff --git a/src/main/java/DukeException.java b/src/main/java/DukeException.java index 117c9c3b..c1553a3a 100644 --- a/src/main/java/DukeException.java +++ b/src/main/java/DukeException.java @@ -1,11 +1,9 @@ public class DukeException extends Exception { - public DukeException(String message) - { + public DukeException(String message) { super(message); } - public DukeException(String message, Throwable cause) - { + public DukeException(String message, Throwable cause) { super(message,cause); } } diff --git a/src/main/java/EditCommand.java b/src/main/java/EditCommand.java new file mode 100644 index 00000000..47805e40 --- /dev/null +++ b/src/main/java/EditCommand.java @@ -0,0 +1,26 @@ +public class EditCommand extends Command { + int index; + + public boolean isExit(){ + return false; + } + + public EditCommand(int index){ + this.index = index; + } + + public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException{ + try { + if (index == -1){ + throw new IndexOutOfBoundsException(); + } + Task completedTask = tasks.complete(index); + ui.readDone(completedTask); + storage.editFile(tasks); + } catch (NullPointerException | IndexOutOfBoundsException e) { + throw new DukeException("Index out of bounds."); + } catch (NumberFormatException e) { + throw new DukeException("Please enter the task number that you would like to delete."); + } + } +} \ No newline at end of file diff --git a/src/main/java/Event.java b/src/main/java/Event.java index 5618b711..d4b5de86 100644 --- a/src/main/java/Event.java +++ b/src/main/java/Event.java @@ -1,23 +1,19 @@ public class Event extends Task { DateTime dateTime; - Border lines = new Border(); - - public Event(String description) throws DukeException - { + public Event(String description) throws DukeException { super(description); int divider = description.indexOf("/at"); if (divider == -1 || (divider == description.length() - 3)) { - throw new DukeException((lines.createLine()) + "\n Incorrect deadline format. \n Please key in deadline (task) /by d/mm/yyyy HHmm \n " + (lines.createLine())); + throw new DukeException("\n Incorrect event format. \n Please key in event (details) /at d/mm/yyyy (start time)HHmm-(end time)HHmm \n"); } dateTime = DateTime.setEventTime(description.substring(divider + 4, description.length())); super.taskDesc = super.taskDesc.substring(0,divider); } @Override - public String toString() - { + public String toString() { String output = "[E][" + super.getStatus() + "]" + " " + super.taskDesc + "(at: " + dateTime.toString() + ")"; return output; } diff --git a/src/main/java/ExitCommand.java b/src/main/java/ExitCommand.java new file mode 100644 index 00000000..d67a4e2a --- /dev/null +++ b/src/main/java/ExitCommand.java @@ -0,0 +1,9 @@ +public class ExitCommand extends Command { + public boolean isExit(){ + return true; + } + + public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException{ + storage.closeWriter(); + }; +} \ No newline at end of file diff --git a/src/main/java/FileWriterClass.java b/src/main/java/FileWriterClass.java deleted file mode 100644 index ff5317a7..00000000 --- a/src/main/java/FileWriterClass.java +++ /dev/null @@ -1,25 +0,0 @@ -import java.io.FileWriter; -import java.io.IOException; - -public class FileWriterClass { - public static void writeFile(String filePath, String addText) - { - Border lines = new Border(); - try - { - try - { - FileWriter fw = new FileWriter(filePath); - System.out.println("Written in " + filePath); - fw.write(addText); - fw.close(); - } catch (IOException e) - { - throw new DukeException((lines.createLine()) + "\n Please check. Error message: " + e.getMessage() + "\n" + (lines.createLine())); - } - } catch (DukeException e) - { - System.out.println(e.getMessage()); - } - } -} diff --git a/src/main/java/FullCommand.java b/src/main/java/FullCommand.java new file mode 100644 index 00000000..17a07ec9 --- /dev/null +++ b/src/main/java/FullCommand.java @@ -0,0 +1,29 @@ +public enum FullCommand { + LIST("list"), + TODO ("todo"), + EVENT ("event"), + DEADLINE ("deadline"), + DONE ("done"), + DELETE ("delete"), + BYE("bye"); + + private String activityName; + + FullCommand (String activityName) { + this.activityName = activityName; + } + + public String getActivityName() { + return activityName; + } + + public static FullCommand getByAction (String keyword) throws DukeException + { + for (FullCommand activity : values()) { + if (activity.getActivityName().equals(keyword)) { + return activity; + } + } + throw new DukeException("\nCommand does not exist."); + } +} diff --git a/src/main/java/ListCommand.java b/src/main/java/ListCommand.java new file mode 100644 index 00000000..48654248 --- /dev/null +++ b/src/main/java/ListCommand.java @@ -0,0 +1,18 @@ +public class ListCommand extends Command { + public boolean isExit(){ + return false; + } + int index; + public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException{ + try { + if (index == -1){ + throw new IndexOutOfBoundsException(); + } + ui.readList(tasks); + } catch (NullPointerException | IndexOutOfBoundsException e) { + throw new DukeException("Index out of bounds."); + } catch (NumberFormatException e) { + throw new DukeException("Please enter the task number that you would like to delete."); + } + }; +} \ No newline at end of file diff --git a/src/main/java/Parser.java b/src/main/java/Parser.java new file mode 100644 index 00000000..6191edc0 --- /dev/null +++ b/src/main/java/Parser.java @@ -0,0 +1,27 @@ +public class Parser { + TaskList schedule; + + public static Command parse(String instruction) throws DukeException { + String[] arr = instruction.split(" ", 2); + FullCommand command = FullCommand.getByAction(arr[0]); + try { + switch (command) { + case LIST: + return new ListCommand(); + case BYE: + return new ExitCommand(); + case DONE: + return new EditCommand(Integer.parseInt(arr[1]) - 1); + case DELETE: + return new DeleteCommand(Integer.parseInt(arr[1]) - 1); + default: + if (arr[1].equals("")){ + throw new DukeException(""); + } + return new AddCommand(command, arr[1]); + } + } catch (IndexOutOfBoundsException | DukeException e) { + throw new DukeException("Please provide commands in the format: (command type) (task details)"); + } +} +} diff --git a/src/main/java/Storage.java b/src/main/java/Storage.java new file mode 100644 index 00000000..a95cfa79 --- /dev/null +++ b/src/main/java/Storage.java @@ -0,0 +1,117 @@ +import java.io.FileNotFoundException; +import java.util.Scanner; +import java.util.ArrayList; +import java.io.IOException; +import java.io.File; +import java.io.FileWriter; + +public class Storage { + String filePath; + FileWriter fw; + ArrayList taskList; + boolean isAppend = true; + + public Storage(String filepath) throws DukeException{ + this.filePath = filepath; + this.createWriter(); + } + + private void createWriter() throws DukeException{ + try{ + fw = new FileWriter(filePath, isAppend); + } catch (IOException e) { + throw new DukeException("\n Invalid filepath. Please check. \n"); + } + } + + public void closeWriter() throws DukeException{ + try { + fw.close(); + } catch (IOException e) { + throw new DukeException("\n Invalid filepath. Please check. \n"); + } + } + + public void writeToFile(String textToAdd) throws DukeException{ + try { + fw.write(textToAdd + "\n"); + //System.out.println(textToAdd); + } catch (IOException e) { + throw new DukeException("\n Please check. Error message: " + e.getMessage() + "\n"); + } + } + + public void editFile(TaskList schedule) throws DukeException{ + try { + checkAppend(false); + for (Task task: schedule.getList()) { + fw.write(task.toString() + "\n"); + } + } catch (IOException e) { + throw new DukeException("failed to write to file"); + } + } + + private void checkAppend(boolean toAppend) throws DukeException { + closeWriter(); + isAppend = toAppend; + createWriter(); + } + + private Task read(String line) throws DukeException { + Task output; + switch (line.charAt(line.indexOf("[") + 1)) { + case 'T': + output = new Todo(line.substring(7, line.length())); + return output; + case 'D': + try { + int divider = line.indexOf("(by:"); + String input = line.substring(7, divider); + input += "/by " + DateTime.readDeadLine(line.substring(divider + 5, line.length() - 1)).toString(); + output = new Deadline(input); + return output; + } catch (Exception e) { + System.out.println(e.getMessage()); + throw new DukeException("\n Deadline task not stored properly. \n "); + } + default: + try { + int divider = line.indexOf("(at:"); + String input = line.substring(7, divider); + input += "/at " + DateTime.readEventTime(line.substring(divider + 5, line.length() - 1)).toString(); + output = new Event(input); + return output; + } catch (Exception e) { + throw new DukeException("\n Event task not stored properly. \n "); + } + } + } + + public Storage load() throws DukeException { + try { + taskList = new ArrayList<>(); + File f = new File(filePath); // create a File for the given file path + Scanner s = new Scanner(f); // create a Scanner using the File as the source + while (s.hasNextLine()) { + String line = s.nextLine(); + if (! line.equals("")) { + Task newTask = read(line.replace("\n", "")); + if (line.substring(4,5).equals("\u2713")){ + newTask.markAsDone(); + } + taskList.add(newTask); + } + } + return this; + } catch (FileNotFoundException e){ + throw new DukeException("\n File not found! \n "); + } catch (Exception e){ + throw new DukeException("Unforeseen load errors: " + e.getMessage()); + } + } + + public ArrayList getSchedule(){ + return taskList; + } +} \ No newline at end of file diff --git a/src/main/java/Task.java b/src/main/java/Task.java index 807b262b..11d1011c 100644 --- a/src/main/java/Task.java +++ b/src/main/java/Task.java @@ -4,21 +4,18 @@ public abstract class Task protected boolean isDone; protected boolean deleted; - public Task(String taskDesc) - { + public Task(String taskDesc) { this.taskDesc = taskDesc; this.isDone = false; this.deleted = false; } - public String getStatus() - { + public String getStatus() { //tick if done, else cross return (isDone ? "\u2713" : "\u2718"); } - public void markAsDone() - { + public void markAsDone() { this.isDone = true; } diff --git a/src/main/java/TaskList.java b/src/main/java/TaskList.java index 1936c056..75f05bb4 100644 --- a/src/main/java/TaskList.java +++ b/src/main/java/TaskList.java @@ -3,12 +3,75 @@ public class TaskList { ArrayList taskList = new ArrayList<> (); + public int taskNum; + public boolean isFirst; - int taskNum = 0; - Border lines = new Border(); - String filePath = "data/duke.txt"; + public TaskList(Storage storage){ + try { + this.taskList = storage.getSchedule(); + taskNum = taskList.size(); + if (taskNum == 0){ + isFirst = true; + } else { + isFirst = false; + } + } catch (Exception e){ + System.out.println(e.getMessage()); + } + } + + public TaskList(){ + this.taskList = taskList; + taskNum = taskList.size(); + if (taskNum == 0){ + isFirst = true; + } else { + isFirst = false; + } + System.out.println(taskNum); + } + + + public Task getTask(int index){ + return taskList.get(index); + } + + public Task complete(int index) + throws NullPointerException, IndexOutOfBoundsException, NumberFormatException, DukeException{ + Task completedTask = taskList.get(index); + completedTask.markAsDone(); + return completedTask; + } + + public void addTask(Task task) throws DukeException{ + taskList.add(task); + taskNum++; + } + + public Task remove(int index) + throws NullPointerException, IndexOutOfBoundsException, NumberFormatException, DukeException{ + Task removeTask = taskList.get(index); + taskList.remove(index); + taskNum--; + return removeTask; + } + + public ArrayList getList(){ + return taskList; + } + + public String toString() { + String output = ""; + for (int idx = 0; idx < taskNum; idx ++) + { + Task task = taskList.get(idx); + output += ((idx + 1) + "." + task.toString() + "\n"); + } + return output.substring(0, output.length() - 1); + } +} - public void addTask(String task) { + /*public void addTask(String task) { String[] arr = task.split(" ", 2); try { @@ -111,17 +174,6 @@ private void checkDescription (String[] arr, String taskType) throws DukeExcepti { throw new DukeException((lines.createLine()) + "\n The description of a " + taskType + " cannot be empty.\n" + (lines.createLine())); } - } + }*/ - public String toString() - { - String output = lines.createLine() + "\n" + "Here are the tasks in your list:" + "\n"; - for (int idx = 0; idx < taskNum; idx ++) - { - Task task = taskList.get(idx); - output += ((idx + 1) + "." + task.toString() + "\n"); - } - return output + lines.createLine(); - } -} diff --git a/src/main/java/Todo.java b/src/main/java/Todo.java index 4f1851ac..6e022f31 100644 --- a/src/main/java/Todo.java +++ b/src/main/java/Todo.java @@ -1,13 +1,11 @@ public class Todo extends Task { - public Todo(String description) - { + public Todo(String description) throws DukeException{ super(description); } @Override - public String toString() - { + public String toString() { String output = "[T][" + super.getStatus()+ "]" + " " + super.taskDesc; return output; } diff --git a/src/main/java/Ui.java b/src/main/java/Ui.java new file mode 100644 index 00000000..a9c54615 --- /dev/null +++ b/src/main/java/Ui.java @@ -0,0 +1,67 @@ +import java.util.Scanner; + +public class Ui { + Scanner sc = new Scanner(System.in); + public void showWelcome() { + String logo = "____ _ \n" + + "| _ \\ _ _| | _____ \n" + + "| | | | | | | |/ / _ \\\n" + + "| |_| | |_| | < __/\n" + + "|____/ \\__,_|_|\\_\\___|\n"; + showLine(); + System.out.println("Hello! I'm \n" + logo + "What can I do for you?"); + showLine(); + } + + public void showGoodbye(){ + System.out.println("Bye. Hope to see you again soon!"); + showLine(); + } + + public void showLine(){ + String output = ""; + for (int n = 0; n < 80; n++) { + output += "_"; + } + System.out.println(output); + } + + public void readTask(Task newTask, int taskNum){ + System.out.println("Got it. I've added this task:"); + System.out.println(newTask.toString()); + System.out.println("Now you have " + taskNum + " tasks in the list."); + showLine(); + } + + public void readDelete(Task removeTask, int taskNum){ + System.out.println("Noted. I've removed this task:"); + System.out.println(removeTask.toString()); + System.out.println("Now you have " + taskNum + " tasks in the list."); + showLine(); + } + + public void readDone(Task completed_Task){ + System.out.println("Nice! I've marked this task as done:"); + System.out.println(completed_Task.toString()); + showLine(); + } + + public void readList(TaskList tasks){ + System.out.println("Here are the tasks in your list: "); + System.out.println(tasks); + showLine(); + } + + public String readCommand() { + return sc.nextLine(); + } + + public void showError(String error_msg) { + System.out.println("Your error message: " + error_msg); + showLine(); + } + public void showLoadingError(){ + System.out.println("Loading Error!"); + showLine(); + } +} \ No newline at end of file From 3013de1dee3dd78b53dd32aa509e9cc6d9cfcc9f Mon Sep 17 00:00:00 2001 From: Yan Ru Date: Thu, 28 Oct 2021 11:12:12 +0800 Subject: [PATCH 11/26] Level 7.2 - Divide classes into packages --- src/main/java/Duke.java | 10 ++-- src/main/java/{ => command}/AddCommand.java | 9 +++- src/main/java/{ => command}/Command.java | 7 +++ .../java/{ => command}/DeleteCommand.java | 10 +++- src/main/java/{ => command}/EditCommand.java | 10 +++- src/main/java/{ => command}/ExitCommand.java | 9 +++- src/main/java/{ => command}/FullCommand.java | 4 ++ src/main/java/{ => command}/ListCommand.java | 9 +++- src/main/java/{ => datetime}/DateTime.java | 3 ++ .../java/{ => exception}/DukeException.java | 2 + src/main/java/{ => filewriter}/Storage.java | 5 ++ src/main/java/{ => parser}/Parser.java | 7 ++- src/main/java/{ => task}/Deadline.java | 5 ++ src/main/java/{ => task}/Event.java | 5 ++ src/main/java/{ => task}/Task.java | 2 + src/main/java/{ => task}/TaskList.java | 46 +++++++++++-------- src/main/java/{ => task}/Todo.java | 7 ++- src/main/java/{ => ui}/Ui.java | 5 ++ 18 files changed, 124 insertions(+), 31 deletions(-) rename src/main/java/{ => command}/AddCommand.java (87%) rename src/main/java/{ => command}/Command.java (69%) rename src/main/java/{ => command}/DeleteCommand.java (81%) rename src/main/java/{ => command}/EditCommand.java (83%) rename src/main/java/{ => command}/ExitCommand.java (59%) rename src/main/java/{ => command}/FullCommand.java (93%) rename src/main/java/{ => command}/ListCommand.java (81%) rename src/main/java/{ => datetime}/DateTime.java (98%) rename src/main/java/{ => exception}/DukeException.java (91%) rename src/main/java/{ => filewriter}/Storage.java (97%) rename src/main/java/{ => parser}/Parser.java (93%) rename src/main/java/{ => task}/Deadline.java (92%) rename src/main/java/{ => task}/Event.java (92%) rename src/main/java/{ => task}/Task.java (97%) rename src/main/java/{ => task}/TaskList.java (72%) rename src/main/java/{ => task}/Todo.java (64%) rename src/main/java/{ => ui}/Ui.java (97%) diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 79dc443e..b7c70839 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -1,4 +1,9 @@ -import java.util.Scanner; +import command.Command; +import exception.DukeException; +import filewriter.Storage; +import parser.Parser; +import task.TaskList; +import ui.Ui; public class Duke { @@ -34,8 +39,7 @@ public void run() { ui.showGoodbye(); } - public static void main(String[] args) - { + public static void main(String[] args) { new Duke("data/tasks.txt").run(); } } diff --git a/src/main/java/AddCommand.java b/src/main/java/command/AddCommand.java similarity index 87% rename from src/main/java/AddCommand.java rename to src/main/java/command/AddCommand.java index d71ac32a..c31d72e7 100644 --- a/src/main/java/AddCommand.java +++ b/src/main/java/command/AddCommand.java @@ -1,3 +1,10 @@ +package command; + +import exception.DukeException; +import filewriter.Storage; +import task.*; +import ui.Ui; + public class AddCommand extends Command { String specifics; public AddCommand(FullCommand taskType, String specifics){ @@ -9,7 +16,7 @@ public boolean isExit(){ return false; } - public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException{ + public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { Task newTask; switch(type){ case TODO: diff --git a/src/main/java/Command.java b/src/main/java/command/Command.java similarity index 69% rename from src/main/java/Command.java rename to src/main/java/command/Command.java index 56fb5394..7722d10b 100644 --- a/src/main/java/Command.java +++ b/src/main/java/command/Command.java @@ -1,3 +1,10 @@ +package command; + +import exception.DukeException; +import filewriter.Storage; +import task.*; +import ui.Ui; + public abstract class Command { FullCommand type; diff --git a/src/main/java/DeleteCommand.java b/src/main/java/command/DeleteCommand.java similarity index 81% rename from src/main/java/DeleteCommand.java rename to src/main/java/command/DeleteCommand.java index c5c2ae37..6fe60b09 100644 --- a/src/main/java/DeleteCommand.java +++ b/src/main/java/command/DeleteCommand.java @@ -1,3 +1,11 @@ +package command; + +import exception.DukeException; +import filewriter.Storage; +import task.Task; +import task.TaskList; +import ui.Ui; + public class DeleteCommand extends Command { int index; @@ -9,7 +17,7 @@ public boolean isExit(){ return false; } - public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException{ + public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { try { Task removeTask = tasks.remove(index); ui.readDelete(removeTask, tasks.taskNum); diff --git a/src/main/java/EditCommand.java b/src/main/java/command/EditCommand.java similarity index 83% rename from src/main/java/EditCommand.java rename to src/main/java/command/EditCommand.java index 47805e40..0b539495 100644 --- a/src/main/java/EditCommand.java +++ b/src/main/java/command/EditCommand.java @@ -1,3 +1,11 @@ +package command; + +import exception.DukeException; +import filewriter.Storage; +import task.Task; +import task.TaskList; +import ui.Ui; + public class EditCommand extends Command { int index; @@ -9,7 +17,7 @@ public EditCommand(int index){ this.index = index; } - public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException{ + public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { try { if (index == -1){ throw new IndexOutOfBoundsException(); diff --git a/src/main/java/ExitCommand.java b/src/main/java/command/ExitCommand.java similarity index 59% rename from src/main/java/ExitCommand.java rename to src/main/java/command/ExitCommand.java index d67a4e2a..846eaab6 100644 --- a/src/main/java/ExitCommand.java +++ b/src/main/java/command/ExitCommand.java @@ -1,9 +1,16 @@ +package command; + +import exception.DukeException; +import filewriter.Storage; +import task.TaskList; +import ui.Ui; + public class ExitCommand extends Command { public boolean isExit(){ return true; } - public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException{ + public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { storage.closeWriter(); }; } \ No newline at end of file diff --git a/src/main/java/FullCommand.java b/src/main/java/command/FullCommand.java similarity index 93% rename from src/main/java/FullCommand.java rename to src/main/java/command/FullCommand.java index 17a07ec9..c3d66ebe 100644 --- a/src/main/java/FullCommand.java +++ b/src/main/java/command/FullCommand.java @@ -1,3 +1,7 @@ +package command; + +import exception.DukeException; + public enum FullCommand { LIST("list"), TODO ("todo"), diff --git a/src/main/java/ListCommand.java b/src/main/java/command/ListCommand.java similarity index 81% rename from src/main/java/ListCommand.java rename to src/main/java/command/ListCommand.java index 48654248..431c546d 100644 --- a/src/main/java/ListCommand.java +++ b/src/main/java/command/ListCommand.java @@ -1,9 +1,16 @@ +package command; + +import exception.DukeException; +import filewriter.Storage; +import task.TaskList; +import ui.Ui; + public class ListCommand extends Command { public boolean isExit(){ return false; } int index; - public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException{ + public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { try { if (index == -1){ throw new IndexOutOfBoundsException(); diff --git a/src/main/java/DateTime.java b/src/main/java/datetime/DateTime.java similarity index 98% rename from src/main/java/DateTime.java rename to src/main/java/datetime/DateTime.java index 8f707902..0e3339b2 100644 --- a/src/main/java/DateTime.java +++ b/src/main/java/datetime/DateTime.java @@ -1,6 +1,9 @@ +package datetime; + import java.time.format.DateTimeFormatter; import java.time.LocalTime; import java.time.LocalDateTime; +import exception.DukeException; public class DateTime { private LocalTime endDateTime; diff --git a/src/main/java/DukeException.java b/src/main/java/exception/DukeException.java similarity index 91% rename from src/main/java/DukeException.java rename to src/main/java/exception/DukeException.java index c1553a3a..30ecd7f0 100644 --- a/src/main/java/DukeException.java +++ b/src/main/java/exception/DukeException.java @@ -1,3 +1,5 @@ +package exception; + public class DukeException extends Exception { public DukeException(String message) { diff --git a/src/main/java/Storage.java b/src/main/java/filewriter/Storage.java similarity index 97% rename from src/main/java/Storage.java rename to src/main/java/filewriter/Storage.java index a95cfa79..ee6396a7 100644 --- a/src/main/java/Storage.java +++ b/src/main/java/filewriter/Storage.java @@ -1,3 +1,8 @@ +package filewriter; + +import datetime.DateTime; +import exception.DukeException; +import task.*; import java.io.FileNotFoundException; import java.util.Scanner; import java.util.ArrayList; diff --git a/src/main/java/Parser.java b/src/main/java/parser/Parser.java similarity index 93% rename from src/main/java/Parser.java rename to src/main/java/parser/Parser.java index 6191edc0..7fa83e1b 100644 --- a/src/main/java/Parser.java +++ b/src/main/java/parser/Parser.java @@ -1,6 +1,9 @@ -public class Parser { - TaskList schedule; +package parser; + +import command.*; +import exception.DukeException; +public class Parser { public static Command parse(String instruction) throws DukeException { String[] arr = instruction.split(" ", 2); FullCommand command = FullCommand.getByAction(arr[0]); diff --git a/src/main/java/Deadline.java b/src/main/java/task/Deadline.java similarity index 92% rename from src/main/java/Deadline.java rename to src/main/java/task/Deadline.java index e36b9e0e..36f5ccf7 100644 --- a/src/main/java/Deadline.java +++ b/src/main/java/task/Deadline.java @@ -1,3 +1,8 @@ +package task; + +import datetime.DateTime; +import exception.DukeException; + public class Deadline extends Task { DateTime dateTime; public Deadline(String description) throws DukeException { diff --git a/src/main/java/Event.java b/src/main/java/task/Event.java similarity index 92% rename from src/main/java/Event.java rename to src/main/java/task/Event.java index d4b5de86..6df727bc 100644 --- a/src/main/java/Event.java +++ b/src/main/java/task/Event.java @@ -1,3 +1,8 @@ +package task; + +import datetime.DateTime; +import exception.DukeException; + public class Event extends Task { DateTime dateTime; diff --git a/src/main/java/Task.java b/src/main/java/task/Task.java similarity index 97% rename from src/main/java/Task.java rename to src/main/java/task/Task.java index 11d1011c..0654a0ac 100644 --- a/src/main/java/Task.java +++ b/src/main/java/task/Task.java @@ -1,3 +1,5 @@ +package task; + public abstract class Task { protected String taskDesc; diff --git a/src/main/java/TaskList.java b/src/main/java/task/TaskList.java similarity index 72% rename from src/main/java/TaskList.java rename to src/main/java/task/TaskList.java index 75f05bb4..0699d149 100644 --- a/src/main/java/TaskList.java +++ b/src/main/java/task/TaskList.java @@ -1,3 +1,9 @@ +package task; + +import exception.DukeException; +import filewriter.Storage; +import task.Task; + import java.util.ArrayList; public class TaskList @@ -43,7 +49,7 @@ public Task complete(int index) return completedTask; } - public void addTask(Task task) throws DukeException{ + public void addTask(Task task) throws DukeException { taskList.add(task); taskNum++; } @@ -85,7 +91,7 @@ public String toString() { try { try { if (arr.length < 2) - throw new DukeException((lines.createLine()) + "\n Which tasks have u completed? \n" + (lines.createLine())); + throw new exception.DukeException((lines.createLine()) + "\n Which tasks have u completed? \n" + (lines.createLine())); taskList.get(Integer.parseInt(arr[1]) - 1).markAsDone(); FileWriterClass.writeFile(filePath,taskList.toString()); System.out.println(lines.createLine()); @@ -93,11 +99,11 @@ public String toString() { System.out.println(taskList.get(Integer.parseInt(arr[1]) - 1).toString()); System.out.println(lines.createLine()); } catch (NullPointerException | IndexOutOfBoundsException e) { - throw new DukeException((lines.createLine()) + "\n Index out of bounds. \n" + (lines.createLine())); + throw new exception.DukeException((lines.createLine()) + "\n Index out of bounds. \n" + (lines.createLine())); } catch (NumberFormatException e) { - throw new DukeException((lines.createLine()) + "\n Please enter the task number that you have completed. \n" + (lines.createLine())); + throw new exception.DukeException((lines.createLine()) + "\n Please enter the task number that you have completed. \n" + (lines.createLine())); } - } catch (DukeException e) { + } catch (exception.DukeException e) { System.out.println(e.getMessage()); } finally { break; @@ -106,8 +112,8 @@ public String toString() { try { try { if (arr.length < 2) - throw new DukeException((lines.createLine()) + "\n Which tasks have u completed? \n" + (lines.createLine())); - Task removeTask = taskList.get(Integer.parseInt(arr[1]) - 1); + throw new exception.DukeException((lines.createLine()) + "\n Which tasks have u completed? \n" + (lines.createLine())); + task.Task removeTask = taskList.get(Integer.parseInt(arr[1]) - 1); taskList.remove(removeTask); FileWriterClass.writeFile(filePath, taskList.toString()); taskNum--; @@ -116,11 +122,11 @@ public String toString() { System.out.println("Now you have " + taskNum + " in the list."); System.out.println(lines.createLine()); } catch (NullPointerException | IndexOutOfBoundsException e) { - throw new DukeException(lines.createLine() + "\n Index out of bounds. \n" + (lines.createLine())); + throw new exception.DukeException(lines.createLine() + "\n Index out of bounds. \n" + (lines.createLine())); } catch (NumberFormatException e) { - throw new DukeException((lines.createLine()) + "\n Please enter the task number that you would like to delete. \n" + (lines.createLine())); + throw new exception.DukeException((lines.createLine()) + "\n Please enter the task number that you would like to delete. \n" + (lines.createLine())); } - } catch (DukeException e) { + } catch (exception.DukeException e) { System.out.println(e.getMessage()); } finally { break; @@ -128,7 +134,7 @@ public String toString() { default: try { - Task newTask = track(activity, arr); + task.Task newTask = track(activity, arr); taskList.add(newTask); FileWriterClass.writeFile(filePath, taskList.toString()); taskNum++; @@ -136,43 +142,43 @@ public String toString() { System.out.println("Got it. I've added this task: " + "\n" + newTask.toString()); System.out.println("Now you have " + taskNum + " task(s) in the list." + "\n" + lines.createLine()); break; - } catch (DukeException e) { + } catch (exception.DukeException e) { System.out.println(e.getMessage()); } } - } catch (DukeException e) { + } catch (exception.DukeException e) { System.out.println(e.getMessage()); } } - private Task track(Activity activity, String[] arr) throws DukeException + private task.Task track(Activity activity, String[] arr) throws exception.DukeException { try { switch (activity) { case TODO: checkDescription(arr, "todo"); - return new Todo(arr[1]); + return new task.Todo(arr[1]); case DEADLINE: checkDescription(arr, "deadline"); - return new Deadline(arr[1]); + return new task.Deadline(arr[1]); default: checkDescription(arr, "event"); - return new Event(arr[1]); + return new task.Event(arr[1]); } } - catch(DukeException e) + catch(exception.DukeException e) { throw e; } } - private void checkDescription (String[] arr, String taskType) throws DukeException + private void checkDescription (String[] arr, String taskType) throws exception.DukeException { if (arr.length < 2) { - throw new DukeException((lines.createLine()) + "\n The description of a " + taskType + " cannot be empty.\n" + (lines.createLine())); + throw new exception.DukeException((lines.createLine()) + "\n The description of a " + taskType + " cannot be empty.\n" + (lines.createLine())); } }*/ diff --git a/src/main/java/Todo.java b/src/main/java/task/Todo.java similarity index 64% rename from src/main/java/Todo.java rename to src/main/java/task/Todo.java index 6e022f31..e3e8d503 100644 --- a/src/main/java/Todo.java +++ b/src/main/java/task/Todo.java @@ -1,6 +1,11 @@ +package task; + +import exception.DukeException; +import task.Task; + public class Todo extends Task { - public Todo(String description) throws DukeException{ + public Todo(String description) throws DukeException { super(description); } diff --git a/src/main/java/Ui.java b/src/main/java/ui/Ui.java similarity index 97% rename from src/main/java/Ui.java rename to src/main/java/ui/Ui.java index a9c54615..0043906d 100644 --- a/src/main/java/Ui.java +++ b/src/main/java/ui/Ui.java @@ -1,3 +1,8 @@ +package ui; + +import task.Task; +import task.TaskList; + import java.util.Scanner; public class Ui { From 3aa2e6bc5567c4860730adbce6388beb9d5dc76b Mon Sep 17 00:00:00 2001 From: Yan Ru Date: Thu, 28 Oct 2021 15:51:49 +0800 Subject: [PATCH 12/26] Level 8.1 - Add JUnit tests --- src/main/java/datetime/DateTime.java | 41 +++++++++------- src/main/java/task/Deadline.java | 5 +- src/main/java/task/Event.java | 5 +- src/main/java/task/TaskStub.java | 20 ++++++++ src/test/java/DukeTest.java | 9 ++++ src/test/java/datetime/DateTimeTest.java | 59 ++++++++++++++++++++++++ src/test/java/task/DeadlineTest.java | 23 +++++++++ src/test/java/task/EventTest.java | 19 ++++++++ src/test/java/ui/UiTest.java | 52 +++++++++++++++++++++ 9 files changed, 211 insertions(+), 22 deletions(-) create mode 100644 src/main/java/task/TaskStub.java create mode 100644 src/test/java/DukeTest.java create mode 100644 src/test/java/datetime/DateTimeTest.java create mode 100644 src/test/java/task/DeadlineTest.java create mode 100644 src/test/java/task/EventTest.java create mode 100644 src/test/java/ui/UiTest.java diff --git a/src/main/java/datetime/DateTime.java b/src/main/java/datetime/DateTime.java index 0e3339b2..71890c22 100644 --- a/src/main/java/datetime/DateTime.java +++ b/src/main/java/datetime/DateTime.java @@ -4,6 +4,7 @@ import java.time.LocalTime; import java.time.LocalDateTime; import exception.DukeException; +import java.time.format.DateTimeParseException; public class DateTime { private LocalTime endDateTime; @@ -12,11 +13,15 @@ public class DateTime { private static DateTimeFormatter getTime = DateTimeFormatter.ofPattern("HHmm"); private static DateTimeFormatter getStartDateTime = DateTimeFormatter.ofPattern("d/MM/yyyy HHmm"); - public static DateTime setDeadline(String dateTime) { - DateTime deadlineDateTime = new DateTime(); - deadlineDateTime.startDateTime = LocalDateTime.parse(dateTime, getStartDateTime); - deadlineDateTime.displayDateTime = deadlineDateTime.startDateTime.format(getStartDateTime); - return deadlineDateTime; + public static DateTime setDeadline(String dateTime) throws DukeException { + try { + DateTime deadlineDateTime = new DateTime(); + deadlineDateTime.startDateTime = LocalDateTime.parse(dateTime, getStartDateTime); + deadlineDateTime.displayDateTime = deadlineDateTime.startDateTime.format(getStartDateTime); + return deadlineDateTime; + } catch (DateTimeParseException e) { + throw new DukeException("Incorrect deadline datetime format. \nPlease key in deadline (task) /by d/mm/yyyy HHmm"); + } } public static DateTime readDeadLine(String dateTime){ @@ -27,23 +32,27 @@ public static DateTime readDeadLine(String dateTime){ } public static DateTime setEventTime (String dateTime) throws DukeException { - DateTime eventDateTime = new DateTime(); - int divider = dateTime.indexOf("-"); - if (divider == -1 || (divider == dateTime.length() - 1)) { - throw new DukeException("\n Please key in the end time."); + try { + DateTime eventDateTime = new DateTime(); + int divider = dateTime.indexOf("-"); + if (divider == -1 || (divider == dateTime.length() - 1)) { + throw new DukeException("Please key in the end time."); + } + String end = dateTime.substring(divider + 1, dateTime.length()); + eventDateTime.startDateTime = LocalDateTime.parse(dateTime.substring(0, divider), getStartDateTime); + eventDateTime.endDateTime = LocalTime.parse(end, DateTimeFormatter.ofPattern("HHmm")); + eventDateTime.displayDateTime = eventDateTime.startDateTime.format(getStartDateTime) + "-" + eventDateTime.endDateTime.format(getTime); + return eventDateTime; + } catch (DateTimeParseException e) { + throw new DukeException("Incorrect event format. \n Please key in event (details) /at d/mm/yyyy (start time)HHmm-(end time)HHmm"); } - String end = dateTime.substring(divider + 1, dateTime.length()); - eventDateTime.startDateTime = LocalDateTime.parse(dateTime.substring(0,divider), getStartDateTime); - eventDateTime.endDateTime = LocalTime.parse(end, DateTimeFormatter.ofPattern("HHmm")); - eventDateTime.displayDateTime = eventDateTime.startDateTime.format(getStartDateTime) + "-" + eventDateTime.endDateTime.format(getTime); - return eventDateTime; } public static DateTime readEventTime (String dateTime) throws DukeException { DateTime eventDateTime = new DateTime(); int divider = dateTime.indexOf("-"); - if (divider == -1 || (divider == dateTime.length() - 1)) { - throw new DukeException("\n Please key in the end time."); + if (divider == -1 || (divider == dateTime.length() - 1) || dateTime.substring(divider + 1).replace(" ", "").equals("")) { + throw new DukeException("Please key in the end time."); } String end = dateTime.substring(divider + 1, dateTime.length()); eventDateTime.startDateTime = LocalDateTime.parse(dateTime.substring(0,divider), getStartDateTime); diff --git a/src/main/java/task/Deadline.java b/src/main/java/task/Deadline.java index 36f5ccf7..5a32b4e8 100644 --- a/src/main/java/task/Deadline.java +++ b/src/main/java/task/Deadline.java @@ -8,9 +8,8 @@ public class Deadline extends Task { public Deadline(String description) throws DukeException { super(description); int divider = description.indexOf("/by"); - if (divider == -1 || (divider == description.length() -3)) - { - throw new DukeException("\nIncorrect deadline format. \nPlease key in deadline (task) /by d/mm/yyyy HHmm"); + if (divider == -1 || (divider == description.length() -3)||description.substring(divider + 4).replace(" ", "").equals("")) { + throw new DukeException("Incorrect deadline format. \nPlease key in deadline (task) /by d/mm/yyyy HHmm"); } dateTime = DateTime.setDeadline(description.substring(divider + 4, description.length())); super.taskDesc = super.taskDesc.substring(0,divider); diff --git a/src/main/java/task/Event.java b/src/main/java/task/Event.java index 6df727bc..1ed541bd 100644 --- a/src/main/java/task/Event.java +++ b/src/main/java/task/Event.java @@ -9,9 +9,8 @@ public class Event extends Task public Event(String description) throws DukeException { super(description); int divider = description.indexOf("/at"); - if (divider == -1 || (divider == description.length() - 3)) - { - throw new DukeException("\n Incorrect event format. \n Please key in event (details) /at d/mm/yyyy (start time)HHmm-(end time)HHmm \n"); + if (divider == -1 || (divider == description.length() - 3)||description.substring(divider + 4).replace(" ", "").equals("")) { + throw new DukeException("Incorrect event format. \nPlease key in event (details) /at d/mm/yyyy (start time)HHmm-(end time)HHmm"); } dateTime = DateTime.setEventTime(description.substring(divider + 4, description.length())); super.taskDesc = super.taskDesc.substring(0,divider); diff --git a/src/main/java/task/TaskStub.java b/src/main/java/task/TaskStub.java new file mode 100644 index 00000000..e2a62da8 --- /dev/null +++ b/src/main/java/task/TaskStub.java @@ -0,0 +1,20 @@ +package task; + +public class TaskStub extends Task{ + public TaskStub(String description) { + super(description); + super.isDone = true; + } + + public String getStatusIcon() { + return (isDone ? "\u2713" : "\u2718"); //tick if done, else cross + } + + public void markAsDone(){ + this.isDone = true; + } + + public String toString() { + return taskDesc; + } +} \ No newline at end of file diff --git a/src/test/java/DukeTest.java b/src/test/java/DukeTest.java new file mode 100644 index 00000000..0038726d --- /dev/null +++ b/src/test/java/DukeTest.java @@ -0,0 +1,9 @@ +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class DukeTest { + @Test + public void dummyTest() { + assertEquals(2, 2); + } +} \ No newline at end of file diff --git a/src/test/java/datetime/DateTimeTest.java b/src/test/java/datetime/DateTimeTest.java new file mode 100644 index 00000000..471d901f --- /dev/null +++ b/src/test/java/datetime/DateTimeTest.java @@ -0,0 +1,59 @@ +package datetime; + +import exception.DukeException; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class DateTimeTest { + @Test + public void setDeadLine_incorrectFormat_exceptionThrown(){ + try { + DateTime.setDeadline("10.02.2021 15:00"); + fail(); + } catch (DukeException e){ + assertEquals("Incorrect deadline datetime format. \nPlease key in deadline (task) /by d/mm/yyyy HHmm", e.getMessage()); + } + } + @Test + public void readDeadLineTest(){ + DateTime deadline = DateTime.readDeadLine("30/11/2019 0500"); + assertEquals("30/11/2019 0500", deadline.toString()); + } + @Test + public void setEventTime_noEndTime_exceptionThrown(){ + try { + DateTime.setEventTime("05/11/2021 0500-"); + fail(); + } catch (DukeException e){ + assertEquals("Please key in the end time.", e.getMessage()); + } + } + @Test + public void setEventTime_incorrectFormat_exceptionThrown(){ + try { + DateTime.setEventTime("15.11.2021 05:00-05:30"); + fail(); + } catch (DukeException e){ + assertEquals("Incorrect event format. \n Please key in event (details) /at d/mm/yyyy (start time)HHmm-(end time)HHmm", e.getMessage()); + } + } + @Test + public void readEventTimeTest(){ + try { + DateTime eventTime = DateTime.readEventTime("17/11/2019 0500-0600"); + assertEquals("17/11/2019 0500-0600", eventTime.toString()); + } catch (DukeException e){ + fail(); + } + } + @Test + public void readEventTime_noEndTime_exceptionThrown(){ + try { + DateTime eventTime = DateTime.readEventTime("30/11/2021 0500-"); + fail(); + } catch (DukeException e){ + assertEquals("Please key in the end time.", e.getMessage()); + } + } +} \ No newline at end of file diff --git a/src/test/java/task/DeadlineTest.java b/src/test/java/task/DeadlineTest.java new file mode 100644 index 00000000..7dec78f8 --- /dev/null +++ b/src/test/java/task/DeadlineTest.java @@ -0,0 +1,23 @@ +package task; + +import exception.DukeException; +import org.junit.jupiter.api.Test; + +import exception.DukeException; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; + + +public class DeadlineTest { + @Test + void constructor_noDate_exceptionThrown(){ + try{ + Deadline deadline = new Deadline("Generate Exception /by"); + fail(); + }catch (DukeException e){ + String expectedOutput = "Incorrect deadline format. \nPlease key in deadline (task) /by d/mm/yyyy HHmm"; + assertEquals(expectedOutput, e.getMessage()); + } + } +} \ No newline at end of file diff --git a/src/test/java/task/EventTest.java b/src/test/java/task/EventTest.java new file mode 100644 index 00000000..f3cae3fe --- /dev/null +++ b/src/test/java/task/EventTest.java @@ -0,0 +1,19 @@ +package task; + +import exception.DukeException; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class EventTest { + @Test + void constructor_noDate_exceptionThrown(){ + try{ + Event event = new Event("Generate Exception /at"); + fail(); + }catch (DukeException e){ + String expectedOutput = "Incorrect event format. \nPlease key in event (details) /at d/mm/yyyy (start time)HHmm-(end time)HHmm"; + assertEquals(expectedOutput, e.getMessage()); + } + } +} \ No newline at end of file diff --git a/src/test/java/ui/UiTest.java b/src/test/java/ui/UiTest.java new file mode 100644 index 00000000..d87df8ab --- /dev/null +++ b/src/test/java/ui/UiTest.java @@ -0,0 +1,52 @@ +package ui; + +import org.junit.jupiter.api.Test; +import task.Task; +import task.TaskStub; +import ui.Ui; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class UiTest { + @Test + public void readTaskTest(){ + Task test = new TaskStub("Task Message"); + ByteArrayOutputStream outContent = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outContent)); + new Ui().readTask(test, 1); + String expectedMessage = "Got it. I've added this task:" + System.lineSeparator() + + "Task Message" + System.lineSeparator() + + "Now you have 1 tasks in the list." + System.lineSeparator() + + "________________________________________________________________________________" + + System.lineSeparator(); + assertEquals(expectedMessage, outContent.toString()); + } + @Test + public void readDeleteTest(){ + Task test = new TaskStub("Task Message"); + ByteArrayOutputStream outContent = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outContent)); + new Ui().readDelete(test, 0); + String expectedMessage = "Noted. I've removed this task:" + System.lineSeparator() + + "Task Message" + System.lineSeparator() + + "Now you have 0 tasks in the list." + System.lineSeparator() + + "________________________________________________________________________________" + + System.lineSeparator(); + assertEquals(expectedMessage, outContent.toString()); + } + @Test + public void readDone(){ + Task test = new TaskStub("Completed Task Message"); + ByteArrayOutputStream outContent = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outContent)); + new Ui().readDone(test); + String expectedMessage = "Nice! I've marked this task as done:" + System.lineSeparator() + + "Completed Task Message" + System.lineSeparator() + + "________________________________________________________________________________" + + System.lineSeparator(); + assertEquals(expectedMessage, outContent.toString()); + } +} \ No newline at end of file From 3b6ede00a85b18d8d59912f9df192ae6e3bfc2c9 Mon Sep 17 00:00:00 2001 From: Yan Ru Date: Thu, 28 Oct 2021 16:42:32 +0800 Subject: [PATCH 13/26] Level 9.1 - Find a task by searching for keyword --- src/main/java/command/DeleteCommand.java | 1 + src/main/java/command/EditCommand.java | 1 + src/main/java/command/ExitCommand.java | 7 +++++- src/main/java/command/FindCommand.java | 31 ++++++++++++++++++++++++ src/main/java/command/FullCommand.java | 3 ++- src/main/java/command/ListCommand.java | 12 ++++++--- src/main/java/parser/Parser.java | 2 ++ src/main/java/task/Task.java | 8 ++++++ src/main/java/task/TaskList.java | 20 +++++++++++---- src/main/java/ui/Ui.java | 6 +++++ 10 files changed, 81 insertions(+), 10 deletions(-) create mode 100644 src/main/java/command/FindCommand.java diff --git a/src/main/java/command/DeleteCommand.java b/src/main/java/command/DeleteCommand.java index 6fe60b09..4544f253 100644 --- a/src/main/java/command/DeleteCommand.java +++ b/src/main/java/command/DeleteCommand.java @@ -11,6 +11,7 @@ public class DeleteCommand extends Command { public DeleteCommand(int index){ this.index = index; + super.type = FullCommand.DELETE; } public boolean isExit(){ diff --git a/src/main/java/command/EditCommand.java b/src/main/java/command/EditCommand.java index 0b539495..e56fb78d 100644 --- a/src/main/java/command/EditCommand.java +++ b/src/main/java/command/EditCommand.java @@ -15,6 +15,7 @@ public boolean isExit(){ public EditCommand(int index){ this.index = index; + super.type = FullCommand.DONE; } public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { diff --git a/src/main/java/command/ExitCommand.java b/src/main/java/command/ExitCommand.java index 846eaab6..9e09ebb0 100644 --- a/src/main/java/command/ExitCommand.java +++ b/src/main/java/command/ExitCommand.java @@ -6,7 +6,12 @@ import ui.Ui; public class ExitCommand extends Command { - public boolean isExit(){ + + public ExitCommand() { + this.type = FullCommand.BYE; + } + + public boolean isExit() { return true; } diff --git a/src/main/java/command/FindCommand.java b/src/main/java/command/FindCommand.java new file mode 100644 index 00000000..78cb34f9 --- /dev/null +++ b/src/main/java/command/FindCommand.java @@ -0,0 +1,31 @@ +package command; + +import exception.DukeException; +import filewriter.Storage; +import task.Task; +import task.TaskList; +import ui.Ui; + +import java.util.ArrayList; + +public class FindCommand extends Command { + String keyword; + + public FindCommand(String keyword) { + super.type = FullCommand.FIND; + this.keyword = keyword; + } + public boolean isExit(){ + return false; + } + + public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException{ + ArrayList shortlist = new ArrayList<>(); + for (Task task: tasks.getList()){ + if (task.findWord(keyword)){ + shortlist.add(task); + } + } + ui.showMatches(new TaskList(shortlist)); + } +} \ No newline at end of file diff --git a/src/main/java/command/FullCommand.java b/src/main/java/command/FullCommand.java index c3d66ebe..aa790cc4 100644 --- a/src/main/java/command/FullCommand.java +++ b/src/main/java/command/FullCommand.java @@ -9,7 +9,8 @@ public enum FullCommand { DEADLINE ("deadline"), DONE ("done"), DELETE ("delete"), - BYE("bye"); + BYE("bye"), + FIND("find"); private String activityName; diff --git a/src/main/java/command/ListCommand.java b/src/main/java/command/ListCommand.java index 431c546d..4c7c4e07 100644 --- a/src/main/java/command/ListCommand.java +++ b/src/main/java/command/ListCommand.java @@ -6,13 +6,19 @@ import ui.Ui; public class ListCommand extends Command { - public boolean isExit(){ + int index; + + public ListCommand() { + super.type = FullCommand.LIST; + } + + public boolean isExit() { return false; } - int index; + public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { try { - if (index == -1){ + if (index == -1) { throw new IndexOutOfBoundsException(); } ui.readList(tasks); diff --git a/src/main/java/parser/Parser.java b/src/main/java/parser/Parser.java index 7fa83e1b..6bbdb2ae 100644 --- a/src/main/java/parser/Parser.java +++ b/src/main/java/parser/Parser.java @@ -17,6 +17,8 @@ public static Command parse(String instruction) throws DukeException { return new EditCommand(Integer.parseInt(arr[1]) - 1); case DELETE: return new DeleteCommand(Integer.parseInt(arr[1]) - 1); + case FIND: + return new FindCommand(arr[1]); default: if (arr[1].equals("")){ throw new DukeException(""); diff --git a/src/main/java/task/Task.java b/src/main/java/task/Task.java index 0654a0ac..f79624b0 100644 --- a/src/main/java/task/Task.java +++ b/src/main/java/task/Task.java @@ -12,6 +12,14 @@ public Task(String taskDesc) { this.deleted = false; } + public boolean findWord(String keyword) { + if (taskDesc.indexOf(keyword) == -1){ + return false; + } else { + return true; + } + } + public String getStatus() { //tick if done, else cross return (isDone ? "\u2713" : "\u2718"); diff --git a/src/main/java/task/TaskList.java b/src/main/java/task/TaskList.java index 0699d149..e20f60d6 100644 --- a/src/main/java/task/TaskList.java +++ b/src/main/java/task/TaskList.java @@ -12,7 +12,7 @@ public class TaskList public int taskNum; public boolean isFirst; - public TaskList(Storage storage){ + public TaskList(Storage storage) { try { this.taskList = storage.getSchedule(); taskNum = taskList.size(); @@ -26,8 +26,8 @@ public TaskList(Storage storage){ } } - public TaskList(){ - this.taskList = taskList; + public TaskList() { + this.taskList = new ArrayList(); taskNum = taskList.size(); if (taskNum == 0){ isFirst = true; @@ -38,7 +38,17 @@ public TaskList(){ } - public Task getTask(int index){ + public TaskList(ArrayList schedule) { + this.taskList = schedule; + taskNum = taskList.size(); + if (taskNum == 0) { + isFirst = true; + } else { + isFirst = false; + } + } + + public Task getTask(int index) { return taskList.get(index); } @@ -62,7 +72,7 @@ public Task remove(int index) return removeTask; } - public ArrayList getList(){ + public ArrayList getList() { return taskList; } diff --git a/src/main/java/ui/Ui.java b/src/main/java/ui/Ui.java index 0043906d..6fed6562 100644 --- a/src/main/java/ui/Ui.java +++ b/src/main/java/ui/Ui.java @@ -69,4 +69,10 @@ public void showLoadingError(){ System.out.println("Loading Error!"); showLine(); } + + public void showMatches(TaskList tasks) { + System.out.println("Here are the matching task(s) in your list: "); + System.out.println(tasks); + showLine(); + } } \ No newline at end of file From 4a9e269c5df3aac76688d38e111b8b3ce23ab116 Mon Sep 17 00:00:00 2001 From: Yan Ru Date: Sat, 30 Oct 2021 14:37:53 +0800 Subject: [PATCH 14/26] Level 8.2 - Add JavaDoc comments --- src/main/java/command/AddCommand.java | 20 +++ src/main/java/command/Command.java | 14 ++ src/main/java/command/DeleteCommand.java | 20 ++- src/main/java/command/EditCommand.java | 16 +++ src/main/java/command/ExitCommand.java | 13 ++ src/main/java/command/FindCommand.java | 22 ++- src/main/java/command/FullCommand.java | 11 ++ src/main/java/command/ListCommand.java | 17 +++ src/main/java/datetime/DateTime.java | 74 +++++++--- src/main/java/exception/DukeException.java | 4 + src/main/java/filewriter/Storage.java | 54 ++++++- src/main/java/parser/Parser.java | 9 ++ src/main/java/task/Deadline.java | 18 ++- src/main/java/task/Event.java | 13 ++ src/main/java/task/Task.java | 21 ++- src/main/java/task/TaskList.java | 158 +++++++-------------- src/main/java/task/TaskStub.java | 3 + src/main/java/task/Todo.java | 3 + src/main/java/ui/Ui.java | 53 +++++++ 19 files changed, 408 insertions(+), 135 deletions(-) diff --git a/src/main/java/command/AddCommand.java b/src/main/java/command/AddCommand.java index c31d72e7..3febefe8 100644 --- a/src/main/java/command/AddCommand.java +++ b/src/main/java/command/AddCommand.java @@ -5,17 +5,37 @@ import task.*; import ui.Ui; +/** + * Adds a new Task to Task list. + */ public class AddCommand extends Command { String specifics; + + /** + * Constructor for AddCommand. + * @param taskType type of command/task (e.g. deadline, event, find etc). + * @param specifics includes the command/task name as well as the date and time specially for DEADLINE and EVENT. + */ public AddCommand(FullCommand taskType, String specifics){ super.type = taskType; this.specifics = specifics; } + /** + * Checks if command is an ExitCommand. + * @return false as command will be AddCommand. + */ public boolean isExit(){ return false; } + /** + * Adds task corresponding to command type and specifics to TaskList. + * @param tasks TaskList to add the task to. + * @param ui Instance of user interface to print feedback to user. + * @param storage Updates data record of TaskList stored in storage.filepath. + * @throws DukeException Creates new Deadline and Event, throws exception if not written in correct format. + */ public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { Task newTask; switch(type){ diff --git a/src/main/java/command/Command.java b/src/main/java/command/Command.java index 7722d10b..92ae8bf5 100644 --- a/src/main/java/command/Command.java +++ b/src/main/java/command/Command.java @@ -5,12 +5,26 @@ import task.*; import ui.Ui; +/** + * Abstract class for all other command classes to extend to (e.g. DeleteCommand, EditCommand, etc.), except FulLCommand enum. + */ public abstract class Command { FullCommand type; + /** + * Checks to see if type equals FullCommand.BYE. + * @return boolean value to check if this is an Exit command. + */ public boolean isExit(){ return type.getActivityName().equals("bye"); } + /** + * Carries out the command. + * @param tasks Current TaskList object used in this instance of Duke. + * @param ui Instance of user interface to print feedback to user. + * @param storage Updates data record of TaskList in storage.filepath if needed. + * @throws DukeException Various subclass of Command may throw DukeException when executed with invalid specification. + */ public abstract void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException; } \ No newline at end of file diff --git a/src/main/java/command/DeleteCommand.java b/src/main/java/command/DeleteCommand.java index 4544f253..577f7724 100644 --- a/src/main/java/command/DeleteCommand.java +++ b/src/main/java/command/DeleteCommand.java @@ -6,18 +6,36 @@ import task.TaskList; import ui.Ui; +/** + * Removes Task from Task List. + */ public class DeleteCommand extends Command { int index; + /** + * Constructor for DeleteCommand. + * @param index of the task to delete in the Task List. + */ public DeleteCommand(int index){ this.index = index; super.type = FullCommand.DELETE; } - public boolean isExit(){ + /** + * Checks if command is an ExitCommand. + * @return false as command is a DeleteCommand. + */ + public boolean isExit() { return false; } + /** + * Removes the current (index)th task from the task list. + * @param tasks Current TaskList object used in this instance of Duke. Removes specified task from tasks.schedule. + * @param ui Instance of user interface to print feedback to user. + * @param storage Updates data record of TaskList in storage.filepath. + * @throws DukeException Throws when index equals to 0, or when index is larger than number of Task(s) in TaskList. + */ public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { try { Task removeTask = tasks.remove(index); diff --git a/src/main/java/command/EditCommand.java b/src/main/java/command/EditCommand.java index e56fb78d..d964a5e1 100644 --- a/src/main/java/command/EditCommand.java +++ b/src/main/java/command/EditCommand.java @@ -9,15 +9,31 @@ public class EditCommand extends Command { int index; + /** + * Checks if command is an ExitCommand. + * @return false as command is an EditCommand. + */ public boolean isExit(){ return false; } + /** + * Constructor for EditCommand. + * @param index of Task in TaskList to mark as done. + */ public EditCommand(int index){ this.index = index; super.type = FullCommand.DONE; } + /** + * Marks the (index)th task in TaskList as done. + * @param tasks Current TaskList object used in this instance of Duke. + * @param ui Instance of user interface to print feedback to user. + * @param storage Updates data record of TaskList in storage.filepath if needed. + * @throws DukeException Throws when index equals to, or when index is larger than number of Task(s) in TaskList, + * or when index of Task to markAsComplete is not properly presented. + */ public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { try { if (index == -1){ diff --git a/src/main/java/command/ExitCommand.java b/src/main/java/command/ExitCommand.java index 9e09ebb0..abb3e30d 100644 --- a/src/main/java/command/ExitCommand.java +++ b/src/main/java/command/ExitCommand.java @@ -7,14 +7,27 @@ public class ExitCommand extends Command { + /** + * Ends program. + */ public ExitCommand() { this.type = FullCommand.BYE; } + /** Uses by Duke class method run to check for exit command and terminate program if exit command is found. + * @return true as command is ExitCommand. + */ public boolean isExit() { return true; } + /** + * Closes FileWriter used by storage. + * @param tasks current TaskList object used in this instance of Duke. Not needed here. + * @param ui Instance of user interface to print feedback to user. Not needed here. + * @param storage closes FileWriter of storage. Changes to Task List are recorded in storage.filepath. + * @throws DukeException if invalid filepath (Directory tampered with when program is running). + */ public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { storage.closeWriter(); }; diff --git a/src/main/java/command/FindCommand.java b/src/main/java/command/FindCommand.java index 78cb34f9..c6cf6037 100644 --- a/src/main/java/command/FindCommand.java +++ b/src/main/java/command/FindCommand.java @@ -8,17 +8,37 @@ import java.util.ArrayList; +/** + * Lists all tasks containing keyword. + */ public class FindCommand extends Command { String keyword; + /** + * Constructor for FindCommand. + * @param keyword Searches for all tasks with keyword. + */ public FindCommand(String keyword) { super.type = FullCommand.FIND; this.keyword = keyword; } - public boolean isExit(){ + + /** + * FindCommand is not a ExitCommand. + * @return false. + */ + public boolean isExit() { return false; } + /** + * Generates a TaskList shortlist containing only tasks with the keyword. + * Passes TaskList as an argument for ui.showMatches to display to user. + * @param tasks TaskList containing all Tasks. + * @param ui Instance of user interface to print feedback to user. + * @param storage Updates data record of TaskList in storage.filepath if needed. + * @throws DukeException + */ public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException{ ArrayList shortlist = new ArrayList<>(); for (Task task: tasks.getList()){ diff --git a/src/main/java/command/FullCommand.java b/src/main/java/command/FullCommand.java index aa790cc4..8fdf88fb 100644 --- a/src/main/java/command/FullCommand.java +++ b/src/main/java/command/FullCommand.java @@ -2,6 +2,11 @@ import exception.DukeException; +/** + * Enum detailing all the valid commands Duke understands. + * Uses by Parser to generate Command Object. + * Checks if user types invalid command. + */ public enum FullCommand { LIST("list"), TODO ("todo"), @@ -22,6 +27,12 @@ public String getActivityName() { return activityName; } + /** + * Generates FullCommand based on String user input. + * @param keyword Specifies the type of FullCommand. + * @return Returns FullCommand to be parsed by parser. + * @throws DukeException Throws when user enters invalid command. + */ public static FullCommand getByAction (String keyword) throws DukeException { for (FullCommand activity : values()) { diff --git a/src/main/java/command/ListCommand.java b/src/main/java/command/ListCommand.java index 4c7c4e07..364c88f0 100644 --- a/src/main/java/command/ListCommand.java +++ b/src/main/java/command/ListCommand.java @@ -5,17 +5,34 @@ import task.TaskList; import ui.Ui; +/** + * Displays the full list of recorded tasks to user. + */ public class ListCommand extends Command { int index; + /** + * Constructor of ListCommand. + */ public ListCommand() { super.type = FullCommand.LIST; } + /** + * Checks if command is an ExitCommand. + * @return ConstructorCommand is not ExitCommand. + */ public boolean isExit() { return false; } + /** + * Passes TaskList as an argument for ui.readList to display to user. + * @param tasks Current TaskList object used in this instance of Duke. + * @param ui Instance of user interface to print feedback to user. + * @param storage Updates data record of TaskList in storage.filepath if needed. + * @throws DukeException + */ public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { try { if (index == -1) { diff --git a/src/main/java/datetime/DateTime.java b/src/main/java/datetime/DateTime.java index 71890c22..e5649a85 100644 --- a/src/main/java/datetime/DateTime.java +++ b/src/main/java/datetime/DateTime.java @@ -6,6 +6,10 @@ import exception.DukeException; import java.time.format.DateTimeParseException; +/** + * Wrapper of LocalDateTime. + * Stores date and time for Task. + */ public class DateTime { private LocalTime endDateTime; private LocalDateTime startDateTime; @@ -13,6 +17,13 @@ public class DateTime { private static DateTimeFormatter getTime = DateTimeFormatter.ofPattern("HHmm"); private static DateTimeFormatter getStartDateTime = DateTimeFormatter.ofPattern("d/MM/yyyy HHmm"); + /** + * Calls during construction of Deadline object. + * Reads substring from user input and gets date and time of task. + * @param dateTime Must be in the form (d/MM/yyyy HHmm) + * @return DateTime object as dateTime parameter of Deadline object. + * @throws DukeException Throws if dateTime is not presented in the correct form. + */ public static DateTime setDeadline(String dateTime) throws DukeException { try { DateTime deadlineDateTime = new DateTime(); @@ -20,10 +31,16 @@ public static DateTime setDeadline(String dateTime) throws DukeException { deadlineDateTime.displayDateTime = deadlineDateTime.startDateTime.format(getStartDateTime); return deadlineDateTime; } catch (DateTimeParseException e) { - throw new DukeException("Incorrect deadline datetime format. \nPlease key in deadline (task) /by d/mm/yyyy HHmm"); + throw new DukeException("Incorrect deadline datetime format. \nPlease key in deadline (task) /by d/MM/yyyy HHmm"); } } + /** + * Calls during construction of Deadline object. + * Reads substring from text file and gets date and time of the task. + * @param dateTime Must be in the form (d/MM/yyyy HHmm). + * @return DateTime object as dateTime parameter of Deadline object. + */ public static DateTime readDeadLine(String dateTime){ DateTime deadlineDateTime = new DateTime(); deadlineDateTime.startDateTime = LocalDateTime.parse(dateTime, getStartDateTime); @@ -31,6 +48,13 @@ public static DateTime readDeadLine(String dateTime){ return deadlineDateTime; } + /** + * Calls during construction of Event object. + * Reads substring from user input and gets date, start time and end time of task. + * @param dateTime Must be in the form (d/MM/yyyy HHmm-HHmm). + * @return DateTime object as dateTime parameter of Event object. + * @throws DukeException Throws if missing end time or if dateTime is not presented in the correct form. + */ public static DateTime setEventTime (String dateTime) throws DukeException { try { DateTime eventDateTime = new DateTime(); @@ -48,6 +72,13 @@ public static DateTime setEventTime (String dateTime) throws DukeException { } } + /** + * Calls during construction of Event object. + * Reads substring from text file and gets date, start time and end time of task. + * @param dateTime Must be in the form (d/M/yyyy HHmm-HHmm) + * @return DateTime object as dateTime parameter of Event object. + * @throws DukeException Throws if missing end time. This should not happen unless text file was tampered with. + */ public static DateTime readEventTime (String dateTime) throws DukeException { DateTime eventDateTime = new DateTime(); int divider = dateTime.indexOf("-"); @@ -61,23 +92,28 @@ public static DateTime readEventTime (String dateTime) throws DukeException { return eventDateTime; } - private static String getDayOfMonth(String dd) { - if (dd.substring(0,1).equals("0")){ - dd = dd.substring(1,dd.length()); - } - int day = Integer.parseInt(dd); - if (day >= 11 && day <= 31) { - return day + "th"; - } - switch (day & 10) { - case 1: return day + "st"; - case 2: return day + "nd"; - case 3: return day + "rd"; - default: return day + "th"; - } - } + /** + * Adds suffix to date of month + * @param dd day of month as a 2 digits number. + * @return 1 or 2 digits number with appropriate suffix. + */ + private static String getDayOfMonth(String dd) { + if (dd.substring(0,1).equals("0")){ + dd = dd.substring(1,dd.length()); + } + int day = Integer.parseInt(dd); + if (day >= 11 && day <= 31) { + return day + "th"; + } + switch (day & 10) { + case 1: return day + "st"; + case 2: return day + "nd"; + case 3: return day + "rd"; + default: return day + "th"; + } + } - public String toString() { - return displayDateTime; - } + public String toString() { + return displayDateTime; + } } diff --git a/src/main/java/exception/DukeException.java b/src/main/java/exception/DukeException.java index 30ecd7f0..8e95f0a1 100644 --- a/src/main/java/exception/DukeException.java +++ b/src/main/java/exception/DukeException.java @@ -1,5 +1,9 @@ package exception; +/** + * Wraps exception. Helps to check if exception has been accounted for. + * Catches exception and throws DukeException. + */ public class DukeException extends Exception { public DukeException(String message) { diff --git a/src/main/java/filewriter/Storage.java b/src/main/java/filewriter/Storage.java index ee6396a7..8443a0f9 100644 --- a/src/main/java/filewriter/Storage.java +++ b/src/main/java/filewriter/Storage.java @@ -10,17 +10,30 @@ import java.io.File; import java.io.FileWriter; +/** + * Class of storage. + * Stores and reads from text file. + */ public class Storage { String filePath; FileWriter fw; ArrayList taskList; boolean isAppend = true; + /** + * Constructor of Storage object. + * @param filepath filepath to text file to be read from and edited. + * @throws DukeException When CreateWriter throws DukeException. + */ public Storage(String filepath) throws DukeException{ this.filePath = filepath; this.createWriter(); } + /** + * Creates FileWriter member fw. + * @throws DukeException when invalid filepath. + */ private void createWriter() throws DukeException{ try{ fw = new FileWriter(filePath, isAppend); @@ -29,6 +42,11 @@ private void createWriter() throws DukeException{ } } + /** + * Wrapper to close FileWriter fw. + * Uses in Execute method of ExitCommand object. + * @throws DukeException when invalid filepath. + */ public void closeWriter() throws DukeException{ try { fw.close(); @@ -37,6 +55,12 @@ public void closeWriter() throws DukeException{ } } + /** + * Adds Task to TaskList data stored in txt file. + * Calls during execute method of addCommand object. + * @param textToAdd (Command Object).to String(); + * @throws DukeException Throws when IOException is caught. + */ public void writeToFile(String textToAdd) throws DukeException{ try { fw.write(textToAdd + "\n"); @@ -46,6 +70,12 @@ public void writeToFile(String textToAdd) throws DukeException{ } } + /** + * Rewrites data in txt file with editions made. + * Calls when Execute command of DeleteCommand or EditCommand object modifies TaskList. + * @param schedule Modifies contents of TaskList. + * @throws DukeException Throws when IOException is caught. + */ public void editFile(TaskList schedule) throws DukeException{ try { checkAppend(false); @@ -57,12 +87,23 @@ public void editFile(TaskList schedule) throws DukeException{ } } + /** + * Toggles willAppend boolean, which determines if FileWriter fw will append to txt file or overwrite it. + * @param toAppend New boolean value of toAppend. + * @throws DukeException Throws when createWriter throws DukeException. + */ private void checkAppend(boolean toAppend) throws DukeException { closeWriter(); isAppend = toAppend; createWriter(); } + /** + * Interprets each line in txt file and add corresponding Task to TaskList. + * @param line every line in the txt file represents a task. + * @return Task object with type, task name, date and timing as described in String. + * @throws DukeException Throws when Task is not stored in the correct format in txt file. + */ private Task read(String line) throws DukeException { Task output; switch (line.charAt(line.indexOf("[") + 1)) { @@ -93,6 +134,13 @@ private Task read(String line) throws DukeException { } } + /** + * Reads txt file and updates TaskList accordingly. + * Calls read method line by line, and gets corresponding Task. + * Checks each line to check if Task should markAsDone. + * @return Storage object with updated ArrayList taskList used in construction of TaskList object. + * @throws DukeException when read throws DukeException. + */ public Storage load() throws DukeException { try { taskList = new ArrayList<>(); @@ -116,7 +164,11 @@ public Storage load() throws DukeException { } } - public ArrayList getSchedule(){ + /** + * Calls during construction method of TaskList. + * @return ArrayList of Task + */ + public ArrayList getSchedule() { return taskList; } } \ No newline at end of file diff --git a/src/main/java/parser/Parser.java b/src/main/java/parser/Parser.java index 6bbdb2ae..7392e741 100644 --- a/src/main/java/parser/Parser.java +++ b/src/main/java/parser/Parser.java @@ -3,7 +3,16 @@ import command.*; import exception.DukeException; +/** + * Parses String user input/ text file to get corresponding Command object to execute. + */ public class Parser { + /** + * Processes user input/ line from txt file and gets appropriate command. + * @param instruction String from user input/ text file + * @return Corresponding Task to be executed in run method of Duke class. + * @throws DukeException When there are no matching command. i.e. user input or line from txt file is invalid. + */ public static Command parse(String instruction) throws DukeException { String[] arr = instruction.split(" ", 2); FullCommand command = FullCommand.getByAction(arr[0]); diff --git a/src/main/java/task/Deadline.java b/src/main/java/task/Deadline.java index 5a32b4e8..f218cf3f 100644 --- a/src/main/java/task/Deadline.java +++ b/src/main/java/task/Deadline.java @@ -3,8 +3,16 @@ import datetime.DateTime; import exception.DukeException; +/** + * Deadline task object. Has a task name, date and time. + */ public class Deadline extends Task { DateTime dateTime; + /** + * Constructor for Deadline object. Calls when generating TaskList based on user input. + * @param description Includes task name, date and time. + * @throws DukeException Throws when description format is incorrect. or when setDeadline throws DukeException. + */ public Deadline(String description) throws DukeException { super(description); int divider = description.indexOf("/by"); @@ -15,12 +23,10 @@ public Deadline(String description) throws DukeException { super.taskDesc = super.taskDesc.substring(0,divider); } - public Deadline(String description, DateTime dateTime){ - super(description); - System.out.println(dateTime); - this.dateTime = dateTime; - } - + /** + * toString method of DeadLine + * @return String denoting task name, status date and time of Deadline task. + */ @Override public String toString() { String output = "[D][" + super.getStatus() + "]" + " " + super.taskDesc + "(by: " + dateTime.toString() + ")"; diff --git a/src/main/java/task/Event.java b/src/main/java/task/Event.java index 1ed541bd..79688550 100644 --- a/src/main/java/task/Event.java +++ b/src/main/java/task/Event.java @@ -3,9 +3,18 @@ import datetime.DateTime; import exception.DukeException; +/** + * toString method of DeadLine. + * @return String denoting task name, status date and time of Deadline task. + */ public class Event extends Task { DateTime dateTime; + /** + * Constructor for Event object. Calls when generating TaskList based on user input. + * @param description Contains task name, date, starting time and ending time. + * @throws DukeException Throws when description format is incorrect. or when setEventTime throws DukeException. + */ public Event(String description) throws DukeException { super(description); int divider = description.indexOf("/at"); @@ -16,6 +25,10 @@ public Event(String description) throws DukeException { super.taskDesc = super.taskDesc.substring(0,divider); } + /** + * toString method of Event. + * @return String denoting task name, status, date, and starting and ending time of task. + */ @Override public String toString() { String output = "[E][" + super.getStatus() + "]" + " " + super.taskDesc + "(at: " + dateTime.toString() + ")"; diff --git a/src/main/java/task/Task.java b/src/main/java/task/Task.java index f79624b0..b71b549c 100644 --- a/src/main/java/task/Task.java +++ b/src/main/java/task/Task.java @@ -1,17 +1,30 @@ package task; +/** + * Abstract class which all other Task classes extend (Event, deadline etc.). + */ public abstract class Task { protected String taskDesc; protected boolean isDone; protected boolean deleted; + /** + *Constructor method of Task. + * @param taskDesc contains information of Task, includes task name and may include date and time. + */ public Task(String taskDesc) { this.taskDesc = taskDesc; this.isDone = false; this.deleted = false; } + /** + * Calls when execute method of findCommand is called. + * Checks to see if description of task contains keyword. + * @param keyword word/phrase to find in description. + * @return boolean value indicating if word/phrase can be found in description. + */ public boolean findWord(String keyword) { if (taskDesc.indexOf(keyword) == -1){ return false; @@ -20,11 +33,17 @@ public boolean findWord(String keyword) { } } + /** + * Calls in toString method. Indicates status of completion of task. + * @return tick if done, else cross. + */ public String getStatus() { - //tick if done, else cross return (isDone ? "\u2713" : "\u2718"); } + /** + * sets isDone value to true. + */ public void markAsDone() { this.isDone = true; } diff --git a/src/main/java/task/TaskList.java b/src/main/java/task/TaskList.java index e20f60d6..adbf3ee1 100644 --- a/src/main/java/task/TaskList.java +++ b/src/main/java/task/TaskList.java @@ -6,12 +6,20 @@ import java.util.ArrayList; +/** + * Object containing data structure to store Tasks. + */ public class TaskList { ArrayList taskList = new ArrayList<> (); public int taskNum; public boolean isFirst; + /** + * Constructor for Task List. + * Calls when generating Task List from txt file. + * @param storage Contains ArrayList of Task specified from txt file. + */ public TaskList(Storage storage) { try { this.taskList = storage.getSchedule(); @@ -26,6 +34,9 @@ public TaskList(Storage storage) { } } + /** + * Constructor for Task List. + */ public TaskList() { this.taskList = new ArrayList(); taskNum = taskList.size(); @@ -34,10 +45,13 @@ public TaskList() { } else { isFirst = false; } - System.out.println(taskNum); } + /** + * Constructor for Task List. + * @param schedule ArrayList of tasks. + */ public TaskList(ArrayList schedule) { this.taskList = schedule; taskNum = taskList.size(); @@ -48,10 +62,24 @@ public TaskList(ArrayList schedule) { } } + /** + * Gets Task corresponding to specified index. + * @param index Indicates which task in the task list to return. + * @return Task as specified by index. + */ public Task getTask(int index) { return taskList.get(index); } + /** + * Marks a Task in task list as done. + * @param index Specifies which task to mark as done. + * @return completed Task. + * @throws NullPointerException + * @throws IndexOutOfBoundsException + * @throws NumberFormatException + * @throws DukeException + */ public Task complete(int index) throws NullPointerException, IndexOutOfBoundsException, NumberFormatException, DukeException{ Task completedTask = taskList.get(index); @@ -59,11 +87,25 @@ public Task complete(int index) return completedTask; } + /** + * Adds new Task to TaskList. + * @param task + * @throws DukeException + */ public void addTask(Task task) throws DukeException { taskList.add(task); taskNum++; } + /** + * Deletes Task from TaskList. + * @param index + * @return deleted Task. + * @throws NullPointerException + * @throws IndexOutOfBoundsException + * @throws NumberFormatException + * @throws DukeException + */ public Task remove(int index) throws NullPointerException, IndexOutOfBoundsException, NumberFormatException, DukeException{ Task removeTask = taskList.get(index); @@ -72,10 +114,19 @@ public Task remove(int index) return removeTask; } + /** + * Retrieves ArrayList schedule from TaskList. + * Calls when execute method of FindCommand is called. + * @return ArrayList + */ public ArrayList getList() { return taskList; } + /** + * toString method of TaskList. + * @return contents of TaskList. i.e. tasks. + */ public String toString() { String output = ""; for (int idx = 0; idx < taskNum; idx ++) @@ -87,109 +138,4 @@ public String toString() { } } - /*public void addTask(String task) { - String[] arr = task.split(" ", 2); - - try { - Activity activity = Activity.getByAction(arr[0]); - switch (activity) { - case LIST: - System.out.println(this); - break; - - case DONE: - try { - try { - if (arr.length < 2) - throw new exception.DukeException((lines.createLine()) + "\n Which tasks have u completed? \n" + (lines.createLine())); - taskList.get(Integer.parseInt(arr[1]) - 1).markAsDone(); - FileWriterClass.writeFile(filePath,taskList.toString()); - System.out.println(lines.createLine()); - System.out.println("Nice! I've marked this task as done:"); - System.out.println(taskList.get(Integer.parseInt(arr[1]) - 1).toString()); - System.out.println(lines.createLine()); - } catch (NullPointerException | IndexOutOfBoundsException e) { - throw new exception.DukeException((lines.createLine()) + "\n Index out of bounds. \n" + (lines.createLine())); - } catch (NumberFormatException e) { - throw new exception.DukeException((lines.createLine()) + "\n Please enter the task number that you have completed. \n" + (lines.createLine())); - } - } catch (exception.DukeException e) { - System.out.println(e.getMessage()); - } finally { - break; - } - case DELETE: - try { - try { - if (arr.length < 2) - throw new exception.DukeException((lines.createLine()) + "\n Which tasks have u completed? \n" + (lines.createLine())); - task.Task removeTask = taskList.get(Integer.parseInt(arr[1]) - 1); - taskList.remove(removeTask); - FileWriterClass.writeFile(filePath, taskList.toString()); - taskNum--; - System.out.println(lines.createLine()); - System.out.println("Noted. I've removed this task: \n" + removeTask.toString()); - System.out.println("Now you have " + taskNum + " in the list."); - System.out.println(lines.createLine()); - } catch (NullPointerException | IndexOutOfBoundsException e) { - throw new exception.DukeException(lines.createLine() + "\n Index out of bounds. \n" + (lines.createLine())); - } catch (NumberFormatException e) { - throw new exception.DukeException((lines.createLine()) + "\n Please enter the task number that you would like to delete. \n" + (lines.createLine())); - } - } catch (exception.DukeException e) { - System.out.println(e.getMessage()); - } finally { - break; - } - - default: - try { - task.Task newTask = track(activity, arr); - taskList.add(newTask); - FileWriterClass.writeFile(filePath, taskList.toString()); - taskNum++; - System.out.println(lines.createLine()); - System.out.println("Got it. I've added this task: " + "\n" + newTask.toString()); - System.out.println("Now you have " + taskNum + " task(s) in the list." + "\n" + lines.createLine()); - break; - } catch (exception.DukeException e) { - System.out.println(e.getMessage()); - } - } - } catch (exception.DukeException e) { - System.out.println(e.getMessage()); - } - } - - - private task.Task track(Activity activity, String[] arr) throws exception.DukeException - { - try - { - switch (activity) { - case TODO: - checkDescription(arr, "todo"); - return new task.Todo(arr[1]); - case DEADLINE: - checkDescription(arr, "deadline"); - return new task.Deadline(arr[1]); - default: - checkDescription(arr, "event"); - return new task.Event(arr[1]); - } - } - catch(exception.DukeException e) - { - throw e; - } - } - - private void checkDescription (String[] arr, String taskType) throws exception.DukeException - { - if (arr.length < 2) - { - throw new exception.DukeException((lines.createLine()) + "\n The description of a " + taskType + " cannot be empty.\n" + (lines.createLine())); - } - }*/ - diff --git a/src/main/java/task/TaskStub.java b/src/main/java/task/TaskStub.java index e2a62da8..b485e3af 100644 --- a/src/main/java/task/TaskStub.java +++ b/src/main/java/task/TaskStub.java @@ -1,5 +1,8 @@ package task; +/** + * Used for testing purposes. + */ public class TaskStub extends Task{ public TaskStub(String description) { super(description); diff --git a/src/main/java/task/Todo.java b/src/main/java/task/Todo.java index e3e8d503..77ab309b 100644 --- a/src/main/java/task/Todo.java +++ b/src/main/java/task/Todo.java @@ -3,6 +3,9 @@ import exception.DukeException; import task.Task; +/** + * Extends Task. Has a task name. + */ public class Todo extends Task { public Todo(String description) throws DukeException { diff --git a/src/main/java/ui/Ui.java b/src/main/java/ui/Ui.java index 6fed6562..7fdccd5e 100644 --- a/src/main/java/ui/Ui.java +++ b/src/main/java/ui/Ui.java @@ -5,8 +5,15 @@ import java.util.Scanner; +/** + * Determines what is displayed to user. + */ public class Ui { Scanner sc = new Scanner(System.in); + + /** + * Calls at the start of program. + */ public void showWelcome() { String logo = "____ _ \n" + "| _ \\ _ _| | _____ \n" @@ -18,11 +25,17 @@ public void showWelcome() { showLine(); } + /** + * Calls when execute method of ExitCommand is called. + */ public void showGoodbye(){ System.out.println("Bye. Hope to see you again soon!"); showLine(); } + /** + * Prints border. + */ public void showLine(){ String output = ""; for (int n = 0; n < 80; n++) { @@ -31,6 +44,12 @@ public void showLine(){ System.out.println(output); } + /** + * Calls when a task is entered by user. + * Calls when execute of AddTask is called. + * @param newTask task entered. + * @param taskNum number of tasks in Task List. + */ public void readTask(Task newTask, int taskNum){ System.out.println("Got it. I've added this task:"); System.out.println(newTask.toString()); @@ -38,6 +57,12 @@ public void readTask(Task newTask, int taskNum){ showLine(); } + /** + * Displays delete message. + * Calls when execute of DeleteCommand is called. + * @param removeTask + * @param taskNum + */ public void readDelete(Task removeTask, int taskNum){ System.out.println("Noted. I've removed this task:"); System.out.println(removeTask.toString()); @@ -45,31 +70,59 @@ public void readDelete(Task removeTask, int taskNum){ showLine(); } + /** + * Displays message when a user finishes a task. + * Calls when execute of EditCommand is called. + * @param completed_Task Task which has been marked as done. + */ public void readDone(Task completed_Task){ System.out.println("Nice! I've marked this task as done:"); System.out.println(completed_Task.toString()); showLine(); } + /** + * Displays contents of Task List. + * Calls when execute of ListCommand is called. + * @param tasks TaskList + */ public void readList(TaskList tasks){ System.out.println("Here are the tasks in your list: "); System.out.println(tasks); showLine(); } + /** + * Reads user input. + * @return String to be parsed by parser. + */ public String readCommand() { return sc.nextLine(); } + /** + * Displays message when DukeException is caught. + * @param error_msg DukeException e.getMessage() + */ public void showError(String error_msg) { System.out.println("Your error message: " + error_msg); showLine(); } + + /** + * Calls when Duke Exception is thrown when instantiating Task List and/ or Storage Objects. + * Calls by run method in Duke class. + */ public void showLoadingError(){ System.out.println("Loading Error!"); showLine(); } + /** + * Displays matching Tasks. + * Calls when execute method of FindCommand is called. + * @param tasks TaskList of tasks with keyword. + */ public void showMatches(TaskList tasks) { System.out.println("Here are the matching task(s) in your list: "); System.out.println(tasks); From 2d48c70344cb75ee7977f9a6ba82025bbcf5f43c Mon Sep 17 00:00:00 2001 From: Yan Ru Date: Sat, 30 Oct 2021 15:34:39 +0800 Subject: [PATCH 15/26] Level 9.2 - Further tweak the code to follow coding standard --- src/main/java/command/AddCommand.java | 18 ++++----- src/main/java/command/Command.java | 2 +- src/main/java/command/EditCommand.java | 4 +- src/main/java/command/FindCommand.java | 2 +- src/main/java/datetime/DateTime.java | 14 ++++--- src/main/java/filewriter/Storage.java | 56 +++++++++++++------------- src/main/java/parser/Parser.java | 30 +++++++------- src/main/java/task/TaskList.java | 13 +++--- src/main/java/task/TaskStub.java | 2 +- src/main/java/ui/Ui.java | 10 ++--- 10 files changed, 76 insertions(+), 75 deletions(-) diff --git a/src/main/java/command/AddCommand.java b/src/main/java/command/AddCommand.java index 3febefe8..cd1e581b 100644 --- a/src/main/java/command/AddCommand.java +++ b/src/main/java/command/AddCommand.java @@ -39,15 +39,15 @@ public boolean isExit(){ public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { Task newTask; switch(type){ - case TODO: - newTask = new Todo(specifics); - break; - case DEADLINE: - newTask = new Deadline(specifics); - break; - default: - newTask = new Event(specifics); - break; + case TODO: + newTask = new Todo(specifics); + break; + case DEADLINE: + newTask = new Deadline(specifics); + break; + default: + newTask = new Event(specifics); + break; } tasks.addTask(newTask); ui.readTask(newTask, tasks.taskNum); diff --git a/src/main/java/command/Command.java b/src/main/java/command/Command.java index 92ae8bf5..734b2bea 100644 --- a/src/main/java/command/Command.java +++ b/src/main/java/command/Command.java @@ -15,7 +15,7 @@ public abstract class Command { * Checks to see if type equals FullCommand.BYE. * @return boolean value to check if this is an Exit command. */ - public boolean isExit(){ + public boolean isExit() { return type.getActivityName().equals("bye"); } diff --git a/src/main/java/command/EditCommand.java b/src/main/java/command/EditCommand.java index d964a5e1..7d8097b6 100644 --- a/src/main/java/command/EditCommand.java +++ b/src/main/java/command/EditCommand.java @@ -13,7 +13,7 @@ public class EditCommand extends Command { * Checks if command is an ExitCommand. * @return false as command is an EditCommand. */ - public boolean isExit(){ + public boolean isExit() { return false; } @@ -21,7 +21,7 @@ public boolean isExit(){ * Constructor for EditCommand. * @param index of Task in TaskList to mark as done. */ - public EditCommand(int index){ + public EditCommand(int index) { this.index = index; super.type = FullCommand.DONE; } diff --git a/src/main/java/command/FindCommand.java b/src/main/java/command/FindCommand.java index c6cf6037..bfd6330c 100644 --- a/src/main/java/command/FindCommand.java +++ b/src/main/java/command/FindCommand.java @@ -39,7 +39,7 @@ public boolean isExit() { * @param storage Updates data record of TaskList in storage.filepath if needed. * @throws DukeException */ - public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException{ + public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { ArrayList shortlist = new ArrayList<>(); for (Task task: tasks.getList()){ if (task.findWord(keyword)){ diff --git a/src/main/java/datetime/DateTime.java b/src/main/java/datetime/DateTime.java index e5649a85..4f2858a8 100644 --- a/src/main/java/datetime/DateTime.java +++ b/src/main/java/datetime/DateTime.java @@ -98,7 +98,7 @@ public static DateTime readEventTime (String dateTime) throws DukeException { * @return 1 or 2 digits number with appropriate suffix. */ private static String getDayOfMonth(String dd) { - if (dd.substring(0,1).equals("0")){ + if (dd.substring(0,1).equals("0")) { dd = dd.substring(1,dd.length()); } int day = Integer.parseInt(dd); @@ -106,10 +106,14 @@ private static String getDayOfMonth(String dd) { return day + "th"; } switch (day & 10) { - case 1: return day + "st"; - case 2: return day + "nd"; - case 3: return day + "rd"; - default: return day + "th"; + case 1: + return day + "st"; + case 2: + return day + "nd"; + case 3: + return day + "rd"; + default: + return day + "th"; } } diff --git a/src/main/java/filewriter/Storage.java b/src/main/java/filewriter/Storage.java index 8443a0f9..9dc72d86 100644 --- a/src/main/java/filewriter/Storage.java +++ b/src/main/java/filewriter/Storage.java @@ -25,7 +25,7 @@ public class Storage { * @param filepath filepath to text file to be read from and edited. * @throws DukeException When CreateWriter throws DukeException. */ - public Storage(String filepath) throws DukeException{ + public Storage(String filepath) throws DukeException { this.filePath = filepath; this.createWriter(); } @@ -34,7 +34,7 @@ public Storage(String filepath) throws DukeException{ * Creates FileWriter member fw. * @throws DukeException when invalid filepath. */ - private void createWriter() throws DukeException{ + private void createWriter() throws DukeException { try{ fw = new FileWriter(filePath, isAppend); } catch (IOException e) { @@ -107,30 +107,30 @@ private void checkAppend(boolean toAppend) throws DukeException { private Task read(String line) throws DukeException { Task output; switch (line.charAt(line.indexOf("[") + 1)) { - case 'T': - output = new Todo(line.substring(7, line.length())); + case 'T': + output = new Todo(line.substring(7, line.length())); + return output; + case 'D': + try { + int divider = line.indexOf("(by:"); + String input = line.substring(7, divider); + input += "/by " + DateTime.readDeadLine(line.substring(divider + 5, line.length() - 1)).toString(); + output = new Deadline(input); return output; - case 'D': - try { - int divider = line.indexOf("(by:"); - String input = line.substring(7, divider); - input += "/by " + DateTime.readDeadLine(line.substring(divider + 5, line.length() - 1)).toString(); - output = new Deadline(input); - return output; - } catch (Exception e) { - System.out.println(e.getMessage()); - throw new DukeException("\n Deadline task not stored properly. \n "); - } - default: - try { - int divider = line.indexOf("(at:"); - String input = line.substring(7, divider); - input += "/at " + DateTime.readEventTime(line.substring(divider + 5, line.length() - 1)).toString(); - output = new Event(input); - return output; - } catch (Exception e) { - throw new DukeException("\n Event task not stored properly. \n "); - } + } catch (Exception e) { + System.out.println(e.getMessage()); + throw new DukeException("\n Deadline task not stored properly. \n "); + } + default: + try { + int divider = line.indexOf("(at:"); + String input = line.substring(7, divider); + input += "/at " + DateTime.readEventTime(line.substring(divider + 5, line.length() - 1)).toString(); + output = new Event(input); + return output; + } catch (Exception e) { + throw new DukeException("\n Event task not stored properly. \n "); + } } } @@ -150,16 +150,16 @@ public Storage load() throws DukeException { String line = s.nextLine(); if (! line.equals("")) { Task newTask = read(line.replace("\n", "")); - if (line.substring(4,5).equals("\u2713")){ + if (line.substring(4,5).equals("\u2713")) { newTask.markAsDone(); } taskList.add(newTask); } } return this; - } catch (FileNotFoundException e){ + } catch (FileNotFoundException e) { throw new DukeException("\n File not found! \n "); - } catch (Exception e){ + } catch (Exception e) { throw new DukeException("Unforeseen load errors: " + e.getMessage()); } } diff --git a/src/main/java/parser/Parser.java b/src/main/java/parser/Parser.java index 7392e741..172467c6 100644 --- a/src/main/java/parser/Parser.java +++ b/src/main/java/parser/Parser.java @@ -18,21 +18,21 @@ public static Command parse(String instruction) throws DukeException { FullCommand command = FullCommand.getByAction(arr[0]); try { switch (command) { - case LIST: - return new ListCommand(); - case BYE: - return new ExitCommand(); - case DONE: - return new EditCommand(Integer.parseInt(arr[1]) - 1); - case DELETE: - return new DeleteCommand(Integer.parseInt(arr[1]) - 1); - case FIND: - return new FindCommand(arr[1]); - default: - if (arr[1].equals("")){ - throw new DukeException(""); - } - return new AddCommand(command, arr[1]); + case LIST: + return new ListCommand(); + case BYE: + return new ExitCommand(); + case DONE: + return new EditCommand(Integer.parseInt(arr[1]) - 1); + case DELETE: + return new DeleteCommand(Integer.parseInt(arr[1]) - 1); + case FIND: + return new FindCommand(arr[1]); + default: + if (arr[1].equals("")) { + throw new DukeException(""); + } + return new AddCommand(command, arr[1]); } } catch (IndexOutOfBoundsException | DukeException e) { throw new DukeException("Please provide commands in the format: (command type) (task details)"); diff --git a/src/main/java/task/TaskList.java b/src/main/java/task/TaskList.java index adbf3ee1..b7430b49 100644 --- a/src/main/java/task/TaskList.java +++ b/src/main/java/task/TaskList.java @@ -29,7 +29,7 @@ public TaskList(Storage storage) { } else { isFirst = false; } - } catch (Exception e){ + } catch (Exception e) { System.out.println(e.getMessage()); } } @@ -40,7 +40,7 @@ public TaskList(Storage storage) { public TaskList() { this.taskList = new ArrayList(); taskNum = taskList.size(); - if (taskNum == 0){ + if (taskNum == 0) { isFirst = true; } else { isFirst = false; @@ -80,8 +80,7 @@ public Task getTask(int index) { * @throws NumberFormatException * @throws DukeException */ - public Task complete(int index) - throws NullPointerException, IndexOutOfBoundsException, NumberFormatException, DukeException{ + public Task complete(int index) throws NullPointerException, IndexOutOfBoundsException, NumberFormatException, DukeException { Task completedTask = taskList.get(index); completedTask.markAsDone(); return completedTask; @@ -106,8 +105,7 @@ public void addTask(Task task) throws DukeException { * @throws NumberFormatException * @throws DukeException */ - public Task remove(int index) - throws NullPointerException, IndexOutOfBoundsException, NumberFormatException, DukeException{ + public Task remove(int index) throws NullPointerException, IndexOutOfBoundsException, NumberFormatException, DukeException { Task removeTask = taskList.get(index); taskList.remove(index); taskNum--; @@ -129,8 +127,7 @@ public ArrayList getList() { */ public String toString() { String output = ""; - for (int idx = 0; idx < taskNum; idx ++) - { + for (int idx = 0; idx < taskNum; idx ++) { Task task = taskList.get(idx); output += ((idx + 1) + "." + task.toString() + "\n"); } diff --git a/src/main/java/task/TaskStub.java b/src/main/java/task/TaskStub.java index b485e3af..96468370 100644 --- a/src/main/java/task/TaskStub.java +++ b/src/main/java/task/TaskStub.java @@ -13,7 +13,7 @@ public String getStatusIcon() { return (isDone ? "\u2713" : "\u2718"); //tick if done, else cross } - public void markAsDone(){ + public void markAsDone() { this.isDone = true; } diff --git a/src/main/java/ui/Ui.java b/src/main/java/ui/Ui.java index 7fdccd5e..edfb2b8c 100644 --- a/src/main/java/ui/Ui.java +++ b/src/main/java/ui/Ui.java @@ -28,7 +28,7 @@ public void showWelcome() { /** * Calls when execute method of ExitCommand is called. */ - public void showGoodbye(){ + public void showGoodbye() { System.out.println("Bye. Hope to see you again soon!"); showLine(); } @@ -36,7 +36,7 @@ public void showGoodbye(){ /** * Prints border. */ - public void showLine(){ + public void showLine() { String output = ""; for (int n = 0; n < 80; n++) { output += "_"; @@ -50,7 +50,7 @@ public void showLine(){ * @param newTask task entered. * @param taskNum number of tasks in Task List. */ - public void readTask(Task newTask, int taskNum){ + public void readTask(Task newTask, int taskNum) { System.out.println("Got it. I've added this task:"); System.out.println(newTask.toString()); System.out.println("Now you have " + taskNum + " tasks in the list."); @@ -63,7 +63,7 @@ public void readTask(Task newTask, int taskNum){ * @param removeTask * @param taskNum */ - public void readDelete(Task removeTask, int taskNum){ + public void readDelete(Task removeTask, int taskNum) { System.out.println("Noted. I've removed this task:"); System.out.println(removeTask.toString()); System.out.println("Now you have " + taskNum + " tasks in the list."); @@ -113,7 +113,7 @@ public void showError(String error_msg) { * Calls when Duke Exception is thrown when instantiating Task List and/ or Storage Objects. * Calls by run method in Duke class. */ - public void showLoadingError(){ + public void showLoadingError() { System.out.println("Loading Error!"); showLine(); } From cc35296d28e0db611e8aa9dfea00199b2da980c4 Mon Sep 17 00:00:00 2001 From: Yan Ru Date: Sun, 31 Oct 2021 14:19:29 +0800 Subject: [PATCH 16/26] Level 8.3 - Add gradle support and use checkstyle to follow coding standard --- build.gradle | 2 +- src/main/java/Duke.java | 6 ++ src/main/java/command/AddCommand.java | 16 +++-- src/main/java/command/Command.java | 9 ++- src/main/java/command/DeleteCommand.java | 2 +- src/main/java/command/EditCommand.java | 2 +- src/main/java/command/ExitCommand.java | 2 +- src/main/java/command/FindCommand.java | 6 +- src/main/java/command/FullCommand.java | 15 +++-- src/main/java/command/ListCommand.java | 4 +- src/main/java/datetime/DateTime.java | 71 ++++++++++++---------- src/main/java/exception/DukeException.java | 4 +- src/main/java/filewriter/Storage.java | 28 +++++---- src/main/java/parser/Parser.java | 15 +++-- src/main/java/task/Deadline.java | 30 ++++----- src/main/java/task/Event.java | 53 ++++++++-------- src/main/java/task/Task.java | 5 +- src/main/java/task/TaskList.java | 49 +++++++++------ src/main/java/task/TaskStub.java | 2 +- src/main/java/task/Todo.java | 5 +- src/main/java/ui/Ui.java | 18 +++--- 21 files changed, 192 insertions(+), 152 deletions(-) diff --git a/build.gradle b/build.gradle index 20c0521c..46095dd3 100644 --- a/build.gradle +++ b/build.gradle @@ -29,7 +29,7 @@ test { } application { - mainClassName = "seedu.duke.Duke" + mainClassName = "Duke" } shadowJar { diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index b7c70839..0906bf1d 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -11,6 +11,9 @@ public class Duke { private TaskList tasks; private Ui ui; + /** + * Shows the UI and stores/loads the file. + */ public Duke(String filePath) { ui = new Ui(); try { @@ -22,6 +25,9 @@ public Duke(String filePath) { } } + /** + * Runs the DUKE program. + */ public void run() { ui.showWelcome(); boolean isExit = false; diff --git a/src/main/java/command/AddCommand.java b/src/main/java/command/AddCommand.java index cd1e581b..972d5ce2 100644 --- a/src/main/java/command/AddCommand.java +++ b/src/main/java/command/AddCommand.java @@ -2,7 +2,11 @@ import exception.DukeException; import filewriter.Storage; -import task.*; +import task.Deadline; +import task.Event; +import task.Task; +import task.TaskList; +import task.Todo; import ui.Ui; /** @@ -16,7 +20,7 @@ public class AddCommand extends Command { * @param taskType type of command/task (e.g. deadline, event, find etc). * @param specifics includes the command/task name as well as the date and time specially for DEADLINE and EVENT. */ - public AddCommand(FullCommand taskType, String specifics){ + public AddCommand(FullCommand taskType, String specifics) { super.type = taskType; this.specifics = specifics; } @@ -25,7 +29,7 @@ public AddCommand(FullCommand taskType, String specifics){ * Checks if command is an ExitCommand. * @return false as command will be AddCommand. */ - public boolean isExit(){ + public boolean isExit() { return false; } @@ -38,7 +42,7 @@ public boolean isExit(){ */ public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { Task newTask; - switch(type){ + switch (type) { case TODO: newTask = new Todo(specifics); break; @@ -51,11 +55,11 @@ public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException } tasks.addTask(newTask); ui.readTask(newTask, tasks.taskNum); - if (tasks.isFirst){ + if (tasks.isFirst) { tasks.isFirst = !tasks.isFirst; } else { storage.writeToFile(System.lineSeparator()); } storage.writeToFile(newTask.toString()); - }; + } } \ No newline at end of file diff --git a/src/main/java/command/Command.java b/src/main/java/command/Command.java index 734b2bea..93d84fe2 100644 --- a/src/main/java/command/Command.java +++ b/src/main/java/command/Command.java @@ -2,12 +2,13 @@ import exception.DukeException; import filewriter.Storage; -import task.*; +import task.TaskList; import ui.Ui; /** - * Abstract class for all other command classes to extend to (e.g. DeleteCommand, EditCommand, etc.), except FulLCommand enum. + * Abstract class for all other command classes to extend to (e.g. DeleteCommand, etc.), except FulLCommand enum. */ +@SuppressWarnings("checkstyle:LineLength") public abstract class Command { FullCommand type; @@ -24,7 +25,9 @@ public boolean isExit() { * @param tasks Current TaskList object used in this instance of Duke. * @param ui Instance of user interface to print feedback to user. * @param storage Updates data record of TaskList in storage.filepath if needed. - * @throws DukeException Various subclass of Command may throw DukeException when executed with invalid specification. + * @throws DukeException Various subclass of Command may throw DukeException when + * executed with invalid specification. */ + @SuppressWarnings("checkstyle:LineLength") public abstract void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException; } \ No newline at end of file diff --git a/src/main/java/command/DeleteCommand.java b/src/main/java/command/DeleteCommand.java index 577f7724..f3514465 100644 --- a/src/main/java/command/DeleteCommand.java +++ b/src/main/java/command/DeleteCommand.java @@ -16,7 +16,7 @@ public class DeleteCommand extends Command { * Constructor for DeleteCommand. * @param index of the task to delete in the Task List. */ - public DeleteCommand(int index){ + public DeleteCommand(int index) { this.index = index; super.type = FullCommand.DELETE; } diff --git a/src/main/java/command/EditCommand.java b/src/main/java/command/EditCommand.java index 7d8097b6..a4a94189 100644 --- a/src/main/java/command/EditCommand.java +++ b/src/main/java/command/EditCommand.java @@ -36,7 +36,7 @@ public EditCommand(int index) { */ public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { try { - if (index == -1){ + if (index == -1) { throw new IndexOutOfBoundsException(); } Task completedTask = tasks.complete(index); diff --git a/src/main/java/command/ExitCommand.java b/src/main/java/command/ExitCommand.java index abb3e30d..2073d6e9 100644 --- a/src/main/java/command/ExitCommand.java +++ b/src/main/java/command/ExitCommand.java @@ -30,5 +30,5 @@ public boolean isExit() { */ public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { storage.closeWriter(); - }; + } } \ No newline at end of file diff --git a/src/main/java/command/FindCommand.java b/src/main/java/command/FindCommand.java index bfd6330c..246ea2fd 100644 --- a/src/main/java/command/FindCommand.java +++ b/src/main/java/command/FindCommand.java @@ -37,12 +37,12 @@ public boolean isExit() { * @param tasks TaskList containing all Tasks. * @param ui Instance of user interface to print feedback to user. * @param storage Updates data record of TaskList in storage.filepath if needed. - * @throws DukeException + * @throws DukeException when command cannot be found. */ public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { ArrayList shortlist = new ArrayList<>(); - for (Task task: tasks.getList()){ - if (task.findWord(keyword)){ + for (Task task: tasks.getList()) { + if (task.findWord(keyword)) { shortlist.add(task); } } diff --git a/src/main/java/command/FullCommand.java b/src/main/java/command/FullCommand.java index 8fdf88fb..21aa032a 100644 --- a/src/main/java/command/FullCommand.java +++ b/src/main/java/command/FullCommand.java @@ -9,17 +9,17 @@ */ public enum FullCommand { LIST("list"), - TODO ("todo"), - EVENT ("event"), - DEADLINE ("deadline"), - DONE ("done"), - DELETE ("delete"), + TODO("todo"), + EVENT("event"), + DEADLINE("deadline"), + DONE("done"), + DELETE("delete"), BYE("bye"), FIND("find"); private String activityName; - FullCommand (String activityName) { + FullCommand(String activityName) { this.activityName = activityName; } @@ -33,8 +33,7 @@ public String getActivityName() { * @return Returns FullCommand to be parsed by parser. * @throws DukeException Throws when user enters invalid command. */ - public static FullCommand getByAction (String keyword) throws DukeException - { + public static FullCommand getByAction(String keyword) throws DukeException { for (FullCommand activity : values()) { if (activity.getActivityName().equals(keyword)) { return activity; diff --git a/src/main/java/command/ListCommand.java b/src/main/java/command/ListCommand.java index 364c88f0..87175da1 100644 --- a/src/main/java/command/ListCommand.java +++ b/src/main/java/command/ListCommand.java @@ -31,7 +31,7 @@ public boolean isExit() { * @param tasks Current TaskList object used in this instance of Duke. * @param ui Instance of user interface to print feedback to user. * @param storage Updates data record of TaskList in storage.filepath if needed. - * @throws DukeException + * @throws DukeException if the number is not in the list or no number was indicated. */ public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { try { @@ -44,5 +44,5 @@ public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException } catch (NumberFormatException e) { throw new DukeException("Please enter the task number that you would like to delete."); } - }; + } } \ No newline at end of file diff --git a/src/main/java/datetime/DateTime.java b/src/main/java/datetime/DateTime.java index 4f2858a8..67790560 100644 --- a/src/main/java/datetime/DateTime.java +++ b/src/main/java/datetime/DateTime.java @@ -10,6 +10,7 @@ * Wrapper of LocalDateTime. * Stores date and time for Task. */ +@SuppressWarnings("checkstyle:SummaryJavadoc") public class DateTime { private LocalTime endDateTime; private LocalDateTime startDateTime; @@ -31,7 +32,8 @@ public static DateTime setDeadline(String dateTime) throws DukeException { deadlineDateTime.displayDateTime = deadlineDateTime.startDateTime.format(getStartDateTime); return deadlineDateTime; } catch (DateTimeParseException e) { - throw new DukeException("Incorrect deadline datetime format. \nPlease key in deadline (task) /by d/MM/yyyy HHmm"); + throw new DukeException("Incorrect deadline datetime format. " + + "\n Please key in deadline (task) /by d/MM/yyyy HHmm"); } } @@ -41,7 +43,7 @@ public static DateTime setDeadline(String dateTime) throws DukeException { * @param dateTime Must be in the form (d/MM/yyyy HHmm). * @return DateTime object as dateTime parameter of Deadline object. */ - public static DateTime readDeadLine(String dateTime){ + public static DateTime readDeadLine(String dateTime) { DateTime deadlineDateTime = new DateTime(); deadlineDateTime.startDateTime = LocalDateTime.parse(dateTime, getStartDateTime); deadlineDateTime.displayDateTime = deadlineDateTime.startDateTime.format(getStartDateTime); @@ -55,7 +57,7 @@ public static DateTime readDeadLine(String dateTime){ * @return DateTime object as dateTime parameter of Event object. * @throws DukeException Throws if missing end time or if dateTime is not presented in the correct form. */ - public static DateTime setEventTime (String dateTime) throws DukeException { + public static DateTime setEventTime(String dateTime) throws DukeException { try { DateTime eventDateTime = new DateTime(); int divider = dateTime.indexOf("-"); @@ -65,10 +67,12 @@ public static DateTime setEventTime (String dateTime) throws DukeException { String end = dateTime.substring(divider + 1, dateTime.length()); eventDateTime.startDateTime = LocalDateTime.parse(dateTime.substring(0, divider), getStartDateTime); eventDateTime.endDateTime = LocalTime.parse(end, DateTimeFormatter.ofPattern("HHmm")); - eventDateTime.displayDateTime = eventDateTime.startDateTime.format(getStartDateTime) + "-" + eventDateTime.endDateTime.format(getTime); + eventDateTime.displayDateTime = eventDateTime.startDateTime.format(getStartDateTime) + "-" + + eventDateTime.endDateTime.format(getTime); return eventDateTime; } catch (DateTimeParseException e) { - throw new DukeException("Incorrect event format. \n Please key in event (details) /at d/mm/yyyy (start time)HHmm-(end time)HHmm"); + throw new DukeException("Incorrect event format. " + + "\n Please key in event (details) /at d/mm/yyyy (start time)HHmm-(end time)HHmm"); } } @@ -79,45 +83,46 @@ public static DateTime setEventTime (String dateTime) throws DukeException { * @return DateTime object as dateTime parameter of Event object. * @throws DukeException Throws if missing end time. This should not happen unless text file was tampered with. */ - public static DateTime readEventTime (String dateTime) throws DukeException { + public static DateTime readEventTime(String dateTime) throws DukeException { DateTime eventDateTime = new DateTime(); int divider = dateTime.indexOf("-"); - if (divider == -1 || (divider == dateTime.length() - 1) || dateTime.substring(divider + 1).replace(" ", "").equals("")) { + if (divider == -1 || (divider == dateTime.length() - 1) + || dateTime.substring(divider + 1).replace(" ", "").equals("")) { throw new DukeException("Please key in the end time."); } String end = dateTime.substring(divider + 1, dateTime.length()); eventDateTime.startDateTime = LocalDateTime.parse(dateTime.substring(0,divider), getStartDateTime); eventDateTime.endDateTime = LocalTime.parse(end, DateTimeFormatter.ofPattern("HHmm")); - eventDateTime.displayDateTime = eventDateTime.startDateTime.format(getStartDateTime) + "-" + eventDateTime.endDateTime.format(getTime); + eventDateTime.displayDateTime = eventDateTime.startDateTime.format(getStartDateTime) + "-" + + eventDateTime.endDateTime.format(getTime); return eventDateTime; } - /** - * Adds suffix to date of month + /** + * Adds suffix to date of month. * @param dd day of month as a 2 digits number. * @return 1 or 2 digits number with appropriate suffix. - */ - private static String getDayOfMonth(String dd) { - if (dd.substring(0,1).equals("0")) { - dd = dd.substring(1,dd.length()); - } - int day = Integer.parseInt(dd); - if (day >= 11 && day <= 31) { - return day + "th"; - } - switch (day & 10) { - case 1: - return day + "st"; - case 2: - return day + "nd"; - case 3: - return day + "rd"; - default: - return day + "th"; - } - } + */private static String getDayOfMonth(String dd) { + if (dd.substring(0, 1).equals("0")) { + dd = dd.substring(1, dd.length()); + } + int day = Integer.parseInt(dd); + if (day >= 11 && day <= 31) { + return day + "th"; + } + switch (day & 10) { + case 1: + return day + "st"; + case 2: + return day + "nd"; + case 3: + return day + "rd"; + default: + return day + "th"; + } + } - public String toString() { - return displayDateTime; - } + public String toString() { + return displayDateTime; + } } diff --git a/src/main/java/exception/DukeException.java b/src/main/java/exception/DukeException.java index 8e95f0a1..f321c58d 100644 --- a/src/main/java/exception/DukeException.java +++ b/src/main/java/exception/DukeException.java @@ -4,11 +4,11 @@ * Wraps exception. Helps to check if exception has been accounted for. * Catches exception and throws DukeException. */ -public class DukeException extends Exception -{ +public class DukeException extends Exception { public DukeException(String message) { super(message); } + public DukeException(String message, Throwable cause) { super(message,cause); } diff --git a/src/main/java/filewriter/Storage.java b/src/main/java/filewriter/Storage.java index 9dc72d86..f5048103 100644 --- a/src/main/java/filewriter/Storage.java +++ b/src/main/java/filewriter/Storage.java @@ -2,7 +2,11 @@ import datetime.DateTime; import exception.DukeException; -import task.*; +import task.Deadline; +import task.Event; +import task.Task; +import task.TaskList; +import task.Todo; import java.io.FileNotFoundException; import java.util.Scanner; import java.util.ArrayList; @@ -35,10 +39,10 @@ public Storage(String filepath) throws DukeException { * @throws DukeException when invalid filepath. */ private void createWriter() throws DukeException { - try{ + try { fw = new FileWriter(filePath, isAppend); } catch (IOException e) { - throw new DukeException("\n Invalid filepath. Please check. \n"); + throw new DukeException("\n Invalid filepath. Please check."); } } @@ -47,11 +51,11 @@ private void createWriter() throws DukeException { * Uses in Execute method of ExitCommand object. * @throws DukeException when invalid filepath. */ - public void closeWriter() throws DukeException{ + public void closeWriter() throws DukeException { try { fw.close(); } catch (IOException e) { - throw new DukeException("\n Invalid filepath. Please check. \n"); + throw new DukeException("\n Invalid filepath. Please check."); } } @@ -61,12 +65,12 @@ public void closeWriter() throws DukeException{ * @param textToAdd (Command Object).to String(); * @throws DukeException Throws when IOException is caught. */ - public void writeToFile(String textToAdd) throws DukeException{ + public void writeToFile(String textToAdd) throws DukeException { try { fw.write(textToAdd + "\n"); //System.out.println(textToAdd); } catch (IOException e) { - throw new DukeException("\n Please check. Error message: " + e.getMessage() + "\n"); + throw new DukeException("\n Please check. Error message: " + e.getMessage()); } } @@ -76,7 +80,7 @@ public void writeToFile(String textToAdd) throws DukeException{ * @param schedule Modifies contents of TaskList. * @throws DukeException Throws when IOException is caught. */ - public void editFile(TaskList schedule) throws DukeException{ + public void editFile(TaskList schedule) throws DukeException { try { checkAppend(false); for (Task task: schedule.getList()) { @@ -119,7 +123,7 @@ private Task read(String line) throws DukeException { return output; } catch (Exception e) { System.out.println(e.getMessage()); - throw new DukeException("\n Deadline task not stored properly. \n "); + throw new DukeException("\n Deadline task not stored properly."); } default: try { @@ -129,7 +133,7 @@ private Task read(String line) throws DukeException { output = new Event(input); return output; } catch (Exception e) { - throw new DukeException("\n Event task not stored properly. \n "); + throw new DukeException("\n Event task not stored properly."); } } } @@ -138,7 +142,7 @@ private Task read(String line) throws DukeException { * Reads txt file and updates TaskList accordingly. * Calls read method line by line, and gets corresponding Task. * Checks each line to check if Task should markAsDone. - * @return Storage object with updated ArrayList taskList used in construction of TaskList object. + * @return Storage object with updated Task taskList used in construction of TaskList object. * @throws DukeException when read throws DukeException. */ public Storage load() throws DukeException { @@ -160,7 +164,7 @@ public Storage load() throws DukeException { } catch (FileNotFoundException e) { throw new DukeException("\n File not found! \n "); } catch (Exception e) { - throw new DukeException("Unforeseen load errors: " + e.getMessage()); + throw new DukeException("Unexpected Error: " + e.getMessage()); } } diff --git a/src/main/java/parser/Parser.java b/src/main/java/parser/Parser.java index 172467c6..65d38473 100644 --- a/src/main/java/parser/Parser.java +++ b/src/main/java/parser/Parser.java @@ -1,6 +1,13 @@ package parser; -import command.*; +import command.AddCommand; +import command.Command; +import command.DeleteCommand; +import command.EditCommand; +import command.ExitCommand; +import command.FindCommand; +import command.FullCommand; +import command.ListCommand; import exception.DukeException; /** @@ -34,8 +41,8 @@ public static Command parse(String instruction) throws DukeException { } return new AddCommand(command, arr[1]); } - } catch (IndexOutOfBoundsException | DukeException e) { - throw new DukeException("Please provide commands in the format: (command type) (task details)"); + } catch (IndexOutOfBoundsException | DukeException e) { + throw new DukeException("Please provide commands in the format: (command type) (task details)"); + } } } -} diff --git a/src/main/java/task/Deadline.java b/src/main/java/task/Deadline.java index f218cf3f..d84830f0 100644 --- a/src/main/java/task/Deadline.java +++ b/src/main/java/task/Deadline.java @@ -8,23 +8,25 @@ */ public class Deadline extends Task { DateTime dateTime; - /** - * Constructor for Deadline object. Calls when generating TaskList based on user input. - * @param description Includes task name, date and time. - * @throws DukeException Throws when description format is incorrect. or when setDeadline throws DukeException. - */ - public Deadline(String description) throws DukeException { - super(description); - int divider = description.indexOf("/by"); - if (divider == -1 || (divider == description.length() -3)||description.substring(divider + 4).replace(" ", "").equals("")) { - throw new DukeException("Incorrect deadline format. \nPlease key in deadline (task) /by d/mm/yyyy HHmm"); - } - dateTime = DateTime.setDeadline(description.substring(divider + 4, description.length())); - super.taskDesc = super.taskDesc.substring(0,divider); + + /** + * Constructor for Deadline object. Calls when generating TaskList based on user input. + * @param description Includes task name, date and time. + * @throws DukeException Throws when description format is incorrect. or when setDeadline throws DukeException. + */ + public Deadline(String description) throws DukeException { + super(description); + int divider = description.indexOf("/by"); + if (divider == -1 || (divider == description.length() - 3) + || description.substring(divider + 4).replace(" ", "").equals("")) { + throw new DukeException("Incorrect deadline format. \nPlease key in deadline (task) /by d/mm/yyyy HHmm"); } + dateTime = DateTime.setDeadline(description.substring(divider + 4, description.length())); + super.taskDesc = super.taskDesc.substring(0,divider); + } /** - * toString method of DeadLine + * toString method of DeadLine. * @return String denoting task name, status date and time of Deadline task. */ @Override diff --git a/src/main/java/task/Event.java b/src/main/java/task/Event.java index 79688550..7b027ca7 100644 --- a/src/main/java/task/Event.java +++ b/src/main/java/task/Event.java @@ -7,31 +7,34 @@ * toString method of DeadLine. * @return String denoting task name, status date and time of Deadline task. */ -public class Event extends Task -{ - DateTime dateTime; - /** - * Constructor for Event object. Calls when generating TaskList based on user input. - * @param description Contains task name, date, starting time and ending time. - * @throws DukeException Throws when description format is incorrect. or when setEventTime throws DukeException. - */ - public Event(String description) throws DukeException { - super(description); - int divider = description.indexOf("/at"); - if (divider == -1 || (divider == description.length() - 3)||description.substring(divider + 4).replace(" ", "").equals("")) { - throw new DukeException("Incorrect event format. \nPlease key in event (details) /at d/mm/yyyy (start time)HHmm-(end time)HHmm"); - } - dateTime = DateTime.setEventTime(description.substring(divider + 4, description.length())); - super.taskDesc = super.taskDesc.substring(0,divider); - } +public class Event extends Task { + DateTime dateTime; - /** - * toString method of Event. - * @return String denoting task name, status, date, and starting and ending time of task. - */ - @Override - public String toString() { - String output = "[E][" + super.getStatus() + "]" + " " + super.taskDesc + "(at: " + dateTime.toString() + ")"; - return output; + /** + * Constructor for Event object. Calls when generating TaskList based on user input. + * @param description Contains task name, date, starting time and ending time. + * @throws DukeException Throws when description format is incorrect. or when setEventTime throws DukeException. + */ + public Event(String description) throws DukeException { + super(description); + int divider = description.indexOf("/at"); + if (divider == -1 || (divider == description.length() - 3) + || description.substring(divider + 4).replace(" ", "").equals("")) { + throw new DukeException("Incorrect event format. \n" + + "Please key in event (details) /at d/mm/yyyy (start time)HHmm-(end time)HHmm"); } + dateTime = DateTime.setEventTime(description.substring(divider + 4, description.length())); + super.taskDesc = super.taskDesc.substring(0,divider); + } + + /** + * toString method of Event. + * @return String denoting task name, status, date, and starting and ending time of task. + */ + @Override + public String toString() { + String output = "[E][" + super.getStatus() + "]" + " " + super.taskDesc + + "(at: " + dateTime.toString() + ")"; + return output; + } } diff --git a/src/main/java/task/Task.java b/src/main/java/task/Task.java index b71b549c..0e50bcd9 100644 --- a/src/main/java/task/Task.java +++ b/src/main/java/task/Task.java @@ -3,8 +3,7 @@ /** * Abstract class which all other Task classes extend (Event, deadline etc.). */ -public abstract class Task -{ +public abstract class Task { protected String taskDesc; protected boolean isDone; protected boolean deleted; @@ -26,7 +25,7 @@ public Task(String taskDesc) { * @return boolean value indicating if word/phrase can be found in description. */ public boolean findWord(String keyword) { - if (taskDesc.indexOf(keyword) == -1){ + if (taskDesc.indexOf(keyword) == -1) { return false; } else { return true; diff --git a/src/main/java/task/TaskList.java b/src/main/java/task/TaskList.java index b7430b49..3c263340 100644 --- a/src/main/java/task/TaskList.java +++ b/src/main/java/task/TaskList.java @@ -9,9 +9,8 @@ /** * Object containing data structure to store Tasks. */ -public class TaskList -{ - ArrayList taskList = new ArrayList<> (); +public class TaskList { + ArrayList taskList = new ArrayList<>(); public int taskNum; public boolean isFirst; @@ -24,7 +23,7 @@ public TaskList(Storage storage) { try { this.taskList = storage.getSchedule(); taskNum = taskList.size(); - if (taskNum == 0){ + if (taskNum == 0) { isFirst = true; } else { isFirst = false; @@ -75,12 +74,17 @@ public Task getTask(int index) { * Marks a Task in task list as done. * @param index Specifies which task to mark as done. * @return completed Task. - * @throws NullPointerException - * @throws IndexOutOfBoundsException - * @throws NumberFormatException - * @throws DukeException + * @throws NullPointerException Throws when an application attempts + * to use null in a case where an object is required. + * @throws IndexOutOfBoundsException Throws to indicate that an index of + * some sort (such as to an array, to a string, or to a vector) is out of range. + * @throws NumberFormatException Throws to indicate that the application has attempted to convert a string to + * one of the numeric types, but that the string does not have appropriate format. + * @throws DukeException Wraps exception. Helps to check if + * exception has been accounted for. Catches exception and throws DukeException. */ - public Task complete(int index) throws NullPointerException, IndexOutOfBoundsException, NumberFormatException, DukeException { + public Task complete(int index) throws NullPointerException, IndexOutOfBoundsException, + NumberFormatException, DukeException { Task completedTask = taskList.get(index); completedTask.markAsDone(); return completedTask; @@ -88,8 +92,8 @@ public Task complete(int index) throws NullPointerException, IndexOutOfBoundsExc /** * Adds new Task to TaskList. - * @param task - * @throws DukeException + * @param task to add a new task. + * @throws DukeException if unexpected error occurs. */ public void addTask(Task task) throws DukeException { taskList.add(task); @@ -98,14 +102,19 @@ public void addTask(Task task) throws DukeException { /** * Deletes Task from TaskList. - * @param index + * @param index based on the index number. * @return deleted Task. - * @throws NullPointerException - * @throws IndexOutOfBoundsException - * @throws NumberFormatException - * @throws DukeException + * @throws NullPointerException Throws when an application attempts + * to use null in a case where an object is required. + * @throws IndexOutOfBoundsException Throws to indicate that an index of + * some sort (such as to an array, to a string, or to a vector) is out of range. + * @throws NumberFormatException Throws to indicate that the application has attempted to convert a string to + * one of the numeric types, but that the string does not have appropriate format. + * @throws DukeException Wraps exception. Helps to check if + * exception has been accounted for. Catches exception and throws DukeException. */ - public Task remove(int index) throws NullPointerException, IndexOutOfBoundsException, NumberFormatException, DukeException { + public Task remove(int index) throws NullPointerException, IndexOutOfBoundsException, + NumberFormatException, DukeException { Task removeTask = taskList.get(index); taskList.remove(index); taskNum--; @@ -113,9 +122,9 @@ public Task remove(int index) throws NullPointerException, IndexOutOfBoundsExcep } /** - * Retrieves ArrayList schedule from TaskList. + * Retrieves Task schedule from TaskList array. * Calls when execute method of FindCommand is called. - * @return ArrayList + * @return Task schedule from TaskList array. */ public ArrayList getList() { return taskList; @@ -127,7 +136,7 @@ public ArrayList getList() { */ public String toString() { String output = ""; - for (int idx = 0; idx < taskNum; idx ++) { + for (int idx = 0; idx < taskNum; idx++) { Task task = taskList.get(idx); output += ((idx + 1) + "." + task.toString() + "\n"); } diff --git a/src/main/java/task/TaskStub.java b/src/main/java/task/TaskStub.java index 96468370..23905427 100644 --- a/src/main/java/task/TaskStub.java +++ b/src/main/java/task/TaskStub.java @@ -3,7 +3,7 @@ /** * Used for testing purposes. */ -public class TaskStub extends Task{ +public class TaskStub extends Task { public TaskStub(String description) { super(description); super.isDone = true; diff --git a/src/main/java/task/Todo.java b/src/main/java/task/Todo.java index 77ab309b..e11f4151 100644 --- a/src/main/java/task/Todo.java +++ b/src/main/java/task/Todo.java @@ -6,15 +6,14 @@ /** * Extends Task. Has a task name. */ -public class Todo extends Task -{ +public class Todo extends Task { public Todo(String description) throws DukeException { super(description); } @Override public String toString() { - String output = "[T][" + super.getStatus()+ "]" + " " + super.taskDesc; + String output = "[T][" + super.getStatus() + "]" + " " + super.taskDesc; return output; } } \ No newline at end of file diff --git a/src/main/java/ui/Ui.java b/src/main/java/ui/Ui.java index edfb2b8c..77a05d7c 100644 --- a/src/main/java/ui/Ui.java +++ b/src/main/java/ui/Ui.java @@ -60,8 +60,8 @@ public void readTask(Task newTask, int taskNum) { /** * Displays delete message. * Calls when execute of DeleteCommand is called. - * @param removeTask - * @param taskNum + * @param removeTask for task to be removed. + * @param taskNum number of tasks in the Task List. */ public void readDelete(Task removeTask, int taskNum) { System.out.println("Noted. I've removed this task:"); @@ -73,11 +73,11 @@ public void readDelete(Task removeTask, int taskNum) { /** * Displays message when a user finishes a task. * Calls when execute of EditCommand is called. - * @param completed_Task Task which has been marked as done. + * @param completeTask Task which has been marked as done. */ - public void readDone(Task completed_Task){ + public void readDone(Task completeTask) { System.out.println("Nice! I've marked this task as done:"); - System.out.println(completed_Task.toString()); + System.out.println(completeTask.toString()); showLine(); } @@ -86,7 +86,7 @@ public void readDone(Task completed_Task){ * Calls when execute of ListCommand is called. * @param tasks TaskList */ - public void readList(TaskList tasks){ + public void readList(TaskList tasks) { System.out.println("Here are the tasks in your list: "); System.out.println(tasks); showLine(); @@ -102,10 +102,10 @@ public String readCommand() { /** * Displays message when DukeException is caught. - * @param error_msg DukeException e.getMessage() + * @param errorMsg DukeException e.getMessage() */ - public void showError(String error_msg) { - System.out.println("Your error message: " + error_msg); + public void showError(String errorMsg) { + System.out.println("Your error message: " + errorMsg); showLine(); } From d747162334f7a1cc009c4d971270b92af7678037 Mon Sep 17 00:00:00 2001 From: Yan Ru Date: Sun, 31 Oct 2021 14:43:11 +0800 Subject: [PATCH 17/26] Level 8.4 - Tweak code after 1 unit test case failed when running gradle test after setting up gradle for unit testing based on previous commit. Result: Passed 12 cases. --- src/main/java/datetime/DateTime.java | 4 ++-- src/test/java/datetime/DateTimeTest.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/datetime/DateTime.java b/src/main/java/datetime/DateTime.java index 67790560..5c3c357a 100644 --- a/src/main/java/datetime/DateTime.java +++ b/src/main/java/datetime/DateTime.java @@ -32,8 +32,8 @@ public static DateTime setDeadline(String dateTime) throws DukeException { deadlineDateTime.displayDateTime = deadlineDateTime.startDateTime.format(getStartDateTime); return deadlineDateTime; } catch (DateTimeParseException e) { - throw new DukeException("Incorrect deadline datetime format. " - + "\n Please key in deadline (task) /by d/MM/yyyy HHmm"); + throw new DukeException("Incorrect deadline datetime format." + + "\nPlease key in deadline (task) /by d/MM/yyyy HHmm"); } } diff --git a/src/test/java/datetime/DateTimeTest.java b/src/test/java/datetime/DateTimeTest.java index 471d901f..f113a2ae 100644 --- a/src/test/java/datetime/DateTimeTest.java +++ b/src/test/java/datetime/DateTimeTest.java @@ -12,7 +12,7 @@ public void setDeadLine_incorrectFormat_exceptionThrown(){ DateTime.setDeadline("10.02.2021 15:00"); fail(); } catch (DukeException e){ - assertEquals("Incorrect deadline datetime format. \nPlease key in deadline (task) /by d/mm/yyyy HHmm", e.getMessage()); + assertEquals("Incorrect deadline datetime format.\nPlease key in deadline (task) /by d/MM/yyyy HHmm", e.getMessage()); } } @Test From 53ebd30bc90cea716f5ada9149b26385a15e665b Mon Sep 17 00:00:00 2001 From: Yan Ru Date: Sun, 31 Oct 2021 16:52:37 +0800 Subject: [PATCH 18/26] Level 10 - Implement GUI using JavaFX --- build.gradle | 8 +- src/main/java/DialogBox.java | 50 +++++++++++ src/main/java/Duke.java | 160 ++++++++++++++++++++++++++++++++++- src/main/java/Launcher.java | 12 +++ src/main/java/ui/Ui.java | 10 ++- 5 files changed, 236 insertions(+), 4 deletions(-) create mode 100644 src/main/java/DialogBox.java create mode 100644 src/main/java/Launcher.java diff --git a/build.gradle b/build.gradle index 46095dd3..2d68fd18 100644 --- a/build.gradle +++ b/build.gradle @@ -3,6 +3,7 @@ plugins { id 'application' id 'checkstyle' id 'com.github.johnrengelman.shadow' version '5.1.0' + id 'org.openjfx.javafxplugin' version '0.0.10' } repositories { @@ -29,7 +30,7 @@ test { } application { - mainClassName = "Duke" + mainClassName = "Launcher" } shadowJar { @@ -44,3 +45,8 @@ checkstyle { run{ standardInput = System.in } + +javafx { + version = "11.0.2" + modules = [ 'javafx.controls', 'javafx.fxml' ] +} \ No newline at end of file diff --git a/src/main/java/DialogBox.java b/src/main/java/DialogBox.java new file mode 100644 index 00000000..27206458 --- /dev/null +++ b/src/main/java/DialogBox.java @@ -0,0 +1,50 @@ +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.geometry.Pos; +import javafx.scene.Node; +import javafx.scene.control.Label; +import javafx.scene.image.ImageView; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Priority; +import javafx.scene.layout.Region; + +public class DialogBox extends HBox { + + private Label text; + private ImageView displayPicture; + + /** + * Controls the layout of the UI. + */ + public DialogBox(Label l, ImageView iv) { + text = l; + displayPicture = iv; + text.setWrapText(true); + displayPicture.setFitWidth(100.0); + displayPicture.setFitHeight(100.0); + Region region = new Region(); + HBox.setHgrow(region, Priority.ALWAYS); + this.setAlignment(Pos.TOP_RIGHT); + this.getChildren().addAll(text, region, displayPicture); + } + + /** + * Flips the dialog box such that the ImageView is on the left and text on the right. + */ + private void flip() { + this.setAlignment(Pos.TOP_LEFT); + ObservableList tmp = FXCollections.observableArrayList(this.getChildren()); + FXCollections.reverse(tmp); + this.getChildren().setAll(tmp); + } + + public static DialogBox getUserDialog(Label l, ImageView iv) { + return new DialogBox(l, iv); + } + + public static DialogBox getDukeDialog(Label l, ImageView iv) { + var db = new DialogBox(l, iv); + db.flip(); + return db; + } +} \ No newline at end of file diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 0906bf1d..7a1c415b 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -4,12 +4,53 @@ import parser.Parser; import task.TaskList; import ui.Ui; +import javafx.animation.PauseTransition; +import javafx.application.Application; +import javafx.application.Platform; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.control.ScrollPane; +import javafx.scene.control.TextField; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.Region; +import javafx.scene.layout.VBox; +import javafx.scene.Scene; +import javafx.stage.Stage; +import javafx.util.Duration; +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.util.concurrent.TimeUnit; -public class Duke { +public class Duke extends Application { private Storage storage; private TaskList tasks; private Ui ui; + private ScrollPane scrollPane; + private VBox dialogContainer; + private TextField userInput; + private Button sendButton; + private Scene scene; + private Image user = new Image(this.getClass().getResourceAsStream("/images/UserYAN.png")); + private Image duke = new Image(this.getClass().getResourceAsStream("/images/AdminDUKE.png")); + + /** + * Constructor for Duke with no params. + * Required to for javaFx application to function. + */ + public Duke() { + String filePath = "data/tasks.txt"; + ui = new Ui(); + try { + storage = new Storage(filePath); + tasks = new TaskList(storage.load()); + } catch (DukeException e) { + ui.showLoadingError(); + tasks = new TaskList(); + } + } /** * Shows the UI and stores/loads the file. @@ -48,5 +89,122 @@ public void run() { public static void main(String[] args) { new Duke("data/tasks.txt").run(); } + + @Override + public void start(Stage stage) { + scrollPane = new ScrollPane(); + dialogContainer = new VBox(20); + scrollPane.setContent(dialogContainer); + userInput = new TextField(); + sendButton = new Button("Send"); + AnchorPane mainLayout = new AnchorPane(); + mainLayout.getChildren().addAll(scrollPane, userInput, sendButton); + scene = new Scene(mainLayout); + stage.setScene(scene); + stage.show(); + stage.setTitle("Duke"); + stage.setResizable(false); + stage.setMinHeight(600.0); + stage.setMinWidth(400.0); + mainLayout.setPrefSize(400.0, 600.0); + scrollPane.setPrefSize(385, 535); + scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER); + scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.ALWAYS); + + scrollPane.setVvalue(1.0); + scrollPane.setFitToWidth(true); + + dialogContainer.setPrefHeight(Region.USE_COMPUTED_SIZE); + + userInput.setPrefWidth(325.0); + + sendButton.setPrefWidth(55.0); + + AnchorPane.setTopAnchor(scrollPane, 1.0); + + AnchorPane.setBottomAnchor(sendButton, 1.0); + AnchorPane.setRightAnchor(sendButton, 1.0); + + AnchorPane.setLeftAnchor(userInput, 1.0); + AnchorPane.setBottomAnchor(userInput, 1.0); + + sendButton.setOnMouseClicked((event) -> { + handleUserInput(); + }); + + userInput.setOnAction((event) -> { + handleUserInput(); + }); + + //Scroll down to the end every time dialogContainer's height changes. + dialogContainer.heightProperty().addListener((observable) -> scrollPane.setVvalue(1.0)); + Label welcomeText = displayWelcome(); + dialogContainer.getChildren().add( + DialogBox.getUserDialog(welcomeText, new ImageView(duke))); + } + + private Label displayWelcome() { + ByteArrayOutputStream outContent = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outContent)); + ui.showWelcome(); + return new Label(outContent.toString()); + } + + /** + * Iteration 1: + * Creates a label with the specified text and adds it to the dialog container. + * @param text String containing text to add + * @return a label with the specified text that has word wrap enabled. + */ + private Label getDialogLabel(String text) { + // You will need to import `javafx.scene.control.Label`. + Label textToAdd = new Label(text); + textToAdd.setWrapText(true); + + return textToAdd; + } + + /** + * Iteration 2: + * Creates two dialog boxes, one echoing user input and the other containing Duke's reply and then appends them to + * the dialog container. Clears the user input after processing. + */ + private void handleUserInput() { + Label userText = new Label(userInput.getText()); + String response = getResponse(userInput.getText()); + Label dukeText = new Label(response); + dialogContainer.getChildren().addAll( + DialogBox.getUserDialog(userText, new ImageView(user)), + DialogBox.getDukeDialog(dukeText, new ImageView(duke)) + ); + userInput.clear(); + if (response.replace(" ","").substring(0,3).equals("Bye")) { + PauseTransition pause = new PauseTransition(Duration.seconds(1)); + pause.setOnFinished(event -> Platform.exit()); + pause.play(); + } + } + + + /** + * You should have your own function to generate a response to user input. + * Replace this stub with your completed method. + */ + private String getResponse(String input) { + try { + Command c = Parser.parse(input); + ByteArrayOutputStream outContent = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outContent)); + if (c.isExit()) { + ui.showGoodbye(); + } else { + c.execute(tasks, ui, storage); + + } + return outContent.toString(); + } catch (DukeException e) { + return e.getMessage(); + } + } } diff --git a/src/main/java/Launcher.java b/src/main/java/Launcher.java new file mode 100644 index 00000000..0f6b5cb6 --- /dev/null +++ b/src/main/java/Launcher.java @@ -0,0 +1,12 @@ +import javafx.application.Application; + +/** + * A launcher class to workaround classpath issues. + */ +public class Launcher { + + + public static void main(String[] args) { + Application.launch(Duke.class, args); + } +} \ No newline at end of file diff --git a/src/main/java/ui/Ui.java b/src/main/java/ui/Ui.java index 77a05d7c..97ced8f1 100644 --- a/src/main/java/ui/Ui.java +++ b/src/main/java/ui/Ui.java @@ -20,7 +20,6 @@ public void showWelcome() { + "| | | | | | | |/ / _ \\\n" + "| |_| | |_| | < __/\n" + "|____/ \\__,_|_|\\_\\___|\n"; - showLine(); System.out.println("Hello! I'm \n" + logo + "What can I do for you?"); showLine(); } @@ -38,7 +37,7 @@ public void showGoodbye() { */ public void showLine() { String output = ""; - for (int n = 0; n < 80; n++) { + for (int n = 0; n < 50; n++) { output += "_"; } System.out.println(output); @@ -51,6 +50,7 @@ public void showLine() { * @param taskNum number of tasks in Task List. */ public void readTask(Task newTask, int taskNum) { + showLine(); System.out.println("Got it. I've added this task:"); System.out.println(newTask.toString()); System.out.println("Now you have " + taskNum + " tasks in the list."); @@ -64,6 +64,7 @@ public void readTask(Task newTask, int taskNum) { * @param taskNum number of tasks in the Task List. */ public void readDelete(Task removeTask, int taskNum) { + showLine(); System.out.println("Noted. I've removed this task:"); System.out.println(removeTask.toString()); System.out.println("Now you have " + taskNum + " tasks in the list."); @@ -76,6 +77,7 @@ public void readDelete(Task removeTask, int taskNum) { * @param completeTask Task which has been marked as done. */ public void readDone(Task completeTask) { + showLine(); System.out.println("Nice! I've marked this task as done:"); System.out.println(completeTask.toString()); showLine(); @@ -87,6 +89,7 @@ public void readDone(Task completeTask) { * @param tasks TaskList */ public void readList(TaskList tasks) { + showLine(); System.out.println("Here are the tasks in your list: "); System.out.println(tasks); showLine(); @@ -105,6 +108,7 @@ public String readCommand() { * @param errorMsg DukeException e.getMessage() */ public void showError(String errorMsg) { + showLine(); System.out.println("Your error message: " + errorMsg); showLine(); } @@ -114,6 +118,7 @@ public void showError(String errorMsg) { * Calls by run method in Duke class. */ public void showLoadingError() { + showLine(); System.out.println("Loading Error!"); showLine(); } @@ -124,6 +129,7 @@ public void showLoadingError() { * @param tasks TaskList of tasks with keyword. */ public void showMatches(TaskList tasks) { + showLine(); System.out.println("Here are the matching task(s) in your list: "); System.out.println(tasks); showLine(); From 3c9c9dcc8d3a994e060ff1094c49593545c20c1a Mon Sep 17 00:00:00 2001 From: Yan Ru Date: Sun, 31 Oct 2021 17:09:23 +0800 Subject: [PATCH 19/26] Level 10 - Add two images (missed out in previous commit) --- src/main/resources/images/AdminDUKE.png | Bin 0 -> 24377 bytes src/main/resources/images/UserYAN.png | Bin 0 -> 22293 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/main/resources/images/AdminDUKE.png create mode 100644 src/main/resources/images/UserYAN.png diff --git a/src/main/resources/images/AdminDUKE.png b/src/main/resources/images/AdminDUKE.png new file mode 100644 index 0000000000000000000000000000000000000000..337b996f0ebea76a4437a27403cce66b6421344a GIT binary patch literal 24377 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 At^fc4 literal 0 HcmV?d00001 From c2c7ba92588a0552ab4a498369b38913af3615fc Mon Sep 17 00:00:00 2001 From: Yan Ru Date: Sun, 31 Oct 2021 18:26:01 +0800 Subject: [PATCH 20/26] Level 10.1 - Rebuilding the Scene using FXML --- config/checkstyle/checkstyle.xml | 255 ++++++++++++++++++++++++ src/main/java/DialogBox.java | 60 +++--- src/main/java/Duke.java | 21 +- src/main/java/Launcher.java | 2 +- src/main/java/Main.java | 29 +++ src/main/java/MainWindow.java | 50 +++++ src/main/resources/view/DialogBox.fxml | 16 ++ src/main/resources/view/MainWindow.fxml | 19 ++ 8 files changed, 416 insertions(+), 36 deletions(-) create mode 100644 config/checkstyle/checkstyle.xml create mode 100644 src/main/java/Main.java create mode 100644 src/main/java/MainWindow.java create mode 100644 src/main/resources/view/DialogBox.fxml create mode 100644 src/main/resources/view/MainWindow.fxml diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml new file mode 100644 index 00000000..748b3053 --- /dev/null +++ b/config/checkstyle/checkstyle.xml @@ -0,0 +1,255 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/java/DialogBox.java b/src/main/java/DialogBox.java index 27206458..3cd9a4bf 100644 --- a/src/main/java/DialogBox.java +++ b/src/main/java/DialogBox.java @@ -1,49 +1,61 @@ +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; -import javafx.scene.layout.Priority; -import javafx.scene.layout.Region; +/** + * An example of a custom control using FXML. + * This control represents a dialog box consisting of an ImageView to represent the speaker's face and a label + * containing text from the speaker. + */ public class DialogBox extends HBox { - - private Label text; + @FXML + private Label dialog; + @FXML private ImageView displayPicture; - /** - * Controls the layout of the UI. - */ - public DialogBox(Label l, ImageView iv) { - text = l; - displayPicture = iv; - text.setWrapText(true); - displayPicture.setFitWidth(100.0); - displayPicture.setFitHeight(100.0); - Region region = new Region(); - HBox.setHgrow(region, Priority.ALWAYS); - this.setAlignment(Pos.TOP_RIGHT); - this.getChildren().addAll(text, region, displayPicture); + private DialogBox(String text, Image img) { + try { + FXMLLoader fxmlLoader = new FXMLLoader(MainWindow.class.getResource("/view/DialogBox.fxml")); + fxmlLoader.setController(this); + fxmlLoader.setRoot(this); + fxmlLoader.load(); + } catch (IOException e) { + e.printStackTrace(); + } + + dialog.setText(text); + displayPicture.setImage(img); } /** * Flips the dialog box such that the ImageView is on the left and text on the right. */ private void flip() { - this.setAlignment(Pos.TOP_LEFT); ObservableList tmp = FXCollections.observableArrayList(this.getChildren()); - FXCollections.reverse(tmp); - this.getChildren().setAll(tmp); + Collections.reverse(tmp); + getChildren().setAll(tmp); + setAlignment(Pos.TOP_LEFT); } - public static DialogBox getUserDialog(Label l, ImageView iv) { - return new DialogBox(l, iv); + public static DialogBox getUserDialog(String text, Image img) { + return new DialogBox(text, img); } - public static DialogBox getDukeDialog(Label l, ImageView iv) { - var db = new DialogBox(l, iv); + /** + * To get the dialog box. + */ + 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.java b/src/main/java/Duke.java index 7a1c415b..87db30d8 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -138,16 +138,16 @@ public void start(Stage stage) { //Scroll down to the end every time dialogContainer's height changes. dialogContainer.heightProperty().addListener((observable) -> scrollPane.setVvalue(1.0)); - Label welcomeText = displayWelcome(); + String welcomeText = displayWelcome(); dialogContainer.getChildren().add( - DialogBox.getUserDialog(welcomeText, new ImageView(duke))); + DialogBox.getUserDialog(welcomeText, duke)); } - private Label displayWelcome() { + private String displayWelcome() { ByteArrayOutputStream outContent = new ByteArrayOutputStream(); System.setOut(new PrintStream(outContent)); ui.showWelcome(); - return new Label(outContent.toString()); + return outContent.toString(); } /** @@ -170,15 +170,14 @@ private Label getDialogLabel(String text) { * the dialog container. Clears the user input after processing. */ private void handleUserInput() { - Label userText = new Label(userInput.getText()); - String response = getResponse(userInput.getText()); - Label dukeText = new Label(response); + String userText = userInput.getText(); + String dukeText = getResponse(userInput.getText()); dialogContainer.getChildren().addAll( - DialogBox.getUserDialog(userText, new ImageView(user)), - DialogBox.getDukeDialog(dukeText, new ImageView(duke)) + DialogBox.getUserDialog(userText, user), + DialogBox.getDukeDialog(dukeText, duke) ); userInput.clear(); - if (response.replace(" ","").substring(0,3).equals("Bye")) { + if (dukeText.replace(" ","").substring(0,3).equals("Bye")) { PauseTransition pause = new PauseTransition(Duration.seconds(1)); pause.setOnFinished(event -> Platform.exit()); pause.play(); @@ -190,7 +189,7 @@ private void handleUserInput() { * You should have your own function to generate a response to user input. * Replace this stub with your completed method. */ - private String getResponse(String input) { + String getResponse(String input) { try { Command c = Parser.parse(input); ByteArrayOutputStream outContent = new ByteArrayOutputStream(); diff --git a/src/main/java/Launcher.java b/src/main/java/Launcher.java index 0f6b5cb6..b572e961 100644 --- a/src/main/java/Launcher.java +++ b/src/main/java/Launcher.java @@ -7,6 +7,6 @@ public class Launcher { public static void main(String[] args) { - Application.launch(Duke.class, args); + Application.launch(Main.class, args); } } \ No newline at end of file diff --git a/src/main/java/Main.java b/src/main/java/Main.java new file mode 100644 index 00000000..318bd74b --- /dev/null +++ b/src/main/java/Main.java @@ -0,0 +1,29 @@ +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/MainWindow.java b/src/main/java/MainWindow.java new file mode 100644 index 00000000..38730270 --- /dev/null +++ b/src/main/java/MainWindow.java @@ -0,0 +1,50 @@ +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/UserYAN.png")); + private Image dukeImage = new Image(this.getClass().getResourceAsStream("/images/AdminDUKE.png")); + + @FXML + public void initialize() { + scrollPane.vvalueProperty().bind(dialogContainer.heightProperty()); + } + + public void setDuke(Duke d) { + duke = d; + } + + /** + * Creates two dialog boxes, one echoing user input and the other containing Duke's reply and then appends them to + * the dialog container. Clears the user input after processing. + */ + @FXML + private void handleUserInput() { + String input = userInput.getText(); + String response = duke.getResponse(input); + dialogContainer.getChildren().addAll( + DialogBox.getUserDialog(input, userImage), + DialogBox.getDukeDialog(response, dukeImage) + ); + userInput.clear(); + } +} \ No newline at end of file diff --git a/src/main/resources/view/DialogBox.fxml b/src/main/resources/view/DialogBox.fxml new file mode 100644 index 00000000..c9521bd5 --- /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 00000000..6f988e75 --- /dev/null +++ b/src/main/resources/view/MainWindow.fxml @@ -0,0 +1,19 @@ + + + + + + + + + + + +