From 3b19ba1790d6035a9e246f5d4ae627bf68778606 Mon Sep 17 00:00:00 2001 From: damithc Date: Mon, 25 May 2020 00:58:18 +0800 Subject: [PATCH 01/44] Add Gradle support --- build.gradle | 46 ++++++ gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 58695 bytes gradle/wrapper/gradle-wrapper.properties | 5 + gradlew | 183 +++++++++++++++++++++++ gradlew.bat | 103 +++++++++++++ text-ui-test/runtest.sh | 0 6 files changed, 337 insertions(+) create mode 100644 build.gradle create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat mode change 100644 => 100755 text-ui-test/runtest.sh diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000000..b0c5528fb5 --- /dev/null +++ b/build.gradle @@ -0,0 +1,46 @@ +plugins { + id 'java' + id 'application' + id 'checkstyle' + id 'com.github.johnrengelman.shadow' version '5.1.0' +} + +repositories { + mavenCentral() +} + +dependencies { + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.5.0' + testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.5.0' +} + +test { + useJUnitPlatform() + + testLogging { + events "passed", "skipped", "failed" + + showExceptions true + exceptionFormat "full" + showCauses true + showStackTraces true + showStandardStreams = false + } +} + +application { + mainClassName = "seedu.duke.Duke" +} + +shadowJar { + archiveBaseName = "duke" + archiveClassifier = null +} + +checkstyle { + toolVersion = '8.23' +} + +run{ + standardInput = System.in +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..f3d88b1c2faf2fc91d853cd5d4242b5547257070 GIT binary patch literal 58695 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 literal 0 HcmV?d00001 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000..b7c8c5dbf5 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 0000000000..2fe81a7d95 --- /dev/null +++ b/gradlew @@ -0,0 +1,183 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000000..62bd9b9cce --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,103 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/text-ui-test/runtest.sh b/text-ui-test/runtest.sh old mode 100644 new mode 100755 From 075f321aa9c335fd9599e95b23fd2aa35232f626 Mon Sep 17 00:00:00 2001 From: Javier Date: Wed, 19 Aug 2020 21:20:54 +0800 Subject: [PATCH 02/44] added new greeting, Duke can now echo, Duke can now exit --- src/main/java/Duke.java | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 5d313334cc..d4b4e60d85 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -1,10 +1,42 @@ +import java.util.Scanner; + public class Duke { public static void main(String[] args) { - String logo = " ____ _ \n" + greet(); + run(); + } + + public static void greet(){ + String logo = "____________________________________________________________\n" + /*+ " ____ _ \n" + "| _ \\ _ _| | _____ \n" + "| | | | | | | |/ / _ \\\n" + "| |_| | |_| | < __/\n" - + "|____/ \\__,_|_|\\_\\___|\n"; - System.out.println("Hello from\n" + logo); + + "|____/ \\__,_|_|\\_\\___|\n"*/ + + " Hello I'm Duke \n" + + " What can I do for you?\n" + + "____________________________________________________________\n"; + System.out.println(logo); + } + + public static void run(){ + boolean isRunning = true; + Scanner sc = new Scanner(System.in); + String input; + String line = "____________________________________________________________\n"; + while(isRunning){ + input = sc.nextLine(); + if(input.equals("bye")){ + System.out.println(line); + System.out.println("Bye. Hope to see you again soon!"); + System.out.println(line); + isRunning = false; + } + else { + System.out.println(line); + System.out.println(input); + System.out.println(line); + } + } } } From 2f0c6e3e5d8c48713840e5edde34a6225b1a8dce Mon Sep 17 00:00:00 2001 From: Javier Date: Wed, 19 Aug 2020 21:46:15 +0800 Subject: [PATCH 03/44] level 2: add and list operation added --- src/main/java/Duke.java | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index d4b4e60d85..fb8f8093cb 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -1,3 +1,4 @@ +import java.util.ArrayList; import java.util.Scanner; public class Duke { @@ -20,21 +21,30 @@ public static void greet(){ } public static void run(){ - boolean isRunning = true; Scanner sc = new Scanner(System.in); + boolean isRunning = true; String input; String line = "____________________________________________________________\n"; + ArrayList list = new ArrayList<>(); while(isRunning){ input = sc.nextLine(); - if(input.equals("bye")){ + if(input.equals("list")){ + System.out.println(line); + for(int i = 1; i <= list.size(); i++){ + System.out.println(i + ". " + list.get(i-1)); + } + System.out.println(line); + } + else if(input.equals("bye")){ System.out.println(line); System.out.println("Bye. Hope to see you again soon!"); System.out.println(line); isRunning = false; } - else { + else{ + list.add(input); System.out.println(line); - System.out.println(input); + System.out.println("added: " + input); System.out.println(line); } } From 879a4b297f7b13040bba82b7f483ec71f5c0b08a Mon Sep 17 00:00:00 2001 From: Javier Date: Thu, 20 Aug 2020 12:36:34 +0800 Subject: [PATCH 04/44] mark as done. Incomplete todo, deadline and event operations --- src/main/java/Duke.java | 50 ++++++++++++++++++++++++++++++++----- src/main/java/Task.java | 43 +++++++++++++++++++++++++++++++ src/main/java/TaskType.java | 5 ++++ 3 files changed, 92 insertions(+), 6 deletions(-) create mode 100644 src/main/java/Task.java create mode 100644 src/main/java/TaskType.java diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index fb8f8093cb..db89b655ea 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -25,13 +25,17 @@ public static void run(){ boolean isRunning = true; String input; String line = "____________________________________________________________\n"; - ArrayList list = new ArrayList<>(); + ArrayList list = new ArrayList<>(); + Task task; + int num; while(isRunning){ - input = sc.nextLine(); + input = sc.next(); if(input.equals("list")){ System.out.println(line); + System.out.println("Here are the tasks in your list:"); for(int i = 1; i <= list.size(); i++){ - System.out.println(i + ". " + list.get(i-1)); + task = list.get(i-1); + System.out.println(i + "." + task.getTypeString() + task.getDoneString() + " " + task.getString()); } System.out.println(line); } @@ -41,10 +45,44 @@ else if(input.equals("bye")){ System.out.println(line); isRunning = false; } - else{ - list.add(input); + else if(input.equals("done")){ + num = sc.nextInt(); + task = list.get(num-1).done(); + list.add(num-1, task); //update task in list to done System.out.println(line); - System.out.println("added: " + input); + System.out.println("Nice! I've marked this task as done: "); + System.out.println(" " + task.getDoneString() + " " + task.getString()); + System.out.println(line); + + } + else if(input.equals("todo")){ + input = sc.nextLine(); + task = new Task(TaskType.TODO,false, input); + list.add(task); + System.out.println(line); + System.out.println("Got it. I've added this task: "); + System.out.println(" " + task.getTypeString() + task.getDoneString() + " " + input); + System.out.println("Now you have " + list.size() +" tasks in the list."); + System.out.println(line); + } + else if(input.equals("deadline")) { + input = sc.nextLine(); + task = new Task(TaskType.DEADLINE,false, input); + list.add(task); + System.out.println(line); + System.out.println("Got it. I've added this task: "); + System.out.println(" " + task.getTypeString() + task.getDoneString() + " " + input); + System.out.println("Now you have " + list.size() +" tasks in the list."); + System.out.println(line); + } + else if(input.equals("event")) { + input = sc.nextLine(); + task = new Task(TaskType.EVENT,false, input); + list.add(task); + System.out.println(line); + System.out.println("Got it. I've added this task: "); + System.out.println(" " + task.getTypeString() + task.getDoneString() + " " + input); + System.out.println("Now you have " + list.size() +" tasks in the list."); System.out.println(line); } } diff --git a/src/main/java/Task.java b/src/main/java/Task.java new file mode 100644 index 0000000000..5381878d8d --- /dev/null +++ b/src/main/java/Task.java @@ -0,0 +1,43 @@ +public class Task{ + TaskType taskType; + boolean isDone; + String string; + + public Task(TaskType taskType, boolean isDone, String string){ + this.taskType = taskType; + this.isDone = isDone; + this.string = string; + } + + public String getString() { + return string; + } + public String getDoneString(){ + String string; + if(isDone){ + string = "[✓]"; + } + else{ + string = "[✗]"; + } + return string; + } + + public Task done(){ + return new Task(taskType, true, string); + } + + public String getTypeString(){ + String string; + if(taskType.equals(TaskType.TODO)){ + string = "[T]"; + } + else if(taskType.equals(TaskType.DEADLINE)){ + string = "[D]"; + } + else{ + string = "[E]"; + } + return string; + } +} \ No newline at end of file diff --git a/src/main/java/TaskType.java b/src/main/java/TaskType.java new file mode 100644 index 0000000000..7eb9facb39 --- /dev/null +++ b/src/main/java/TaskType.java @@ -0,0 +1,5 @@ +public enum TaskType{ + TODO, + DEADLINE, + EVENT; +} \ No newline at end of file From 94fffe0fe7dceb39535671e0a342031929bf7391 Mon Sep 17 00:00:00 2001 From: Javier Date: Thu, 20 Aug 2020 13:58:22 +0800 Subject: [PATCH 05/44] completed todo, event, deadline operation --- src/main/java/Duke.java | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index db89b655ea..e5e330c46b 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -25,6 +25,7 @@ public static void run(){ boolean isRunning = true; String input; String line = "____________________________________________________________\n"; + String[] strings; ArrayList list = new ArrayList<>(); Task task; int num; @@ -35,7 +36,7 @@ public static void run(){ System.out.println("Here are the tasks in your list:"); for(int i = 1; i <= list.size(); i++){ task = list.get(i-1); - System.out.println(i + "." + task.getTypeString() + task.getDoneString() + " " + task.getString()); + System.out.println(i + "." + task.getTypeString() + task.getDoneString() + task.getString()); } System.out.println(line); } @@ -51,7 +52,7 @@ else if(input.equals("done")){ list.add(num-1, task); //update task in list to done System.out.println(line); System.out.println("Nice! I've marked this task as done: "); - System.out.println(" " + task.getDoneString() + " " + task.getString()); + System.out.println(" " + task.getDoneString() + task.getString()); System.out.println(line); } @@ -61,27 +62,35 @@ else if(input.equals("todo")){ list.add(task); System.out.println(line); System.out.println("Got it. I've added this task: "); - System.out.println(" " + task.getTypeString() + task.getDoneString() + " " + input); + System.out.println(" " + task.getTypeString() + task.getDoneString() + input); System.out.println("Now you have " + list.size() +" tasks in the list."); System.out.println(line); } else if(input.equals("deadline")) { input = sc.nextLine(); + strings = input.split("/by"); + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(strings[0]).append("(by:").append(strings[1]).append(")"); + input = stringBuilder.toString(); task = new Task(TaskType.DEADLINE,false, input); list.add(task); System.out.println(line); System.out.println("Got it. I've added this task: "); - System.out.println(" " + task.getTypeString() + task.getDoneString() + " " + input); + System.out.println(" " + task.getTypeString() + task.getDoneString() + input); System.out.println("Now you have " + list.size() +" tasks in the list."); System.out.println(line); } else if(input.equals("event")) { input = sc.nextLine(); + strings = input.split("/at"); + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(strings[0]).append("(at:").append(strings[1]).append(")"); + input = stringBuilder.toString(); task = new Task(TaskType.EVENT,false, input); list.add(task); System.out.println(line); System.out.println("Got it. I've added this task: "); - System.out.println(" " + task.getTypeString() + task.getDoneString() + " " + input); + System.out.println(" " + task.getTypeString() + task.getDoneString() + input); System.out.println("Now you have " + list.size() +" tasks in the list."); System.out.println(line); } From aaf6c022c3e6c4ec8117fa0f7b98999a77ce51f3 Mon Sep 17 00:00:00 2001 From: Javier Date: Thu, 20 Aug 2020 16:47:13 +0800 Subject: [PATCH 06/44] placed Task and TaskType into Duke class for automated testing --- src/main/java/Duke.java | 50 ++++++++++++++++++++++++++++++++++++- src/main/java/Task.java | 43 ------------------------------- src/main/java/TaskType.java | 5 ---- text-ui-test/EXPECTED.TXT | 15 ++++++----- text-ui-test/input.txt | 1 + 5 files changed, 59 insertions(+), 55 deletions(-) delete mode 100644 src/main/java/Task.java delete mode 100644 src/main/java/TaskType.java diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index e5e330c46b..cb9e9b7720 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -14,7 +14,7 @@ public static void greet(){ + "| | | | | | | |/ / _ \\\n" + "| |_| | |_| | < __/\n" + "|____/ \\__,_|_|\\_\\___|\n"*/ - + " Hello I'm Duke \n" + + " Hello I'm Duke\n" + " What can I do for you?\n" + "____________________________________________________________\n"; System.out.println(logo); @@ -97,3 +97,51 @@ else if(input.equals("event")) { } } } +class Task{ + TaskType taskType; + boolean isDone; + String string; + + public Task(TaskType taskType, boolean isDone, String string){ + this.taskType = taskType; + this.isDone = isDone; + this.string = string; + } + + public String getString() { + return string; + } + public String getDoneString(){ + String string; + if(isDone){ + string = "[✓]"; + } + else{ + string = "[✗]"; + } + return string; + } + + public Task done(){ + return new Task(taskType, true, string); + } + + public String getTypeString(){ + String string; + if(taskType.equals(TaskType.TODO)){ + string = "[T]"; + } + else if(taskType.equals(TaskType.DEADLINE)){ + string = "[D]"; + } + else{ + string = "[E]"; + } + return string; + } +} +enum TaskType{ + TODO, + DEADLINE, + EVENT; +} \ No newline at end of file diff --git a/src/main/java/Task.java b/src/main/java/Task.java deleted file mode 100644 index 5381878d8d..0000000000 --- a/src/main/java/Task.java +++ /dev/null @@ -1,43 +0,0 @@ -public class Task{ - TaskType taskType; - boolean isDone; - String string; - - public Task(TaskType taskType, boolean isDone, String string){ - this.taskType = taskType; - this.isDone = isDone; - this.string = string; - } - - public String getString() { - return string; - } - public String getDoneString(){ - String string; - if(isDone){ - string = "[✓]"; - } - else{ - string = "[✗]"; - } - return string; - } - - public Task done(){ - return new Task(taskType, true, string); - } - - public String getTypeString(){ - String string; - if(taskType.equals(TaskType.TODO)){ - string = "[T]"; - } - else if(taskType.equals(TaskType.DEADLINE)){ - string = "[D]"; - } - else{ - string = "[E]"; - } - return string; - } -} \ No newline at end of file diff --git a/src/main/java/TaskType.java b/src/main/java/TaskType.java deleted file mode 100644 index 7eb9facb39..0000000000 --- a/src/main/java/TaskType.java +++ /dev/null @@ -1,5 +0,0 @@ -public enum TaskType{ - TODO, - DEADLINE, - EVENT; -} \ No newline at end of file diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 657e74f6e7..e7876fe34a 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,7 +1,10 @@ -Hello from - ____ _ -| _ \ _ _| | _____ -| | | | | | | |/ / _ \ -| |_| | |_| | < __/ -|____/ \__,_|_|\_\___| +____________________________________________________________ + Hello I'm Duke + What can I do for you? +____________________________________________________________ + +____________________________________________________________ + +Bye. Hope to see you again soon! +____________________________________________________________ diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index e69de29bb2..0abaeaa993 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -0,0 +1 @@ +bye \ No newline at end of file From 87bcf40e67d3ef3a517a36b76c8606b4f7c328d5 Mon Sep 17 00:00:00 2001 From: Javier Date: Thu, 20 Aug 2020 17:32:31 +0800 Subject: [PATCH 07/44] error handling added --- src/main/java/Duke.java | 148 ++++++++++++++++++++++++++++------------ 1 file changed, 106 insertions(+), 42 deletions(-) diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index cb9e9b7720..0f23378f46 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -24,77 +24,141 @@ public static void run(){ Scanner sc = new Scanner(System.in); boolean isRunning = true; String input; - String line = "____________________________________________________________\n"; String[] strings; ArrayList list = new ArrayList<>(); Task task; - int num; + int num, index; while(isRunning){ - input = sc.next(); + input = sc.nextLine(); + if(input.equals("list")){ - System.out.println(line); - System.out.println("Here are the tasks in your list:"); - for(int i = 1; i <= list.size(); i++){ - task = list.get(i-1); - System.out.println(i + "." + task.getTypeString() + task.getDoneString() + task.getString()); - } - System.out.println(line); + list(list); } else if(input.equals("bye")){ - System.out.println(line); - System.out.println("Bye. Hope to see you again soon!"); - System.out.println(line); - isRunning = false; + isRunning = bye(); } - else if(input.equals("done")){ + else if(getWord(input).equals("done")){ //incomplete num = sc.nextInt(); task = list.get(num-1).done(); list.add(num-1, task); //update task in list to done - System.out.println(line); + printLine(); System.out.println("Nice! I've marked this task as done: "); System.out.println(" " + task.getDoneString() + task.getString()); - System.out.println(line); + printLine(); } - else if(input.equals("todo")){ - input = sc.nextLine(); - task = new Task(TaskType.TODO,false, input); - list.add(task); - System.out.println(line); - System.out.println("Got it. I've added this task: "); - System.out.println(" " + task.getTypeString() + task.getDoneString() + input); - System.out.println("Now you have " + list.size() +" tasks in the list."); - System.out.println(line); + else if(getWord(input).equals("todo")){ + todo(input, list); } - else if(input.equals("deadline")) { - input = sc.nextLine(); - strings = input.split("/by"); + else if(getWord(input).equals("deadline")) { + deadline(input, list); + } + else if(getWord(input).equals("event")) { + event(input, list); + } + else{ + error(); + } + } + } + + public static String getWord(String string){ + + int firstSpaceIndex = string.indexOf(' '); + if(firstSpaceIndex == -1){ + return string; + } + return string.substring(0, firstSpaceIndex); + } + + public static void list(ArrayList list){ + printLine(); + Task task; + System.out.println("Here are the tasks in your list:"); + for(int i = 1; i <= list.size(); i++){ + task = list.get(i-1); + System.out.println(i + "." + task.getTypeString() + task.getDoneString() + task.getString()); + } + printLine(); + } + + public static boolean bye(){ + printLine(); + System.out.println("Bye. Hope to see you again soon!"); + printLine(); + return false; + } + + public static void todo(String input, ArrayList list){ + printLine(); + input = input.substring(4); + if(input.isEmpty()){ + System.out.println("☹ OOPS!!! The description of a todo cannot be empty."); + } + else { + Task task = new Task(TaskType.TODO, false, input); + list.add(task); + System.out.println("Got it. I've added this task: "); + System.out.println(" " + task.getTypeString() + task.getDoneString() + input); + System.out.println("Now you have " + list.size() + " tasks in the list."); + } + printLine(); + } + + public static void deadline(String input, ArrayList list){ + input = input.substring(8); + printLine(); + if(input.isEmpty()){ + System.out.println("☹ OOPS!!! The description of a deadline cannot be empty."); + }else { + int index = input.indexOf("/by"); + if (index == -1) { + System.out.println("☹ OOPS!!! The description of a deadline must have a indicated deadline."); + } else { StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append(strings[0]).append("(by:").append(strings[1]).append(")"); + stringBuilder.append(input.substring(0, index)).append("(by:").append(input.substring(index + 3)).append(")"); input = stringBuilder.toString(); - task = new Task(TaskType.DEADLINE,false, input); + Task task = new Task(TaskType.DEADLINE, false, input); list.add(task); - System.out.println(line); System.out.println("Got it. I've added this task: "); System.out.println(" " + task.getTypeString() + task.getDoneString() + input); - System.out.println("Now you have " + list.size() +" tasks in the list."); - System.out.println(line); + System.out.println("Now you have " + list.size() + " tasks in the list."); } - else if(input.equals("event")) { - input = sc.nextLine(); - strings = input.split("/at"); + } + printLine(); + } + + public static void event(String input, ArrayList list){ + input = input.substring(5); + printLine(); + if(input.isEmpty()){ + System.out.println("☹ OOPS!!! The description of a event cannot be empty."); + }else { + int index = input.indexOf("/at"); + if (index == -1) { + System.out.println("☹ OOPS!!! The description of a event must have a indicated deadline."); + } else { StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append(strings[0]).append("(at:").append(strings[1]).append(")"); + stringBuilder.append(input.substring(0, index)).append("(at:").append(input.substring(index + 3)).append(")"); input = stringBuilder.toString(); - task = new Task(TaskType.EVENT,false, input); + Task task = new Task(TaskType.DEADLINE, false, input); list.add(task); - System.out.println(line); System.out.println("Got it. I've added this task: "); System.out.println(" " + task.getTypeString() + task.getDoneString() + input); - System.out.println("Now you have " + list.size() +" tasks in the list."); - System.out.println(line); + System.out.println("Now you have " + list.size() + " tasks in the list."); } } + printLine(); + } + + public static void printLine(){ + System.out.println("____________________________________________________________\n"); + } + + public static void error(){ + printLine(); + System.out.println("☹ OOPS!!! I'm sorry, but I don't know what that means :-("); + printLine(); } } class Task{ From 1b1cdac2d0d1e5470d698844f68951593b35433f Mon Sep 17 00:00:00 2001 From: Javier Date: Thu, 20 Aug 2020 18:12:47 +0800 Subject: [PATCH 08/44] done is updated. delete method is added. Main method cleaned up. --- src/main/java/Duke.java | 84 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 76 insertions(+), 8 deletions(-) diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 0f23378f46..0da4ab6e3e 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -38,13 +38,7 @@ else if(input.equals("bye")){ isRunning = bye(); } else if(getWord(input).equals("done")){ //incomplete - num = sc.nextInt(); - task = list.get(num-1).done(); - list.add(num-1, task); //update task in list to done - printLine(); - System.out.println("Nice! I've marked this task as done: "); - System.out.println(" " + task.getDoneString() + task.getString()); - printLine(); + done(input, list); } else if(getWord(input).equals("todo")){ @@ -56,6 +50,9 @@ else if(getWord(input).equals("deadline")) { else if(getWord(input).equals("event")) { event(input, list); } + else if(getWord(input).equals("delete")) { + delete(input, list); + } else{ error(); } @@ -136,7 +133,7 @@ public static void event(String input, ArrayList list){ }else { int index = input.indexOf("/at"); if (index == -1) { - System.out.println("☹ OOPS!!! The description of a event must have a indicated deadline."); + System.out.println("☹ OOPS!!! The description of an event must have a indicated deadline."); } else { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append(input.substring(0, index)).append("(at:").append(input.substring(index + 3)).append(")"); @@ -151,6 +148,73 @@ public static void event(String input, ArrayList list){ printLine(); } + public static void done(String input, ArrayList list){ + input = input.substring(4); + printLine(); + if(input.isEmpty()){ + System.out.println("☹ OOPS!!! Please indicate which task is done."); + }else{ + input = input.substring(1); + boolean isInteger = true; + for(int i = 0; i < input.length(); i++){ + if(!Character.isDigit(input.charAt(i))){ + isInteger = false; + break; + } + } + if(isInteger){ + int tasknumber = Integer.parseInt(input); + if( (tasknumber == 0) ||(tasknumber > list.size()) ){ + System.out.println("☹ OOPS!!! Task number is not found in the list."); + } + else{ + Task task = list.remove(tasknumber - 1); + Task newTask = new Task(task.taskType, true, task.toString()); + list.add(tasknumber-1, newTask); + System.out.println("Nice! I've marked this task as done: "); + System.out.println(" " + newTask.getTypeString() + newTask.getDoneString() + newTask.toString()); + } + } + else{ + System.out.println("☹ OOPS!!! Incorrect entry for finished task."); + } + } + printLine(); + } + + public static void delete(String input, ArrayList list){ + input = input.substring(6); + printLine(); + if(input.isEmpty()){ + System.out.println("☹ OOPS!!! Please indicate which task is done."); + }else{ + input = input.substring(1); + boolean isInteger = true; + for(int i = 0; i < input.length(); i++){ + if(!Character.isDigit(input.charAt(i))){ + isInteger = false; + break; + } + } + if(isInteger){ + int tasknumber = Integer.parseInt(input); + if( (tasknumber == 0) ||(tasknumber > list.size()) ){ + System.out.println("☹ OOPS!!! Task number is not found in the list."); + } + else{ + Task task = list.remove(tasknumber - 1); + System.out.println("Noted. I've removed this task:"); + System.out.println(" " + task.getTypeString() + task.getDoneString() + task.toString()); + System.out.println("Now you have " + list.size() + " tasks in the list."); + } + } + else{ + System.out.println("☹ OOPS!!! Incorrect entry for finished task."); + } + } + printLine(); + } + public static void printLine(){ System.out.println("____________________________________________________________\n"); } @@ -203,6 +267,10 @@ else if(taskType.equals(TaskType.DEADLINE)){ } return string; } + + public String toString(){ + return string; + } } enum TaskType{ TODO, From e800b235de2c8876f45bf9ba72c1d7cd45f39a6e Mon Sep 17 00:00:00 2001 From: Javier Date: Thu, 20 Aug 2020 18:34:18 +0800 Subject: [PATCH 09/44] minor edit --- src/main/java/Duke.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 0da4ab6e3e..939796fc89 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -37,7 +37,7 @@ public static void run(){ else if(input.equals("bye")){ isRunning = bye(); } - else if(getWord(input).equals("done")){ //incomplete + else if(getWord(input).equals("done")){ done(input, list); } From 034305a00bfce2776cea3403a9dd26e5fca17406 Mon Sep 17 00:00:00 2001 From: Javier Date: Thu, 20 Aug 2020 18:34:18 +0800 Subject: [PATCH 10/44] Remove incorrect notes Incomplete note on some methods were removed --- src/main/java/Duke.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 0da4ab6e3e..939796fc89 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -37,7 +37,7 @@ public static void run(){ else if(input.equals("bye")){ isRunning = bye(); } - else if(getWord(input).equals("done")){ //incomplete + else if(getWord(input).equals("done")){ done(input, list); } From a33904d7c0bb87907af43b025f06baca43ec6d0e Mon Sep 17 00:00:00 2001 From: Javier Date: Fri, 28 Aug 2020 01:28:16 +0800 Subject: [PATCH 11/44] Add save method Save method puts the list of tasks into a text file. This allows the client to access tasks even if Duke has been closed. The path of the text file can be found in the Duke.java file as a global variable at the top of the file. --- data/tasks.txt | 2 + src/main/java/Duke.java | 92 +++++++++++++++++++++++++++++------------ 2 files changed, 68 insertions(+), 26 deletions(-) create mode 100644 data/tasks.txt diff --git a/data/tasks.txt b/data/tasks.txt new file mode 100644 index 0000000000..b78725c8d1 --- /dev/null +++ b/data/tasks.txt @@ -0,0 +1,2 @@ +[T][✗] borrow book +[T][✗] save time diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 939796fc89..b00a8bd22f 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -1,13 +1,22 @@ +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; import java.util.ArrayList; import java.util.Scanner; public class Duke { + private static String TASKS_PATHNAME = "data/tasks.txt"; + public static void main(String[] args) { greet(); - run(); + try { + run(); + } catch(IOException e) { + System.err.println(e); + } } - public static void greet(){ + public static void greet() { String logo = "____________________________________________________________\n" /*+ " ____ _ \n" + "| _ \\ _ _| | _____ \n" @@ -20,17 +29,47 @@ public static void greet(){ System.out.println(logo); } - public static void run(){ - Scanner sc = new Scanner(System.in); + public static void run() throws IOException, SecurityException { + File tasks = new File(TASKS_PATHNAME); + + if(tasks.getParentFile() != null){ + tasks.getParentFile().mkdirs(); + } + tasks.createNewFile(); + + Scanner sc = new Scanner(tasks); + ArrayList list = new ArrayList<>(); + String task, taskString; + TaskType taskType; + boolean isDone; + while(sc.hasNext()) { + task = sc.nextLine(); + if(task.charAt(1) == 'T') { + taskType = TaskType.TODO; + } + else if(task.charAt(1) == 'D') { + taskType = TaskType.DEADLINE; + } + else { + taskType = TaskType.EVENT; + } + + if(task.charAt(4) == '✓') { + isDone = true; + } + else { + isDone = false; + } + taskString = task.substring(6); + list.add(new Task(taskType, isDone, taskString)); + } + + sc = new Scanner(System.in); boolean isRunning = true; String input; - String[] strings; - ArrayList list = new ArrayList<>(); - Task task; - int num, index; - while(isRunning){ + while(isRunning) { input = sc.nextLine(); - + printLine(); if(input.equals("list")){ list(list); } @@ -53,9 +92,13 @@ else if(getWord(input).equals("event")) { else if(getWord(input).equals("delete")) { delete(input, list); } + else if(getWord(input).equals("save")){ + save(list); + } else{ error(); } + printLine(); } } @@ -69,25 +112,20 @@ public static String getWord(String string){ } public static void list(ArrayList list){ - printLine(); Task task; System.out.println("Here are the tasks in your list:"); for(int i = 1; i <= list.size(); i++){ task = list.get(i-1); System.out.println(i + "." + task.getTypeString() + task.getDoneString() + task.getString()); } - printLine(); } public static boolean bye(){ - printLine(); System.out.println("Bye. Hope to see you again soon!"); - printLine(); return false; } public static void todo(String input, ArrayList list){ - printLine(); input = input.substring(4); if(input.isEmpty()){ System.out.println("☹ OOPS!!! The description of a todo cannot be empty."); @@ -99,12 +137,10 @@ public static void todo(String input, ArrayList list){ System.out.println(" " + task.getTypeString() + task.getDoneString() + input); System.out.println("Now you have " + list.size() + " tasks in the list."); } - printLine(); } public static void deadline(String input, ArrayList list){ input = input.substring(8); - printLine(); if(input.isEmpty()){ System.out.println("☹ OOPS!!! The description of a deadline cannot be empty."); }else { @@ -122,12 +158,10 @@ public static void deadline(String input, ArrayList list){ System.out.println("Now you have " + list.size() + " tasks in the list."); } } - printLine(); } public static void event(String input, ArrayList list){ input = input.substring(5); - printLine(); if(input.isEmpty()){ System.out.println("☹ OOPS!!! The description of a event cannot be empty."); }else { @@ -145,12 +179,10 @@ public static void event(String input, ArrayList list){ System.out.println("Now you have " + list.size() + " tasks in the list."); } } - printLine(); } public static void done(String input, ArrayList list){ input = input.substring(4); - printLine(); if(input.isEmpty()){ System.out.println("☹ OOPS!!! Please indicate which task is done."); }else{ @@ -179,12 +211,10 @@ public static void done(String input, ArrayList list){ System.out.println("☹ OOPS!!! Incorrect entry for finished task."); } } - printLine(); } public static void delete(String input, ArrayList list){ input = input.substring(6); - printLine(); if(input.isEmpty()){ System.out.println("☹ OOPS!!! Please indicate which task is done."); }else{ @@ -212,7 +242,6 @@ public static void delete(String input, ArrayList list){ System.out.println("☹ OOPS!!! Incorrect entry for finished task."); } } - printLine(); } public static void printLine(){ @@ -220,9 +249,20 @@ public static void printLine(){ } public static void error(){ - printLine(); System.out.println("☹ OOPS!!! I'm sorry, but I don't know what that means :-("); - printLine(); + } + + public static void save(ArrayList list) throws IOException { + FileWriter fileWriter = new FileWriter(TASKS_PATHNAME); + Task task; + + for(int listIndex = 0; listIndex < list.size(); listIndex++){ + task = list.get(listIndex); + fileWriter.write( task.getTypeString() + task.getDoneString() + task.getString() + System.lineSeparator()); + } + fileWriter.close(); + + System.out.println("Tasks have been saved! "); } } class Task{ From 7dd4badbe3c2e286e87835e0b4388f9e58a9457c Mon Sep 17 00:00:00 2001 From: Javier Date: Fri, 28 Aug 2020 12:37:08 +0800 Subject: [PATCH 12/44] Put classes into package duke --- src/main/java/Duke.java | 57 ++++--------------------------------- src/main/java/Task.java | 49 +++++++++++++++++++++++++++++++ src/main/java/TaskType.java | 7 +++++ 3 files changed, 61 insertions(+), 52 deletions(-) create mode 100644 src/main/java/Task.java create mode 100644 src/main/java/TaskType.java diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index b00a8bd22f..6abaeb89dd 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -1,3 +1,8 @@ +package duke; + +import duke.Task; +import duke.TaskType; + import java.io.File; import java.io.FileWriter; import java.io.IOException; @@ -264,56 +269,4 @@ public static void save(ArrayList list) throws IOException { System.out.println("Tasks have been saved! "); } -} -class Task{ - TaskType taskType; - boolean isDone; - String string; - - public Task(TaskType taskType, boolean isDone, String string){ - this.taskType = taskType; - this.isDone = isDone; - this.string = string; - } - - public String getString() { - return string; - } - public String getDoneString(){ - String string; - if(isDone){ - string = "[✓]"; - } - else{ - string = "[✗]"; - } - return string; - } - - public Task done(){ - return new Task(taskType, true, string); - } - - public String getTypeString(){ - String string; - if(taskType.equals(TaskType.TODO)){ - string = "[T]"; - } - else if(taskType.equals(TaskType.DEADLINE)){ - string = "[D]"; - } - else{ - string = "[E]"; - } - return string; - } - - public String toString(){ - return string; - } -} -enum TaskType{ - TODO, - DEADLINE, - EVENT; } \ No newline at end of file diff --git a/src/main/java/Task.java b/src/main/java/Task.java new file mode 100644 index 0000000000..cccfcffd38 --- /dev/null +++ b/src/main/java/Task.java @@ -0,0 +1,49 @@ +package duke; + +class Task{ + duke.TaskType taskType; + boolean isDone; + String string; + + public Task(duke.TaskType taskType, boolean isDone, String string){ + this.taskType = taskType; + this.isDone = isDone; + this.string = string; + } + + public String getString() { + return string; + } + public String getDoneString(){ + String string; + if(isDone){ + string = "[✓]"; + } + else{ + string = "[✗]"; + } + return string; + } + + public duke.Task done(){ + return new duke.Task(taskType, true, string); + } + + public String getTypeString(){ + String string; + if(taskType.equals(duke.TaskType.TODO)){ + string = "[T]"; + } + else if(taskType.equals(duke.TaskType.DEADLINE)){ + string = "[D]"; + } + else{ + string = "[E]"; + } + return string; + } + + public String toString(){ + return string; + } +} \ No newline at end of file diff --git a/src/main/java/TaskType.java b/src/main/java/TaskType.java new file mode 100644 index 0000000000..801ef69c71 --- /dev/null +++ b/src/main/java/TaskType.java @@ -0,0 +1,7 @@ +package duke; + +enum TaskType{ + TODO, + DEADLINE, + EVENT; +} \ No newline at end of file From bcd63153dfb8716ba801b9aa3bef1c69e2c64378 Mon Sep 17 00:00:00 2001 From: Javier Date: Sat, 29 Aug 2020 17:10:32 +0800 Subject: [PATCH 13/44] Add LocalDateTime attribute to Task class & modified deadline method to allow only a date time description Level 8. Date and Times. The Task class was modified to include a new field of type LocalDateTime. This is to keep the Time and Date for the tasks. Deadline method was also modified to only allow for a date and time description. --- data/tasks.txt | 2 + src/main/java/Duke.java | 155 ++++++++++++++++++++++++---------------- 2 files changed, 96 insertions(+), 61 deletions(-) create mode 100644 data/tasks.txt diff --git a/data/tasks.txt b/data/tasks.txt new file mode 100644 index 0000000000..b78725c8d1 --- /dev/null +++ b/data/tasks.txt @@ -0,0 +1,2 @@ +[T][✗] borrow book +[T][✗] save time diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 939796fc89..2b24f05b3e 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -1,4 +1,10 @@ +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.time.format.FormatStyle; import java.util.ArrayList; +import java.util.Optional; import java.util.Scanner; public class Duke { @@ -7,7 +13,7 @@ public static void main(String[] args) { run(); } - public static void greet(){ + public static void greet() { String logo = "____________________________________________________________\n" /*+ " ____ _ \n" + "| _ \\ _ _| | _____ \n" @@ -20,7 +26,7 @@ public static void greet(){ System.out.println(logo); } - public static void run(){ + public static void run() { Scanner sc = new Scanner(System.in); boolean isRunning = true; String input; @@ -28,106 +34,130 @@ public static void run(){ ArrayList list = new ArrayList<>(); Task task; int num, index; - while(isRunning){ + while (isRunning) { input = sc.nextLine(); - if(input.equals("list")){ + if (input.equals("list")) { + printLine() list(list); - } - else if(input.equals("bye")){ + printLine() + } else if (input.equals("bye")) { + printLine() isRunning = bye(); - } - else if(getWord(input).equals("done")){ + printLine() + } else if (getWord(input).equals("done")) { + printLine() done(input, list); - - } - else if(getWord(input).equals("todo")){ + printLine() + } else if (getWord(input).equals("todo")) { + printLine() todo(input, list); - } - else if(getWord(input).equals("deadline")) { + printLine() + } else if (getWord(input).equals("deadline")) { + printLine(); deadline(input, list); - } - else if(getWord(input).equals("event")) { + printLine(); + } else if (getWord(input).equals("event")) { + printLine() event(input, list); - } - else if(getWord(input).equals("delete")) { + printLine() + } else if (getWord(input).equals("delete")) { + printLine() delete(input, list); - } - else{ + printLine() + } else { + printLine() error(); + printLine() } } } - public static String getWord(String string){ + public static String getWord(String string) { int firstSpaceIndex = string.indexOf(' '); - if(firstSpaceIndex == -1){ + if (firstSpaceIndex == -1) { return string; } return string.substring(0, firstSpaceIndex); } - public static void list(ArrayList list){ - printLine(); + public static void list(ArrayList list) { Task task; System.out.println("Here are the tasks in your list:"); - for(int i = 1; i <= list.size(); i++){ - task = list.get(i-1); + for (int i = 1; i <= list.size(); i++) { + task = list.get(i - 1); System.out.println(i + "." + task.getTypeString() + task.getDoneString() + task.getString()); } - printLine(); } - public static boolean bye(){ - printLine(); + public static boolean bye() { System.out.println("Bye. Hope to see you again soon!"); - printLine(); return false; } - public static void todo(String input, ArrayList list){ - printLine(); + public static void todo(String input, ArrayList list) { input = input.substring(4); - if(input.isEmpty()){ + if (input.isEmpty()) { System.out.println("☹ OOPS!!! The description of a todo cannot be empty."); - } - else { + } else { Task task = new Task(TaskType.TODO, false, input); list.add(task); System.out.println("Got it. I've added this task: "); System.out.println(" " + task.getTypeString() + task.getDoneString() + input); System.out.println("Now you have " + list.size() + " tasks in the list."); } - printLine(); } - public static void deadline(String input, ArrayList list){ + public static void deadline(String input, ArrayList list) { input = input.substring(8); - printLine(); - if(input.isEmpty()){ + if (input.isEmpty()) { System.out.println("☹ OOPS!!! The description of a deadline cannot be empty."); - }else { - int index = input.indexOf("/by"); - if (index == -1) { - System.out.println("☹ OOPS!!! The description of a deadline must have a indicated deadline."); - } else { - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append(input.substring(0, index)).append("(by:").append(input.substring(index + 3)).append(")"); - input = stringBuilder.toString(); - Task task = new Task(TaskType.DEADLINE, false, input); - list.add(task); - System.out.println("Got it. I've added this task: "); - System.out.println(" " + task.getTypeString() + task.getDoneString() + input); - System.out.println("Now you have " + list.size() + " tasks in the list."); + return; + } + int index = input.indexOf("/by"); + if (index == -1) { + System.out.println("☹ OOPS!!! The description of a deadline must have a indicated deadline."); + return; + } + StringBuilder stringBuilder = new StringBuilder(); + String[] strings = new String[1]; + if(input.length() >= index + 4) { + strings = input.substring(index + 4).split("/"); + } + if( (strings.length >= 3) && (strings[2].length() >= 9) ) { + if(strings[0].length() < 2){ + strings[0] = "0" + strings[0]; } + stringBuilder.append(strings[2].substring(0, 4)) + .append("-").append(strings[1]) + .append("-").append(strings[0]) + .append("T") + .append(strings[2].substring(5, 7)) + .append(":") + .append(strings[2].substring(7, 9)); } - printLine(); - } + try{ + LocalDateTime localDateTime = LocalDateTime.parse(stringBuilder.toString(), DateTimeFormatter.ISO_LOCAL_DATE_TIME); + stringBuilder = new StringBuilder(); + stringBuilder.append(input.substring(0, index)) + .append("(by: ") + .append(localDateTime.format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.SHORT))) + .append(input.substring(index + 18)) + .append(")"); + input = stringBuilder.toString(); + Task task = new Task(TaskType.DEADLINE, false, input, Optional.of(localDateTime)); + list.add(task); + System.out.println("Got it. I've added this task: "); + System.out.println(" " + task.getTypeString() + task.getDoneString() + input); + System.out.println("Now you have " + list.size() + " tasks in the list."); + } catch(DateTimeParseException e) { + System.out.println("☹ OOPS!!! The description of a deadline must have a valid date and time. (Format: /by dd/mm/yyyy tttt e.g 2/12/2019 1800"); + } + } public static void event(String input, ArrayList list){ input = input.substring(5); - printLine(); if(input.isEmpty()){ System.out.println("☹ OOPS!!! The description of a event cannot be empty."); }else { @@ -145,12 +175,10 @@ public static void event(String input, ArrayList list){ System.out.println("Now you have " + list.size() + " tasks in the list."); } } - printLine(); } public static void done(String input, ArrayList list){ input = input.substring(4); - printLine(); if(input.isEmpty()){ System.out.println("☹ OOPS!!! Please indicate which task is done."); }else{ @@ -179,12 +207,10 @@ public static void done(String input, ArrayList list){ System.out.println("☹ OOPS!!! Incorrect entry for finished task."); } } - printLine(); } public static void delete(String input, ArrayList list){ input = input.substring(6); - printLine(); if(input.isEmpty()){ System.out.println("☹ OOPS!!! Please indicate which task is done."); }else{ @@ -212,7 +238,6 @@ public static void delete(String input, ArrayList list){ System.out.println("☹ OOPS!!! Incorrect entry for finished task."); } } - printLine(); } public static void printLine(){ @@ -220,20 +245,27 @@ public static void printLine(){ } public static void error(){ - printLine(); System.out.println("☹ OOPS!!! I'm sorry, but I don't know what that means :-("); - printLine(); } + } class Task{ TaskType taskType; boolean isDone; String string; + Optional dateTime; + public Task(TaskType taskType, boolean isDone, String string, Optional date){ + this.taskType = taskType; + this.isDone = isDone; + this.string = string; + this.dateTime = dateTime; + } public Task(TaskType taskType, boolean isDone, String string){ this.taskType = taskType; this.isDone = isDone; this.string = string; + this.dateTime = Optional.empty(); } public String getString() { @@ -272,6 +304,7 @@ public String toString(){ return string; } } + enum TaskType{ TODO, DEADLINE, From d9cdb2961b5a453a221c22115fac717e8329b8f7 Mon Sep 17 00:00:00 2001 From: Javier Date: Mon, 31 Aug 2020 23:36:46 +0800 Subject: [PATCH 14/44] Add Ui class ui class deals with interaction with the user. Ui class takes commands from the user as well as print output to user. --- src/main/java/Duke.java | 39 ++++++++++++++------------------------- src/main/java/Ui.java | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 25 deletions(-) create mode 100644 src/main/java/Ui.java diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 3c2d5d9332..afae4a24dc 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -2,6 +2,7 @@ import duke.Task; import duke.TaskType; +import duke.Ui; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; @@ -16,30 +17,23 @@ public class Duke { private static String TASKS_PATHNAME = "data/tasks.txt"; + private Ui ui; + + public Duke(String filePath) { + ui = new Ui(); + } public static void main(String[] args) { - greet(); try { - run(); + new Duke(TASKS_PATHNAME).run(); } catch(IOException e) { - System.err.println(e); + } } - public static void greet() { - String logo = "____________________________________________________________\n" - /*+ " ____ _ \n" - + "| _ \\ _ _| | _____ \n" - + "| | | | | | | |/ / _ \\\n" - + "| |_| | |_| | < __/\n" - + "|____/ \\__,_|_|\\_\\___|\n"*/ - + " Hello I'm Duke\n" - + " What can I do for you?\n" - + "____________________________________________________________\n"; - System.out.println(logo); - } + public void run() throws IOException, SecurityException { + ui.showWelcome(); - public static void run() throws IOException, SecurityException { File tasks = new File(TASKS_PATHNAME); if(tasks.getParentFile() != null){ tasks.getParentFile().mkdirs(); @@ -81,12 +75,11 @@ else if(task.charAt(1) == 'D') { } } - sc = new Scanner(System.in); boolean isRunning = true; String input; while(isRunning) { - input = sc.nextLine(); - printLine(); + input = ui.readCommand(); + ui.showLine(); if(input.equals("list")){ list(list); } else if (input.equals("bye")) { @@ -106,9 +99,9 @@ else if(getWord(input).equals("save")){ save(list); } else{ - error(); + ui.showError(); } - printLine(); + ui.showLine(); } } @@ -281,10 +274,6 @@ public static void printLine(){ System.out.println("____________________________________________________________\n"); } - public static void error(){ - System.out.println("☹ OOPS!!! I'm sorry, but I don't know what that means :-("); - } - public static void save(ArrayList list) throws IOException { FileWriter fileWriter = new FileWriter(TASKS_PATHNAME); Task task; diff --git a/src/main/java/Ui.java b/src/main/java/Ui.java new file mode 100644 index 0000000000..7b03d41909 --- /dev/null +++ b/src/main/java/Ui.java @@ -0,0 +1,36 @@ +package duke; + +import java.util.Scanner; + +class Ui{ + + public void showWelcome() { + String logo = "____________________________________________________________\n" + /*+ " ____ _ \n" + + "| _ \\ _ _| | _____ \n" + + "| | | | | | | |/ / _ \\\n" + + "| |_| | |_| | < __/\n" + + "|____/ \\__,_|_|\\_\\___|\n"*/ + + " Hello I'm Duke\n" + + " What can I do for you?\n" + + "____________________________________________________________\n"; + System.out.println(logo); + } + + public String readCommand() { + Scanner scanner = new Scanner(System.in); + return scanner.nextLine(); + } + +/* public void showLoadingError() { + + } +*/ + public void showLine() { + System.out.println("____________________________________________________________\n"); + } + + public void showError() { + System.out.println("☹ OOPS!!! I'm sorry, but I don't know what that means :-("); + } +} \ No newline at end of file From a7af505945e77452c7ae6c21222363787e85bc32 Mon Sep 17 00:00:00 2001 From: Javier Date: Mon, 31 Aug 2020 23:37:17 +0800 Subject: [PATCH 15/44] Correct add Ui class --- src/main/java/Duke.java | 81 ++++++++++++++++++++--------------------- src/main/java/Ui.java | 4 ++ 2 files changed, 43 insertions(+), 42 deletions(-) diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index afae4a24dc..854cf9aa79 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -105,7 +105,7 @@ else if(getWord(input).equals("save")){ } } - public static String getWord(String string) { + public String getWord(String string) { int firstSpaceIndex = string.indexOf(' '); if (firstSpaceIndex == -1) { @@ -114,42 +114,42 @@ public static String getWord(String string) { return string.substring(0, firstSpaceIndex); } - public static void list(ArrayList list) { + public void list(ArrayList list) { Task task; - System.out.println("Here are the tasks in your list:"); + ui.showOutput("Here are the tasks in your list:"); for (int i = 1; i <= list.size(); i++) { task = list.get(i - 1); - System.out.println(i + "." + task.getTypeString() + task.getDoneString() + task.getString()); + ui.showOutput(i + "." + task.getTypeString() + task.getDoneString() + task.getString()); } } - public static boolean bye() { - System.out.println("Bye. Hope to see you again soon!"); + public boolean bye() { + ui.showOutput("Bye. Hope to see you again soon!"); return false; } - public static void todo(String input, ArrayList list) { + public void todo(String input, ArrayList list) { input = input.substring(4); if (input.isEmpty()) { - System.out.println("☹ OOPS!!! The description of a todo cannot be empty."); + ui.showOutput("☹ OOPS!!! The description of a todo cannot be empty."); } else { Task task = new Task(TaskType.TODO, false, input); list.add(task); - System.out.println("Got it. I've added this task: "); - System.out.println(" " + task.getTypeString() + task.getDoneString() + input); - System.out.println("Now you have " + list.size() + " tasks in the list."); + ui.showOutput("Got it. I've added this task: "); + ui.showOutput(" " + task.getTypeString() + task.getDoneString() + input); + ui.showOutput("Now you have " + list.size() + " tasks in the list."); } } - public static void deadline(String input, ArrayList list) { + public void deadline(String input, ArrayList list) { input = input.substring(8); if (input.isEmpty()) { - System.out.println("☹ OOPS!!! The description of a deadline cannot be empty."); + ui.showOutput("☹ OOPS!!! The description of a deadline cannot be empty."); return; } int index = input.indexOf("/by"); if (index == -1) { - System.out.println("☹ OOPS!!! The description of a deadline must have a indicated deadline."); + ui.showOutput("☹ OOPS!!! The description of a deadline must have a indicated deadline."); return; } StringBuilder stringBuilder = new StringBuilder(); @@ -179,38 +179,38 @@ public static void deadline(String input, ArrayList list) { input = stringBuilder.toString(); Task task = new Task(TaskType.DEADLINE, false, input, Optional.of(localDateTime)); list.add(task); - System.out.println("Got it. I've added this task: "); - System.out.println(" " + task.getTypeString() + task.getDoneString() + input); - System.out.println("Now you have " + list.size() + " tasks in the list."); + ui.showOutput("Got it. I've added this task: "); + ui.showOutput(" " + task.getTypeString() + task.getDoneString() + input); + ui.showOutput("Now you have " + list.size() + " tasks in the list."); } catch(DateTimeParseException e) { - System.out.println("☹ OOPS!!! The description of a deadline must have a valid date and time. (Format: /by dd/mm/yyyy tttt e.g 2/12/2019 1800"); + ui.showOutput("☹ OOPS!!! The description of a deadline must have a valid date and time. (Format: /by dd/mm/yyyy tttt e.g 2/12/2019 1800"); } } - public static void event(String input, ArrayList list){ + public void event(String input, ArrayList list){ input = input.substring(5); if(input.isEmpty()){ - System.out.println("☹ OOPS!!! The description of a event cannot be empty."); + ui.showOutput("☹ OOPS!!! The description of a event cannot be empty."); }else { int index = input.indexOf("/at"); if (index == -1) { - System.out.println("☹ OOPS!!! The description of an event must have a indicated deadline."); + ui.showOutput("☹ OOPS!!! The description of an event must have a indicated deadline."); } else { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append(input.substring(0, index)).append("(at:").append(input.substring(index + 3)).append(")"); input = stringBuilder.toString(); Task task = new Task(TaskType.DEADLINE, false, input); list.add(task); - System.out.println("Got it. I've added this task: "); - System.out.println(" " + task.getTypeString() + task.getDoneString() + input); - System.out.println("Now you have " + list.size() + " tasks in the list."); + ui.showOutput("Got it. I've added this task: "); + ui.showOutput(" " + task.getTypeString() + task.getDoneString() + input); + ui.showOutput("Now you have " + list.size() + " tasks in the list."); } } } - public static void done(String input, ArrayList list){ + public void done(String input, ArrayList list){ input = input.substring(4); if(input.isEmpty()){ - System.out.println("☹ OOPS!!! Please indicate which task is done."); + ui.showOutput("☹ OOPS!!! Please indicate which task is done."); }else{ input = input.substring(1); boolean isInteger = true; @@ -223,26 +223,26 @@ public static void done(String input, ArrayList list){ if(isInteger){ int tasknumber = Integer.parseInt(input); if( (tasknumber == 0) ||(tasknumber > list.size()) ){ - System.out.println("☹ OOPS!!! Task number is not found in the list."); + ui.showOutput("☹ OOPS!!! Task number is not found in the list."); } else{ Task task = list.remove(tasknumber - 1); Task newTask = new Task(task.taskType, true, task.toString()); list.add(tasknumber-1, newTask); - System.out.println("Nice! I've marked this task as done: "); - System.out.println(" " + newTask.getTypeString() + newTask.getDoneString() + newTask.toString()); + ui.showOutput("Nice! I've marked this task as done: "); + ui.showOutput(" " + newTask.getTypeString() + newTask.getDoneString() + newTask.toString()); } } else{ - System.out.println("☹ OOPS!!! Incorrect entry for finished task."); + ui.showOutput("☹ OOPS!!! Incorrect entry for finished task."); } } } - public static void delete(String input, ArrayList list){ + public void delete(String input, ArrayList list){ input = input.substring(6); if(input.isEmpty()){ - System.out.println("☹ OOPS!!! Please indicate which task is done."); + ui.showOutput("☹ OOPS!!! Please indicate which task is done."); }else{ input = input.substring(1); boolean isInteger = true; @@ -255,26 +255,23 @@ public static void delete(String input, ArrayList list){ if(isInteger){ int tasknumber = Integer.parseInt(input); if( (tasknumber == 0) ||(tasknumber > list.size()) ){ - System.out.println("☹ OOPS!!! Task number is not found in the list."); + ui.showOutput("☹ OOPS!!! Task number is not found in the list."); } else{ Task task = list.remove(tasknumber - 1); - System.out.println("Noted. I've removed this task:"); - System.out.println(" " + task.getTypeString() + task.getDoneString() + task.toString()); - System.out.println("Now you have " + list.size() + " tasks in the list."); + ui.showOutput("Noted. I've removed this task:"); + ui.showOutput(" " + task.getTypeString() + task.getDoneString() + task.toString()); + ui.showOutput("Now you have " + list.size() + " tasks in the list."); } } else{ - System.out.println("☹ OOPS!!! Incorrect entry for finished task."); + ui.showOutput("☹ OOPS!!! Incorrect entry for finished task."); } } } - public static void printLine(){ - System.out.println("____________________________________________________________\n"); - } - public static void save(ArrayList list) throws IOException { + public void save(ArrayList list) throws IOException { FileWriter fileWriter = new FileWriter(TASKS_PATHNAME); Task task; @@ -284,6 +281,6 @@ public static void save(ArrayList list) throws IOException { } fileWriter.close(); - System.out.println("Tasks have been saved! "); + ui.showOutput("Tasks have been saved! "); } } \ No newline at end of file diff --git a/src/main/java/Ui.java b/src/main/java/Ui.java index 7b03d41909..0f1f7bfa53 100644 --- a/src/main/java/Ui.java +++ b/src/main/java/Ui.java @@ -33,4 +33,8 @@ public void showLine() { public void showError() { System.out.println("☹ OOPS!!! I'm sorry, but I don't know what that means :-("); } + + public void showOutput(String string){ + System.out.println(string); + } } \ No newline at end of file From 716ad9d544a58c2cc24d7e0e46491c53f8ed0856 Mon Sep 17 00:00:00 2001 From: Javier Date: Tue, 1 Sep 2020 00:22:11 +0800 Subject: [PATCH 16/44] Add TaskList class TaskList stores an ArrayList and deals with all changes to this list. Additionally, the class deals with all outputs needed from this arraytist --- src/main/java/Duke.java | 130 ++++++++++++++++++++---------------- src/main/java/Task.java | 2 +- src/main/java/TaskList.java | 39 +++++++++++ src/main/java/TaskType.java | 2 +- src/main/java/Ui.java | 2 +- 5 files changed, 113 insertions(+), 62 deletions(-) create mode 100644 src/main/java/TaskList.java diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 854cf9aa79..7701ba3c8d 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -3,7 +3,9 @@ import duke.Task; import duke.TaskType; import duke.Ui; +import duke.TaskList; +import java.io.FileNotFoundException; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; @@ -18,35 +20,29 @@ public class Duke { private static String TASKS_PATHNAME = "data/tasks.txt"; private Ui ui; + private TaskList taskList; public Duke(String filePath) { ui = new Ui(); - } - - public static void main(String[] args) { - try { - new Duke(TASKS_PATHNAME).run(); - } catch(IOException e) { + //load tasks from file + File file = new File(filePath); + if(file.getParentFile() != null){ + file.getParentFile().mkdirs(); } - } - - public void run() throws IOException, SecurityException { - ui.showWelcome(); - - File tasks = new File(TASKS_PATHNAME); - if(tasks.getParentFile() != null){ - tasks.getParentFile().mkdirs(); + try { + file.createNewFile(); + } catch (IOException e) { + ui.showOutput(e.getMessage()); } - tasks.createNewFile(); - - Scanner sc = new Scanner(tasks); - ArrayList list = new ArrayList<>(); - String task, taskString; - TaskType taskType; - boolean isDone; - LocalDateTime dateTime; - while(sc.hasNext()) { + try { + Scanner sc = new Scanner(file); + ArrayList list = new ArrayList<>(); + String task, taskString; + TaskType taskType; + boolean isDone; + LocalDateTime dateTime; + while(sc.hasNext()) { task = sc.nextLine(); if(task.charAt(1) == 'T') { taskType = TaskType.TODO; @@ -73,7 +69,19 @@ else if(task.charAt(1) == 'D') { else { list.add(new Task(taskType, isDone, taskString)); } + } + taskList = new TaskList(list); + } catch (FileNotFoundException e) { + ui.showOutput(e.getMessage()); } + } + + public static void main(String[] args) { + new Duke(TASKS_PATHNAME).run(); + } + + public void run() { + ui.showWelcome(); boolean isRunning = true; String input; @@ -81,22 +89,22 @@ else if(task.charAt(1) == 'D') { input = ui.readCommand(); ui.showLine(); if(input.equals("list")){ - list(list); + list(); } else if (input.equals("bye")) { isRunning = bye(); } else if (getWord(input).equals("done")) { - done(input, list); + done(input); } else if (getWord(input).equals("todo")) { - todo(input, list); + todo(input); } else if (getWord(input).equals("deadline")) { - deadline(input, list); + deadline(input); } else if (getWord(input).equals("event")) { - event(input, list); + event(input); } else if (getWord(input).equals("delete")) { - delete(input, list); + delete(input); } else if(getWord(input).equals("save")){ - save(list); + save(); } else{ ui.showError(); @@ -114,11 +122,11 @@ public String getWord(String string) { return string.substring(0, firstSpaceIndex); } - public void list(ArrayList list) { + public void list() { Task task; ui.showOutput("Here are the tasks in your list:"); - for (int i = 1; i <= list.size(); i++) { - task = list.get(i - 1); + for (int i = 1; i <= taskList.size(); i++) { + task = taskList.get(i - 1); ui.showOutput(i + "." + task.getTypeString() + task.getDoneString() + task.getString()); } } @@ -128,20 +136,20 @@ public boolean bye() { return false; } - public void todo(String input, ArrayList list) { + public void todo(String input) { input = input.substring(4); if (input.isEmpty()) { ui.showOutput("☹ OOPS!!! The description of a todo cannot be empty."); } else { Task task = new Task(TaskType.TODO, false, input); - list.add(task); + taskList.add(task); ui.showOutput("Got it. I've added this task: "); ui.showOutput(" " + task.getTypeString() + task.getDoneString() + input); - ui.showOutput("Now you have " + list.size() + " tasks in the list."); + ui.showOutput("Now you have " + taskList.size() + " tasks in the list."); } } - public void deadline(String input, ArrayList list) { + public void deadline(String input) { input = input.substring(8); if (input.isEmpty()) { ui.showOutput("☹ OOPS!!! The description of a deadline cannot be empty."); @@ -178,15 +186,15 @@ public void deadline(String input, ArrayList list) { .append(")"); input = stringBuilder.toString(); Task task = new Task(TaskType.DEADLINE, false, input, Optional.of(localDateTime)); - list.add(task); + taskList.add(task); ui.showOutput("Got it. I've added this task: "); ui.showOutput(" " + task.getTypeString() + task.getDoneString() + input); - ui.showOutput("Now you have " + list.size() + " tasks in the list."); + ui.showOutput("Now you have " + taskList.size() + " tasks in the list."); } catch(DateTimeParseException e) { ui.showOutput("☹ OOPS!!! The description of a deadline must have a valid date and time. (Format: /by dd/mm/yyyy tttt e.g 2/12/2019 1800"); } } - public void event(String input, ArrayList list){ + public void event(String input){ input = input.substring(5); if(input.isEmpty()){ ui.showOutput("☹ OOPS!!! The description of a event cannot be empty."); @@ -199,15 +207,15 @@ public void event(String input, ArrayList list){ stringBuilder.append(input.substring(0, index)).append("(at:").append(input.substring(index + 3)).append(")"); input = stringBuilder.toString(); Task task = new Task(TaskType.DEADLINE, false, input); - list.add(task); + taskList.add(task); ui.showOutput("Got it. I've added this task: "); ui.showOutput(" " + task.getTypeString() + task.getDoneString() + input); - ui.showOutput("Now you have " + list.size() + " tasks in the list."); + ui.showOutput("Now you have " + taskList.size() + " tasks in the list."); } } } - public void done(String input, ArrayList list){ + public void done(String input){ input = input.substring(4); if(input.isEmpty()){ ui.showOutput("☹ OOPS!!! Please indicate which task is done."); @@ -222,13 +230,13 @@ public void done(String input, ArrayList list){ } if(isInteger){ int tasknumber = Integer.parseInt(input); - if( (tasknumber == 0) ||(tasknumber > list.size()) ){ + if( (tasknumber == 0) ||(tasknumber >taskList.size()) ){ ui.showOutput("☹ OOPS!!! Task number is not found in the list."); } else{ - Task task = list.remove(tasknumber - 1); + Task task =taskList.remove(tasknumber - 1); Task newTask = new Task(task.taskType, true, task.toString()); - list.add(tasknumber-1, newTask); + taskList.add(tasknumber-1, newTask); ui.showOutput("Nice! I've marked this task as done: "); ui.showOutput(" " + newTask.getTypeString() + newTask.getDoneString() + newTask.toString()); } @@ -239,7 +247,7 @@ public void done(String input, ArrayList list){ } } - public void delete(String input, ArrayList list){ + public void delete(String input){ input = input.substring(6); if(input.isEmpty()){ ui.showOutput("☹ OOPS!!! Please indicate which task is done."); @@ -254,14 +262,14 @@ public void delete(String input, ArrayList list){ } if(isInteger){ int tasknumber = Integer.parseInt(input); - if( (tasknumber == 0) ||(tasknumber > list.size()) ){ + if( (tasknumber == 0) ||(tasknumber > taskList.size()) ){ ui.showOutput("☹ OOPS!!! Task number is not found in the list."); } else{ - Task task = list.remove(tasknumber - 1); + Task task = taskList.remove(tasknumber - 1); ui.showOutput("Noted. I've removed this task:"); ui.showOutput(" " + task.getTypeString() + task.getDoneString() + task.toString()); - ui.showOutput("Now you have " + list.size() + " tasks in the list."); + ui.showOutput("Now you have " + taskList.size() + " tasks in the list."); } } else{ @@ -271,16 +279,20 @@ public void delete(String input, ArrayList list){ } - public void save(ArrayList list) throws IOException { - FileWriter fileWriter = new FileWriter(TASKS_PATHNAME); - Task task; + public void save() { + try { + FileWriter fileWriter = new FileWriter(TASKS_PATHNAME); + Task task; - for (int listIndex = 0; listIndex < list.size(); listIndex++) { - task = list.get(listIndex); - fileWriter.write(task.getTypeString() + task.getDoneString() + task.getString() + System.lineSeparator()); - } - fileWriter.close(); + for (int listIndex = 0; listIndex < taskList.size(); listIndex++) { + task = taskList.get(listIndex); + fileWriter.write(task.getTypeString() + task.getDoneString() + task.getString() + System.lineSeparator()); + } + fileWriter.close(); - ui.showOutput("Tasks have been saved! "); + ui.showOutput("Tasks have been saved! "); + } catch (IOException e) { + ui.showOutput(e.getMessage()); + } } } \ No newline at end of file diff --git a/src/main/java/Task.java b/src/main/java/Task.java index dc0245b855..a196a0d8e6 100644 --- a/src/main/java/Task.java +++ b/src/main/java/Task.java @@ -3,7 +3,7 @@ import java.time.LocalDateTime; import java.util.Optional; -class Task{ +public class Task{ duke.TaskType taskType; boolean isDone; String string; diff --git a/src/main/java/TaskList.java b/src/main/java/TaskList.java new file mode 100644 index 0000000000..ae1bb4ae7d --- /dev/null +++ b/src/main/java/TaskList.java @@ -0,0 +1,39 @@ +package duke; + +import duke.Task; + +import java.util.ArrayList; + +public class TaskList { + private ArrayList tasks; + + public TaskList() { + tasks = new ArrayList<>(); + } + + public TaskList(ArrayList tasks) { + this.tasks = tasks; + } + + public int size() { + return tasks.size(); + } + + public Task get(int i) { + return tasks.get(i); + } + + public boolean add(Task task){ + tasks.add(task); + return true; + } + + public void add(int index, Task task){ + tasks.add(index, task); + } + + public Task remove(int index){ + return tasks.remove(index); + } + +} \ No newline at end of file diff --git a/src/main/java/TaskType.java b/src/main/java/TaskType.java index 801ef69c71..0ea7f59bd3 100644 --- a/src/main/java/TaskType.java +++ b/src/main/java/TaskType.java @@ -1,6 +1,6 @@ package duke; -enum TaskType{ +public enum TaskType{ TODO, DEADLINE, EVENT; diff --git a/src/main/java/Ui.java b/src/main/java/Ui.java index 0f1f7bfa53..453f3bafd4 100644 --- a/src/main/java/Ui.java +++ b/src/main/java/Ui.java @@ -2,7 +2,7 @@ import java.util.Scanner; -class Ui{ +public class Ui{ public void showWelcome() { String logo = "____________________________________________________________\n" From 9bb8360318d75767147e61c93459b0f70f590170 Mon Sep 17 00:00:00 2001 From: Javier Date: Tue, 1 Sep 2020 00:57:22 +0800 Subject: [PATCH 17/44] Add DukeException and Storage classes Storage class deals with loading tasks from the file and saving tasks in the file DukeException is thrown when no file is present with the name represented by filePath --- data/tasks.txt | 4 +- src/main/java/Duke.java | 71 ++++--------------------- src/main/java/DukeException.java | 7 +++ src/main/java/Storage.java | 91 ++++++++++++++++++++++++++++++++ src/main/java/Ui.java | 6 +-- 5 files changed, 112 insertions(+), 67 deletions(-) create mode 100644 src/main/java/DukeException.java create mode 100644 src/main/java/Storage.java diff --git a/data/tasks.txt b/data/tasks.txt index b78725c8d1..b6dccd7f46 100644 --- a/data/tasks.txt +++ b/data/tasks.txt @@ -1,2 +1,2 @@ -[T][✗] borrow book -[T][✗] save time +[T][✓] borrow book +[T][✗] eat food diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 7701ba3c8d..26a733ffe0 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -4,6 +4,8 @@ import duke.TaskType; import duke.Ui; import duke.TaskList; +import duke.Storage; +import duke.DukeException; import java.io.FileNotFoundException; import java.time.LocalDateTime; @@ -21,58 +23,16 @@ public class Duke { private static String TASKS_PATHNAME = "data/tasks.txt"; private Ui ui; private TaskList taskList; + private Storage storage; public Duke(String filePath) { ui = new Ui(); - - //load tasks from file - File file = new File(filePath); - if(file.getParentFile() != null){ - file.getParentFile().mkdirs(); - } + storage = new Storage(filePath); try { - file.createNewFile(); - } catch (IOException e) { - ui.showOutput(e.getMessage()); - } - try { - Scanner sc = new Scanner(file); - ArrayList list = new ArrayList<>(); - String task, taskString; - TaskType taskType; - boolean isDone; - LocalDateTime dateTime; - while(sc.hasNext()) { - task = sc.nextLine(); - if(task.charAt(1) == 'T') { - taskType = TaskType.TODO; - } - else if(task.charAt(1) == 'D') { - taskType = TaskType.DEADLINE; - } - else { - taskType = TaskType.EVENT; - } - - if(task.charAt(4) == '✓') { - isDone = true; - } - else { - isDone = false; - } - taskString = task.substring(6); - if(taskType == TaskType.DEADLINE){ - int index = task.indexOf("(by:"); - dateTime = LocalDateTime.parse(task.substring(index + 4, task.length()-1), DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.SHORT)); - list.add(new Task(taskType, isDone, taskString, Optional.of(dateTime))); - } - else { - list.add(new Task(taskType, isDone, taskString)); - } - } - taskList = new TaskList(list); - } catch (FileNotFoundException e) { - ui.showOutput(e.getMessage()); + taskList = new TaskList(storage.load()); + } catch (DukeException e) { + ui.showLoadingError(); + taskList = new TaskList(); } } @@ -280,19 +240,6 @@ public void delete(String input){ public void save() { - try { - FileWriter fileWriter = new FileWriter(TASKS_PATHNAME); - Task task; - - for (int listIndex = 0; listIndex < taskList.size(); listIndex++) { - task = taskList.get(listIndex); - fileWriter.write(task.getTypeString() + task.getDoneString() + task.getString() + System.lineSeparator()); - } - fileWriter.close(); - - ui.showOutput("Tasks have been saved! "); - } catch (IOException e) { - ui.showOutput(e.getMessage()); - } + ui.showOutput(storage.save(taskList)); } } \ No newline at end of file diff --git a/src/main/java/DukeException.java b/src/main/java/DukeException.java new file mode 100644 index 0000000000..404cfc267b --- /dev/null +++ b/src/main/java/DukeException.java @@ -0,0 +1,7 @@ +package duke; + +public class DukeException extends Exception { + public DukeException() { + super("Storage file is not found."); + } +} \ No newline at end of file diff --git a/src/main/java/Storage.java b/src/main/java/Storage.java new file mode 100644 index 0000000000..ce646a0e5f --- /dev/null +++ b/src/main/java/Storage.java @@ -0,0 +1,91 @@ +package duke; + +import duke.Task; +import duke.DukeException; +import duke.TaskList; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileWriter; +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.FormatStyle; +import java.util.ArrayList; +import java.util.Optional; +import java.util.Scanner; + + +public class Storage{ + private String filePath; + + public Storage(String filePath) { + this.filePath = filePath; + } + + public ArrayList load() throws DukeException { + File file = new File(filePath); + ArrayList list = new ArrayList<>(); + try { + Scanner sc = new Scanner(file); + String task, taskString; + duke.TaskType taskType; + boolean isDone; + LocalDateTime dateTime; + while(sc.hasNext()) { + task = sc.nextLine(); + if(task.charAt(1) == 'T') { + taskType = duke.TaskType.TODO; + } + else if(task.charAt(1) == 'D') { + taskType = duke.TaskType.DEADLINE; + } + else { + taskType = duke.TaskType.EVENT; + } + + if(task.charAt(4) == '✓') { + isDone = true; + } + else { + isDone = false; + } + taskString = task.substring(6); + if(taskType == duke.TaskType.DEADLINE){ + int index = task.indexOf("(by:"); + dateTime = LocalDateTime.parse(task.substring(index + 4, task.length()-1), DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.SHORT)); + list.add(new Task(taskType, isDone, taskString, Optional.of(dateTime))); + } + else { + list.add(new Task(taskType, isDone, taskString)); + } + } + + } catch (FileNotFoundException e) { + throw new DukeException(); + } + return list; + } + + public String save(TaskList taskList) { + File file = new File(filePath); + if(file.getParentFile() != null) { + file.getParentFile().mkdirs(); + } + try { + file.createNewFile(); + FileWriter fileWriter = new FileWriter(filePath); + Task task; + + for (int listIndex = 0; listIndex < taskList.size(); listIndex++) { + task = taskList.get(listIndex); + fileWriter.write(task.getTypeString() + task.getDoneString() + task.getString() + System.lineSeparator()); + } + fileWriter.close(); + + return "Tasks have been saved! "; + } catch (IOException e) { + return e.getMessage(); + } + } +} \ No newline at end of file diff --git a/src/main/java/Ui.java b/src/main/java/Ui.java index 453f3bafd4..1c5b797eaa 100644 --- a/src/main/java/Ui.java +++ b/src/main/java/Ui.java @@ -22,10 +22,10 @@ public String readCommand() { return scanner.nextLine(); } -/* public void showLoadingError() { - + public void showLoadingError() { + System.out.println("No storage file found."); } -*/ + public void showLine() { System.out.println("____________________________________________________________\n"); } From 60c5f05a4bc216f4ddac1cf3c06fe091ec9882bb Mon Sep 17 00:00:00 2001 From: Javier Date: Tue, 1 Sep 2020 01:43:38 +0800 Subject: [PATCH 18/44] Add Command and Parser classes Command is an interface with two abstract methods. Parser deals with making sense of the user command. A Command instance is implemented using Parser's static parse method. --- data/tasks.txt | 2 +- src/main/java/Command.java | 11 ++ src/main/java/Duke.java | 211 ++-------------------- src/main/java/DukeException.java | 4 +- src/main/java/Parser.java | 289 +++++++++++++++++++++++++++++++ src/main/java/Storage.java | 2 +- src/main/java/Ui.java | 4 +- 7 files changed, 320 insertions(+), 203 deletions(-) create mode 100644 src/main/java/Command.java create mode 100644 src/main/java/Parser.java diff --git a/data/tasks.txt b/data/tasks.txt index b6dccd7f46..ce76bf19c1 100644 --- a/data/tasks.txt +++ b/data/tasks.txt @@ -1,2 +1,2 @@ [T][✓] borrow book -[T][✗] eat food +[T][✗] borrow book diff --git a/src/main/java/Command.java b/src/main/java/Command.java new file mode 100644 index 0000000000..56d043aabe --- /dev/null +++ b/src/main/java/Command.java @@ -0,0 +1,11 @@ +package duke; + +import duke.TaskList; +import duke.Ui; +import duke.Storage; + + +public interface Command { + public void execute(TaskList taskList, Ui ui, Storage storage); + public boolean isExit(); +} \ No newline at end of file diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 26a733ffe0..8246aa13a7 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -6,6 +6,8 @@ import duke.TaskList; import duke.Storage; import duke.DukeException; +import duke.Command; +import duke.Parser; import java.io.FileNotFoundException; import java.time.LocalDateTime; @@ -42,204 +44,19 @@ public static void main(String[] args) { public void run() { ui.showWelcome(); - - boolean isRunning = true; - String input; - while(isRunning) { - input = ui.readCommand(); - ui.showLine(); - if(input.equals("list")){ - list(); - } else if (input.equals("bye")) { - isRunning = bye(); - } else if (getWord(input).equals("done")) { - done(input); - } else if (getWord(input).equals("todo")) { - todo(input); - } else if (getWord(input).equals("deadline")) { - deadline(input); - } else if (getWord(input).equals("event")) { - event(input); - } else if (getWord(input).equals("delete")) { - delete(input); - } - else if(getWord(input).equals("save")){ - save(); - } - else{ - ui.showError(); + boolean isExit = false; + while (!isExit) { + try { + String fullCommand = ui.readCommand(); + ui.showLine(); // show the divider line ("_______") + Command c = Parser.parse(fullCommand); + c.execute(taskList, ui, storage); + isExit = c.isExit(); + } catch (DukeException e) { + ui.showError(e.getMessage()); + } finally { + ui.showLine(); } - ui.showLine(); } } - - public String getWord(String string) { - - int firstSpaceIndex = string.indexOf(' '); - if (firstSpaceIndex == -1) { - return string; - } - return string.substring(0, firstSpaceIndex); - } - - public void list() { - Task task; - ui.showOutput("Here are the tasks in your list:"); - for (int i = 1; i <= taskList.size(); i++) { - task = taskList.get(i - 1); - ui.showOutput(i + "." + task.getTypeString() + task.getDoneString() + task.getString()); - } - } - - public boolean bye() { - ui.showOutput("Bye. Hope to see you again soon!"); - return false; - } - - public void todo(String input) { - input = input.substring(4); - if (input.isEmpty()) { - ui.showOutput("☹ OOPS!!! The description of a todo cannot be empty."); - } else { - Task task = new Task(TaskType.TODO, false, input); - taskList.add(task); - ui.showOutput("Got it. I've added this task: "); - ui.showOutput(" " + task.getTypeString() + task.getDoneString() + input); - ui.showOutput("Now you have " + taskList.size() + " tasks in the list."); - } - } - - public void deadline(String input) { - input = input.substring(8); - if (input.isEmpty()) { - ui.showOutput("☹ OOPS!!! The description of a deadline cannot be empty."); - return; - } - int index = input.indexOf("/by"); - if (index == -1) { - ui.showOutput("☹ OOPS!!! The description of a deadline must have a indicated deadline."); - return; - } - StringBuilder stringBuilder = new StringBuilder(); - String[] strings = new String[1]; - if(input.length() >= index + 4) { - strings = input.substring(index + 4).split("/"); - } - if( (strings.length >= 3) && (strings[2].length() >= 9) ) { - if(strings[0].length() < 2){ - strings[0] = "0" + strings[0]; - } - stringBuilder.append(strings[2].substring(0, 4)) - .append("-").append(strings[1]) - .append("-").append(strings[0]) - .append("T") - .append(strings[2].substring(5, 7)) - .append(":") - .append(strings[2].substring(7, 9)); - } - try{ - LocalDateTime localDateTime = LocalDateTime.parse(stringBuilder.toString(), DateTimeFormatter.ISO_LOCAL_DATE_TIME); - stringBuilder = new StringBuilder(); - stringBuilder.append(input.substring(0, index)) - .append("(by: ") - .append(localDateTime.format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.SHORT))) - .append(")"); - input = stringBuilder.toString(); - Task task = new Task(TaskType.DEADLINE, false, input, Optional.of(localDateTime)); - taskList.add(task); - ui.showOutput("Got it. I've added this task: "); - ui.showOutput(" " + task.getTypeString() + task.getDoneString() + input); - ui.showOutput("Now you have " + taskList.size() + " tasks in the list."); - } catch(DateTimeParseException e) { - ui.showOutput("☹ OOPS!!! The description of a deadline must have a valid date and time. (Format: /by dd/mm/yyyy tttt e.g 2/12/2019 1800"); - } - } - public void event(String input){ - input = input.substring(5); - if(input.isEmpty()){ - ui.showOutput("☹ OOPS!!! The description of a event cannot be empty."); - }else { - int index = input.indexOf("/at"); - if (index == -1) { - ui.showOutput("☹ OOPS!!! The description of an event must have a indicated deadline."); - } else { - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append(input.substring(0, index)).append("(at:").append(input.substring(index + 3)).append(")"); - input = stringBuilder.toString(); - Task task = new Task(TaskType.DEADLINE, false, input); - taskList.add(task); - ui.showOutput("Got it. I've added this task: "); - ui.showOutput(" " + task.getTypeString() + task.getDoneString() + input); - ui.showOutput("Now you have " + taskList.size() + " tasks in the list."); - } - } - } - - public void done(String input){ - input = input.substring(4); - if(input.isEmpty()){ - ui.showOutput("☹ OOPS!!! Please indicate which task is done."); - }else{ - input = input.substring(1); - boolean isInteger = true; - for(int i = 0; i < input.length(); i++){ - if(!Character.isDigit(input.charAt(i))){ - isInteger = false; - break; - } - } - if(isInteger){ - int tasknumber = Integer.parseInt(input); - if( (tasknumber == 0) ||(tasknumber >taskList.size()) ){ - ui.showOutput("☹ OOPS!!! Task number is not found in the list."); - } - else{ - Task task =taskList.remove(tasknumber - 1); - Task newTask = new Task(task.taskType, true, task.toString()); - taskList.add(tasknumber-1, newTask); - ui.showOutput("Nice! I've marked this task as done: "); - ui.showOutput(" " + newTask.getTypeString() + newTask.getDoneString() + newTask.toString()); - } - } - else{ - ui.showOutput("☹ OOPS!!! Incorrect entry for finished task."); - } - } - } - - public void delete(String input){ - input = input.substring(6); - if(input.isEmpty()){ - ui.showOutput("☹ OOPS!!! Please indicate which task is done."); - }else{ - input = input.substring(1); - boolean isInteger = true; - for(int i = 0; i < input.length(); i++){ - if(!Character.isDigit(input.charAt(i))){ - isInteger = false; - break; - } - } - if(isInteger){ - int tasknumber = Integer.parseInt(input); - if( (tasknumber == 0) ||(tasknumber > taskList.size()) ){ - ui.showOutput("☹ OOPS!!! Task number is not found in the list."); - } - else{ - Task task = taskList.remove(tasknumber - 1); - ui.showOutput("Noted. I've removed this task:"); - ui.showOutput(" " + task.getTypeString() + task.getDoneString() + task.toString()); - ui.showOutput("Now you have " + taskList.size() + " tasks in the list."); - } - } - else{ - ui.showOutput("☹ OOPS!!! Incorrect entry for finished task."); - } - } - } - - - public void save() { - ui.showOutput(storage.save(taskList)); - } } \ No newline at end of file diff --git a/src/main/java/DukeException.java b/src/main/java/DukeException.java index 404cfc267b..e017a0adcf 100644 --- a/src/main/java/DukeException.java +++ b/src/main/java/DukeException.java @@ -1,7 +1,7 @@ package duke; public class DukeException extends Exception { - public DukeException() { - super("Storage file is not found."); + public DukeException(String string) { + super(string); } } \ 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 0000000000..de3ea620c7 --- /dev/null +++ b/src/main/java/Parser.java @@ -0,0 +1,289 @@ +package duke; + +import duke.DukeException; +import duke.Command; +import duke.TaskList; +import duke.Ui; +import duke.Storage; +import duke.Task; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.time.format.FormatStyle; +import java.util.Optional; + +public class Parser { + public static Command parse(String fullCommand) throws DukeException { + if(fullCommand.equals("list")){ + return list(); + } else if (fullCommand.equals("bye")) { + return bye(); + } else if (getWord(fullCommand).equals("done")) { + return done(fullCommand); + } else if (getWord(fullCommand).equals("todo")) { + return todo(fullCommand); + } else if (getWord(fullCommand).equals("deadline")) { + return deadline(fullCommand); + } else if (getWord(fullCommand).equals("event")) { + return event(fullCommand); + } else if (getWord(fullCommand).equals("delete")) { + return delete(fullCommand); + } + else if(getWord(fullCommand).equals("save")){ + return save(); + } + else{ + throw new DukeException("☹ OOPS!!! I'm sorry, but I don't know what that means :-("); + } + } + public static String getWord(String string) { + + int firstSpaceIndex = string.indexOf(' '); + if (firstSpaceIndex == -1) { + return string; + } + return string.substring(0, firstSpaceIndex); + } + + public static Command list() { + return new Command() { + @Override + public void execute(TaskList taskList, Ui ui, Storage storage) { + Task task; + ui.showOutput("Here are the tasks in your list:"); + for (int i = 1; i <= taskList.size(); i++) { + task = taskList.get(i - 1); + ui.showOutput(i + "." + task.getTypeString() + task.getDoneString() + task.getString()); + } + } + + @Override + public boolean isExit() { + return false; + } + }; + } + + public static Command bye() { + return new Command() { + @Override + public void execute(TaskList taskList, Ui ui, Storage storage) { + ui.showOutput("Bye. Hope to see you again soon!"); + } + + @Override + public boolean isExit() { + return true; + } + }; + } + + public static Command todo(String string) { + return new Command() { + @Override + public void execute(TaskList taskList, Ui ui, Storage storage) { + String input = string.substring(4); + if (input.isEmpty()) { + ui.showOutput("☹ OOPS!!! The description of a todo cannot be empty."); + } else { + duke.Task task = new duke.Task(duke.TaskType.TODO, false, input); + taskList.add(task); + ui.showOutput("Got it. I've added this task: "); + ui.showOutput(" " + task.getTypeString() + task.getDoneString() + input); + ui.showOutput("Now you have " + taskList.size() + " tasks in the list."); + } + } + + @Override + public boolean isExit() { + return false; + } + }; + } + + public static Command deadline(String string) { + return new Command() { + @Override + public void execute(TaskList taskList, Ui ui, Storage storage) { + String input = string.substring(8); + if (input.isEmpty()) { + ui.showOutput("☹ OOPS!!! The description of a deadline cannot be empty."); + return; + } + int index = input.indexOf("/by"); + if (index == -1) { + ui.showOutput("☹ OOPS!!! The description of a deadline must have a indicated deadline."); + return; + } + StringBuilder stringBuilder = new StringBuilder(); + String[] strings = new String[1]; + if(input.length() >= index + 4) { + strings = input.substring(index + 4).split("/"); + } + if( (strings.length >= 3) && (strings[2].length() >= 9) ) { + if(strings[0].length() < 2){ + strings[0] = "0" + strings[0]; + } + stringBuilder.append(strings[2].substring(0, 4)) + .append("-").append(strings[1]) + .append("-").append(strings[0]) + .append("T") + .append(strings[2].substring(5, 7)) + .append(":") + .append(strings[2].substring(7, 9)); + } + try{ + LocalDateTime localDateTime = LocalDateTime.parse(stringBuilder.toString(), DateTimeFormatter.ISO_LOCAL_DATE_TIME); + stringBuilder = new StringBuilder(); + stringBuilder.append(input.substring(0, index)) + .append("(by: ") + .append(localDateTime.format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.SHORT))) + .append(")"); + input = stringBuilder.toString(); + duke.Task task = new duke.Task(duke.TaskType.DEADLINE, false, input, Optional.of(localDateTime)); + taskList.add(task); + ui.showOutput("Got it. I've added this task: "); + ui.showOutput(" " + task.getTypeString() + task.getDoneString() + input); + ui.showOutput("Now you have " + taskList.size() + " tasks in the list."); + } catch(DateTimeParseException e) { + ui.showOutput("☹ OOPS!!! The description of a deadline must have a valid date and time. (Format: /by dd/mm/yyyy tttt e.g 2/12/2019 1800"); + } + } + + @Override + public boolean isExit() { + return false; + } + }; + + } + public static Command event(String string){ + return new Command() { + @Override + public void execute(TaskList taskList, Ui ui, Storage storage) { + String input = string.substring(5); + if(input.isEmpty()){ + ui.showOutput("☹ OOPS!!! The description of a event cannot be empty."); + }else { + int index = input.indexOf("/at"); + if (index == -1) { + ui.showOutput("☹ OOPS!!! The description of an event must have a indicated deadline."); + } else { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(input.substring(0, index)).append("(at:").append(input.substring(index + 3)).append(")"); + input = stringBuilder.toString(); + duke.Task task = new duke.Task(duke.TaskType.DEADLINE, false, input); + taskList.add(task); + ui.showOutput("Got it. I've added this task: "); + ui.showOutput(" " + task.getTypeString() + task.getDoneString() + input); + ui.showOutput("Now you have " + taskList.size() + " tasks in the list."); + } + } + } + + @Override + public boolean isExit() { + return false; + } + }; + } + + public static Command done(String string){ + return new Command() { + @Override + public void execute(TaskList taskList, Ui ui, Storage storage) { + String input = string.substring(4); + if(input.isEmpty()){ + ui.showOutput("☹ OOPS!!! Please indicate which task is done."); + }else{ + input = input.substring(1); + boolean isInteger = true; + for(int i = 0; i < input.length(); i++){ + if(!Character.isDigit(input.charAt(i))){ + isInteger = false; + break; + } + } + if(isInteger){ + int tasknumber = Integer.parseInt(input); + if( (tasknumber == 0) ||(tasknumber >taskList.size()) ){ + ui.showOutput("☹ OOPS!!! Task number is not found in the list."); + } + else{ + duke.Task task =taskList.remove(tasknumber - 1); + duke.Task newTask = new duke.Task(task.taskType, true, task.toString()); + taskList.add(tasknumber-1, newTask); + ui.showOutput("Nice! I've marked this task as done: "); + ui.showOutput(" " + newTask.getTypeString() + newTask.getDoneString() + newTask.toString()); + } + } + else{ + ui.showOutput("☹ OOPS!!! Incorrect entry for finished task."); + } + } + } + + @Override + public boolean isExit() { + return false; + } + }; + } + + public static Command delete(String string){ + return new Command() { + @Override + public void execute(TaskList taskList, Ui ui, Storage storage) { + String input = string.substring(6); + if(input.isEmpty()){ + ui.showOutput("☹ OOPS!!! Please indicate which task is done."); + }else{ + input = input.substring(1); + boolean isInteger = true; + for(int i = 0; i < input.length(); i++){ + if(!Character.isDigit(input.charAt(i))){ + isInteger = false; + break; + } + } + if(isInteger){ + int tasknumber = Integer.parseInt(input); + if( (tasknumber == 0) ||(tasknumber > taskList.size()) ){ + ui.showOutput("☹ OOPS!!! Task number is not found in the list."); + } + else{ + duke.Task task = taskList.remove(tasknumber - 1); + ui.showOutput("Noted. I've removed this task:"); + ui.showOutput(" " + task.getTypeString() + task.getDoneString() + task.toString()); + ui.showOutput("Now you have " + taskList.size() + " tasks in the list."); + } + } + else{ + ui.showOutput("☹ OOPS!!! Incorrect entry for finished task."); + } + } + } + + @Override + public boolean isExit() { + return false; + } + }; + } + + + public static Command save() { + return new Command() { + @Override + public void execute(TaskList taskList, Ui ui, Storage storage) { + ui.showOutput(storage.save(taskList)); + } + + @Override + public boolean isExit() { + return false; + } + }; + } +} \ No newline at end of file diff --git a/src/main/java/Storage.java b/src/main/java/Storage.java index ce646a0e5f..fd8695632e 100644 --- a/src/main/java/Storage.java +++ b/src/main/java/Storage.java @@ -62,7 +62,7 @@ else if(task.charAt(1) == 'D') { } } catch (FileNotFoundException e) { - throw new DukeException(); + throw new DukeException("File not found."); } return list; } diff --git a/src/main/java/Ui.java b/src/main/java/Ui.java index 1c5b797eaa..1374d21164 100644 --- a/src/main/java/Ui.java +++ b/src/main/java/Ui.java @@ -30,8 +30,8 @@ public void showLine() { System.out.println("____________________________________________________________\n"); } - public void showError() { - System.out.println("☹ OOPS!!! I'm sorry, but I don't know what that means :-("); + public void showError(String string) { + System.out.println(string); } public void showOutput(String string){ From 9eeb71e3f72454b01aea3666ada2325ce54f7229 Mon Sep 17 00:00:00 2001 From: Javier Date: Tue, 1 Sep 2020 03:38:55 +0800 Subject: [PATCH 19/44] Add ParserTest and TaskTest classes; Modified Command interface's method, execute, output type to String from void Test classes uses JUnit for testing of the classes' methods. --- src/main/java/Command.java | 2 +- src/main/java/Parser.java | 191 +++++++++++++++++++++++++--------- src/test/java/ParserTest.java | 39 +++++++ src/test/java/TaskTest.java | 27 +++++ 4 files changed, 209 insertions(+), 50 deletions(-) create mode 100644 src/test/java/ParserTest.java create mode 100644 src/test/java/TaskTest.java diff --git a/src/main/java/Command.java b/src/main/java/Command.java index 56d043aabe..cd70f4e655 100644 --- a/src/main/java/Command.java +++ b/src/main/java/Command.java @@ -6,6 +6,6 @@ public interface Command { - public void execute(TaskList taskList, Ui ui, Storage storage); + public String execute(TaskList taskList, Ui ui, Storage storage); public boolean isExit(); } \ No newline at end of file diff --git a/src/main/java/Parser.java b/src/main/java/Parser.java index de3ea620c7..b24e65c3c4 100644 --- a/src/main/java/Parser.java +++ b/src/main/java/Parser.java @@ -49,13 +49,19 @@ public static String getWord(String string) { public static Command list() { return new Command() { @Override - public void execute(TaskList taskList, Ui ui, Storage storage) { + public String execute(TaskList taskList, Ui ui, Storage storage) { Task task; - ui.showOutput("Here are the tasks in your list:"); + String string = "Here are the tasks in your list:"; + StringBuilder stringBuilder = new StringBuilder().append(string).append("\n"); + ui.showOutput(string); for (int i = 1; i <= taskList.size(); i++) { task = taskList.get(i - 1); - ui.showOutput(i + "." + task.getTypeString() + task.getDoneString() + task.getString()); + string = i + "." + task.getTypeString() + task.getDoneString() + task.getString(); + stringBuilder.append(string).append("\n"); + ui.showOutput(string); } + stringBuilder.deleteCharAt(stringBuilder.length()-1); + return stringBuilder.toString(); } @Override @@ -68,8 +74,10 @@ public boolean isExit() { public static Command bye() { return new Command() { @Override - public void execute(TaskList taskList, Ui ui, Storage storage) { - ui.showOutput("Bye. Hope to see you again soon!"); + public String execute(TaskList taskList, Ui ui, Storage storage) { + String string = "Bye. Hope to see you again soon!"; + ui.showOutput(string); + return string; } @Override @@ -82,17 +90,31 @@ public boolean isExit() { public static Command todo(String string) { return new Command() { @Override - public void execute(TaskList taskList, Ui ui, Storage storage) { + public String execute(TaskList taskList, Ui ui, Storage storage) { + StringBuilder stringBuilder = new StringBuilder(); String input = string.substring(4); + String temp; if (input.isEmpty()) { - ui.showOutput("☹ OOPS!!! The description of a todo cannot be empty."); + temp = "☹ OOPS!!! The description of a todo cannot be empty."; + stringBuilder.append(temp); + ui.showOutput(temp); } else { duke.Task task = new duke.Task(duke.TaskType.TODO, false, input); taskList.add(task); - ui.showOutput("Got it. I've added this task: "); - ui.showOutput(" " + task.getTypeString() + task.getDoneString() + input); - ui.showOutput("Now you have " + taskList.size() + " tasks in the list."); + + temp = "Got it. I've added this task:"; + ui.showOutput(temp); + stringBuilder.append(temp).append("\n"); + + temp = " " + task.getTypeString() + task.getDoneString() + input; + ui.showOutput(temp); + stringBuilder.append(temp).append("\n"); + + temp ="Now you have " + taskList.size() + " tasks in the list."; + ui.showOutput(temp); + stringBuilder.append(temp); } + return stringBuilder.toString(); } @Override @@ -105,18 +127,24 @@ public boolean isExit() { public static Command deadline(String string) { return new Command() { @Override - public void execute(TaskList taskList, Ui ui, Storage storage) { + public String execute(TaskList taskList, Ui ui, Storage storage) { String input = string.substring(8); + String temp; + StringBuilder outputStringBuilder = new StringBuilder(); if (input.isEmpty()) { - ui.showOutput("☹ OOPS!!! The description of a deadline cannot be empty."); - return; + temp = "☹ OOPS!!! The description of a deadline cannot be empty."; + ui.showOutput(temp); + outputStringBuilder.append(temp); + return outputStringBuilder.toString(); } int index = input.indexOf("/by"); if (index == -1) { - ui.showOutput("☹ OOPS!!! The description of a deadline must have a indicated deadline."); - return; + temp = "☹ OOPS!!! The description of a deadline must have a indicated deadline."; + ui.showOutput(temp); + outputStringBuilder.append(temp); + return outputStringBuilder.toString(); } - StringBuilder stringBuilder = new StringBuilder(); + StringBuilder dateStringBuilder = new StringBuilder(); String[] strings = new String[1]; if(input.length() >= index + 4) { strings = input.substring(index + 4).split("/"); @@ -125,7 +153,7 @@ public void execute(TaskList taskList, Ui ui, Storage storage) { if(strings[0].length() < 2){ strings[0] = "0" + strings[0]; } - stringBuilder.append(strings[2].substring(0, 4)) + dateStringBuilder.append(strings[2].substring(0, 4)) .append("-").append(strings[1]) .append("-").append(strings[0]) .append("T") @@ -134,20 +162,34 @@ public void execute(TaskList taskList, Ui ui, Storage storage) { .append(strings[2].substring(7, 9)); } try{ - LocalDateTime localDateTime = LocalDateTime.parse(stringBuilder.toString(), DateTimeFormatter.ISO_LOCAL_DATE_TIME); - stringBuilder = new StringBuilder(); - stringBuilder.append(input.substring(0, index)) + LocalDateTime localDateTime = LocalDateTime.parse(dateStringBuilder.toString(), DateTimeFormatter.ISO_LOCAL_DATE_TIME); + StringBuilder taskStringBuilder = new StringBuilder(); + taskStringBuilder.append(input.substring(0, index)) .append("(by: ") .append(localDateTime.format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.SHORT))) .append(")"); - input = stringBuilder.toString(); + input = taskStringBuilder.toString(); duke.Task task = new duke.Task(duke.TaskType.DEADLINE, false, input, Optional.of(localDateTime)); taskList.add(task); - ui.showOutput("Got it. I've added this task: "); - ui.showOutput(" " + task.getTypeString() + task.getDoneString() + input); - ui.showOutput("Now you have " + taskList.size() + " tasks in the list."); + + temp = "Got it. I've added this task:"; + ui.showOutput(temp); + outputStringBuilder.append(temp).append("\n"); + + temp = " " + task.getTypeString() + task.getDoneString() + input; + ui.showOutput(temp); + outputStringBuilder.append(temp).append("\n"); + + temp = "Now you have " + taskList.size() + " tasks in the list."; + ui.showOutput(temp); + outputStringBuilder.append(temp); + + return outputStringBuilder.toString(); } catch(DateTimeParseException e) { - ui.showOutput("☹ OOPS!!! The description of a deadline must have a valid date and time. (Format: /by dd/mm/yyyy tttt e.g 2/12/2019 1800"); + temp = "☹ OOPS!!! The description of a deadline must have a valid date and time. (Format: /by dd/mm/yyyy tttt e.g 2/12/2019 1800"; + ui.showOutput(temp); + outputStringBuilder.append(temp); + return outputStringBuilder.toString(); } } @@ -161,25 +203,41 @@ public boolean isExit() { public static Command event(String string){ return new Command() { @Override - public void execute(TaskList taskList, Ui ui, Storage storage) { + public String execute(TaskList taskList, Ui ui, Storage storage) { String input = string.substring(5); + StringBuilder outputStringBuilder = new StringBuilder(); + String temp; if(input.isEmpty()){ - ui.showOutput("☹ OOPS!!! The description of a event cannot be empty."); + temp = "☹ OOPS!!! The description of a event cannot be empty."; + ui.showOutput(temp); + outputStringBuilder.append(temp); }else { int index = input.indexOf("/at"); if (index == -1) { - ui.showOutput("☹ OOPS!!! The description of an event must have a indicated deadline."); + temp ="☹ OOPS!!! The description of an event must have a indicated deadline."; + ui.showOutput(temp); + outputStringBuilder.append(temp); } else { - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append(input.substring(0, index)).append("(at:").append(input.substring(index + 3)).append(")"); - input = stringBuilder.toString(); + StringBuilder taskStringBuilder = new StringBuilder(); + taskStringBuilder.append(input.substring(0, index)).append("(at:").append(input.substring(index + 3)).append(")"); + input = taskStringBuilder.toString(); duke.Task task = new duke.Task(duke.TaskType.DEADLINE, false, input); taskList.add(task); - ui.showOutput("Got it. I've added this task: "); - ui.showOutput(" " + task.getTypeString() + task.getDoneString() + input); - ui.showOutput("Now you have " + taskList.size() + " tasks in the list."); + + temp = "Got it. I've added this task:"; + ui.showOutput(temp); + outputStringBuilder.append(temp).append("\n"); + + temp = " " + task.getTypeString() + task.getDoneString() + input; + ui.showOutput(temp); + outputStringBuilder.append(temp).append("\n"); + + temp = "Now you have " + taskList.size() + " tasks in the list."; + ui.showOutput(temp); + outputStringBuilder.append(temp); } } + return outputStringBuilder.toString(); } @Override @@ -192,10 +250,14 @@ public boolean isExit() { public static Command done(String string){ return new Command() { @Override - public void execute(TaskList taskList, Ui ui, Storage storage) { + public String execute(TaskList taskList, Ui ui, Storage storage) { String input = string.substring(4); + StringBuilder outputStringBuilder = new StringBuilder(); + String temp; if(input.isEmpty()){ - ui.showOutput("☹ OOPS!!! Please indicate which task is done."); + temp = "☹ OOPS!!! Please indicate which task is done."; + ui.showOutput(temp); + outputStringBuilder.append(temp); }else{ input = input.substring(1); boolean isInteger = true; @@ -208,20 +270,31 @@ public void execute(TaskList taskList, Ui ui, Storage storage) { if(isInteger){ int tasknumber = Integer.parseInt(input); if( (tasknumber == 0) ||(tasknumber >taskList.size()) ){ - ui.showOutput("☹ OOPS!!! Task number is not found in the list."); + temp = "☹ OOPS!!! Task number is not found in the list."; + ui.showOutput(temp); + outputStringBuilder.append(temp); } else{ duke.Task task =taskList.remove(tasknumber - 1); duke.Task newTask = new duke.Task(task.taskType, true, task.toString()); taskList.add(tasknumber-1, newTask); - ui.showOutput("Nice! I've marked this task as done: "); - ui.showOutput(" " + newTask.getTypeString() + newTask.getDoneString() + newTask.toString()); + + temp = "Nice! I've marked this task as done:"; + ui.showOutput(temp); + outputStringBuilder.append(temp).append("\n"); + + temp = " " + newTask.getTypeString() + newTask.getDoneString() + newTask.toString(); + ui.showOutput(temp); + outputStringBuilder.append(temp); } } else{ - ui.showOutput("☹ OOPS!!! Incorrect entry for finished task."); + temp = "☹ OOPS!!! Incorrect entry for finished task."; + ui.showOutput(temp); + outputStringBuilder.append(temp); } } + return outputStringBuilder.toString(); } @Override @@ -234,10 +307,14 @@ public boolean isExit() { public static Command delete(String string){ return new Command() { @Override - public void execute(TaskList taskList, Ui ui, Storage storage) { + public String execute(TaskList taskList, Ui ui, Storage storage) { String input = string.substring(6); + StringBuilder outputStringBuilder = new StringBuilder(); + String temp; if(input.isEmpty()){ - ui.showOutput("☹ OOPS!!! Please indicate which task is done."); + temp = "☹ OOPS!!! Please indicate which task is done."; + ui.showOutput(temp); + outputStringBuilder.append(temp); }else{ input = input.substring(1); boolean isInteger = true; @@ -250,19 +327,33 @@ public void execute(TaskList taskList, Ui ui, Storage storage) { if(isInteger){ int tasknumber = Integer.parseInt(input); if( (tasknumber == 0) ||(tasknumber > taskList.size()) ){ - ui.showOutput("☹ OOPS!!! Task number is not found in the list."); + temp = "☹ OOPS!!! Task number is not found in the list."; + ui.showOutput(temp); + outputStringBuilder.append(temp); } else{ duke.Task task = taskList.remove(tasknumber - 1); - ui.showOutput("Noted. I've removed this task:"); - ui.showOutput(" " + task.getTypeString() + task.getDoneString() + task.toString()); - ui.showOutput("Now you have " + taskList.size() + " tasks in the list."); + + temp = "Noted. I've removed this task:"; + ui.showOutput(temp); + outputStringBuilder.append(temp).append("\n"); + + temp = " " + task.getTypeString() + task.getDoneString() + task.toString(); + ui.showOutput(temp); + outputStringBuilder.append(temp).append("\n"); + + temp = "Now you have " + taskList.size() + " tasks in the list."; + ui.showOutput(temp); + outputStringBuilder.append(temp); } } else{ - ui.showOutput("☹ OOPS!!! Incorrect entry for finished task."); + temp = "☹ OOPS!!! Incorrect entry for finished task."; + ui.showOutput(temp); + outputStringBuilder.append(temp); } } + return outputStringBuilder.toString(); } @Override @@ -276,8 +367,10 @@ public boolean isExit() { public static Command save() { return new Command() { @Override - public void execute(TaskList taskList, Ui ui, Storage storage) { - ui.showOutput(storage.save(taskList)); + public String execute(TaskList taskList, Ui ui, Storage storage) { + String temp = storage.save(taskList); + ui.showOutput(temp); + return temp; } @Override diff --git a/src/test/java/ParserTest.java b/src/test/java/ParserTest.java new file mode 100644 index 0000000000..faca20307e --- /dev/null +++ b/src/test/java/ParserTest.java @@ -0,0 +1,39 @@ +package duke; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class ParserTest { + @Test + public void testParse1(){ + String string; + try { + string = duke.Parser.parse("bye").execute(new duke.TaskList(), new duke.Ui(), new duke.Storage("duke")); + } catch (duke.DukeException e) { + string = e.getMessage(); + } + assertEquals("Bye. Hope to see you again soon!", string); + } + @Test + public void testParse2(){ + String string; + try { + string = duke.Parser.parse("asdas").execute(new duke.TaskList(), new duke.Ui(), new duke.Storage("duke")); + } catch (duke.DukeException e) { + string = e.getMessage(); + } + assertEquals("☹ OOPS!!! I'm sorry, but I don't know what that means :-(", string); + } + @Test + public void testParse3(){ + String outputstring, expectedString; + try { + outputstring = duke.Parser.parse("todo borrow book").execute(new duke.TaskList(), new duke.Ui(), new duke.Storage("duke")); + } catch (duke.DukeException e) { + outputstring = e.getMessage(); + } + expectedString = "Got it. I've added this task:\n [T][✗] borrow book\nNow you have 1 tasks in the list."; + assertEquals(expectedString, outputstring); + } +} \ No newline at end of file diff --git a/src/test/java/TaskTest.java b/src/test/java/TaskTest.java new file mode 100644 index 0000000000..fd063e04c0 --- /dev/null +++ b/src/test/java/TaskTest.java @@ -0,0 +1,27 @@ +package duke; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class TaskTest { + @Test + public void testGetDoneString(){ + assertEquals("[✓]", new duke.Task(duke.TaskType.DEADLINE, true, "aaaaa bbbb").getDoneString()); + assertEquals("[✗]", new duke.Task(duke.TaskType.TODO, false, "abc bbbb").getDoneString()); + } + + @Test + public void testGetTypeString(){ + assertEquals("[T]", new duke.Task(duke.TaskType.TODO, true, "ERRRR").getTypeString()); + assertEquals("[D]", new duke.Task(duke.TaskType.DEADLINE, false, "aserfasdf").getTypeString()); + assertEquals("[E]", new duke.Task(duke.TaskType.EVENT, false, "asdfsadf").getTypeString()); + } + + @Test + public void testToString(){ + assertEquals("biaaaa", new duke.Task(duke.TaskType.TODO, true, "biaaaa").toString()); + assertEquals("aswasasuperfasdf", new duke.Task(duke.TaskType.DEADLINE, false, "aswasasuperfasdf").toString()); + assertEquals("gcg", new duke.Task(duke.TaskType.EVENT, false, "gcg").toString()); + } +} From 15d0e0c9837af0ad3897d5c7b94c2503dc907b6d Mon Sep 17 00:00:00 2001 From: Javier Date: Tue, 1 Sep 2020 13:04:51 +0800 Subject: [PATCH 20/44] Add MANIFEST.MF file --- src/main/java/META-INF/MANIFEST.MF | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 src/main/java/META-INF/MANIFEST.MF diff --git a/src/main/java/META-INF/MANIFEST.MF b/src/main/java/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..2c9a9745c5 --- /dev/null +++ b/src/main/java/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: duke.Duke + From d08b4b72fb227f0a1154d5200309152ad1125429 Mon Sep 17 00:00:00 2001 From: Javier Date: Tue, 1 Sep 2020 13:29:24 +0800 Subject: [PATCH 21/44] Add JavaDoc comments Only header comments for classes have been added --- src/main/java/Duke.java | 20 ++++++++++++++++---- src/main/java/DukeException.java | 3 +++ src/main/java/Parser.java | 18 ++++++++++++++++++ src/main/java/Storage.java | 3 +++ src/main/java/Task.java | 5 +++++ src/main/java/TaskList.java | 4 ++++ src/main/java/TaskType.java | 3 +++ src/main/java/Ui.java | 4 ++++ 8 files changed, 56 insertions(+), 4 deletions(-) diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 8246aa13a7..08465ceb15 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -21,12 +21,28 @@ import java.util.Optional; import java.util.Scanner; +/** + * Class contains main method of the Duke application. + * Duke manages, stores and track tasks as specified by the user. + */ public class Duke { private static String TASKS_PATHNAME = "data/tasks.txt"; private Ui ui; private TaskList taskList; private Storage storage; + /** + * main method of Duke + * @param args + */ + public static void main(String[] args) { + new Duke(TASKS_PATHNAME).run(); + } + + /** + * Constructor which takes in file path of the storage file. + * @param filePath + */ public Duke(String filePath) { ui = new Ui(); storage = new Storage(filePath); @@ -38,10 +54,6 @@ public Duke(String filePath) { } } - public static void main(String[] args) { - new Duke(TASKS_PATHNAME).run(); - } - public void run() { ui.showWelcome(); boolean isExit = false; diff --git a/src/main/java/DukeException.java b/src/main/java/DukeException.java index e017a0adcf..7c3d2598ce 100644 --- a/src/main/java/DukeException.java +++ b/src/main/java/DukeException.java @@ -1,5 +1,8 @@ package duke; +/** + * Exception class that gets thrown by supporting classes' methods. + */ public class DukeException extends Exception { public DukeException(String string) { super(string); diff --git a/src/main/java/Parser.java b/src/main/java/Parser.java index b24e65c3c4..1438b1e127 100644 --- a/src/main/java/Parser.java +++ b/src/main/java/Parser.java @@ -13,6 +13,11 @@ import java.time.format.FormatStyle; import java.util.Optional; +/** + * Parser deals with the full commands given by the user. + * Inputs given are deciphered and corresponding responses are outputted. + */ + public class Parser { public static Command parse(String fullCommand) throws DukeException { if(fullCommand.equals("list")){ @@ -37,6 +42,14 @@ else if(getWord(fullCommand).equals("save")){ throw new DukeException("☹ OOPS!!! I'm sorry, but I don't know what that means :-("); } } + + /** + * Returns the first word in the string indicated by the first blank space. + * If there are no blank space, the entire string is returned. + * + * @param string + * @return + */ public static String getWord(String string) { int firstSpaceIndex = string.indexOf(' '); @@ -46,6 +59,11 @@ public static String getWord(String string) { return string.substring(0, firstSpaceIndex); } + /** + * Returns an implemented instance of the Command interface when the list command is invoked. + * + * @return + */ public static Command list() { return new Command() { @Override diff --git a/src/main/java/Storage.java b/src/main/java/Storage.java index fd8695632e..642e658305 100644 --- a/src/main/java/Storage.java +++ b/src/main/java/Storage.java @@ -15,6 +15,9 @@ import java.util.Optional; import java.util.Scanner; +/** + * Storage deals with loading and saving task list from/ to memory. + */ public class Storage{ private String filePath; diff --git a/src/main/java/Task.java b/src/main/java/Task.java index a196a0d8e6..da57f89f6f 100644 --- a/src/main/java/Task.java +++ b/src/main/java/Task.java @@ -3,6 +3,11 @@ import java.time.LocalDateTime; import java.util.Optional; +/** + * Task stores it type, a boolean indicating whether the task has been completed, a string describing the task and a + * time. + */ + public class Task{ duke.TaskType taskType; boolean isDone; diff --git a/src/main/java/TaskList.java b/src/main/java/TaskList.java index ae1bb4ae7d..3c6157fd18 100644 --- a/src/main/java/TaskList.java +++ b/src/main/java/TaskList.java @@ -4,6 +4,10 @@ import java.util.ArrayList; +/** + * TaskList contains an ArrayList of Task. + * It serves as a barrier to directly using the ArrayList class so as to limit the functions available to the client. + */ public class TaskList { private ArrayList tasks; diff --git a/src/main/java/TaskType.java b/src/main/java/TaskType.java index 0ea7f59bd3..5be4d92556 100644 --- a/src/main/java/TaskType.java +++ b/src/main/java/TaskType.java @@ -1,5 +1,8 @@ package duke; +/** + * TaskType is an enum that represents that types of tasks. + */ public enum TaskType{ TODO, DEADLINE, diff --git a/src/main/java/Ui.java b/src/main/java/Ui.java index 1374d21164..97b69955f2 100644 --- a/src/main/java/Ui.java +++ b/src/main/java/Ui.java @@ -2,6 +2,10 @@ import java.util.Scanner; +/** + * Ui deals with any interaction with the user. + * This includes both user inputs and outputs to user. + */ public class Ui{ public void showWelcome() { From cd09121a1e6d2c9efd787c47fb7b071c35c92f73 Mon Sep 17 00:00:00 2001 From: Javier Date: Tue, 1 Sep 2020 14:04:06 +0800 Subject: [PATCH 22/44] Tweak the code to comply with a coding standard --- src/main/java/Parser.java | 257 +++++++++++++++++++----------------- src/main/java/Task.java | 2 +- src/main/java/TaskType.java | 2 +- src/main/java/Ui.java | 2 +- 4 files changed, 140 insertions(+), 123 deletions(-) diff --git a/src/main/java/Parser.java b/src/main/java/Parser.java index b24e65c3c4..e473898213 100644 --- a/src/main/java/Parser.java +++ b/src/main/java/Parser.java @@ -15,7 +15,7 @@ public class Parser { public static Command parse(String fullCommand) throws DukeException { - if(fullCommand.equals("list")){ + if(fullCommand.equals("list")) { return list(); } else if (fullCommand.equals("bye")) { return bye(); @@ -29,11 +29,9 @@ public static Command parse(String fullCommand) throws DukeException { return event(fullCommand); } else if (getWord(fullCommand).equals("delete")) { return delete(fullCommand); - } - else if(getWord(fullCommand).equals("save")){ + } else if(getWord(fullCommand).equals("save")) { return save(); - } - else{ + } else { throw new DukeException("☹ OOPS!!! I'm sorry, but I don't know what that means :-("); } } @@ -91,30 +89,32 @@ public static Command todo(String string) { return new Command() { @Override public String execute(TaskList taskList, Ui ui, Storage storage) { - StringBuilder stringBuilder = new StringBuilder(); + StringBuilder outputStringBuilder = new StringBuilder(); String input = string.substring(4); String temp; if (input.isEmpty()) { temp = "☹ OOPS!!! The description of a todo cannot be empty."; - stringBuilder.append(temp); + outputStringBuilder.append(temp); ui.showOutput(temp); - } else { - duke.Task task = new duke.Task(duke.TaskType.TODO, false, input); - taskList.add(task); + return outputStringBuilder.toString(); + } - temp = "Got it. I've added this task:"; - ui.showOutput(temp); - stringBuilder.append(temp).append("\n"); + duke.Task task = new duke.Task(duke.TaskType.TODO, false, input); + taskList.add(task); - temp = " " + task.getTypeString() + task.getDoneString() + input; - ui.showOutput(temp); - stringBuilder.append(temp).append("\n"); + temp = "Got it. I've added this task:"; + ui.showOutput(temp); + outputStringBuilder.append(temp).append("\n"); - temp ="Now you have " + taskList.size() + " tasks in the list."; - ui.showOutput(temp); - stringBuilder.append(temp); - } - return stringBuilder.toString(); + temp = " " + task.getTypeString() + task.getDoneString() + input; + ui.showOutput(temp); + outputStringBuilder.append(temp).append("\n"); + + temp ="Now you have " + taskList.size() + " tasks in the list."; + ui.showOutput(temp); + outputStringBuilder.append(temp); + + return outputStringBuilder.toString(); } @Override @@ -131,12 +131,16 @@ public String execute(TaskList taskList, Ui ui, Storage storage) { String input = string.substring(8); String temp; StringBuilder outputStringBuilder = new StringBuilder(); + + //No description if (input.isEmpty()) { temp = "☹ OOPS!!! The description of a deadline cannot be empty."; ui.showOutput(temp); outputStringBuilder.append(temp); return outputStringBuilder.toString(); } + + //No /by int index = input.indexOf("/by"); if (index == -1) { temp = "☹ OOPS!!! The description of a deadline must have a indicated deadline."; @@ -144,6 +148,8 @@ public String execute(TaskList taskList, Ui ui, Storage storage) { outputStringBuilder.append(temp); return outputStringBuilder.toString(); } + + //Convert user date into string which is readable by LocalDateTime class. StringBuilder dateStringBuilder = new StringBuilder(); String[] strings = new String[1]; if(input.length() >= index + 4) { @@ -161,7 +167,9 @@ public String execute(TaskList taskList, Ui ui, Storage storage) { .append(":") .append(strings[2].substring(7, 9)); } - try{ + + //Try to input date if it a valid is date is used. + try { LocalDateTime localDateTime = LocalDateTime.parse(dateStringBuilder.toString(), DateTimeFormatter.ISO_LOCAL_DATE_TIME); StringBuilder taskStringBuilder = new StringBuilder(); taskStringBuilder.append(input.substring(0, index)) @@ -200,7 +208,7 @@ public boolean isExit() { }; } - public static Command event(String string){ + public static Command event(String string) { return new Command() { @Override public String execute(TaskList taskList, Ui ui, Storage storage) { @@ -211,32 +219,35 @@ public String execute(TaskList taskList, Ui ui, Storage storage) { temp = "☹ OOPS!!! The description of a event cannot be empty."; ui.showOutput(temp); outputStringBuilder.append(temp); - }else { - int index = input.indexOf("/at"); - if (index == -1) { - temp ="☹ OOPS!!! The description of an event must have a indicated deadline."; - ui.showOutput(temp); - outputStringBuilder.append(temp); - } else { - StringBuilder taskStringBuilder = new StringBuilder(); - taskStringBuilder.append(input.substring(0, index)).append("(at:").append(input.substring(index + 3)).append(")"); - input = taskStringBuilder.toString(); - duke.Task task = new duke.Task(duke.TaskType.DEADLINE, false, input); - taskList.add(task); - - temp = "Got it. I've added this task:"; - ui.showOutput(temp); - outputStringBuilder.append(temp).append("\n"); - - temp = " " + task.getTypeString() + task.getDoneString() + input; - ui.showOutput(temp); - outputStringBuilder.append(temp).append("\n"); - - temp = "Now you have " + taskList.size() + " tasks in the list."; - ui.showOutput(temp); - outputStringBuilder.append(temp); - } + return outputStringBuilder.toString(); } + + int index = input.indexOf("/at"); + if (index == -1) { + temp ="☹ OOPS!!! The description of an event must have a indicated deadline."; + ui.showOutput(temp); + outputStringBuilder.append(temp); + return outputStringBuilder.toString(); + } + + StringBuilder taskStringBuilder = new StringBuilder(); + taskStringBuilder.append(input.substring(0, index)).append("(at:").append(input.substring(index + 3)).append(")"); + input = taskStringBuilder.toString(); + duke.Task task = new duke.Task(duke.TaskType.DEADLINE, false, input); + taskList.add(task); + + temp = "Got it. I've added this task:"; + ui.showOutput(temp); + outputStringBuilder.append(temp).append("\n"); + + temp = " " + task.getTypeString() + task.getDoneString() + input; + ui.showOutput(temp); + outputStringBuilder.append(temp).append("\n"); + + temp = "Now you have " + taskList.size() + " tasks in the list."; + ui.showOutput(temp); + outputStringBuilder.append(temp); + return outputStringBuilder.toString(); } @@ -247,7 +258,7 @@ public boolean isExit() { }; } - public static Command done(String string){ + public static Command done(String string) { return new Command() { @Override public String execute(TaskList taskList, Ui ui, Storage storage) { @@ -258,42 +269,46 @@ public String execute(TaskList taskList, Ui ui, Storage storage) { temp = "☹ OOPS!!! Please indicate which task is done."; ui.showOutput(temp); outputStringBuilder.append(temp); - }else{ - input = input.substring(1); - boolean isInteger = true; - for(int i = 0; i < input.length(); i++){ - if(!Character.isDigit(input.charAt(i))){ - isInteger = false; - break; - } - } - if(isInteger){ - int tasknumber = Integer.parseInt(input); - if( (tasknumber == 0) ||(tasknumber >taskList.size()) ){ - temp = "☹ OOPS!!! Task number is not found in the list."; - ui.showOutput(temp); - outputStringBuilder.append(temp); - } - else{ - duke.Task task =taskList.remove(tasknumber - 1); - duke.Task newTask = new duke.Task(task.taskType, true, task.toString()); - taskList.add(tasknumber-1, newTask); - - temp = "Nice! I've marked this task as done:"; - ui.showOutput(temp); - outputStringBuilder.append(temp).append("\n"); - - temp = " " + newTask.getTypeString() + newTask.getDoneString() + newTask.toString(); - ui.showOutput(temp); - outputStringBuilder.append(temp); - } - } - else{ - temp = "☹ OOPS!!! Incorrect entry for finished task."; - ui.showOutput(temp); - outputStringBuilder.append(temp); + return outputStringBuilder.toString(); + } + + input = input.substring(1); + boolean isInteger = true; + for(int charIndex = 0; charIndex < input.length(); charIndex++){ + if(!Character.isDigit(input.charAt(charIndex))){ + isInteger = false; + break; } } + if(!isInteger) { + temp = "☹ OOPS!!! Incorrect entry for finished task."; + ui.showOutput(temp); + outputStringBuilder.append(temp); + return outputStringBuilder.toString(); + } + + int tasknumber = Integer.parseInt(input); + if( (tasknumber == 0) ||(tasknumber >taskList.size()) ){ + temp = "☹ OOPS!!! Task number is not found in the list."; + ui.showOutput(temp); + outputStringBuilder.append(temp); + return outputStringBuilder.toString(); + } + + duke.Task task = taskList.remove(tasknumber - 1); + Task newTask = task.done(); + taskList.add(tasknumber - 1, newTask); + + + temp = "Nice! I've marked this task as done:"; + ui.showOutput(temp); + outputStringBuilder.append(temp).append("\n"); + + temp = " " + newTask.getTypeString() + newTask.getDoneString() + newTask.toString(); + ui.showOutput(temp); + outputStringBuilder.append(temp); + + return outputStringBuilder.toString(); } @@ -311,48 +326,50 @@ public String execute(TaskList taskList, Ui ui, Storage storage) { String input = string.substring(6); StringBuilder outputStringBuilder = new StringBuilder(); String temp; - if(input.isEmpty()){ + if(input.isEmpty()) { temp = "☹ OOPS!!! Please indicate which task is done."; ui.showOutput(temp); outputStringBuilder.append(temp); - }else{ - input = input.substring(1); - boolean isInteger = true; - for(int i = 0; i < input.length(); i++){ - if(!Character.isDigit(input.charAt(i))){ - isInteger = false; - break; - } - } - if(isInteger){ - int tasknumber = Integer.parseInt(input); - if( (tasknumber == 0) ||(tasknumber > taskList.size()) ){ - temp = "☹ OOPS!!! Task number is not found in the list."; - ui.showOutput(temp); - outputStringBuilder.append(temp); - } - else{ - duke.Task task = taskList.remove(tasknumber - 1); - - temp = "Noted. I've removed this task:"; - ui.showOutput(temp); - outputStringBuilder.append(temp).append("\n"); - - temp = " " + task.getTypeString() + task.getDoneString() + task.toString(); - ui.showOutput(temp); - outputStringBuilder.append(temp).append("\n"); - - temp = "Now you have " + taskList.size() + " tasks in the list."; - ui.showOutput(temp); - outputStringBuilder.append(temp); - } - } - else{ - temp = "☹ OOPS!!! Incorrect entry for finished task."; - ui.showOutput(temp); - outputStringBuilder.append(temp); + return outputStringBuilder.toString(); + } + + input = input.substring(1); + boolean isInteger = true; + for(int charIndex = 0; charIndex < input.length(); charIndex++){ + if(!Character.isDigit(input.charAt(charIndex))){ + isInteger = false; + break; } } + if(!isInteger) { + temp = "☹ OOPS!!! Incorrect entry for finished task."; + ui.showOutput(temp); + outputStringBuilder.append(temp); + return outputStringBuilder.toString(); + } + + int tasknumber = Integer.parseInt(input); + if( (tasknumber == 0) ||(tasknumber > taskList.size()) ){ + temp = "☹ OOPS!!! Task number is not found in the list."; + ui.showOutput(temp); + outputStringBuilder.append(temp); + return outputStringBuilder.toString(); + } + + duke.Task task = taskList.remove(tasknumber - 1); + + temp = "Noted. I've removed this task:"; + ui.showOutput(temp); + outputStringBuilder.append(temp).append("\n"); + + temp = " " + task.getTypeString() + task.getDoneString() + task.toString(); + ui.showOutput(temp); + outputStringBuilder.append(temp).append("\n"); + + temp = "Now you have " + taskList.size() + " tasks in the list."; + ui.showOutput(temp); + outputStringBuilder.append(temp); + return outputStringBuilder.toString(); } diff --git a/src/main/java/Task.java b/src/main/java/Task.java index a196a0d8e6..4366318000 100644 --- a/src/main/java/Task.java +++ b/src/main/java/Task.java @@ -15,7 +15,7 @@ public Task(duke.TaskType taskType, boolean isDone, String string){ this.string = string; this.dateTime = Optional.empty(); } - public Task(duke.TaskType taskType, boolean isDone, String string, Optional date) { + public Task(duke.TaskType taskType, boolean isDone, String string, Optional dateTime) { this.taskType = taskType; this.isDone = isDone; this.string = string; diff --git a/src/main/java/TaskType.java b/src/main/java/TaskType.java index 0ea7f59bd3..ade8a1c11a 100644 --- a/src/main/java/TaskType.java +++ b/src/main/java/TaskType.java @@ -1,6 +1,6 @@ package duke; -public enum TaskType{ +public enum TaskType { TODO, DEADLINE, EVENT; diff --git a/src/main/java/Ui.java b/src/main/java/Ui.java index 1374d21164..71e44a52c5 100644 --- a/src/main/java/Ui.java +++ b/src/main/java/Ui.java @@ -2,7 +2,7 @@ import java.util.Scanner; -public class Ui{ +public class Ui { public void showWelcome() { String logo = "____________________________________________________________\n" From df9575e6cf88f4b86b130885c4bdde8c386fd558 Mon Sep 17 00:00:00 2001 From: Javier Date: Tue, 1 Sep 2020 14:28:16 +0800 Subject: [PATCH 23/44] Add Find method List all task that contains the phrase --- src/main/java/Parser.java | 29 +++++++++++++++++++++++++++++ src/main/java/Task.java | 6 ++++++ 2 files changed, 35 insertions(+) diff --git a/src/main/java/Parser.java b/src/main/java/Parser.java index b24e65c3c4..9efc1d6064 100644 --- a/src/main/java/Parser.java +++ b/src/main/java/Parser.java @@ -32,6 +32,8 @@ public static Command parse(String fullCommand) throws DukeException { } else if(getWord(fullCommand).equals("save")){ return save(); + } else if (getWord(fullCommand).equals("find")) { + return find(fullCommand); } else{ throw new DukeException("☹ OOPS!!! I'm sorry, but I don't know what that means :-("); @@ -379,4 +381,31 @@ public boolean isExit() { } }; } + + public static Command find(String fullCommand) { + return new Command() { + @Override + public String execute(TaskList taskList, Ui ui, Storage storage) { + String phrase = fullCommand.substring(5); + StringBuilder outputStringBuilder = new StringBuilder(); + String temp = "Here are the matching tasks in your list:"; + ui.showOutput(temp); + outputStringBuilder.append(temp).append("\n"); + for (int taskListIndex = 1; taskListIndex <= taskList.size(); taskListIndex++) { + Task task = taskList.get(taskListIndex -1); + if (task.getFullString().contains(phrase)) { + temp = taskListIndex + "." + task.getFullString(); + ui.showOutput(temp); + outputStringBuilder.append(temp).append("\n"); + } + } + return outputStringBuilder.deleteCharAt(outputStringBuilder.length()-1).toString(); + } + + @Override + public boolean isExit() { + return false; + } + }; + } } \ No newline at end of file diff --git a/src/main/java/Task.java b/src/main/java/Task.java index a196a0d8e6..20dbb49389 100644 --- a/src/main/java/Task.java +++ b/src/main/java/Task.java @@ -57,4 +57,10 @@ else if(taskType.equals(duke.TaskType.DEADLINE)){ public String toString(){ return string; } + + public String getFullString(){ + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(getTypeString()).append(getDoneString()).append(toString()); + return stringBuilder.toString(); + } } From 1e87679395974cad444e897b3bfe56110e03b3dd Mon Sep 17 00:00:00 2001 From: Javier Date: Wed, 2 Sep 2020 23:23:23 +0800 Subject: [PATCH 24/44] Add gradle support --- build.gradle | 19 +- config/checkstyle/checkstyle.xml | 403 +++++++++++++++++++++++++++++ config/checkstyle/suppressions.xml | 10 + src/main/java/Command.java | 10 +- src/main/java/Duke.java | 39 +-- src/main/java/Storage.java | 2 +- 6 files changed, 443 insertions(+), 40 deletions(-) create mode 100644 config/checkstyle/checkstyle.xml create mode 100644 config/checkstyle/suppressions.xml diff --git a/build.gradle b/build.gradle index b0c5528fb5..c06f5d57ce 100644 --- a/build.gradle +++ b/build.gradle @@ -12,6 +12,21 @@ repositories { dependencies { testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.5.0' testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.5.0' + + String javaFxVersion = '11' + + implementation group: 'org.openjfx', name: 'javafx-base', version: javaFxVersion, classifier: 'win' + implementation group: 'org.openjfx', name: 'javafx-base', version: javaFxVersion, classifier: 'mac' + implementation group: 'org.openjfx', name: 'javafx-base', version: javaFxVersion, classifier: 'linux' + implementation group: 'org.openjfx', name: 'javafx-controls', version: javaFxVersion, classifier: 'win' + implementation group: 'org.openjfx', name: 'javafx-controls', version: javaFxVersion, classifier: 'mac' + implementation group: 'org.openjfx', name: 'javafx-controls', version: javaFxVersion, classifier: 'linux' + implementation group: 'org.openjfx', name: 'javafx-fxml', version: javaFxVersion, classifier: 'win' + implementation group: 'org.openjfx', name: 'javafx-fxml', version: javaFxVersion, classifier: 'mac' + implementation group: 'org.openjfx', name: 'javafx-fxml', version: javaFxVersion, classifier: 'linux' + implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'win' + implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'mac' + implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'linux' } test { @@ -29,7 +44,7 @@ test { } application { - mainClassName = "seedu.duke.Duke" + mainClassName = "duke.Duke" } shadowJar { @@ -38,7 +53,7 @@ shadowJar { } checkstyle { - toolVersion = '8.23' + toolVersion = '8.29' } run{ diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml new file mode 100644 index 0000000000..4c001417ae --- /dev/null +++ b/config/checkstyle/checkstyle.xml @@ -0,0 +1,403 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/config/checkstyle/suppressions.xml b/config/checkstyle/suppressions.xml new file mode 100644 index 0000000000..39efb6e4ac --- /dev/null +++ b/config/checkstyle/suppressions.xml @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/src/main/java/Command.java b/src/main/java/Command.java index 37744b2384..bb83a31ce3 100644 --- a/src/main/java/Command.java +++ b/src/main/java/Command.java @@ -1,14 +1,10 @@ package duke; -import duke.TaskList; -import duke.Ui; -import duke.Storage; - /** * Interface which would be implemented in Parser class when specific commands are called. */ public interface Command { - public String execute(TaskList taskList, Ui ui, Storage storage); - public boolean isExit(); -} \ No newline at end of file + String execute(TaskList taskList, Ui ui, Storage storage); + boolean isExit(); +} diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 08465ceb15..9602cede9b 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -1,26 +1,5 @@ package duke; -import duke.Task; -import duke.TaskType; -import duke.Ui; -import duke.TaskList; -import duke.Storage; -import duke.DukeException; -import duke.Command; -import duke.Parser; - -import java.io.FileNotFoundException; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.time.format.DateTimeParseException; -import java.time.format.FormatStyle; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Optional; -import java.util.Scanner; - /** * Class contains main method of the Duke application. * Duke manages, stores and track tasks as specified by the user. @@ -31,14 +10,6 @@ public class Duke { private TaskList taskList; private Storage storage; - /** - * main method of Duke - * @param args - */ - public static void main(String[] args) { - new Duke(TASKS_PATHNAME).run(); - } - /** * Constructor which takes in file path of the storage file. * @param filePath @@ -54,6 +25,14 @@ public Duke(String filePath) { } } + /** + * main method of Duke + * @param args + */ + public static void main(String[] args) { + new Duke(TASKS_PATHNAME).run(); + } + public void run() { ui.showWelcome(); boolean isExit = false; @@ -71,4 +50,4 @@ public void run() { } } } -} \ No newline at end of file +} diff --git a/src/main/java/Storage.java b/src/main/java/Storage.java index ffdb219711..4a958f1fff 100644 --- a/src/main/java/Storage.java +++ b/src/main/java/Storage.java @@ -45,7 +45,7 @@ public ArrayList load() throws DukeException { taskType = duke.TaskType.EVENT; } - if (task.charAt(4) == '✓') { + if (task.charAt(4) == '\u2713') { //✓ isDone = true; } else { isDone = false; From da66a0ee4939effc3b64118615823114f337149f Mon Sep 17 00:00:00 2001 From: Javier Date: Thu, 10 Sep 2020 14:44:59 +0800 Subject: [PATCH 25/44] Add GUI part 4 successfully --- build.gradle | 2 +- src/main/java/DialogBox.java | 61 ++++++++++++ src/main/java/Duke.java | 122 ++++++++++++++++++++++++ src/main/java/Launcher.java | 12 +++ src/main/java/Main.java | 31 ++++++ src/main/java/MainWindow.java | 51 ++++++++++ src/main/resources/images/DaDuke.png | Bin 0 -> 32657 bytes src/main/resources/images/DaUser.png | Bin 0 -> 37794 bytes src/main/resources/view/DialogBox.fxml | 16 ++++ src/main/resources/view/MainWindow.fxml | 19 ++++ 10 files changed, 313 insertions(+), 1 deletion(-) create mode 100644 src/main/java/DialogBox.java create mode 100644 src/main/java/Launcher.java create mode 100644 src/main/java/Main.java create mode 100644 src/main/java/MainWindow.java create mode 100644 src/main/resources/images/DaDuke.png create mode 100644 src/main/resources/images/DaUser.png create mode 100644 src/main/resources/view/DialogBox.fxml create mode 100644 src/main/resources/view/MainWindow.fxml diff --git a/build.gradle b/build.gradle index c06f5d57ce..b8fa30093a 100644 --- a/build.gradle +++ b/build.gradle @@ -44,7 +44,7 @@ test { } application { - mainClassName = "duke.Duke" + mainClassName = "duke.Launcher" } shadowJar { diff --git a/src/main/java/DialogBox.java b/src/main/java/DialogBox.java new file mode 100644 index 0000000000..9232f24914 --- /dev/null +++ b/src/main/java/DialogBox.java @@ -0,0 +1,61 @@ +package duke; + +import java.io.IOException; +import java.util.Collections; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.geometry.Pos; +import javafx.scene.Node; +import javafx.scene.control.Label; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.layout.HBox; + +/** + * An example of a custom control using FXML. + * This control represents a dialog box consisting of an ImageView to represent the speaker's face and a label + * containing text from the speaker. + */ +public class DialogBox extends HBox { + @FXML + private Label dialog; + @FXML + private ImageView displayPicture; + + private DialogBox(String text, Image img) { + try { + FXMLLoader fxmlLoader = new FXMLLoader(MainWindow.class.getResource("/view/DialogBox.fxml")); + fxmlLoader.setController(this); + fxmlLoader.setRoot(this); + fxmlLoader.load(); + } catch (IOException e) { + e.printStackTrace(); + } + + dialog.setText(text); + displayPicture.setImage(img); + } + + /** + * Flips the dialog box such that the ImageView is on the left and text on the right. + */ + private void flip() { + ObservableList tmp = FXCollections.observableArrayList(this.getChildren()); + Collections.reverse(tmp); + getChildren().setAll(tmp); + setAlignment(Pos.TOP_LEFT); + } + + public static DialogBox getUserDialog(String text, Image img) { + return new DialogBox(text, img); + } + + public static DialogBox getDukeDialog(String text, Image img) { + var db = new DialogBox(text, img); + db.flip(); + return db; + } +} \ No newline at end of file diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 9602cede9b..eccd5723ca 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -1,5 +1,18 @@ package duke; +import javafx.application.Application; +import javafx.scene.Scene; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.control.ScrollPane; +import javafx.scene.control.TextField; +import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.VBox; +import javafx.stage.Stage; +import javafx.scene.layout.Region; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; + /** * Class contains main method of the Duke application. * Duke manages, stores and track tasks as specified by the user. @@ -10,6 +23,15 @@ public class Duke { private TaskList taskList; private Storage storage; + private ScrollPane scrollPane; + private VBox dialogContainer; + private TextField userInput; + private Button sendButton; + private Scene scene; + + private Image user = new Image(this.getClass().getResourceAsStream("/images/DaUser.png")); + private Image duke = new Image(this.getClass().getResourceAsStream("/images/DaDuke.png")); + /** * Constructor which takes in file path of the storage file. * @param filePath @@ -25,6 +47,10 @@ public Duke(String filePath) { } } + public Duke(){ + super(); + } + /** * main method of Duke * @param args @@ -50,4 +76,100 @@ public void run() { } } } + +/* @Override + public void start(Stage stage) { + //Step 1. Setting up required components + + //The container for the content of the chat to scroll. + scrollPane = new ScrollPane(); + dialogContainer = new VBox(); + scrollPane.setContent(dialogContainer); + + userInput = new TextField(); + sendButton = new Button("Send"); + + AnchorPane mainLayout = new AnchorPane(); + mainLayout.getChildren().addAll(scrollPane, userInput, sendButton); + + scene = new Scene(mainLayout); + + stage.setScene(scene); + stage.show(); + + //Step 2. Formatting the window to look as expected + stage.setTitle("Duke"); + stage.setResizable(false); + stage.setMinHeight(600.0); + stage.setMinWidth(400.0); + + mainLayout.setPrefSize(400.0, 600.0); + + scrollPane.setPrefSize(385, 535); + scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER); + scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.ALWAYS); + + scrollPane.setVvalue(1.0); + scrollPane.setFitToWidth(true); + + // You will need to import `javafx.scene.layout.Region` for this. + dialogContainer.setPrefHeight(Region.USE_COMPUTED_SIZE); + + userInput.setPrefWidth(325.0); + + sendButton.setPrefWidth(55.0); + + AnchorPane.setTopAnchor(scrollPane, 1.0); + + AnchorPane.setBottomAnchor(sendButton, 1.0); + AnchorPane.setRightAnchor(sendButton, 1.0); + + AnchorPane.setLeftAnchor(userInput , 1.0); + AnchorPane.setBottomAnchor(userInput, 1.0); + + //Part 3. Add functionality to handle user input. + sendButton.setOnMouseClicked((event) -> { + handleUserInput(); + }); + + userInput.setOnAction((event) -> { + handleUserInput(); + }); + } + */ /** + * Iteration 1: + * Creates a label with the specified text and adds it to the dialog container. + * @param text String containing text to add + * @return a label with the specified text that has word wrap enabled. + */ +/* private Label getDialogLabel(String text) { + // You will need to import `javafx.scene.control.Label`. + Label textToAdd = new Label(text); + textToAdd.setWrapText(true); + + return textToAdd; + } +*/ /** + * Iteration 2: + * Creates two dialog boxes, one echoing user input and the other containing Duke's reply and then appends them to + * the dialog container. Clears the user input after processing. + */ +/* private void handleUserInput() { + Label userText = new Label(userInput.getText()); + Label dukeText = new Label(getResponse(userInput.getText())); + dialogContainer.getChildren().addAll( + DialogBox.getUserDialog(userText, new ImageView(user)), + DialogBox.getDukeDialog(dukeText, new ImageView(duke)) + ); + userInput.clear(); + } + + /** + * You should have your own function to generate a response to user input. + * Replace this stub with your completed method. + */ + + public String getResponse(String input) { + return "Duke heard: " + input; + } } diff --git a/src/main/java/Launcher.java b/src/main/java/Launcher.java new file mode 100644 index 0000000000..710c207b0f --- /dev/null +++ b/src/main/java/Launcher.java @@ -0,0 +1,12 @@ +package duke; + +import javafx.application.Application; + +/** + * A launcher class to workaround classpath issues. + */ +public class Launcher { + public static void main(String[] args) { + Application.launch(Main.class, args); + } +} diff --git a/src/main/java/Main.java b/src/main/java/Main.java new file mode 100644 index 0000000000..5448233f80 --- /dev/null +++ b/src/main/java/Main.java @@ -0,0 +1,31 @@ +package duke; + +import java.io.IOException; + +import javafx.application.Application; +import javafx.fxml.FXMLLoader; +import javafx.scene.Scene; +import javafx.scene.layout.AnchorPane; +import javafx.stage.Stage; + +/** + * A GUI for Duke using FXML. + */ +public class Main extends Application { + + private Duke duke = new Duke(); + + @Override + public void start(Stage stage) { + try { + FXMLLoader fxmlLoader = new FXMLLoader(Main.class.getResource("/view/MainWindow.fxml")); + AnchorPane ap = fxmlLoader.load(); + Scene scene = new Scene(ap); + stage.setScene(scene); + fxmlLoader.getController().setDuke(duke); + stage.show(); + } catch (IOException e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/src/main/java/MainWindow.java b/src/main/java/MainWindow.java new file mode 100644 index 0000000000..b008c3f9a5 --- /dev/null +++ b/src/main/java/MainWindow.java @@ -0,0 +1,51 @@ +package duke; + +import javafx.fxml.FXML; +import javafx.scene.control.Button; +import javafx.scene.control.ScrollPane; +import javafx.scene.control.TextField; +import javafx.scene.image.Image; +import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.VBox; +/** + * Controller for MainWindow. Provides the layout for the other controls. + */ +public class MainWindow extends AnchorPane { + @FXML + private ScrollPane scrollPane; + @FXML + private VBox dialogContainer; + @FXML + private TextField userInput; + @FXML + private Button sendButton; + + private Duke duke; + + private Image userImage = new Image(this.getClass().getResourceAsStream("/images/DaUser.png")); + private Image dukeImage = new Image(this.getClass().getResourceAsStream("/images/DaDuke.png")); + + @FXML + public void initialize() { + scrollPane.vvalueProperty().bind(dialogContainer.heightProperty()); + } + + public void setDuke(Duke d) { + duke = d; + } + + /** + * Creates two dialog boxes, one echoing user input and the other containing Duke's reply and then appends them to + * the dialog container. Clears the user input after processing. + */ + @FXML + private void handleUserInput() { + String input = userInput.getText(); + String response = duke.getResponse(input); + dialogContainer.getChildren().addAll( + DialogBox.getUserDialog(input, userImage), + DialogBox.getDukeDialog(response, dukeImage) + ); + userInput.clear(); + } +} \ No newline at end of file diff --git a/src/main/resources/images/DaDuke.png b/src/main/resources/images/DaDuke.png new file mode 100644 index 0000000000000000000000000000000000000000..d893658717e29b50b4ceedada235d9f75835a118 GIT binary patch literal 32657 zcmV)5K*_&}P)Zf>Tg`t%N;{PE9Y2%-o7`CdniQG#9szpJX+ zcrVK`zK7pG^!n{1T{qo3os(XNkTqW0IXIfGihv^wpO4cHg6YjrzS7VYZJZVWev% z!fK9RcD(ntf zCxMCmC7oRZecuOe6J~p&f5@?I;U2zE;O2T?Vy_8UZ1S4i=b>l%q0jeYAI+i(>K zsIun-!&|SPodfa+5u7Z5yhtd`XRjxuF+Ww~1H4xg)(?GXYLo{q2VPiixh(3*`0iyFHj`V{p-_+Q4jWKC=o6ohr zW)q;Nb7IxtY}qD4RyBpAMv`rR!YytU)sgEIa8F)wX(O<=RwjYY+sLdy0`5o0>=a zd(GePvuhIc}2|`2Y7OgN%4u~XV>q*3e$65qJ;}bK0Li-Wh%~iQn_FhV_*O6|& z$E%S!>BN;~l+|jbw5B2<783HDuPYE-T1Z)&EZTFhZGIj)nseO*|7}7`Vz4%vMl&l& z!c*D(>n3|ulhD`D1FzG)Tik79GgrpJ#HE0O)y{gM#c%L1=xtu`A<9oj(;}Q6cpHb= zIjOSNTO3vD|G|N_tH+^nj?I`D4+@;j;9`s4!*lY>X2_|khHWO LGkWs%c&z?S( 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 z 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 KMNUMnLSTa5`eeEQ literal 0 HcmV?d00001 diff --git a/src/main/resources/view/DialogBox.fxml b/src/main/resources/view/DialogBox.fxml new file mode 100644 index 0000000000..e433809947 --- /dev/null +++ b/src/main/resources/view/DialogBox.fxml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml new file mode 100644 index 0000000000..832f84382e --- /dev/null +++ b/src/main/resources/view/MainWindow.fxml @@ -0,0 +1,19 @@ + + + + + + + + + + + +