From d8398594b7bc43da5eb865321c5a50cec4b3923d Mon Sep 17 00:00:00 2001 From: damithc <damithch@damithch-mbp.comp.nus.edu.sg> Date: Mon, 25 May 2020 00:58:18 +0800 Subject: [PATCH 01/47] 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..20c0521cc7 --- /dev/null +++ b/build.gradle @@ -0,0 +1,46 @@ +plugins { + id 'java' + id 'application' + id 'checkstyle' + id 'com.github.johnrengelman.shadow' version '5.1.0' +} + +repositories { + mavenCentral() +} + +dependencies { + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.5.0' + testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.5.0' +} + +test { + useJUnitPlatform() + + testLogging { + events "passed", "skipped", "failed" + + showExceptions true + exceptionFormat "full" + showCauses true + showStackTraces true + showStandardStreams = false + } +} + +application { + mainClassName = "seedu.duke.Duke" +} + +shadowJar { + archiveBaseName = "duke" + archiveClassifier = null +} + +checkstyle { + toolVersion = '8.29' +} + +run{ + standardInput = System.in +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..f3d88b1c2faf2fc91d853cd5d4242b5547257070 GIT binary patch literal 58695 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 literal 0 HcmV?d00001 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 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 487213cece3e4879e7a973b1526ef5ab3846184b Mon Sep 17 00:00:00 2001 From: brian16600 <brian974902@gmail.com> Date: Thu, 20 Jan 2022 03:22:36 +0800 Subject: [PATCH 02/47] Level-1 --- src/main/java/Duke.java | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 5d313334cc..760527bf8b 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -1,3 +1,5 @@ +import java.util.Scanner; //import Scanner + public class Duke { public static void main(String[] args) { String logo = " ____ _ \n" @@ -5,6 +7,27 @@ public static void main(String[] args) { + "| | | | | | | |/ / _ \\\n" + "| |_| | |_| | < __/\n" + "|____/ \\__,_|_|\\_\\___|\n"; - System.out.println("Hello from\n" + logo); + String dukeGreeting = "Hello! I'm Duke \nWhat can I do for you?"; + boolean endChat = false; + String endMessage = "Bye. Hope to see you again soon!"; + Scanner sc = new Scanner(System.in); + System.out.println(dukeGreeting); + while (!endChat) { + String userResponse = sc.nextLine(); + endChat = levelOneRespond(userResponse); + } + System.out.println(endMessage); + + + } + public static boolean levelOneRespond (String input) { + String bye = "bye"; + String dukeMessage = "Duke: "; + if (!input.equals(bye)) { + System.out.println(dukeMessage + input); + return false; + } else { + return true; + } } } From 63bd8fe5fa1e35941f97c56157ed342ccf82131e Mon Sep 17 00:00:00 2001 From: brian16600 <brian974902@gmail.com> Date: Thu, 20 Jan 2022 11:34:59 +0800 Subject: [PATCH 03/47] Level-2. Changed the return values of levelOneRespond and implemented levelTwoRespond --- src/main/java/Duke.java | 47 ++++++++++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 760527bf8b..252635a3c0 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -1,4 +1,5 @@ import java.util.Scanner; //import Scanner +import java.util.ArrayList; //import ArrayList public class Duke { public static void main(String[] args) { @@ -10,24 +11,54 @@ public static void main(String[] args) { String dukeGreeting = "Hello! I'm Duke \nWhat can I do for you?"; boolean endChat = false; String endMessage = "Bye. Hope to see you again soon!"; - Scanner sc = new Scanner(System.in); + + //arraylist to store the list of tasks + ArrayList<String> arrayLst = new ArrayList<>(); + System.out.println(dukeGreeting); - while (!endChat) { - String userResponse = sc.nextLine(); - endChat = levelOneRespond(userResponse); - } + levelTwoRespond(arrayLst); System.out.println(endMessage); } - public static boolean levelOneRespond (String input) { + + /** + * Method that handles the response for Level-1. + */ + public static void levelOneRespond () { + Scanner sc = new Scanner(System.in); String bye = "bye"; String dukeMessage = "Duke: "; + String endMessage = "Bye. Hope to see you again soon!"; + String input = sc.nextLine(); if (!input.equals(bye)) { System.out.println(dukeMessage + input); - return false; + levelOneRespond(); + } + } + + /** + * Method that handles the response for Level-2. Includes Add and List + * @param arrayLst arraylist that stores the String entries for add and can be listed out. + */ + public static void levelTwoRespond (ArrayList<String> arrayLst) { + Scanner sc = new Scanner(System.in); + String bye = "bye"; + String lst = "list"; + String input = sc.nextLine(); + if (input.equals(bye)); + else if (input.equals(lst)) { + int i = 0; + for (String item : arrayLst) { + i += 1; + System.out.println(i + ". " + item); + } + levelTwoRespond(arrayLst); } else { - return true; + String added = "added: "; + arrayLst.add(input); + System.out.println(added + input); + levelTwoRespond(arrayLst); } } } From 63b73848fb4f40c66524d6d108021c2dbd1a3108 Mon Sep 17 00:00:00 2001 From: brian16600 <brian974902@gmail.com> Date: Thu, 20 Jan 2022 20:03:45 +0800 Subject: [PATCH 04/47] Level-3 --- src/main/java/Duke.java | 86 +++++++++++++++++++++++++++++++++++------ 1 file changed, 75 insertions(+), 11 deletions(-) diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 252635a3c0..a5a7c0c350 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -13,10 +13,10 @@ public static void main(String[] args) { String endMessage = "Bye. Hope to see you again soon!"; //arraylist to store the list of tasks - ArrayList<String> arrayLst = new ArrayList<>(); + ArrayList<Task> arrayLst = new ArrayList<>(); System.out.println(dukeGreeting); - levelTwoRespond(arrayLst); + levelThreeRespond(arrayLst); System.out.println(endMessage); @@ -38,27 +38,91 @@ public static void levelOneRespond () { } /** - * Method that handles the response for Level-2. Includes Add and List - * @param arrayLst arraylist that stores the String entries for add and can be listed out. + * Method that handles the response for Level-3. Building on level 2, it adds the ability to mark and unmark tasks as well as different representation for Tasks. Furthermore, + * the arraylist is changed to contain Task objects + * @param arrayLst arraylist that stores the Task entries for add and can be listed out. */ - public static void levelTwoRespond (ArrayList<String> arrayLst) { + public static void levelThreeRespond (ArrayList<Task> arrayLst) { Scanner sc = new Scanner(System.in); String bye = "bye"; String lst = "list"; + String markCommand = "mark"; + String unmarkCommand = "unmark"; + String tasksInList = "Here are the tasks in your list:\n"; String input = sc.nextLine(); - if (input.equals(bye)); + + if (input.equals(bye)); //ends recursive loop, causing the bye statement in main to be executed else if (input.equals(lst)) { + System.out.println(tasksInList); int i = 0; - for (String item : arrayLst) { + for (Task item : arrayLst) { i += 1; - System.out.println(i + ". " + item); + if (item.isMark()) { + System.out.println(i + ". " + item); + } else { + System.out.println(i + ". " + item); + } } - levelTwoRespond(arrayLst); + levelThreeRespond(arrayLst); + + } else if (input.contains(unmarkCommand)) { + String stringIdx = input.split(" ")[1]; + int idx = Integer.parseInt(stringIdx) - 1; + Task unmarkTask = arrayLst.get(idx); + unmarkTask.unmarkTask(); + levelThreeRespond(arrayLst); + + } else if (input.contains(markCommand)){ + String stringIdx = input.split(" ")[1]; + int idx = Integer.parseInt(stringIdx) - 1; + Task markTask = arrayLst.get(idx); + markTask.markTask(); + levelThreeRespond(arrayLst); + } else { String added = "added: "; - arrayLst.add(input); + Task newTask = new Task(input); + arrayLst.add(newTask); System.out.println(added + input); - levelTwoRespond(arrayLst); + levelThreeRespond(arrayLst); + } + } + + /** + * Represents a Task. Contains a Task constructor, two methods to mark and unmark tasks, toString() method as well as a isMark() method to check if Task is marked + */ + + public static class Task { + private boolean mark; + public String name; + + public Task (String name) { + this.name = name; + this.mark = false; + } + + public void markTask () { + String markedMessage = "Nice! I've marked this task as done:\n"; + this.mark = true; + System.out.println(markedMessage + " " + this); + } + public void unmarkTask() { + String unmarkedMessage = "OK, I've marked this task as not done yet:\n"; + this.mark = false; + System.out.println(unmarkedMessage + " " + this); + } + public boolean isMark() { + return this.mark; + } + public String toString() { + if (this.mark) { + String marked = "[X] "; + return marked + this.name; + } else { + String unmarked = "[ ] "; + return unmarked + this.name; + } } } } + From 5188549336628de1e81b5f8dc25f967be078b8dd Mon Sep 17 00:00:00 2001 From: brian16600 <brian974902@gmail.com> Date: Fri, 21 Jan 2022 00:34:09 +0800 Subject: [PATCH 05/47] Level-3 --- src/main/java/Duke.java | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index a5a7c0c350..8752cc9c23 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -52,6 +52,8 @@ public static void levelThreeRespond (ArrayList<Task> arrayLst) { String input = sc.nextLine(); if (input.equals(bye)); //ends recursive loop, causing the bye statement in main to be executed + + //list command else if (input.equals(lst)) { System.out.println(tasksInList); int i = 0; @@ -64,22 +66,21 @@ else if (input.equals(lst)) { } } levelThreeRespond(arrayLst); - - } else if (input.contains(unmarkCommand)) { + } else if (input.contains(unmarkCommand)) { //unmark command String stringIdx = input.split(" ")[1]; int idx = Integer.parseInt(stringIdx) - 1; Task unmarkTask = arrayLst.get(idx); unmarkTask.unmarkTask(); levelThreeRespond(arrayLst); - } else if (input.contains(markCommand)){ + } else if (input.contains(markCommand)){ //mark command String stringIdx = input.split(" ")[1]; int idx = Integer.parseInt(stringIdx) - 1; Task markTask = arrayLst.get(idx); markTask.markTask(); levelThreeRespond(arrayLst); - } else { + } else { //add task String added = "added: "; Task newTask = new Task(input); arrayLst.add(newTask); @@ -96,24 +97,45 @@ public static class Task { private boolean mark; public String name; + /** + * Constructor + * @param name name of the task + */ public Task (String name) { this.name = name; this.mark = false; } + /** + * markTask as done + */ public void markTask () { String markedMessage = "Nice! I've marked this task as done:\n"; this.mark = true; System.out.println(markedMessage + " " + this); } + + /** + * unmarkTask + */ public void unmarkTask() { String unmarkedMessage = "OK, I've marked this task as not done yet:\n"; this.mark = false; System.out.println(unmarkedMessage + " " + this); } + + /** + * + * @return boolean on whether task is marked + */ public boolean isMark() { return this.mark; } + + /** + * + * @return String version of task, with marked and name + */ public String toString() { if (this.mark) { String marked = "[X] "; From 629d77c37e31fb970f268633bc3d7d1875eba78a Mon Sep 17 00:00:00 2001 From: brian16600 <brian974902@gmail.com> Date: Fri, 21 Jan 2022 15:12:26 +0800 Subject: [PATCH 06/47] Level-3 --- src/main/java/Duke.java | 98 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 87 insertions(+), 11 deletions(-) diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 8752cc9c23..c7ba360ed3 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -1,5 +1,6 @@ import java.util.Scanner; //import Scanner import java.util.ArrayList; //import ArrayList +import java.util.Arrays; public class Duke { public static void main(String[] args) { @@ -16,7 +17,7 @@ public static void main(String[] args) { ArrayList<Task> arrayLst = new ArrayList<>(); System.out.println(dukeGreeting); - levelThreeRespond(arrayLst); + levelFourRespond(arrayLst); System.out.println(endMessage); @@ -42,13 +43,16 @@ public static void levelOneRespond () { * the arraylist is changed to contain Task objects * @param arrayLst arraylist that stores the Task entries for add and can be listed out. */ - public static void levelThreeRespond (ArrayList<Task> arrayLst) { + public static void levelFourRespond (ArrayList<Task> arrayLst) { Scanner sc = new Scanner(System.in); String bye = "bye"; String lst = "list"; String markCommand = "mark"; String unmarkCommand = "unmark"; - String tasksInList = "Here are the tasks in your list:\n"; + String todo = "todo"; + String event = "event"; + String deadline = "deadline"; + String tasksInList = "Here are the tasks in your list:"; String input = sc.nextLine(); if (input.equals(bye)); //ends recursive loop, causing the bye statement in main to be executed @@ -65,28 +69,63 @@ else if (input.equals(lst)) { System.out.println(i + ". " + item); } } - levelThreeRespond(arrayLst); + levelFourRespond(arrayLst); } else if (input.contains(unmarkCommand)) { //unmark command String stringIdx = input.split(" ")[1]; int idx = Integer.parseInt(stringIdx) - 1; Task unmarkTask = arrayLst.get(idx); unmarkTask.unmarkTask(); - levelThreeRespond(arrayLst); + levelFourRespond(arrayLst); } else if (input.contains(markCommand)){ //mark command String stringIdx = input.split(" ")[1]; int idx = Integer.parseInt(stringIdx) - 1; Task markTask = arrayLst.get(idx); markTask.markTask(); - levelThreeRespond(arrayLst); - - } else { //add task + levelFourRespond(arrayLst); + + } else if (input.contains(todo)) { + String[] stringArray = input.split(" "); + String[] nameArray = Arrays.copyOfRange(stringArray, 1, stringArray.length); + String name = String.join(" ", nameArray); + Todo newTodo = new Todo(name); + arrayLst.add(newTodo); + String todoMessage = "Got it. I've added this task:\n" + newTodo + "\nNow you have " + arrayLst.size() + " tasks in the list."; + System.out.println(todoMessage); + levelFourRespond(arrayLst); + } else if (input.contains(event)) { + String[] stringArrayIncludingEvent = input.split(" "); + String[] stringArrayExcludingEvent = Arrays.copyOfRange(stringArrayIncludingEvent, 1, stringArrayIncludingEvent.length); + String stringExcludingEvent = String.join(" ", stringArrayExcludingEvent); + String[] nameAndTimeArray = stringExcludingEvent.split("/at"); + String name = nameAndTimeArray[0]; + String time = nameAndTimeArray[1]; + Event newEvent = new Event(name, time); + arrayLst.add(newEvent); + String eventMessage = "Got it. I've added this task:\n" + newEvent + "\nNow you have " + arrayLst.size() + " tasks in the list."; + System.out.println(eventMessage); + levelFourRespond(arrayLst); + } else if (input.contains(deadline)) { + String[] stringArrayIncludingDeadline = input.split(" "); + String[] stringArrayExcludingDeadline = Arrays.copyOfRange(stringArrayIncludingDeadline, 1, stringArrayIncludingDeadline.length); + String stringExcludingDeadline = String.join(" ", stringArrayExcludingDeadline); + String[] nameAndTimeArray = stringExcludingDeadline.split("/by"); + String name = nameAndTimeArray[0]; + String time = nameAndTimeArray[1]; + Deadline newDeadline = new Deadline(name, time); + arrayLst.add(newDeadline); + String deadlineMessage = "Got it. I've added this task:\n" + newDeadline + "\nNow you have " + arrayLst.size() + " tasks in the list."; + System.out.println(deadlineMessage); + levelFourRespond(arrayLst); + } + else { //add task String added = "added: "; Task newTask = new Task(input); arrayLst.add(newTask); System.out.println(added + input); - levelThreeRespond(arrayLst); + levelFourRespond(arrayLst); } + } /** @@ -133,8 +172,8 @@ public boolean isMark() { } /** - * - * @return String version of task, with marked and name + * @override + * @return String version of task, with marked and name. E.g. [X] Task */ public String toString() { if (this.mark) { @@ -146,5 +185,42 @@ public String toString() { } } } + + public static class Todo extends Task { + public Todo (String name) { + super(name); + } + + /** + * @override + * @return String of Todo task, eg: [T][X] Todo + */ + public String toString() { + return "[T]" + super.toString(); + } + } + + public static class Event extends Task { + private static String dueDate; + public Event (String name, String time) { super(name); this.dueDate = time;} + + /** + * @override + * @return String of Event task, eg: [E][X] Event + */ + public String toString() { return "[E]" + super.toString() + "(at:" + this.dueDate + ")"; } + } + + public static class Deadline extends Task { + private static String dueDate; + public Deadline(String name, String time) {super(name); this.dueDate = time;} + + /** + * @override + * @return String of Deadline task, eg [D][X] Deadline (by:XX) + */ + + public String toString() { return "[D]" + super.toString() + "(by:" + this.dueDate + ")"; } + } } From 62a2177f7cd01dccdfa4bd50d07d7373222a2b23 Mon Sep 17 00:00:00 2001 From: brian16600 <brian974902@gmail.com> Date: Wed, 26 Jan 2022 09:29:29 +0800 Subject: [PATCH 07/47] Level-4. Added Event, Deadline and Todo subclasses of Task. Updated if-else in levelFourRespond to handle the 3 subclasses. --- src/main/java/Deadline.java | 20 ++++++ src/main/java/Duke.java | 119 +++++++----------------------------- src/main/java/Event.java | 18 ++++++ src/main/java/Task.java | 60 ++++++++++++++++++ src/main/java/Todo.java | 22 +++++++ 5 files changed, 141 insertions(+), 98 deletions(-) create mode 100644 src/main/java/Deadline.java create mode 100644 src/main/java/Event.java create mode 100644 src/main/java/Task.java create mode 100644 src/main/java/Todo.java diff --git a/src/main/java/Deadline.java b/src/main/java/Deadline.java new file mode 100644 index 0000000000..1fb1752801 --- /dev/null +++ b/src/main/java/Deadline.java @@ -0,0 +1,20 @@ +import java.util.Scanner; +import java.util.ArrayList; +import java.util.Arrays; + + +/** + * Represents a Deadline which is a subclass of Task + * Includes a dueDate attribute. Overrides toString() from Task + */ +public class Deadline extends Task { + private String dueDate; + public Deadline(String name, String time) {super(name); this.dueDate = time;} + + /** + * @override + * @return String of Deadline task, eg [D][X] Deadline (by:XX) + */ + + public String toString() { return "[D]" + super.toString() + "(by:" + this.dueDate + ")"; } +} \ No newline at end of file diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index c7ba360ed3..1c60f909da 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -1,6 +1,6 @@ import java.util.Scanner; //import Scanner import java.util.ArrayList; //import ArrayList -import java.util.Arrays; +import java.util.Arrays; //import Arrays public class Duke { public static void main(String[] args) { @@ -26,7 +26,7 @@ public static void main(String[] args) { /** * Method that handles the response for Level-1. */ - public static void levelOneRespond () { + public static void levelOneRespond() { Scanner sc = new Scanner(System.in); String bye = "bye"; String dukeMessage = "Duke: "; @@ -39,11 +39,14 @@ public static void levelOneRespond () { } /** - * Method that handles the response for Level-3. Building on level 2, it adds the ability to mark and unmark tasks as well as different representation for Tasks. Furthermore, + * Method that handles the response for Level-4. + * Level 3:added the ability to mark and unmark tasks as well as different representation for Tasks. Furthermore, * the arraylist is changed to contain Task objects + * Level 4: Added Todo, Event and Deadline subclasses to be detected and handled in each seperate case + * * @param arrayLst arraylist that stores the Task entries for add and can be listed out. */ - public static void levelFourRespond (ArrayList<Task> arrayLst) { + public static void levelFourRespond(ArrayList<Task> arrayLst) { Scanner sc = new Scanner(System.in); String bye = "bye"; String lst = "list"; @@ -55,9 +58,9 @@ public static void levelFourRespond (ArrayList<Task> arrayLst) { String tasksInList = "Here are the tasks in your list:"; String input = sc.nextLine(); - if (input.equals(bye)); //ends recursive loop, causing the bye statement in main to be executed + if (input.equals(bye)) ; //ends recursive loop, causing the bye statement in main to be executed - //list command + //list command else if (input.equals(lst)) { System.out.println(tasksInList); int i = 0; @@ -70,20 +73,24 @@ else if (input.equals(lst)) { } } levelFourRespond(arrayLst); + + //unmark } else if (input.contains(unmarkCommand)) { //unmark command String stringIdx = input.split(" ")[1]; - int idx = Integer.parseInt(stringIdx) - 1; + int idx = Integer.parseInt(stringIdx) - 1; Task unmarkTask = arrayLst.get(idx); unmarkTask.unmarkTask(); levelFourRespond(arrayLst); - } else if (input.contains(markCommand)){ //mark command + //mark + } else if (input.contains(markCommand)) { //mark command String stringIdx = input.split(" ")[1]; int idx = Integer.parseInt(stringIdx) - 1; Task markTask = arrayLst.get(idx); markTask.markTask(); levelFourRespond(arrayLst); + //Todo } else if (input.contains(todo)) { String[] stringArray = input.split(" "); String[] nameArray = Arrays.copyOfRange(stringArray, 1, stringArray.length); @@ -93,6 +100,8 @@ else if (input.equals(lst)) { String todoMessage = "Got it. I've added this task:\n" + newTodo + "\nNow you have " + arrayLst.size() + " tasks in the list."; System.out.println(todoMessage); levelFourRespond(arrayLst); + + //Event } else if (input.contains(event)) { String[] stringArrayIncludingEvent = input.split(" "); String[] stringArrayExcludingEvent = Arrays.copyOfRange(stringArrayIncludingEvent, 1, stringArrayIncludingEvent.length); @@ -105,6 +114,8 @@ else if (input.equals(lst)) { String eventMessage = "Got it. I've added this task:\n" + newEvent + "\nNow you have " + arrayLst.size() + " tasks in the list."; System.out.println(eventMessage); levelFourRespond(arrayLst); + + //Deadline } else if (input.contains(deadline)) { String[] stringArrayIncludingDeadline = input.split(" "); String[] stringArrayExcludingDeadline = Arrays.copyOfRange(stringArrayIncludingDeadline, 1, stringArrayIncludingDeadline.length); @@ -117,8 +128,7 @@ else if (input.equals(lst)) { String deadlineMessage = "Got it. I've added this task:\n" + newDeadline + "\nNow you have " + arrayLst.size() + " tasks in the list."; System.out.println(deadlineMessage); levelFourRespond(arrayLst); - } - else { //add task + } else { //add task String added = "added: "; Task newTask = new Task(input); arrayLst.add(newTask); @@ -127,100 +137,13 @@ else if (input.equals(lst)) { } } +} - /** - * Represents a Task. Contains a Task constructor, two methods to mark and unmark tasks, toString() method as well as a isMark() method to check if Task is marked - */ - - public static class Task { - private boolean mark; - public String name; - - /** - * Constructor - * @param name name of the task - */ - public Task (String name) { - this.name = name; - this.mark = false; - } - - /** - * markTask as done - */ - public void markTask () { - String markedMessage = "Nice! I've marked this task as done:\n"; - this.mark = true; - System.out.println(markedMessage + " " + this); - } - - /** - * unmarkTask - */ - public void unmarkTask() { - String unmarkedMessage = "OK, I've marked this task as not done yet:\n"; - this.mark = false; - System.out.println(unmarkedMessage + " " + this); - } - - /** - * - * @return boolean on whether task is marked - */ - public boolean isMark() { - return this.mark; - } - /** - * @override - * @return String version of task, with marked and name. E.g. [X] Task - */ - public String toString() { - if (this.mark) { - String marked = "[X] "; - return marked + this.name; - } else { - String unmarked = "[ ] "; - return unmarked + this.name; - } - } - } - public static class Todo extends Task { - public Todo (String name) { - super(name); - } - /** - * @override - * @return String of Todo task, eg: [T][X] Todo - */ - public String toString() { - return "[T]" + super.toString(); - } - } - - public static class Event extends Task { - private static String dueDate; - public Event (String name, String time) { super(name); this.dueDate = time;} - /** - * @override - * @return String of Event task, eg: [E][X] Event - */ - public String toString() { return "[E]" + super.toString() + "(at:" + this.dueDate + ")"; } - } - public static class Deadline extends Task { - private static String dueDate; - public Deadline(String name, String time) {super(name); this.dueDate = time;} - /** - * @override - * @return String of Deadline task, eg [D][X] Deadline (by:XX) - */ - public String toString() { return "[D]" + super.toString() + "(by:" + this.dueDate + ")"; } - } -} diff --git a/src/main/java/Event.java b/src/main/java/Event.java new file mode 100644 index 0000000000..3f9375e89b --- /dev/null +++ b/src/main/java/Event.java @@ -0,0 +1,18 @@ +import java.util.Scanner; +import java.util.ArrayList; +import java.util.Arrays; + +/** + * Represents a Event which is a subclass of Task + * Overrides toString() from Task + */ +public class Event extends Task { + private String dueDate; + public Event (String name, String time) { super(name); this.dueDate = time;} + + /** + * @override + * @return String of Event task, eg: [E][X] Event + */ + public String toString() { return "[E]" + super.toString() + "(at:" + this.dueDate + ")"; } +} \ 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..576ac76756 --- /dev/null +++ b/src/main/java/Task.java @@ -0,0 +1,60 @@ +import java.util.Scanner; +import java.util.ArrayList; +import java.util.Arrays; + +/** + * Represents a Task. Contains a Task constructor, two methods to mark and unmark tasks, toString() method as well as a isMark() method to check if Task is marked + */ +public class Task { + private boolean mark; + public String name; + + /** + * Constructor + * @param name name of the task + */ + public Task (String name) { + this.name = name; + this.mark = false; + } + + /** + * markTask as done + */ + public void markTask () { + String markedMessage = "Nice! I've marked this task as done:\n"; + this.mark = true; + System.out.println(markedMessage + " " + this); + } + + /** + * unmarkTask + */ + public void unmarkTask() { + String unmarkedMessage = "OK, I've marked this task as not done yet:\n"; + this.mark = false; + System.out.println(unmarkedMessage + " " + this); + } + + /** + * + * @return boolean on whether task is marked + */ + public boolean isMark() { + return this.mark; + } + + /** + * @override + * @return String version of task, with marked and name. E.g. [X] Task + */ + public String toString() { + if (this.mark) { + String marked = "[X] "; + return marked + this.name; + } else { + String unmarked = "[ ] "; + return unmarked + this.name; + } + } +} \ No newline at end of file diff --git a/src/main/java/Todo.java b/src/main/java/Todo.java new file mode 100644 index 0000000000..90e54c56ed --- /dev/null +++ b/src/main/java/Todo.java @@ -0,0 +1,22 @@ +import java.util.Scanner; +import java.util.ArrayList; +import java.util.Arrays; + +/** + * Represents a Todo which is a subclass of Task + * Includes a dueDate attribute. Overrides toString() from Task + */ + +public class Todo extends Task { + public Todo (String name) { + super(name); + } + + /** + * @override + * @return String of Todo task, eg: [T][X] Todo + */ + public String toString() { + return "[T]" + super.toString(); + } +} \ No newline at end of file From 49d9e0a907e3748dffcf5efcc94e1697eedb7c63 Mon Sep 17 00:00:00 2001 From: brian16600 <brian974902@gmail.com> Date: Sat, 29 Jan 2022 16:46:13 +0800 Subject: [PATCH 08/47] Level-5. Created DukeException class to handle incorrect input length/commands. Shifted if-else loop to a switch-case in InputHandler.java, --- src/main/java/Duke.java | 129 +++---------------------------- src/main/java/DukeException.java | 12 +++ src/main/java/InputHandler.java | 116 +++++++++++++++++++++++++++ src/main/java/Todo.java | 1 + 4 files changed, 139 insertions(+), 119 deletions(-) create mode 100644 src/main/java/DukeException.java create mode 100644 src/main/java/InputHandler.java diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 1c60f909da..e62eb4d75e 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -10,133 +10,24 @@ public static void main(String[] args) { + "| |_| | |_| | < __/\n" + "|____/ \\__,_|_|\\_\\___|\n"; String dukeGreeting = "Hello! I'm Duke \nWhat can I do for you?"; - boolean endChat = false; String endMessage = "Bye. Hope to see you again soon!"; - //arraylist to store the list of tasks - ArrayList<Task> arrayLst = new ArrayList<>(); - System.out.println(dukeGreeting); - levelFourRespond(arrayLst); - System.out.println(endMessage); - - - } - - /** - * Method that handles the response for Level-1. - */ - public static void levelOneRespond() { Scanner sc = new Scanner(System.in); - String bye = "bye"; - String dukeMessage = "Duke: "; - String endMessage = "Bye. Hope to see you again soon!"; - String input = sc.nextLine(); - if (!input.equals(bye)) { - System.out.println(dukeMessage + input); - levelOneRespond(); + InputHandler inputHandler = new InputHandler(); + boolean isChatEnded = false; + while (!isChatEnded) { + try { + String input = sc.nextLine(); + isChatEnded = inputHandler.handleInput(input); + } catch (DukeException e) { + System.out.println(e.getMessage()); + } } + System.out.println(endMessage); } - /** - * Method that handles the response for Level-4. - * Level 3:added the ability to mark and unmark tasks as well as different representation for Tasks. Furthermore, - * the arraylist is changed to contain Task objects - * Level 4: Added Todo, Event and Deadline subclasses to be detected and handled in each seperate case - * - * @param arrayLst arraylist that stores the Task entries for add and can be listed out. - */ - public static void levelFourRespond(ArrayList<Task> arrayLst) { - Scanner sc = new Scanner(System.in); - String bye = "bye"; - String lst = "list"; - String markCommand = "mark"; - String unmarkCommand = "unmark"; - String todo = "todo"; - String event = "event"; - String deadline = "deadline"; - String tasksInList = "Here are the tasks in your list:"; - String input = sc.nextLine(); - - if (input.equals(bye)) ; //ends recursive loop, causing the bye statement in main to be executed - - //list command - else if (input.equals(lst)) { - System.out.println(tasksInList); - int i = 0; - for (Task item : arrayLst) { - i += 1; - if (item.isMark()) { - System.out.println(i + ". " + item); - } else { - System.out.println(i + ". " + item); - } - } - levelFourRespond(arrayLst); - - //unmark - } else if (input.contains(unmarkCommand)) { //unmark command - String stringIdx = input.split(" ")[1]; - int idx = Integer.parseInt(stringIdx) - 1; - Task unmarkTask = arrayLst.get(idx); - unmarkTask.unmarkTask(); - levelFourRespond(arrayLst); - - //mark - } else if (input.contains(markCommand)) { //mark command - String stringIdx = input.split(" ")[1]; - int idx = Integer.parseInt(stringIdx) - 1; - Task markTask = arrayLst.get(idx); - markTask.markTask(); - levelFourRespond(arrayLst); - //Todo - } else if (input.contains(todo)) { - String[] stringArray = input.split(" "); - String[] nameArray = Arrays.copyOfRange(stringArray, 1, stringArray.length); - String name = String.join(" ", nameArray); - Todo newTodo = new Todo(name); - arrayLst.add(newTodo); - String todoMessage = "Got it. I've added this task:\n" + newTodo + "\nNow you have " + arrayLst.size() + " tasks in the list."; - System.out.println(todoMessage); - levelFourRespond(arrayLst); - - //Event - } else if (input.contains(event)) { - String[] stringArrayIncludingEvent = input.split(" "); - String[] stringArrayExcludingEvent = Arrays.copyOfRange(stringArrayIncludingEvent, 1, stringArrayIncludingEvent.length); - String stringExcludingEvent = String.join(" ", stringArrayExcludingEvent); - String[] nameAndTimeArray = stringExcludingEvent.split("/at"); - String name = nameAndTimeArray[0]; - String time = nameAndTimeArray[1]; - Event newEvent = new Event(name, time); - arrayLst.add(newEvent); - String eventMessage = "Got it. I've added this task:\n" + newEvent + "\nNow you have " + arrayLst.size() + " tasks in the list."; - System.out.println(eventMessage); - levelFourRespond(arrayLst); - - //Deadline - } else if (input.contains(deadline)) { - String[] stringArrayIncludingDeadline = input.split(" "); - String[] stringArrayExcludingDeadline = Arrays.copyOfRange(stringArrayIncludingDeadline, 1, stringArrayIncludingDeadline.length); - String stringExcludingDeadline = String.join(" ", stringArrayExcludingDeadline); - String[] nameAndTimeArray = stringExcludingDeadline.split("/by"); - String name = nameAndTimeArray[0]; - String time = nameAndTimeArray[1]; - Deadline newDeadline = new Deadline(name, time); - arrayLst.add(newDeadline); - String deadlineMessage = "Got it. I've added this task:\n" + newDeadline + "\nNow you have " + arrayLst.size() + " tasks in the list."; - System.out.println(deadlineMessage); - levelFourRespond(arrayLst); - } else { //add task - String added = "added: "; - Task newTask = new Task(input); - arrayLst.add(newTask); - System.out.println(added + input); - levelFourRespond(arrayLst); - } - - } } diff --git a/src/main/java/DukeException.java b/src/main/java/DukeException.java new file mode 100644 index 0000000000..5ff77f264d --- /dev/null +++ b/src/main/java/DukeException.java @@ -0,0 +1,12 @@ +public class DukeException extends Exception{ + private String errorMessage; + public DukeException (String errorMessage) { + this.errorMessage = errorMessage; + } + + /** + * @override + * @return String errorMessage + */ + public String getMessage() {return this.errorMessage;} +} diff --git a/src/main/java/InputHandler.java b/src/main/java/InputHandler.java new file mode 100644 index 0000000000..cd2ae2d506 --- /dev/null +++ b/src/main/java/InputHandler.java @@ -0,0 +1,116 @@ +import java.util.ArrayList; +import java.util.Arrays; + +/** + * Handles input in main in Duke.java. Receives String from Scanner in main + * Processes the input into 7 categories: Todo, Event, Deadline, list, mark, unmark, bye and throws error + */ +public class InputHandler { + private ArrayList<Task> arr; + public InputHandler() { + this.arr = new ArrayList<>(); + } + + + /** + * + * @param input String input from main in Duke.java, from user input + * @return boolean representing isChatEnded variable in main. If "bye" command is given, a true boolean is returned, else false is returned + * @throws DukeException Invalid input types, or unrecognisable commands + */ + public boolean handleInput(String input) throws DukeException { + String[] splitInput = input.split(" "); + String inputCommand = splitInput[0]; + switch (inputCommand) { + case "todo": + if (splitInput.length > 1) { + String[] nameArray = Arrays.copyOfRange(splitInput, 1, splitInput.length); + String name = String.join(" ", nameArray); + Todo newTodo = new Todo(name); + arr.add(newTodo); + printMessage(newTodo); + return false; + } else { + throw new DukeException(":( OOPS!!! The description of a todo cannot be empty."); + } + case "event": + if (splitInput.length > 3) { + String[] stringArrayExcludingEvent = Arrays.copyOfRange(splitInput, 1, splitInput.length); + String stringExcludingEvent = String.join(" ", stringArrayExcludingEvent); + String[] nameAndTimeArray = stringExcludingEvent.split("/at"); + String name = nameAndTimeArray[0]; + String time = nameAndTimeArray[1]; + Event newEvent = new Event(name, time); + arr.add(newEvent); + printMessage(newEvent); + return false; + } else { + throw new DukeException(":( OOPS!!! The description of a todo cannot be empty."); + } + case "deadline": + if (splitInput.length > 3) { + String[] stringArrayExcludingDeadline = Arrays.copyOfRange(splitInput, 1, splitInput.length); + String stringExcludingDeadline = String.join(" ", stringArrayExcludingDeadline); + String[] nameAndTimeArray = stringExcludingDeadline.split("/by"); + String name = nameAndTimeArray[0]; + String time = nameAndTimeArray[1]; + Deadline newDeadline = new Deadline(name, time); + arr.add(newDeadline); + printMessage(newDeadline); + return false; + } else { + throw new DukeException(":( OOPS!!! The description of a todo cannot be empty."); + } + case "list": + if (splitInput.length == 1) { + System.out.println("Here are the tasks in your list:"); + int i = 0; + for (Task item : arr) { + i += 1; + if (item.isMark()) { + System.out.println(i + ". " + item); + } else { + System.out.println(i + ". " + item); + } + } + return false; + } else { + throw new DukeException("Wrong usage of list!"); + } + case "mark": + if (splitInput.length == 2) { + System.out.println("Nice! I've marked this task as done:\n"); + int idx = Integer.parseInt(splitInput[1]) - 1; + Task taskToBeMarked = arr.get(idx); + taskToBeMarked.markTask(); + return false; + } else { + throw new DukeException("Wrong usage of mark!"); + } + case "unmark": + if (splitInput.length == 2) { + System.out.println("OK, I've marked this task as not done yet:\n"); + int idx = Integer.parseInt(splitInput[1]) - 1; + Task taskToBeUnmarked = arr.get(idx); + taskToBeUnmarked.unmarkTask(); + return false; + } else { + throw new DukeException("Wrong usage of unmark!"); + } + case "bye": + return true; + default: + throw new DukeException(":( OOPS!!! I'm sorry, but I don't know what that means :("); + } + + } + + /** + * Prints out the task name that has been added as well as the number of tasks in the list + * @param task The task that has been added + */ + public void printMessage(Task task) { + System.out.println("Got it. I've added this task:\n" + task + "\nNow you have " + arr.size() + " tasks in the list." ); + } +} + diff --git a/src/main/java/Todo.java b/src/main/java/Todo.java index 90e54c56ed..d5f0339cea 100644 --- a/src/main/java/Todo.java +++ b/src/main/java/Todo.java @@ -19,4 +19,5 @@ public Todo (String name) { public String toString() { return "[T]" + super.toString(); } + } \ No newline at end of file From 89c0746e803b244bad1f2e2e427e6c71328fe9fd Mon Sep 17 00:00:00 2001 From: brian16600 <brian974902@gmail.com> Date: Sat, 29 Jan 2022 17:17:43 +0800 Subject: [PATCH 09/47] Level-6 Added recommended inputs for exceptions and added delete case in InputHandler.java. --- src/main/java/InputHandler.java | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/main/java/InputHandler.java b/src/main/java/InputHandler.java index cd2ae2d506..31548be1ae 100644 --- a/src/main/java/InputHandler.java +++ b/src/main/java/InputHandler.java @@ -28,10 +28,10 @@ public boolean handleInput(String input) throws DukeException { String name = String.join(" ", nameArray); Todo newTodo = new Todo(name); arr.add(newTodo); - printMessage(newTodo); + printAddTaskMessage(newTodo); return false; } else { - throw new DukeException(":( OOPS!!! The description of a todo cannot be empty."); + throw new DukeException(":( OOPS!!! The description of a todo cannot be empty. Correct usage: todo [task]"); } case "event": if (splitInput.length > 3) { @@ -42,10 +42,10 @@ public boolean handleInput(String input) throws DukeException { String time = nameAndTimeArray[1]; Event newEvent = new Event(name, time); arr.add(newEvent); - printMessage(newEvent); + printAddTaskMessage(newEvent); return false; } else { - throw new DukeException(":( OOPS!!! The description of a todo cannot be empty."); + throw new DukeException(":( OOPS!!! The description of a event cannot be empty. Correct usage: event [task] /at [time]"); } case "deadline": if (splitInput.length > 3) { @@ -56,10 +56,10 @@ public boolean handleInput(String input) throws DukeException { String time = nameAndTimeArray[1]; Deadline newDeadline = new Deadline(name, time); arr.add(newDeadline); - printMessage(newDeadline); + printAddTaskMessage(newDeadline); return false; } else { - throw new DukeException(":( OOPS!!! The description of a todo cannot be empty."); + throw new DukeException(":( OOPS!!! The description of a deadline cannot be empty. Correct usage: deadline [task] /by [time]"); } case "list": if (splitInput.length == 1) { @@ -75,7 +75,7 @@ public boolean handleInput(String input) throws DukeException { } return false; } else { - throw new DukeException("Wrong usage of list!"); + throw new DukeException("Wrong usage of list! Correct usage: list"); } case "mark": if (splitInput.length == 2) { @@ -85,7 +85,7 @@ public boolean handleInput(String input) throws DukeException { taskToBeMarked.markTask(); return false; } else { - throw new DukeException("Wrong usage of mark!"); + throw new DukeException("Wrong usage of mark! Correct usage: mark [index]"); } case "unmark": if (splitInput.length == 2) { @@ -95,12 +95,23 @@ public boolean handleInput(String input) throws DukeException { taskToBeUnmarked.unmarkTask(); return false; } else { - throw new DukeException("Wrong usage of unmark!"); + throw new DukeException("Wrong usage of unmark! Correct usage: unmark [index]"); + } + case "delete": + if (splitInput.length == 2) { + int idx = Integer.parseInt(splitInput[1]) - 1; + Task taskToBeDeleted = arr.get(idx); + arr.remove(idx); + System.out.println("Noted. I've removed this task:\n" + taskToBeDeleted + "\nNow you have " + arr.size() + " tasks in the list"); + return false; + } else { + throw new DukeException("Wrong usage of delete! Correct usage: delete [index]"); } case "bye": return true; default: - throw new DukeException(":( OOPS!!! I'm sorry, but I don't know what that means :("); + throw new DukeException(":( OOPS!!! I'm sorry, but I don't know what that means! Possible commands: todo [task], event [task] /at [time]," + + " deadline [task] /by [time], mark [index], unmark [index], delete [index], bye"); } } @@ -109,7 +120,7 @@ public boolean handleInput(String input) throws DukeException { * Prints out the task name that has been added as well as the number of tasks in the list * @param task The task that has been added */ - public void printMessage(Task task) { + public void printAddTaskMessage(Task task) { System.out.println("Got it. I've added this task:\n" + task + "\nNow you have " + arr.size() + " tasks in the list." ); } } From 22f31af4e31bc82a11cd6d55b76e7f1dd6be50d6 Mon Sep 17 00:00:00 2001 From: brian16600 <brian974902@gmail.com> Date: Wed, 2 Feb 2022 10:50:30 +0800 Subject: [PATCH 10/47] Level-7 Rewrote code into InputHandler.java. Added Storage.java to handle the storage of tasks as well as writing to data.txt file for saving. --- src/main/java/Deadline.java | 4 +- src/main/java/Duke.java | 3 +- src/main/java/Event.java | 4 +- src/main/java/InputHandler.java | 41 +++++---- src/main/java/Storage.java | 146 ++++++++++++++++++++++++++++++++ src/main/java/Task.java | 6 +- 6 files changed, 175 insertions(+), 29 deletions(-) create mode 100644 src/main/java/Storage.java diff --git a/src/main/java/Deadline.java b/src/main/java/Deadline.java index 1fb1752801..9276cccb8b 100644 --- a/src/main/java/Deadline.java +++ b/src/main/java/Deadline.java @@ -8,12 +8,12 @@ * Includes a dueDate attribute. Overrides toString() from Task */ public class Deadline extends Task { - private String dueDate; + public String dueDate; public Deadline(String name, String time) {super(name); this.dueDate = time;} /** * @override - * @return String of Deadline task, eg [D][X] Deadline (by:XX) + * @return String of Deadline task, eg [D][X] Deadline (by:XX) vs [D][✓] Deadline (by;XX) */ public String toString() { return "[D]" + super.toString() + "(by:" + this.dueDate + ")"; } diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index e62eb4d75e..d626af3dee 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -1,9 +1,10 @@ import java.util.Scanner; //import Scanner import java.util.ArrayList; //import ArrayList import java.util.Arrays; //import Arrays +import java.io.IOException; public class Duke { - public static void main(String[] args) { + public static void main(String[] args) throws IOException { String logo = " ____ _ \n" + "| _ \\ _ _| | _____ \n" + "| | | | | | | |/ / _ \\\n" diff --git a/src/main/java/Event.java b/src/main/java/Event.java index 3f9375e89b..a353c88523 100644 --- a/src/main/java/Event.java +++ b/src/main/java/Event.java @@ -7,12 +7,12 @@ * Overrides toString() from Task */ public class Event extends Task { - private String dueDate; + public String dueDate; public Event (String name, String time) { super(name); this.dueDate = time;} /** * @override - * @return String of Event task, eg: [E][X] Event + * @return String of Event task, eg: [E][X] Event (at:) vs [E] [✓] Event (at:) */ public String toString() { return "[E]" + super.toString() + "(at:" + this.dueDate + ")"; } } \ No newline at end of file diff --git a/src/main/java/InputHandler.java b/src/main/java/InputHandler.java index 31548be1ae..6fa628f88a 100644 --- a/src/main/java/InputHandler.java +++ b/src/main/java/InputHandler.java @@ -1,14 +1,20 @@ import java.util.ArrayList; import java.util.Arrays; +import java.io.IOException; /** * Handles input in main in Duke.java. Receives String from Scanner in main * Processes the input into 7 categories: Todo, Event, Deadline, list, mark, unmark, bye and throws error */ public class InputHandler { - private ArrayList<Task> arr; - public InputHandler() { - this.arr = new ArrayList<>(); + private Storage storage; + + /** + * Constructs an InputHandler for Duke.java to handle inputs from user + * @throws IOException If Storage class fails to initialise + */ + public InputHandler() throws IOException { + this.storage = new Storage(); } @@ -18,7 +24,7 @@ public InputHandler() { * @return boolean representing isChatEnded variable in main. If "bye" command is given, a true boolean is returned, else false is returned * @throws DukeException Invalid input types, or unrecognisable commands */ - public boolean handleInput(String input) throws DukeException { + public boolean handleInput(String input) throws DukeException, IOException { String[] splitInput = input.split(" "); String inputCommand = splitInput[0]; switch (inputCommand) { @@ -27,7 +33,7 @@ public boolean handleInput(String input) throws DukeException { String[] nameArray = Arrays.copyOfRange(splitInput, 1, splitInput.length); String name = String.join(" ", nameArray); Todo newTodo = new Todo(name); - arr.add(newTodo); + this.storage.writeData(newTodo); printAddTaskMessage(newTodo); return false; } else { @@ -41,7 +47,7 @@ public boolean handleInput(String input) throws DukeException { String name = nameAndTimeArray[0]; String time = nameAndTimeArray[1]; Event newEvent = new Event(name, time); - arr.add(newEvent); + this.storage.writeData(newEvent); printAddTaskMessage(newEvent); return false; } else { @@ -55,7 +61,7 @@ public boolean handleInput(String input) throws DukeException { String name = nameAndTimeArray[0]; String time = nameAndTimeArray[1]; Deadline newDeadline = new Deadline(name, time); - arr.add(newDeadline); + this.storage.writeData(newDeadline); printAddTaskMessage(newDeadline); return false; } else { @@ -65,14 +71,7 @@ public boolean handleInput(String input) throws DukeException { if (splitInput.length == 1) { System.out.println("Here are the tasks in your list:"); int i = 0; - for (Task item : arr) { - i += 1; - if (item.isMark()) { - System.out.println(i + ". " + item); - } else { - System.out.println(i + ". " + item); - } - } + System.out.println(this.storage.list()); return false; } else { throw new DukeException("Wrong usage of list! Correct usage: list"); @@ -81,7 +80,7 @@ public boolean handleInput(String input) throws DukeException { if (splitInput.length == 2) { System.out.println("Nice! I've marked this task as done:\n"); int idx = Integer.parseInt(splitInput[1]) - 1; - Task taskToBeMarked = arr.get(idx); + Task taskToBeMarked = this.storage.get(idx); taskToBeMarked.markTask(); return false; } else { @@ -91,7 +90,7 @@ public boolean handleInput(String input) throws DukeException { if (splitInput.length == 2) { System.out.println("OK, I've marked this task as not done yet:\n"); int idx = Integer.parseInt(splitInput[1]) - 1; - Task taskToBeUnmarked = arr.get(idx); + Task taskToBeUnmarked = this.storage.get(idx); taskToBeUnmarked.unmarkTask(); return false; } else { @@ -100,9 +99,9 @@ public boolean handleInput(String input) throws DukeException { case "delete": if (splitInput.length == 2) { int idx = Integer.parseInt(splitInput[1]) - 1; - Task taskToBeDeleted = arr.get(idx); - arr.remove(idx); - System.out.println("Noted. I've removed this task:\n" + taskToBeDeleted + "\nNow you have " + arr.size() + " tasks in the list"); + Task taskToBeDeleted = storage.get(idx); + this.storage.deleteData(idx); + System.out.println("Noted. I've removed this task:\n" + taskToBeDeleted + "\nNow you have " + this.storage.taskListSize() + " tasks in the list"); return false; } else { throw new DukeException("Wrong usage of delete! Correct usage: delete [index]"); @@ -121,7 +120,7 @@ public boolean handleInput(String input) throws DukeException { * @param task The task that has been added */ public void printAddTaskMessage(Task task) { - System.out.println("Got it. I've added this task:\n" + task + "\nNow you have " + arr.size() + " tasks in the list." ); + System.out.println("Got it. I've added this task:\n" + task + "\nNow you have " + this.storage.taskListSize() + " tasks in the list." ); } } diff --git a/src/main/java/Storage.java b/src/main/java/Storage.java new file mode 100644 index 0000000000..bdcbc37270 --- /dev/null +++ b/src/main/java/Storage.java @@ -0,0 +1,146 @@ +import java.util.ArrayList; +import java.io.File; +import java.util.Scanner; +import java.io.IOException; +import java.nio.file.Paths; +import java.nio.file.Files; +import java.nio.file.Path; +import java.io.FileWriter; + +public class Storage { + ArrayList<Task> arr; + String FILEPATH = "data/data.txt"; + + /** + * Constructs a Storage object. Loads the data from data/data.txt. if no data dir or data.txt is found, create an empty one + */ + public Storage() throws IOException { + try { + File dataFile = new File(FILEPATH); + Scanner sc = new Scanner(dataFile); + ArrayList<Task> newArr = new ArrayList<>(); + while (sc.hasNextLine()) { + String nextLine = sc.nextLine(); + String[] nextLineArr = nextLine.split(" "); + String taskType = nextLineArr[0]; + switch (taskType) { + case "[T]": + Todo newTodo = new Todo(nextLineArr[2]); + if (nextLineArr[1].equals("[✓]")) { + newTodo.markTask(); + } + newArr.add(newTodo); + break; + case "[D]": + Deadline newDeadline = new Deadline(nextLineArr[2], nextLineArr[4]); + if (nextLineArr[1].equals("[✓]")) { + newDeadline.markTask(); + } + newArr.add(newDeadline); + break; + case "[E]": + Event newEvent = new Event(nextLineArr[2], nextLineArr[4]); + if (nextLineArr[1].equals("[✓]")) { + newEvent.markTask(); + } + newArr.add(newEvent); + break; + default: + } + } + this.arr = newArr; + } catch (IOException e){ + Path filePath = Paths.get("data"); + boolean dataDirectoryExists = Files.exists(filePath); + if (!dataDirectoryExists) { + new File("data").mkdir(); + } + new File(FILEPATH).createNewFile(); + this.arr = new ArrayList<>(); + } + } + + /** + * + * @param task Converts task to string format for storage in the data.txt file for records + * @return String format of the task eg: [D] [✓] deadline | duedate + */ + public String taskToStringConverter(Task task) { + String output = ""; + if (task instanceof Todo) { + String mark = (task.isMark()) ? "[✓]" : "[X]"; + output = "[T] " + mark + " " + task.name + "\n"; + } else if (task instanceof Deadline deadline) { + String mark = (deadline.isMark()) ? "[✓]" : "[X]"; + output = "[D] " + mark + " " + deadline.name + "|" + deadline.dueDate +"\n"; + } else if (task instanceof Event event) { + String mark = (event.isMark()) ? "[✓]" : "[X]"; + output = "[E] " + mark + " " + event.name + "|" + event.dueDate + "\n"; + } + return output; + } + + /** + * Appends a single task to the file + * @param task task to be added to the data.txt file + * @throws IOException if there is an error appending the task to data.txt + */ + public void writeData(Task task) throws IOException { + this.arr.add(task); + FileWriter fw = new FileWriter(this.FILEPATH, true); + fw.write(taskToStringConverter(task)); + fw.close(); + } + + /** + * Used when delete [index] is called for Duke. Deletes the entire file and rewrites it based on the new array + * Amends the current stored array as well + * @param idx index of task to be deleted + * @throws IOException if there is an error rewriting data.txt + */ + public void deleteData(int idx) throws IOException { + Task taskToBeDeleted = this.arr.get(idx); + arr.remove(idx); + FileWriter fw = new FileWriter(this.FILEPATH); + for (Task task : this.arr) { + fw.write(taskToStringConverter(task)); + } + fw.close(); + } + + /** + * Obtains list of tasks from this.arr and returns it + * @return String that lists out the tasks currently + */ + public String list() { + String listOfTasks = ""; + int i = 0; + for (Task item : this.arr) { + i += 1; + if (item.isMark()) { + listOfTasks += i + ". " + item + "\n"; + } else { + listOfTasks += i + ". " + item + "\n"; + } + } + return listOfTasks; + } + + /** + * + * @param idx index of task to be gotten + * @return task that is requested + */ + public Task get(int idx) { + return this.arr.get(idx); + } + + /** + * + * @return size of task list + */ + public int taskListSize() { + return this.arr.size(); + } + +} diff --git a/src/main/java/Task.java b/src/main/java/Task.java index 576ac76756..74c6e3bf56 100644 --- a/src/main/java/Task.java +++ b/src/main/java/Task.java @@ -46,14 +46,14 @@ public boolean isMark() { /** * @override - * @return String version of task, with marked and name. E.g. [X] Task + * @return String version of task, with marked and name. E.g. [X] Task vs [✓] Task */ public String toString() { if (this.mark) { - String marked = "[X] "; + String marked = "[✓] "; return marked + this.name; } else { - String unmarked = "[ ] "; + String unmarked = "[X] "; return unmarked + this.name; } } From a2b7ffd528cfa5a86f14c6fc9937b4e9dd40c9be Mon Sep 17 00:00:00 2001 From: brian16600 <brian974902@gmail.com> Date: Thu, 3 Feb 2022 11:57:58 +0800 Subject: [PATCH 11/47] Level-8 Changed multiple variable names to better suit their purposes. Added ability to take in specific date/time for Event/Deadline classes in yyyy-mm-dd hh:mm format and convert it. Added handling for DateTimeParseException --- src/main/java/Deadline.java | 24 ++++- src/main/java/Duke.java | 1 + src/main/java/DukeException.java | 2 +- src/main/java/Event.java | 23 ++++- src/main/java/InputHandler.java | 165 +++++++++++++++++-------------- src/main/java/Storage.java | 58 +++++------ src/main/java/Task.java | 28 +++++- 7 files changed, 185 insertions(+), 116 deletions(-) diff --git a/src/main/java/Deadline.java b/src/main/java/Deadline.java index 9276cccb8b..c8fa37f96f 100644 --- a/src/main/java/Deadline.java +++ b/src/main/java/Deadline.java @@ -1,20 +1,36 @@ +import java.time.format.DateTimeParseException; import java.util.Scanner; import java.util.ArrayList; import java.util.Arrays; - +import java.time.LocalDateTime; +import java.time.LocalDate; +import java.time.LocalTime; /** * Represents a Deadline which is a subclass of Task * Includes a dueDate attribute. Overrides toString() from Task */ public class Deadline extends Task { - public String dueDate; - public Deadline(String name, String time) {super(name); this.dueDate = time;} + public LocalDate dueDate; + public LocalTime dueTime; + public Deadline(String name, String date) throws DateTimeParseException { + super(name); + this.dueDate = LocalDate.parse(date); + this.dueTime = null; + } + + public Deadline(String name, String date, String time) throws DateTimeParseException { + super(name); + this.dueDate = LocalDate.parse(date); + this.dueTime = LocalTime.parse(time); + } /** * @override * @return String of Deadline task, eg [D][X] Deadline (by:XX) vs [D][✓] Deadline (by;XX) */ - public String toString() { return "[D]" + super.toString() + "(by:" + this.dueDate + ")"; } + public String toString() { + String dueDateAndTime = (this.dueTime == null) ? dateConverterToString(this.dueDate) : dateConverterToString(this.dueDate) + " " + timeConverterToString(this.dueTime); + return "[D]" + super.toString() + " (by:" + dueDateAndTime + ")"; } } \ No newline at end of file diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index d626af3dee..11eaf55de6 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -1,3 +1,4 @@ +import java.time.format.DateTimeParseException; import java.util.Scanner; //import Scanner import java.util.ArrayList; //import ArrayList import java.util.Arrays; //import Arrays diff --git a/src/main/java/DukeException.java b/src/main/java/DukeException.java index 5ff77f264d..5d023a23bc 100644 --- a/src/main/java/DukeException.java +++ b/src/main/java/DukeException.java @@ -5,7 +5,7 @@ public DukeException (String errorMessage) { } /** - * @override + * @override Returns customised error message for DukeException when input is incorrect * @return String errorMessage */ public String getMessage() {return this.errorMessage;} diff --git a/src/main/java/Event.java b/src/main/java/Event.java index a353c88523..b339f6f62a 100644 --- a/src/main/java/Event.java +++ b/src/main/java/Event.java @@ -1,18 +1,35 @@ +import java.time.format.DateTimeParseException; +import java.util.Date; import java.util.Scanner; import java.util.ArrayList; import java.util.Arrays; +import java.time.LocalDateTime; +import java.time.LocalDate; +import java.time.LocalTime; /** * Represents a Event which is a subclass of Task * Overrides toString() from Task */ public class Event extends Task { - public String dueDate; - public Event (String name, String time) { super(name); this.dueDate = time;} + public LocalDate dueDate; + public LocalTime dueTime; + public Event (String name, String date) throws DateTimeParseException { + super(name); + this.dueDate = LocalDate.parse(date); + this.dueTime = null; + } + public Event (String name, String date, String time) throws DateTimeParseException { + super(name); + this.dueDate = LocalDate.parse(date); + this.dueTime = LocalTime.parse(time); + } /** * @override * @return String of Event task, eg: [E][X] Event (at:) vs [E] [✓] Event (at:) */ - public String toString() { return "[E]" + super.toString() + "(at:" + this.dueDate + ")"; } + public String toString() { + String dueDateAndTime = (this.dueTime == null) ? dateConverterToString(this.dueDate) : dateConverterToString(this.dueDate) + " " + timeConverterToString(this.dueTime); + return "[E]" + super.toString() + " (at:" + dueDateAndTime + ")"; } } \ No newline at end of file diff --git a/src/main/java/InputHandler.java b/src/main/java/InputHandler.java index 6fa628f88a..7e83abed04 100644 --- a/src/main/java/InputHandler.java +++ b/src/main/java/InputHandler.java @@ -1,6 +1,9 @@ -import java.util.ArrayList; import java.util.Arrays; import java.io.IOException; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.LocalDate; +import java.time.format.DateTimeParseException; /** * Handles input in main in Duke.java. Receives String from Scanner in main @@ -28,89 +31,99 @@ public boolean handleInput(String input) throws DukeException, IOException { String[] splitInput = input.split(" "); String inputCommand = splitInput[0]; switch (inputCommand) { - case "todo": - if (splitInput.length > 1) { - String[] nameArray = Arrays.copyOfRange(splitInput, 1, splitInput.length); - String name = String.join(" ", nameArray); - Todo newTodo = new Todo(name); - this.storage.writeData(newTodo); - printAddTaskMessage(newTodo); - return false; - } else { - throw new DukeException(":( OOPS!!! The description of a todo cannot be empty. Correct usage: todo [task]"); - } - case "event": - if (splitInput.length > 3) { - String[] stringArrayExcludingEvent = Arrays.copyOfRange(splitInput, 1, splitInput.length); - String stringExcludingEvent = String.join(" ", stringArrayExcludingEvent); - String[] nameAndTimeArray = stringExcludingEvent.split("/at"); - String name = nameAndTimeArray[0]; - String time = nameAndTimeArray[1]; - Event newEvent = new Event(name, time); + case "todo": + if (splitInput.length > 1) { + String[] nameArray = Arrays.copyOfRange(splitInput, 1, splitInput.length); + String name = String.join(" ", nameArray); + Todo newTodo = new Todo(name); + this.storage.writeData(newTodo); + printAddTaskMessage(newTodo); + return false; + } else { + throw new DukeException(":( OOPS!!! The description of a todo cannot be empty. Correct usage: todo [task]"); + } + case "event": + if (splitInput.length > 3) { + String[] stringArrayExcludingEvent = Arrays.copyOfRange(splitInput, 1, splitInput.length); + String stringExcludingEvent = String.join(" ", stringArrayExcludingEvent); + String[] nameAndTimeArray = stringExcludingEvent.split("/at "); + String name = nameAndTimeArray[0]; + String time = nameAndTimeArray[1]; + String[] timeArray = time.split(" "); + try { + Event newEvent = (timeArray.length > 1) ? new Event(name, timeArray[0], timeArray[1]) : new Event(name, timeArray[0]); this.storage.writeData(newEvent); printAddTaskMessage(newEvent); return false; - } else { - throw new DukeException(":( OOPS!!! The description of a event cannot be empty. Correct usage: event [task] /at [time]"); + } catch (DateTimeParseException e) { + System.out.println(":( OOPS!!! The correct format for date and time is yyyy-mm-dd and hh:mm"); } - case "deadline": - if (splitInput.length > 3) { - String[] stringArrayExcludingDeadline = Arrays.copyOfRange(splitInput, 1, splitInput.length); - String stringExcludingDeadline = String.join(" ", stringArrayExcludingDeadline); - String[] nameAndTimeArray = stringExcludingDeadline.split("/by"); - String name = nameAndTimeArray[0]; - String time = nameAndTimeArray[1]; - Deadline newDeadline = new Deadline(name, time); + } else { + throw new DukeException(":( OOPS!!! The description of a event cannot be empty. Correct usage: event [task] /at [time]"); + } + case "deadline": + if (splitInput.length > 3) { + String[] stringArrayExcludingDeadline = Arrays.copyOfRange(splitInput, 1, splitInput.length); + String stringExcludingDeadline = String.join(" ", stringArrayExcludingDeadline); + String[] nameAndTimeArray = stringExcludingDeadline.split("/by "); + String name = nameAndTimeArray[0]; + String time = nameAndTimeArray[1]; + String[] timeArray = time.split(" "); + try { + Deadline newDeadline = (timeArray.length > 1) ? new Deadline(name, timeArray[0], timeArray[1]) : new Deadline(name, timeArray[0]); this.storage.writeData(newDeadline); printAddTaskMessage(newDeadline); return false; - } else { - throw new DukeException(":( OOPS!!! The description of a deadline cannot be empty. Correct usage: deadline [task] /by [time]"); - } - case "list": - if (splitInput.length == 1) { - System.out.println("Here are the tasks in your list:"); - int i = 0; - System.out.println(this.storage.list()); - return false; - } else { - throw new DukeException("Wrong usage of list! Correct usage: list"); - } - case "mark": - if (splitInput.length == 2) { - System.out.println("Nice! I've marked this task as done:\n"); - int idx = Integer.parseInt(splitInput[1]) - 1; - Task taskToBeMarked = this.storage.get(idx); - taskToBeMarked.markTask(); - return false; - } else { - throw new DukeException("Wrong usage of mark! Correct usage: mark [index]"); - } - case "unmark": - if (splitInput.length == 2) { - System.out.println("OK, I've marked this task as not done yet:\n"); - int idx = Integer.parseInt(splitInput[1]) - 1; - Task taskToBeUnmarked = this.storage.get(idx); - taskToBeUnmarked.unmarkTask(); - return false; - } else { - throw new DukeException("Wrong usage of unmark! Correct usage: unmark [index]"); - } - case "delete": - if (splitInput.length == 2) { - int idx = Integer.parseInt(splitInput[1]) - 1; - Task taskToBeDeleted = storage.get(idx); - this.storage.deleteData(idx); - System.out.println("Noted. I've removed this task:\n" + taskToBeDeleted + "\nNow you have " + this.storage.taskListSize() + " tasks in the list"); - return false; - } else { - throw new DukeException("Wrong usage of delete! Correct usage: delete [index]"); + } catch (DateTimeParseException e) { + System.out.println(":( OOPS!!! The correct format for date and time is yyyy-mm-dd and hh:mm"); } - case "bye": - return true; - default: - throw new DukeException(":( OOPS!!! I'm sorry, but I don't know what that means! Possible commands: todo [task], event [task] /at [time]," - + " deadline [task] /by [time], mark [index], unmark [index], delete [index], bye"); + } else { + throw new DukeException(":( OOPS!!! The description of a deadline cannot be empty. Correct usage: deadline [task] /by [time]"); + } + case "list": + if (splitInput.length == 1) { + System.out.println("Here are the tasks in your list:"); + int i = 0; + System.out.println(this.storage.list()); + return false; + } else { + throw new DukeException("Wrong usage of list! Correct usage: list"); + } + case "mark": + if (splitInput.length == 2) { + System.out.println("Nice! I've marked this task as done:\n"); + int idx = Integer.parseInt(splitInput[1]) - 1; + Task taskToBeMarked = this.storage.get(idx); + taskToBeMarked.setMarkedTask(); + return false; + } else { + throw new DukeException("Wrong usage of mark! Correct usage: mark [index]"); + } + case "unmark": + if (splitInput.length == 2) { + System.out.println("OK, I've marked this task as not done yet:\n"); + int idx = Integer.parseInt(splitInput[1]) - 1; + Task taskToBeUnmarked = this.storage.get(idx); + taskToBeUnmarked.setUnmarkedTask(); + return false; + } else { + throw new DukeException("Wrong usage of unmark! Correct usage: unmark [index]"); + } + case "delete": + if (splitInput.length == 2) { + int idx = Integer.parseInt(splitInput[1]) - 1; + Task taskToBeDeleted = storage.get(idx); + this.storage.deleteData(idx); + System.out.println("Noted. I've removed this task:\n" + taskToBeDeleted + "\nNow you have " + this.storage.taskListSize() + " tasks in the list"); + return false; + } else { + throw new DukeException("Wrong usage of delete! Correct usage: delete [index]"); + } + case "bye": + return true; + default: + throw new DukeException(":( OOPS!!! I'm sorry, but I don't know what that means! Possible commands: todo [task], event [task] /at [time]," + + " deadline [task] /by [time], mark [index], unmark [index], delete [index], bye"); } } diff --git a/src/main/java/Storage.java b/src/main/java/Storage.java index bdcbc37270..1b8411a98a 100644 --- a/src/main/java/Storage.java +++ b/src/main/java/Storage.java @@ -24,28 +24,28 @@ public Storage() throws IOException { String[] nextLineArr = nextLine.split(" "); String taskType = nextLineArr[0]; switch (taskType) { - case "[T]": - Todo newTodo = new Todo(nextLineArr[2]); - if (nextLineArr[1].equals("[✓]")) { - newTodo.markTask(); - } - newArr.add(newTodo); - break; - case "[D]": - Deadline newDeadline = new Deadline(nextLineArr[2], nextLineArr[4]); - if (nextLineArr[1].equals("[✓]")) { - newDeadline.markTask(); - } - newArr.add(newDeadline); - break; - case "[E]": - Event newEvent = new Event(nextLineArr[2], nextLineArr[4]); - if (nextLineArr[1].equals("[✓]")) { - newEvent.markTask(); - } - newArr.add(newEvent); - break; - default: + case "[T]": + Todo newTodo = new Todo(nextLineArr[2]); + if (nextLineArr[1].equals("[✓]")) { + newTodo.setMarkedTask(); + } + newArr.add(newTodo); + break; + case "[D]": + Deadline newDeadline = (nextLineArr[6].equals("null")) ? new Deadline(nextLineArr[2], nextLineArr[4]) : new Deadline(nextLineArr[2], nextLineArr[4], nextLineArr[6]); + if (nextLineArr[1].equals("[✓]")) { + newDeadline.setMarkedTask(); + } + newArr.add(newDeadline); + break; + case "[E]": + Event newEvent = (nextLineArr[6].equals("null")) ? new Event(nextLineArr[2], nextLineArr[4]) : new Event(nextLineArr[2], nextLineArr[4], nextLineArr[6]); + if (nextLineArr[1].equals("[✓]")) { + newEvent.setMarkedTask(); + } + newArr.add(newEvent); + break; + default: } } this.arr = newArr; @@ -63,19 +63,19 @@ public Storage() throws IOException { /** * * @param task Converts task to string format for storage in the data.txt file for records - * @return String format of the task eg: [D] [✓] deadline | duedate + * @return String format of the task eg: [D] [✓] deadline | duedate | duetime */ public String taskToStringConverter(Task task) { String output = ""; if (task instanceof Todo) { - String mark = (task.isMark()) ? "[✓]" : "[X]"; + String mark = (task.hasBeenMarked()) ? "[✓]" : "[X]"; output = "[T] " + mark + " " + task.name + "\n"; } else if (task instanceof Deadline deadline) { - String mark = (deadline.isMark()) ? "[✓]" : "[X]"; - output = "[D] " + mark + " " + deadline.name + "|" + deadline.dueDate +"\n"; + String mark = (deadline.hasBeenMarked()) ? "[✓]" : "[X]"; + output = "[D] " + mark + " " + deadline.name + "| " + deadline.dueDate + " | " + deadline.dueTime + "\n"; } else if (task instanceof Event event) { - String mark = (event.isMark()) ? "[✓]" : "[X]"; - output = "[E] " + mark + " " + event.name + "|" + event.dueDate + "\n"; + String mark = (event.hasBeenMarked()) ? "[✓]" : "[X]"; + output = "[E] " + mark + " " + event.name + "| " + event.dueDate + " | " + event.dueTime + "\n"; } return output; } @@ -117,7 +117,7 @@ public String list() { int i = 0; for (Task item : this.arr) { i += 1; - if (item.isMark()) { + if (item.hasBeenMarked()) { listOfTasks += i + ". " + item + "\n"; } else { listOfTasks += i + ". " + item + "\n"; diff --git a/src/main/java/Task.java b/src/main/java/Task.java index 74c6e3bf56..eb83ebfe1b 100644 --- a/src/main/java/Task.java +++ b/src/main/java/Task.java @@ -1,6 +1,10 @@ import java.util.Scanner; import java.util.ArrayList; import java.util.Arrays; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.format.FormatStyle; +import java.time.format.DateTimeFormatter; /** * Represents a Task. Contains a Task constructor, two methods to mark and unmark tasks, toString() method as well as a isMark() method to check if Task is marked @@ -21,7 +25,7 @@ public Task (String name) { /** * markTask as done */ - public void markTask () { + public void setMarkedTask () { String markedMessage = "Nice! I've marked this task as done:\n"; this.mark = true; System.out.println(markedMessage + " " + this); @@ -30,7 +34,7 @@ public void markTask () { /** * unmarkTask */ - public void unmarkTask() { + public void setUnmarkedTask() { String unmarkedMessage = "OK, I've marked this task as not done yet:\n"; this.mark = false; System.out.println(unmarkedMessage + " " + this); @@ -40,10 +44,28 @@ public void unmarkTask() { * * @return boolean on whether task is marked */ - public boolean isMark() { + public boolean hasBeenMarked() { return this.mark; } + /** + * + * @param date LocalDate for Deadline/Event tasks + * @return String format: converts from yyyy-mm-dd format to Aug dd, yyyy format + */ + public String dateConverterToString(LocalDate date) { + return DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG).format(date); + } + + /** + * + * @param time LocalTime for Deadline/Event tasks + * @return String format: converts hh:mm format to hh:mm am/pm format + */ + public String timeConverterToString(LocalTime time) { + return DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT).format(time); + } + /** * @override * @return String version of task, with marked and name. E.g. [X] Task vs [✓] Task From 3c16b7bf68fcfcda178b66581f0c398ec1d36575 Mon Sep 17 00:00:00 2001 From: brian16600 <brian974902@gmail.com> Date: Sat, 5 Feb 2022 13:28:54 +0800 Subject: [PATCH 12/47] Added Parser, Storage and Tasklist --- src/main/java/Parser.java | 84 +++++++++++++++++++++++++++++++++++++ src/main/java/Storage.java | 68 +++++++++++++++--------------- src/main/java/TaskList.java | 23 ++++++++++ 3 files changed, 141 insertions(+), 34 deletions(-) create mode 100644 src/main/java/Parser.java create mode 100644 src/main/java/TaskList.java diff --git a/src/main/java/Parser.java b/src/main/java/Parser.java new file mode 100644 index 0000000000..2c46e638fe --- /dev/null +++ b/src/main/java/Parser.java @@ -0,0 +1,84 @@ +import java.time.format.DateTimeParseException; +import java.util.Arrays; +import java.io.IOException; + +public class Parser { + + public Task parse(InputHandler.CommandType type, String[] splitInput) throws DukeException { + switch (type) { + case TODO: + String[] nameArray = Arrays.copyOfRange(splitInput, 1, splitInput.length); + String todoName = String.join(" ", nameArray); + return new Todo(todoName); + + case EVENT: + String[] stringArrayExcludingEvent = Arrays.copyOfRange(splitInput, 1, splitInput.length); + String stringExcludingEvent = String.join(" ", stringArrayExcludingEvent); + String[] eventNameAndTimeArray = stringExcludingEvent.split("/at "); + String eventName = eventNameAndTimeArray[0]; + String eventTime = eventNameAndTimeArray[1]; + String[] eventTimeArray = eventTime.split(" "); + try { + Event newEvent = (eventTimeArray.length > 1) ? new Event(eventName, eventTimeArray[0], eventTimeArray[1]) : new Event(eventName, eventTimeArray[0]); + return newEvent; + } catch (DateTimeParseException e) { + throw new DukeException(":( OOPS!!! The correct format for date and time is yyyy-mm-dd and hh:mm"); + + } + + case DEADLINE: + String[] stringArrayExcludingDeadline = Arrays.copyOfRange(splitInput, 1, splitInput.length); + String stringExcludingDeadline = String.join(" ", stringArrayExcludingDeadline); + String[] deadlineNameAndTimeArray = stringExcludingDeadline.split("/by "); + String deadlineName = deadlineNameAndTimeArray[0]; + String deadlineTime = deadlineNameAndTimeArray[1]; + String[] deadlineTimeArray = deadlineTime.split(" "); + try { + Deadline newDeadline = (deadlineTimeArray.length > 1) ? new Deadline(deadlineName, deadlineTimeArray[0], deadlineTimeArray[1]) : new Deadline(deadlineName, deadlineTimeArray[0]); + return newDeadline; + } catch (DateTimeParseException e) { + throw new DukeException(":( OOPS!!! The correct format for date and time is yyyy-mm-dd and hh:mm"); + + } + + default: + throw new DukeException(":( OOPS!!! I'm sorry, but I don't know what that means! Possible commands: todo [task], event [task] /at [time]," + + " deadline [task] /by [time], mark [index], unmark [index], delete [index], bye"); + } + } + + public void parse(InputHandler.CommandType type, Storage storage, String[] splitInput) throws DukeException, IOException { + switch (type) { + case LIST: + System.out.println("Here are the tasks in your list:"); + System.out.println(storage.list()); + break; + + case MARK: + System.out.println("Nice! I've marked this task as done:\n"); + int taskToBeMarkedIndex = Integer.parseInt(splitInput[1]) - 1; + Task taskToBeMarked = storage.get(taskToBeMarkedIndex); + taskToBeMarked.setMarkedTask(); + break; + + case UNMARK: + System.out.println("OK, I've marked this task as not done yet:\n"); + int taskToBeUnmarkedIndex = Integer.parseInt(splitInput[1]) - 1; + Task taskToBeUnmarked = storage.get(taskToBeUnmarkedIndex); + taskToBeUnmarked.setUnmarkedTask(); + break; + + case DELETE: + int idx = Integer.parseInt(splitInput[1]) - 1; + Task taskToBeDeleted = storage.get(idx); + storage.deleteData(idx); + System.out.println("Noted. I've removed this task:\n" + taskToBeDeleted + "\nNow you have " + storage.taskListSize() + " tasks in the list"); + break; + + default: + throw new DukeException(":( OOPS!!! I'm sorry, but I don't know what that means! Possible commands: todo [task], event [task] /at [time]," + + " deadline [task] /by [time], mark [index], unmark [index], delete [index], bye"); + } + } + +} \ No newline at end of file diff --git a/src/main/java/Storage.java b/src/main/java/Storage.java index 1b8411a98a..95d5d06169 100644 --- a/src/main/java/Storage.java +++ b/src/main/java/Storage.java @@ -1,4 +1,3 @@ -import java.util.ArrayList; import java.io.File; import java.util.Scanner; import java.io.IOException; @@ -8,7 +7,7 @@ import java.io.FileWriter; public class Storage { - ArrayList<Task> arr; + TaskList taskList; String FILEPATH = "data/data.txt"; /** @@ -18,37 +17,38 @@ public Storage() throws IOException { try { File dataFile = new File(FILEPATH); Scanner sc = new Scanner(dataFile); - ArrayList<Task> newArr = new ArrayList<>(); + TaskList newTaskList = new TaskList(); while (sc.hasNextLine()) { String nextLine = sc.nextLine(); - String[] nextLineArr = nextLine.split(" "); - String taskType = nextLineArr[0]; + String[] taskListaySplitBySpaces = nextLine.split(" "); + String taskType = taskListaySplitBySpaces[0]; + String[] taskListaySplitBySlash = nextLine.split(" / "); switch (taskType) { case "[T]": - Todo newTodo = new Todo(nextLineArr[2]); - if (nextLineArr[1].equals("[✓]")) { + Todo newTodo = new Todo(taskListaySplitBySlash[1]); + if (taskListaySplitBySpaces[1].equals("[✓]")) { newTodo.setMarkedTask(); } - newArr.add(newTodo); + newTaskList.add(newTodo); break; case "[D]": - Deadline newDeadline = (nextLineArr[6].equals("null")) ? new Deadline(nextLineArr[2], nextLineArr[4]) : new Deadline(nextLineArr[2], nextLineArr[4], nextLineArr[6]); - if (nextLineArr[1].equals("[✓]")) { + Deadline newDeadline = (taskListaySplitBySlash[3].equals("null")) ? new Deadline(taskListaySplitBySlash[1], taskListaySplitBySlash[2]) : new Deadline(taskListaySplitBySlash[1], taskListaySplitBySlash[2], taskListaySplitBySlash[3]); + if (taskListaySplitBySpaces[1].equals("[✓]")) { newDeadline.setMarkedTask(); } - newArr.add(newDeadline); + newTaskList.add(newDeadline); break; case "[E]": - Event newEvent = (nextLineArr[6].equals("null")) ? new Event(nextLineArr[2], nextLineArr[4]) : new Event(nextLineArr[2], nextLineArr[4], nextLineArr[6]); - if (nextLineArr[1].equals("[✓]")) { + Event newEvent = (taskListaySplitBySlash[3].equals("null")) ? new Event(taskListaySplitBySlash[1], taskListaySplitBySlash[2]) : new Event(taskListaySplitBySlash[1], taskListaySplitBySlash[2], taskListaySplitBySlash[3]); + if (taskListaySplitBySpaces[1].equals("[✓]")) { newEvent.setMarkedTask(); } - newArr.add(newEvent); + newTaskList.add(newEvent); break; default: } } - this.arr = newArr; + this.taskList = newTaskList; } catch (IOException e){ Path filePath = Paths.get("data"); boolean dataDirectoryExists = Files.exists(filePath); @@ -56,7 +56,7 @@ public Storage() throws IOException { new File("data").mkdir(); } new File(FILEPATH).createNewFile(); - this.arr = new ArrayList<>(); + this.taskList = new TaskList(); } } @@ -69,13 +69,13 @@ public String taskToStringConverter(Task task) { String output = ""; if (task instanceof Todo) { String mark = (task.hasBeenMarked()) ? "[✓]" : "[X]"; - output = "[T] " + mark + " " + task.name + "\n"; + output = "[T] " + mark + " / " + task.name + "\n"; } else if (task instanceof Deadline deadline) { String mark = (deadline.hasBeenMarked()) ? "[✓]" : "[X]"; - output = "[D] " + mark + " " + deadline.name + "| " + deadline.dueDate + " | " + deadline.dueTime + "\n"; + output = "[D] " + mark + " / " + deadline.name + " / " + deadline.dueDate + " / " + deadline.dueTime + "\n"; } else if (task instanceof Event event) { String mark = (event.hasBeenMarked()) ? "[✓]" : "[X]"; - output = "[E] " + mark + " " + event.name + "| " + event.dueDate + " | " + event.dueTime + "\n"; + output = "[E] " + mark + " / " + event.name + " / " + event.dueDate + " / " + event.dueTime + "\n"; } return output; } @@ -86,41 +86,41 @@ public String taskToStringConverter(Task task) { * @throws IOException if there is an error appending the task to data.txt */ public void writeData(Task task) throws IOException { - this.arr.add(task); + this.taskList.add(task); FileWriter fw = new FileWriter(this.FILEPATH, true); fw.write(taskToStringConverter(task)); fw.close(); } /** - * Used when delete [index] is called for Duke. Deletes the entire file and rewrites it based on the new array - * Amends the current stored array as well + * Used when delete [index] is called for Duke. Deletes the entire file and rewrites it based on the new taskListay + * Amends the current stored taskListay as well * @param idx index of task to be deleted * @throws IOException if there is an error rewriting data.txt */ public void deleteData(int idx) throws IOException { - Task taskToBeDeleted = this.arr.get(idx); - arr.remove(idx); + Task taskToBeDeleted = this.taskList.get(idx); + taskList.remove(idx); FileWriter fw = new FileWriter(this.FILEPATH); - for (Task task : this.arr) { + for (int i = 0; i < this.taskList.size(); i++) { + Task task = this.taskList.get(i); fw.write(taskToStringConverter(task)); } fw.close(); } /** - * Obtains list of tasks from this.arr and returns it + * Obtains list of tasks from this.taskList and returns it * @return String that lists out the tasks currently */ public String list() { String listOfTasks = ""; - int i = 0; - for (Task item : this.arr) { - i += 1; - if (item.hasBeenMarked()) { - listOfTasks += i + ". " + item + "\n"; + for (int i = 1; i <= this.taskList.size(); i++) { + Task task = this.taskList.get(i - 1); + if (task.hasBeenMarked()) { + listOfTasks += i + ". " + task + "\n"; } else { - listOfTasks += i + ". " + item + "\n"; + listOfTasks += i + ". " + task + "\n"; } } return listOfTasks; @@ -132,7 +132,7 @@ public String list() { * @return task that is requested */ public Task get(int idx) { - return this.arr.get(idx); + return this.taskList.get(idx); } /** @@ -140,7 +140,7 @@ public Task get(int idx) { * @return size of task list */ public int taskListSize() { - return this.arr.size(); + return this.taskList.size(); } } diff --git a/src/main/java/TaskList.java b/src/main/java/TaskList.java new file mode 100644 index 0000000000..e7d891e1d3 --- /dev/null +++ b/src/main/java/TaskList.java @@ -0,0 +1,23 @@ +import java.util.ArrayList; +public class TaskList { + ArrayList<Task> list; + public TaskList() { + this.list = new ArrayList<>(); + } + + public void add(Task task) { + this.list.add(task); + } + + public void remove(int index) { + this.list.remove(index); + } + + public Task get(int index) { + return this.list.get(index); + } + + public int size() { + return this.list.size(); + } +} From 633dca67e82576a6b215d54640213d6545d463b0 Mon Sep 17 00:00:00 2001 From: brian16600 <brian974902@gmail.com> Date: Sat, 5 Feb 2022 11:13:57 +0800 Subject: [PATCH 13/47] Added TaskList and Parser classes. TaskList contains the arraylist of tasks with methods and Parser makes sense of user input for Duke --- src/main/java/InputHandler.java | 71 ++++++++++++--------------------- 1 file changed, 25 insertions(+), 46 deletions(-) diff --git a/src/main/java/InputHandler.java b/src/main/java/InputHandler.java index 7e83abed04..dc354f56a0 100644 --- a/src/main/java/InputHandler.java +++ b/src/main/java/InputHandler.java @@ -30,12 +30,11 @@ public InputHandler() throws IOException { public boolean handleInput(String input) throws DukeException, IOException { String[] splitInput = input.split(" "); String inputCommand = splitInput[0]; + Parser parser = new Parser(); switch (inputCommand) { case "todo": if (splitInput.length > 1) { - String[] nameArray = Arrays.copyOfRange(splitInput, 1, splitInput.length); - String name = String.join(" ", nameArray); - Todo newTodo = new Todo(name); + Todo newTodo = (Todo) parser.parse(CommandType.TODO, splitInput); this.storage.writeData(newTodo); printAddTaskMessage(newTodo); return false; @@ -44,77 +43,47 @@ public boolean handleInput(String input) throws DukeException, IOException { } case "event": if (splitInput.length > 3) { - String[] stringArrayExcludingEvent = Arrays.copyOfRange(splitInput, 1, splitInput.length); - String stringExcludingEvent = String.join(" ", stringArrayExcludingEvent); - String[] nameAndTimeArray = stringExcludingEvent.split("/at "); - String name = nameAndTimeArray[0]; - String time = nameAndTimeArray[1]; - String[] timeArray = time.split(" "); - try { - Event newEvent = (timeArray.length > 1) ? new Event(name, timeArray[0], timeArray[1]) : new Event(name, timeArray[0]); - this.storage.writeData(newEvent); - printAddTaskMessage(newEvent); - return false; - } catch (DateTimeParseException e) { - System.out.println(":( OOPS!!! The correct format for date and time is yyyy-mm-dd and hh:mm"); - } + Event newEvent = (Event) parser.parse(CommandType.EVENT, splitInput); + this.storage.writeData(newEvent); + printAddTaskMessage(newEvent); + return false; + } else { throw new DukeException(":( OOPS!!! The description of a event cannot be empty. Correct usage: event [task] /at [time]"); } case "deadline": if (splitInput.length > 3) { - String[] stringArrayExcludingDeadline = Arrays.copyOfRange(splitInput, 1, splitInput.length); - String stringExcludingDeadline = String.join(" ", stringArrayExcludingDeadline); - String[] nameAndTimeArray = stringExcludingDeadline.split("/by "); - String name = nameAndTimeArray[0]; - String time = nameAndTimeArray[1]; - String[] timeArray = time.split(" "); - try { - Deadline newDeadline = (timeArray.length > 1) ? new Deadline(name, timeArray[0], timeArray[1]) : new Deadline(name, timeArray[0]); - this.storage.writeData(newDeadline); - printAddTaskMessage(newDeadline); - return false; - } catch (DateTimeParseException e) { - System.out.println(":( OOPS!!! The correct format for date and time is yyyy-mm-dd and hh:mm"); - } + Deadline newDeadline = (Deadline) parser.parse(CommandType.DEADLINE, splitInput); + this.storage.writeData(newDeadline); + printAddTaskMessage(newDeadline); + return false; } else { throw new DukeException(":( OOPS!!! The description of a deadline cannot be empty. Correct usage: deadline [task] /by [time]"); } case "list": if (splitInput.length == 1) { - System.out.println("Here are the tasks in your list:"); - int i = 0; - System.out.println(this.storage.list()); + parser.parse(CommandType.LIST, this.storage, splitInput); return false; } else { throw new DukeException("Wrong usage of list! Correct usage: list"); } case "mark": if (splitInput.length == 2) { - System.out.println("Nice! I've marked this task as done:\n"); - int idx = Integer.parseInt(splitInput[1]) - 1; - Task taskToBeMarked = this.storage.get(idx); - taskToBeMarked.setMarkedTask(); + parser.parse(CommandType.MARK, this.storage, splitInput); return false; } else { throw new DukeException("Wrong usage of mark! Correct usage: mark [index]"); } case "unmark": if (splitInput.length == 2) { - System.out.println("OK, I've marked this task as not done yet:\n"); - int idx = Integer.parseInt(splitInput[1]) - 1; - Task taskToBeUnmarked = this.storage.get(idx); - taskToBeUnmarked.setUnmarkedTask(); + parser.parse(CommandType.UNMARK, this.storage, splitInput); return false; } else { throw new DukeException("Wrong usage of unmark! Correct usage: unmark [index]"); } case "delete": if (splitInput.length == 2) { - int idx = Integer.parseInt(splitInput[1]) - 1; - Task taskToBeDeleted = storage.get(idx); - this.storage.deleteData(idx); - System.out.println("Noted. I've removed this task:\n" + taskToBeDeleted + "\nNow you have " + this.storage.taskListSize() + " tasks in the list"); + parser.parse(CommandType.DELETE, this.storage, splitInput); return false; } else { throw new DukeException("Wrong usage of delete! Correct usage: delete [index]"); @@ -135,5 +104,15 @@ public boolean handleInput(String input) throws DukeException, IOException { public void printAddTaskMessage(Task task) { System.out.println("Got it. I've added this task:\n" + task + "\nNow you have " + this.storage.taskListSize() + " tasks in the list." ); } + + enum CommandType { + TODO, + EVENT, + DEADLINE, + LIST, + MARK, + UNMARK, + DELETE + } } From 38a246268aa0330d21a1a82d623e2759bce4daac Mon Sep 17 00:00:00 2001 From: brian16600 <brian974902@gmail.com> Date: Sat, 5 Feb 2022 14:37:46 +0800 Subject: [PATCH 14/47] Added more OOP and documentation --- src/main/data/data.txt | 0 src/main/java/Deadline.java | 14 ++++++++++++++ src/main/java/Event.java | 15 +++++++++++++++ src/main/java/InputHandler.java | 3 +++ src/main/java/Parser.java | 15 +++++++++++++++ src/main/java/Storage.java | 4 ++-- src/main/java/Task.java | 12 ++++++------ src/main/java/TaskList.java | 21 +++++++++++++++++++++ src/main/java/Todo.java | 5 +++++ 9 files changed, 81 insertions(+), 8 deletions(-) create mode 100644 src/main/data/data.txt diff --git a/src/main/data/data.txt b/src/main/data/data.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/main/java/Deadline.java b/src/main/java/Deadline.java index c8fa37f96f..f032ac301f 100644 --- a/src/main/java/Deadline.java +++ b/src/main/java/Deadline.java @@ -13,12 +13,26 @@ public class Deadline extends Task { public LocalDate dueDate; public LocalTime dueTime; + + /** + * Constructor for Deadline with date + * @param name Name of Deadline + * @param date Date of deadline in yyyy-mm-dd format + * @throws DateTimeParseException If date time is in wrong format + */ public Deadline(String name, String date) throws DateTimeParseException { super(name); this.dueDate = LocalDate.parse(date); this.dueTime = null; } + /** + * Constructor for Deadline with date and time + * @param name Name of Deadline + * @param date Date of deadline in yyyy-mm-dd format + * @param time Time of deadline in hh:mm format + * @throws DateTimeParseException If date time is in wrong format + */ public Deadline(String name, String date, String time) throws DateTimeParseException { super(name); this.dueDate = LocalDate.parse(date); diff --git a/src/main/java/Event.java b/src/main/java/Event.java index b339f6f62a..63f75d1b38 100644 --- a/src/main/java/Event.java +++ b/src/main/java/Event.java @@ -14,11 +14,26 @@ public class Event extends Task { public LocalDate dueDate; public LocalTime dueTime; + + /** + * Constructor for Event with date + * @param name Name of Event + * @param date Date of event in yyyy-mm-dd format + * @throws DateTimeParseException If date time is in wrong format + */ public Event (String name, String date) throws DateTimeParseException { super(name); this.dueDate = LocalDate.parse(date); this.dueTime = null; } + + /** + * Constructor for Event with date and time + * @param name Name of Event + * @param date Date of event in yyyy-mm-dd format + * @param time Time of event in hh:mm format + * @throws DateTimeParseException If date time is in wrong format + */ public Event (String name, String date, String time) throws DateTimeParseException { super(name); this.dueDate = LocalDate.parse(date); diff --git a/src/main/java/InputHandler.java b/src/main/java/InputHandler.java index dc354f56a0..933b19a716 100644 --- a/src/main/java/InputHandler.java +++ b/src/main/java/InputHandler.java @@ -105,6 +105,9 @@ public void printAddTaskMessage(Task task) { System.out.println("Got it. I've added this task:\n" + task + "\nNow you have " + this.storage.taskListSize() + " tasks in the list." ); } + /** + * Type of Command enum to be passed into Parser object + */ enum CommandType { TODO, EVENT, diff --git a/src/main/java/Parser.java b/src/main/java/Parser.java index 2c46e638fe..9a90350d81 100644 --- a/src/main/java/Parser.java +++ b/src/main/java/Parser.java @@ -4,6 +4,13 @@ public class Parser { + /** + * Parses input from InputHandler and returns a new Task to be added to TaskList + * @param type CommandType of input, including (TODO, DEADLINE, EVENT) + * @param splitInput SplitInput from InputHandler is user's input, split by empty spaces for processing + * @return Task object of new task to be added to TaskList + * @throws DukeException If format is wrong + */ public Task parse(InputHandler.CommandType type, String[] splitInput) throws DukeException { switch (type) { case TODO: @@ -47,6 +54,14 @@ public Task parse(InputHandler.CommandType type, String[] splitInput) throws Duk } } + /** + * Parses input from InputHandler and writes/deletes/prints from storage accordingly + * @param type CommandType of input, including (LIST, MARK, UNMARK, DELETE) + * @param storage Storage object in InputHandler to write/delete/get data from + * @param splitInput SplitInput from InputHandler is user's input, split by empty spaces for processing + * @throws DukeException Handles unrecognised commands + * @throws IOException Handles IO Errors + */ public void parse(InputHandler.CommandType type, Storage storage, String[] splitInput) throws DukeException, IOException { switch (type) { case LIST: diff --git a/src/main/java/Storage.java b/src/main/java/Storage.java index 95d5d06169..ae56d1c86f 100644 --- a/src/main/java/Storage.java +++ b/src/main/java/Storage.java @@ -127,7 +127,7 @@ public String list() { } /** - * + * Get task * @param idx index of task to be gotten * @return task that is requested */ @@ -136,7 +136,7 @@ public Task get(int idx) { } /** - * + * Size of tasklist * @return size of task list */ public int taskListSize() { diff --git a/src/main/java/Task.java b/src/main/java/Task.java index eb83ebfe1b..2fc75c6d5d 100644 --- a/src/main/java/Task.java +++ b/src/main/java/Task.java @@ -14,7 +14,7 @@ public class Task { public String name; /** - * Constructor + * Constructor for Task. Is called in subclasses Todo, Event and Deadline * @param name name of the task */ public Task (String name) { @@ -23,7 +23,7 @@ public Task (String name) { } /** - * markTask as done + * Marks task as done */ public void setMarkedTask () { String markedMessage = "Nice! I've marked this task as done:\n"; @@ -32,7 +32,7 @@ public void setMarkedTask () { } /** - * unmarkTask + * Sets task to be unmarked */ public void setUnmarkedTask() { String unmarkedMessage = "OK, I've marked this task as not done yet:\n"; @@ -41,7 +41,7 @@ public void setUnmarkedTask() { } /** - * + * Whether task has been marked * @return boolean on whether task is marked */ public boolean hasBeenMarked() { @@ -49,7 +49,7 @@ public boolean hasBeenMarked() { } /** - * + * Converts date to String format for printing * @param date LocalDate for Deadline/Event tasks * @return String format: converts from yyyy-mm-dd format to Aug dd, yyyy format */ @@ -58,7 +58,7 @@ public String dateConverterToString(LocalDate date) { } /** - * + * Converts time to String format for printing * @param time LocalTime for Deadline/Event tasks * @return String format: converts hh:mm format to hh:mm am/pm format */ diff --git a/src/main/java/TaskList.java b/src/main/java/TaskList.java index e7d891e1d3..ff26570281 100644 --- a/src/main/java/TaskList.java +++ b/src/main/java/TaskList.java @@ -1,22 +1,43 @@ import java.util.ArrayList; public class TaskList { ArrayList<Task> list; + + /** + * Constructor for TaskList. Initialises an empty ArrayList<Task> + */ public TaskList() { this.list = new ArrayList<>(); } + /** + * Add Task to TaskList + * @param task Task to be added + */ public void add(Task task) { this.list.add(task); } + /** + * Remove Task from TaskList + * @param index index of Task to be removed + */ public void remove(int index) { this.list.remove(index); } + /** + * Get task by index + * @param index index of Task to be retrieved + * @return Retrieved Task + */ public Task get(int index) { return this.list.get(index); } + /** + * Size of tasklist + * @return integer size of TaskList + */ public int size() { return this.list.size(); } diff --git a/src/main/java/Todo.java b/src/main/java/Todo.java index d5f0339cea..9d51277f17 100644 --- a/src/main/java/Todo.java +++ b/src/main/java/Todo.java @@ -8,6 +8,11 @@ */ public class Todo extends Task { + + /** + * Constructor for Todo + * @param name Name of Todo + */ public Todo (String name) { super(name); } From e129edd266cb6c36c86889e973fdcabe13673abd Mon Sep 17 00:00:00 2001 From: brian16600 <brian974902@gmail.com> Date: Sat, 5 Feb 2022 14:54:25 +0800 Subject: [PATCH 15/47] Added packages: duke.duke, duke.ui, duke.storage and duke.task --- src/main/java/Deadline.java | 18 ++++++++++++------ src/main/java/Duke.java | 10 ++++++---- src/main/java/DukeException.java | 11 +++++++++++ src/main/java/Event.java | 19 +++++++++++++------ src/main/java/InputHandler.java | 14 ++++++++++++++ src/main/java/Parser.java | 10 ++++++++++ src/main/java/Storage.java | 11 +++++++++++ src/main/java/Task.java | 16 ++++++++++++---- src/main/java/TaskList.java | 12 ++++++++++++ src/main/java/Todo.java | 15 +++++++++++---- 10 files changed, 112 insertions(+), 24 deletions(-) diff --git a/src/main/java/Deadline.java b/src/main/java/Deadline.java index f032ac301f..250765a535 100644 --- a/src/main/java/Deadline.java +++ b/src/main/java/Deadline.java @@ -1,11 +1,17 @@ -import java.time.format.DateTimeParseException; -import java.util.Scanner; -import java.util.ArrayList; -import java.util.Arrays; -import java.time.LocalDateTime; +package duke.task; +import duke.duke.Duke; +import duke.ui.Parser; +import duke.ui.DukeException; +import duke.ui.InputHandler; +import duke.storage.Storage; +import duke.storage.TaskList; +import duke.task.Event; +import duke.task.Task; +import duke.task.Todo; +import duke.task.Deadline; import java.time.LocalDate; import java.time.LocalTime; - +import java.time.format.DateTimeParseException; /** * Represents a Deadline which is a subclass of Task * Includes a dueDate attribute. Overrides toString() from Task diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 11eaf55de6..c8927fe565 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -1,8 +1,10 @@ -import java.time.format.DateTimeParseException; -import java.util.Scanner; //import Scanner -import java.util.ArrayList; //import ArrayList -import java.util.Arrays; //import Arrays +package duke.duke; + +import duke.ui.DukeException; +import duke.ui.InputHandler; + import java.io.IOException; +import java.util.Scanner; public class Duke { public static void main(String[] args) throws IOException { diff --git a/src/main/java/DukeException.java b/src/main/java/DukeException.java index 5d023a23bc..cc21e512af 100644 --- a/src/main/java/DukeException.java +++ b/src/main/java/DukeException.java @@ -1,3 +1,14 @@ +package duke.ui; +import duke.duke.Duke; +import duke.ui.Parser; +import duke.ui.DukeException; +import duke.ui.InputHandler; +import duke.storage.Storage; +import duke.storage.TaskList; +import duke.task.Event; +import duke.task.Task; +import duke.task.Todo; +import duke.task.Deadline; public class DukeException extends Exception{ private String errorMessage; public DukeException (String errorMessage) { diff --git a/src/main/java/Event.java b/src/main/java/Event.java index 63f75d1b38..682c800384 100644 --- a/src/main/java/Event.java +++ b/src/main/java/Event.java @@ -1,11 +1,18 @@ -import java.time.format.DateTimeParseException; -import java.util.Date; -import java.util.Scanner; -import java.util.ArrayList; -import java.util.Arrays; -import java.time.LocalDateTime; +package duke.task; +import duke.duke.Duke; +import duke.ui.Parser; +import duke.ui.DukeException; +import duke.ui.InputHandler; +import duke.storage.Storage; +import duke.storage.TaskList; +import duke.task.Event; +import duke.task.Task; +import duke.task.Todo; +import duke.task.Deadline; + import java.time.LocalDate; import java.time.LocalTime; +import java.time.format.DateTimeParseException; /** * Represents a Event which is a subclass of Task diff --git a/src/main/java/InputHandler.java b/src/main/java/InputHandler.java index 933b19a716..fd24eea6ee 100644 --- a/src/main/java/InputHandler.java +++ b/src/main/java/InputHandler.java @@ -1,3 +1,17 @@ +package duke.ui; +import duke.duke.Duke; +import duke.ui.Parser; +import duke.ui.DukeException; +import duke.ui.InputHandler; +import duke.storage.Storage; +import duke.storage.TaskList; +import duke.task.Event; +import duke.task.Task; +import duke.task.Todo; +import duke.task.Deadline; +import java.time.format.DateTimeParseException; +import java.util.Arrays; +import java.io.IOException; import java.util.Arrays; import java.io.IOException; import java.time.LocalDateTime; diff --git a/src/main/java/Parser.java b/src/main/java/Parser.java index 9a90350d81..ea21177a3f 100644 --- a/src/main/java/Parser.java +++ b/src/main/java/Parser.java @@ -1,3 +1,13 @@ +package duke.ui; +import duke.duke.Duke; +import duke.ui.DukeException; +import duke.ui.InputHandler; +import duke.storage.Storage; +import duke.storage.TaskList; +import duke.task.Event; +import duke.task.Task; +import duke.task.Todo; +import duke.task.Deadline; import java.time.format.DateTimeParseException; import java.util.Arrays; import java.io.IOException; diff --git a/src/main/java/Storage.java b/src/main/java/Storage.java index ae56d1c86f..25f06949ac 100644 --- a/src/main/java/Storage.java +++ b/src/main/java/Storage.java @@ -1,3 +1,14 @@ +package duke.storage; +import duke.duke.Duke; +import duke.ui.Parser; +import duke.ui.DukeException; +import duke.ui.InputHandler; +import duke.storage.Storage; +import duke.storage.TaskList; +import duke.task.Event; +import duke.task.Task; +import duke.task.Todo; +import duke.task.Deadline; import java.io.File; import java.util.Scanner; import java.io.IOException; diff --git a/src/main/java/Task.java b/src/main/java/Task.java index 2fc75c6d5d..4ebb1f3d1b 100644 --- a/src/main/java/Task.java +++ b/src/main/java/Task.java @@ -1,10 +1,18 @@ -import java.util.Scanner; -import java.util.ArrayList; -import java.util.Arrays; +package duke.task; +import duke.duke.Duke; +import duke.ui.Parser; +import duke.ui.DukeException; +import duke.ui.InputHandler; +import duke.storage.Storage; +import duke.storage.TaskList; +import duke.task.Event; +import duke.task.Task; +import duke.task.Todo; +import duke.task.Deadline; import java.time.LocalDate; import java.time.LocalTime; -import java.time.format.FormatStyle; import java.time.format.DateTimeFormatter; +import java.time.format.FormatStyle; /** * Represents a Task. Contains a Task constructor, two methods to mark and unmark tasks, toString() method as well as a isMark() method to check if Task is marked diff --git a/src/main/java/TaskList.java b/src/main/java/TaskList.java index ff26570281..afcecb6489 100644 --- a/src/main/java/TaskList.java +++ b/src/main/java/TaskList.java @@ -1,3 +1,15 @@ +package duke.storage; +import duke.duke.Duke; +import duke.ui.Parser; +import duke.ui.DukeException; +import duke.ui.InputHandler; +import duke.storage.Storage; +import duke.storage.TaskList; +import duke.task.Event; +import duke.task.Task; +import duke.task.Todo; +import duke.task.Deadline; +import duke.task.Task; import java.util.ArrayList; public class TaskList { ArrayList<Task> list; diff --git a/src/main/java/Todo.java b/src/main/java/Todo.java index 9d51277f17..26c0a7b67b 100644 --- a/src/main/java/Todo.java +++ b/src/main/java/Todo.java @@ -1,7 +1,14 @@ -import java.util.Scanner; -import java.util.ArrayList; -import java.util.Arrays; - +package duke.task; +import duke.duke.Duke; +import duke.ui.Parser; +import duke.ui.DukeException; +import duke.ui.InputHandler; +import duke.storage.Storage; +import duke.storage.TaskList; +import duke.task.Event; +import duke.task.Task; +import duke.task.Todo; +import duke.task.Deadline; /** * Represents a Todo which is a subclass of Task * Includes a dueDate attribute. Overrides toString() from Task From 6ace06841e1eb642ae0bbef9e749fad09ac551ab Mon Sep 17 00:00:00 2001 From: brian16600 <brian974902@gmail.com> Date: Sat, 5 Feb 2022 22:52:08 +0800 Subject: [PATCH 16/47] Added add-gradle-support branch by merging into master --- src/main/java/Deadline.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/Deadline.java b/src/main/java/Deadline.java index 250765a535..e648f3ddd7 100644 --- a/src/main/java/Deadline.java +++ b/src/main/java/Deadline.java @@ -12,6 +12,7 @@ import java.time.LocalDate; import java.time.LocalTime; import java.time.format.DateTimeParseException; + /** * Represents a Deadline which is a subclass of Task * Includes a dueDate attribute. Overrides toString() from Task From 33617d461fc368b91a475cdc44f0d34d1f1b55be Mon Sep 17 00:00:00 2001 From: brian16600 <brian974902@gmail.com> Date: Sat, 5 Feb 2022 23:00:44 +0800 Subject: [PATCH 17/47] Ensured javadoc documentation is complete for all methods --- src/main/java/DukeException.java | 4 ++++ src/main/java/Parser.java | 4 ++-- src/main/java/Storage.java | 6 ++++-- src/main/java/Task.java | 10 +++++----- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/main/java/DukeException.java b/src/main/java/DukeException.java index cc21e512af..c2d455a278 100644 --- a/src/main/java/DukeException.java +++ b/src/main/java/DukeException.java @@ -9,6 +9,10 @@ import duke.task.Task; import duke.task.Todo; import duke.task.Deadline; + +/** + * Custom DukeException to be handled by InputHandler + */ public class DukeException extends Exception{ private String errorMessage; public DukeException (String errorMessage) { diff --git a/src/main/java/Parser.java b/src/main/java/Parser.java index ea21177a3f..b5afbbd244 100644 --- a/src/main/java/Parser.java +++ b/src/main/java/Parser.java @@ -15,7 +15,7 @@ public class Parser { /** - * Parses input from InputHandler and returns a new Task to be added to TaskList + * Parses input from InputHandler and returns a new Task to be added to TaskList. Handles event, deadline, todo commands * @param type CommandType of input, including (TODO, DEADLINE, EVENT) * @param splitInput SplitInput from InputHandler is user's input, split by empty spaces for processing * @return Task object of new task to be added to TaskList @@ -65,7 +65,7 @@ public Task parse(InputHandler.CommandType type, String[] splitInput) throws Duk } /** - * Parses input from InputHandler and writes/deletes/prints from storage accordingly + * Parses input from InputHandler and writes/deletes/prints from storage accordingly. Handles list, mark, unmark, delete commands * @param type CommandType of input, including (LIST, MARK, UNMARK, DELETE) * @param storage Storage object in InputHandler to write/delete/get data from * @param splitInput SplitInput from InputHandler is user's input, split by empty spaces for processing diff --git a/src/main/java/Storage.java b/src/main/java/Storage.java index 25f06949ac..e77c46cfb4 100644 --- a/src/main/java/Storage.java +++ b/src/main/java/Storage.java @@ -81,10 +81,12 @@ public String taskToStringConverter(Task task) { if (task instanceof Todo) { String mark = (task.hasBeenMarked()) ? "[✓]" : "[X]"; output = "[T] " + mark + " / " + task.name + "\n"; - } else if (task instanceof Deadline deadline) { + } else if (task instanceof Deadline) { + Deadline deadline = (Deadline) task; String mark = (deadline.hasBeenMarked()) ? "[✓]" : "[X]"; output = "[D] " + mark + " / " + deadline.name + " / " + deadline.dueDate + " / " + deadline.dueTime + "\n"; - } else if (task instanceof Event event) { + } else if (task instanceof Event) { + Event event = (Event) task; String mark = (event.hasBeenMarked()) ? "[✓]" : "[X]"; output = "[E] " + mark + " / " + event.name + " / " + event.dueDate + " / " + event.dueTime + "\n"; } diff --git a/src/main/java/Task.java b/src/main/java/Task.java index 4ebb1f3d1b..313bccb1b1 100644 --- a/src/main/java/Task.java +++ b/src/main/java/Task.java @@ -18,7 +18,7 @@ * Represents a Task. Contains a Task constructor, two methods to mark and unmark tasks, toString() method as well as a isMark() method to check if Task is marked */ public class Task { - private boolean mark; + private boolean isMarked; public String name; /** @@ -27,7 +27,7 @@ public class Task { */ public Task (String name) { this.name = name; - this.mark = false; + this.isMarked = false; } /** @@ -35,7 +35,7 @@ public Task (String name) { */ public void setMarkedTask () { String markedMessage = "Nice! I've marked this task as done:\n"; - this.mark = true; + this.isMarked = true; System.out.println(markedMessage + " " + this); } @@ -44,7 +44,7 @@ public void setMarkedTask () { */ public void setUnmarkedTask() { String unmarkedMessage = "OK, I've marked this task as not done yet:\n"; - this.mark = false; + this.isMarked = false; System.out.println(unmarkedMessage + " " + this); } @@ -79,7 +79,7 @@ public String timeConverterToString(LocalTime time) { * @return String version of task, with marked and name. E.g. [X] Task vs [✓] Task */ public String toString() { - if (this.mark) { + if (this.isMarked) { String marked = "[✓] "; return marked + this.name; } else { From 6da11ff4aa530b7dd305c9fc3b7ada8e7a19b26b Mon Sep 17 00:00:00 2001 From: brian16600 <brian974902@gmail.com> Date: Sun, 6 Feb 2022 18:12:31 +0800 Subject: [PATCH 18/47] Added InputHandlerTest.java and TaskListTest.java --- build.gradle | 3 ++- src/main/java/Parser.java | 6 ++++-- src/main/java/Storage.java | 1 + src/main/java/Task.java | 2 +- src/test/java/InputHandlerTest.java | 30 +++++++++++++++++++++++++++++ src/test/java/TaskListTest.java | 22 +++++++++++++++++++++ 6 files changed, 60 insertions(+), 4 deletions(-) create mode 100644 src/test/java/InputHandlerTest.java create mode 100644 src/test/java/TaskListTest.java diff --git a/build.gradle b/build.gradle index 20c0521cc7..0182b89fc1 100644 --- a/build.gradle +++ b/build.gradle @@ -29,7 +29,7 @@ test { } application { - mainClassName = "seedu.duke.Duke" + mainClassName = "duke.Duke" } shadowJar { @@ -43,4 +43,5 @@ checkstyle { run{ standardInput = System.in + enableAssertions = true; } diff --git a/src/main/java/Parser.java b/src/main/java/Parser.java index b5afbbd244..8cbf55eb56 100644 --- a/src/main/java/Parser.java +++ b/src/main/java/Parser.java @@ -32,7 +32,8 @@ public Task parse(InputHandler.CommandType type, String[] splitInput) throws Duk String[] stringArrayExcludingEvent = Arrays.copyOfRange(splitInput, 1, splitInput.length); String stringExcludingEvent = String.join(" ", stringArrayExcludingEvent); String[] eventNameAndTimeArray = stringExcludingEvent.split("/at "); - String eventName = eventNameAndTimeArray[0]; + String eventNameWithExtraSpace = eventNameAndTimeArray[0]; + String eventName = eventNameWithExtraSpace.substring(0, eventNameWithExtraSpace.length() -1); String eventTime = eventNameAndTimeArray[1]; String[] eventTimeArray = eventTime.split(" "); try { @@ -47,7 +48,8 @@ public Task parse(InputHandler.CommandType type, String[] splitInput) throws Duk String[] stringArrayExcludingDeadline = Arrays.copyOfRange(splitInput, 1, splitInput.length); String stringExcludingDeadline = String.join(" ", stringArrayExcludingDeadline); String[] deadlineNameAndTimeArray = stringExcludingDeadline.split("/by "); - String deadlineName = deadlineNameAndTimeArray[0]; + String deadlineNameWithSpace = deadlineNameAndTimeArray[0]; + String deadlineName = deadlineNameWithSpace.substring(0, deadlineNameWithSpace.length() - 1); String deadlineTime = deadlineNameAndTimeArray[1]; String[] deadlineTimeArray = deadlineTime.split(" "); try { diff --git a/src/main/java/Storage.java b/src/main/java/Storage.java index e77c46cfb4..f065e5c438 100644 --- a/src/main/java/Storage.java +++ b/src/main/java/Storage.java @@ -101,6 +101,7 @@ public String taskToStringConverter(Task task) { public void writeData(Task task) throws IOException { this.taskList.add(task); FileWriter fw = new FileWriter(this.FILEPATH, true); + System.out.println(new File(this.FILEPATH).getAbsolutePath()); fw.write(taskToStringConverter(task)); fw.close(); } diff --git a/src/main/java/Task.java b/src/main/java/Task.java index 313bccb1b1..f9feb0fe2a 100644 --- a/src/main/java/Task.java +++ b/src/main/java/Task.java @@ -53,7 +53,7 @@ public void setUnmarkedTask() { * @return boolean on whether task is marked */ public boolean hasBeenMarked() { - return this.mark; + return this.isMarked; } /** diff --git a/src/test/java/InputHandlerTest.java b/src/test/java/InputHandlerTest.java new file mode 100644 index 0000000000..d40b991c48 --- /dev/null +++ b/src/test/java/InputHandlerTest.java @@ -0,0 +1,30 @@ +import duke.ui.InputHandler; +import duke.ui.DukeException; + +import java.io.IOException; +import java.io.File; + +import java.util.Scanner; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.DisplayName; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class InputHandlerTest { + + @Test + @DisplayName("test tasks") + void AddTodoEventDeadline() throws IOException, DukeException { + InputHandler ui = new InputHandler(); + ui.handleInput("todo test todo"); + ui.handleInput("event test event /at 2022-02-09 12:00"); + ui.handleInput("deadline test deadline /by 2022-02-11 03:59"); + String FILEPATH = "data/data.txt"; + File dataFile = new File(FILEPATH); + Scanner sc = new Scanner(dataFile); + assertEquals(sc.nextLine(), "[T] [X] / test todo"); + assertEquals(sc.nextLine(), "[E] [X] / test event / 2022-02-09 / 12:00"); + assertEquals(sc.nextLine(), "[D] [X] / test deadline / 2022-02-11 / 03:59"); + } +} + diff --git a/src/test/java/TaskListTest.java b/src/test/java/TaskListTest.java new file mode 100644 index 0000000000..07aed79401 --- /dev/null +++ b/src/test/java/TaskListTest.java @@ -0,0 +1,22 @@ +import duke.storage.TaskList; +import duke.task.Task; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.DisplayName; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class TaskListTest { + + @Test + @DisplayName("test task list") + void testTaskList() { + TaskList tl = new TaskList(); + int taskListSize = tl.size(); + Task newTask = new Task("test task"); + tl.add(newTask); + assertEquals(tl.size(), taskListSize + 1); + assertEquals(tl.get(tl.size() - 1), newTask); + tl.remove(tl.size() - 1); + assertEquals(tl.size(), taskListSize); + } +} From ae099f53ead0bf815ed2ceea413e7940309449d2 Mon Sep 17 00:00:00 2001 From: brian16600 <brian974902@gmail.com> Date: Sun, 6 Feb 2022 18:32:52 +0800 Subject: [PATCH 19/47] Added NumberFormatException handling for delete, unmark and mark if not in correct format. Added proper javadocs documentation --- src/main/java/InputHandler.java | 15 ++++++++++++- src/main/java/Parser.java | 38 ++++++++++++++++++++++----------- 2 files changed, 40 insertions(+), 13 deletions(-) diff --git a/src/main/java/InputHandler.java b/src/main/java/InputHandler.java index fd24eea6ee..ee7a706581 100644 --- a/src/main/java/InputHandler.java +++ b/src/main/java/InputHandler.java @@ -36,7 +36,7 @@ public InputHandler() throws IOException { /** - * + * Handles input from Duke.java * @param input String input from main in Duke.java, from user input * @return boolean representing isChatEnded variable in main. If "bye" command is given, a true boolean is returned, else false is returned * @throws DukeException Invalid input types, or unrecognisable commands @@ -47,6 +47,7 @@ public boolean handleInput(String input) throws DukeException, IOException { Parser parser = new Parser(); switch (inputCommand) { case "todo": + //Confirms that input is in the format: todo [task] if (splitInput.length > 1) { Todo newTodo = (Todo) parser.parse(CommandType.TODO, splitInput); this.storage.writeData(newTodo); @@ -56,6 +57,7 @@ public boolean handleInput(String input) throws DukeException, IOException { throw new DukeException(":( OOPS!!! The description of a todo cannot be empty. Correct usage: todo [task]"); } case "event": + //Confirms that input is in the format: event [task] /at [date] [time(optional)] if (splitInput.length > 3) { Event newEvent = (Event) parser.parse(CommandType.EVENT, splitInput); this.storage.writeData(newEvent); @@ -66,6 +68,7 @@ public boolean handleInput(String input) throws DukeException, IOException { throw new DukeException(":( OOPS!!! The description of a event cannot be empty. Correct usage: event [task] /at [time]"); } case "deadline": + //Confirms that input is in the format: deadline [task] /by [date] [time(optional)] if (splitInput.length > 3) { Deadline newDeadline = (Deadline) parser.parse(CommandType.DEADLINE, splitInput); this.storage.writeData(newDeadline); @@ -74,28 +77,38 @@ public boolean handleInput(String input) throws DukeException, IOException { } else { throw new DukeException(":( OOPS!!! The description of a deadline cannot be empty. Correct usage: deadline [task] /by [time]"); } + case "list": + //Confirms that input command is simply "list" if (splitInput.length == 1) { parser.parse(CommandType.LIST, this.storage, splitInput); return false; } else { throw new DukeException("Wrong usage of list! Correct usage: list"); } + case "mark": + //Confirms that input is in the format mark [index] if (splitInput.length == 2) { + parser.parse(CommandType.MARK, this.storage, splitInput); return false; + } else { throw new DukeException("Wrong usage of mark! Correct usage: mark [index]"); } + case "unmark": + //Confirms that input is in the format mark [index] if (splitInput.length == 2) { parser.parse(CommandType.UNMARK, this.storage, splitInput); return false; } else { throw new DukeException("Wrong usage of unmark! Correct usage: unmark [index]"); } + case "delete": + //Confirms that input is in the format mark [index] if (splitInput.length == 2) { parser.parse(CommandType.DELETE, this.storage, splitInput); return false; diff --git a/src/main/java/Parser.java b/src/main/java/Parser.java index 8cbf55eb56..df5ad6c598 100644 --- a/src/main/java/Parser.java +++ b/src/main/java/Parser.java @@ -82,24 +82,38 @@ public void parse(InputHandler.CommandType type, Storage storage, String[] split break; case MARK: - System.out.println("Nice! I've marked this task as done:\n"); - int taskToBeMarkedIndex = Integer.parseInt(splitInput[1]) - 1; - Task taskToBeMarked = storage.get(taskToBeMarkedIndex); - taskToBeMarked.setMarkedTask(); + + try { + int taskToBeMarkedIndex = Integer.parseInt(splitInput[1]) - 1; + Task taskToBeMarked = storage.get(taskToBeMarkedIndex); + System.out.println("Nice! I've marked this task as done:\n"); + taskToBeMarked.setMarkedTask(); + } catch (NumberFormatException e) { + System.out.println("Make sure mark is in the format: mark [index]!"); + } break; case UNMARK: - System.out.println("OK, I've marked this task as not done yet:\n"); - int taskToBeUnmarkedIndex = Integer.parseInt(splitInput[1]) - 1; - Task taskToBeUnmarked = storage.get(taskToBeUnmarkedIndex); - taskToBeUnmarked.setUnmarkedTask(); + + try { + int taskToBeUnmarkedIndex = Integer.parseInt(splitInput[1]) - 1; + Task taskToBeUnmarked = storage.get(taskToBeUnmarkedIndex); + System.out.println("OK, I've marked this task as not done yet:\n"); + taskToBeUnmarked.setUnmarkedTask(); + } catch (NumberFormatException e) { + System.out.println("Make sure mark is in the format: mark [index]!"); + } break; case DELETE: - int idx = Integer.parseInt(splitInput[1]) - 1; - Task taskToBeDeleted = storage.get(idx); - storage.deleteData(idx); - System.out.println("Noted. I've removed this task:\n" + taskToBeDeleted + "\nNow you have " + storage.taskListSize() + " tasks in the list"); + try { + int idx = Integer.parseInt(splitInput[1]) - 1; + Task taskToBeDeleted = storage.get(idx); + storage.deleteData(idx); + System.out.println("Noted. I've removed this task:\n" + taskToBeDeleted + "\nNow you have " + storage.taskListSize() + " tasks in the list"); + } catch (NumberFormatException e) { + System.out.println("Make sure mark is in the format: mark [index]!"); + } break; default: From 390edec700f3ce30bdfdea2728f4978863f264de Mon Sep 17 00:00:00 2001 From: brian16600 <brian974902@gmail.com> Date: Sun, 6 Feb 2022 18:45:55 +0800 Subject: [PATCH 20/47] Changed formatting for coding standards based on feedback --- src/main/java/Deadline.java | 4 ++-- src/main/java/Duke.java | 2 -- src/main/java/DukeException.java | 5 +++++ src/main/java/Event.java | 3 ++- src/main/java/InputHandler.java | 8 +++++--- src/main/java/Task.java | 3 ++- src/main/java/Todo.java | 3 ++- 7 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/main/java/Deadline.java b/src/main/java/Deadline.java index e648f3ddd7..e5e3d99917 100644 --- a/src/main/java/Deadline.java +++ b/src/main/java/Deadline.java @@ -47,10 +47,10 @@ public Deadline(String name, String date, String time) throws DateTimeParseExcep } /** - * @override + * * @return String of Deadline task, eg [D][X] Deadline (by:XX) vs [D][✓] Deadline (by;XX) */ - + @Override public String toString() { String dueDateAndTime = (this.dueTime == null) ? dateConverterToString(this.dueDate) : dateConverterToString(this.dueDate) + " " + timeConverterToString(this.dueTime); return "[D]" + super.toString() + " (by:" + dueDateAndTime + ")"; } diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index c8927fe565..68fc032552 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -30,8 +30,6 @@ public static void main(String[] args) throws IOException { } System.out.println(endMessage); } - - } diff --git a/src/main/java/DukeException.java b/src/main/java/DukeException.java index c2d455a278..3c9b4f956a 100644 --- a/src/main/java/DukeException.java +++ b/src/main/java/DukeException.java @@ -15,6 +15,11 @@ */ public class DukeException extends Exception{ private String errorMessage; + + /** + * Constructs a DukeException. DukeException handles wrong inputs by user + * @param errorMessage Error message to be printed + */ public DukeException (String errorMessage) { this.errorMessage = errorMessage; } diff --git a/src/main/java/Event.java b/src/main/java/Event.java index 682c800384..a7e6e9fbc0 100644 --- a/src/main/java/Event.java +++ b/src/main/java/Event.java @@ -48,9 +48,10 @@ public Event (String name, String date, String time) throws DateTimeParseExcepti } /** - * @override + * * @return String of Event task, eg: [E][X] Event (at:) vs [E] [✓] Event (at:) */ + @Override public String toString() { String dueDateAndTime = (this.dueTime == null) ? dateConverterToString(this.dueDate) : dateConverterToString(this.dueDate) + " " + timeConverterToString(this.dueTime); return "[E]" + super.toString() + " (at:" + dueDateAndTime + ")"; } diff --git a/src/main/java/InputHandler.java b/src/main/java/InputHandler.java index ee7a706581..a9bf58e443 100644 --- a/src/main/java/InputHandler.java +++ b/src/main/java/InputHandler.java @@ -75,7 +75,8 @@ public boolean handleInput(String input) throws DukeException, IOException { printAddTaskMessage(newDeadline); return false; } else { - throw new DukeException(":( OOPS!!! The description of a deadline cannot be empty. Correct usage: deadline [task] /by [time]"); + throw new DukeException(":( OOPS!!! The description of a deadline cannot be empty. " + + "Correct usage: deadline [task] /by [time]"); } case "list": @@ -118,8 +119,9 @@ public boolean handleInput(String input) throws DukeException, IOException { case "bye": return true; default: - throw new DukeException(":( OOPS!!! I'm sorry, but I don't know what that means! Possible commands: todo [task], event [task] /at [time]," - + " deadline [task] /by [time], mark [index], unmark [index], delete [index], bye"); + throw new DukeException(":( OOPS!!! I'm sorry, but I don't know what that means! Possible commands: " + + "todo [task], event [task] /at [time]," + + " deadline [task] /by [time], mark [index], unmark [index], delete [index], bye"); } } diff --git a/src/main/java/Task.java b/src/main/java/Task.java index f9feb0fe2a..1af59f0616 100644 --- a/src/main/java/Task.java +++ b/src/main/java/Task.java @@ -75,9 +75,10 @@ public String timeConverterToString(LocalTime time) { } /** - * @override + * * @return String version of task, with marked and name. E.g. [X] Task vs [✓] Task */ + @Override public String toString() { if (this.isMarked) { String marked = "[✓] "; diff --git a/src/main/java/Todo.java b/src/main/java/Todo.java index 26c0a7b67b..d2af1fe964 100644 --- a/src/main/java/Todo.java +++ b/src/main/java/Todo.java @@ -25,9 +25,10 @@ public Todo (String name) { } /** - * @override + * * @return String of Todo task, eg: [T][X] Todo */ + @Override public String toString() { return "[T]" + super.toString(); } From 2607baf257cc90010fdffe3f2eb1e4b9a43f96ce Mon Sep 17 00:00:00 2001 From: brian16600 <brian974902@gmail.com> Date: Sun, 6 Feb 2022 20:08:22 +0800 Subject: [PATCH 21/47] Added find and NumberFormatException in cases where non-integer is passed in. --- src/main/java/InputHandler.java | 15 ++++++++++++- src/main/java/Parser.java | 40 +++++++++++++++++++++++++++++++-- src/main/java/Storage.java | 8 +++++-- src/main/java/TaskList.java | 2 +- 4 files changed, 59 insertions(+), 6 deletions(-) diff --git a/src/main/java/InputHandler.java b/src/main/java/InputHandler.java index a9bf58e443..336fb2da9b 100644 --- a/src/main/java/InputHandler.java +++ b/src/main/java/InputHandler.java @@ -116,8 +116,20 @@ public boolean handleInput(String input) throws DukeException, IOException { } else { throw new DukeException("Wrong usage of delete! Correct usage: delete [index]"); } + + case "find": + if (splitInput.length > 1) { + parser.parse(CommandType.FIND, this.storage, splitInput); + return false; + } else { + throw new DukeException("Uh oh! It seems like you did not specify what to find"); + } + case "bye": return true; + + + default: throw new DukeException(":( OOPS!!! I'm sorry, but I don't know what that means! Possible commands: " + "todo [task], event [task] /at [time]," @@ -144,7 +156,8 @@ enum CommandType { LIST, MARK, UNMARK, - DELETE + DELETE, + FIND } } diff --git a/src/main/java/Parser.java b/src/main/java/Parser.java index df5ad6c598..8fab487ab8 100644 --- a/src/main/java/Parser.java +++ b/src/main/java/Parser.java @@ -8,8 +8,12 @@ import duke.task.Task; import duke.task.Todo; import duke.task.Deadline; + import java.time.format.DateTimeParseException; + import java.util.Arrays; +import java.util.ArrayList; + import java.io.IOException; public class Parser { @@ -24,11 +28,13 @@ public class Parser { public Task parse(InputHandler.CommandType type, String[] splitInput) throws DukeException { switch (type) { case TODO: + //Removes the todo command word: i.e. todo task -> task String[] nameArray = Arrays.copyOfRange(splitInput, 1, splitInput.length); String todoName = String.join(" ", nameArray); return new Todo(todoName); case EVENT: + //Removes the event command word and separates into date and time (optional) String[] stringArrayExcludingEvent = Arrays.copyOfRange(splitInput, 1, splitInput.length); String stringExcludingEvent = String.join(" ", stringArrayExcludingEvent); String[] eventNameAndTimeArray = stringExcludingEvent.split("/at "); @@ -40,11 +46,13 @@ public Task parse(InputHandler.CommandType type, String[] splitInput) throws Duk Event newEvent = (eventTimeArray.length > 1) ? new Event(eventName, eventTimeArray[0], eventTimeArray[1]) : new Event(eventName, eventTimeArray[0]); return newEvent; } catch (DateTimeParseException e) { + //Datetime unable to be parsed throw new DukeException(":( OOPS!!! The correct format for date and time is yyyy-mm-dd and hh:mm"); } case DEADLINE: + //Removes the deadline command word and separates into date and time (optional) String[] stringArrayExcludingDeadline = Arrays.copyOfRange(splitInput, 1, splitInput.length); String stringExcludingDeadline = String.join(" ", stringArrayExcludingDeadline); String[] deadlineNameAndTimeArray = stringExcludingDeadline.split("/by "); @@ -56,6 +64,7 @@ public Task parse(InputHandler.CommandType type, String[] splitInput) throws Duk Deadline newDeadline = (deadlineTimeArray.length > 1) ? new Deadline(deadlineName, deadlineTimeArray[0], deadlineTimeArray[1]) : new Deadline(deadlineName, deadlineTimeArray[0]); return newDeadline; } catch (DateTimeParseException e) { + //Datetime unable to be parsed throw new DukeException(":( OOPS!!! The correct format for date and time is yyyy-mm-dd and hh:mm"); } @@ -82,40 +91,67 @@ public void parse(InputHandler.CommandType type, Storage storage, String[] split break; case MARK: - + //Marks task by index try { int taskToBeMarkedIndex = Integer.parseInt(splitInput[1]) - 1; Task taskToBeMarked = storage.get(taskToBeMarkedIndex); System.out.println("Nice! I've marked this task as done:\n"); taskToBeMarked.setMarkedTask(); } catch (NumberFormatException e) { + //Addresses the error of a non-integer being passed in System.out.println("Make sure mark is in the format: mark [index]!"); } break; case UNMARK: - + //Unmarks task by index try { int taskToBeUnmarkedIndex = Integer.parseInt(splitInput[1]) - 1; Task taskToBeUnmarked = storage.get(taskToBeUnmarkedIndex); System.out.println("OK, I've marked this task as not done yet:\n"); taskToBeUnmarked.setUnmarkedTask(); } catch (NumberFormatException e) { + //Addresses the issue of a non-integer being passed in System.out.println("Make sure mark is in the format: mark [index]!"); } break; case DELETE: + //Delete task by index try { int idx = Integer.parseInt(splitInput[1]) - 1; Task taskToBeDeleted = storage.get(idx); storage.deleteData(idx); System.out.println("Noted. I've removed this task:\n" + taskToBeDeleted + "\nNow you have " + storage.taskListSize() + " tasks in the list"); } catch (NumberFormatException e) { + //Addresses the issue of a non-integer being passed in System.out.println("Make sure mark is in the format: mark [index]!"); } break; + case FIND: + //Removes the find command and iterates through the TaskList to find a task name that contains the keyword + String[] stringArrayExcludingFind = Arrays.copyOfRange(splitInput, 1, splitInput.length); + String nameOfKeyWord = String.join(" ", stringArrayExcludingFind); + ArrayList<Task> arrayOfTasks = storage.accessTaskList().list; + ArrayList<Integer> indexOfFoundObjects = new ArrayList<>(); + for (int i = 0; i < arrayOfTasks.size(); i++) { + Task currentTask = arrayOfTasks.get(i); + if (currentTask.name.contains(nameOfKeyWord)) { + indexOfFoundObjects.add(i); + } + } + if (!indexOfFoundObjects.isEmpty()) { + //Task found and print + for (int j = 0; j < indexOfFoundObjects.size(); j++) { + System.out.println((j + 1) + "." + storage.get(indexOfFoundObjects.get(j))); + } + break; + } else { + //Unable to find + System.out.println("Uh oh! No task matches the description you've given :("); + } + default: throw new DukeException(":( OOPS!!! I'm sorry, but I don't know what that means! Possible commands: todo [task], event [task] /at [time]," + " deadline [task] /by [time], mark [index], unmark [index], delete [index], bye"); diff --git a/src/main/java/Storage.java b/src/main/java/Storage.java index f065e5c438..4e5c20fe5a 100644 --- a/src/main/java/Storage.java +++ b/src/main/java/Storage.java @@ -18,8 +18,8 @@ import java.io.FileWriter; public class Storage { - TaskList taskList; - String FILEPATH = "data/data.txt"; + private TaskList taskList; + final String FILEPATH = "data/data.txt"; /** * Constructs a Storage object. Loads the data from data/data.txt. if no data dir or data.txt is found, create an empty one @@ -157,4 +157,8 @@ public int taskListSize() { return this.taskList.size(); } + public TaskList accessTaskList() { + return this.taskList; + } + } diff --git a/src/main/java/TaskList.java b/src/main/java/TaskList.java index afcecb6489..3e840fd224 100644 --- a/src/main/java/TaskList.java +++ b/src/main/java/TaskList.java @@ -12,7 +12,7 @@ import duke.task.Task; import java.util.ArrayList; public class TaskList { - ArrayList<Task> list; + public ArrayList<Task> list; /** * Constructor for TaskList. Initialises an empty ArrayList<Task> From aca80eb3af1a6c3c283c28a8ec3a93c27305e677 Mon Sep 17 00:00:00 2001 From: brian16600 <brian974902@gmail.com> Date: Sat, 12 Feb 2022 17:28:15 +0800 Subject: [PATCH 22/47] Added JAR functionality --- 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..918fec1efb --- /dev/null +++ b/src/main/java/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: duke.duke.Duke + From f97ff93c138c33b51139c8f39361333cd943b3ec Mon Sep 17 00:00:00 2001 From: brian16600 <brian974902@gmail.com> Date: Sun, 13 Feb 2022 00:27:23 +0800 Subject: [PATCH 23/47] Shifted into packages --- build.gradle | 14 ++++++++++++++ src/main/java/{ => duke/duke}/Duke.java | 5 +++++ src/main/java/duke/duke/Launcher.java | 9 +++++++++ src/main/java/{ => duke/storage}/Storage.java | 0 src/main/java/{ => duke/storage}/TaskList.java | 0 src/main/java/{ => duke/task}/Deadline.java | 0 src/main/java/{ => duke/task}/Event.java | 0 src/main/java/{ => duke/task}/Task.java | 0 src/main/java/{ => duke/task}/Todo.java | 0 src/main/java/{ => duke/ui}/DukeException.java | 0 src/main/java/{ => duke/ui}/InputHandler.java | 0 src/main/java/{ => duke/ui}/Parser.java | 0 text-ui-test/input.txt | 6 ++++++ text-ui-test/runtest.bat | 6 ++++-- 14 files changed, 38 insertions(+), 2 deletions(-) rename src/main/java/{ => duke/duke}/Duke.java (89%) create mode 100644 src/main/java/duke/duke/Launcher.java rename src/main/java/{ => duke/storage}/Storage.java (100%) rename src/main/java/{ => duke/storage}/TaskList.java (100%) rename src/main/java/{ => duke/task}/Deadline.java (100%) rename src/main/java/{ => duke/task}/Event.java (100%) rename src/main/java/{ => duke/task}/Task.java (100%) rename src/main/java/{ => duke/task}/Todo.java (100%) rename src/main/java/{ => duke/ui}/DukeException.java (100%) rename src/main/java/{ => duke/ui}/InputHandler.java (100%) rename src/main/java/{ => duke/ui}/Parser.java (100%) diff --git a/build.gradle b/build.gradle index 0182b89fc1..133badba50 100644 --- a/build.gradle +++ b/build.gradle @@ -12,6 +12,20 @@ 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 { diff --git a/src/main/java/Duke.java b/src/main/java/duke/duke/Duke.java similarity index 89% rename from src/main/java/Duke.java rename to src/main/java/duke/duke/Duke.java index 68fc032552..ea7f3c9dac 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/duke/duke/Duke.java @@ -3,6 +3,11 @@ import duke.ui.DukeException; import duke.ui.InputHandler; +import javafx.application.Application; +import javafx.scene.Scene; +import javafx.scene.control.Label; +import javafx.stage.Stage; + import java.io.IOException; import java.util.Scanner; diff --git a/src/main/java/duke/duke/Launcher.java b/src/main/java/duke/duke/Launcher.java new file mode 100644 index 0000000000..235f012dd2 --- /dev/null +++ b/src/main/java/duke/duke/Launcher.java @@ -0,0 +1,9 @@ +package duke.duke; + +import javafx.application.Application; + +public class Launcher { + public static void main(String[] args) { + Application.launch(Duke.class, args); + } +} diff --git a/src/main/java/Storage.java b/src/main/java/duke/storage/Storage.java similarity index 100% rename from src/main/java/Storage.java rename to src/main/java/duke/storage/Storage.java diff --git a/src/main/java/TaskList.java b/src/main/java/duke/storage/TaskList.java similarity index 100% rename from src/main/java/TaskList.java rename to src/main/java/duke/storage/TaskList.java diff --git a/src/main/java/Deadline.java b/src/main/java/duke/task/Deadline.java similarity index 100% rename from src/main/java/Deadline.java rename to src/main/java/duke/task/Deadline.java diff --git a/src/main/java/Event.java b/src/main/java/duke/task/Event.java similarity index 100% rename from src/main/java/Event.java rename to src/main/java/duke/task/Event.java diff --git a/src/main/java/Task.java b/src/main/java/duke/task/Task.java similarity index 100% rename from src/main/java/Task.java rename to src/main/java/duke/task/Task.java diff --git a/src/main/java/Todo.java b/src/main/java/duke/task/Todo.java similarity index 100% rename from src/main/java/Todo.java rename to src/main/java/duke/task/Todo.java diff --git a/src/main/java/DukeException.java b/src/main/java/duke/ui/DukeException.java similarity index 100% rename from src/main/java/DukeException.java rename to src/main/java/duke/ui/DukeException.java diff --git a/src/main/java/InputHandler.java b/src/main/java/duke/ui/InputHandler.java similarity index 100% rename from src/main/java/InputHandler.java rename to src/main/java/duke/ui/InputHandler.java diff --git a/src/main/java/Parser.java b/src/main/java/duke/ui/Parser.java similarity index 100% rename from src/main/java/Parser.java rename to src/main/java/duke/ui/Parser.java diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index e69de29bb2..851a579e6e 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -0,0 +1,6 @@ +todo test1 +deadline test2 /by 2022-12-21 01:00 +event test3 /at 2022-03-05 13:00 +mark 2 +list +bye \ No newline at end of file diff --git a/text-ui-test/runtest.bat b/text-ui-test/runtest.bat index 0873744649..35045a388a 100644 --- a/text-ui-test/runtest.bat +++ b/text-ui-test/runtest.bat @@ -7,7 +7,8 @@ REM delete output from previous run if exist ACTUAL.TXT del ACTUAL.TXT REM compile the code into the bin folder -javac -cp ..\src\main\java -Xlint:none -d ..\bin ..\src\main\java\*.java +javac -cp ..\src\main\java\ -Xlint:none -d ..\bin ..\src\main\java\duke\duke\*.java + IF ERRORLEVEL 1 ( echo ********** BUILD FAILURE ********** exit /b 1 @@ -15,7 +16,8 @@ IF ERRORLEVEL 1 ( REM no error here, errorlevel == 0 REM run the program, feed commands from input.txt file and redirect the output to the ACTUAL.TXT -java -classpath ..\bin Duke < input.txt > ACTUAL.TXT +java -classpath ..\bin\duke\duke Duke < input.txt > ACTUAL.TXT +pause REM compare the output to the expected output FC ACTUAL.TXT EXPECTED.TXT From bb37f219d8572a3bc8ac94b41da556b3ecb29d36 Mon Sep 17 00:00:00 2001 From: brian16600 <brian974902@gmail.com> Date: Sun, 13 Feb 2022 00:31:30 +0800 Subject: [PATCH 24/47] revert --- src/main/java/duke/storage/Storage.java | 4 ++-- src/main/java/duke/storage/TaskList.java | 14 +------------- src/main/java/duke/task/Event.java | 15 --------------- src/main/java/duke/task/Task.java | 12 ++++++------ src/main/java/duke/task/Todo.java | 5 ----- src/main/java/duke/ui/InputHandler.java | 3 --- 6 files changed, 9 insertions(+), 44 deletions(-) diff --git a/src/main/java/duke/storage/Storage.java b/src/main/java/duke/storage/Storage.java index 4e5c20fe5a..f13bf7622a 100644 --- a/src/main/java/duke/storage/Storage.java +++ b/src/main/java/duke/storage/Storage.java @@ -141,7 +141,7 @@ public String list() { } /** - * Get task + * * @param idx index of task to be gotten * @return task that is requested */ @@ -150,7 +150,7 @@ public Task get(int idx) { } /** - * Size of tasklist + * * @return size of task list */ public int taskListSize() { diff --git a/src/main/java/duke/storage/TaskList.java b/src/main/java/duke/storage/TaskList.java index 3e840fd224..ff26570281 100644 --- a/src/main/java/duke/storage/TaskList.java +++ b/src/main/java/duke/storage/TaskList.java @@ -1,18 +1,6 @@ -package duke.storage; -import duke.duke.Duke; -import duke.ui.Parser; -import duke.ui.DukeException; -import duke.ui.InputHandler; -import duke.storage.Storage; -import duke.storage.TaskList; -import duke.task.Event; -import duke.task.Task; -import duke.task.Todo; -import duke.task.Deadline; -import duke.task.Task; import java.util.ArrayList; public class TaskList { - public ArrayList<Task> list; + ArrayList<Task> list; /** * Constructor for TaskList. Initialises an empty ArrayList<Task> diff --git a/src/main/java/duke/task/Event.java b/src/main/java/duke/task/Event.java index a7e6e9fbc0..d9ba7d0a87 100644 --- a/src/main/java/duke/task/Event.java +++ b/src/main/java/duke/task/Event.java @@ -21,26 +21,11 @@ public class Event extends Task { public LocalDate dueDate; public LocalTime dueTime; - - /** - * Constructor for Event with date - * @param name Name of Event - * @param date Date of event in yyyy-mm-dd format - * @throws DateTimeParseException If date time is in wrong format - */ public Event (String name, String date) throws DateTimeParseException { super(name); this.dueDate = LocalDate.parse(date); this.dueTime = null; } - - /** - * Constructor for Event with date and time - * @param name Name of Event - * @param date Date of event in yyyy-mm-dd format - * @param time Time of event in hh:mm format - * @throws DateTimeParseException If date time is in wrong format - */ public Event (String name, String date, String time) throws DateTimeParseException { super(name); this.dueDate = LocalDate.parse(date); diff --git a/src/main/java/duke/task/Task.java b/src/main/java/duke/task/Task.java index 1af59f0616..0e2b91db1e 100644 --- a/src/main/java/duke/task/Task.java +++ b/src/main/java/duke/task/Task.java @@ -22,7 +22,7 @@ public class Task { public String name; /** - * Constructor for Task. Is called in subclasses Todo, Event and Deadline + * Constructor * @param name name of the task */ public Task (String name) { @@ -31,7 +31,7 @@ public Task (String name) { } /** - * Marks task as done + * markTask as done */ public void setMarkedTask () { String markedMessage = "Nice! I've marked this task as done:\n"; @@ -40,7 +40,7 @@ public void setMarkedTask () { } /** - * Sets task to be unmarked + * unmarkTask */ public void setUnmarkedTask() { String unmarkedMessage = "OK, I've marked this task as not done yet:\n"; @@ -49,7 +49,7 @@ public void setUnmarkedTask() { } /** - * Whether task has been marked + * * @return boolean on whether task is marked */ public boolean hasBeenMarked() { @@ -57,7 +57,7 @@ public boolean hasBeenMarked() { } /** - * Converts date to String format for printing + * * @param date LocalDate for Deadline/Event tasks * @return String format: converts from yyyy-mm-dd format to Aug dd, yyyy format */ @@ -66,7 +66,7 @@ public String dateConverterToString(LocalDate date) { } /** - * Converts time to String format for printing + * * @param time LocalTime for Deadline/Event tasks * @return String format: converts hh:mm format to hh:mm am/pm format */ diff --git a/src/main/java/duke/task/Todo.java b/src/main/java/duke/task/Todo.java index d2af1fe964..f030b28144 100644 --- a/src/main/java/duke/task/Todo.java +++ b/src/main/java/duke/task/Todo.java @@ -15,11 +15,6 @@ */ public class Todo extends Task { - - /** - * Constructor for Todo - * @param name Name of Todo - */ public Todo (String name) { super(name); } diff --git a/src/main/java/duke/ui/InputHandler.java b/src/main/java/duke/ui/InputHandler.java index 336fb2da9b..4f7cacb157 100644 --- a/src/main/java/duke/ui/InputHandler.java +++ b/src/main/java/duke/ui/InputHandler.java @@ -146,9 +146,6 @@ public void printAddTaskMessage(Task task) { System.out.println("Got it. I've added this task:\n" + task + "\nNow you have " + this.storage.taskListSize() + " tasks in the list." ); } - /** - * Type of Command enum to be passed into Parser object - */ enum CommandType { TODO, EVENT, From 255fbefdc5da7de07a13ac485f3ad3d45145f90c Mon Sep 17 00:00:00 2001 From: brian16600 <brian974902@gmail.com> Date: Sun, 13 Feb 2022 01:08:38 +0800 Subject: [PATCH 25/47] Tentative changes --- src/main/data/data.txt | 0 src/main/java/duke/duke/Duke.java | 5 +++++ src/main/java/duke/storage/TaskList.java | 12 ++++++++++++ 3 files changed, 17 insertions(+) delete mode 100644 src/main/data/data.txt diff --git a/src/main/data/data.txt b/src/main/data/data.txt deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/main/java/duke/duke/Duke.java b/src/main/java/duke/duke/Duke.java index ea7f3c9dac..88c156a7fd 100644 --- a/src/main/java/duke/duke/Duke.java +++ b/src/main/java/duke/duke/Duke.java @@ -6,6 +6,11 @@ import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.control.Label; +import javafx.scene.control.Button; +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 java.io.IOException; diff --git a/src/main/java/duke/storage/TaskList.java b/src/main/java/duke/storage/TaskList.java index ff26570281..54aba747f8 100644 --- a/src/main/java/duke/storage/TaskList.java +++ b/src/main/java/duke/storage/TaskList.java @@ -1,3 +1,15 @@ +package duke.storage; + +import duke.duke.Duke; +import duke.ui.Parser; +import duke.ui.DukeException; +import duke.ui.InputHandler; +import duke.storage.Storage; +import duke.task.Event; +import duke.task.Task; +import duke.task.Todo; +import duke.task.Deadline; + import java.util.ArrayList; public class TaskList { ArrayList<Task> list; From b4458cc834af5620718b0b60e56a9d19b8e53d18 Mon Sep 17 00:00:00 2001 From: brian16600 <brian974902@gmail.com> Date: Tue, 15 Feb 2022 09:24:11 +0800 Subject: [PATCH 26/47] Adding Duke GUI --- src/main/java/duke/duke/Duke.java | 138 ++++++++++++++++++++++- src/main/java/duke/duke/Launcher.java | 4 +- src/main/java/duke/duke/Main.java | 37 ++++++ src/main/java/duke/storage/Storage.java | 8 +- src/main/java/duke/storage/TaskList.java | 2 +- src/main/java/duke/task/Task.java | 8 +- src/main/java/duke/ui/DialogBox.java | 75 ++++++++++++ src/main/java/duke/ui/InputHandler.java | 41 ++++--- src/main/java/duke/ui/MainWindow.java | 67 +++++++++++ src/main/java/duke/ui/Parser.java | 53 +++++---- src/main/resources/view/DialogBox.fxml | 16 +++ src/main/resources/view/MainWindow.fxml | 19 ++++ text-ui-test/EXPECTED.TXT | 24 +++- 13 files changed, 426 insertions(+), 66 deletions(-) create mode 100644 src/main/java/duke/duke/Main.java create mode 100644 src/main/java/duke/ui/DialogBox.java create mode 100644 src/main/java/duke/ui/MainWindow.java create mode 100644 src/main/resources/view/DialogBox.fxml create mode 100644 src/main/resources/view/MainWindow.fxml diff --git a/src/main/java/duke/duke/Duke.java b/src/main/java/duke/duke/Duke.java index 88c156a7fd..f94821995f 100644 --- a/src/main/java/duke/duke/Duke.java +++ b/src/main/java/duke/duke/Duke.java @@ -2,8 +2,12 @@ import duke.ui.DukeException; import duke.ui.InputHandler; +import duke.ui.DialogBox; +import duke.ui.Parser; +import duke.ui.InputHandler; import javafx.application.Application; +import javafx.application.Platform; import javafx.scene.Scene; import javafx.scene.control.Label; import javafx.scene.control.Button; @@ -12,11 +16,26 @@ 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; import java.io.IOException; import java.util.Scanner; -public class Duke { +public class Duke extends Application { + + //For GUI + private ScrollPane scrollPane; + private VBox dialogBox; + private TextField userInput; + private Button sendButton; + private Scene scene; + //images + private Image user = new Image(this.getClass().getResourceAsStream("/images/human.jpg")); + private Image duke = new Image(this.getClass().getResourceAsStream("/images/bear.jpg")); + + public static void main(String[] args) throws IOException { String logo = " ____ _ \n" + "| _ \\ _ _| | _____ \n" @@ -29,17 +48,126 @@ public static void main(String[] args) throws IOException { System.out.println(dukeGreeting); Scanner sc = new Scanner(System.in); InputHandler inputHandler = new InputHandler(); - boolean isChatEnded = false; - while (!isChatEnded) { + String response = ""; + while (!response.equals(endMessage)) { try { String input = sc.nextLine(); - isChatEnded = inputHandler.handleInput(input); + System.out.println(response); + response = inputHandler.handleInput(input); } catch (DukeException e) { System.out.println(e.getMessage()); } } - System.out.println(endMessage); } + + + /** + * Start the application + * @param stage Stage object + */ + @Override + public void start(Stage stage) { + //Setting up required components + Label dukeLabel = new Label("Duke"); + + //Creating container for the chat to scroll + scrollPane = new ScrollPane(); + dialogBox = new VBox(); + scrollPane.setContent(dialogBox); + + userInput = new TextField(); + sendButton = new Button("Send"); + + AnchorPane mainLayout = new AnchorPane(); + mainLayout.getChildren().addAll(scrollPane, userInput, sendButton); + + Scene scene = new Scene(mainLayout); + stage.setScene(scene); + stage.show(); + + //Formatting window + 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); + + dialogBox.setPrefHeight(Region.USE_COMPUTED_SIZE); + + userInput.setPrefWidth(325.0); + userInput.setPrefHeight(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); + + //Set functionality on User Input + sendButton.setOnMouseClicked((event) -> { + handleUserInput(); + }); + + userInput.setOnAction((event) -> { + handleUserInput(); + }); + + //scroll down if dialogBox's height changes + dialogBox.heightProperty().addListener((observable) -> scrollPane.setVvalue(1.0)); + + } + + /** + * Creates the dialogboxes as well as user and duke's inputs + */ + private void handleUserInput() { + String userText = userInput.getText(); + String dukeText = getResponse(userInput.getText()); + dialogBox.getChildren().addAll( + DialogBox.getUserDialog(userText, user), + DialogBox.getDukeDialog(dukeText, duke) + ); + userInput.clear(); + } + + /** + * Obtains the response of Duke + * @param input user's input + * @return Duke's reply to user's input + */ + public String getResponse(String input) { + String output = ""; + try { + InputHandler inputHandler = new InputHandler(); + output = inputHandler.handleInput(input); + } catch (DukeException e) { + return e.getMessage(); + } catch (IOException e) { + return e.getMessage(); + } + + return output; + } + + /** + * Returns a Label around the text + * @param text text for the dialog + * @return Label object with text + */ + private Label getDialogLabel(String text) { + Label textToAdd = new Label(text); + textToAdd.setWrapText(true); + return textToAdd; + } + + } diff --git a/src/main/java/duke/duke/Launcher.java b/src/main/java/duke/duke/Launcher.java index 235f012dd2..70364ce52b 100644 --- a/src/main/java/duke/duke/Launcher.java +++ b/src/main/java/duke/duke/Launcher.java @@ -1,9 +1,11 @@ package duke.duke; +import duke.duke.Duke; +import duke.ui.MainWindow; import javafx.application.Application; public class Launcher { public static void main(String[] args) { - Application.launch(Duke.class, args); + Application.launch(Main.class,args); } } diff --git a/src/main/java/duke/duke/Main.java b/src/main/java/duke/duke/Main.java new file mode 100644 index 0000000000..5119ce8f75 --- /dev/null +++ b/src/main/java/duke/duke/Main.java @@ -0,0 +1,37 @@ +package duke.duke; + +import duke.ui.MainWindow; + +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(); + + /** + * Starts the application + * @param stage stage object passed in + */ + @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.<MainWindow>getController().setDuke(duke); + stage.show(); + } catch (IOException e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/src/main/java/duke/storage/Storage.java b/src/main/java/duke/storage/Storage.java index f13bf7622a..f9014eec86 100644 --- a/src/main/java/duke/storage/Storage.java +++ b/src/main/java/duke/storage/Storage.java @@ -107,8 +107,8 @@ public void writeData(Task task) throws IOException { } /** - * Used when delete [index] is called for Duke. Deletes the entire file and rewrites it based on the new taskListay - * Amends the current stored taskListay as well + * Used when delete [index] is called for Duke. Deletes the entire file and rewrites it based on the new taskList + * Amends the current stored TaskList as well * @param idx index of task to be deleted * @throws IOException if there is an error rewriting data.txt */ @@ -157,6 +157,10 @@ public int taskListSize() { return this.taskList.size(); } + /** + * Allows external access to TaskList + * @return TaskList + */ public TaskList accessTaskList() { return this.taskList; } diff --git a/src/main/java/duke/storage/TaskList.java b/src/main/java/duke/storage/TaskList.java index 54aba747f8..59fa9d40fc 100644 --- a/src/main/java/duke/storage/TaskList.java +++ b/src/main/java/duke/storage/TaskList.java @@ -12,7 +12,7 @@ import java.util.ArrayList; public class TaskList { - ArrayList<Task> list; + public ArrayList<Task> list; /** * Constructor for TaskList. Initialises an empty ArrayList<Task> diff --git a/src/main/java/duke/task/Task.java b/src/main/java/duke/task/Task.java index 0e2b91db1e..52186be544 100644 --- a/src/main/java/duke/task/Task.java +++ b/src/main/java/duke/task/Task.java @@ -34,18 +34,14 @@ public Task (String name) { * markTask as done */ public void setMarkedTask () { - String markedMessage = "Nice! I've marked this task as done:\n"; this.isMarked = true; - System.out.println(markedMessage + " " + this); } /** * unmarkTask */ public void setUnmarkedTask() { - String unmarkedMessage = "OK, I've marked this task as not done yet:\n"; this.isMarked = false; - System.out.println(unmarkedMessage + " " + this); } /** @@ -57,7 +53,7 @@ public boolean hasBeenMarked() { } /** - * + * Converts date to String format for printing * @param date LocalDate for Deadline/Event tasks * @return String format: converts from yyyy-mm-dd format to Aug dd, yyyy format */ @@ -66,7 +62,7 @@ public String dateConverterToString(LocalDate date) { } /** - * + * Converts time to String format for printing * @param time LocalTime for Deadline/Event tasks * @return String format: converts hh:mm format to hh:mm am/pm format */ diff --git a/src/main/java/duke/ui/DialogBox.java b/src/main/java/duke/ui/DialogBox.java new file mode 100644 index 0000000000..b930de90d9 --- /dev/null +++ b/src/main/java/duke/ui/DialogBox.java @@ -0,0 +1,75 @@ +package duke.ui; + +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; + +public class DialogBox extends HBox { + @FXML + private Label dialog; + @FXML + private ImageView displayPicture; + + + /** + * Constructor + * @param text Text to be displayed + * @param img Image to be displayed + */ + 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<Node> tmp = FXCollections.observableArrayList(this.getChildren()); + Collections.reverse(tmp); + getChildren().setAll(tmp); + setAlignment(Pos.TOP_LEFT); + } + + /** + * Gets the user dialogbox + * @param text User's text + * @param img Image for the user + * @return DialogBox to be displayed + */ + public static DialogBox getUserDialog(String text, Image img) { + return new DialogBox(text, img); + } + + /** + * Gets Duke's dialogbox + * @param text User's text + * @param img Image for the user + * @return DialogBox to be displayed + */ + 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/ui/InputHandler.java b/src/main/java/duke/ui/InputHandler.java index 4f7cacb157..71ed0628ae 100644 --- a/src/main/java/duke/ui/InputHandler.java +++ b/src/main/java/duke/ui/InputHandler.java @@ -41,7 +41,9 @@ public InputHandler() throws IOException { * @return boolean representing isChatEnded variable in main. If "bye" command is given, a true boolean is returned, else false is returned * @throws DukeException Invalid input types, or unrecognisable commands */ - public boolean handleInput(String input) throws DukeException, IOException { + public String handleInput(String input) throws DukeException, IOException { + String endMessage = "Bye. Hope to see you again soon!"; + String[] splitInput = input.split(" "); String inputCommand = splitInput[0]; Parser parser = new Parser(); @@ -51,8 +53,8 @@ public boolean handleInput(String input) throws DukeException, IOException { if (splitInput.length > 1) { Todo newTodo = (Todo) parser.parse(CommandType.TODO, splitInput); this.storage.writeData(newTodo); - printAddTaskMessage(newTodo); - return false; + return addTaskMessage(newTodo); + } else { throw new DukeException(":( OOPS!!! The description of a todo cannot be empty. Correct usage: todo [task]"); } @@ -61,8 +63,7 @@ public boolean handleInput(String input) throws DukeException, IOException { if (splitInput.length > 3) { Event newEvent = (Event) parser.parse(CommandType.EVENT, splitInput); this.storage.writeData(newEvent); - printAddTaskMessage(newEvent); - return false; + return addTaskMessage(newEvent); } else { throw new DukeException(":( OOPS!!! The description of a event cannot be empty. Correct usage: event [task] /at [time]"); @@ -72,8 +73,7 @@ public boolean handleInput(String input) throws DukeException, IOException { if (splitInput.length > 3) { Deadline newDeadline = (Deadline) parser.parse(CommandType.DEADLINE, splitInput); this.storage.writeData(newDeadline); - printAddTaskMessage(newDeadline); - return false; + return addTaskMessage(newDeadline); } else { throw new DukeException(":( OOPS!!! The description of a deadline cannot be empty. " + "Correct usage: deadline [task] /by [time]"); @@ -82,8 +82,8 @@ public boolean handleInput(String input) throws DukeException, IOException { case "list": //Confirms that input command is simply "list" if (splitInput.length == 1) { - parser.parse(CommandType.LIST, this.storage, splitInput); - return false; + return parser.parse(CommandType.LIST, this.storage, splitInput); + } else { throw new DukeException("Wrong usage of list! Correct usage: list"); } @@ -91,9 +91,7 @@ public boolean handleInput(String input) throws DukeException, IOException { case "mark": //Confirms that input is in the format mark [index] if (splitInput.length == 2) { - - parser.parse(CommandType.MARK, this.storage, splitInput); - return false; + return parser.parse(CommandType.MARK, this.storage, splitInput); } else { throw new DukeException("Wrong usage of mark! Correct usage: mark [index]"); @@ -102,8 +100,8 @@ public boolean handleInput(String input) throws DukeException, IOException { case "unmark": //Confirms that input is in the format mark [index] if (splitInput.length == 2) { - parser.parse(CommandType.UNMARK, this.storage, splitInput); - return false; + return parser.parse(CommandType.UNMARK, this.storage, splitInput); + } else { throw new DukeException("Wrong usage of unmark! Correct usage: unmark [index]"); } @@ -111,22 +109,22 @@ public boolean handleInput(String input) throws DukeException, IOException { case "delete": //Confirms that input is in the format mark [index] if (splitInput.length == 2) { - parser.parse(CommandType.DELETE, this.storage, splitInput); - return false; + return parser.parse(CommandType.DELETE, this.storage, splitInput); + } else { throw new DukeException("Wrong usage of delete! Correct usage: delete [index]"); } case "find": if (splitInput.length > 1) { - parser.parse(CommandType.FIND, this.storage, splitInput); - return false; + return parser.parse(CommandType.FIND, this.storage, splitInput); + } else { throw new DukeException("Uh oh! It seems like you did not specify what to find"); } case "bye": - return true; + return endMessage; @@ -142,8 +140,9 @@ public boolean handleInput(String input) throws DukeException, IOException { * Prints out the task name that has been added as well as the number of tasks in the list * @param task The task that has been added */ - public void printAddTaskMessage(Task task) { - System.out.println("Got it. I've added this task:\n" + task + "\nNow you have " + this.storage.taskListSize() + " tasks in the list." ); + public String addTaskMessage(Task task) { + return "Got it. I've added this task:\n" + task + "\nNow you have " + this.storage.taskListSize() + + " tasks in the list." ; } enum CommandType { diff --git a/src/main/java/duke/ui/MainWindow.java b/src/main/java/duke/ui/MainWindow.java new file mode 100644 index 0000000000..0e0ddf9950 --- /dev/null +++ b/src/main/java/duke/ui/MainWindow.java @@ -0,0 +1,67 @@ +package duke.ui; + +import duke.duke.Duke; + +import javafx.application.Platform; +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; + + //setting the images + private Image userImage = new Image(this.getClass().getResourceAsStream("/images/human.jpg")); + private Image dukeImage = new Image(this.getClass().getResourceAsStream("/images/bear.jpg")); + + /** + * Initialises scrollpane + */ + @FXML + public void initialize() { + scrollPane.vvalueProperty().bind(dialogContainer.heightProperty()); + } + + /** + * Sets duke + * @param d Duke object passed in + */ + 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) + ); + String endMessage = "Bye. Hope to see you again soon!"; + if (response.equals(endMessage)) { + Platform.exit(); + } + userInput.clear(); + + } +} \ No newline at end of file diff --git a/src/main/java/duke/ui/Parser.java b/src/main/java/duke/ui/Parser.java index 8fab487ab8..26f148ce58 100644 --- a/src/main/java/duke/ui/Parser.java +++ b/src/main/java/duke/ui/Parser.java @@ -1,9 +1,6 @@ package duke.ui; -import duke.duke.Duke; -import duke.ui.DukeException; -import duke.ui.InputHandler; + import duke.storage.Storage; -import duke.storage.TaskList; import duke.task.Event; import duke.task.Task; import duke.task.Todo; @@ -18,6 +15,9 @@ public class Parser { + String defaultErrorMessage = ":( OOPS!!! I'm sorry, but I don't know what that means! Possible commands: todo [task]," + + " event [task] /at [time], deadline [task] /by [time], mark [index], unmark [index], delete [index], bye"; + /** * Parses input from InputHandler and returns a new Task to be added to TaskList. Handles event, deadline, todo commands * @param type CommandType of input, including (TODO, DEADLINE, EVENT) @@ -70,8 +70,7 @@ public Task parse(InputHandler.CommandType type, String[] splitInput) throws Duk } default: - throw new DukeException(":( OOPS!!! I'm sorry, but I don't know what that means! Possible commands: todo [task], event [task] /at [time]," - + " deadline [task] /by [time], mark [index], unmark [index], delete [index], bye"); + throw new DukeException(defaultErrorMessage); } } @@ -83,38 +82,44 @@ public Task parse(InputHandler.CommandType type, String[] splitInput) throws Duk * @throws DukeException Handles unrecognised commands * @throws IOException Handles IO Errors */ - public void parse(InputHandler.CommandType type, Storage storage, String[] splitInput) throws DukeException, IOException { + public String parse(InputHandler.CommandType type, Storage storage, String[] splitInput) throws DukeException, IOException { + + String wrongMarkFormatErrorMessage = "Make sure mark is in the format: mark [index]!"; + String wrongUnmarkFormatErrorMessage = "Make sure unmark is in the format: unmark [index]!"; + String wrongDeleteFormatErrorMessage = "Make sure delete is in the format delete [index]!"; + String cannotFindTaskMessage = "Uh oh! No task matches the description you've given :("; + + String listCommandStringIntro = "Here are the tasks in your list:\n"; + String markedMessage = "Nice! I've marked this task as done:\n"; + String unmarkedMessage = "OK, I've marked this task as not done yet:\n"; + switch (type) { case LIST: - System.out.println("Here are the tasks in your list:"); - System.out.println(storage.list()); - break; + return listCommandStringIntro + storage.list(); case MARK: //Marks task by index try { int taskToBeMarkedIndex = Integer.parseInt(splitInput[1]) - 1; Task taskToBeMarked = storage.get(taskToBeMarkedIndex); - System.out.println("Nice! I've marked this task as done:\n"); taskToBeMarked.setMarkedTask(); + return markedMessage + taskToBeMarked; } catch (NumberFormatException e) { //Addresses the error of a non-integer being passed in - System.out.println("Make sure mark is in the format: mark [index]!"); + throw new DukeException(wrongMarkFormatErrorMessage); } - break; case UNMARK: //Unmarks task by index try { int taskToBeUnmarkedIndex = Integer.parseInt(splitInput[1]) - 1; Task taskToBeUnmarked = storage.get(taskToBeUnmarkedIndex); - System.out.println("OK, I've marked this task as not done yet:\n"); taskToBeUnmarked.setUnmarkedTask(); + return unmarkedMessage + taskToBeUnmarked; } catch (NumberFormatException e) { //Addresses the issue of a non-integer being passed in - System.out.println("Make sure mark is in the format: mark [index]!"); + throw new DukeException(wrongUnmarkFormatErrorMessage); } - break; case DELETE: //Delete task by index @@ -122,12 +127,11 @@ public void parse(InputHandler.CommandType type, Storage storage, String[] split int idx = Integer.parseInt(splitInput[1]) - 1; Task taskToBeDeleted = storage.get(idx); storage.deleteData(idx); - System.out.println("Noted. I've removed this task:\n" + taskToBeDeleted + "\nNow you have " + storage.taskListSize() + " tasks in the list"); + return "Noted. I've removed this task:\n" + taskToBeDeleted + "\nNow you have " + storage.taskListSize() + " tasks in the list"; } catch (NumberFormatException e) { //Addresses the issue of a non-integer being passed in - System.out.println("Make sure mark is in the format: mark [index]!"); + throw new DukeException(wrongDeleteFormatErrorMessage); } - break; case FIND: //Removes the find command and iterates through the TaskList to find a task name that contains the keyword @@ -135,26 +139,27 @@ public void parse(InputHandler.CommandType type, Storage storage, String[] split String nameOfKeyWord = String.join(" ", stringArrayExcludingFind); ArrayList<Task> arrayOfTasks = storage.accessTaskList().list; ArrayList<Integer> indexOfFoundObjects = new ArrayList<>(); + for (int i = 0; i < arrayOfTasks.size(); i++) { Task currentTask = arrayOfTasks.get(i); if (currentTask.name.contains(nameOfKeyWord)) { indexOfFoundObjects.add(i); } } + String outputString = ""; if (!indexOfFoundObjects.isEmpty()) { //Task found and print for (int j = 0; j < indexOfFoundObjects.size(); j++) { - System.out.println((j + 1) + "." + storage.get(indexOfFoundObjects.get(j))); + outputString += (j + 1) + "." + storage.get(indexOfFoundObjects.get(j)); } - break; + return outputString; } else { //Unable to find - System.out.println("Uh oh! No task matches the description you've given :("); + throw new DukeException(cannotFindTaskMessage); } default: - throw new DukeException(":( OOPS!!! I'm sorry, but I don't know what that means! Possible commands: todo [task], event [task] /at [time]," - + " deadline [task] /by [time], mark [index], unmark [index], delete [index], bye"); + throw new DukeException(defaultErrorMessage); } } diff --git a/src/main/resources/view/DialogBox.fxml b/src/main/resources/view/DialogBox.fxml new file mode 100644 index 0000000000..ede775d4f9 --- /dev/null +++ b/src/main/resources/view/DialogBox.fxml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<?import javafx.geometry.Insets?> +<?import javafx.scene.control.Label?> +<?import javafx.scene.image.ImageView?> +<?import javafx.scene.layout.HBox?> + +<fx:root alignment="TOP_RIGHT" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" prefWidth="400.0" type="javafx.scene.layout.HBox" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1"> + <children> + <Label fx:id="dialog" text="Label" wrapText="true" /> + <ImageView fx:id="displayPicture" fitHeight="99.0" fitWidth="99.0" pickOnBounds="true" preserveRatio="true" /> + </children> + <padding> + <Insets bottom="15.0" left="5.0" right="5.0" top="15.0" /> + </padding> +</fx:root> diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml new file mode 100644 index 0000000000..83fddd291c --- /dev/null +++ b/src/main/resources/view/MainWindow.fxml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<?import javafx.scene.control.Button?> +<?import javafx.scene.control.ScrollPane?> +<?import javafx.scene.control.TextField?> +<?import javafx.scene.layout.AnchorPane?> +<?import javafx.scene.layout.VBox?> + +<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="400.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="duke.ui.MainWindow"> + <children> + <TextField fx:id="userInput" layoutY="558.0" onAction="#handleUserInput" prefHeight="41.0" prefWidth="324.0" AnchorPane.bottomAnchor="1.0" /> + <Button fx:id="sendButton" layoutX="324.0" layoutY="558.0" mnemonicParsing="false" onAction="#handleUserInput" prefHeight="41.0" prefWidth="76.0" text="Send" /> + <ScrollPane fx:id="scrollPane" hbarPolicy="NEVER" hvalue="1.0" prefHeight="557.0" prefWidth="400.0" vvalue="1.0"> + <content> + <VBox fx:id="dialogContainer" prefHeight="552.0" prefWidth="388.0" /> + </content> + </ScrollPane> + </children> +</AnchorPane> \ No newline at end of file diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 657e74f6e7..8dd60438c1 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,7 +1,19 @@ -Hello from - ____ _ -| _ \ _ _| | _____ -| | | | | | | |/ / _ \ -| |_| | |_| | < __/ -|____/ \__,_|_|\_\___| +Hello! I'm Duke +What can I do for you? +Got it. I've added this task: +[T][X] test1 +Now you have 1 tasks in the list. +Got it. I've added this task: +[D][X] test2 (by:21 December 2022 1:00 am) +Now you have 2 tasks in the list. +Got it. I've added this task: +[E][X] test3 (at:5 March 2022 1:00 pm) +Now you have 3 tasks in the list. +Nice! I've marked this task as done: + [D][✓] test2 (by:21 December 2022 1:00 am) +Here are the tasks in your list: +1. [T][X] test1 +2. [D][✓] test2 (by:21 December 2022 1:00 am) +3. [E][X] test3 (at:5 March 2022 1:00 pm) +Bye. Hope to see you again soon! \ No newline at end of file From 3960ddcabb2be8bb4072478b9d0db99fff43d2f8 Mon Sep 17 00:00:00 2001 From: brian16600 <brian974902@gmail.com> Date: Wed, 16 Feb 2022 00:32:47 +0800 Subject: [PATCH 27/47] Add Assertions Add Assertions for index out of bounds exception for delete/mark/unmark commands. Had to handle this exception for user error, change made addresses this. It is done using assertions to ensure index is within bounds. Change some of the code behind the bye command and storage. GUI was not exiting properly when bye was called, and some of the writing to storage had issues. Add a new method rewriteData() to address this issue and fixed bye command. Change storage writing for marked vs unmarked to 1(marked) vs 0(unmarked), since the GUI had issues displaying the tick. --- src/main/java/duke/duke/Duke.java | 1 + src/main/java/duke/storage/Storage.java | 13 +++++++++++++ src/main/java/duke/task/Task.java | 4 ++-- src/main/java/duke/ui/MainWindow.java | 7 +++++++ src/main/java/duke/ui/Parser.java | 16 +++++++++++++--- 5 files changed, 36 insertions(+), 5 deletions(-) diff --git a/src/main/java/duke/duke/Duke.java b/src/main/java/duke/duke/Duke.java index f94821995f..acbbf895d6 100644 --- a/src/main/java/duke/duke/Duke.java +++ b/src/main/java/duke/duke/Duke.java @@ -128,6 +128,7 @@ public void start(Stage stage) { * Creates the dialogboxes as well as user and duke's inputs */ private void handleUserInput() { + String userText = userInput.getText(); String dukeText = getResponse(userInput.getText()); dialogBox.getChildren().addAll( diff --git a/src/main/java/duke/storage/Storage.java b/src/main/java/duke/storage/Storage.java index f9014eec86..0c3dfd5c6f 100644 --- a/src/main/java/duke/storage/Storage.java +++ b/src/main/java/duke/storage/Storage.java @@ -93,6 +93,19 @@ public String taskToStringConverter(Task task) { return output; } + /** + * Rewrites entire storage based on current taskList + * @throws IOException If there is an error with writing to data.txt + */ + public void rewriteData() throws IOException { + FileWriter fw = new FileWriter(this.FILEPATH); + for (int i = 0; i < this.taskListSize(); i++) { + Task task = this.taskList.get(i); + fw.write(taskToStringConverter(task)); + } + fw.close(); + } + /** * Appends a single task to the file * @param task task to be added to the data.txt file diff --git a/src/main/java/duke/task/Task.java b/src/main/java/duke/task/Task.java index 52186be544..1b1646e929 100644 --- a/src/main/java/duke/task/Task.java +++ b/src/main/java/duke/task/Task.java @@ -77,10 +77,10 @@ public String timeConverterToString(LocalTime time) { @Override public String toString() { if (this.isMarked) { - String marked = "[✓] "; + String marked = "[1] "; return marked + this.name; } else { - String unmarked = "[X] "; + String unmarked = "[0] "; return unmarked + this.name; } } diff --git a/src/main/java/duke/ui/MainWindow.java b/src/main/java/duke/ui/MainWindow.java index 0e0ddf9950..39beb1727a 100644 --- a/src/main/java/duke/ui/MainWindow.java +++ b/src/main/java/duke/ui/MainWindow.java @@ -23,6 +23,8 @@ public class MainWindow extends AnchorPane { @FXML private Button sendButton; + boolean introductionDone = false; + private Duke duke; //setting the images @@ -51,6 +53,11 @@ public void setDuke(Duke d) { */ @FXML private void handleUserInput() { + if (!introductionDone) { + String dukeGreeting = "Hello! I'm Duke \nWhat can I do for you?"; + DialogBox.getDukeDialog(dukeGreeting, dukeImage); + introductionDone = true; + } String input = userInput.getText(); String response = duke.getResponse(input); dialogContainer.getChildren().addAll( diff --git a/src/main/java/duke/ui/Parser.java b/src/main/java/duke/ui/Parser.java index 26f148ce58..5938879046 100644 --- a/src/main/java/duke/ui/Parser.java +++ b/src/main/java/duke/ui/Parser.java @@ -38,10 +38,12 @@ public Task parse(InputHandler.CommandType type, String[] splitInput) throws Duk String[] stringArrayExcludingEvent = Arrays.copyOfRange(splitInput, 1, splitInput.length); String stringExcludingEvent = String.join(" ", stringArrayExcludingEvent); String[] eventNameAndTimeArray = stringExcludingEvent.split("/at "); + assert eventNameAndTimeArray.length > 1 : "Make sure to include the /at command!"; String eventNameWithExtraSpace = eventNameAndTimeArray[0]; String eventName = eventNameWithExtraSpace.substring(0, eventNameWithExtraSpace.length() -1); String eventTime = eventNameAndTimeArray[1]; String[] eventTimeArray = eventTime.split(" "); + try { Event newEvent = (eventTimeArray.length > 1) ? new Event(eventName, eventTimeArray[0], eventTimeArray[1]) : new Event(eventName, eventTimeArray[0]); return newEvent; @@ -56,10 +58,12 @@ public Task parse(InputHandler.CommandType type, String[] splitInput) throws Duk String[] stringArrayExcludingDeadline = Arrays.copyOfRange(splitInput, 1, splitInput.length); String stringExcludingDeadline = String.join(" ", stringArrayExcludingDeadline); String[] deadlineNameAndTimeArray = stringExcludingDeadline.split("/by "); + assert deadlineNameAndTimeArray.length > 1 : "Make sure to include the /by command!"; String deadlineNameWithSpace = deadlineNameAndTimeArray[0]; String deadlineName = deadlineNameWithSpace.substring(0, deadlineNameWithSpace.length() - 1); String deadlineTime = deadlineNameAndTimeArray[1]; String[] deadlineTimeArray = deadlineTime.split(" "); + try { Deadline newDeadline = (deadlineTimeArray.length > 1) ? new Deadline(deadlineName, deadlineTimeArray[0], deadlineTimeArray[1]) : new Deadline(deadlineName, deadlineTimeArray[0]); return newDeadline; @@ -101,8 +105,10 @@ public String parse(InputHandler.CommandType type, Storage storage, String[] spl //Marks task by index try { int taskToBeMarkedIndex = Integer.parseInt(splitInput[1]) - 1; + assert taskToBeMarkedIndex < storage.taskListSize() : "Index too big!"; Task taskToBeMarked = storage.get(taskToBeMarkedIndex); taskToBeMarked.setMarkedTask(); + storage.rewriteData(); return markedMessage + taskToBeMarked; } catch (NumberFormatException e) { //Addresses the error of a non-integer being passed in @@ -113,8 +119,10 @@ public String parse(InputHandler.CommandType type, Storage storage, String[] spl //Unmarks task by index try { int taskToBeUnmarkedIndex = Integer.parseInt(splitInput[1]) - 1; + assert taskToBeUnmarkedIndex < storage.taskListSize() : "Index too big!"; Task taskToBeUnmarked = storage.get(taskToBeUnmarkedIndex); taskToBeUnmarked.setUnmarkedTask(); + storage.rewriteData(); return unmarkedMessage + taskToBeUnmarked; } catch (NumberFormatException e) { //Addresses the issue of a non-integer being passed in @@ -124,9 +132,11 @@ public String parse(InputHandler.CommandType type, Storage storage, String[] spl case DELETE: //Delete task by index try { - int idx = Integer.parseInt(splitInput[1]) - 1; - Task taskToBeDeleted = storage.get(idx); - storage.deleteData(idx); + int idxOfTaskToBeDeleted = Integer.parseInt(splitInput[1]) - 1; + System.out.println(idxOfTaskToBeDeleted); + assert idxOfTaskToBeDeleted < storage.taskListSize() : "Index too big!"; + Task taskToBeDeleted = storage.get(idxOfTaskToBeDeleted); + storage.deleteData(idxOfTaskToBeDeleted); return "Noted. I've removed this task:\n" + taskToBeDeleted + "\nNow you have " + storage.taskListSize() + " tasks in the list"; } catch (NumberFormatException e) { //Addresses the issue of a non-integer being passed in From 9bd5f23bbdc04e57ee609c15575974f69df48c83 Mon Sep 17 00:00:00 2001 From: brian16600 <brian974902@gmail.com> Date: Wed, 16 Feb 2022 01:06:23 +0800 Subject: [PATCH 28/47] Clean up text-ui-test --- text-ui-test/EXPECTED.TXT | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 8dd60438c1..db3a588d2f 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,13 +1,13 @@ Hello! I'm Duke What can I do for you? Got it. I've added this task: -[T][X] test1 + [T][X] test1 Now you have 1 tasks in the list. Got it. I've added this task: -[D][X] test2 (by:21 December 2022 1:00 am) + [D][X] test2 (by:21 December 2022 1:00 am) Now you have 2 tasks in the list. Got it. I've added this task: -[E][X] test3 (at:5 March 2022 1:00 pm) + [E][X] test3 (at:5 March 2022 1:00 pm) Now you have 3 tasks in the list. Nice! I've marked this task as done: [D][✓] test2 (by:21 December 2022 1:00 am) From ee148e4fc3aa79f6913deeeb37c6a57fc6bc1d37 Mon Sep 17 00:00:00 2001 From: brian16600 <brian974902@gmail.com> Date: Wed, 16 Feb 2022 02:20:08 +0800 Subject: [PATCH 29/47] Tentative changes to code quality --- src/main/java/duke/storage/Storage.java | 5 +++-- src/main/java/duke/task/Deadline.java | 7 +++++-- src/main/java/duke/task/Event.java | 18 ++++++++++++++++++ src/main/java/duke/task/Task.java | 19 ++++++++++++++----- src/main/java/duke/task/Todo.java | 1 + 5 files changed, 41 insertions(+), 9 deletions(-) diff --git a/src/main/java/duke/storage/Storage.java b/src/main/java/duke/storage/Storage.java index f9014eec86..b22ac1a6f6 100644 --- a/src/main/java/duke/storage/Storage.java +++ b/src/main/java/duke/storage/Storage.java @@ -107,8 +107,9 @@ public void writeData(Task task) throws IOException { } /** - * Used when delete [index] is called for Duke. Deletes the entire file and rewrites it based on the new taskList - * Amends the current stored TaskList as well + * Deletes the entire file and rewrites it based on the new taskList. + * Amends the current stored TaskList as well. + * * @param idx index of task to be deleted * @throws IOException if there is an error rewriting data.txt */ diff --git a/src/main/java/duke/task/Deadline.java b/src/main/java/duke/task/Deadline.java index e5e3d99917..4b2956ea84 100644 --- a/src/main/java/duke/task/Deadline.java +++ b/src/main/java/duke/task/Deadline.java @@ -23,9 +23,10 @@ public class Deadline extends Task { /** * Constructor for Deadline with date + * * @param name Name of Deadline * @param date Date of deadline in yyyy-mm-dd format - * @throws DateTimeParseException If date time is in wrong format + * @throws DateTimeParseException If date is not in yyyy-mm-dd format */ public Deadline(String name, String date) throws DateTimeParseException { super(name); @@ -35,10 +36,11 @@ public Deadline(String name, String date) throws DateTimeParseException { /** * Constructor for Deadline with date and time + * * @param name Name of Deadline * @param date Date of deadline in yyyy-mm-dd format * @param time Time of deadline in hh:mm format - * @throws DateTimeParseException If date time is in wrong format + * @throws DateTimeParseException If date is not in yyyy-mm-dd format AND/OR time is not in hh:mm format */ public Deadline(String name, String date, String time) throws DateTimeParseException { super(name); @@ -47,6 +49,7 @@ public Deadline(String name, String date, String time) throws DateTimeParseExcep } /** + * String representation of Deadline * * @return String of Deadline task, eg [D][X] Deadline (by:XX) vs [D][✓] Deadline (by;XX) */ diff --git a/src/main/java/duke/task/Event.java b/src/main/java/duke/task/Event.java index d9ba7d0a87..a694fd6789 100644 --- a/src/main/java/duke/task/Event.java +++ b/src/main/java/duke/task/Event.java @@ -21,11 +21,28 @@ public class Event extends Task { public LocalDate dueDate; public LocalTime dueTime; + + /** + * Constructor for Event. Takes in name and date but no time. + * + * @param name name of the Event + * @param date date in yyyy-mm-dd format only + * @throws DateTimeParseException If date format is not in yyyy-mm-dd format + */ public Event (String name, String date) throws DateTimeParseException { super(name); this.dueDate = LocalDate.parse(date); this.dueTime = null; } + + /** + * Constructor for Event. Includes name, date and time + * + * @param name name of the Event + * @param date date in yyyy-mm-dd format only + * @param time time in hh:mm format + * @throws DateTimeParseException if date format is not in yyyy-mm-dd format AND/OR time is not in hh:mm format + */ public Event (String name, String date, String time) throws DateTimeParseException { super(name); this.dueDate = LocalDate.parse(date); @@ -33,6 +50,7 @@ public Event (String name, String date, String time) throws DateTimeParseExcepti } /** + * String representation of Event * * @return String of Event task, eg: [E][X] Event (at:) vs [E] [✓] Event (at:) */ diff --git a/src/main/java/duke/task/Task.java b/src/main/java/duke/task/Task.java index 52186be544..74a09363b9 100644 --- a/src/main/java/duke/task/Task.java +++ b/src/main/java/duke/task/Task.java @@ -22,7 +22,8 @@ public class Task { public String name; /** - * Constructor + * Constructor for Task + * * @param name name of the task */ public Task (String name) { @@ -31,6 +32,8 @@ public Task (String name) { } /** + * Marks current Task object as done + * * markTask as done */ public void setMarkedTask () { @@ -38,6 +41,8 @@ public void setMarkedTask () { } /** + * Unmarks current Task object + * * unmarkTask */ public void setUnmarkedTask() { @@ -45,6 +50,7 @@ public void setUnmarkedTask() { } /** + * Returns whether current Task object has been marked * * @return boolean on whether task is marked */ @@ -53,24 +59,27 @@ public boolean hasBeenMarked() { } /** - * Converts date to String format for printing + * Converts date to String format for display + * * @param date LocalDate for Deadline/Event tasks - * @return String format: converts from yyyy-mm-dd format to Aug dd, yyyy format + * @return String format: converts from yyyy-mm-dd format to mmm dd yyyy format, eg: Aug 21 2022 */ public String dateConverterToString(LocalDate date) { return DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG).format(date); } /** - * Converts time to String format for printing + * Converts time to String format for display + * * @param time LocalTime for Deadline/Event tasks - * @return String format: converts hh:mm format to hh:mm am/pm format + * @return String format: converts hh:mm format to hh:mm am/pm format eg: 1:30pm */ public String timeConverterToString(LocalTime time) { return DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT).format(time); } /** + * String representation of Task * * @return String version of task, with marked and name. E.g. [X] Task vs [✓] Task */ diff --git a/src/main/java/duke/task/Todo.java b/src/main/java/duke/task/Todo.java index f030b28144..48587770bf 100644 --- a/src/main/java/duke/task/Todo.java +++ b/src/main/java/duke/task/Todo.java @@ -20,6 +20,7 @@ public Todo (String name) { } /** + * String representation of Todo * * @return String of Todo task, eg: [T][X] Todo */ From 5344d8ee8352125cbf75b0ec884768484684932d Mon Sep 17 00:00:00 2001 From: brian16600 <brian974902@gmail.com> Date: Thu, 17 Feb 2022 01:12:31 +0800 Subject: [PATCH 30/47] Improve Code Quality based on Issue Tracker Update and add documentation for all methods. Improve abstraction throughout the code by abstracting out many Strings for return statements. Some methods reused the same String, hence abstracting them out makes the code cleaner. Shorten the method length for methods in Parser.java, InputHandler.java and Storage.java as method lengths were too long and add new methods for each case in the switch to handle the code. This was aimed at making the code cleaner. --- src/main/java/duke/duke/Duke.java | 19 +- src/main/java/duke/duke/Launcher.java | 5 +- src/main/java/duke/duke/Main.java | 1 + src/main/java/duke/storage/Storage.java | 149 ++++++++++---- src/main/java/duke/storage/TaskList.java | 21 +- src/main/java/duke/task/Deadline.java | 21 +- src/main/java/duke/task/Event.java | 20 +- src/main/java/duke/task/Task.java | 23 +-- src/main/java/duke/task/Todo.java | 16 +- src/main/java/duke/ui/DialogBox.java | 15 +- src/main/java/duke/ui/DukeException.java | 8 +- src/main/java/duke/ui/InputHandler.java | 144 +++++++------ src/main/java/duke/ui/MainWindow.java | 16 +- src/main/java/duke/ui/Parser.java | 252 +++++++++++++++-------- 14 files changed, 409 insertions(+), 301 deletions(-) diff --git a/src/main/java/duke/duke/Duke.java b/src/main/java/duke/duke/Duke.java index f94821995f..fccbce07e6 100644 --- a/src/main/java/duke/duke/Duke.java +++ b/src/main/java/duke/duke/Duke.java @@ -3,11 +3,9 @@ import duke.ui.DukeException; import duke.ui.InputHandler; import duke.ui.DialogBox; -import duke.ui.Parser; -import duke.ui.InputHandler; + import javafx.application.Application; -import javafx.application.Platform; import javafx.scene.Scene; import javafx.scene.control.Label; import javafx.scene.control.Button; @@ -18,7 +16,6 @@ import javafx.stage.Stage; import javafx.scene.layout.Region; import javafx.scene.image.Image; -import javafx.scene.image.ImageView; import java.io.IOException; import java.util.Scanner; @@ -31,17 +28,13 @@ public class Duke extends Application { private TextField userInput; private Button sendButton; private Scene scene; - //images + + //Images for Duke & user private Image user = new Image(this.getClass().getResourceAsStream("/images/human.jpg")); private Image duke = new Image(this.getClass().getResourceAsStream("/images/bear.jpg")); public static void main(String[] args) throws IOException { - String logo = " ____ _ \n" - + "| _ \\ _ _| | _____ \n" - + "| | | | | | | |/ / _ \\\n" - + "| |_| | |_| | < __/\n" - + "|____/ \\__,_|_|\\_\\___|\n"; String dukeGreeting = "Hello! I'm Duke \nWhat can I do for you?"; String endMessage = "Bye. Hope to see you again soon!"; @@ -125,7 +118,7 @@ public void start(Stage stage) { } /** - * Creates the dialogboxes as well as user and duke's inputs + * Creates the dialogboxes as well as handles user and duke's inputs */ private void handleUserInput() { String userText = userInput.getText(); @@ -138,7 +131,8 @@ private void handleUserInput() { } /** - * Obtains the response of Duke + * Returns the Response of Duke to user input + * * @param input user's input * @return Duke's reply to user's input */ @@ -158,6 +152,7 @@ public String getResponse(String input) { /** * Returns a Label around the text + * * @param text text for the dialog * @return Label object with text */ diff --git a/src/main/java/duke/duke/Launcher.java b/src/main/java/duke/duke/Launcher.java index 70364ce52b..c4f9be716e 100644 --- a/src/main/java/duke/duke/Launcher.java +++ b/src/main/java/duke/duke/Launcher.java @@ -1,9 +1,10 @@ package duke.duke; -import duke.duke.Duke; -import duke.ui.MainWindow; import javafx.application.Application; +/** + * Launches the Application for Duke + */ public class Launcher { public static void main(String[] args) { Application.launch(Main.class,args); diff --git a/src/main/java/duke/duke/Main.java b/src/main/java/duke/duke/Main.java index 5119ce8f75..ee17ea41d4 100644 --- a/src/main/java/duke/duke/Main.java +++ b/src/main/java/duke/duke/Main.java @@ -19,6 +19,7 @@ public class Main extends Application { /** * Starts the application + * * @param stage stage object passed in */ @Override diff --git a/src/main/java/duke/storage/Storage.java b/src/main/java/duke/storage/Storage.java index b22ac1a6f6..0c0b9c08d8 100644 --- a/src/main/java/duke/storage/Storage.java +++ b/src/main/java/duke/storage/Storage.java @@ -1,28 +1,36 @@ package duke.storage; -import duke.duke.Duke; -import duke.ui.Parser; + import duke.ui.DukeException; -import duke.ui.InputHandler; -import duke.storage.Storage; -import duke.storage.TaskList; import duke.task.Event; import duke.task.Task; import duke.task.Todo; import duke.task.Deadline; + import java.io.File; -import java.util.Scanner; import java.io.IOException; import java.nio.file.Paths; import java.nio.file.Files; import java.nio.file.Path; import java.io.FileWriter; +import java.util.Scanner; + public class Storage { + private TaskList taskList; + + final String symbolForMarked = "[1]"; + final String symbolForUnMarked = "[0]"; + final String symbolForTask = "[T]"; + final String symbolForDeadline = "[D]"; + final String symbolForEvent = "[E]"; final String FILEPATH = "data/data.txt"; + final String FILEDIRECTORY = "data"; /** - * Constructs a Storage object. Loads the data from data/data.txt. if no data dir or data.txt is found, create an empty one + * Constructs a Storage object from data in data.txt file. + * + * @throws IOException Issue when writing or reading from data.txt */ public Storage() throws IOException { try { @@ -31,70 +39,115 @@ public Storage() throws IOException { TaskList newTaskList = new TaskList(); while (sc.hasNextLine()) { String nextLine = sc.nextLine(); - String[] taskListaySplitBySpaces = nextLine.split(" "); - String taskType = taskListaySplitBySpaces[0]; - String[] taskListaySplitBySlash = nextLine.split(" / "); - switch (taskType) { - case "[T]": - Todo newTodo = new Todo(taskListaySplitBySlash[1]); - if (taskListaySplitBySpaces[1].equals("[✓]")) { - newTodo.setMarkedTask(); - } - newTaskList.add(newTodo); - break; - case "[D]": - Deadline newDeadline = (taskListaySplitBySlash[3].equals("null")) ? new Deadline(taskListaySplitBySlash[1], taskListaySplitBySlash[2]) : new Deadline(taskListaySplitBySlash[1], taskListaySplitBySlash[2], taskListaySplitBySlash[3]); - if (taskListaySplitBySpaces[1].equals("[✓]")) { - newDeadline.setMarkedTask(); - } - newTaskList.add(newDeadline); - break; - case "[E]": - Event newEvent = (taskListaySplitBySlash[3].equals("null")) ? new Event(taskListaySplitBySlash[1], taskListaySplitBySlash[2]) : new Event(taskListaySplitBySlash[1], taskListaySplitBySlash[2], taskListaySplitBySlash[3]); - if (taskListaySplitBySpaces[1].equals("[✓]")) { - newEvent.setMarkedTask(); - } - newTaskList.add(newEvent); - break; - default: - } + String[] taskLineSplitBySpace = nextLine.split(" "); + String taskType = taskLineSplitBySpace[0]; + String[] taskLineSplitBySlash = nextLine.split(" / "); + + newTaskList.add(convertToTask(taskType, taskLineSplitBySlash, taskLineSplitBySpace)); } this.taskList = newTaskList; } catch (IOException e){ - Path filePath = Paths.get("data"); + Path filePath = Paths.get(FILEDIRECTORY); boolean dataDirectoryExists = Files.exists(filePath); if (!dataDirectoryExists) { - new File("data").mkdir(); + new File(FILEDIRECTORY).mkdir(); } new File(FILEPATH).createNewFile(); this.taskList = new TaskList(); + } catch (DukeException e) { + //Wrong format of tasks in data.txt + System.out.println(e.getMessage()); } } /** + * Comprehends and converts input from data.txt into Task object * - * @param task Converts task to string format for storage in the data.txt file for records - * @return String format of the task eg: [D] [✓] deadline | duedate | duetime + * @param taskType Type of Task: Deadline, Event or Todo + * @param taskLineSplitBySlash Format of the line in a String array format split by slashes + * @param taskLineSplitBySpace Format of the line in a String array format split by spaces + * @return Task object to be put into TaskList + * @throws DukeException If tasks are stored in wrong format in data.txt + */ + public Task convertToTask(String taskType, String[] taskLineSplitBySlash, String[] taskLineSplitBySpace) throws DukeException { + + String wrongFormatError = "Tasks stored in wrong format"; + + switch (taskType) { + case symbolForTask: + Todo newTodo = new Todo(taskLineSplitBySlash[1]); + if (taskLineSplitBySpace[1].equals(symbolForMarked)) { + newTodo.setMarkedTask(); + } + return newTodo; + + case symbolForDeadline: + //Checks whether there is a time component for the stored Deadline + Deadline newDeadline = (taskLineSplitBySlash[3].equals("null")) + ? new Deadline(taskLineSplitBySlash[1], taskLineSplitBySlash[2]) + : new Deadline(taskLineSplitBySlash[1], taskLineSplitBySlash[2], taskLineSplitBySlash[3]); + + if (taskLineSplitBySpace[1].equals(symbolForMarked)) { + newDeadline.setMarkedTask(); + } + return newDeadline; + + case symbolForEvent: + //Checks whether there is a time component for the stored Event + Event newEvent = (taskLineSplitBySlash[3].equals("null")) + ? new Event(taskLineSplitBySlash[1], taskLineSplitBySlash[2]) + : new Event(taskLineSplitBySlash[1], taskLineSplitBySlash[2], taskLineSplitBySlash[3]); + + if (taskLineSplitBySpace[1].equals(symbolForMarked)) { + newEvent.setMarkedTask(); + } + return newEvent; + + default: + throw new DukeException(wrongFormatError); + } + } + + /** + * Converts task to string format for storage in the data.txt file for writing into the txt file + * + * @param task Task to be converted and written + * @return String format of the task eg: [D] [1] / deadline / duedate / duetime */ public String taskToStringConverter(Task task) { String output = ""; if (task instanceof Todo) { - String mark = (task.hasBeenMarked()) ? "[✓]" : "[X]"; - output = "[T] " + mark + " / " + task.name + "\n"; + String mark = (task.hasBeenMarked()) ? symbolForMarked : symbolForUnMarked; + output = symbolForTask + " " + mark + " / " + task.name + "\n"; } else if (task instanceof Deadline) { Deadline deadline = (Deadline) task; - String mark = (deadline.hasBeenMarked()) ? "[✓]" : "[X]"; - output = "[D] " + mark + " / " + deadline.name + " / " + deadline.dueDate + " / " + deadline.dueTime + "\n"; + String mark = (deadline.hasBeenMarked()) ? symbolForMarked : symbolForUnMarked; + output = symbolForDeadline + " " + mark + " / " + deadline.name + " / " + deadline.dueDate + " / " + deadline.dueTime + "\n"; } else if (task instanceof Event) { Event event = (Event) task; - String mark = (event.hasBeenMarked()) ? "[✓]" : "[X]"; - output = "[E] " + mark + " / " + event.name + " / " + event.dueDate + " / " + event.dueTime + "\n"; + String mark = (event.hasBeenMarked()) ? symbolForMarked : symbolForUnMarked; + output = symbolForEvent + " " + mark + " / " + event.name + " / " + event.dueDate + " / " + event.dueTime + "\n"; } return output; } + /** + * Rewrites entire storage based on current taskList + * + * @throws IOException If there is an error with writing to data.txt + */ + public void rewriteData() throws IOException { + FileWriter fw = new FileWriter(this.FILEPATH); + for (int i = 0; i < this.taskListSize(); i++) { + Task task = this.taskList.get(i); + fw.write(taskToStringConverter(task)); + } + fw.close(); + } + /** * Appends a single task to the file + * * @param task task to be added to the data.txt file * @throws IOException if there is an error appending the task to data.txt */ @@ -114,7 +167,6 @@ public void writeData(Task task) throws IOException { * @throws IOException if there is an error rewriting data.txt */ public void deleteData(int idx) throws IOException { - Task taskToBeDeleted = this.taskList.get(idx); taskList.remove(idx); FileWriter fw = new FileWriter(this.FILEPATH); for (int i = 0; i < this.taskList.size(); i++) { @@ -126,6 +178,7 @@ public void deleteData(int idx) throws IOException { /** * Obtains list of tasks from this.taskList and returns it + * * @return String that lists out the tasks currently */ public String list() { @@ -138,10 +191,14 @@ public String list() { listOfTasks += i + ". " + task + "\n"; } } + if (listOfTasks.equals("")) { + return "You currently have no tasks. Yay! :)"; + } return listOfTasks; } /** + * Gets task from Storage * * @param idx index of task to be gotten * @return task that is requested @@ -151,6 +208,7 @@ public Task get(int idx) { } /** + * Size of TaskList * * @return size of task list */ @@ -160,6 +218,7 @@ public int taskListSize() { /** * Allows external access to TaskList + * * @return TaskList */ public TaskList accessTaskList() { diff --git a/src/main/java/duke/storage/TaskList.java b/src/main/java/duke/storage/TaskList.java index 59fa9d40fc..fc80253fab 100644 --- a/src/main/java/duke/storage/TaskList.java +++ b/src/main/java/duke/storage/TaskList.java @@ -1,17 +1,10 @@ package duke.storage; -import duke.duke.Duke; -import duke.ui.Parser; -import duke.ui.DukeException; -import duke.ui.InputHandler; -import duke.storage.Storage; -import duke.task.Event; import duke.task.Task; -import duke.task.Todo; -import duke.task.Deadline; import java.util.ArrayList; public class TaskList { + public ArrayList<Task> list; /** @@ -22,7 +15,8 @@ public TaskList() { } /** - * Add Task to TaskList + * Adds Task to TaskList + * * @param task Task to be added */ public void add(Task task) { @@ -30,7 +24,8 @@ public void add(Task task) { } /** - * Remove Task from TaskList + * Removes Task from TaskList + * * @param index index of Task to be removed */ public void remove(int index) { @@ -38,7 +33,8 @@ public void remove(int index) { } /** - * Get task by index + * Gets task by index + * * @param index index of Task to be retrieved * @return Retrieved Task */ @@ -47,7 +43,8 @@ public Task get(int index) { } /** - * Size of tasklist + * Returns Size of TaskList + * * @return integer size of TaskList */ public int size() { diff --git a/src/main/java/duke/task/Deadline.java b/src/main/java/duke/task/Deadline.java index 4b2956ea84..d56d738ac5 100644 --- a/src/main/java/duke/task/Deadline.java +++ b/src/main/java/duke/task/Deadline.java @@ -1,14 +1,5 @@ package duke.task; -import duke.duke.Duke; -import duke.ui.Parser; -import duke.ui.DukeException; -import duke.ui.InputHandler; -import duke.storage.Storage; -import duke.storage.TaskList; -import duke.task.Event; -import duke.task.Task; -import duke.task.Todo; -import duke.task.Deadline; + import java.time.LocalDate; import java.time.LocalTime; import java.time.format.DateTimeParseException; @@ -49,12 +40,14 @@ public Deadline(String name, String date, String time) throws DateTimeParseExcep } /** - * String representation of Deadline + * Returns String representation of Deadline * - * @return String of Deadline task, eg [D][X] Deadline (by:XX) vs [D][✓] Deadline (by;XX) + * @return String of Deadline task, eg [D][0] Deadline (by: 22 Feb 2022 2:22pm) vs [D][1] Deadline (by: 22 Feb 2022 2:22pm) */ @Override public String toString() { - String dueDateAndTime = (this.dueTime == null) ? dateConverterToString(this.dueDate) : dateConverterToString(this.dueDate) + " " + timeConverterToString(this.dueTime); - return "[D]" + super.toString() + " (by:" + dueDateAndTime + ")"; } + String dueDateAndTime = (this.dueTime == null) + ? dateConverterToString(this.dueDate) + : dateConverterToString(this.dueDate) + " " + timeConverterToString(this.dueTime); + return "[D]" + super.toString() + " (by: " + dueDateAndTime + ")"; } } \ No newline at end of file diff --git a/src/main/java/duke/task/Event.java b/src/main/java/duke/task/Event.java index a694fd6789..dde0bc5b7a 100644 --- a/src/main/java/duke/task/Event.java +++ b/src/main/java/duke/task/Event.java @@ -1,14 +1,4 @@ package duke.task; -import duke.duke.Duke; -import duke.ui.Parser; -import duke.ui.DukeException; -import duke.ui.InputHandler; -import duke.storage.Storage; -import duke.storage.TaskList; -import duke.task.Event; -import duke.task.Task; -import duke.task.Todo; -import duke.task.Deadline; import java.time.LocalDate; import java.time.LocalTime; @@ -50,12 +40,14 @@ public Event (String name, String date, String time) throws DateTimeParseExcepti } /** - * String representation of Event + * Returns String representation of Event * - * @return String of Event task, eg: [E][X] Event (at:) vs [E] [✓] Event (at:) + * @return String of Event task, eg: [E][0] Event (at: 22 Feb 2022 2:22pm) vs [E][1] Event (at: 22 Feb 2022 2:22pm) */ @Override public String toString() { - String dueDateAndTime = (this.dueTime == null) ? dateConverterToString(this.dueDate) : dateConverterToString(this.dueDate) + " " + timeConverterToString(this.dueTime); - return "[E]" + super.toString() + " (at:" + dueDateAndTime + ")"; } + String dueDateAndTime = (this.dueTime == null) + ? dateConverterToString(this.dueDate) + : dateConverterToString(this.dueDate) + " " + timeConverterToString(this.dueTime); + return "[E]" + super.toString() + " (at: " + dueDateAndTime + ")"; } } \ No newline at end of file diff --git a/src/main/java/duke/task/Task.java b/src/main/java/duke/task/Task.java index 74a09363b9..6c71c5f86c 100644 --- a/src/main/java/duke/task/Task.java +++ b/src/main/java/duke/task/Task.java @@ -1,14 +1,5 @@ package duke.task; -import duke.duke.Duke; -import duke.ui.Parser; -import duke.ui.DukeException; -import duke.ui.InputHandler; -import duke.storage.Storage; -import duke.storage.TaskList; -import duke.task.Event; -import duke.task.Task; -import duke.task.Todo; -import duke.task.Deadline; + import java.time.LocalDate; import java.time.LocalTime; import java.time.format.DateTimeFormatter; @@ -33,8 +24,6 @@ public Task (String name) { /** * Marks current Task object as done - * - * markTask as done */ public void setMarkedTask () { this.isMarked = true; @@ -42,8 +31,6 @@ public void setMarkedTask () { /** * Unmarks current Task object - * - * unmarkTask */ public void setUnmarkedTask() { this.isMarked = false; @@ -79,17 +66,17 @@ public String timeConverterToString(LocalTime time) { } /** - * String representation of Task + * Returns String representation of Task * - * @return String version of task, with marked and name. E.g. [X] Task vs [✓] Task + * @return String version of task, with marked and name. E.g. [0] Task vs [1] Task */ @Override public String toString() { if (this.isMarked) { - String marked = "[✓] "; + String marked = "[1] "; return marked + this.name; } else { - String unmarked = "[X] "; + String unmarked = "[0] "; return unmarked + this.name; } } diff --git a/src/main/java/duke/task/Todo.java b/src/main/java/duke/task/Todo.java index 48587770bf..55cc2e600b 100644 --- a/src/main/java/duke/task/Todo.java +++ b/src/main/java/duke/task/Todo.java @@ -1,28 +1,18 @@ package duke.task; -import duke.duke.Duke; -import duke.ui.Parser; -import duke.ui.DukeException; -import duke.ui.InputHandler; -import duke.storage.Storage; -import duke.storage.TaskList; -import duke.task.Event; -import duke.task.Task; -import duke.task.Todo; -import duke.task.Deadline; + /** * Represents a Todo which is a subclass of Task * Includes a dueDate attribute. Overrides toString() from Task */ - public class Todo extends Task { public Todo (String name) { super(name); } /** - * String representation of Todo + * Returns String representation of Todo * - * @return String of Todo task, eg: [T][X] Todo + * @return String of Todo task, eg: [T][1] Todo */ @Override public String toString() { diff --git a/src/main/java/duke/ui/DialogBox.java b/src/main/java/duke/ui/DialogBox.java index b930de90d9..e43bc2c8af 100644 --- a/src/main/java/duke/ui/DialogBox.java +++ b/src/main/java/duke/ui/DialogBox.java @@ -16,20 +16,23 @@ import javafx.scene.layout.HBox; public class DialogBox extends HBox { + @FXML private Label dialog; @FXML private ImageView displayPicture; + final String dialogBoxFxmlLocation = "/view/DialogBox.fxml"; /** - * Constructor + * Constructor for DialogBox. Contains text and an image for Duke/User + * * @param text Text to be displayed * @param img Image to be displayed */ private DialogBox(String text, Image img) { try { - FXMLLoader fxmlLoader = new FXMLLoader(MainWindow.class.getResource("/view/DialogBox.fxml")); + FXMLLoader fxmlLoader = new FXMLLoader(MainWindow.class.getResource(dialogBoxFxmlLocation)); fxmlLoader.setController(this); fxmlLoader.setRoot(this); fxmlLoader.load(); @@ -52,7 +55,8 @@ private void flip() { } /** - * Gets the user dialogbox + * Returns new DialogBox for user with text contained + * * @param text User's text * @param img Image for the user * @return DialogBox to be displayed @@ -62,9 +66,10 @@ public static DialogBox getUserDialog(String text, Image img) { } /** - * Gets Duke's dialogbox + * Returns new DialogBox for Duke with response contained + * * @param text User's text - * @param img Image for the user + * @param img Image for Duke * @return DialogBox to be displayed */ public static DialogBox getDukeDialog(String text, Image img) { diff --git a/src/main/java/duke/ui/DukeException.java b/src/main/java/duke/ui/DukeException.java index 3c9b4f956a..cc9a84cc04 100644 --- a/src/main/java/duke/ui/DukeException.java +++ b/src/main/java/duke/ui/DukeException.java @@ -14,10 +14,12 @@ * Custom DukeException to be handled by InputHandler */ public class DukeException extends Exception{ + private String errorMessage; /** * Constructs a DukeException. DukeException handles wrong inputs by user + * * @param errorMessage Error message to be printed */ public DukeException (String errorMessage) { @@ -25,8 +27,12 @@ public DukeException (String errorMessage) { } /** + * Gets error message + * * @override Returns customised error message for DukeException when input is incorrect * @return String errorMessage */ - public String getMessage() {return this.errorMessage;} + public String getMessage() { + return this.errorMessage; + } } diff --git a/src/main/java/duke/ui/InputHandler.java b/src/main/java/duke/ui/InputHandler.java index 71ed0628ae..fd57bd4f12 100644 --- a/src/main/java/duke/ui/InputHandler.java +++ b/src/main/java/duke/ui/InputHandler.java @@ -1,143 +1,148 @@ package duke.ui; -import duke.duke.Duke; -import duke.ui.Parser; -import duke.ui.DukeException; -import duke.ui.InputHandler; + import duke.storage.Storage; -import duke.storage.TaskList; + import duke.task.Event; import duke.task.Task; import duke.task.Todo; import duke.task.Deadline; -import java.time.format.DateTimeParseException; -import java.util.Arrays; -import java.io.IOException; -import java.util.Arrays; + import java.io.IOException; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.LocalDate; -import java.time.format.DateTimeParseException; /** - * Handles input in main in Duke.java. Receives String from Scanner in main - * Processes the input into 7 categories: Todo, Event, Deadline, list, mark, unmark, bye and throws error + * Handles input from user + * Processes the input into 7 categories: Todo, Event, Deadline, list, mark, unmark, bye */ public class InputHandler { private Storage storage; + private Parser parser; /** - * Constructs an InputHandler for Duke.java to handle inputs from user + * Constructs an InputHandler and loads data into Storage object + * * @throws IOException If Storage class fails to initialise */ public InputHandler() throws IOException { this.storage = new Storage(); + this.parser = new Parser(); } /** - * Handles input from Duke.java - * @param input String input from main in Duke.java, from user input - * @return boolean representing isChatEnded variable in main. If "bye" command is given, a true boolean is returned, else false is returned - * @throws DukeException Invalid input types, or unrecognisable commands + * Handles input from Duke.java. + * + * @param input String input from user input. + * @return String output from Duke as response to user. + * @throws DukeException For invalid input types, or unrecognisable commands. */ public String handleInput(String input) throws DukeException, IOException { String endMessage = "Bye. Hope to see you again soon!"; String[] splitInput = input.split(" "); String inputCommand = splitInput[0]; - Parser parser = new Parser(); + switch (inputCommand) { case "todo": - //Confirms that input is in the format: todo [task] - if (splitInput.length > 1) { - Todo newTodo = (Todo) parser.parse(CommandType.TODO, splitInput); - this.storage.writeData(newTodo); - return addTaskMessage(newTodo); - - } else { - throw new DukeException(":( OOPS!!! The description of a todo cannot be empty. Correct usage: todo [task]"); - } + return taskCaseHandler(CommandType.TODO, splitInput ); case "event": - //Confirms that input is in the format: event [task] /at [date] [time(optional)] - if (splitInput.length > 3) { - Event newEvent = (Event) parser.parse(CommandType.EVENT, splitInput); - this.storage.writeData(newEvent); - return addTaskMessage(newEvent); - - } else { - throw new DukeException(":( OOPS!!! The description of a event cannot be empty. Correct usage: event [task] /at [time]"); - } + return taskCaseHandler(CommandType.EVENT, splitInput); case "deadline": - //Confirms that input is in the format: deadline [task] /by [date] [time(optional)] - if (splitInput.length > 3) { - Deadline newDeadline = (Deadline) parser.parse(CommandType.DEADLINE, splitInput); - this.storage.writeData(newDeadline); - return addTaskMessage(newDeadline); - } else { - throw new DukeException(":( OOPS!!! The description of a deadline cannot be empty. " + - "Correct usage: deadline [task] /by [time]"); - } - + return taskCaseHandler(CommandType.DEADLINE, splitInput); case "list": //Confirms that input command is simply "list" if (splitInput.length == 1) { return parser.parse(CommandType.LIST, this.storage, splitInput); - } else { throw new DukeException("Wrong usage of list! Correct usage: list"); } - case "mark": //Confirms that input is in the format mark [index] if (splitInput.length == 2) { - return parser.parse(CommandType.MARK, this.storage, splitInput); - + return this.parser.parse(CommandType.MARK, this.storage, splitInput); } else { throw new DukeException("Wrong usage of mark! Correct usage: mark [index]"); } - case "unmark": //Confirms that input is in the format mark [index] if (splitInput.length == 2) { - return parser.parse(CommandType.UNMARK, this.storage, splitInput); - + return this.parser.parse(CommandType.UNMARK, this.storage, splitInput); } else { throw new DukeException("Wrong usage of unmark! Correct usage: unmark [index]"); } - case "delete": //Confirms that input is in the format mark [index] if (splitInput.length == 2) { - return parser.parse(CommandType.DELETE, this.storage, splitInput); - + return this.parser.parse(CommandType.DELETE, this.storage, splitInput); } else { throw new DukeException("Wrong usage of delete! Correct usage: delete [index]"); } - case "find": if (splitInput.length > 1) { - return parser.parse(CommandType.FIND, this.storage, splitInput); - + return this.parser.parse(CommandType.FIND, this.storage, splitInput); } else { throw new DukeException("Uh oh! It seems like you did not specify what to find"); } - case "bye": return endMessage; + default: + throw new DukeException(":( OOPS!!! I'm sorry, but I don't know what that means! Possible commands: " + + "todo [task], event [task] /at [time]," + + " deadline [task] /by [time], mark [index], unmark [index], delete [index], bye"); + } + } + /** + * Handles Todo, Event and Deadline CommandTypes. Writes the task to storage and returns Duke's reply. + * + * @param typeOfTask Type of Task, Todo, Event or Deadline. + * @param splitInput String array of user input, split by spaces. + * @return String format of Duke's reply. + * @throws DukeException If CommandType passed in is wrong. + * @throws IOException If there is an issue writing to Storage. + */ + public String taskCaseHandler(CommandType typeOfTask, String[] splitInput) throws DukeException, IOException { + String emptyDescription = ":( OOPS!!! The description of a todo cannot be empty. Correct usage: "; + String todoFormat = "todo [name]"; + String eventFormat = "event [name] /at [date] [time(optional)]"; + String deadlineFormat = "deadline [name] /by /by [date] [time(optional)]"; + switch(typeOfTask) { + case TODO: + //Confirms that input is in the format: todo [task] + if (splitInput.length > 1) { + Todo newTodo = (Todo) parser.parse(CommandType.TODO, splitInput); + this.storage.writeData(newTodo); + return addTaskMessage(newTodo); + } else { + throw new DukeException(emptyDescription + todoFormat); + } + case DEADLINE: + //Confirms that input is in the format: deadline [task] /by [date] [time(optional)] + if (splitInput.length > 3) { + Deadline newDeadline = (Deadline) parser.parse(CommandType.DEADLINE, splitInput); + this.storage.writeData(newDeadline); + return addTaskMessage(newDeadline); + } else { + throw new DukeException(emptyDescription + deadlineFormat); + } + case EVENT: + //Confirms that input is in the format: event [task] /at [date] [time(optional)] + if (splitInput.length > 3) { + Event newEvent = (Event) parser.parse(CommandType.EVENT, splitInput); + this.storage.writeData(newEvent); + return addTaskMessage(newEvent); + } else { + throw new DukeException(emptyDescription + eventFormat); + } default: - throw new DukeException(":( OOPS!!! I'm sorry, but I don't know what that means! Possible commands: " + - "todo [task], event [task] /at [time]," - + " deadline [task] /by [time], mark [index], unmark [index], delete [index], bye"); + throw new DukeException("Incorrect format. Should never reach this stage"); } - } /** - * Prints out the task name that has been added as well as the number of tasks in the list + * Prints out the task name that has been added as well as the number of tasks in the list. + * * @param task The task that has been added */ public String addTaskMessage(Task task) { @@ -145,6 +150,9 @@ public String addTaskMessage(Task task) { " tasks in the list." ; } + /** + * Types of commands + */ enum CommandType { TODO, EVENT, diff --git a/src/main/java/duke/ui/MainWindow.java b/src/main/java/duke/ui/MainWindow.java index 0e0ddf9950..abd97d7df3 100644 --- a/src/main/java/duke/ui/MainWindow.java +++ b/src/main/java/duke/ui/MainWindow.java @@ -34,11 +34,14 @@ public class MainWindow extends AnchorPane { */ @FXML public void initialize() { + String dukeGreeting = "Hello! I'm Duke ^^ \nWhat can I do for you?"; scrollPane.vvalueProperty().bind(dialogContainer.heightProperty()); + dialogContainer.getChildren().addAll(DialogBox.getDukeDialog(dukeGreeting, dukeImage)); } /** * Sets duke + * * @param d Duke object passed in */ public void setDuke(Duke d) { @@ -51,17 +54,18 @@ public void setDuke(Duke d) { */ @FXML private void handleUserInput() { + String endMessage = "Bye. Hope to see you again soon!"; + String sleepErrorMessage = "Issue shutting down Duke! Force shutting down..."; + String input = userInput.getText(); String response = duke.getResponse(input); - dialogContainer.getChildren().addAll( - DialogBox.getUserDialog(input, userImage), - DialogBox.getDukeDialog(response, dukeImage) - ); - String endMessage = "Bye. Hope to see you again soon!"; + dialogContainer.getChildren().addAll(DialogBox.getUserDialog(input, userImage), + DialogBox.getDukeDialog(response, dukeImage)); + if (response.equals(endMessage)) { Platform.exit(); } - userInput.clear(); + userInput.clear(); } } \ No newline at end of file diff --git a/src/main/java/duke/ui/Parser.java b/src/main/java/duke/ui/Parser.java index 26f148ce58..535f652664 100644 --- a/src/main/java/duke/ui/Parser.java +++ b/src/main/java/duke/ui/Parser.java @@ -17,9 +17,11 @@ public class Parser { String defaultErrorMessage = ":( OOPS!!! I'm sorry, but I don't know what that means! Possible commands: todo [task]," + " event [task] /at [time], deadline [task] /by [time], mark [index], unmark [index], delete [index], bye"; + String dateAndTimeErrorMessage = ":( OOPS!!! The correct format for date and time is yyyy-mm-dd and hh:mm"; /** * Parses input from InputHandler and returns a new Task to be added to TaskList. Handles event, deadline, todo commands + * * @param type CommandType of input, including (TODO, DEADLINE, EVENT) * @param splitInput SplitInput from InputHandler is user's input, split by empty spaces for processing * @return Task object of new task to be added to TaskList @@ -32,50 +34,19 @@ public Task parse(InputHandler.CommandType type, String[] splitInput) throws Duk String[] nameArray = Arrays.copyOfRange(splitInput, 1, splitInput.length); String todoName = String.join(" ", nameArray); return new Todo(todoName); - case EVENT: - //Removes the event command word and separates into date and time (optional) - String[] stringArrayExcludingEvent = Arrays.copyOfRange(splitInput, 1, splitInput.length); - String stringExcludingEvent = String.join(" ", stringArrayExcludingEvent); - String[] eventNameAndTimeArray = stringExcludingEvent.split("/at "); - String eventNameWithExtraSpace = eventNameAndTimeArray[0]; - String eventName = eventNameWithExtraSpace.substring(0, eventNameWithExtraSpace.length() -1); - String eventTime = eventNameAndTimeArray[1]; - String[] eventTimeArray = eventTime.split(" "); - try { - Event newEvent = (eventTimeArray.length > 1) ? new Event(eventName, eventTimeArray[0], eventTimeArray[1]) : new Event(eventName, eventTimeArray[0]); - return newEvent; - } catch (DateTimeParseException e) { - //Datetime unable to be parsed - throw new DukeException(":( OOPS!!! The correct format for date and time is yyyy-mm-dd and hh:mm"); - - } - + return parseEvent(splitInput); case DEADLINE: - //Removes the deadline command word and separates into date and time (optional) - String[] stringArrayExcludingDeadline = Arrays.copyOfRange(splitInput, 1, splitInput.length); - String stringExcludingDeadline = String.join(" ", stringArrayExcludingDeadline); - String[] deadlineNameAndTimeArray = stringExcludingDeadline.split("/by "); - String deadlineNameWithSpace = deadlineNameAndTimeArray[0]; - String deadlineName = deadlineNameWithSpace.substring(0, deadlineNameWithSpace.length() - 1); - String deadlineTime = deadlineNameAndTimeArray[1]; - String[] deadlineTimeArray = deadlineTime.split(" "); - try { - Deadline newDeadline = (deadlineTimeArray.length > 1) ? new Deadline(deadlineName, deadlineTimeArray[0], deadlineTimeArray[1]) : new Deadline(deadlineName, deadlineTimeArray[0]); - return newDeadline; - } catch (DateTimeParseException e) { - //Datetime unable to be parsed - throw new DukeException(":( OOPS!!! The correct format for date and time is yyyy-mm-dd and hh:mm"); - - } - + return parseDeadline(splitInput); default: throw new DukeException(defaultErrorMessage); } } /** - * Parses input from InputHandler and writes/deletes/prints from storage accordingly. Handles list, mark, unmark, delete commands + * Parses input from InputHandler and writes/deletes/prints from storage accordingly. Handles list, mark, unmark, + * delete commands + * * @param type CommandType of input, including (LIST, MARK, UNMARK, DELETE) * @param storage Storage object in InputHandler to write/delete/get data from * @param splitInput SplitInput from InputHandler is user's input, split by empty spaces for processing @@ -84,83 +55,182 @@ public Task parse(InputHandler.CommandType type, String[] splitInput) throws Duk */ public String parse(InputHandler.CommandType type, Storage storage, String[] splitInput) throws DukeException, IOException { - String wrongMarkFormatErrorMessage = "Make sure mark is in the format: mark [index]!"; - String wrongUnmarkFormatErrorMessage = "Make sure unmark is in the format: unmark [index]!"; - String wrongDeleteFormatErrorMessage = "Make sure delete is in the format delete [index]!"; - String cannotFindTaskMessage = "Uh oh! No task matches the description you've given :("; - String listCommandStringIntro = "Here are the tasks in your list:\n"; - String markedMessage = "Nice! I've marked this task as done:\n"; - String unmarkedMessage = "OK, I've marked this task as not done yet:\n"; switch (type) { case LIST: return listCommandStringIntro + storage.list(); - case MARK: - //Marks task by index - try { - int taskToBeMarkedIndex = Integer.parseInt(splitInput[1]) - 1; - Task taskToBeMarked = storage.get(taskToBeMarkedIndex); - taskToBeMarked.setMarkedTask(); - return markedMessage + taskToBeMarked; - } catch (NumberFormatException e) { - //Addresses the error of a non-integer being passed in - throw new DukeException(wrongMarkFormatErrorMessage); + return parseMarkAndUnmark(InputHandler.CommandType.MARK, storage, splitInput); + case UNMARK: + return parseMarkAndUnmark(InputHandler.CommandType.UNMARK, storage, splitInput); + case DELETE: + return parseDelete(storage, splitInput); + case FIND: + return parseFind(storage, splitInput); + default: + throw new DukeException(defaultErrorMessage); + } + } + + /** + * Parses the EVENT CommandType and user input to create the Event object using the relevant data. + * + * @param splitInput User's input, split by spaces + * @return Event object to be added to storage. + * @throws DukeException if input is in the wrong datetime format. + */ + private Event parseEvent(String[] splitInput) throws DukeException { + //Removes the event command word and separates into date and time (optional) + String[] stringArrayExcludingEvent = Arrays.copyOfRange(splitInput, 1, splitInput.length); + String stringExcludingEvent = String.join(" ", stringArrayExcludingEvent); + String[] eventNameAndTimeArray = stringExcludingEvent.split("/at "); + String eventNameWithExtraSpace = eventNameAndTimeArray[0]; + String eventName = eventNameWithExtraSpace.substring(0, eventNameWithExtraSpace.length() -1); + String eventTime = eventNameAndTimeArray[1]; + String[] eventTimeArray = eventTime.split(" "); + + try { + Event newEvent = (eventTimeArray.length > 1) ? new Event(eventName, eventTimeArray[0], eventTimeArray[1]) : new Event(eventName, eventTimeArray[0]); + return newEvent; + } catch (DateTimeParseException e) { + //Datetime unable to be parsed + throw new DukeException(dateAndTimeErrorMessage); + } + } + + /** + * Parses the DEADLINE CommandType and user input to create the Deadline object using the relevant data. + * + * @param splitInput User's input, split by spaces. + * @return Deadline object to be added to storage. + * @throws DukeException If input is in the wrong datetime format/ + */ + private Deadline parseDeadline(String[] splitInput) throws DukeException { + //Removes the deadline command word and separates into date and time (optional) + String[] stringArrayExcludingDeadline = Arrays.copyOfRange(splitInput, 1, splitInput.length); + String stringExcludingDeadline = String.join(" ", stringArrayExcludingDeadline); + String[] deadlineNameAndTimeArray = stringExcludingDeadline.split("/by "); + String deadlineNameWithSpace = deadlineNameAndTimeArray[0]; + String deadlineName = deadlineNameWithSpace.substring(0, deadlineNameWithSpace.length() - 1); + String deadlineTime = deadlineNameAndTimeArray[1]; + String[] deadlineTimeArray = deadlineTime.split(" "); + + try { + Deadline newDeadline = (deadlineTimeArray.length > 1) ? new Deadline(deadlineName, deadlineTimeArray[0], deadlineTimeArray[1]) : new Deadline(deadlineName, deadlineTimeArray[0]); + return newDeadline; + } catch (DateTimeParseException e) { + //Datetime unable to be parsed + throw new DukeException(dateAndTimeErrorMessage); + } + } + + /** + * Parses the FIND CommandType and user input to find a list of tasks with matching descriptions + * + * @param storage Storage contains the task. This storage is iterated through to search. + * @param splitInput User's input split by spaces. + * @return String format of the task. + * @throws DukeException Unable to find the task. + */ + private String parseFind(Storage storage, String[] splitInput) throws DukeException{ + //Removes the find command and iterates through the TaskList to find a task name that contains the keyword + String cannotFindTaskMessage = "Uh oh! No task matches the description you've given :("; + String[] stringArrayExcludingFind = Arrays.copyOfRange(splitInput, 1, splitInput.length); + String nameOfKeyWord = String.join(" ", stringArrayExcludingFind); + ArrayList<Task> arrayOfTasks = storage.accessTaskList().list; + ArrayList<Integer> indexOfFoundObjects = new ArrayList<>(); + + for (int i = 0; i < arrayOfTasks.size(); i++) { + Task currentTask = arrayOfTasks.get(i); + if (currentTask.name.contains(nameOfKeyWord)) { + indexOfFoundObjects.add(i); } + } + + String outputString = ""; + if (!indexOfFoundObjects.isEmpty()) { + //Task found and print + for (int j = 0; j < indexOfFoundObjects.size(); j++) { + outputString += (j + 1) + "." + storage.get(indexOfFoundObjects.get(j)) + "\n"; + } + return outputString; + } else { + //Unable to find + throw new DukeException(cannotFindTaskMessage); + } + } + + /** + * Parses the CommandType DELETE and deletes the task from storage. + * + * @param storage Storage contains the task to be deleted. + * @param splitInput User's input split by spaces. + * @return String of Duke's reply that task has been deleted. + * @throws DukeException If there is a NumberFormatException with the index. + * @throws IOException If there is error by Storage in reading/writing to data.txt. + */ + private String parseDelete(Storage storage, String[] splitInput) throws DukeException, IOException { + //Delete task by index + String wrongDeleteFormatErrorMessage = "Make sure delete is in the format delete [index]!"; + String indexTooLarge = "Index too large!"; + + try { + int idxOfTaskToBeDeleted = Integer.parseInt(splitInput[1]) - 1; + System.out.println(idxOfTaskToBeDeleted); + assert idxOfTaskToBeDeleted < storage.taskListSize() : indexTooLarge; + Task taskToBeDeleted = storage.get(idxOfTaskToBeDeleted); + storage.deleteData(idxOfTaskToBeDeleted); + return "Noted. I've removed this task:\n" + taskToBeDeleted + "\nNow you have " + storage.taskListSize() + + " tasks in the list"; + } catch (NumberFormatException e) { + //Addresses the issue of a non-integer being passed in + throw new DukeException(wrongDeleteFormatErrorMessage); + } + } + + /** + * Marks or Unmarks task. + * + * @param type CommandType of task + * @param storage Storage to be iterated through for task + * @param splitInput User's input split by spaces + * @return String of Duke's reply that task has been marked/unmarked + * @throws DukeException NumberFormatError due to index or invalid CommandType + * @throws IOException If there is an issue reading/writing from data by Storage + */ + private String parseMarkAndUnmark(InputHandler.CommandType type, Storage storage, String[] splitInput) throws DukeException, IOException { + String markedMessage = "Nice! I've marked this task as done:\n"; + String unmarkedMessage = "OK, I've marked this task as not done yet:\n"; + String wrongMarkFormatErrorMessage = "Make sure mark is in the format: mark [index]!"; + String wrongUnmarkFormatErrorMessage = "Make sure unmark is in the format: unmark [index]!"; + switch (type) { case UNMARK: - //Unmarks task by index try { int taskToBeUnmarkedIndex = Integer.parseInt(splitInput[1]) - 1; Task taskToBeUnmarked = storage.get(taskToBeUnmarkedIndex); taskToBeUnmarked.setUnmarkedTask(); + storage.rewriteData(); return unmarkedMessage + taskToBeUnmarked; } catch (NumberFormatException e) { //Addresses the issue of a non-integer being passed in throw new DukeException(wrongUnmarkFormatErrorMessage); } - - case DELETE: - //Delete task by index + case MARK: + //Marks task by index try { - int idx = Integer.parseInt(splitInput[1]) - 1; - Task taskToBeDeleted = storage.get(idx); - storage.deleteData(idx); - return "Noted. I've removed this task:\n" + taskToBeDeleted + "\nNow you have " + storage.taskListSize() + " tasks in the list"; + int taskToBeMarkedIndex = Integer.parseInt(splitInput[1]) - 1; + Task taskToBeMarked = storage.get(taskToBeMarkedIndex); + taskToBeMarked.setMarkedTask(); + storage.rewriteData(); + return markedMessage + taskToBeMarked; } catch (NumberFormatException e) { - //Addresses the issue of a non-integer being passed in - throw new DukeException(wrongDeleteFormatErrorMessage); - } - - case FIND: - //Removes the find command and iterates through the TaskList to find a task name that contains the keyword - String[] stringArrayExcludingFind = Arrays.copyOfRange(splitInput, 1, splitInput.length); - String nameOfKeyWord = String.join(" ", stringArrayExcludingFind); - ArrayList<Task> arrayOfTasks = storage.accessTaskList().list; - ArrayList<Integer> indexOfFoundObjects = new ArrayList<>(); - - for (int i = 0; i < arrayOfTasks.size(); i++) { - Task currentTask = arrayOfTasks.get(i); - if (currentTask.name.contains(nameOfKeyWord)) { - indexOfFoundObjects.add(i); - } - } - String outputString = ""; - if (!indexOfFoundObjects.isEmpty()) { - //Task found and print - for (int j = 0; j < indexOfFoundObjects.size(); j++) { - outputString += (j + 1) + "." + storage.get(indexOfFoundObjects.get(j)); - } - return outputString; - } else { - //Unable to find - throw new DukeException(cannotFindTaskMessage); + //Addresses the error of a non-integer being passed in + throw new DukeException(wrongMarkFormatErrorMessage); } - default: - throw new DukeException(defaultErrorMessage); + throw new DukeException("Invalid CommandType. Should not reach here"); } } - } \ No newline at end of file From fee4ebbacfa8d19ed32448ae8245d69b3ae096d7 Mon Sep 17 00:00:00 2001 From: brian16600 <83962069+brian16600@users.noreply.github.com> Date: Thu, 17 Feb 2022 15:34:50 +0800 Subject: [PATCH 31/47] Update README.md --- README.md | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 8715d4d915..78eb333298 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,34 @@ -# Duke project template +# Duke -This is a project template for a greenfield Java project. It's named after the Java mascot _Duke_. Given below are instructions on how to use it. +Duke is a interactive chatbot with UI. -## Setting up in Intellij +Duke is a task tracker that can track three categories of tasks: +- Todo +- Deadline +- Event -Prerequisites: JDK 11, update Intellij to the most recent version. +## Setting up + +Setting up Duke is extremely easy. All you need to do is: +1) Download JDK 11 and above from [here](https://www.jetbrains.com/help/idea/sdk.html#set-up-jdk) +2) Download the latest JAR file release from the right side of this repository +3) Run the command java -jar ip.jar + +It's that simple! :) + +## Commands +There are a few commands you can run in Duke. These include: +[X] todo +[X] deadline +[X] event +[X] delete +[X] list +[X] find +[X] bye + +## Using an IDE +You can further edit/modify Duke by navigating to the `src` folder which contains all the `java` files -1. Open Intellij (if you are not in the welcome screen, click `File` > `Close Project` to close the existing project first) -1. Open the project into Intellij as follows: - 1. Click `Open`. - 1. Select the project directory, and click `OK`. - 1. If there are any further prompts, accept the defaults. -1. Configure the project to use **JDK 11** (not other versions) as explained in [here](https://www.jetbrains.com/help/idea/sdk.html#set-up-jdk).<br> - In the same dialog, set the **Project language level** field to the `SDK default` option. -3. After that, locate the `src/main/java/Duke.java` file, right-click it, and choose `Run Duke.main()` (if the code editor is showing compile errors, try restarting the IDE). If the setup is correct, you should see something like the below as the output: ``` Hello from ____ _ From e1dec30baebe8af07c33a217bb9116fa245191a3 Mon Sep 17 00:00:00 2001 From: brian16600 <83962069+brian16600@users.noreply.github.com> Date: Thu, 17 Feb 2022 15:36:47 +0800 Subject: [PATCH 32/47] Update README.md --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 78eb333298..3958fa3396 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,14 @@ There are a few commands you can run in Duke. These include: ## Using an IDE You can further edit/modify Duke by navigating to the `src` folder which contains all the `java` files +You can run it from Launcher.java: +``` +public class Launcher { + public static void main(String[] args) { + Application.launch(Main.class,args); + } +} +``` ``` Hello from ____ _ From c126d99d847efea969361326037737f14a3c7543 Mon Sep 17 00:00:00 2001 From: brian16600 <83962069+brian16600@users.noreply.github.com> Date: Thu, 17 Feb 2022 15:38:18 +0800 Subject: [PATCH 33/47] Create README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 3958fa3396..9b574cb6b2 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Duke Duke is a interactive chatbot with UI. +> "Your mind is for having ideas, not holding them" - David Allen Duke is a task tracker that can track three categories of tasks: - Todo From 133c672a1725330b7823f1a4b64048f7df655ee6 Mon Sep 17 00:00:00 2001 From: brian16600 <83962069+brian16600@users.noreply.github.com> Date: Thu, 17 Feb 2022 15:40:34 +0800 Subject: [PATCH 34/47] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 9b574cb6b2..d9394790e2 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,11 @@ You can further edit/modify Duke by navigating to the `src` folder which contain You can run it from Launcher.java: ``` public class Launcher { +```` +``` public static void main(String[] args) { + ``` + ```` Application.launch(Main.class,args); } } From 5d7d4fb894b83468297c5f4254ad616686a8b03a Mon Sep 17 00:00:00 2001 From: brian16600 <83962069+brian16600@users.noreply.github.com> Date: Thu, 17 Feb 2022 15:41:16 +0800 Subject: [PATCH 35/47] Update README.md --- README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d9394790e2..c736fed999 100644 --- a/README.md +++ b/README.md @@ -33,11 +33,9 @@ You can further edit/modify Duke by navigating to the `src` folder which contain You can run it from Launcher.java: ``` public class Launcher { -```` -``` +```ruby public static void main(String[] args) { - ``` - ```` + Application.launch(Main.class,args); } } From 87677f2969b9e5289c1a8a0942f7a940d04eb39c Mon Sep 17 00:00:00 2001 From: brian16600 <83962069+brian16600@users.noreply.github.com> Date: Thu, 17 Feb 2022 15:43:48 +0800 Subject: [PATCH 36/47] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c736fed999..8700935b82 100644 --- a/README.md +++ b/README.md @@ -31,15 +31,15 @@ There are a few commands you can run in Duke. These include: You can further edit/modify Duke by navigating to the `src` folder which contains all the `java` files You can run it from Launcher.java: -``` +<pre> public class Launcher { -```ruby - public static void main(String[] args) { + + <b>public static void main(String[] args)</b> { Application.launch(Main.class,args); } } -``` +</pre> ``` Hello from ____ _ From 805ab54bdd5db088ed44c682480f1aec408dca08 Mon Sep 17 00:00:00 2001 From: brian16600 <83962069+brian16600@users.noreply.github.com> Date: Thu, 17 Feb 2022 15:44:11 +0800 Subject: [PATCH 37/47] Update README.md --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 8700935b82..83e0617a77 100644 --- a/README.md +++ b/README.md @@ -33,9 +33,7 @@ You can further edit/modify Duke by navigating to the `src` folder which contain You can run it from Launcher.java: <pre> public class Launcher { - <b>public static void main(String[] args)</b> { - Application.launch(Main.class,args); } } From effc8c7d1ec06d7ffa3755d85a014178ae804963 Mon Sep 17 00:00:00 2001 From: brian16600 <83962069+brian16600@users.noreply.github.com> Date: Thu, 17 Feb 2022 15:45:40 +0800 Subject: [PATCH 38/47] Update README.md --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 83e0617a77..e9c2b8a263 100644 --- a/README.md +++ b/README.md @@ -19,18 +19,18 @@ It's that simple! :) ## Commands There are a few commands you can run in Duke. These include: -[X] todo -[X] deadline -[X] event -[X] delete -[X] list -[X] find -[X] bye +- [X] todo +- [X] deadline +- [X] event +- [X] delete +- [X] list +- [X] find +- [X] bye ## Using an IDE You can further edit/modify Duke by navigating to the `src` folder which contains all the `java` files -You can run it from Launcher.java: +You can run it from Launcher.java by using __main__: <pre> public class Launcher { <b>public static void main(String[] args)</b> { From 5e3b5c97bd1f73860d1a2376cdfe4eec9e5e60f4 Mon Sep 17 00:00:00 2001 From: brian16600 <83962069+brian16600@users.noreply.github.com> Date: Thu, 17 Feb 2022 15:46:22 +0800 Subject: [PATCH 39/47] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e9c2b8a263..74a398fa33 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ It's that simple! :) ## Commands There are a few commands you can run in Duke. These include: -- [X] todo +- [x] todo - [X] deadline - [X] event - [X] delete From 4c29aa2004ee539f22184af8623c5ad51fe3ce15 Mon Sep 17 00:00:00 2001 From: brian16600 <brian974902@gmail.com> Date: Thu, 17 Feb 2022 20:11:54 +0800 Subject: [PATCH 40/47] Add snooze command Add snooze command for the type B Extension. This functionality was added as an 8th command type. --- src/main/java/duke/storage/Storage.java | 6 +-- src/main/java/duke/task/Deadline.java | 43 ++++++++++++++++++-- src/main/java/duke/task/Event.java | 43 ++++++++++++++++++-- src/main/java/duke/ui/InputHandler.java | 5 ++- src/main/java/duke/ui/Parser.java | 53 +++++++++++++++++++++++-- 5 files changed, 136 insertions(+), 14 deletions(-) diff --git a/src/main/java/duke/storage/Storage.java b/src/main/java/duke/storage/Storage.java index 7fd1f702ed..7b1c48b8f7 100644 --- a/src/main/java/duke/storage/Storage.java +++ b/src/main/java/duke/storage/Storage.java @@ -122,12 +122,12 @@ public String taskToStringConverter(Task task) { } else if (task instanceof Deadline) { Deadline deadline = (Deadline) task; String mark = (deadline.hasBeenMarked()) ? symbolForMarked : symbolForUnMarked; - output = symbolForDeadline + " " + mark + " / " + deadline.name + " / " + deadline.dueDate + " / " - + deadline.dueTime + "\n"; + output = symbolForDeadline + " " + mark + " / " + deadline.name + " / " + deadline.getDueDate() + " / " + + deadline.getDueTime() + "\n"; } else if (task instanceof Event) { Event event = (Event) task; String mark = (event.hasBeenMarked()) ? symbolForMarked : symbolForUnMarked; - output = symbolForEvent + " " + mark + " / " + event.name + " / " + event.dueDate + " / " + event.dueTime + "\n"; + output = symbolForEvent + " " + mark + " / " + event.name + " / " + event.getDueDate() + " / " + event.getDueTime() + "\n"; } return output; } diff --git a/src/main/java/duke/task/Deadline.java b/src/main/java/duke/task/Deadline.java index d56d738ac5..1b54c44e9f 100644 --- a/src/main/java/duke/task/Deadline.java +++ b/src/main/java/duke/task/Deadline.java @@ -9,8 +9,8 @@ * Includes a dueDate attribute. Overrides toString() from Task */ public class Deadline extends Task { - public LocalDate dueDate; - public LocalTime dueTime; + private LocalDate dueDate; + private LocalTime dueTime; /** * Constructor for Deadline with date @@ -39,6 +39,42 @@ public Deadline(String name, String date, String time) throws DateTimeParseExcep this.dueTime = LocalTime.parse(time); } + /** + * Returns the dueDate of this Deadline object + * + * @return LocalDate object of dueDate + */ + public LocalDate getDueDate() { + return this.dueDate; + } + + /** + * Returns the dueTime of this Deadline object + * + * @return LocalTime object of dueTime + */ + public LocalTime getDueTime() { + return this.dueTime; + } + + /** + * Changes dueDate to new LocalDate + * + * @param date LocalDate object of new date + */ + public void changeDueDate(LocalDate date) { + this.dueDate = date; + } + + /** + * Changes dueTime to new LocalTime + * + * @param time LocalTIme object of new time + */ + public void changeDueTime(LocalTime time) { + this.dueTime = time; + } + /** * Returns String representation of Deadline * @@ -49,5 +85,6 @@ public String toString() { String dueDateAndTime = (this.dueTime == null) ? dateConverterToString(this.dueDate) : dateConverterToString(this.dueDate) + " " + timeConverterToString(this.dueTime); - return "[D]" + super.toString() + " (by: " + dueDateAndTime + ")"; } + return "[D]" + super.toString() + " (by: " + dueDateAndTime + ")"; + } } \ No newline at end of file diff --git a/src/main/java/duke/task/Event.java b/src/main/java/duke/task/Event.java index dde0bc5b7a..5e5e040535 100644 --- a/src/main/java/duke/task/Event.java +++ b/src/main/java/duke/task/Event.java @@ -9,8 +9,8 @@ * Overrides toString() from Task */ public class Event extends Task { - public LocalDate dueDate; - public LocalTime dueTime; + private LocalDate dueDate; + private LocalTime dueTime; /** * Constructor for Event. Takes in name and date but no time. @@ -39,6 +39,42 @@ public Event (String name, String date, String time) throws DateTimeParseExcepti this.dueTime = LocalTime.parse(time); } + /** + * Returns the LocalDate associated with the Event object + * + * @return LocalDate object of the dueDate + */ + public LocalDate getDueDate() { + return this.dueDate; + } + + /** + * Returns the dueTime of this Event object + * + * @return LocalTime object of dueTime + */ + public LocalTime getDueTime() { + return this.dueTime; + } + + /** + * Changes dueDate to new LocalDate + * + * @param date LocalDate object of new date + */ + public void changeDueDate(LocalDate date) { + this.dueDate = date; + } + + /** + * Changes dueTime to new LocalTime + * + * @param time LocalTIme object of new time + */ + public void changeDueTime(LocalTime time) { + this.dueTime = time; + } + /** * Returns String representation of Event * @@ -49,5 +85,6 @@ public String toString() { String dueDateAndTime = (this.dueTime == null) ? dateConverterToString(this.dueDate) : dateConverterToString(this.dueDate) + " " + timeConverterToString(this.dueTime); - return "[E]" + super.toString() + " (at: " + dueDateAndTime + ")"; } + return "[E]" + super.toString() + " (at: " + dueDateAndTime + ")"; + } } \ No newline at end of file diff --git a/src/main/java/duke/ui/InputHandler.java b/src/main/java/duke/ui/InputHandler.java index 0b61d1998c..6264dacc0b 100644 --- a/src/main/java/duke/ui/InputHandler.java +++ b/src/main/java/duke/ui/InputHandler.java @@ -92,8 +92,9 @@ public String handleInput(String input) throws DukeException, IOException { throw new DukeException(unableToFindErrorMessage); } case "snooze": - if (splitInput.length > 2) { - return parser.parse(CommandType.SNOOZE, this.storage, splitInput); + String splitInputBySlash[] = input.split(" /t "); + if (splitInputBySlash.length > 1 && splitInput.length > 5) { + return parser.parse(CommandType.SNOOZE, this.storage, splitInputBySlash); } else { throw new DukeException(unableToSnoozeErrorMessage); } diff --git a/src/main/java/duke/ui/Parser.java b/src/main/java/duke/ui/Parser.java index 31dc521fb4..25fdff2afb 100644 --- a/src/main/java/duke/ui/Parser.java +++ b/src/main/java/duke/ui/Parser.java @@ -6,6 +6,8 @@ import duke.task.Todo; import duke.task.Deadline; +import java.time.LocalDate; +import java.time.LocalTime; import java.time.format.DateTimeParseException; import java.util.Arrays; @@ -18,6 +20,7 @@ public class Parser { String defaultErrorMessage = ":( OOPS!!! I'm sorry, but I don't know what that means! Possible commands: todo [task]," + " event [task] /at [time], deadline [task] /by [time], mark [index], unmark [index], delete [index], bye"; String dateAndTimeErrorMessage = ":( OOPS!!! The correct format for date and time is yyyy-mm-dd and hh:mm"; + String snoozeErrorMessage = "Unable to find a matching task with that name and date :( Check again?"; /** * Parses input from InputHandler and returns a new Task to be added to TaskList. Handles event, deadline, todo commands @@ -196,7 +199,7 @@ private String parseDelete(Storage storage, String[] splitInput) throws DukeExce * * @param type CommandType of task * @param storage Storage to be iterated through for task - * @param splitInput User's input split by spaces + * @param splitInput User's input split by spaces except for snooze which is split by /t * @return String of Duke's reply that task has been marked/unmarked * @throws DukeException NumberFormatError due to index or invalid CommandType * @throws IOException If there is an issue reading/writing from data by Storage @@ -237,7 +240,51 @@ private String parseMarkAndUnmark(InputHandler.CommandType type, Storage storage } } - private String parseSnooze(Storage storage, String[] splitInput) { - return "snooze"; + private String parseSnooze(Storage storage, String[] splitInput) throws DukeException, IOException { + String[] nameAndPrevTimeArr = splitInput[0].split(" "); + String[] newDateAndTimeArr = splitInput[1].split(" "); + + String previousDateString = nameAndPrevTimeArr[nameAndPrevTimeArr.length - 1]; + LocalDate previousDate = LocalDate.parse(previousDateString); + String newDateString = newDateAndTimeArr[newDateAndTimeArr.length - 2]; + LocalDate newDate = LocalDate.parse(newDateString); + String newTimeString = newDateAndTimeArr[newDateAndTimeArr.length - 1]; + LocalTime newTime = LocalTime.parse(newTimeString); + + String taskName = ""; + for (int i = 1; i < nameAndPrevTimeArr.length - 1; i++) { + if (i == nameAndPrevTimeArr.length - 2) { + taskName += nameAndPrevTimeArr[i]; + } else { + taskName += nameAndPrevTimeArr[i] + " "; + } + } + + for (int j = 0; j < storage.taskListSize(); j++) { + if (storage.get(j).name.equals(taskName) && storage.get(j) instanceof Deadline) { + Deadline deadlineToBeChanged = (Deadline) storage.get(j); + if (deadlineToBeChanged.getDueDate().equals(previousDate)) { + deadlineToBeChanged.changeDueDate(newDate); + deadlineToBeChanged.changeDueTime(newTime); + storage.rewriteData(); + return "Changed Deadline " + taskName + " from " + deadlineToBeChanged.dateConverterToString(previousDate) + + " " + deadlineToBeChanged.dateConverterToString(newDate) + + " " + deadlineToBeChanged.timeConverterToString(newTime); + } + } + if (storage.get(j).name.equals(taskName) && storage.get(j) instanceof Event) { + Event eventToBeChanged = (Event) storage.get(j); + if (eventToBeChanged.getDueDate().equals(previousDate)) { + eventToBeChanged.changeDueDate(newDate); + eventToBeChanged.changeDueTime(newTime); + storage.rewriteData(); + return "Changed Event " + taskName + " from " + eventToBeChanged.dateConverterToString(previousDate) + + " " + eventToBeChanged.dateConverterToString(newDate) + + " " + eventToBeChanged.timeConverterToString(newTime); + } + } + } + throw new DukeException(snoozeErrorMessage); + } } \ No newline at end of file From 436be428d6f78ef1f382e08bdf43da0264038a92 Mon Sep 17 00:00:00 2001 From: brian16600 <brian974902@gmail.com> Date: Thu, 17 Feb 2022 21:13:36 +0800 Subject: [PATCH 41/47] Add Ui.png --- docs/Ui.png | Bin 0 -> 76039 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/Ui.png diff --git a/docs/Ui.png b/docs/Ui.png new file mode 100644 index 0000000000000000000000000000000000000000..342abd51f1f8b3112e1060e7070965c8f70139cc GIT binary patch literal 76039 zcmeFY2UJu`w=UX9RI-X>BnpCn<g6kYY>=EmGELK@h9(<8$vHGiXmV<xk(?1ka?Ux& zCP|hcdfOdz-+T7C=l{>U?~U=s-HSo>T)nERYR#H!);GUd>-)s_1pt|{yplWs3kwV2 zf%yY`UnCon^8i@`06-vs6951Z0C2D*09P;)jya<RAP@w=!N^#c3(CnUKRz)v0AwFG z=>Yf`kqC4C0P_!^+x-2?5BZ<t9}WDYfqyjcj|TqHz&{%Jzfc3XQP{sBh?^Ls@HPtP zr(75l{hN#}CJX??#Qc(fp$USpANu+SP5fVIHUHf5KN|Q)1OI5?9}WDYfxl>g|2{vz z_<eqHJ`qO#`{IHE;)48uzxxgVFb6OKEHJ<PzDRh>*45QXoR`-D%425fXl})0;RxaN zFmvMN<GIfZkbLOjWM%=ja%D8PvH>|rv45`nz|IJ=lw#Kv0^SEY$ynKf6un%mG`&={ zEWE%LVwUU=9}r)Y^bq%eI6<sj%@{o(_6|^S4=Lu~S{KL2KfdN=ejw>$X)UfHEB{9g z%$XGPA7gQMcjs{z;Bj=Z;pGz(6XU(l&&$uxjj6#6^>lDG^Wb)Xvi#A2tQFM41?1!k za&%z)(V&^RqnoP~vzr^pQrz0iTFBheQi$8!Ou&+xkI&MK+ssmckK0l}z*0nrPn6%9 z&z$*>?kz2T_U`27V*gv;mKMBL_Er!p2UjRY8+^RXyuT0n|5TqC75(h=Z`F^vM@$`Y z85b)vS1Vb}|AQaX$$g)nn@>dR&ne~+6Bdx<{a@-y^8Qf9Uk3VrcI5w(wjM}YT8LZx z7`vm(@7-%!IsZSKZF|rI4Au7g&0*x<1x%|Lle>ELswD4UJN%Ct`0X2v#bJK;(>^i3 zWc>f&_@|S9OUeJJ>mPOfTN?PcD*t0$|ETNV(!jq}`5){0|CzdeVO%Q*46t>_AlUEI z02u&2E-oG}4n7_(9sxc+!8LM1492)dK}tqMPDMdOO+`UPNz1^_L`%;`Pf5kh!_3BU zm+KxE4I{4rFDE}cCl}|BMzHV+2(Dedc9W3sCMO*g9p}G&d~X7f6W|eGeZ|IN23#S> z!Y0T1{s};bNiSZxg8d`M_~(I(hmC`E1)l(O)r1Uyg^i7cjYEt}f{nQeJWRza*f_Z4 zc-NWu@NY<~-(=<&5E3zSc7eVsDW%lVj!vgw5qzxW`Z^}ItbA~2_*Psqfv~yX9xI#X z;=YWmg@4A!J35wb@tH9A2&R2ntRL-RieCp7_7xmlJbcVWL2@ii?^w9F_&9$K5bFvy zIS!_4X?5HiX3l#znKfQTmo!uGrw<A&K9<>M`91|8`Y}XuY;wRuz_Ei|{nJUG<J+GU zi|v9tszPXa5t*mKH{qySLRstlo&4xRBHM^-{*<o9F=}7apUCzskqeMh$7egoKN|K5 z;Fi-CB(H45zUi|D^iD}xun3G-ZehwE6)cZ#e7~B$>pJQfjUoYG7{}JZn#7cH%$@In zP>GrMD>9e^Fwp=r^Ob+U;4s#A%`36(9-Rv=Y0Q|HmeK;oU2hkQu4xjzLc%Yy@Fssk zeEvJ2NBqTiz=F+pz_(Agw6uG9oojXay)zqn6{O@!7w#r;vk9bIh6@fnxoUwpAP^v* zb;Y%KBrA&}`$*2DpmAhM2>V%m`wedGVtZG&LL7o@MiM*wl-9mohWl3~aPaLD@_8#n zd>?Jx8~k*kePfp13f#=B|A-|q5Z@DASC#xyz6p0)?v<IW?9B%TmOgY*CM#ht*AA(= zrRl#t@UL-Ok@++mc@(+CYUDD%WOuRVe;PC{8F-pl@-6zSY><Qt?@~bbdfqo?xm1$@ zC1{r5`&uTJnkp>TaO+3B{Wn$#Ntx$3>>Ir>4d@2Sw=YA&VWO5+PgL2$wOU=byL(-& zP%88vJ;7`_7N#Uk%;|y8r8gePfq1hJUmO#uS$x)v1ML+_?O0+nrL5Er$-V;?70%OL zCM?XlS-@&~w%4eptAMpIvuJYiXF)*(K`;N+A`?|<sC^zES5*gL>q?2dF3j0MD?qbA zK!Vj2wh*0qSA)Z^;sBcQ@M}^oW?pzm(VFGu1L_(rK&6{bnjf3#)McB-<OlB442S># zO<4B7TgSDv`W!@YY>JYK5>9Nt5E3bx5&Wh<x{VSr_NqQc6+l>gdN6p#koFyL>9TnF z#!8j~NXu7Hz3Fu_tfM(RW}L)I|4xZpV8u2g$IQtx99&Y~PPDwQT#@m+`Q^`}x!jD6 zRg>4)a!R-HjF4GoAS5`zGYC2$y|Y4xHR%Y6Y4wY#Qq~>@^_d$a@ibdiB84ewbjoes zCeZ^(hGaewC%`6nD}RhO{a|EFBKl$?eofe%s9Hqy;;S!VG1Ip@g)g$4zAnj4!%>ba zeqZ__c;-ZKqUbND<0dj+fqLHoir<)JY3jp9d&D>@6mAkZg%4MOhR9PtNbWUn={9yF zzb5id-$rXO$Vuu9cujurT74niZZ-NGNVyYvl>4WEQpcxH&^{A1t$a9?@~2QEO9wO7 zT^W2n@g1Q2OIjs!o^&&fmAkOGy!)rX757i0|LF98H7SLeJHfrAxi*lN>#^SfQ#F+M z(Fm&##oqxjV@3UQCy&_K0m&rS=nM*8kZd=9QIYB!ToZn{w?FzFa7!TNn=F}R#SGmy zC0=r>N7t$32cA2BS!19&;Qsd0?&S!(A3Rrf@P333k%=Nv@U=@^dH8AcH|Hpw-H{tI zMQSMW*{FCkyu&?Y>b5>cZlJ`%OXQ6I5?{pitl*|265d!W>MF>0m|a&^zkXYtQ<%r7 zyad6q^{n=3rA!1YBi1VXFnN8GkRAz*0DW5KI_H5GG~n*e(?fDloJdbRLsr%P@e!Xl zj?eBzjmXu!8hJ~vC^!gNxO0VA*5GiHi|t-t0CL|kKt5S4Mw<_X-*wfnd{po_O4v(3 z1HtajFT1yv%16M*Jl&)oJM#>Ju3Vz;Z)EUr*nvs6GnCU3l;@+Ow2ihn%t_btx@F(d z)>#J<d0$&wh5C)E(Tz&4mOkl_Z|a<KZaT5TTjJ*O9U_d~Oj2bazPIZKOplmwZjW}a z9&Z7s#h|i8+o^3Ou-QEUzS(#~+{zcPg}3bEN3q&Cn%FHZKQq2SF2UDvjopB((qC8a zn?0A`ovficxk(!oYOrWbQ97+|_>|5(H>_i?Ip)!(bZH1ll2$V}P*Nz4V(eSzEKa<7 zIloNUGB@<Sh*L3ti3N_S*T8t{CUu70FW1{)06Js;b=!V{y|k>MIsEfj{^=ez(w z8^Dt)xHn%_xXOK<WXn<<8W;3$@^8jF;5g;g(xya)8mT$w=h{M>9qphaUW(|=uBhJ^ z+TT{&|4l*TjvqA9{sC$j<1?X$T_E;;smr4!K*p4-GBA~x3D)2t^{wbT;HvSack~*= z%1s`fO&L$VT5%8TFc;ZQW}cbnr*o-%UGILHllC{guln;1KDTcwH+Q@UF1U@xpdNqI zsCkNVhEm758u^2|0x%0ARcQiAq^%t`UG(m?HOF%Y4?o|DEe)f9)1vg_*yqRHy{Pi4 zB89HC+=~=$K{Y??k7sKR#<aBXDofYBxeB3#x=@Ol9(l2Tl2C3<it(G>MUA6M_OBIG z2n~mf+-uqk<11M0<KL8)6oaUkTDa&P5Tmf5tdvrXIYtXu@|ZwkY!?mQQl@GC2JTJG z$EK2Hmi!35cl@G$;*^0wsIOn(n^XOwU)Ozn=0cJxGlZ@AA~a^+&swg0)@XOlV&9e0 z{G>h7l=8M&Y7YzZ3??X_UqKy%(<4N5XFzvli>7QI3Rq0hTd?vbn%dXSn>p2v!07M! z@7dx*24_Vt;4|VT<FturGdG#U8Q;s*<xPo%w2~=T(2g9Xja?r4EJ1iJq@YT;sO>J| z!|>Fmr_SKbVR33V;``pJB;6*9#{p3_S*x0|V<VL`5Sb??%P$$m-}^cMaF+-5ciu;t zr@wmb^vUc_xnVBMI^)&)w>!8ATkx_AtA{y8?Yn^kT3^6!R+8~I-Ht}d<Pa`;+2F(; zqU0zi5njSk@sB&jC~d9E%}MTaFKQwn*{-h%$(I|W$qtJ<bLrN@f(nok)x<ZSac-zF z*)~(L*aYn9q)3j|np2^-%pr0$)wX$Ws$HMQz8D40jf9R>x?Ujnzq-M1Zsg2*)?N5K z<SyO|PmVEFU9aBfjsA2o?&a;)qT0iC6z+56rcONYjxaOIjqJsd@g}b`e7GGPJbo=P z?FqTu>70hwmAKp?8dOn$K~f5RL;GvK=Dy2Z!qA<>aLHD8)b?5b?S$KlbuQsj>Loz2 zgUIf&ng^r%TR<)fPq0Nn_{^vlaG?|bw%PKI!Fa85V$q8yQxdPsd2f=i?YFhZ2%nlQ zyir=QN+5htHlhR0oI=ynY2!RrinLwlX_sb<%=h|QM7Iya8{<pnh1k_Pr~|ufT-%<% zU+8&aE#>U#+nK;gEhIo~be?b`$B~x@zt2Z*0V}00tD|!cpEMGW5ck4=5U~o$&-V3Y zV5}_pC_39ml$;=H8RlS;Y+*LlZ8+_2Sba9GGyJh_yY!AwO&|87`LV+A`HyVtYEU^j zLoS3x#e*}BBAC;R{Ym<Idd>1+GX$zd|H?OHTvB}!>t#C&-4pIuQ1&#djXMdwm~P8W z2XNw=V8!$d!HoL*wu!UKwx<3>8opyzhcK@NHM(>m$BP7g|LESb7hih~yoQ87JQ*tU z_Ux*)u71BAL)f%LkyS2M#{-DSpu{b5YDpy6WD;BNSUC)R<2R38p&)c4v5SgU=$_fS z^;L|=IEnC6q#Z3*uM@x4&@a(T7|cGX`6gUKAFMh~KcmZ*-plQToAu03gk$yfr;n+w zHR^MPBe$ldw1g_u67d<@J)$0uRg2bqvJ`P*c|n&K(w%%$*zgh;_!I(0y|bh>_2G6c zVtmz3CF#K{8$n>!FK~mUpC0}y&XuedSS9=M@U%6P)AM{!)+xv{Sp#qD+P()HT(uv! zAHQP#gtnCRJ0P$iQ__n(3TW($^tBqARB(=1{DSZjNsOj)x1*kZX<%3fb@tQEd7BAU zbcHarv)<Y_qGIB|O7w)6j?REzDxbDw@moY8<u!7bG`D-D4Pl;iYi$zGNK|~<{m?*g zM9iYPwi?UCR6w~ndHBl(zH-UxrW5WJY0_IMez%5d#nx^N_n^ZrDzkZZ{i`hSFR#3n z{nvam`FJt@htUs<Tn!2?k}Wf}CiM*80Xp9SuTjVJ9fzbYq!O!<ttl7d&HJ*2bbkgm zAvfalUeBb=Q*FPr2@FICiTw1!nG$=+4&A?8@#9U74=r@X4|1$a;tV2gRm1@BdIrO8 zrCf^j`J@9~XqPJe$AA~(T-MpcIy<^n@M&161G}Tebj`j*dao1?x3P6%xOPb4=*PV8 zfHs}2BsV$rPMHz+3D+IC0lL}y6;Z(btNe-{LY|{JgFPd?P!zFmTTz>m5e>lFef!lw z?TDrk+vxBV%3Wmku020hCS0*nIBNk~zfQHJ)wSn}W-kLF`=DC;Z_!;3tI|qB4=Jrh z6i9o5g7O_P;ZilDDA{6^*R|DKkC_K2Y8CX_O-`vezzX+GlVK8_eey4Eeg}->ZjnTX zhc)zrEwF8?lL6v~BlT0Y5nOL>mY0Q<$}gNr7Ffs`IMStG3zb?sn6Twmv((laC_fwp z&ik1TknpXxD^NgulXSsGIe7&)Xp#M@nhZRUsb!U9dGz91wTogWYCJ^_X~d^uEC9-P znW5yr7hbsD9Vx0S_L<jTOKW=bZJTCglvXQ8pXyao{N7i``Ciwq85-jVYLS&*v!$Y@ zv!^1)(mLKjEISE8^)uGlr`!Z5V0}zcfWz&N=eS8)lb9IML)QGkTc;dYj<vzbC0Nri zYrVP=E?Quyb$9vYIzFiBe#+UdBA0`|{$OooDSYdm>p_bi=-vBv_z_n5HrbF8nhDgK z3pOmu7YkCD2@lYEvL}zu1X&nHaxh-p5>F!bE~JULrEx>b$`ei={f@SBh?F4>wZ7FH zSzOzxoyQ%gy_1Q|yZ5fO=(d-qEzbgB(<IKBax-XTB*DAF7l*uOl4E6pZC?^U3_BVA zQgp9BUWa$Ad-h|7hpCFGaL=Cj+ZG9nT=I%uc2Es&-;9TUi?a@wAcdA!z-}j+jclZA zG9wQ+`?bALmUEvA>0^aR6waxLgUAI0X)R;;)R1esoSIH8D&RP3z)3ROnJ1&7+1ZL{ zl*j%+RH!ESLX3qGvTS)>oIjHd0?E}J*AbqUYNJ_`r`jNdlG`?e{exP(Tm(MtJ{<?U z?Jl}HAz~UIcb#Uv8SER=6einqFK3u+a_%_UQx(A1P6X;(hLnsHqps)xd+G`1WNs(h z*Gx2>#3RmJV#XaKGH&thy2+}W&D{SI5PO20l?$n#aJ#8K#L!VtO`Q%1Yp8=Tu1iU) z+Fj#J?B<pf+<(UgktKyn)B2kXsf}E7iHz~=@4c(5i%ZNJFXU3mVL_|rT@6U*IJV-3 z9C+DUf<a7K6r^w+adyE3QMy2^S1(C%Q?;LJ8Qt4~(3yi6^CF&GCPULvosOvmT?OM@ zR^v~{XRINm%2caqA);?&5{V)$vs+P<?0`M=jF#6oQq!(MhY{iKBAK*|JZLuY^h6AM zo6%fJIo`lQSP|j3EZ@>&NX0N-rCs}VG;3~*d_^WUeXdeJ-7Z=Wt#9b|!EiIHQq3X6 zkFsLTrKiKVRM@aob+PQzayn$zmXp(s4e9aL^R0QFeasb!#|u5)Aqc0SxvpNItuNGe zT8{|zLYThyLlU&n_PMaqft6Ke<1~t5Xh>ZPI{bKD_|e2{z^!C5<1m}fc-`D8qws)G z)yDCAYB>mz2UVc@LGbO0auQgna|=DYSJF@=dS~PdmNuy%x?nB7rrmz%KzepXgOzyc zRf&$`COlvuX39_cVzSM*JsqrV7b->nC?KQE>J;Q`uLjje(5hk+pG2iO#*xMvG|K_Y zdW~s13ur5v-y=j1PW&fy`3H?S%o?Uy%DZ)VG!!@N^wtg`*NM!|#AqKpatD;G#vK|M zLNncD_k>~ZK+D&a2T1LP_(><yKkL?R8v3{!8pUZ0kbR_PVX#aEO|%fO;N!!j)r-?= zCU=EWA{`SKxVZP5I+>v|`YZ;9)0TdC`}I~1(>rc_!{|!wA!TC~!q;h1b>tG3j!tOF zNA3EWy&*mAkhXDhzel1{gr&@^{~P>{;nHMf|I39!YSl|YuCwB-ce(%LgNcubzrywU z{{Yv6`?l}@aQ}h7x&Ku+P1~YTAxCO^a&Zmu7U_o@c|L#Q^sad%c1jj6*2@H%t1XX~ z5s;3+zA0U6l_&9r-J6`O(Yx$y?Y5Dyn^SLwta5?F_D5GpQzb@*&Q2<=-Gq9M6Y_`U z?`blSEmU8@Vx)AUUaB!M%$E>KOw6_Kp+hSLdd|)4emo(j$lW@=aW^BcM=rX2RPoV{ zswgSvMBU^t@FtJ~0yK&<9Mja+ZdDJ;&Inn}UmlpEk8`%e)?}@~C+4e9u*mKMgyAwl z2egFXYhxg^s!`MFbX9pLK7D(Up{a!(C=={@%8VE&(Kh6jT5hJ0iF?qOV<oBQqb{_2 zop>QI!v8zK7?ftmxtf(9VZN?e(vk#?a7Qruh6E4vfP6E-tA{NQ!4cm9HSN8QhCYaK z;nA)`P*0zwt{wAGkAqK`6ef&UbRVm@-m&%-iI!i193EbmOdixZk<(N~zK!s_;M=)o zZvW({Wkaq=#Fxto44hX!=@;Ml4}74IS(W;*4p19c(0N2{-U{q(bJjKW??Q?C%OA)A z?X~B9Y547WUH=T}{V$A%je7O^rXNK8m%{bLNcV*-E?G{4DPF*D-_reTir-4}-sSd^ zUHiGh{&mLY4#z(k0^(lqgux!&I1!oG<HJ4~ADw~oP7G_dk@vtZH#1hvP6CyLWKYgY z8G5XU?Z8f-wln8*@i^MVzsrXG&@KqQTX~no?^u>p*hE5q@{rEAy9>RW!`VK~t4`}X zbfb)0vaZx?%1gR$b>%`++F0byadnT{?W?dO10;-j78)FJ^?F-PQ%zcQQ*^V}!#mnf zuW{CDEZkZ5Y#MI20eF8r%ua+Oy*S}&Dl8dr0eDTW-zr*2Ag6(1lse!fqu<xTHozE| zdrW!H%P&Wfqj71P8{(=}s?%i1CUYLCN;)E$%X!>cwEIO}nk`g)BD+f4Aj8t<)VfEL zc)@-d<|MMP+>?&3k*{mH)ic;EQ8wF<b-RoZx8KAVCAj;lepb){w|GsuJ23e(Lo0f7 zCq=l=LJS>vT|)e1?8qVCS4uS-<w^`Pi~HQc)Sh*rrD8BfB$S?+hdXJOhxChKVbkgf ze#_1a6Ik0aQLZhhbFpR#+jGwkvCU!j6yW}*QZ(;gqg+rA1^TjOkeG`RNR^``r5B?F zwwa`-@;)an<qd&sp9sEO0>|wz3x_S2pS4GRQ<)V+yv-P*fe`}hKs}K>K9ORskrLgJ z$;C>O#;L~0y6KYORgwpF+Q5$8jm2K^ik#2c_C=sNv`KKc>wo2fe)B_rSN!x%<(ExI z3N>y&8HL<k{0ylM$<M!8Ss&wRrVhg|zRGm<C8dkab7CCCbNZH{zgQ*NvlWq62x(!` z7nDgz{nig&;lU4H!HbimfvfY(6+#C$v?9w4|8niTC@A-Spt$H1mM4is^X~JE1}l#$ zE*GGBmv&725Q915)iJltSF6>mOTBY=W5&9>HS%1xtU`X)SxsL<KG9z9rY_B<j%%ry zyKjw5?xr|z!fkv1Gw$gM@12o7C0d$qE;*u#?vI?8Pk2=aXdJ}ML=47+E)>Z4T149^ zd0BFDHS<x-?2%$Y2$D;5nccg05e$wJxdSvGb5cs5j*Qky?h44yq!#0v7Z0501dv@6 zSsC%Awgrb%9Y!bfW&!{XMpEI?S&~;s$#l~Yz2PaNj$1+Bl0SfxKZnn`oQ0v$LCuXu zL1?;-Zz?+L*^upW?FV=2dnQ6($4J(B%qKI%zS*eHH;{iDBGFJwKH<_cHX@Qirh4!& z26a-ZP!ADmd4(qIpDWPLuA$ZZ=;_c$ui`rKL3X>H$I)`2>eg(zP)1GB8&qO^MD+WY zG~*T?=cB=%#Y}F|y`AU`u+gL~_)f@Eg)7FD5883jDpQ}<_zz3DF~(xZW#DkG6%q8W zCp7+NN9QMgyzu3`>MI*8siA6+uxP9K^ReTp@{WkNBgR}fBnv!h8@)Cf_VXKmSqoOz zcEAImBg#Rb|D(vcdj2osQ&#N`$uG}T&{speZz!{M=W;6{BZH1&X(k{0pvd7n;3V+* zQL(M5_(7^K{6pO27Dpz(#>6s&wKZJqxf1W;cI*jmZc#2+O<T%>uuzp}Sc<K{Hv?Wf zg)2<7*EN|55+#AEU1>;-?NzfMF|ecB>4UOo*al=Iss}SWQO$`*N}DLDXyZa8Y_g+E z+ZL<K3n*EgRd^adXlqKs7<1mlELcii?EAMc+OTw0`-U5<8|xq=ZnqQnCD^Lpc%plw zb%!9RK_(*0rP4O3^UcIKo~XS@A_>bKU>cre{qxaP;r;uS!y$-^-YQ3W51lzZ&O3Rd zx45iS6sr!^@*KS1S>w~Pr!6?1Ame@Qg*T}=ZJfX?4v*Mr71DTlr-gQAGe-FuCbv%~ zGmx7p-TllY&RHWb!{^<MpsN#h$81Yq{nQMJA?NctmpReIW;n3s1MK8ffyXED8-?cF zn&e`$ByulmeP({Rir;2myT_QE9E+Unzf<72{dK}A4afcGH|O`_ryNe^Wq+(wl6vzW zL<2GFl;_WN$~%5f?T*Mc(dD<>Awp&=4_TrM7+zc|sC@_IMdSTvv!Ay7(p2mNyPjRt zw});y9j5XWD>PfN-c2L%;-LybR&JWo1!>QzEB8-(rEG%xRo#o|-9m~d5;>M8hK)jX z&5_j{ZU!dfT1Jb6>CSX!&JLs|c-tuMbKkKJ9KkAZC=2*(OeoeqK<TiJ??Yb|8~lrm z&ZajCUMI~W|9l@S`8L?yx1hkLLFY-Ax9ZzEp!yw{>`~jT@L3+%=W<>#In+?*OxXFp z8?snIw?XuH;e*SIE>ZF4cHBzpLO`p=^rN+0n>*Iw3#mLLlGR;#x^`eh2?~kw9V#WX zSoLu|dfH{-&|>`3xU6+LzD(7H#pWVzvckd&Svw-yEf$czU-J4J2QiE2mUed}yLgg{ zF?*=$G_8ACH&-e)OR}(@f%|q!*~#brXm=suV>kV`RXUe5BA%b2_r}e6WXDxM7sFR@ zyLz=@_hG&#>k@a9ja57EOp&FYLR(&*C+L-L8(pxW3XsKob#}rd51Dd{Zyz@+*G?8D z-Dw`Ic=vTl;G`%>(7LX^!^{dP!72+l56Msb0#H2|nE8xd%;463;xJUl2Yg}F{;ih& zBqIFi5|hciF%!<O9IleTHA#JQ-Xn)}wqYCJ)>qaSJi*s04Wj3g$=oKN=01aw%#=yg z#;=Elq?lxklH1J+DsTij7a+k%yUo=f&}!0P*nOzT{6E60e>DZmQfxx{qTt0xT~OV$ zFInx}Pf}*+OXWEeE_OVKmXckh{liNHufyrc`tOO!7v#*vF3n4AmuH_I-n47^#mUef zB=5H9Mqd>c<4&7R?*5T^AuPr3z0gf@x%-2F>BSH*=WznCM`s`Gpv!BQ1=kpR`y-Q1 zaxF=dP6|2vMIJnGoJu~$T>TjId#&H9{_V-9FOWJ(N`ikm)BHa>X3Woi-a7x6oO34U zKR{$nLW9U4f#m0SOg2j7a~~6CCFl9&n~hgrw7$7G40_Hn8XAR}MxBR44}+rd5||NO zwo>-<4K5#$_`l?||JH7WzuDhFu=R)M=xIPV1{wXL&MZH3A3eq}XP8la`Cmt+>uI`g z=KFOC<44SYhDiTYg6!3=WZA~QVlcHey%s768H2$Xr6w%rn}hv?R(|r`v~30_81`z6 zKzbJAC0$(yOLgSWs3vd6VDjz85u)^xx~@1zH(te5<ny>vUfGv6RMWB$^$5`UVAJnk zgULx5fa8fRV@e|`5$+$IS<K^6_YPmWv$6I0Etqg{fi}ZzNXfd!x6k(68K>k5RG?|7 z7{!eO5Ym*=$DY4p72!m(iqN<4V^<NFrn9Gp&N<PDcS_IRx~4p3aEdb`bbsWNDo#`f z!|)CvtEPg4@M3hS2rGoIDJ+5s-_@86E^6T#D^*gG$ph^JoD>SV({TB4!y8Kxi6V4D zhstADd|t=Va@1)aLdzg1QZqnBuNcm~nQrmM5t@xkF}Ry=eotA!jx;LN;+>7|RPrAe zh@Q~jwhON|;fA-{YmdC*;b#t`W@RZhOH1~2@(10dJyrv92W&l^V_FQeeOS2nycM+r zrhjmL`OqUL%sDpEV(Ovx%4LXfx&(hIb42;3H^r{1-MlI7I`*BEvk3Kdi}#;Po*b&D zUTUTP(Q$1ztwGPLAwT9n;0Bc4(=mgR@(;%KokX`UnSbBe-`IopoNm4D{EOcfp_)Gz zAu`Zk9@n(L$p6XS8_sV<_1Xq`XqL1H1q|F|1ZP+ntr1;UBw@<hMb8uxs;=5+*QuWf zfeGK0GYpO2s)?W@VDWhHG(!W~>Nz6#v{kB&GhnFIs3Vn4OPycI+@np=jM2XSRUI`U zu4-;wzc!AbZ;VB8qD|ALo`q|8fTZ9v$$*p$Xl%Tlm0ys-CI_3qh>Qk7{BR$d;qzH= ze^S0%-Y|SKghpATli7+vf_|@SW1eB#!AWc(``SY7rqXTe^pxE+1QN6iS4t0S*e|y# zo}{*Q-drf(vg1kM*mwrtxd&CB>*dOcplTNj_&~xKW<zgSAF@gj5W+tA;d&<<wReV3 zOif>RU51iG=Ru(+Ius&%Lv~};MnZE^Zog}-ru6H=f{Roz9ds*<uEC?kuIs$pBq=6i zo=abYH8|XRf=<GwB9~<<4aw<7C**EtJ=YAu<+7V(%8mrBl&ARD(MlCR>ifnyYYNG! zd3wTTVa#d>gb4?Ufp8oTU)CzwX2#gnm{Ri;yq4yhk}3nRFsQQ0uHSgQ+jI9=Jn_t_ z5KgEi6z(P@s9_E#FqvfahP_{Ac}2V+Vf@t{1s1xMUQn&3B%@@Ea}>8<GdL_m^lGT4 z&XAWEwm3L-=3YQAA79ze>LOAKnx}hAkMs&`Gih!{O|sZ}j(hbAITqzZWWr^y2Yr$| z8YK;C$b1Rwq?PH*QD^w@#w&6+sY2V=7-%_AZO9UT6cNEc7F#v7PCcVwu;o~5vO6lA zsO;@;JE?9*!QT*dzs?a%`-%#?^NmeRiws+nj=7FX&mqY~d@p$N16C6qo#npK;Djy5 zom_Y#yU@|mQX;>JDhs~ocR;U{Z=Xp$MS=FoP9D9q9EEL0Y{`>j3cgR{;*S~h0|KuA zc9bM)<96Ywg5ol9!`D5DFfAatHx&&5xP!L7Sz+dA_*np4LZ~YFeqcb?bSN6pv>Y>= zHl)a&$*G_^nN@-w8>rY1F8#7vB*6_0mI!>xCPp{thEZzQmIplZZ9uSJNTN<z@=oVY zx8V>h$U!pXR^_sL@$i=^P9$>Twn|T<n2p~Z<+VC$91-p|_w|r!P4tvmt|6%K;*ek} zdYPN&Ijpjowz=;sDhjP)q;(v93&??bW+k(h(hqG~oW7A9QNHSrFdTV*2!qKPKeUVh zUsvKXm`x8q0|1be^tII}-B6%dR%UjU3Xw*@HjSS=?B!We+mcsTm2Y>hTgq4`(a>-s z%Z;#@ntpB;`av;+I0L(w*NgEKhYIg4*6g@gWB7;o!V|7vckhX=Wg<FPR#s_5+}v;L z$jW*bYvJVT%`H`>*NNUT6yC6?*}MVG%z+4%(%5KLtky}z#wIVXg*mWCD9`#UO`#VN zcS_w$ZJ$uZxYs3x?SuKf)z^%ML0;lI;=-dwmcfHmVVb&%C)?2xILm$>(Jch$!u*SQ z>D5#DVg<f8ugkMp$N)8iX%oc_ggp?fyIaPN6$eHjTjso~QS}zKHjY)u(`W0nfwx<o zXQM>(RbWZk72ULGBnr2LNcb+;(&w3*ee9bCr>bFNj#L#xh8fl787FDlW*jHRqg*wk z12<iC(ViNTxAHQkODz4>5I_5^@C*_Urw(i>Gv}<8TMf6r1FSL<(@REr#+skdRzx`M zO)>f+y(U396|yIq&<NLvsTid!eyR1Y<@(*3(x(HO)1aCVd3bx}`a+C*^~(N19L0`> z67Iabnzp`*plSdM3)8!ad$65dK$POiqM{LUBX8rbsk$N;XDUOhB=IxoiYPU<BtB7W z^<B<Q4hKR0A#K50>ZH;=5`M`o<;C2*T_G~Hd+|@ZRa@vBbMk#;1^4OmsTatshTvyA z@N0I;8N*z(33z)BsxXBpC(V!t<MLD29L&XR;)~`hBvH}r-)ydH`Mp~r2uiyp#%jq| zt3)@(hgsz!0Q8oq%WDm$Iu~!+RJ>(?+z%ueqg>Wgb{us8&5|Aw85!2h0(XPnB};4T z>bh}T-Hgb|gu$|CvtoM>73jZuIO*FKX_W&dd$=u<m7LoS)UK;tFKcR|t@gZSrD|Pt zP)Dx1;d;M{YW}#$y^Y^sXA9IvOwFa=4*G=LY7uvN*?=zaZGAN=*y{=EwxlnNMpRZi z;wlW<aaBe{Dfl2%vcrh3jT7~&Z1K%jedA%t<(!c>kY3ON%5szVsA2W8;E;d1nNt(Y znZst6n;(Kq6hp7tmnaUbNd+=B?J=SbzkIMeEaEb_WJen2a^6k?w%DoYv(!sE`iO0| z+t1%oczmQ^H(vpfUE?2n`)QH*PuLe*kBQGi|A1ND7u`~1*%(~q-Rk={xXNVT^#3TX z`WtqEe|X=e@4Y6bz#vt@UAK~Hh_?2yWjpXjwiBJAE&FYAztZ7G#5kqo!dW)mcE4>C zbaq<wLP@`3W;b_;g2a)s7sp15X_zm|SGMpyd5?fi!2P^BNhY_vwO-m2$?lT3N4Vh> zV(}A!Y_08VP8r2h5z{&O)aq_YN!KN;g$1qpB~`D?rWNPb8ZMwRUWlowQ^klx$s2gk z-Q1U?C45}%dzM?seSF(sy9e;~X@+RF?%0T0{?OsbP}EenGsQ^1*vx62y~7qD(ynoe zC)(s_mAEfuT3!2Nr-6d1ycYJvB$eJtpim1AL)3d0pzfp(+>qox6YopUw+Hh_hL|l3 zauaKaKiM^fFK{m=-roZi^~*&>kWT(nG407S;YMgUkK@Bzj~MhuzA&vzhU*=E*u(5_ z)$Y^v`4iNf{RwJrT6kGo5cB-n52;1+OV)Vx-wW{Z44(7ok6ptQ1LWkm;5dROwag96 z-vRDxn`()7X_+o)w1i9ws`MbvS~G~f$qq7^8?cqQ=oEOlGpD10-8EXgRJii#1=}dK zAB}uiZTc?N1om|O5w6~V7mW%;QddfWsj&9&vFJ&kp~`I;D)IAvC2gY|_K>bSW`VoA zRhG<K!b#Z|sVTWnD_!h9c`PGV>T>C2iu4P0r(u~@CswG(YuIHhbi$@L0&mqc;M^4t z4Dj>fixf}S{4^n^AEe*blAQ_NEpl{`R}fY_`|2A~G*sooRbOpeq=MYEj~G_3wOxcx zb6m%RT21Ukc!~Ii4j%R9pK$X+D!N4;ve64LyQWIM^3&=xI8>uEJWOTO>*@LQ*qlWS z%JRvc$Nh1rcj%mvM@(I>L=Nn-$%&B0EYaU}^+95Sk?+Th@Yr@`R-yi8wj{oUlw=30 zHWIJV^_VHuPb;OpQ}B07+kOPixGQy+x^L$Mxf>&&_5;B_!*dnJ`j(OGzCyxdBdyi6 z0eJi#>x6R|xe+mbG#>IS!Rg^t8`y-_L1ET3qdkZ6KLgs#ixw%egx>)%$G5$H*?Xh) zpX28#zRdp?<30El;~A~Gx4QlQ)JOim7vpuSwRVd)|0u4sNcQBuIp{hm2?Ifsm#eYr z<KKCH;iM=V8=fwYZ&;0;zcZQABUSF&JI{1r1BI@tVk<o12Rb9I$0&B3(q6v3w<qa9 zgin%0@33*eH8Qk)5*<OOLqns;9Y;2#@m_(>p2*_S41h{+b3SMvF*&1Cq5WjfUXw~@ z)9|ZKsOA>k@Le2!D#KYI)67_;nEJUvuTW`>L0xD~cM9}uvqn5p20uE}$so~Rbq3`g zf(h1qoEl%%Wc%rzEJOQTjEC!=SvnaT7P3|`fVHMJA**Q#S|3}aBwl`n<b*}?T@CMC zZ?IaQt@sWg(FIBPX$cw>1N*V<m>Ugis=~?}^NSv;Grk%6pbx6$WFJxUt%3sSORQ9k zo~J22Xr+o<yS^!W=9@lz(mpn0*@87`38D5uNzx2{iqVpB({4Lm#tVHvIzzW047Pg< zLMIY*z=i{c1K-}=5$fDZt8MokbxU<a?cDa-fth6=$oEBx&9_<sYY_MBYRH?1AE9d( z^&b?BltA4LP6A|9-A^bUlHX3=hl%@ieG&0KLG93TV~3zIQo*vTne6A}^v{1dp?UwZ z!K;5P9T8vY5kVS(DF<~+wHLoJBJDpJk%DBD2deR#GifVL$-&RG)6j}$D+Ip?!}{wU zKl&vf{awc4U-*t1bXsGhcNbGUGV#PVc3I8YQNIte%JHMIRa4}*R=Aix=9S@mWqsv= zRt5O#PS!REZ~$fWZr0Pi&>w8I_GFu6XiXQ)&^MAk<Q}IJM5M=)H&4>(6gu0Nq38xq z;MEw`Uf!i7SaFHJ+VvE~gOx@UW87>Rkm8V9W<-PQTN>je3XLjXw4ErbG&H@dTG0&7 z_M@4$uzLo+J4M6L4*&36JXD06mUt*v!(<Z`t$Z?xZqCiNo#CWM%*m{DG#xTj)a0c~ z-KrWCiY6O_Jt;49gLg7KI-e-Zan_;Irq1P6wxDSXc#l`^03ac(B_XJ!S!#dieNq@o zclmkBkLHh{m;O}Z*CYP9d2-`5gL~I{$n@uRvaiksw|{b1D3_Y0${#aDXZLHS{8ggW zFuOV{G1(W>QS4tb#;^ak$X~16rPY*YKMH(Y&jvTl*30+}%j|chl{u;Jxrm-c(`~bD zgWQSWHPh8$cS)WWyI<44cfuum>96kdZjWZ$a=4^u3f6J9;()U^r>(1{m!1{+1Z^yN zsAy_n7yd{>bt?GHwCck!m1Kdj0+fZnxJA<7DrnMa3*W177WKk;kwQrDKs^54`brpt zN}-Q?$#6`5e{|F=D@Gq=;ie*dXYyhhnk<nG8oLjZX%i83qz4;_R(cA7kY<HB5gBm# zZlWR*%P=t#{45GBRAPaN$?EVeF}iMFiI;V{ZB8@~_ozN6dwKc29M;m^>K#@G)p1<B zvkgGA7#j`Gv559Ki>%*>MPPGH3GReU=h*K}L&$>iml)h75j@UIN>W_ri6U!SqbA!j zy?K@m8w)#pz?@ym;JYr|hpnX(mj3&&IW@brJr-(F%@Zj~&+2&)@6_BPU986Sa}~%P z*_*p8w6ZCmVWc<48VH`Dbex~au9}Q217^s{EkEh4e||n1ZIRk=@l@s)Q%1V5ee`(k z4@+@+fp3#R<~h%W;Bx$sEVJKMnEZ3cS=BF-VgG3|{~2!h*S5a>hi6zCo*D{QqI)z` zf`NHGi{&C|IWBLWC1{{Q&$3{xzFgDVa_qhDlBTyM+AE&oj@6=xGxetK0)fUgF`BxD z!7Xjhm9*ahWZWljb~H3spE}GSbZL*ZyPwRZ*ld5L>3B^XUF`G~=08&%!DgXl<IibB zr$OVbYf!;PjqvlS$-Vw*G4RuF)Y?8SL|bvZPle<W_7u{^MM8--RbK{H-R$K&<<_B+ zP=74e(8=(G+bhZR?)`y&hqWSiO67Jr`qmh6d;u0(2Wx_k15WFaPrHVc%pSD|Hg0a( zm5q&}Gi~EW$UAKhPhXS1=T*sl*JsN~`vgQ)e(4u7Gfqt`2(&<WG)&C-X6r3wH!W0( zn8^t8vf1BrZlfvv@qW$UJlIYz@N6<DMgCy3{7(g>ey~}F`-VSIc!ZGUPj*wgQRl19 z4>oH#T>oe4|KDY^{wva!D5_d8CG$-}icOzEhrbQxL)AG2tWRbSY4g&wdr*N25|)_| zy}7P`2rp%v#C*j5rKVe|7?_zDf%$Na-+|sIaWCZfnIm-m4yaSuc+>kw&Ikh|-6DUZ zqCZen{Xi+pI;wv~DORW#Y~uq1O6yCa3<~mvK7U$pkdP-ec~vYY6Jhekr{V3I-bq#{ zYKh<ZxL_&$fM0L-NnRK?l*&5Q3I!X)PI15SDmCBqzm?tkJHPld<MsRK3E74OgdPf$ z5KgW6uM;Y2OFkZZ0C!poqZ44+k_N8cfoUGNd%;as%i2?UP8>9U-a|-D*NUSJ>z6>q zp%&IbQNexe<Y)88T)%vHP?)M_%EVuJ%FF{^Rg0CO80M4ecEN?3Kt_by%(cc7v9o#e zI6mg?iS>2g;2%ZMpC>)HQ%)E^h4NaYrkt?<DE{k{dyWn*C9U}X3RnI&F&#mWzfM`0 z33S)ka0WG+Y<`&PvN^YkpL#I7ghHcbF2?dvzvlOLS3MDO<)HQ|w2U>XeBnD_pC;>< zNjrC}^yaN>BCH?i)10k4xwiARi|?{$<zJc>a8HT1U4(ZeSZ>Q;OVp$e41PVQu!;Ko z#~TZt-vM_M{SZ4{t^>NG^3!e+!lj}!%l=$d)!Ro=?hc#RlEx(ppe6zlPx5jd@^U4W zcl3)Y1V4I(7Q)4!tj3KfSe>@3RHr)VVCfdvu0()g#WmuJolaI-$;<RBEP-S1HB7nV z`zEssgLnIcl_uGHNGTZW-^>k<$@TPXpIThF?`)JG%I|y=Gxn5P<wqXF#J#nK+tu9c zz17vITR0yrzZxH|>#jEQGC#9WH@;h$ev!nLRi~jmnYImsz(vX2&nVM6yYcdnWWtj< z`RIW5`P;@~u)zcSWd`#Gh9nF}YF;<DeY)EpCi=zo5IC7L-@rg>dy=ttRcQR~jS;b> zb;qpmb|_0OxJ9Ad%01NhAQGrGUc2g86h^<_tvb>2LCa=G=XrW|pYS3>w>R}<LL;Y1 z`6^F;CgLuueZ{y)6(~;dVm6}ccI)hiB2DUUq4R)5XOCgNZpNv#we%=BcG=WwE|0|N z-b`MtOL_vkwy_;jU(=;9uKOu;`aUA!{reGc(Nx8p`e)DQnqW0!cVTw-#8vg~TV?R{ zwa_Q(awOjJ{Vd7bo{CPl&ErZBX4<r5fQ9x41~t8HD0aw4QhE6UQMY&<R)CTywkLbK ztnS>lT7<IlW{1f*kys|rZ9T$hn@Zks6JY@caxur5?F(1UlzO#@+bWtPExJH@Il)3W zwE<lxmCHDGlV5=@$fjn~!ZreFV4!z5N&V>tgCXmiPXXnkj@T9Kuw&H~=5Ka|UkGn( z(m&;n3DoQipXt7cMo3D}co1=oE)8l*O-ZEW<j0K`Mkc<HV3=_!3nBsdSI0=>IGZHC z3l2(PDP*?&4DU9kP)aK-uI{j-CA_<ta%bAoX=bK_A8R?~lB;N3bM`L8UG&>py9zWX zXFF(m5^MXO7_IcQL<i$8%T#Hd^5{q<GN-hb+!>xB{&vNWd14t#TPmTU#2B{zLb>}+ zQem~6Q_N>W!p%)Ikc(c8+=AKRF!{QYnAlS3>Q?TFLzikIG6VR4(}66=`aWetv`WQb zN?+3#2klLIX;%c=JRl$%l~$M`Ad!lkiiPzZs3@`86hGIFNQ^1iWjEL9zOl|n)f0Wc zqW(U~RrVcqty(+en=##?F}W&-T)~6G=<BSY$xbSXNbsuWcfh@p?|=xg?|`M}>8<6Z zHMuzVqZH&<b+U?3*+WO-e)_A(2*T$hM;8kGG19#Kdej>_oCaQ%Q)eOP@DZ(B@LKEk z_ar`{^rd4{^bgg*DIa0&fH&(@q*To_CG*rR+|y-Se!`1|ce&SW8j_r3o^;N0cmOEt zh^#NCPuC^<*3~wv9o>cp#mam0g2Urr+=g~kYjhDCSpHG&+y=ha$3RncZgTo?&<c1` zy`&)t2WmktO3dK9X)-8hs%)|qR<6d1SFWM?HZXZ5Ms1>Wru{jMxW5+WwS!GP_S{^- zbsL_Tl9G<?I{GY2tBHO+gQ{K?opaijdq+Hp;+*cmcCWuqXFhN9mS5N*kMelax~!-@ z!-XQv#ehoALuyZWs)M}!GSoO3tF9mmbzEz*w6Uu-HXSE;R3*&dp>)-IVre!;&jo;= z=RZ_3AD|wTz&)ZJnR^qy$*v1jQ|#o-cKtfn40Qp_z*53bX9pWV!(b-I68)u=0ida- z{Nx-MOpEQ@!nQ&e<Ha{n^`S-gno_nea+!!3i6^7p_tChaXmZ+`pt;>del<K%J|{-P zvYbMzuOKt)W{fy8xlznO#Q&u<z8D&<4~<hGI3Apf)%P8P2-s^emx7qqSHZbN_P|aD z;g@1!_zYGRSzyir*LpW0S{E&`A&2<s13IUx^J{vuW;HU#6uynMiJZBbL7Xz^&hUln zGKmB71f5ID7+?7A9$YWaI&VASef2vS3|6YNxbu|rhMme0T-46)+1d)_D{MK*GyW<_ z%d?YAY^V7Tl3EScx$e(&6`a%I@SF$_z0)+LM9a)<0{%-8$2q>iH!~pRMtjZ1${T9* z-DMe{6zs38i<T!Zd@*vO^hU(`enhTv6o4hfj&=cM5_E(3#m}9hm$zQ^mdNB;SDUhD zWM^2U<-{f?>T11WArU?F*o9WpeKzJDH!Ql9R<1FfpANr<Sqe;rn?>m)Vp}2;{1(vX zofY&_ACVaeN!i{h)B(wrHiCZGblVG6FLWOnCWLAO-K-i>AsM39nQ?X3+l<C5T5uc~ z*Sk{i;?B!Tgy@88kzTN?t7~r!^~P0_uU`$NCKg>rWM^w4Id{Ea`<il3CKv+{4i4Gz zk2>1R9j2bqr=yp2?{92&Y=LILpl~TE=K>tf%ag~jD$EKWj(SN6at(*rkCu*&4hb8C zh~1Sz=Z6ZPe4tUC*dY&}XuqEMyqlwKn7cqjTh}o80@CmWPxE7w&APwmnY&d{$9zPB zO~k4-i02s(Eo)fxNP*4{pRold=hW#Of`BUq^r!{e%JHfseQ=UE5Fd?C3ji!9Sa`Ny zFHN>G77K#O$`QT(&-boM7wEU~ihbcJ+SRq|<MZsVadUL>3+=j;L5m4ht5JbmT^>Jj zCpjOz`;#_E_XO?hx*nxpZfTAGj6sO#0{z5z()+)$wtoj~yW~n+WJSUuPp8AQj&JO= zYpPWasK^}bOdgQOyR1l0`@9{jqv|Q6BZ+LRab0Br&Jg=D6b=tSkH=2L#S@<?3f*^R z^prnbogIDCzr!unD<4y*lS}c$;bM8c*$SDTu_~l``dWKSS>I8x^xC<o!KELri~-H* z*YsJ!u8nDET_-mH#Zx(~xN#wV-{3q-goLv{ucGKdc9rIo>(LOuLCbdlxEp4SV^It0 zS6al7c=x60g(R5Xq!)h$LI5@zPAXO;22`0`{ocs0WN{J<TQ5jqM{Z$XHi-XE9- zLEZ=DGlB7y+P*N9dg%-?hZG?5r;t$Yb5Mu{88dS$b8z)UdO>`JR>ZdQ7@47@MPyV& zRCct!!zygES0I355a=BIni%d^rL}oyve*SmuJOq-`SB&9bG|zAj%cw4<{ibI@~^$b zIArZZv{du!CXViY9;U=oQ+%e6cPcF&`Z%hPh!g0%tC6nicFehDExzbh_-xyYU<y{* zFH`#xJ^yvo7_&RSAW;gzZtZR^DtnxIb#ovkR7iVH^;_8>Q)db>D;?6#8BE>+#FZp+ zefpBNQo3((h~S~Y(I#@rz$-5ekwcY})X;VYfPQWI%vsmj7n7<yk?q!}E;P2tmtW{H zhuy#AKm4*1JT-n0EK@$<EPa=nPQto40}au(>%Fy^+#@U7A1A6k`YthZ%&K};&JsG) zHpfBu_9|3PlYc;T-K)=`@}w0f0+mBPLihMk!1?YbsP8+#ykNt6wDVb({Yj$E(4hha zv(tLr3l-sqqaeMv<C<uI0Reg6BbI=*qWrKYCB|?1UXRs==?o|4gy%~4RoRo2&aAp; z7iFV@I%PD8pFG=6XkqlgFJMw<3Qk=llyiEvy*{J&MNy)3T{7@9dPa5CY-k6}Sq!m| zc~t!nK;()`zIDp(@-f6;-*e=OL<)qvOPD`m?XG#Qn}mwd(B9m{7kyg&83o*!#XO!9 z-de#}E%9XmV^zku$89yq=w>P^P#cNB7=Lslzf&@=VxFcQw~SroXd+S>Eu0m?slRFV zrW7K%*kTniDY_&O!NO&*Bb=T;e7vvIX$mBA8Ls6_40>i6c}L!FMt}vdu<ouwDosI_ z7^`|G5<xu02O&-q-)|@6%rbEl;d)X<^(@~$M#F$wf-UCGjyN!vD4CRTw%J+pMcssz zb5Q}L1yLoQALa^t7V+p&6AQjgC7p}Av&{IUhu#{328KVP+>vNRL-z&GPjx}$2-zzn zw-ox+094?RQOY@C6(dhYHJczPLd%xsuoFrDO}oaeRlcxns0%B&FfkK9duI}GAsmA6 z^Q2%(xOjaY5+4^I4}Gc&WF?LtDG|N}%kpcPjS+r9^{nvxLBxjZj>J26@9%&eRp_Vp z?^j>-;bsCX*zq|TQ@XvAX@=Ix0Mx8U+@8ByS|ffKG=-pXx43~xC$}KzOGFK7{btpm zrCQQ@;}7nX^xq?46n*JLA{xmYQmHkdZW}5)IuIQC%;H&)0I1)SHgRw)v$z9&sLzDp ze5)@cSqNtjtJrpe8i_a4=`!2S3S`)a^9x>C!zX&_as8%hLRwzcl+jeBFlDb~ECG!R z6NxjEe{Vd6u2C1U%JV#51&i$=&2mNRN||6MDo3TxnVWr!4sz?Ic7{7b80uE+etr%- zS(?q2yy7&v7w1-4Mcd)3Atl%BTGvH5*VYA(-a>9(fbOF<IHddi+5!>zOXuMRTr`E< zx^GOtzMz1%Fe0ULQFvUW+~D9~!1>dW!&9}9sxCvwPL6WK^F%7^oS0g~9f>Mf=Z=1L zmAmTYhSXK3b-ePLPXIh6scQvNbBoWA;p28?x=P$PR!R9PvXqBYqF;&z0wlYvUv^ze z&=yCA_q$4H<2K>FlvgXe+Oal~S@U#;=1^K+O^eFu5U1#Q=ccGc4186_xDQ4Qkx~<O z`ev>3;n|s!JEv8TlBNo*ej43Nf5X<BLBxJr+_|Fhi7Vi0n|nU~(9n=wk_uyp=mLCJ zzy=!&^=zVTeC%^!TFH>n23L<?KxJZ9`|Kfq#RY>8eRq~fl9jAWr<!wCeAJh5eps~v zn#|o>i-PF>%;&xu&0*ohQDQFi$;|94&Da>{!$-8i%Q10UQFFz`MY);w!j&4eZz%<A zN-HDjd<Mfm?RQFpaKzHe{Ydr{lvkxU&a#qJG@vM6*JthJFQ^2gNGEdtmNl>&a}MqE z?Jn?+pZYV{P0KiFcyitc+(IvNgR|jySC*@-b@nxWac%<f=(^jnDseT<ps>h?qBFio zwZCLu{<7=!^@5{``_Gp)PzE73nrQFB9#(O2F$BQ|)9bXe&4{eI3W~Mw09LD%1g*7? zL(>0)w)c)|s@=N9qll;!MFc59QJPfgEr2M!caSPbF9AXzG*LvQM2HY1NDG}n=p}R% zq_<E)5eU6^r3wh%yyth`@0@$id++_m8Q<@pz}}3p!ybD->sf2AIp_KojR6En1>MSg zH>Az3qhRT6z4B-S!t3HsGf*qO?wYvqA$KKj$ne&9uIwda3sf~cbd8hU<u1EzfZeoJ zR2th}fjECrQ7Dhg?qTl*M+#r`wSqc}(fr)HwN~Ti)cV5>tV=Fbs1t6@${mbm=%KHA zK1LOVsytI#awYN~vFNj=sd50mLzwP9C6*QF!jfRv+~=~1Z4+D&IwqI<5no&x`Sn{J zA_?+}wb5QI;X0;n^e2>CpczWmTd^tflE8($CEW_^?fR)SzXF=`nSd#$>D73HhUmJa z@#2T51Z9?I`Yf*bX}wXi9$qx-Oe_uSJTQVtpKA9ZI_au}vV}uVY|VVr&~JeMU4u24 z+<2CLSvN`K3f39S<gLIBy&|j%#y=~;4fkMO1by~e+Z8D@P}1xiHBxV49y5aKybD=* zOM*u9%nk(T%dx4yMl3{J-_$=vcn!=gfyx`xqyo+jHnel*a2?6Y#zvktvcrZWnj8ZC z780dTMzaa@l}4iyEoK*xlT{oK-_9#+I+3|Srj`t8r&u@-Gbj6ITgbRXz#jKnqwLO& zCn(#>^ULd!7Su&;cfzD6wm!(yEJ-t$xg`6_HI07lx|Hmw(&_+-ERnTjN+Ij;-{4E8 zFV3Bq=osz4MKCs!@wSaBE9zId%BwJ9Sf$8<iI6K?q10?wd<m0j$Xmz4TDz=dN%|71 z0RNWA*$Zpkud}xvdHB%Z_>@{^sK;y1;`HKm^Z2eOnfG#Wi)XQ~AvR-D98x76WY-`W z1Q~uj6I0EB#YFbs>}R2JpY(?|jeFwd6d}{4&9G`Cf=p)8XVg>1tprVAAJjZBl7eZP zuE|wW+oJ7rpmemdmN9@LDJi^j4#5aTsL=dC#JBkLQ4rVRJgLU|^ePfla+-_&&7Ccg zoy|<d9O>2CZMPIB=?Yv~r6{7o_d#8%x~g(8a>k0osL%oAxEYyC<uU;c!=IzaiYP?Z zlHE6BE6UBZDvNSr`xD#nQ8yL1rSW@T;9V9DS!p}lIbfwSf_!Rh=PvUyh{<K4gX|?k zoA&68(zhwIXWBaBLE|XL%13>1jgIts(nFW~nS`l2$vT{cQ_w||Lw+eEns+{7Lw^Iv z+skR%9rz!@A558_U|0)ZiiV6kcK~FJ&hV3*4%)2RVntzrW+IfymB5zTKKbD30R}UV z!*8rtlIAp-@;XHBimFPZSwKgp41tm3+L|1@6bvdu^Nd6D3hQKWdnJMkpRs_CxV{Lu zE`XJT-)DzEvh@bcTn8XM%b@1dSC?|Rm75BzYhNx3djIt7*JZEEd-b7PwvF$X#P+VK z=LaNCT}8NqVVFp=@I>!EWq0J6Kq?Lbv)8)r<EXNO_q&j?Xy^ac=cY*ec*YBFD%_TU z*DgK1VJ`SUtWwW0mti23&4>PabRT2K4R3#e3Adupl^V_)YuHP2-`hm3%2t|*m2TqV zRAT#k`r6b%Tv!KTA+52?(&O_gfi-N?>DA>kI;(c*T$_bdY~&PQIIcAwbIw$i#GB;8 z#GjepJk-UH_jw!To?7lL{2Eax(B)C}lO3gAu$E>2@LPO;V!Fz9CbZ`G+P3RLFc~)A z9`1ZxekiL(1{6kb_;<w1Dp>}q40U1|mvwpaeLNC(@kg~wZyAvD(E1BtlcxeBdq$eZ z7lN=nZDNU#=_YluPH+F>_B};%es3b5^P1(dc&kiKJ-e*XLR56eD;<V~^?QhvVR;h? zhHB?dOEQIxekzWiG4DR8OfYk;u%rF@ue{#X)jO&w1-?c?Yrd~rc4Q0i!}CAb?=e>i z$H}{fH||hLd%ElR@zZVuSt-la<szRhVT$4h>lFD^g~JkHL`aQqewC%OeTI11sNv_2 zREd*(nt=U^x3sr6_>ZtX!F;oCE!J|uic8S0p!fbll0YfoN>*q>aYpEZXA(R=yIGX- z4)Z+WgVb#O(5RuQVdN_Ie%`x|i)z*+KJPWbnLPQ`N3_jA8xu{9ZEr6i>?)IU2ROxR zRhO)7I!=4YAiT2Q%8+hBScVx}%NbR<?_LvAXSinJASbSLucC7zLW@QOe}wATGgt3X zIltgoFbspsnAc|44AtnDes&abOb}(g<OEr5UbBFmz7rn`<Lzcj6ubFHvg5Vqv8Ka) z$==)`>;Xr}Fwr4QElLn>zGgjz6mCLif6#_A`<?aFn2LC~mkV|kAf3ktQ$sg_JgqwH z1|;$Ih#MyJP0&3XXKqY))!<FP(7zykQ=9);EY_R+eM|j#=T(m`ydINteaAfazv#NR z9tz0wZlgawYW!y>q&NSiCbRY!e&$n%s{~od#p!ZLuTQw#UC$%_se0`h)O*pAqtlj9 zHVx$zD~X(qVwmf=>v(JE+tSV6GnS&oda=7TN0=?ysMt$q(tAHvPZ<jCjI+BfkP<!- zAkAaS78n{`Ai0k9H~xGlPBBo>JYt0<-mN$iG3Og>>8rxky_L^sfz<Jv-G6AiSgku) zo;aeM92}u^12=gnGyq+h&gbpP!YP!F$X`phQU|?;0H1ENX!LQFyVff`D-a{Jv}949 zIOk3oI!g94gbSL_Z(xdH6MW=E1G)4bSufd0QS_K+mObL#+Mzkt$~bFv-IZ1MX?Gs8 zgHTFoJ<E^O1&Rs!XPPcntdEVoYvmUwaaL`3I$cPRX4Q3u+1EAu2gUqVMSNX-%3q5I zAxS1CV($799*`(D^a@ZEjcoMw+jMutX3<bfY=gHWd+_4h(GJ=p`%$01o|pmIgOYKT zEiJr{bX-Qi*(HlYhq_fPF9XOzI&e?7#mg-{M2))vFG?P=v9`!jTKNan^~7nLIHy5D zSt|)y)wvi~LE}3|`mmug?PopYa7GDAXFC+u>a<CK|2IIdviLT$n*$HJT%moZX$`V| z;aTmSp?EQ)_(0SiU1mw0jVe`ia~LfVQHGXx7Yw&^S;y`K3D?PQc?>ktde$RE4(Rn8 z%_pHFzuJrhefg`#R8}^(DLi54Z-$C`NWwPL;+D62w5mb*!6)!cwBNAdU^5_@<$iv2 z0Jr`3F2{M9ZX^mBr$1)c=8~O+<|4&Btq&L<sR@91V=0T$4V}$7Ykl22+*gUa725v! zIq>xp8PtdgnJZGH<<kP2r2DbRSjuleYl&Y0$aTN)<Bh`-xJ2E}NA#O5$%i4iCPiOE z`QlmDT<?n7z4GntUhQHvWBIYDj+dm3h`*s~7*HNOgGw@EM_2K1yv`Ez6$>Eq(>b4{ z#LXvf*L&bl8q&!cJhn8fh}Ecm`+6rTuS+q5Q46ogU*=-q$RwFS=SiTxn0S<PlQSzN zZvgGacK*bMa=-btB-cO`L4r66B`B;m!^S(eLO==P5>>{#uMOq2xj~G$Dv8D)OSKPS zD*jHPxJ<~0lHFG&3)lI%8eFh4*YdC~$w$J?9~`dBag=}*lR?_|-Y_m8WKDh@p1u{* zyXNfJHy<X$8&~>r-=Yn_zC{_HLcSV-LF+zqN+xb`u`CUt=1i@$wGDypxtp#G_&ZHa zQ!(=$3^yLHk-ap{Fi`FtY+)eGlr~Laj#T}0;~kZO2xkFts#uu~eETw($O(ldsSI#K z?kKZ(L084W-Try3vIGl@Z!-a$<#P1_E?$hRK(+4bOoaZO8t$~>+JJbHo8dE~lj^Z9 zP4UtW&_eazeMzt%zelHFJM@eEa0z@3LfFcw77Oq2dTTfa=T@?oP@D9zr``fZHc=Av zbxT$r9Q?9f$@v&nI@rul535_hO0?W=30Ax-D4Af30T<(;9o^T;RMwM<$b``mhDPej zY@7b$QLJ$_l38*ZO>piPTE6T)t$8LEs&X$vHP&xS7nZf%5NY+gD%j(<|DA~0e_>Fq zJUL$&-4|OLR*RIp6mW$%fWh#K{2yPue7Jsjcyhmu|KrVN1nz6j2MkmZ$hb)t8!F{F zn?O>Sled(O>+7mguT{t@0s@wM?F8Mf+-q@OdA(*G1D9SIBT=xC*<lQ$!-P@e5s6&A z6`9OW3L?xDu1%u{z7V}zKE?tydbRr%n^i}q?4J+)#KLEfNgBtukq@H#K^d>NtE>cI zKRJrB8IF|<`ju=^k}(h_4!|$;rR&lr@2R`V6v5eHNf}{kSGzD!=C8dMS!0B}{d{)} zBQV@0r4jJI>XMSq{Z*N?cW(Z|cuMZUtH2TQ{}&eckMK<rSN&;k@KE_N23n<WEkF{V zt!3rsezg73NepdxXyJ_gth2UQXSQL7>U5w6Q>z4Y5_j6{!k|BikS*)qfPsb}6n1%d zMr<iYovSPM0IEa?i;)yc6Q(&CEzV8$H7{t0YE^mp-n|cE>8`{1m^pgT#$>`RGhq6X z`wGSA?ML(Qmr0Zndjxu+W2PIIRdFFH)LBvJ90A)>qnQmVv9?0D%l5HE=0_v`b)foq zWwFT_IWB8>Q60Oe_3C$0d!bEYfI`MG0v&nJi!a<Y879BW%Ppt>MBREyw+jcCB6H0` z=A1o*_PlH=Gu?G_LESyA_dx1Nf<QLMd*|;9<l>DCaTQ;3UlCL%Go6^u@9_G{N=<px zI%S>rniRF4`G?I2C`q6@>{u%8lN7c(yqZ`+C&r(5Jd--i*WF0)E=`N24%ta*pm$aQ zo4A;0lW-zC^m1V5*JWjsEz5}FnX5C2Y5T-a#dTcIzhpnz%Q)t*QsLPd>mHqPUAA+0 zjdxytPEkc3y)pNttDbTg@=k3rY>u-9d8(OXXYTTC-L_(g9bzL-<f}UpGt?G_+d=z! zdv_B4P#2qYk0yVPPh$NKc}BFfmy4Vj8&-&Q)wNayQf({pOCycl*B>om+N=me;YUS= zmVXih;I~m}^nSV;cc*dMf9ZihA24?BGoN#?JxR#6Z}c}s@$7C9xDzix)3K#-7(YiW zFnN5NPonB}W#eeeoeR@rOhA>uYLc2bGF2SH%J~uv2~0UKI#AgN=0hx3Ro#}Jq6Va{ zbT;lOPiCbPZnYJiE^giI+_XXZEsQt&w`q$R`}BwAgsNpTLUlE&ca`%B-VM#=6#IH$ z9XP;AaCN_~KC}RLHuX14`F1REq~cMX=m#{b;9zc@o$y!4FNX~^l9D)T4{gq$=Hnw) zeGP`_u9mAPJAK+jv9<RQl#_@Pi`8nW9A^Yxw_aKWmRZj*q<+vD+)S!(9*017^u<C~ zfW9dg2HW&_<{ZSuyjlVFU}`|X?f9*t2$GwHtQ8tRk>nU4_$%yo2WH1e#Z;bGGkN^R zBlys0S^(d$X{`5kuN-XdFgQ&2i_9E;&nd#z8E|HEra5MsQ-PZz;5u$Bnnvfh#Q&EU z2JU_4U!;2f2VK>_Fi}Pd7jCDVCYT)Y;r@K$k5!i-W%Y4MKjg<G?8DvcFGBFu>G^1t z36?H_PhtRyM>T=Rw{G)$2rRbGTeQjpUyfisW5cC?#hO`e+VB06dl4aa{0|X%iOfIQ z5xgnUI4;mPU{x*hl~Xp_rfajd!Ng}>zISKY?%QV+<qBxo=QrTN(ME8;BTX+>{Ns?+ zT)FTs0C#rK?rl(|<D$zl_(zn4PnptGL`f*$cJz8EV4~j1u{OFZ>8irSo##@=A4|9= zCgdJ@)mRBKXucisb%?Ev?I{0d>%9FMb|e+oBoN>7CvchC91t!4r3UNz9)goSI!jHi zvpy6yhJJ{ftUSy}A3jtQtjY(TxKvbBkpR^<p+hpw#yvZ{Chx_Ef*iyJth%enB3X0> zOFMij8&WRHB*d?F7df$Vwu5<G3k$1_MjJPin8sOj0bZ=j^9=7o16~(o);T0R#b(p% zcYe2ihKox*{kgz5xbB+(HcO6G)sYuePQGR8{UE6>8+VAy{=Cj-g`ae<y9W19)ini{ z!ZO}2y2t3~cxSj4$NM=`bCMW!x78<l<g_d<N}yw|)3rS{DGKgJPEuwUCYH_BKh8eH z@6p7s6tA!7d-HDZnf7mP=wr5>lVE2vmZL9~dCZgxaP(q|R0w2jR!{BZSb5yL<&@~P z^*Tzc;N3N?lfZ(8j7__)33v9IOFj_piN=TUgTwn+up#}Ah^QLzUTvt@_nz`U;!OD; zJ{Z=`gZ~-b{r|9UWq5bYw=b==kQgkVm4E4tzdHU6_!9f%FVCtC|I<6xK==pIa@UBu zjXKW<mtaIv3hvI(8!Bu8=fXt?y{6^f`*Dis1rQs;#+i)Pz@|aCDoL7yOLHw7Uff%j zqA`<V@w9a-0rf;%N9ZL2U@XY|!l`NIVy;F>U3F@q%8Nv>j&Le|_|~nex+~5_-$kxD zoBw}aWu`a^Ze|O^QJBN%#F+6*v4O@xxzn;qhFCN!?<-Cb<0vCjZ`LfW<R{nKtCtTg z)B4mWq<IDp1IrAxUEB#`p<~8sYgJdIFM;Vo3y*n_SvOL8+^MLjd*U(3L|#4OAKL6X z`^^8|;{KPl{<p)wTJTRuE_u;Cgg*lNi}yb>%6|j)egj_Jf4da35Jwpc3L}!Lz~sfJ zZ@S1ohF&@S4Y>a7ek|)hbiV5!x+v|OxPAIwzl*KPOc%~x4B_<CXn274OZhjJ;(tnR z{EK$yoBrd!=xBy7r8zCutyHI`)<6+bE9RJE)?0Q+H%THvIQR6t<5BeI-vBOXG|@XE zdUaZPdQDm=BM5J~hT%9s`*xq?0pb2d=?4`r54z7I|I^*mdo;n1oe%Bu8z4?$WL1m# zHN2d*r{}9Yne?hp=bz3B*FdhwWUkmwsh4hN1MhG3JzB<y{RSkx!3q)y=dQD5eN60E z#QFZ*f4BbZ`0D8eII($KK?OYqu7|Tr_0N!Q5}1U}Lb{1n`eY(+Lr;YwDGy(g?$a-I zh4jO<#QQJUU`k3J|4eoJXGt!}PK&(A>X{}u+gID?>w_eP3AU8DCV*tzBBjG)*FfH+ zFpKqko<9AI0{0z^3!rlfIW;~No3gY17!ht_U_547!OotUY45RO-67mWQeqVYR`TpP zJqV<bW(+(K8U`g}I%HTWCk|J_ofTlewx=46wqZfIt)svUPjrmhiHmJSHxoyu{93Kc zmet6Yg1~PK6P;TJI2e|)lHU7M*2mG2t6VQ^4!_FQHr=?|+<C}-&+fhc#Bz0?`9%6R zHdt@28nHv@fT4!Me$mG6rICPR)rD$lfkOMT!Dg!H6}uqT$|F?Fatdv|KQYs`)RNTT ziq{`7VUU<3H}Wzuy2_at9VHg34%%zD2&spU*A7^D3O3?<w1)fgb1{g^8q45@swY-4 zSIO?Yyz=QOA~P#n+}qM^Z2?Zv{*??!<>7ycf^~L9j#5rA3uj_D+)w?KcIapuf{3bs z`Go`g5Uj36UW6m&$l|A25A8?xy(>6iy*7@63xlB*uB>uiwI<WiVjk|$$6C(?;_Om< zJOxP(dCg5^^7cy`&A`zh@OtQ`{Vh7_?e{G1FFc}KWNCmGIub}7hVyay!O@kGR}<eZ zys$-p08ydymjNB>#4NFqI;oA^A~8vgfGF*V&kIHlf`GWNXp9Hb!qQXTLUG?}Io)!+ zy(Nzs6&+GCHB|N#t}`$BL(|YTNZBIyhVe>bRQFaMdf=&4ZT9O**(@H#{LLfYPqSHt zH|0&XRJ1(Y?C*ZB31Qvj%_q-F#u;gvdekc#YI-E5EIGI!*))X|FOPRhtyALa5aq{? zr9&c90#rT_C;|)dXzq1L7p33^nN3q%S{D#<byL=cy4)+Ah{p@qE>AP=N`*B<C)^b_ zH<g1pR}UF$0+Gh*8uMN3a3&fcYbfO#HF6b$K}$@P`6klbA;*`h*C*bgPxuBNbpsaZ z_mPIv&jHz3r*W#q12Hr-6B`s|7a`*sm{}srA=R;=1oE%wAUC&`UZ16uIiDi_aYnqF z*QFqnkId*n{p%oB%{S8RtyD2;y|q(#i6l|<XG)IRB7(O>W~dYN<5Ed>fOAl_O*LC= znk!ajG7vq;$E5l6#8o4YI~!9NB)!YLS(~g7qW~1~P8sa79X3buiIZ!^eC9SXl%eC} z=%linW<kCfKS2csYrEu!W||nglhE1mx3O~X3Xi{eU5X~E7#sZXY_NX0X(ZCm|2(5I z^NM2-Sy~yFCCyc`Eek(h%6EGe>t=3AQRQQt9QwUx)uK})p(>X*?f3I$8c}PXw5Z)M zcty9}*@4+Xs(1Y^zZ|1eYCPse82p~)O^E|zO$pb`Bc2Hfx|JVRUbJLQ0f>I#dx9j$ z`fX?{u7x1b#8vC9sjNtkozgsKpED@WmH@WuvvYbjmgMx<n4a30A}Kwuz#DL;&+@Z~ z)-_0}($Kgnr!VX#S;|iiTj{wFbIOXvZq?x2g8Yn4nY>9>%|(ya0kyG)>IkNGyljSP zMn*Ae&{W&XvrgC3JvQm7eSBy!zh|cx>^qHcf)iY1<V6W`czs#(2s2#SBiIxh1@-#+ zeQMI$J^Lr+E#{;_L(2&@H;EkR`=<fbVX}yOCF~9zy{2k72V`Kop|T|_`=U@a?H4C^ zCjr6s3q-%0>G{sZhLZv?bper~x-dr>jq>GB5l)&{KuY!96gTVIY-?0=>~g{PzzX$; zkDb{;z{Al|=$_Hu!K`4MR(D_kS7LKn`_PfpK0rhxU*FBU=gs{Fm2T(dY_Jv>&iz?S z8V_Ko=Jq1v25Nj9-VwjGKl-WP6Y^1IZsf-F-T}!*Dbd-u#ms#aQA3#EF3LGYcQUVg z_vO||cQ8U&%HGj-j4ACNjL2yRRFCT362oc5@3!=pO3GD;My=q>n=R<UbXb9H_k}7d zP!lw4(+Ma#lvmB-;1H)X$;;-(UdNREy3p-MMSon3%%RTp(t;?Tor<+)@;aJ8bJrKj zH>4)LSi)Z9aKFuz`YNb-_O;)FUvf~BPZ=0b!cu@k19*XZcxk)}%~Buh`uFbjr-EJv zcluxN60S%jmoA6E?x;%>RlZZ`EU~I9%C*mUg%*!{BbK~!p&Ug8w3}iJP?a7$^VQjl zy)3Z8BxKQOq%De|$<^y!TZ1+j0Ju=pPPHz;z2*Z?Ib1yao<1z9no<Uqpi>D?)*Wk; zK^zyyz;vUM$48uU_zmq>nChuEmPs?Z#R!6UpYSGa%w`;V)sxX&LUOoi9iH@FyzfXo zQ!rRrFG9SPDq=D`6}1xbb-9U`qsV8>!g#Ry8j7LER47X1-kw?cuDNVWw_y^U<YWq} zo3T#}jLgk+Y$kg~+yZ4Uqyu!zoQEt+=rpas@gHCL<W~X5&Bv}-c6h<?(E3Yue5|9* z0&<r`CeutU#KkRmm5jx8Vrl@7-83WHSLk!}kL+dU{4=YDTjXrvsWE4peLygt@ZG8+ zCGIsk=c@M8ZC_ts`h8Q{3;7uzg?9}@8NV-wQ(aW2pz!bBz&@m}($rkfdq8fHM47Vk z#Ub|fP@pbrQ?RX@`2)5*T#wQOOHn0w>$8g^ZI9@O+u*XrxUu+(7gZW(_e?!ASFjvD zAgUGnhe03W@t`xTeVAM0tOb8+LK$A?@HG|tZ-C1^mgHZ*LUL1T`vM4JlFSj~z!9M; z)w0C#O9(PPjWvv`$4$c#MQC0&3Z2s4nF#dsP@4{NOt}+P>=R7xo2EQSFiqsdhOz!= z5MxO3llItg^-f8>Y`Hz`*R7$Mm*r#J{A&Q?_9jaJu)~p>@un3JV<k8|b`r=^;KJcx zWT4ifF-D<Sf!N?!1pWYcnM-hw)GduyyNP!a9bH_K^zVw2`EQC|=eRB?5@;$W7N?yz zTbNN{WX?=EWxl3K<|^6*7z3v4bahO;Nk$dX6s5xnkSap6DdiG9eV><GsRA`?h2IVz ztZv0uk`avh+#((vJ$|zf1hi}SB2(}Jb;^Mn3g{icN{5Fn`Z<_*!q>UJ%J$`U9hII) zcCy!<6!3wh&UFwq_FB@llgW4Dk3_iA(~`!CAk#<AtSQI8y1Hb~{P|D3*N&d|)7xdy z$}L>CkskMwVwd;Iy~Pk$dDcqU-`Ayfo0!6c`Q2+>T$nIgPn>U;x)PCb|30GDlAPml zOf3SNaG#OrAKMz+LB=kRucVBgKoi>|Emt~!9MzB<ZsU68i&kI<^qk6vi@K8huui{) zWM`z|u=iZK>^FH|H0r~{6)bDNj;beL*xusq_4_k`3$JT<9cv3hzvZ&SFKSEoQ)3I_ zCsggK?$>TooSVeRx8<D){cInpJMhbmOHUeW%sMx{Dc|Ngk4u}Ok>XuX%1V}b&iNEP zWAY6xy+D1K*hsJ(q*h?HK@3j2UzaIcn=%9s$9yTxf%4vOLZcvM{M`9{zXAF+#ql>J zNONN5f!7MAHiHvDZd-fIEDeAN*9Dfd+t=G&*7;;Se3ea3<k*0PP?BiA*m=wF{9Yys zjf=w6>(wY*XkGUvS*H93ydyp~kv^TlS!b~PnbrP-$N1NY{ky+FpCZI+Z~d@%yy1AZ z5r3v9{g2=O1t6tRe{MhUnaBJt%Y^xIjr|S(cdbGlx|H``M<XPtQDs>dvhwZ!7`pV& z9*#hU&{?%h2nyis2NB}-)%tzUSlufJ<#V|<JNS|l#C$JS#r`{2<$u#|Q_x5;@#cFZ zZ0Bf=Io&Xm9?zYHTmePwSJzJ>-f<T2rmE+-%>Yw|KYU2E>zJ>5!bmu%;TMBkah{7# zr$$|P-4dF+IWJU2^z8AVtNFm&tp?N>VX5J!H!^-l&~ZSjLu<d7w?~vEc0U(-FwyQl z4D|BqMw*LH{rK!a+r}Gxl5ygC>N#Sx^87(ISfKSH;Kg+q8RDf~g#2?;Oyd^va$~pv z><W7}uiSEomAmgS&G>IXWz)~Ffll&1w!2RBeCZxHr=+FJhljtUq!5J5>w%VoZw}v8 zc=*|tcneEw{MkVK2Pdb%SoSyIFYv$z)_;LP|NkeBBt3lNV?_?<t`7>>9PYkx+QMNZ z_^V9&QgwbF^_V6xFAHkHF}&mM;dl89lglBHe%U0sXu;C3O*1z;X;PVvna|_tR!`Yy zsfr;PtIZ7~Vy*+YF7w{Y+in3hXZ>Jbo<cn6F4|K%#fe3_pS-qwI|jS!?UZX*38Q>K z#^LEP(cZ8(cUu1viWr^6?N7}wxqYUV%*)-Z?qUii4<I_(`IG4UKcb?~WR0I9G+~&e z+8)&h;(-z%2hPU+FpaNAnjFCcZjg}cD!<M&eq<Gk-v-}yt}$us&&$t7m;skVEY<L8 zKsv+K_cW!x>^SlpSTWCs?*-YJ=4qs&iPsVlTrDimqcfsx#@)<?0Q#aV!l9zLL>R9~ zuEI&E>V+DCBy7m&rqEJHBr7GU9YlVk53YgrcfB9#GH*|y>r*)jSrcxU^*LpzX8+N3 zm-8cVjoZ~Fh4_oW9&vg*%8Fyy*L*{7#%&=F9yYwjmd*m=QU_8&a3MdYH<7moPILy` zW$qK)gWk_*YqiU@+K@=iC!>A0=8CM$z<=@1(C0OjeCpsz;MQi0kM_~w=ZG#;BSb~7 zcXU~a-FlHU$(C)4V2}w$CD?!X(&>-w;NKQltXfsYM+AF|>+VXMkr449Iiu!`U8XK7 zmik|>Ret^ChS9$GM;-sqHGR&12%Jz={<5qnyhZv*7rffR+7th5v<@l9mxJ|CPT6ds zHGCq~!&fbwH4%y{6dW;6(0diSX(+=Wo~enmbb6tWrX|};JtTLnn#|{~>U3UxZZGs? zfaIJ4cpyiCNwX=FKG<F<HZBntuc(hg)(0HYkagZ#()^K7&td0fWco~bcxcT`kC6}; z>K`{if4`agXShjU-kqWKYg~1xnbb+AM=ug4R48Huf}t;Ml9Jj4H1nCv-k6!askp=u z8=X;e+a@Z-qZ_BEsIBc8$_!}MX}{dc=q&-MXMF1I)NbI|Sldow@XCzk<;QMKO(0SP ztijDws<I~PpmDvf=-cW!2=wh9*=V>_J7N0BM$8xK&zn(8JQ&d|3KaWH`4Z+@;*eUP zs%hKrvmIT0K})IslChC0gR9J|AETeclH{kmFw1mCvNWTC4X{F1+Z6SUuLovf_Yd=? z<ZLnsCNe=_tf1=^Rh=WbDM82@#}21@|MXdb<EK{JBBN#R=zj;O4Z{Cg-gdi(9E7bE ztNGd?{BGwhiLhIAy5Sc$oEyLxV(6{v@UEgwkEQ=?_ok-R(J{?t$jesRhQEGG{zL!2 zHv*4t^KX!oK2|C5zKWMKmP`hnA=k4j4U^w)QA8DUmW9fONReQ~c!dfv_3qEbi7nMO zIT3e%DzTjsP3ZAF4qA>T314WaffV!@Q?6&FQj+j-(9gVLpM=z6B<{KQcsr{@b||cK zJOiQ9DfE3g6e#n-y!eX>j0%yUHN{(W9X5EQG?GnFPIj`KqT(9<itKG}@BRm03qax{ za-`Z+98srtc{omyL%E}u^<7we()yl-u@e&8=3W%WrHOC36lYu9Ndu^%5n%s@Ne#+9 ztr5__t!{9CC9Paig*Xeo6Y96nYf^~2Pw1{f=lLuZ*6Z~tt)2jXcE)1Xp`%9lUz*n{ zXg@jTEMk$xjz+E<MGohW!=ZGvtfE#?KqcbE>j}JjnwsSFAhw*ySY^hRr|lttJ@Qh? z%x`-9f8-0DWu!-+$L{v;keB|kr7(^5wp`2}P{&6b+5#=7ie~E2rGMDjs;}5ZkCF)t z8*Z2Wr7?;M4?fok6<yQ=R{kug3)DM5z`Fet@AH4yRq-E=8#MGZDa?0FJ!#cc1dW7@ z+Li=*J$_mo6v(H48E{FKt>!Dtm0~B7sSvUuv+alkx*GDOqm8~%IJU1~XHyI*Ut`$f z5t&}c?82;BMU+gSj@cU!DlK7bk&$ZTQjC~dBv)E_Rl)nrB~FNBO4P!3@Xq41xwgFr zSX}F)FT|cOtlCgEt>4@SDyLa009ptPUC=v0o?S-c_Ljmjb4{T}!-d-`G2fl<><whB z9t?|(GS`;8E~>Fu*9Hz49{&2(5)uUgDZD{M<u}r2yA=q2nJ=<#ntU?}=Ot^bq&*0* z4%98P(54glik-hl-?b%;ZWEZuU)D_ZK?Fn5LbkE>QA$3S7Z?wV-L@;u)2!FJ1*E87 zR3%l&85=1I5h{|QnwmdvdP|bq)q7hwHaz3ybLDkgz!ZRd#e|INq@3bbYG_g}te6y7 zCC%7pI*^(h%o;GNEAuwar}VKZyxsXFQ!V~pmYIC?f3s&O|J&PgW#Yd}W_ra3xTDKg z^_Ju6J?=Do`<i)>yARK2wUoPERvCT4{2_Z&IF2CFpu?^v8@u&|k2ik(&&LJ4m-BQf zNtxfpuI{mT2il!4#?_LqDZOqX;Nokf<U!j>Cf$FmbKUyj?u+8kxo!p&Jg?GV(@2+& zsM0f2J{pFo2*25QZRs)n*RVjDn${RyCWOiz8q)AC|3)J*243dqdqutT%&L1R?5N6- zj2g*D)S-{!x+fLy1}0gCJ8o>v29cB+(>(o2sCifhm19H7_3jE;JJ^mUA)7T;o(Y&T z_pn=4khcvy_nNM~Pa(B?EXDV96s55tr#y{X(KXbiQc>MhBMTvu8V5D+(kl%bX`JPK zB8DyQwz$d^=*_G@ryG^o0J#wND!(;>q@7o}fu@DZM%Q+ywfN(GCghQ>2xv+HbUn37 zXbvw`PtnsDs4T1FrZ8VMFDdTmm&3&7-HFSv1)6k|w&?#?Fp#!IYglV-iz;j-GXB(5 z2ao_<ivd4+@y<3aTuQ8&QyyFA`76#MY8Fh47l7v7g})%U{f{g7|Hu%ER2nL3t!h^e z<wXgqBJKs6=Ay?W|R)b5|(Q0cWqCL;E*QZ}|9<0k5##@DHUAtd0Zoco%KfO|k;x zz+h?O0L#mSXE-xy(iI~6AC+uAZuQY`Z!e3cc2fb6c~Oemx|s<6*LB=4yz3raNgF%l zmOcMccDY-?;~#(eTlv-Y$0aOfRHbY_GW-6i4Tt@?p+5LelXX61GT16k@v%#ta<2(N z^BzhdeJd4{Ks_7dcf*g~_v=M2x+_;cX|dh66$*)b`}f0tEwBEi+WNm7{*%-#unEGn zdTC|diiMc|m&6v_u_ob2PCLC|3nt^yUHGxx?6%aX{Z(e2qZ?8IMqfB#AVl&ae#E8G z@K7B5NDkjtHMY5>bws_z!HTDVJg#@JONG{6d<=3YjqYVLv~2kWzuz`zk&xT}mdAA? znzbs7<hdDUNccK*%n{J!tP5C99i>b1voMXas<jxF>>~EDsC8dsn<aO!fTeL=oo4nH zMnpMvSL&26-2~jFKS8F4U$jF;7t!2-G`kQ-*S1E=tn}meUAD)VB7#c9Hk|}4_pa|g z1Pu@i&WQCQ9eJzDRh!?nl*5I!q-K5T*Mby_z=lS~EJk=+tyV(aQ|a(yx=*iS-S2l% zzqCD9?hx57trEE<pD}!xD&QeLhHW=&g_d6J$!5>U$`RoRNbto$dh3e{y4P!iFrv># za<+|QXi?pJec*tw>y4gf0bXS$y3}&pVdXviB?4|3kLQQ=ezmr@A0&=PPA9@MMZ>d5 z|DNCnj}D&~1+w%vhMT(bNk)0HVDU|9xA3RD*M0-M3mkmu%SV?x3{A!whNbKkSs<0O zGDlJT$8%Q|>k&)cCHj`^)y(63I5AU3mqZ-9@QoK7PSUg?e%n)vR*Beo|JbmpXP18U z<c3Lq8a-6-WJLlWv**oX$l$LgB!S#2D!&0D5d@Oeb`^8r30B(0J~nxh4RH6t7Gg*K z;KrE+$3-F!Pk|t~pb$yXX%{734^jvr3#U%;oR`i2>gC-{d>`U{VZ7Zh#@H3U<eOJr zRSzzKqp{gWl|taU2R(1hr-<GcDJAH_>1oe7tD(0H!S%_7?+9h5>{QX|Hy4H_-?e?w zCyb4je;gVal2Pl8k;xPq6jc$i2)`*6-EoDBR>f*Zpt1tIFCMQ=EuyyW+)P^unGp0k z2|Fm!&tGFlJ;=AglI(`xZi7Y1YHRu_>Rnp$wpotYl79nAWaf<BPfEL<SAEW9hHt#H zS5c(q*Pa}wU~%G1Cr6H8VVmyQLPQ`uay<ur<$l!FJIqG_37t+xvTV!+N6USu424uH zC6{?0qXTba(o_#HogMHyG$<o<D|IU1>sD^@-qiW3I>@@2+G0MMq!e2guOSBF&mVBh zKnTOBOtLa3^jr&t1&N+t>SUN!`>Ir~tOPMzgg(_#8SG+1N7<^sg0DGb6}cA{rYE7} z$|<uW>vbHjdo-z2_=6DFyt`)0ZB|(2G@}ru>GQ*-GMT(mJOzAKWh~jK2gwnK?J5JE z?F&EC7jmHaB_j;HuIes%IfbIbRoqvaw#@y{Mg7ok=^`=;e1S%ErN#rMBHYr^!8$E4 z_J?flaX2)GCmGp;Ddk&7O!+)6yxH!8J~~(`Ai<gid39pqaN%l`R=^l<J_hGph%=DL zDIXb=|1@j_6SJd@{yEVujEio8mm3(tyQQ$^UH5NRN=cY();OgjaI%+ul=S{D<{NLU zD;hqi@w;!EXyA9{*vcwuAEV!V#{^PL--093fALJeK6-aQ^y1&6#6!uY+#Sx~>8Tv{ zxaTpYqhnICRFyz+OddowP}N}n{oQvS*2(fW8Zc%qd4j}xb=K)Su%sZU)4QSortxu) zUq1G%J%$!A{<V!_e<l8Eoe>DD-u}ofZ}Bn%UCKj?kajg1icbRZ(pFvDDTVK82P6;* zSKL2lw|fWbNF5_g)3lBKGwS*<Y%h$BZ}l@3yYyJxdBphQm&e!dGJ%B~qD1`9h1AKq zlj(h{UxRQrgd9s?;_%==WSYT*`NXTwL!GSQxCQ_5HPdbyt9!^c1@<!2F`^ZVvH~@& zDlU4dL>hnIz$<r|q^XWe!*Z1ubqFSTd|P=1@aNGZGbd9Zab4~jY(Cw3XrEPIz2o0w zy^Qc2tA1AZ3A^;(w8!A(ic6pr+bUSA4SzDBaC+OY`>+bGvN!Q^oIhaet%&ShTsPk< z-A1`D25F-QOmh@#sJiYc^`3V?Zw6ON^5C-MGQKN`ZwhOoY=YhJ%XPm2AH?OX&ga6v zj_ny4;bCJU%}opG=v1QLe!jtlOSCE5&A$Of>v!sPTaT%Y%i7_kp4UG>Eg!E@%G8?| zDd*$lH~Zza*dH?#z}Jqlj<raW?Wt(UVDjlWzP`_D+H!5*FUvLd7L&10Zq8=f6f{hE z#5}$99A0ONhGk^oXkSlB%JCk1Ebi&*DIcv%5V`#P1oT&~%GunZe4khAsVpp6kb?g? zKC{>3>T!f#y0y}m5X<)iQ&_X6&TAoi1JRid8-(~c)iKSzMxD70ic^;tyRP%4=CzGi z20boJsEtRo6V!p%U4=|Pty$2sCL~-lF=GeI8dz|m{3oF83N#c7+kWJmdplkoi9wC@ zKZjjb>vgYwNba}9IM(K<V+npNQ_8{0U^#Kz-cUK?<gJEPLHjU;6<E<)Q%9_)vPZpT zu@O$vnJ1q`m2x3UQ8=b^CFH7H-ncVa*smoL%8RDUlB4tlesX%LFY_ExHD!azs8PP< zZ78HPrpPrAq)OIg*PD?^sMgQ~H$3U&LL-+1)FX#bxZamD3pP7j70niV2v7n^uj7k? z^4s~?7Cc!WlQ8r`FwlzDX3%o;uX6W{;@l<HimHxn^S?dx)f%FXYby@$I@~dE@?-5v zJ6YFnLm8dO-DV5h&@|;!mM#5^il~Uvmm*okSbY$p6igz7jT_&J`v(5#-F#bucWqJd zMJh{J=Qv(+NA4O4%(Nh2Smo4N^?6bGgps4bBN>@M*LA0%Pg(HX=cCQ{`e!5*{Zbe5 zw&!ay%B|fiI>av-#S11Ocmt9-a#9GNuF$xQgA`vu>%-~6r?MOIj8kut65I%51?=Dv zA!SWdL;AbVhn0>sS!x!gi<>t<rKOr#e2!ImGIdGt&sY>T)0Ch#e?J~sFEzn78$CsC z{XH;N3+^KWE1wak?<TlZ5A|P-FCXaOal13N@JfoM19ummQ#n=l`5kW8N;+1+*aAcS z7CAs8BzeH|<)oV}&vO4@DEpa9x`TAs0*7}SUk)3t@7pDYg>$+ueBwJgW0rip4jADJ zoH_g8i2&BxxiqeGS>r7B5)5UE9b!i&%KKp8$H9nBvMzDTD>GP*Q|zX~eYb*ze5m!) zsnk4TFRW=cdIck8$c&mX_DT=m4zdeYmQ#X?mp`(9WWU4IJZ^YruC>@J@Z*ou6v-DK zLHPK(DYtdbBqYMAOIActUu>~1H5zJZ=LUO$*lK8y%x4%E<TiwNy11;`O+u3Vd96tL zeGi&WADas?YFp)y=<ZQJh}MJl#U=HjljlEj?%DeUe-Pzm7tFUu*6%<s)b>erQFyV# zWaz)f3^S{xntvNNFgt3d<xD>o9VlO<;q)%Y>)jW;MWYW{Rsv@FIAs+<&_(C=z)as` zsbjj|0HaH~C-NjzUkCVVUqQLu@KG1VgZFQNnF~B0fthon^>VDv(#jn=w;zyPl>S>` z{);X5c2N((=_|cwJhiliDeL8|GQ!>wNV6yPM!QqUk^3}_uHG&>g^x4*Yr;eAv@3?+ zxZ&73o?4|rOGhqY{t96eySNsQlk?XncIEESD)STM{jkI)aQj||OM^=PGpf1A=3DbK zYK-%DL0vC~&?2%s+#6O097d9NOa%{a8O1&(zp$*+d#@h)5`+L{rR>O2y7<efAn~D& zZD}dz^16?+lZrcnp7os4(z;Z)l5Tyq{|OImb)Z>~U5y3Wt|aTe8juimP<ec<c4)&w zalcYBsXJGpqclNf#bFXmTn6|duN##3tbAjTBXg@1jc?I~WnuChtRBg!_d9%*HlNk$ zoJ0B@3sr5df??4{<MmSQKhrvv@FOF}5y>B+*2ltxgZSH^u(C!1O!kQF+v;3g(0qs; z(^O5$^*lbBLJS;xgqhEeQfI<K1iU(>Y#*%_J+5ISyI^|2O75%NK4`3X%is{Wtj@ww z&+=Z=Taxv3tO4hmY-H#W(W(NxnwZ%8<0Cmndt&=H0LERA<B~6V_1gq$JN8r18<^GN z*RhIDH&15Q1&|_k1(RoA;pI~g0su*KPU$7YDjSvzEdmM=$YQ4+0a4~An(>~Hm-ufi zCu2_I>nwR&9@)8}Ea1;fj6)n-i0Mvh>W&K)9WKv4O&knf>qCU0a43rx-*Lk@Ei~4@ z|LXM6z*N!ckGT5odS?Gg<x38U5~nl5T1S5R9aTa4i>rnbeZo%##LA4jo%x(O;y&f) z#Hgb{LFGWA=Q>+7(L`hFks&>6UDzZGCqkxucz9S_u!p#bTycLLsxH0ywOy*CXIuB` zL04_Q$3a!D#kdEYhpP|^mRfoL#+=d;R!uvcz8#4FjHt^+Tvp^5JuKB~AMEEQEmWzp zcZpO)1*R_ifpe{SW^Iqsv3(FV@Yg9x)gNN<W^oIZs)L4ZSD5NqVjr*?4Dd2hGFtGE zF>SqWZr_aZy7JL)>U1i2x(+o)&cUA@Dh<QF=}D`aDhj>8xOWz*n(tw{cMGQGlHZww z$TT$(?{|K<{S?3+Zoclb^%Y}mwa#Q^sz|N`QQ-);*&{J~AQYGG#J*Xw*cmEo;WscV zaKd$VvgoWRFwU`4cwd3){P+^+{X{0s*m7Hv(t(v)j$g#6jc>(gQiqmtnpvTl34egg zaeYW<I@LB>jCRf}fHIPtnlvFJ39Qq~^~$;V5BT!dUDDXKIYF~-nibVmj|Cwcoo1^7 zccI(HIDKOcyMnk>9~$a$x9=jekkm6Xwe#~;(zwB{l99CeF$=Q1wfnXnEv|O3DK&PE z_E|;mtL>7hJ=x5;xmJ=SFyzTy0Y5ub2&4k<?(~7a(F`;7%-jAzk~x8(%I65?Vq^vm z*3-0)iZ<*_S59TIKXiM!HhN9P_*#`gbSkO~hwETsN6hQqLa3)6f4w;dZLk0z!GAu_ zMTVgkq><8OQ6-+z4V9r}kdZej0+V6+4Q&GR=FG0#1oPu+>GCab@0fUY$m15Bb1C-D z>g24aW4bIJzQN)82J4CWYK}cbD_nF@YPu-mk`v2be62)cZeq1bV$Q{t*IZXlr<s{0 z{CqEc`05muA!r`DpsNfC-BVQ*V)$W9$H-d#7)IsUHPaH&k?0}v8}M{Xr3Xq)4}4q* zWl#M;lkegTjq<!C2^$p-^%K}YCFa&9)~@9^fmC>Cx>%Bt&=&YSMw_{=^O>P{J~D}k zg^x$gqZ8_XV-xJ-#5B`<1D-qV8^1rMYH|%Q;qL&=<FISpZ0aO2vnzDf0W>)ilYzCw zIR}9-bl4{Yf{MdR7BtE7km9n~MWM^8y9U%llh8hKUp%&`AA?L1IV{VScm%DFk@!G$ z+lg_vI7=P=yu(5;-sk!aiK|w0Pqzb#X4RlN*FV}mameg5gw`5QtQ8pzm&w<~@2Rba ztXxSMx-3asDUw^cHKf@{@?;{>Am;iJCLPGBTqGfCr}^uKCg#Cv0ZE4Se*BxyHpV`n z^Iq#s-30IP%8ATMeP-u7N0r^jBInZ3st*%JB!<p4u@BhTQI=>r^YxABcGa40(y}ex zBpXujRITXFP0KI#G3X*u%Tjl;3*o$pwlvhj-51=NI8ZT~kGtgf>V2|@%6S8Ltu$H5 z4KW!>DbYm<WFgBnwDs~M6LU_endimuE9-(IV>iu@Q%hTLQR4V^fSZ>jz@PhyaH5C) zl-sr`PY?|=)H5_^K*lt#a+X=d;x|BEHSziA@P~pS-$mnL!z$hY4yet`e(4F;)*l^l z(-jk&m!~|c6uN6aIX8=h)%$F-2l!e4N|TAL87N<J%g;#Mw&Q*7;{o*#*^KTI?h?cQ zjE5b(3=1PDdWe75A0AVpkdrNP66=Nacd@^u*-nX{jDtty4h-LC4(so$EbDhWL_mkU zjo(!YUAZ6mqe{mmnHoBAwdwA$KoDmpC4DaMHB;%qj=0Lri+;zKcQGkt2n|@gZyge0 zk#flNcK_F};+jw6;g^v*tW{Pme&p!)Pe&~~5zgkTu4~!QfI9VWF5ihED-G@>38GMu z5rb<Wi(=8Z=##VbA6$~*K=cP*LOP**pygZpj;T0{oe#vYLFh{J)YRor$RN^$F~zPK zW%mJ2p*^)ddiKZ$SuW!=FmhR8;OjO_#9R#mkz!j2A%pj+W4Cw5*S4X}DxFw23$0G` zvD*M^b4#63NkkM63oI5)VXMlCFD^!fS79)pBppfsA@d5C7PtX%ZH~VI*9g>NlOR`a z+7339beP1q*|T(&O%Lf+vz3=mTXk6ZN_K~cf<Ddrr4q<1ISBQ=XbfGe(<>UXz;_-s zCBE4FA1&IXLFpFMAl5#0N{*X$FMCcMN!-GZFQX84#sb>-@<8kxx~QG;Emo!;O>=<r zLX_Pd<!{cb*4>Ei8e=1MLw$NB<Rkl=t3O^x*^pHXXG&q>3QrK&aeXWDQ7O&YEC;3Q zk;Tr5t(%)sabMhnb*?>2qDSfV<k?Bg)1(%H*nHB2^o|NFs~fdFqJ}hC#p!x0jBW$Q zRW7&zD7a|=J7l%Q+=mj){y5DiYm1nhv`3vG95!#7n?faRqP+Bf18(gZWVeY^kGkFR z%&Iij)YWDQ5ug$DcmLq2Mvh@xYhMx|af)&4@b|`ipgS{$o;Q0at;z?sGJ)G=F+#5f z)u};Z!*u$V#Uu1FYVEipVdc9L7Ew|RV!$Y>0F-EsQl)Y1h$S{RzaY9yCNE69&t{q# z;LJ!WX?KlI&To7u8yM8|sb$fU`(XcqItsN^h*HoYOfI%xV289XC1?={5@%N8a5D1r z_V#K!DCbAI%0ooq4T(Chs;S%!t}5NfhA#_HSM6wV4ZZx|nDARDM4=~ssn!iQYC$Ad zo(}9ZZyHtR5Ed@#xF}(My@EF&3(03T)cM1+V6!_+LZ;K4%|4kQ@^IQx=0yijvCCb~ zx@)LWBb9HiHmALw<8EDviGm$>CF6Ch1j?pd7T`|Oq0)&$0vj%!SADBGU>TccDa)cM z_Q_fcs0OLv3?=V9-A8dM2kO2xdX~;>liI!78Z2ZzDKz8w(ZOjzyJSd#LoRX<iqjuw zAz@fj*=QNbt;AXG>jJO~+!tD|O_{nSWd<z4myC5BBBPugyJ%4<TY$&Y&^L?b`>5MP zb=Y*GQ_|<0BD;b+?G)lga(UG4o$cq*OP2&cp%HSX#_Dz$G@5uKwItldlIa}h92Hh` zQ3OEY_oOy&(kjJbDJj!6-;H;r37KhgK>)~1s{g6>FAQaW%=#1ozd2U8!#+gG0*Xie z9Y4`?`V%e5%6oLpFFklpExqL13m)(N@$c_!R*g@uzZ3ie8!2SH#vbahM<14HK2hIj zkDu_>eu1PLHR=v8H|PSB=3m9tFuCw8M!jy`F{ErhL<M6D4+5=#9T2T2H@C3P0`$+( zFCjxww=av?)-uh+^QoIs`*Qf++L=J8Y>6H4e$E;4HMPhn%8XA8QnVZgT146SJXBn4 zQEldN;516v6a%ktT(SeVwJ!9<7@zk%->UZ+PkZT)eLt30{CQXIn{&%!GyS{Ly6UpM zc!{h?Jjolga6evbk|cxY2U$wDGpuZwB200?ElMtMGoPQKJS%B-;7M-Vbo;rto~tv1 zTZVxui&Nlq=Yrz>0qGIFc@y#GzW$4TUj0A_Wro~47M6meq}Yj<hZ5G(-n9htd}?ir z6vivg)JzcW-vH$jmoEd>0nJa2wp6DiRKikxw=I6<IIqAX1%~?p?Q<N0aZ&>8uXm4M zz{_$f+pN%^HL&CC92K@j(QQOsx-^lMAi<>f^nUTL_QO_!13d_3cNX`|N8_+u)o#?{ z-WWH^j2a2LN~uVY;#T`hTwT_>nYd04p;GJz69;PLRtOE7%B=2qysl&Ksj-w(VF$$I znT9}Ms0fXp9sHY7u<th~y|vLPuH`jWj(Kei3OAL`?u>Z1W9{N*St5)=qcflK<ln7! zME)ElTvq=?oOYOej9CVgweU5Jn#-8;Eq)jNszLb%L<L6jv=eBUhCy}*n36L2FY)Jy zC2G>N5>e(uZMC!Ta+Rp>J(jX>>1zrq-_~0X*M|EMR|5#6^D7IJQNgaQ;li{xAS-vk z8z29z?a-glKk&c%=-zqa=ksq$8S22lqkU$4pw9i!cKp)i!0TUASMT~8@XGv&|6M!# zdzoiK^X^&cPlKhkin(je1DR4>*0;Ok<0tIQMfDXKPZMF(VFiHTZQB~U`qv9%DaW_m zEXPWv5i~CCS3uSKC4Tkh4@ywuMMoO2VMCpvamMqshcO5A^X6G`r=Q=rA5=Y=jDGzl z#mBIhML=pMg)YRR<$uxkmH};bX}d5j))uSaR*HLZ3(!*B-KAJ@m*8!IVkv~;B|vZs z1d0TQ;#%AT!66W|XuT)x^E~s-J2UTmbIy6spPilT{MdW#d#!cLbzj$u`|JT#_nm=m z7>;(t*BlbYI&3-&?Hl<N^lld=&k()GVISd)!EpoQvNdrKE~Y(sZDh96-{H$A5a8nw zyT0P)ZAw~Eb0SXgyyAWG*&H0>W&zqO@ns}M_%F69eVFaT*rj0jG~pP3f`XQ=up%0F z3WC9%b?05LA-qKKyK1AQ_0qHiKXLRemCINo`grJ%n^ORMc0+VX-$`)g@kUz*iK;t` zfQ6mI{5QqsD}KirqmvKgK5?UCA?rZtWn<rqkR^c340>nhD@l9xN-gGfk7vLTcLDNt zcpaVP%%jZlx%^YX<N(g_3f`E6kVl5ALpB+Py)3@FWiA+?&tX1ej|#3N(t`WI<)N{t zp*GE_UwNwoN|1aun}hMPe?Ur^AUeJ&C7!glIEb_Mk*@1ae}h9T753qwfUdhpLSs_I zNpOIDNa$<IH(PU!W>b1rqKft!bsr{0M99IC&y_Z={O-&S47XQBM?e<kDH!kpOr=q+ zykNnXlUr6Pb**B4(<_$W$CRo~SC#r6AsX_J8bBdYV-ZyWcQHa63nw?8&k>LYi*rTk z%+0&D*A~q2=GRgUUTNZHrHHuG@Db*1N?10^Sl>~ai;fl{$bLyk3T7aJy1!zaEV`HB zLH!eFybx5~r~`W4j+i;CH^(>?mp%7xwXdW5mMLkuK0D*9dQ-%|rbidrUmj{_>qFo* zjYvxBVdAKMD#xYjURnStF$1<{!!okd#=HT_jL%<U)soG_B=&S_Tq^7ll!GiGBURol zDq4g6gmM_us2@p4|Ep*rJC%N#8^m;qg>eI!c%@oW#`KS-hYK+N3@u3??y1N#Xkn61 zR%R>lu0eCQmumW4g^l$PwU?F*?sx3T%ABBLVE+Uf;23=XxG*Ay{HjLEt1vUDIAoW4 zZYV2~XdS?kcis2kfzs3CS;`z$*W!faaS8)pD*Ya6-z{Gt@G%E<{|`>yFbRt?69liQ zSlK>jnX60lK#RD3ADu$HuhtDO{!Xleyjl*&mH-N3vYpFIMTvz(u0>}{-{)ivx~*Pi zK=`R6S>-`;GPt4UOkjAyI-&S0&YHw_r!^EePA_%Vdl?>I(Wo*_|1n14N<08Tfb^!J z8!)Syn6=;%m3L1w#`U(Z?{_>@=zmK(ceF(^Hg<rH7_uzm<`Q*hC;_;g_30Bq9j%?a z=p<Gwd!Bz^#K_6?EP!Yr>Dd-hY?^G(_!qoE&V?jk&7_nFddrJ&XcV2#tdDw01rIzG zVXz}6q{0DZcT<@<5t>X6bOaGSReG^o`Xn4H3p&vNEpTWEDhxl29ih8sU)!{46H~m1 znvjNRt(sskTA@?gDhKIrVYIjdnB?2;pBqcxmmBU~gt1~}vqt^9gI5AF7sInW7XV#d zICe79UL|m275>XfQ?((J59uwEbhSH8^pr_GiD?9<L03Vul%;0#QlrL3oDKyCt|`v1 zbLlMT$9(PMuWup}1`hNmTw195%)Y#ZIc?>W2=z%@KO#N4vFJ_6wc*?C$x_L0U|IW> zqQAt<QQuH^Mrq=VL|@)wC;740qBr%jfpklRQ&ppii;F#o)@3;G!EJ@9$06p(>~s5% zyK=7BJ#737r^yAY@9a1QTSrU&yXKFXn+dJ>hZ_^>b=gNl)E0Z@TVJAkNWu*1Q>&jJ zX4sLQf)|d?@-qJ(o%{FoV!3M573CsIu>c-6#~{WxCX35?<8q($+i{O?DJttcCqI7t zwL3}f?-uBa%lo%1+QmX2m`IT;<Bde^EH^G|&@$86`}%se7%AgI2(FY!EfasF?Gb`b zt;`nYQT1;E2M<7ziJW#`uy&(97!0f{G#rbpVh)07MzB8_9HBMqkB$$2`DoH@(hTKY ziER)D*r$CXAXv-y2fP;PbA?#Y(+Ze*gBM<u7+3o^I)QkYn%hLDwULNb(L$i?`*W*@ z%0(6W1|iE1zNbACE)S<&*abV7@0_<KE}=_CQ3pb;cnzHW*Dyn4#=@tP&e6>K=8(m@ z>=8PZM><4PHO&I;_D*gGZpU9nULT74X19@|pQTTlIJ-I{2O7h15rO-J#V+ZBlhh+} zHgs(5cy@sUnZX&{QlVDoGC@(*2av${q_<=A-tTEH9T(cb(|VXzMUi6`_m2scq@-(4 zPQQF7-*Z96eb8u?vQ)52$V@m^+!A{?lwZ=QO5>J3JY?-XCO3qnK6AGTHv9NPi5<$I z2;(bWpE&F@BGiYHKJTQeKCiZMfVUdVN^h`rHfxRgYAZh@3~CrP45^=0tQnn>QdUiA zixJ1Y<!xPd6q9Mur2<SS_b4g%6RJrM+T*aZ!#nI_Z9?LBEus+UQ$A3L=0<Tkk}g%G zMsKO;uJ1=Ri@n!oW-Akoh_QfU?wPm|LQw+Edw%Y0JvBQo4Qlh~>|V#-QOuwRj4ZWA zH1=t#y1%PUE}k4vCzqayeu4}1j!X`gfM3iWxFXFiV?>Ua;v+9UYUvY7ml^Rr5?$3? zCfdP;@<m50i>xUUg~uQ7vN|G*IciZ4r^JtXMb0&%lTD}}ydAdpdnp3NwI?R&0o5b& z;Uun)ojP%duN~~3@ec{+3?vtN)IVkLuI*UIr@w8^9O|)C{Lt^!OWbH>*PfXB-09Vb zaeWwH#*Irn@*0hyYaFca9r4I)-zrLe*h-j;qP67lqszq?^%t&iVNV9Dj!=JOshz(| z_a0ZKe4LzCYLhXeacWu$+st4**lnU>4bmLP`8Y_;GJL(QAl{>EoZEd#ITDB^&PM23 zY`C*H=V%*ph}YDrx<U?91_1r!-$Z;T_NFymOG}E&?eXO4#FgeZz1DlUHSeWn&yGqe z`!o)k8oQLf8eN((x;4$4{Jt_FJh$3s+_!Q5y{?W0rzvq#ANjlC`|KkSMrAi~`YUPj z4Gwu&jOSE<3jeub-IOjZ$Rhw+>30;Gra0Qk!Q!=!+&WVAOB;jD`xb#9P?WYI9qq6L zCWke|X03VCYXLPuh<JJjUzX&NcV6wtvjbW{PIDGB!HkB<##nkvWE;Kp@!2ztz2RF+ z{-tl5RO^3ukJ(91bBJ^Y2)i8v2@cZuW4S5a1)s_pK1&XGV*X?}oSjj1WSPkOf-4N* zR)**@87}B{tb_p5?}ivlBpr6YRoK$HkP4IZONUma*ycj~ClQls&m?^{vOMscmX&i~ zBn5Qralan5T-=`Y_1Dpll^^r@lx-Q~u@*oEH&0Ry&-rXzk4iN!O%E_RylkfL9EF*R zE&s?t+uJ3Q?5n-kFrGEGV2kgm)AcGTcd!b1+XJlIUN&r|v}E0?&@=TQo@H;4G7AaC z9~W-9ITB^1+T|CB11NQUp*Lz>&~64dFd&Q0aJZ??nv%oX!h4r~f>z*Q%`H2TyQTnq z+yB39S$3TZNZP|s$Qd5`bFJhQp7?WR)SbB>1(=XMDbg`MkR@*pcKh_?ogeq#xwdz@ zgw})Z^VJSeEU9)V+Y}Udr_7m5P{HZXL#ST*6w+PX=R%|k53+z3m8oAgvc|k`Kw<_Z zFZtXJ4fJixtD^-kmTNy4iaUdXYcz@d1!QT=5%dX*ER_tyLqk?2@W!V~2~C4Td-#%& z2)pJ3oXxC&r>gim%x!b#>$g{&xLH#@(9WAnZjqxCjUq<1@A}{gn4LY^VQyav8DU!? zRhH$WgKN!Aj8<vrul%Sb<I;71e}t~ja;SE33O5!*12;=v=BZ3enw>tRq!N~?h`BAd zLYba7H%B7OV$*6t$Dr^s{c(Rr`$~UgqRWM_AnC(M<5U?r#+*sRSyKG78G&hirIm+C zo4A;ju-AQ+Gwdj*=$!9M=Jns`F9%xmL(5%9B|{4949%;^{0&KTeRu8SJ_NaDMQkR; z$NQhd+P)OTo!oKtt#Gkye$zkTDB~D_j}Ecow}fyNh<p3{-BYB~H>OxhT3k;_Wl*Ez z0ruV&x6Zz+G#3y=RbQRk%#s)OKJ^owBtZ~dSY0?~Q*x^DCl1>=4nEK?OW5{p=_~o1 z5SeXL%)vW@0QnDfl@tsUXMDwOD*rXe`8NoMe?U9`r<Jgi;@^d^$b78TQ2c2%oZy=o z)K>Uaz8>444es!S3|gnB8mCJ4YsIu=nytR%iE72y?bEVPuk0z5NT2Bo9CR=%-&Sso zn4d98P%LjW76a~Q`S*DbZH8HsNnu#*0uI7wyx<h{aTZ)?n_8FQXob1Wtg2U<iDCkT zGc`~pf4oycgjt7JPd(9B##c(;{Bi4zv%cT`ro{yl*#A-JVYgBmoL}N^Ij(`>nK3fs z9&QP0E|ro}(-)>;2iSUvGvY41)3@ZV4o*Kb4+5ziX=T0-DT~PpVOgZZwLa;-%Hb&D zL&}bE^0{%ac9?E;g^o*GY|a(<KV`eLG`8XwYAK1>z#)Jotb@H6thesV3WL)YoAV$C z0lF6G+vv&i#pXtl0h%E1ghZe^t@gdvAE5mja;7#!(o58Fr_(@gxy9C&thh9#OY7LU z*3yFNS4x-X@@^$S*<=3}OBSa`K~U>CbTtC!dqtYIEh=Z)H(<0~rcg;-xBcc2eusKT zRWn9+jF$7kft2zl$(v-N!F?=QVNQtJTz=!UIcGs(<8&<)N_kMr*^jH7<o;0AW!bEN zvos~iQr}YF;EeJXcsD5BKccXO@tG?n>wj&3`g;fKzw|}@(}n-c{j#h}UdkD44Dc9Z zjMb;#{Nb&0nS1k>6Lw9V>2T~3njC%q41|dV_$GetlN|WA(NaLVjQy7uR=`N2sw}b* ztInLQpZ#Kt2lh{Y_h-%7D)=Sy^nb>J^Ph$Kho#-d*orQW-Z1<0Iu8~R_>#;1pN8V! z57d8q^*<cJ`cG=u48z(jQw3xw+pn}4KJP!w-T(iuE$xR1;7~BJtCFgBCoX8}YqK+7 zTKArk0*erI&~xQG-`Dms@ktj=zsBh)Ay@%cZ6aXP*7(C*&Ek557O9?jamNi7hzrJP zBdt|;@H2$=V{0Jm@rHzd9@1j%f-}n@zqqN3H@QU$IAF}iBRM38rk{6<cPzT#d)+sh z{@NF|sx2eu++!D&r4_W9@HGZOaDtc9Sp~E_)1E<8rgfv;i>urm9!T@O5y(zQ=c_Tz zEC!4|(sOIw$t`y;AkoYSoxWfn>2Us%j@;xF71P$WIB2COo2i{K=u^inE=#LXLT!&r zT$39KgTn!0!FOu1Z$a7+9bRL*Ux5Z1z3j}65$1&DVp_tHQC4AmKl0o)pC{*_c#DhU z#ed>dN_?*{y*!koe5wlLwpi~<nSQd9`^Yu7tP(J6>ZUeZ8=PW)alc%D2_`C&%Y5t> zv8Od1GxAtNuipPa;?42gtr6RqhEYYWJ$HX*WhHN3e*9X6qf(KzL?_eU-V-}Pf#p9Q z=8Xge;tiE)$fDjW|33qk%CwxVU^zQSpGX<Pb**!<HZBT=zd{&%;9my!?4I*U8g|v~ zB(MG7we^X1#HsUsB#U!Mjyfj^F1C}KLT}0DrG;N>uDjFi3AOug8%0qiLQ+>y?z5<& zM~-PMep^s7>9|<~N>_iD{Pg7183PC4WG0Hr6N#wo!j%M!sGufVoWEL38SH^S6lRV_ zNWlw5On6sN*)IjeU(Dn0mOc-_PTm=I+3GJpafVpH%GoA++;Ak+WvQKYdSc|Mh~L{- zXkSQ?OpS+lj7Lw4K=s91%aYz)glb%;-7qQTINo{}yD+tI+_1Q+;78{yUpBk9m(wDY zP1X^FYEZL@;}jX$i`o>)>;jfMSoH>06K<HPswV4{<FiN(dR2^RU9%C#8_S2s{UoC8 zOIYelkX`e@0_os-TeF?Ph7iO~IL4beLzPBmD1qO#q41Tb+w+x_?VmW4sx{#Wox=m9 z!eAmHtbWh-bHNs3zM#jhCzQs+wBo#Y$vy^#0uWmttm1ZN6CfvHY#4^(y&k+{0k6#p zTftHA7X<JBpaPRQ%MivO;TYNG@Y)zs!j*S%*+-6o^vk>7_4W@%W-_SF)qw@TK0`oY z5aUWiQoCu<v6hu_dGiRvGk$np;_>COP^D<=PBE)H8JDLYSqcNsbU`_|Dk`OOZFk!` z<OW9i7P0_dHey!Ri}))?Y5!N!J?md(EB^<VFdC*~Xx}@;+OH#a(GlI}HdR&>)o9Hm zA_kA^&~Vq4+JypX9Sy=4@79k9dTz=LI1eCIFU$EHTjihL*lisDT3hdS>Tb@&z$|uj z$?Y+=YwjUlM4*g^RdO_@lV^EBzB+qo;Q6#^v$rZ(cyg|Mxp*JAYbh{%DOv>$dUfZ} z%R@=MBmWX!rFFskrA~3$5~Jafj178r7i$@6u^J7LU1E+jN;8f`QZv;wc2cJeW83)_ z-^+33j<Bt1np|t(5~vD?rzCkLZ-}-EOW+wJQ7IdOf^_Q#EqCWiFiNCei$sz-VY~1} z7SSV8K&m5KRcfQrP2rj=;B&Vi@fTG_eH|c=6ARZrlf!=-c%SHhktY6sIPh4W=c+~n zJq=;{k5b~1GwYGZR>~eTSB#y-?-m~%v_I*mc67b!Q-7y!b(OSDXz$H^SmkeU8CZTO zX4K$%INC3zGWPNIUfO2)d(S6m)jbz?VRv7%Ak5J6vkKVrtI-E57TS$ohO=N-&T&@4 zp&S`Et;!iHDa^=+g%^%OzW0UJlkwc?AlCp*pN&HpzU(=743u6NL=;b7NAjd7;eDdv z4cvXvZsFoU;xH-uOui~`CKv+_&-7=2$Qc^)1UoQA3*2W{eWX_k`k>}$>0}P<sO|G? zQ5U(+rBE|OAO=xV5|3<FHL3KI@)*)|M)We5lvumc&2P)<`KGllCiyp8L*Au-KWvhD zX7`?sw~%Ip_w0s=s!Zc!-72;*BnL?HC};#cDdYO8hXcINbBNp@?lqFK-aQ0H518um z_{h_)c1WTbwPx`5rBn3{28NbJ5euSJlEqa){iLixUSDq*Cg@Uqia66O;)A<7Hl>gF zWEO^szyYVe&13z8t5X3F%|K8oGfMsLiG>YotlTM%I3GkX9#OF|{nu3dtNvx=Mzi~8 z-Kz}qXDNY`r{OOrh%-z3RTZ}Wg;V_IzYXI4{RR8Tju6W3`QOLuvrY3@q7wwgZ0(J; z()b~kxMiG;hKyXFygmMi(PA4`v5&@(0@5G0ypo$p;1d!{d}SHTrg7!b$0S2vI*}yl z=VWSRPOA@C8f5jV(#XcOon~j_pYch1_2QaMU~|bh_|a9G=>)j(AI%VkqGHbR+{gj_ z*L)2!G5tI|_diTF7~?FTJOsWVx4-YQI6dc7nI5fQD=$(dy>-SYtP;>XFlN;E3<g>| zgdv?^q(cs@9utcagO_WLAINV{%@ZkPPF)2|>qk5~5OqiA<B*bYG<d$-94rxxd{wDM z%Fhbr;aVR3=2b!3=6Xww3DYjAYWe6VPGL2#ht$a>`p|^yf)Q2)qr7w`(48H*lgv2O zEFy|!iaIp&ZHuDgLdKdA996Ub;BF0`W#^uBn0`l8d}=uwp0z}%U+lF;XiQP3UEm*e zk(8tP0<?I|bCrwt;6ZOBb_Y4ty0Q6^VnAz;M=7n~z?_ZTOC-DFiCFG~eP8ftC~UBC z*3wvtrCuwM)&+~w9|n)M89&N_S9+~nh0a}wc;O=9LRY2yYc_rv?ULqqV!O+;$8=}^ z-1%#benseIuH`mt$umj=U_O2tz37X-ya8;IO|sN?J;dH)+;2K*qG-a{UH0y;sOx&V zu2=-g@E?xhKhPfhr(X$F0O%SwzQA8crp<gEe!+&)^e;Xip?$TNnSk)ns-Z6sik~?7 zx*nZVKJPuslA6E`^Q<HRN;5u=_YGdtzIUrF1@)fQA4_&8K;32lEM5|~6pn(<$^x?6 zQl^ZGyxn~qhOIAnIao>w%U$P<<=pZh4K2@IAO!A%R}7;>tU`tsdz|Rf2xrKmi<K$S zwUtu>Ey0<>hyFt5{$i=EXYpVbQ#QP>@{(@wK}Id4SQ!Hq0AV;*ZD8n5EHjhpH;zZ^ zfcxI*PV*6q>u6NtFrOiL`iN$p8e412wx*0Ii;(?@J)u**Cl2v3psBJ@&_MoHjmY-C zb(OKo*=YR7{tJ{FqHoITd>i%a&EWm^%FI%={fr<UZU=`7^8ms)88-LdDU?#pHHfy} zst?I)$7qg6QaRv&)UEjMARGB5X}*U$li#%TZ}0$L+*oe6lf@|^m&xrGe`tb@6{4_7 z6lUhs|2z!;|6KV<R)({M2FkWy$D0G>)H@LFz^A;YLJ|v8Xqlr<Cwf(xYeA$Hv)Zkb zjfNB53vP3?*oQY}Aw^lUDzgvn+K^Iz?&Z19bCC0e<h0I3P0g&Sv?{!4Cm7h;=s<X< zjXp^HNGG_X1%&pPHo^$ZO&TvdUjiSK2-ef!V7jny@IhFW1I;AOQ=2kyErk0mBjb<> z{k(WMVYp5Ph^9DUa%xsK*?XyvM@e2=_kO(8K)v;`Uc;9g@cQobY+Hh4JI4?7UeY=0 z41r%a$&y^p+)fQDTQZ>F7Ra~3UDw062MvV4s8`QwMZQgMU&7Tzt4dQ_>y2r5R|!&! zpU;X2U1#uq;}yhk2OK_S;Ln~~G$UR0w4_y=r_36h2`(fg&gsgMck{m<=(kmkMb>c^ zxOvsoAh3y7GXYw{$7g&HXD~N+b#iI&)@fB0Hgobkw`0@}ToWPnX>Y?dK8uCK_a;N( zOBA${OGY%$t;KW<3d$b0<o{d&vmDcWAg$`XKHcQ8QVD+}8DM%eX6V^ZZ+FcnwunkK zWIDqiYcpGSe$Qa%=|J|Bna1VD-@e#%$%!UMxo0y$$kV9pZ;Q~=5(Cld?$aM`<3akI z|1a#LPWmotg!q?<+*UGrj06SQWJaiLxxT%Wu$KUM6AsEro<5}ZuLX^>ImFJ)GDG8D z%`EO0y%A`T-on{zl6kd8T6~1<-q+301bDRdE*zyAT*TXqn2Xpf?CJ`>)fA2k+>S{d zs+<*?SYZ!CE)lE;7l)5oW9|~$v7p6yCY+DSGhhyeOBkE-@E%0yL(==UH4Ovm3=*Xv z2>TNP$#-3K7k0Nw&4);{$|{mez&8(*)mKS60=n<d-EhWzp;qNS!0zp12dc<3*Y#;y zJ?2dv!gn9j8UTvFT@WJ(&B`d9s#(tiuvT4$Nh;bR^q1i88faY{4wSmy;|6G}a;v6x z!4^=r&>{2>P&{s4mSz(xM89Gr{$%WWyI<74H^@@)%T=2$KGA%LeJ>ur7u!;`7e|n% z*vU+M`JQOxbDhk#Mw#nhFHFbi;K!Zj;Qr8S8k`}IUU^v6<l^XKp(*wXik$y`BmUP{ z33-7%wS-TG7D+KgcbP`8A2%0CKj+&j&`4hS<B!W8LnvA0QgR=O@P|A=$zgx~^#S;o zr(S!nA#&I36lgDg?H$f~pV%{gS>A7Fbn4G1B<QQ|K70Pl-neP!*Mr`RIkuIjQnX^z zC~bc}3iJ3nUE;4ihClg<Mr`c=DK*AFM52FWQ|ik@@2O+VkIWH&e}|E_f89wMiR($* zYL@Vd!SJo^Uu)#=%!*L&ghvMNx69B6S_gGyVQN?$5bU+uXP6tUi@?|IVQ3<thtcO$ zI6gW)JIFf<pNt1WYK(xte@v83D^?IlBWsz>g^retDyLLaYvKOlFrzb0qQQS;AJ;W1 zJ5lFWx06_YcUH0S-74gs&3h518SLpego-+`EKEgI*WIm-rc*QBA8&Bh0-lo|I(fml zRJ^k4R#psZDSjgb|2FfW^YREYu8hDo+B#S4<-wkb)Y>}s@6mQnsk(Znw2o1lus_oT zu*W#nY%(<$mm+TrUgizHj*6BV4ub8JfFJMWEu!fnk%z>IH3WWU`YW7VmO30|t|S7^ z8BCX1mg7&B@owK(3uLUvwle%;cn!II$GWm2A|lxg){Ljqiz;o4M1m1%iEc><Y`bN& zS?~l<vAbun{yWsEd2*3ikSme!K~Yj}B{|Kcop3MPPJBFCO#M=L$qk(C-94aasp(Y+ z#?~!)?<SN;Ul+rsUb=Zj5Q0XJ7xR{Qy_wwmcNkWlTr*`&N>bWm3o+@But3w}5Z;3p zF(moBLM~$jVnlnGz<5DalB?kA8bVIQ9#+I=rwjzOzM57Y9Y$^1ER4y{)=Yy_@hrS( z0B!rwY`+U8<}N<_+XixYwFE;tDsfFR&GcS%=Z=LmXL-#0w|!nm{p$Aqr_9}7HYAO; zp{&2zP#|;0toU-IOvP`z4oRJgLIm1+jxHW^JLBR13B=K*FWxs5d7f+2h^R%mJ{{HV zU2m1)mvt+5{S#f*_97LNl`xVnrl+F=DtF?19F@H@=nOnP%T>?U1)}{OT=(b~r!@?% z*ludt+1Ek#XA+y1SplBug^fLRIg9J>**wP;)%8Z>_BN7Yo)cYLtC`WkEV*hRfU(>K zU97q)z_)}Z(qXUZjazQI=Rh$p2MN%J-gX_%^uAkGwA&+Bm}BPlYHBE%qKBIzKw{uk z&uh`Kij=IG0CV5%u(Sb+dby>S(iW3Fl9Ibmm2_u&EEP!%c_UODtV0;5-^kGr^6YT& z@CSTarRX5gn;b-?$UGj&$yv&}@HQ7*<Y8@?nppPCxCJqD2xkzNQqb%_5b?eiaNeFz z>F$Pc6b<zLk-gNS+HB?4|KR+Y;g4}e!RUrVd2Oa|sxH*?VG5PrnAdpY#L=HUk_Nm; zD&lj4KQDdba1wGK()xUJFIidQdoeIYZE=g~lF5dyE1|FHG6MeHQ#;!>EOg;VPDrI} z=AhS@akA?V-J=%)9Mi$lwOnh|l~vC_GQPMIiN<DwhIIGbFXl3H#B$p$_{P0F%!WqZ zZjf!_W=Gvo6g7hQCA3VRO^kCTT}<BHtk<R@O<3_y8=G$QnYp6F?8!FPlqwpE_F8^# zw`te6fi;aTQn_yTSLM}<j)yLtZJCRqU1S7k_#qv$5=t9qsyg4BicW3pVXc!>leZc* z81BWA2wwJ~%Yuwuq%`#dtRWGTBqIEDn*O1$Bzp!4J|0@A`F|Of`Lf>|xbfh<Wdi@$ z2PMa@18J;K5&|AO&if>NUE_xfEX%biz+|t><O1G6gg}nS8!ZnAZxb?1JZQ%AzwM=* z`cQnUMZGvB4FaQUm-dU^RI=LXixG1aTyPEFrAA&>d%#Mfo!8S%AfF!$CG>ha1He?( z2%m@MB~97A8vzpjN~8K@vL($kkMz3aya3`n{`l&wEWiaifDqXO!bhJgFM~l9`|wo5 zt6NPhlua05{@eb~TR@+Uk8PW{`^HpDPQ`H1WW8Pt*s1p!@8N6Voa_r;3L_%TXRIO@ zMwp<ym$qWE($QobGt!_Vt2UD_U%7loUNl)8c`t2yf6cyB#!I${=OZD(lQ|%Muy(wn zb>URsed(fJG}R&W+59Ge6fc5>?qlx@*ix>x88^(fysS}wh?hxBl793M_vN^j)b(X- zpy+>hMu<|()Vq;gz%rpn)*}iF*5eZ~4DS9+qM(^BuBfGMA<nQ!^Of8pZT`m5+1{k# z!$*9+_8`r=2Bg)&8v3P7L9-a-2IfA1*(qHIcZIHfVOgjJB4uZW>>(miHyw>rIgAWk zW32oRV@e}k)h(;?ci1F)#V&P5p1nOZYG*QkkWLL0%z<X30jH%GuhMW9_x4~V#T254 zVCC+^49Q|J>PXL_$3<MbMQK%gF>gz|W(iGADD&K~QHD)Mz0hAlBPH{NB#m!K49Oy+ z<sLl~nwvPu7>g9+rU_~S96kG`_l}lW;CJ?YdUn=0)rzW`)oK;%SpJf(h~Ld^`jpMa z%|4m-@`iT|$>$*^biU=PTcMw?;CT|I);jCUcQ!}VD*@iih@$DaAlC%@^kGMXY(~F+ zvDzE`KWf4+I3zwlCxyB98qAuAh8Y?@Gw6mo=SSb;+F2M~Y6#?ga^)lCP|{`~3y3TM zrK_<#6yx0Fa0HW{oqhbY)Wq`{G0}1>R+HCaR+ZhsEnXUmm@*e_aJEl3<D}oS-LXOo z01tMZQEgtRPR^e=3RsG0x2aV)xv1cb_QLtaJvn;Mu&!CXPR0Y7B`qf=YvLfS04_RB zUO(x41N}Zr8Kb`DOp1Fn^gUybim;rArR9~BFe%A$F8KLJ{qYu+tott5qRk#Qq0T=; z32=w^&0(myP_?v{bYfZ(YnzD_sos*onD8d^&5Ts162A+%?iDnFSO9@NfO>K4(V31} z5W;ALJrf??QNC0A+}0%L!Om+Ui^s%kHx}tWK@l&?8+7Ij<aCYI$)`G1LCWv$egH?j zIJXd+eKN=tRqxfn?daY+(G`p8ywf^tpiE||dU3AH3~MoT9_HS|xN;On)k(qYXO*}* zkrIJ`1_%xdUWHb^n<4UU+W>Fd(yrBIdo{23wO8>;3Fsoea=+PhVD}K*ulB~x9msAp z&3g{q@%P9g4R5J?H|>IIQOV0514iWDaT?xnI+%un+6i;M)A=D@KlbwRuX($)b2*EC zStIWP#i@xZ_N#=K_>4mm6!mujq~aP*u;#|T2Y{8Gc*45q<1!w*`o4sdu6uBUsMM{u z5L;>+0Cq!N{Ngjp`bOoeyI`2G*@9|3!%K;4t%c><iC8^qa?!dW_z~rmfx5iB3~<%H zrk7@g#e<m?&+P)5!SlQnMt-^LjO1qEvls<YPLGaC9x>${f75;G^JE1w>Rt|Z&{o7t z$k|Ype@nU)L&&hKBu=r?fKv7@KEPIZFic}X&9paAXlxVp-Z0ZXw<vjI%f58|M@`<U zqNc$`>*%(#DFs5G#dq`@%U^?M;~K3zA9rlJhRzaji_5a;0ih~%4PY%5^JM2N-uj4i zbxWRn(xsgb@{X>!FLHg7z&EU4%9Fd>yL$}UnF+@GhdC+jtrXt!)(n|WyN=d3myu^n z`v9>$Iht;9k{SC^iQh{h>ZrYRfzx6r+fh&=)^Sa6C#U{<h7!x#4MJ(*RuE!pZ(oMw zF%*o_#LMsN`VdceJ;39%>f?gDu7zedPZ+>u*~gKX&U8=m>Z`$yP`rm8@fCqNPxZqV zBPlc2o_sJ?snV83z{bWC_stQ9M5ifJ`HZp47YNdpS!5pf_ENKCyesY{(DH@@sd@HY z-<0f%W`qt<cwOYpnV#xca~2CJaBgn$bnBUIa+fn?W9e1;mw?#_dJf*ha9mst)Kr0F z@NVX6Mu@#;9O7EqaV3Aw5pX8_+ad=WbiBrsS4Xc)y>VfpLDqjq`Jl1I8!(=IURn09 z5(c9Cze1U*q7%i3Fl;FEJ1tSHx(0e`@GFw3Cf5AiDzmN0X>J_N@ePFyJ>UEd*HPd) z>|hdPSpT%>l^A4O%gO$&BVPuKqpTOLOqA7M;E#nFq)VisDLVqi!XK@*SmoSP7`V0r zjSy~j!SE^-^clg>_lWl@Y|APN@(m41@LALR^%^*BZG53xIJhs6ZP7$-R7>B!M3_0q z;Z3<uPm5T4tX%~K2L^?L<n^(=X6gYRajo;yb6t!dre+jWl2w$w=C|ym$+UA+MB#S0 zW;i4`I8<1Xtm@i^GFTX<#U}>%p}&;kg9jLg+6mW-9%t90fV;#Yutkur<~lPc{_KHU zyf3R$h=qVzpxx+HD>L-oy?bqM<2Ji2!K?D67ls|1y7y47ckk|=rFT7`CLJ&)D-U6@ zVx(0%IouqUs1}EnfNGP~oR<*v@CTh$B5<S1w!_`8lEu+3?6&H!j(f6hdXeyXs=w3v ziNisEY>E*vZZ&qa4ckRc2RuC-Yc_Cpa2aTQ<lm@DX!Y3{UJ)(U0vVTIsLps1>f6v5 zr0mvy>njj~GWIMjEq2#Pt9|>@uWiADKy{4l$zD-?4ryX-L&VCw%oxCT{K3dh&<2_j zQC<FGg#j?Ts7><|XFKh<J~(jNq+nF|tL=mCDtJpt`A(m5Xv_SZp%{3asJt9=AGgHc z#x>i_JDb4&v(X(GhUA_06<>SW;!e!l1L(YyWM`|VU5x8Y!9huMmd^a8+m#9o<&*g# zR;=)?moNAxj+<R658VsG`#*7#A8iDqP>B7mS7#Tq_OKcEk{NPfp)w#>2WTlPNJl#6 z#V{3`|E$rnq@s0x3ByH)TTVn|wFrzvuIE(Y+QUP(ghoPZWF?@N1A3=dI4af)OpfLL z4Y@yY=⁡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 MYGfKQ_RZA40b&K<;{X5v literal 0 HcmV?d00001 From 38d5c9adad1cc6fb2af5aeb4e5b18977ba9f688e Mon Sep 17 00:00:00 2001 From: brian16600 <brian974902@gmail.com> Date: Thu, 17 Feb 2022 21:14:17 +0800 Subject: [PATCH 42/47] Add Ui.png --- src/main/resources/images/bear.jpg | Bin 0 -> 48635 bytes src/main/resources/images/human.jpg | Bin 0 -> 5371 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/main/resources/images/bear.jpg create mode 100644 src/main/resources/images/human.jpg diff --git a/src/main/resources/images/bear.jpg b/src/main/resources/images/bear.jpg new file mode 100644 index 0000000000000000000000000000000000000000..306f93845486355894550b30f2deb051edc57b32 GIT binary patch literal 48635 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 ROR>iPZ~iZD;KHx*{|BZiAz1(b literal 0 HcmV?d00001 diff --git a/src/main/resources/images/human.jpg b/src/main/resources/images/human.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a7c2e69ebf0e1ed46d294e10bcebb1326de5264b GIT binary patch literal 5371 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 C5V`vR literal 0 HcmV?d00001 From 56d537e2afa3f5ea1891ee5aa5d6f7b12a8801d5 Mon Sep 17 00:00:00 2001 From: brian16600 <83962069+brian16600@users.noreply.github.com> Date: Thu, 17 Feb 2022 22:02:15 +0800 Subject: [PATCH 43/47] Update README.md --- docs/README.md | 111 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 100 insertions(+), 11 deletions(-) diff --git a/docs/README.md b/docs/README.md index 8077118ebe..32dbfdb688 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,29 +1,118 @@ # User Guide +Duke is a interactive chatbot with UI. +> “Software and cathedrals are much the same; first we build them, then we pray.” + +Duke is a task tracker that can track three categories of tasks: +- Todo +- Deadline +- Event ## Features +1)`todo` +2)`deadline` +3)`event` +4)`list` +5)`delete` +6)`mark` +7)`unmark` +8)`snooze` +9)`bye` -### Feature-ABC +## Usage -Description of the feature. +### `todo NAME` - creates a Todo task with name NAME -### Feature-XYZ +Example of usage: -Description of the feature. +`todo work` -## Usage +Expected outcome: -### `Keyword` - Describe action +Expected outcome: +Creates a new Todo object called work +*Take note of the space between todo and NAME! -Describe the action and its outcome. +### `deadline NAME /by DATE TIME(optional)` - creates a Deadline task with name NAME at DATE and TIME -Example of usage: +Example of usage: +``` +deadline report submission /by 2022-02-14 23:59 +deadline report submission /by 2022-02-14 +``` + +Expected outcome: +Creates a new Deadline object with DATE and TIME(optional) +*Format of date: yyyy-mm-dd +*Format of time: hh:mm -`keyword (optional arguments)` +### `event NAME /at DATE TIME(optional)` - creates a Event task with name NAME at DATE and TIME + +Example of usage: +``` +event meeting /at 2022-02-12 01:00 +event meeting /at 2022-02-12 +``` Expected outcome: +Creates a new Event object with DATE and TIME(optional) +*Format of date: yyyy-mm-dd +*Format of time: hh:mm -Description of the outcome. +### `list` - lists out all tasks +Example of usage: ``` -expected output +list ``` + +Expected outcome: +A list of all tasks in their respective formats + +### `delete INDEX` - deletes task at specific INDEX + +Example of usage: +``` +delete 1 +``` + +Expected outcome: +Delete task from tasklist and prints the deleted task +*Recommended to use alongside `list` command so that you get the right index +*Important to delete a valid index - Do not go out of bounds! + +### `mark INDEX` - marks task as done + +Example of usage: +``` +mark 0 +``` + +Expected outcome: +Marks task and prints the marked task +*Recommended to use alongside `list` command so that you get the right index + +### `unmark INDEX` - marks task as undone + +Example of usage: +``` +unmark 0 +``` + +Expected outcome: +Marks task and prints the marked task +*Recommended to use alongisde `list` command so that you get the right index + +### `snooze NAME DATE /t NEWDATE NEWTIME` - changes the date & time of task + +Example of usage: +``` +snooze meeting 2022-02-12 /t 2022-03-20 23:59 +``` + +Expected outcome: +Sets new task date and time and prints new date and time for task + +__Keeping track of the exact time is difficult__, which is why all you need to input is the date of the task and not the time. +*Getting the exact name and date of the task is important + + From 18b799556f0aea6e060da633afe158052e9bb89a Mon Sep 17 00:00:00 2001 From: brian16600 <83962069+brian16600@users.noreply.github.com> Date: Thu, 17 Feb 2022 22:02:42 +0800 Subject: [PATCH 44/47] Update README.md --- docs/README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/README.md b/docs/README.md index 32dbfdb688..95d5052302 100644 --- a/docs/README.md +++ b/docs/README.md @@ -8,15 +8,15 @@ Duke is a task tracker that can track three categories of tasks: - Event ## Features -1)`todo` -2)`deadline` -3)`event` -4)`list` -5)`delete` -6)`mark` -7)`unmark` -8)`snooze` -9)`bye` +1) `todo` +2) `deadline` +3) `event` +4) `list` +5) `delete` +6) `mark` +7) `unmark` +8) `snooze` +9) `bye` ## Usage From ca00570c7cbc3fb551fa6a35da58f263b158a86c Mon Sep 17 00:00:00 2001 From: brian16600 <83962069+brian16600@users.noreply.github.com> Date: Thu, 17 Feb 2022 22:05:25 +0800 Subject: [PATCH 45/47] Update README.md --- docs/README.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index 95d5052302..32635621ce 100644 --- a/docs/README.md +++ b/docs/README.md @@ -32,6 +32,7 @@ Expected outcome: Creates a new Todo object called work *Take note of the space between todo and NAME! + ### `deadline NAME /by DATE TIME(optional)` - creates a Deadline task with name NAME at DATE and TIME Example of usage: @@ -45,6 +46,7 @@ Creates a new Deadline object with DATE and TIME(optional) *Format of date: yyyy-mm-dd *Format of time: hh:mm + ### `event NAME /at DATE TIME(optional)` - creates a Event task with name NAME at DATE and TIME Example of usage: @@ -58,6 +60,7 @@ Creates a new Event object with DATE and TIME(optional) *Format of date: yyyy-mm-dd *Format of time: hh:mm + ### `list` - lists out all tasks Example of usage: @@ -68,6 +71,7 @@ list Expected outcome: A list of all tasks in their respective formats + ### `delete INDEX` - deletes task at specific INDEX Example of usage: @@ -80,6 +84,7 @@ Delete task from tasklist and prints the deleted task *Recommended to use alongside `list` command so that you get the right index *Important to delete a valid index - Do not go out of bounds! + ### `mark INDEX` - marks task as done Example of usage: @@ -91,6 +96,7 @@ Expected outcome: Marks task and prints the marked task *Recommended to use alongside `list` command so that you get the right index + ### `unmark INDEX` - marks task as undone Example of usage: @@ -102,6 +108,7 @@ Expected outcome: Marks task and prints the marked task *Recommended to use alongisde `list` command so that you get the right index + ### `snooze NAME DATE /t NEWDATE NEWTIME` - changes the date & time of task Example of usage: @@ -112,7 +119,17 @@ snooze meeting 2022-02-12 /t 2022-03-20 23:59 Expected outcome: Sets new task date and time and prints new date and time for task -__Keeping track of the exact time is difficult__, which is why all you need to input is the date of the task and not the time. +_ _Keeping track of the exact time is difficult_ _, which is why all you need to input is the date of the task and not the time. *Getting the exact name and date of the task is important +### `bye` - ends the program + +Example of usage: +``` +bye +``` + +Expected outcome: +Duke sends a goodbye message and exits the program, closing the UI + From 446075637fb4ce3512921a2fcd1383bf08cebfd4 Mon Sep 17 00:00:00 2001 From: brian16600 <83962069+brian16600@users.noreply.github.com> Date: Thu, 17 Feb 2022 22:07:12 +0800 Subject: [PATCH 46/47] Update README.md --- docs/README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/README.md b/docs/README.md index 32635621ce..6f5ba5e335 100644 --- a/docs/README.md +++ b/docs/README.md @@ -20,7 +20,7 @@ Duke is a task tracker that can track three categories of tasks: ## Usage -### `todo NAME` - creates a Todo task with name NAME +## `todo NAME` - creates a Todo task with name NAME Example of usage: @@ -33,7 +33,7 @@ Creates a new Todo object called work *Take note of the space between todo and NAME! -### `deadline NAME /by DATE TIME(optional)` - creates a Deadline task with name NAME at DATE and TIME +## `deadline NAME /by DATE TIME(optional)` - creates a Deadline task with name NAME at DATE and TIME Example of usage: ``` @@ -47,7 +47,7 @@ Creates a new Deadline object with DATE and TIME(optional) *Format of time: hh:mm -### `event NAME /at DATE TIME(optional)` - creates a Event task with name NAME at DATE and TIME +## `event NAME /at DATE TIME(optional)` - creates a Event task with name NAME at DATE and TIME Example of usage: ``` @@ -61,7 +61,7 @@ Creates a new Event object with DATE and TIME(optional) *Format of time: hh:mm -### `list` - lists out all tasks +## `list` - lists out all tasks Example of usage: ``` @@ -85,7 +85,7 @@ Delete task from tasklist and prints the deleted task *Important to delete a valid index - Do not go out of bounds! -### `mark INDEX` - marks task as done +## `mark INDEX` - marks task as done Example of usage: ``` @@ -97,7 +97,7 @@ Marks task and prints the marked task *Recommended to use alongside `list` command so that you get the right index -### `unmark INDEX` - marks task as undone +## `unmark INDEX` - marks task as undone Example of usage: ``` @@ -109,7 +109,7 @@ Marks task and prints the marked task *Recommended to use alongisde `list` command so that you get the right index -### `snooze NAME DATE /t NEWDATE NEWTIME` - changes the date & time of task +## `snooze NAME DATE /t NEWDATE NEWTIME` - changes the date & time of task Example of usage: ``` @@ -123,7 +123,7 @@ _ _Keeping track of the exact time is difficult_ _, which is why all you need to *Getting the exact name and date of the task is important -### `bye` - ends the program +## `bye` - ends the program Example of usage: ``` From a58d8da0b3c377d0982e734c8b01004023f4bf32 Mon Sep 17 00:00:00 2001 From: brian16600 <83962069+brian16600@users.noreply.github.com> Date: Thu, 17 Feb 2022 22:26:42 +0800 Subject: [PATCH 47/47] Update README.md --- docs/README.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/README.md b/docs/README.md index 6f5ba5e335..61cb7553bd 100644 --- a/docs/README.md +++ b/docs/README.md @@ -8,15 +8,15 @@ Duke is a task tracker that can track three categories of tasks: - Event ## Features -1) `todo` -2) `deadline` -3) `event` -4) `list` -5) `delete` -6) `mark` -7) `unmark` -8) `snooze` -9) `bye` +1. `todo` +2. `deadline` +3. `event` +4. `list` +5. `delete` +6. `mark` +7. `unmark` +8. `snooze` +9. `bye` ## Usage @@ -72,7 +72,7 @@ Expected outcome: A list of all tasks in their respective formats -### `delete INDEX` - deletes task at specific INDEX +## `delete INDEX` - deletes task at specific INDEX Example of usage: ```