diff --git a/README.md b/README.md
index 8715d4d9..63d0ad6b 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# Duke project template
+# Duke.Duke project template
This is a project template for a greenfield Java project. It's named after the Java mascot _Duke_. Given below are instructions on how to use it.
@@ -13,7 +13,7 @@ Prerequisites: JDK 11, update Intellij to the most recent version.
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).
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:
+3. After that, locate the `src/main/java/Duke.Duke.java` file, right-click it, and choose `Run Duke.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
____ _
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 00000000..73b1f710
--- /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 = "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 00000000..f3d88b1c
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 00000000..b7c8c5db
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.2-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
new file mode 100755
index 00000000..2fe81a7d
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,183 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=`expr $i + 1`
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 00000000..62bd9b9c
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,103 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java
deleted file mode 100644
index 5d313334..00000000
--- a/src/main/java/Duke.java
+++ /dev/null
@@ -1,10 +0,0 @@
-public class Duke {
- public static void main(String[] args) {
- String logo = " ____ _ \n"
- + "| _ \\ _ _| | _____ \n"
- + "| | | | | | | |/ / _ \\\n"
- + "| |_| | |_| | < __/\n"
- + "|____/ \\__,_|_|\\_\\___|\n";
- System.out.println("Hello from\n" + logo);
- }
-}
diff --git a/src/main/java/Duke/Checker/FileLineChecker.java b/src/main/java/Duke/Checker/FileLineChecker.java
new file mode 100644
index 00000000..d887cf04
--- /dev/null
+++ b/src/main/java/Duke/Checker/FileLineChecker.java
@@ -0,0 +1,82 @@
+package Duke.Checker;
+
+/**
+ * FileLine Checker contains methods to check whether
+ * the lines from the files
+ * contains valid task information.
+ */
+public class FileLineChecker {
+
+ /**
+ * Takes a string array and checks whether
+ * it contains parts with valid information of a "to do" object.
+ * @param parts
+ * @return boolean
+ */
+ public static boolean isValidTodoLine(String[] parts) {
+ assert(parts[0].trim().equals("T"));
+ if (parts.length != 4) {
+ return false;
+ } else if (parts[2].trim().equals("")) {
+ return false;
+ } else if (!parts[1].trim().equals("1") && !parts[1].trim().equals("0")) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Takes a string array and checks whether
+ * it contains parts with valid information of a "deadline" object.
+ * @param parts
+ * @return boolean
+ */
+ public static boolean isValidDeadlineLine(String[] parts) {
+ assert(parts[0].trim().equals("D"));
+ if (parts.length != 5) {
+ return false;
+ } else if (parts[2].trim().equals("") || parts[3].trim().equals("")) {
+ return false;
+ } else if (!parts[1].trim().equals("1") && !parts[1].trim().equals("0")) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Takes a string array and checks whether
+ * it contains parts with valid information of an "event" object.
+ * @param parts
+ * @return boolean
+ */
+ public static boolean isValidEventLine(String[] parts) {
+ assert(parts[0].trim().equals("E"));
+ if (parts.length != 5) {
+ return false;
+ } else if (parts[2].trim().equals("") || parts[3].trim().equals("")) {
+ return false;
+ } else if (!parts[1].trim().equals("1") && !parts[1].trim().equals("0")) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Takes a string array and checks whether
+ * it contains parts with valid information of a "dowithin" object.
+ * @param parts
+ * @return
+ */
+ public static boolean isValidDoWithinLine(String[] parts) {
+ assert(parts[0].trim().equals("W"));
+ if (parts.length != 6) {
+ return false;
+ } else if (parts[2].trim().equals("") || parts[3].trim().equals("")
+ || parts[4].trim().equals("")) {
+ return false;
+ } else if (!parts[1].trim().equals("1") && !parts[1].trim().equals("0")) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/src/main/java/Duke/Checker/InputChecker.java b/src/main/java/Duke/Checker/InputChecker.java
new file mode 100644
index 00000000..2bf08022
--- /dev/null
+++ b/src/main/java/Duke/Checker/InputChecker.java
@@ -0,0 +1,175 @@
+package Duke.Checker;
+import Duke.DukeLogic.DukeException;
+
+/**
+ * Checks whether the input by the Duke user
+ * contains valid task information.
+ */
+public class InputChecker {
+
+ /**
+ * Check whether input contains all the parts of a "to do" object.
+ * Returns true if it contains all the respective parts.
+ * @param input
+ * @return boolean
+ * @throws DukeException
+ */
+ public static boolean isValidTodoInput(String input) throws DukeException {
+ assert(input.startsWith("todo "));
+ if (input.length() < 5) {
+ throw new DukeException("☹ OOPS!!! The description of a todo cannot be empty.");
+ } else if (input.substring(4).trim().equals("")) {
+ throw new DukeException("☹ OOPS!!! The description of a todo cannot be empty.");
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * Checks whether input contains all that parts of a "deadline" object.
+ * Returns true if it contains all the respective parts.
+ * @param input
+ * @return boolean
+ * @throws DukeException
+ */
+ public static boolean isValidDeadlineInput(String input) throws DukeException {
+ assert(input.startsWith("deadline "));
+ if (input.contains("/by")) {
+ String[] parts = input.substring(8).split("/by");
+ if (parts.length != 2) {
+ throw new DukeException("☹ OOPS!!! Invalid syntax for adding deadline.");
+ } else if (parts[0].trim().equals("")) {
+ throw new DukeException("☹ OOPS!!! The task description of a deadline cannot be empty.");
+ } else if (parts[1].trim().equals("")) {
+ throw new DukeException("☹ OOPS!!! The due date/time of a deadline cannot be empty.");
+ } else {
+ return true;
+ }
+ } else {
+ throw new DukeException("☹ OOPS!!! Invalid syntax for adding deadline.");
+ }
+ }
+
+ /**
+ * Checks whether input contains all that parts of an "event" object.
+ * Returns true if it contains all the respective parts.
+ * @param input
+ * @return boolean
+ * @throws DukeException
+ */
+ public static boolean isValidEventInput(String input) throws DukeException {
+ assert(input.startsWith("event "));
+ if (input.contains("/at")) {
+ String[] parts = input.substring(5).split("/at");
+ if (parts.length != 2) {
+ throw new DukeException("☹ OOPS!!! Invalid syntax for adding event.");
+ } else if (parts[0].trim().equals("")) {
+ throw new DukeException("☹ OOPS!!! The task description of an event cannot be empty.");
+ } else if (parts[1].trim().equals("")) {
+ throw new DukeException("☹ OOPS!!! The date/time of an event cannot be empty.");
+ } else {
+ return true;
+ }
+ } else {
+ throw new DukeException("☹ OOPS!!! Invalid syntax for adding event.");
+ }
+ }
+
+ /**
+ * Checks whether input contains all that parts of a "dowithin" object.
+ * Returns true if it contains all the respective parts.
+ * @param input
+ * @return boolean
+ * @throws DukeException
+ */
+ public static boolean isValidDoWithinInput(String input) throws DukeException {
+ assert(input.startsWith("dowithin "));
+ if (input.contains("/start")) {
+ String[] parts = input.substring(8).split("/start");
+ String[] startEnd;
+ if (parts[1].contains("/end")) {
+ startEnd = parts[1].split("/end");
+ } else {
+ throw new DukeException("☹ OOPS!!! Invalid syntax for adding dowithin.");
+ }
+ if (parts.length != 2) {
+ throw new DukeException("☹ OOPS!!! Invalid syntax for adding dowithin.");
+ } else if (parts[0].trim().equals("")) {
+ throw new DukeException("☹ OOPS!!! The task description of " +
+ "an dowithin cannot be empty.");
+ }
+ if (startEnd.length != 2) {
+ throw new DukeException("☹ OOPS!!! Invalid syntax for adding dowithin.");
+ } else if (startEnd[0].trim().equals("")) {
+ throw new DukeException("☹ OOPS!!! The start date/time " +
+ "of a dowithin cannot be empty.");
+ } else if (startEnd[1].trim().equals("")) {
+ throw new DukeException("☹ OOPS!!! The end date/time " +
+ "of a dowithin cannot be empty.");
+ }
+ return true;
+ } else {
+ throw new DukeException("☹ OOPS!!! Invalid syntax for adding dowithin.");
+ }
+ }
+
+ /**
+ * Checks whether input contains the enough and correct information
+ * to mark a task as done.
+ * @param input
+ * @return boolean
+ * @throws DukeException
+ */
+ public static boolean isValidDoneInput(String input) throws DukeException {
+ assert(input.startsWith("done "));
+ if (input.length() < 5) {
+ throw new DukeException("☹ OOPS!!! The index of the task to be marked as done is missing.");
+ } else {
+ try {
+ int index = Integer.parseInt(input.substring(4).trim()) - 1;
+ } catch (NumberFormatException e) {
+ throw new DukeException("☹ OOPS!!! " +
+ "The index of the task to mark as done has to be an integer!");
+ }
+ return true;
+ }
+ }
+
+ /**
+ * Checks whether input contains the enough and correct information
+ * to delete a task.
+ * @param input
+ * @return boolean
+ * @throws DukeException
+ */
+ public static boolean isValidDeleteInput(String input) throws DukeException {
+ assert(input.startsWith("delete "));
+ if (input.length() < 7) {
+ throw new DukeException("☹ OOPS!!! The index of the task to delete is missing.");
+ } else {
+ try {
+ int test = Integer.parseInt(input.substring(6).trim()) - 1;
+ } catch (NumberFormatException e) {
+ throw new DukeException("☹ OOPS!!! " +
+ "The index of the task to delete has to be an integer!");
+ }
+ return true;
+ }
+ }
+
+ /**
+ * Checks whether input contains enough and correct information
+ * to find a task with a keyword.
+ * @param parts
+ * @return
+ * @throws DukeException
+ */
+ public static boolean isValidFindInput(String[] parts) throws DukeException {
+ assert(parts[0].trim().equals("find"));
+ if (parts.length != 2) {
+ throw new DukeException("☹ OOPS!!! The keyword to find is missing.");
+ } else {
+ return true;
+ }
+ }
+}
diff --git a/src/main/java/Duke/Duke.java b/src/main/java/Duke/Duke.java
new file mode 100644
index 00000000..310697cf
--- /dev/null
+++ b/src/main/java/Duke/Duke.java
@@ -0,0 +1,74 @@
+package Duke;
+
+import Duke.DukeLogic.DukeException;
+import Duke.DukeLogic.Storage;
+import Duke.Parser.*;
+import Duke.DukeLogic.Ui;
+import java.io.File;
+import java.util.Scanner;
+
+/**
+ * Duke is a program that opens a storage file,
+ * parses the tasks in the file into a list in Duke
+ * and that allows users to do CRUD operations on tasks.
+ */
+public class Duke {
+ static Scanner in = new Scanner(System.in);
+
+ /**
+ * Scans and returns user input as String.
+ * @return
+ */
+ public static String getInput() {
+ String input = in.nextLine();
+ return input;
+ }
+
+ /**
+ * Starts Duke by opening storage file
+ * and updating the duke list using th information from the file.
+ * Then print the duke start up message and a greeting from the program.
+ */
+ public static void runDuke() {
+ try {
+ File StorageFile = Storage.openStorageFile();
+ Storage.readFileToArray(StorageFile);
+ } catch (DukeException e) {
+ e.printErrMsg();
+ }
+ Ui.startDuke();
+ Ui.greet();
+ }
+
+ /**
+ * Continuously read input from the user of Duke
+ * and parse the different inputs.
+ */
+ public static void useDuke() {
+ while (true) {
+ String input = getInput();
+ System.out.println(Ui.LINE);
+ if (input.equals("bye")) {
+ Ui.endDuke();
+ break;
+ } else {
+ InputParser.parseInput(input);
+ }
+ System.out.println(Ui.LINE + "\n");
+ }
+ }
+
+ /**
+ * Runs the main logic of Duke program.
+ * @param args
+ */
+ public static void main(String[] args) {
+ runDuke();
+ useDuke();
+ try {
+ Storage.writeListToFile(Storage.openStorageFile());
+ } catch (DukeException e) {
+ System.out.println("Failed to write to file");
+ }
+ }
+}
diff --git a/src/main/java/Duke/DukeLogic/DukeException.java b/src/main/java/Duke/DukeLogic/DukeException.java
new file mode 100644
index 00000000..3d1da4bd
--- /dev/null
+++ b/src/main/java/Duke/DukeLogic/DukeException.java
@@ -0,0 +1,31 @@
+package Duke.DukeLogic;
+
+/**
+ * An exception class that represents the errors that are thrown when using duke.
+ */
+public class DukeException extends Exception{
+ protected String ErrMsg;
+
+ /**
+ * DukeException constructor that creates a DukeException
+ * with an empty error message.
+ */
+ public DukeException() {
+ this.ErrMsg = "";
+ }
+
+ /**
+ * DukeException constructor takes in a custom error message.
+ * @param invalid
+ */
+ public DukeException(String invalid) {
+ this.ErrMsg = invalid;
+ }
+
+ /**
+ * Prints the custom error message of a DukeException if any.
+ */
+ public void printErrMsg() {
+ System.out.println(ErrMsg);
+ }
+}
diff --git a/src/main/java/Duke/DukeLogic/Storage.java b/src/main/java/Duke/DukeLogic/Storage.java
new file mode 100644
index 00000000..839ac8ed
--- /dev/null
+++ b/src/main/java/Duke/DukeLogic/Storage.java
@@ -0,0 +1,131 @@
+package Duke.DukeLogic;
+
+import Duke.Parser.FileLineParser;
+import Duke.Models.*;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Scanner;
+
+/**
+ * Storage class contains the methods related to the storage file.
+ */
+public class Storage {
+
+ /**
+ * Creates a new file and directory.
+ * @return the new File object.
+ * @throws DukeException
+ */
+ public static File createStorageFile() throws DukeException {
+ File newFile = new File("data\\Duke.DukeLogic.Storage.txt");
+ try {
+ newFile.getParentFile().mkdirs();
+ newFile.createNewFile();
+ System.out.println("New Duke.DukeLogic.Storage.txt file created.");
+ return newFile;
+ } catch (IOException err) {
+ throw new DukeException("Failed to create new Duke.DukeLogic.Storage.txt file.");
+ }
+ }
+
+ /**
+ * Opens and returns storage file.
+ * If a storage file does not exist, create one and return that.
+ * @return an opened File object.
+ * @throws DukeException
+ */
+ public static File openStorageFile() throws DukeException {
+ File newFile = new File("data\\storage.txt");
+ if (!newFile.exists()) {
+ try {
+ newFile = createStorageFile();
+ } catch (DukeException e) {
+ throw new DukeException("Duke.Duke will run without " +
+ "any prior stored task information");
+ }
+ }
+ return newFile;
+ }
+
+ /**
+ * Takes a file and parse the contents into task objects
+ * The parsed task objects will then be added to Duke's task list.
+ * @param storageFile
+ */
+ public static void readFileToArray(File storageFile) {
+ try {
+ Scanner s = new Scanner(storageFile);
+ while (s.hasNext()) {
+ try {
+ Task taskFromLine = FileLineParser.parseLineFromFile(s.nextLine());
+ TaskList.addTaskToList(taskFromLine);
+ } catch (DukeException e) {
+ e.printErrMsg();
+ }
+ }
+ } catch (FileNotFoundException e) {
+ System.out.println("File cannot be opened, " +
+ "Duke will not have any prior stored task information");
+ }
+ }
+
+ /**
+ * Retrieves information of all the tasks inside Duke task list
+ * and converts each into a string to store inside storage file.
+ * @param FileWrite
+ */
+ public static void writeListToFile(File FileWrite) {
+ try {
+ FileWriter fw = new FileWriter(FileWrite, false);
+ ArrayList list = TaskList.getDukeList();
+ for (int i = 0; i < list.size(); i++) {
+ Task taskAtIndex = list.get(i);
+ String taskType = taskAtIndex.getTaskType();
+ String newLine = "";
+ if (taskType.equals("T")) {
+ newLine = buildStorageLine(taskAtIndex);
+ } else if (taskType.equals("D")) {
+ newLine = buildStorageLine(taskAtIndex);
+ } else if (taskType.equals("E")) {
+ newLine = buildStorageLine(taskAtIndex);
+ } else if (taskType.equals("W")) {
+ newLine = buildStorageLine(taskAtIndex);
+ }
+ fw.write(newLine + System.getProperty("line.separator"));
+ }
+ fw.close();
+ } catch (IOException e) {
+ System.out.println("Write to file failed");
+ }
+ }
+
+ /**
+ * Takes in a task object using it to build
+ * and return a string with the format of lines in storage file.
+ * @param targetTask
+ * @return a String with task information in storage line format.
+ */
+ public static String buildStorageLine(Task targetTask) {
+ assert(targetTask != null);
+ String divider = " | ";
+ String initial = targetTask.getTaskType();
+ String newLine = initial + divider;
+ if (targetTask.isCompleted()) {
+ newLine = newLine + "1" + divider;
+ } else {
+ newLine = newLine + "0" + divider;
+ }
+ newLine = newLine + targetTask.getDescription();
+
+ if (initial.equals("D") || initial.equals("E") || initial.equals("W")) {
+ newLine = newLine + divider + targetTask.getAdditionalInfo();
+ }
+
+ newLine = newLine + divider + targetTask.getPriority();
+
+ return newLine;
+ }
+}
diff --git a/src/main/java/Duke/DukeLogic/TaskList.java b/src/main/java/Duke/DukeLogic/TaskList.java
new file mode 100644
index 00000000..bae15ed7
--- /dev/null
+++ b/src/main/java/Duke/DukeLogic/TaskList.java
@@ -0,0 +1,123 @@
+package Duke.DukeLogic;
+
+import Duke.Models.*;
+
+import java.util.ArrayList;
+
+/**
+ * Represents the list that contains the task inputted by using Duke.
+ *
+ */
+public class TaskList {
+ protected static ArrayList DukeList = new ArrayList<>();
+
+ public static ArrayList getDukeList() {
+ return DukeList;
+ }
+
+ /**
+ * Returns the number of tasks inside the task list.
+ * @return int of the size of task list.
+ */
+ public static int size() {
+ return DukeList.size();
+ }
+
+ /**
+ * Takes in a new Task
+ * and adds it to the task list.
+ * @param newEntry
+ */
+ public static void addTaskToList(Task newEntry) {
+ assert(newEntry != null);
+ DukeList.add(newEntry);
+ }
+
+ /**
+ * Takes in a index and removes the task at the index from the task list.
+ * @param index
+ */
+ public static void removeTask(int index) {
+ assert(index < DukeList.size());
+ DukeList.remove(index);
+ }
+
+ /**
+ * Prints all the task in the task list with their information.
+ */
+ public static void printTaskList() {
+ System.out.println("Here are the tasks in your list:");
+ for (int i = 0; i < DukeList.size(); i++) {
+ System.out.println((i + 1) + "." + DukeList.get(i).getTaskInfo());
+ }
+ }
+
+ /**
+ * Takes in a valid "done" input and gets the index.
+ * If index is valid, marks the task at that index of the list as completed.
+ *
+ * @param input
+ * @throws DukeException
+ */
+ public static void markTaskAtIndex(String input) throws DukeException {
+ int index = Integer.parseInt(input.substring(4).trim()) - 1;
+ if (index < DukeList.size() && index > -1) {
+ markTask(DukeList.get(index));
+ System.out.println("Nice! I've marked this task as done:\n "
+ + DukeList.get(index).getTaskInfo());
+ } else {
+ throw new DukeException("☹ OOPS!!! " +
+ "The index number of the task to be done is invalid!");
+ }
+ }
+
+ /**
+ * Mark the task given as a parameter as completed.
+ * @param inputTask
+ */
+ public static void markTask(Task inputTask) {
+ assert(inputTask != null);
+ inputTask.markCompleted();
+ }
+
+ /**
+ * Takes in a valid "delete" input and gets the index.
+ * If index is valid, the task at that index of the list is deleted from the list.
+ *
+ * @param input
+ * @throws DukeException
+ */
+ public static void deleteTaskAtIndex(String input) throws DukeException {
+ int index = Integer.parseInt(input.substring(6).trim()) - 1;
+ if (index < DukeList.size() && index > -1) {
+ String DeletedInfo = DukeList.get(index).getTaskInfo();
+ removeTask(index);
+ System.out.println("Noted! I've removed this task:\n " + DeletedInfo);
+ Ui.printTaskCount();
+ } else {
+ throw new DukeException("☹ OOPS!!! The index number of the task to delete is invalid!");
+ }
+ }
+
+ /**
+ * Prints tasks with description matching the input string.
+ * @param desc
+ */
+ public static void printTaskWithDesc(String desc) {
+ assert(desc.trim() != "");
+ int count = 0;
+ for (int i = 0; i < TaskList.size(); i++) {
+ Task currentTask = DukeList.get(i);
+ if (currentTask.getDescription().toLowerCase().contains(desc.toLowerCase())) {
+ if (count == 0) {
+ System.out.println("Here are the matching tasks in your list:");
+ }
+ count++;
+ System.out.println(count + ". " + currentTask.getTaskInfo());
+ }
+ }
+ if (count == 0) {
+ System.out.println("There are no matching tasks in your list.");
+ }
+ }
+}
diff --git a/src/main/java/Duke/DukeLogic/Ui.java b/src/main/java/Duke/DukeLogic/Ui.java
new file mode 100644
index 00000000..cc2e7561
--- /dev/null
+++ b/src/main/java/Duke/DukeLogic/Ui.java
@@ -0,0 +1,68 @@
+package Duke.DukeLogic;
+
+import Duke.Models.Task;
+
+/**
+ * Ui class holds Ui display methods that interact with the user.
+ */
+public class Ui {
+ public final static String LINE = "____________________________________________________________";
+
+ /**
+ * Prints the opening message of Duke.
+ */
+ public static void startDuke() {
+ String logo = " ____ _ \n"
+ + "| _ \\ _ _| | _____ \n"
+ + "| | | | | | | |/ / _ \\\n"
+ + "| |_| | |_| | < __/\n"
+ + "|____/ \\__,_|_|\\_\\___|\n";
+ System.out.println(LINE + "\nWelcome to\n" + logo + " chatbot!");
+ }
+
+ /**
+ * Prints a greeting from Duke.
+ */
+ public static void greet() {
+ String start = "Hello, I'm Duke.\n"
+ + "What can I do for you?\n";
+ System.out.println(LINE + "\n" + start + LINE + "\n");
+ }
+
+ /**
+ * Prints a message with the number of task inside the task list of Duke.
+ */
+ public static void printTaskCount() {
+ System.out.println("Now you have " + TaskList.size() + " tasks in the list.");
+ }
+
+ /**
+ * Prints a message that a Task that has been added.
+ * @param newTask
+ */
+ public static void printTaskAdded(Task newTask) {
+ assert(newTask != null);
+ System.out.println(LINE);
+ System.out.println("Got it. I've added this task:\n"
+ + newTask.getTaskInfo());
+ }
+
+ /**
+ * Prints a request for user to choose priority leve
+ * of a newly added task.
+ */
+ public static void requestPriorityLevel() {
+ System.out.println("What is the priority level of this task?" +
+ "\n(enter 1 - LOW priority, 2 - MEDIUM priority, 3 - HIGH priority." +
+ "\nOther inputs will assume task has NIL priority level.)");
+ System.out.println(LINE);
+ }
+
+ /**
+ * Prints the closing message of Duke.
+ */
+ public static void endDuke() {
+ System.out.println("Bye. Hope to see you again soon!");
+ System.out.println(LINE);
+ }
+}
diff --git a/src/main/java/Duke/Models/Deadline.java b/src/main/java/Duke/Models/Deadline.java
new file mode 100644
index 00000000..a15ca583
--- /dev/null
+++ b/src/main/java/Duke/Models/Deadline.java
@@ -0,0 +1,121 @@
+package Duke.Models;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
+
+/**
+ * Represents a task with a deadline
+ * with a description of the task
+ * and the date and time when it is due.
+ */
+public class Deadline extends Task {
+ protected String by;
+ protected LocalDate date;
+ protected LocalTime time;
+
+ /**
+ * A constructor for deadline object that takes in two strings
+ * one as the task description
+ * and the other as the deadline of the deadline object.
+ * @param newTask
+ * @param datetime
+ */
+ public Deadline(String newTask, String datetime) {
+ super(newTask);
+ by = datetime;
+ }
+
+ /**
+ * A constructor for deadline object that takes in a string and LocalDate object
+ * where the string is the task description
+ * and the LocalDate is the date the task is due.
+ * @param newTask
+ * @param newDate
+ */
+ public Deadline(String newTask, LocalDate newDate) {
+ super(newTask);
+ date = newDate;
+ }
+
+ /**
+ * A constructor for deadline object that takes in a string and LocalDate object
+ * where the string is the task description,
+ * the LocalDate is the date the task is due
+ * and the LocalTime is the time the task is due.
+ * @param newTask
+ * @param newDate
+ */
+ public Deadline(String newTask, LocalDate newDate, LocalTime newTime) {
+ super(newTask);
+ date = newDate;
+ time = newTime;
+ }
+
+ /**
+ * Get the date and time of the deadline.
+ * @return
+ */
+ public String getAdditionalInfo() {
+ String info;
+ if (date == null) {
+ info = by;
+ } else {
+ info = date.toString();
+ if (time != null) {
+ info = info + " " + time;
+ }
+ }
+ return info;
+ }
+
+ /**
+ * Gets a string with task type symbol
+ * and a cross symbol to indicate whether a task is complete.
+ * @return String with task type and completed symbol.
+ */
+ public String getCompletedSymbol() {
+ if (Completed) {
+ return "[D][X]";
+ } else {
+ return "[D][ ]";
+ }
+ }
+
+ /**
+ * Return string with information of the deadline.
+ * Date is given in a new format if event has a LocalDate object.
+ * @return String with information on the deadline of the task.
+ */
+ public String getTaskInfo() {
+ String info = super.getTaskInfo() + " (by: ";
+ if (date == null) {
+ info = info + by;
+ } else {
+ info = info + date.format(DateTimeFormatter.ofPattern("MMM dd yyyy"));
+ if (time != null) {
+ info = info + " " + time;
+ }
+ }
+ info = info + ")";
+ info = info + " - with " + Priority + " priority";
+ return info;
+ }
+
+ /**
+ * Returns task type symbol.
+ * @return String with initials of the task type.
+ */
+ @Override
+ public String getTaskType() {
+ return "D";
+ }
+
+ /**
+ * Prints event date in "MMM, dd, yyyy" format.
+ * Only works if event object has a LocalTime variable.
+ */
+ public void printDate() {
+ System.out.println(date.format(DateTimeFormatter.ofPattern("MMM dd yyyy")));
+ }
+}
diff --git a/src/main/java/Duke/Models/DoWithin.java b/src/main/java/Duke/Models/DoWithin.java
new file mode 100644
index 00000000..c1a6ada8
--- /dev/null
+++ b/src/main/java/Duke/Models/DoWithin.java
@@ -0,0 +1,138 @@
+package Duke.Models;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
+
+public class DoWithin extends Task {
+ protected String start;
+ protected LocalDate startDate;
+ protected LocalTime startTime;
+ protected String end;
+ protected LocalDate endDate;
+ protected LocalTime endTime;
+
+ /**
+ * A constructor for dowithin object that takes in three strings
+ * one as the task description
+ * and the other two as the start date/time and end date/time
+ * of the "dowithin" task.
+ * @param newTask
+ * @param newStart
+ * @param newEnd
+ */
+ public DoWithin(String newTask, String newStart, String newEnd) {
+ super(newTask);
+ start = newStart;
+ end = newEnd;
+ }
+
+ /**
+ * A constructor for dowithin that takes in
+ * three strings, two localtime and two localdate objects
+ * newTask string representing the task description
+ * newStart, newStartDate and newStartTime representing the starting date/time of task
+ * newEnd, newEndDate and newEndTime representing the ending date/time of task
+ * @param newTask
+ * @param newStart
+ * @param newEnd
+ * @param newStartDate
+ * @param newStartTime
+ * @param newEndDate
+ * @param newEndTime
+ */
+ public DoWithin (String newTask, String newStart, String newEnd, LocalDate newStartDate,
+ LocalTime newStartTime, LocalDate newEndDate, LocalTime newEndTime) {
+ super(newTask.trim());
+ if (newStartDate != null) {
+ startDate = newStartDate;
+ startTime = newStartTime;
+ } else {
+ start = newStart.trim();
+ }
+ if (newEndTime != null) {
+ endDate = newEndDate;
+ endTime = newEndTime;
+ } else {
+ end = newEnd.trim();
+ }
+ }
+
+ /**
+ * Get the start date and time
+ * and end date and time of the deadline.
+ * @return
+ */
+ public String getAdditionalInfo() {
+ String info;
+ if (startDate == null) {
+ info = start;
+ } else {
+ info = startDate.toString();
+ if (startTime != null) {
+ info = info + " " + startTime;
+ }
+ }
+ info = info + " | ";
+ if (endDate == null) {
+ info = info + end;
+ } else {
+ info = info + endDate.toString();
+ if (endTime != null)
+ info = info + " " + endTime;
+ }
+ return info;
+ }
+
+ /**
+ * Gets a string with task type symbol
+ * and a cross symbol to indicate whether a task is complete.
+ * @return String with task type and completed symbol.
+ */
+ public String getCompletedSymbol() {
+ if (Completed) {
+ return "[W][X]";
+ } else {
+ return "[W][ ]";
+ }
+ }
+
+ /**
+ * Return string with information of the "do within" task.
+ * Date is given in a new format if "dowithin" has a LocalDate object.
+ * @return String with information on the event of the task.
+ */
+ public String getTaskInfo() {
+ String info = super.getTaskInfo() + " (between: ";
+ if (startDate == null) {
+ info = info + start;
+ } else {
+ info = info + startDate.format(DateTimeFormatter.ofPattern("MMM dd yyyy"));
+ if (startTime != null) {
+ info = info + " " + startTime;
+ }
+ }
+ info = info + " and ";
+ if (endDate == null) {
+ info = info + end;
+ } else {
+ info = info + endDate.format(DateTimeFormatter.ofPattern("MMM dd yyyy"));
+ if (endTime != null) {
+ info = info + " " + endTime;
+ }
+ }
+ info = info + ")";
+ info = info + " - with " + Priority + " priority";
+ return info;
+ }
+
+ /**
+ * Returns task type symbol.
+ * @return String with initials of the task type.
+ */
+ @Override
+ public String getTaskType() {
+ return "W";
+ }
+
+}
diff --git a/src/main/java/Duke/Models/Event.java b/src/main/java/Duke/Models/Event.java
new file mode 100644
index 00000000..d0493e66
--- /dev/null
+++ b/src/main/java/Duke/Models/Event.java
@@ -0,0 +1,121 @@
+package Duke.Models;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
+
+/**
+ * Represents Events the user has to attend
+ * with the description of the event and date and time of the event.
+ */
+public class Event extends Task {
+ protected String at;
+ protected LocalDate date;
+ protected LocalTime time;
+
+
+ /**
+ * A constructor for event object that takes in two strings
+ * one as the task description
+ * and the other as the date/time of event.
+ * @param newTask
+ * @param datetime
+ */
+ public Event(String newTask, String datetime) {
+ super(newTask);
+ at = datetime;
+ }
+
+ /**
+ * A constructor for event object that takes in a string and LocalDate object
+ * where the string is the task description
+ * and the LocalDate is the date of the event.
+ * @param newTask
+ * @param newDate
+ */
+ public Event(String newTask, LocalDate newDate) {
+ super(newTask);
+ date = newDate;
+ }
+
+ /**
+ * A constructor for event object that takes in a string, LocalDate and LocalTime object
+ * where the string is the task description,
+ * the LocalDate is the date of the event
+ * and the LocalTime is the time of the event.
+ * @param newTask
+ * @param newDate
+ */
+ public Event(String newTask, LocalDate newDate, LocalTime newTime) {
+ super(newTask);
+ date = newDate;
+ time = newTime;
+ }
+
+ /**
+ * Get the date and time information of the event.
+ * @return String with date and time of the event.
+ */
+ public String getAdditionalInfo() {
+ String info;
+ if (date == null) {
+ info = at;
+ } else {
+ info = date.toString();
+ if (time != null) {
+ info = info + " " + time;
+ }
+ }
+ return info;
+ }
+
+ /**
+ * Gets a string with task type symbol
+ * and a cross symbol to indicate whether a task is complete.
+ * @return String with task type and completed symbol.
+ */
+ public String getCompletedSymbol() {
+ if (Completed) {
+ return "[E][X]";
+ } else {
+ return "[E][ ]";
+ }
+ }
+
+ /**
+ * Return string with information of the event.
+ * Date is given in a new format if event has a LocalDate object.
+ * @return String with information on the event of the task.
+ */
+ public String getTaskInfo() {
+ String info = super.getTaskInfo() + " (at: ";
+ if (date == null) {
+ info = info + at;
+ } else {
+ info = info + date.format(DateTimeFormatter.ofPattern("MMM dd yyyy"));
+ if (time != null) {
+ info = info + " " + time;
+ }
+ }
+ info = info + ")";
+ info = info + " - with " + Priority + " priority";
+ return info;
+ }
+
+ /**
+ * Returns task type symbol.
+ * @return String with initials of the task type.
+ */
+ @Override
+ public String getTaskType() {
+ return "E";
+ }
+
+ /**
+ * Prints event date in "MMM, dd, yyyy" format.
+ * Only works if event object has a LocalTime variable.
+ */
+ public void printDate() {
+ System.out.println(date.format(DateTimeFormatter.ofPattern("MMM dd yyyy")));
+ }
+}
diff --git a/src/main/java/Duke/Models/PriorityLevel.java b/src/main/java/Duke/Models/PriorityLevel.java
new file mode 100644
index 00000000..ec65acd2
--- /dev/null
+++ b/src/main/java/Duke/Models/PriorityLevel.java
@@ -0,0 +1,8 @@
+package Duke.Models;
+
+/**
+ * Represents the priority levels of tasks.
+ */
+public enum PriorityLevel {
+ HIGH, MEDIUM, LOW, NIL
+}
diff --git a/src/main/java/Duke/Models/Task.java b/src/main/java/Duke/Models/Task.java
new file mode 100644
index 00000000..69947f63
--- /dev/null
+++ b/src/main/java/Duke/Models/Task.java
@@ -0,0 +1,135 @@
+package Duke.Models;
+
+import Duke.DukeLogic.DukeException;
+
+/**
+ * Represents a Task with information regarding the
+ * description of the task and whether it is completed.
+ */
+public class Task {
+
+ protected String Description;
+ protected boolean Completed;
+ protected PriorityLevel Priority;
+
+ /**
+ * Task constructor method that takes in a string as description
+ * and sets task as uncompleted.
+ * @param newTask
+ */
+ public Task(String newTask) {
+ this.Description = newTask;
+ this.Completed = false;
+ this.Priority = PriorityLevel.NIL;
+ }
+
+ /**
+ * Task constructor method that takes in two strings
+ * one as the description of the task
+ * and the other as an indication whether task is completed.
+ * @param newTask
+ * @param toComplete
+ * @throws DukeException
+ */
+ public Task(String newTask, String toComplete) throws DukeException {
+ this.Description = newTask;
+ if (toComplete.equals("1")){
+ this.Completed = true;
+ } else if (toComplete.equals("0")) {
+ this.Completed = false;
+ } else {
+ throw new DukeException("Invalid input");
+ }
+ }
+
+ /**
+ * Returns description of the task as a string.
+ * @return String with description of the task.
+ */
+ public String getDescription() {
+ return Description;
+ }
+
+ /**
+ * Returns a boolean variable based on whether the task is completed.
+ * @return boolean
+ */
+ public boolean isCompleted() {
+ return Completed;
+ }
+
+ /**
+ * Gets additional info from task object if any.
+ * @return String with additional info.
+ */
+ public String getAdditionalInfo() {
+ return "";
+ }
+
+ /**
+ * Returns a "[X]" symbol as a string if task is completed.
+ * If uncompleted, return "[ ]" as a string.
+ * @return String indicating whether a task is completed.
+ */
+ public String getCompletedSymbol() {
+ if (Completed) {
+ return "[X]";
+ } else {
+ return "[ ]";
+ }
+ }
+
+ /**
+ * Returns string with task information regarding completion and description.
+ * @return String with information on Task.
+ */
+ public String getTaskInfo() {
+ String TaskInfo = this.getCompletedSymbol() + " " + Description;
+ return TaskInfo;
+ }
+
+ /**
+ * Marks "Completed" variable as true.
+ */
+ public void markCompleted() {
+ Completed = true;
+ }
+
+ /**
+ * Gets the task type of a task object.
+ * @return String of initials of task type in any.
+ */
+ public String getTaskType() {
+ return "";
+ }
+
+ /**
+ * Sets the priority level of the task
+ * based on the parameter passed.
+ * @param level
+ */
+ public void setPriority(String level) {
+ try {
+ this.Priority = PriorityLevel.valueOf(level.trim());
+ } catch (IllegalArgumentException e) {
+ switch (level.trim()) {
+ case "3":
+ this.Priority = PriorityLevel.HIGH;
+ break;
+ case "2":
+ this.Priority = PriorityLevel.MEDIUM;
+ break;
+ case "1":
+ this.Priority = PriorityLevel.LOW;
+ break;
+ default:
+ this.Priority = PriorityLevel.NIL;
+ }
+ }
+ }
+
+ public PriorityLevel getPriority() {
+ return Priority;
+
+ }
+}
diff --git a/src/main/java/Duke/Models/Todo.java b/src/main/java/Duke/Models/Todo.java
new file mode 100644
index 00000000..4b73a5d1
--- /dev/null
+++ b/src/main/java/Duke/Models/Todo.java
@@ -0,0 +1,49 @@
+package Duke.Models;
+
+/**
+ * Represents a task that are to be done
+ * with the description of the task.
+ */
+public class Todo extends Task {
+
+ /**
+ * Constructor method for To do object.
+ * @param newTask
+ */
+ public Todo(String newTask) {
+ super(newTask);
+ }
+
+ /**
+ * Gets a string with task type symbol
+ * and a cross symbol to indicate whether a task is complete.
+ * @return String with task type and completed symbol.
+ */
+ public String getCompletedSymbol() {
+ if (Completed) {
+ return "[T][X]";
+ } else {
+ return "[T][ ]";
+ }
+ }
+
+ /**
+ * Returns task type symbol.
+ * @return String with the initials of task type.
+ */
+ @Override
+ public String getTaskType() {
+ return "T";
+ }
+
+ /**
+ * Return a string with information of the "to do" task
+ * @return
+ */
+ @Override
+ public String getTaskInfo() {
+ String info = super.getTaskInfo();
+ info = info + " - with " + Priority + " priority";
+ return info;
+ }
+}
diff --git a/src/main/java/Duke/Parser/FileLineParser.java b/src/main/java/Duke/Parser/FileLineParser.java
new file mode 100644
index 00000000..f3f76772
--- /dev/null
+++ b/src/main/java/Duke/Parser/FileLineParser.java
@@ -0,0 +1,183 @@
+package Duke.Parser;
+
+import Duke.Checker.FileLineChecker;
+
+import Duke.DukeLogic.DukeException;
+import Duke.DukeLogic.TaskList;
+import Duke.Models.*;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.format.DateTimeParseException;
+
+public class FileLineParser {
+ /**
+ * Takes and parse a line from the storage file,
+ * uses the information from the line to create
+ * and return the respective task object based on the line.
+ * @param FileLine
+ * @return Task object created from file line.
+ * @throws DukeException
+ */
+ public static Task parseLineFromFile(String FileLine) throws DukeException {
+ String[] parts = FileLine.split("\\|");
+ if (parts[0].trim().equals("T")) {
+ if(FileLineChecker.isValidTodoLine(parts)) {
+ Todo newTodo = getTodoFromLine(parts);
+ return newTodo;
+ }
+ } else if (parts[0].trim().equals("D")) {
+ if(FileLineChecker.isValidDeadlineLine(parts)) {
+ Deadline newDeadline = getDeadlineFromLine(parts);
+ return newDeadline;
+ }
+ } else if (parts[0].trim().equals("E")) {
+ if (FileLineChecker.isValidEventLine(parts)) {
+ Event newEvent = getEventFromLine(parts);
+ return newEvent;
+ }
+ } else if (parts[0].trim().equals("W")) {
+ if (FileLineChecker.isValidDoWithinLine(parts)) {
+ DoWithin newDoWithin = getDoWithinFromLine(parts);
+ return newDoWithin;
+ }
+ } else {
+ System.out.println("Line is invalid");
+ }
+ throw new DukeException("A line from storage file is invalid and will not be added to Duke.");
+ }
+
+ /**
+ * Takes in the parsed parts of a line from the storage file
+ * and return a new "To do" object created from the parts.
+ * @param parts
+ * @return "To do" object created from information parsed from a file line.
+ */
+ public static Todo getTodoFromLine(String[] parts) {
+ assert(parts[0].trim().equals("T"));
+ Todo newTodo = new Todo(parts[2].trim());
+ if (parts[1].trim().equals("1")) {
+ TaskList.markTask(newTodo);
+ }
+ newTodo.setPriority(parts[3]);
+ return newTodo;
+ }
+
+ /**
+ * Takes in the parsed parts of a line from the storage file,
+ * checks whether it has a date and time and creates and returns
+ * a new "Deadline" object using the correct constructor based on the checks.
+ * @param parts
+ * @return Deadline object created from information parsed from a file line.
+ */
+ public static Deadline getDeadlineFromLine(String[] parts) {
+ assert(parts[0].trim().equals("D"));
+ String[] datetime = parts[3].trim().split(" ");
+ Deadline newDeadline;
+ if (datetime.length > 2 ) {
+ newDeadline = new Deadline(parts[2].trim(), parts[3].trim());
+ } else {
+ LocalDate newDate = null;
+ LocalTime newTime = null;
+ try {
+ newDate = LocalDate.parse(datetime[0]);
+ newTime = LocalTime.parse(datetime[1]);
+ } catch (DateTimeParseException e) {
+ e.getMessage();
+ } catch (ArrayIndexOutOfBoundsException e) {
+ e.getMessage();
+ }
+ if (newDate != null && newTime != null) {
+ newDeadline = new Deadline(parts[2].trim(), newDate, newTime);
+ } else if (newDate != null) {
+ newDeadline = new Deadline(parts[2].trim(), newDate);
+ } else {
+ newDeadline = new Deadline(parts[2].trim(), parts[3].trim());
+ }
+ }
+ if (parts[1].trim().equals("1")) {
+ TaskList.markTask(newDeadline);
+ }
+ newDeadline.setPriority(parts[4]);
+ return newDeadline;
+ }
+
+ /**
+ * Takes in the parsed parts of a line from the storage file,
+ * checks whether it has a date and time and creates and returns
+ * a new "Event" object using the correct constructor based on the checks.
+ * @param parts
+ * @return an Event object created from information parsed from a file line.
+ */
+ public static Event getEventFromLine(String[] parts) {
+ assert(parts[0].trim().equals("E"));
+ String[] datetime = parts[3].trim().split(" ");
+ Event newEvent;
+ if (datetime.length > 2 ) {
+ newEvent = new Event(parts[2].trim(), parts[3].trim());
+ } else {
+ LocalDate newDate = null;
+ LocalTime newTime = null;
+ try {
+ newDate = LocalDate.parse(datetime[0]);
+ newTime = LocalTime.parse(datetime[1]);
+ } catch (DateTimeParseException e) {
+ e.getMessage();
+ } catch (ArrayIndexOutOfBoundsException e) {
+ e.getMessage();
+ }
+ if (newDate != null && newTime != null) {
+ newEvent = new Event(parts[2].trim(), newDate, newTime);
+ } else if (newDate != null) {
+ newEvent = new Event(parts[2].trim(), newDate);
+ } else {
+ newEvent = new Event(parts[2].trim(), parts[3].trim());
+ }
+ }
+ if (parts[1].trim().equals("1")) {
+ TaskList.markTask(newEvent);
+ }
+ newEvent.setPriority(parts[4]);
+ return newEvent;
+ }
+
+ /**
+ * Takes in the parsed parts of a line from the storage file,
+ * checks whether it have start date, start time, end date and end time
+ * and creates and returns
+ * a new "DoWithin" object using the correct constructor based on the checks.
+ * @param parts
+ * @return an DoWithin object created from information parsed from a file line.
+ */
+ public static DoWithin getDoWithinFromLine(String[] parts) {
+ assert(parts[0].trim().equals("W"));
+ String[] startDateTime = parts[3].trim().split(" ");
+ String[] endDateTime = parts[4].trim().split(" ");
+ DoWithin newDoWithin;
+ LocalDate newStartDate = null, newEndDate = null;
+ LocalTime newStartTime = null, newEndTime = null;
+ try {
+ newStartDate = LocalDate.parse(startDateTime[0]);
+ newStartTime = LocalTime.parse(startDateTime[1]);
+ } catch (DateTimeParseException e) {
+ e.getMessage();
+ } catch (ArrayIndexOutOfBoundsException e) {
+ e.getMessage();
+ }
+ try {
+ newEndDate = LocalDate.parse(endDateTime[0]);
+ newEndTime = LocalTime.parse(endDateTime[1]);
+ } catch (DateTimeParseException e) {
+ e.getMessage();
+ } catch (ArrayIndexOutOfBoundsException e) {
+ e.getMessage();
+ }
+ newDoWithin = new DoWithin(parts[2].trim(), parts[3].trim(), parts[4].trim(),
+ newStartDate, newStartTime, newEndDate, newEndTime);
+ if (parts[1].trim().equals("1")) {
+ TaskList.markTask(newDoWithin);
+ }
+ newDoWithin.setPriority(parts[5]);
+ return newDoWithin;
+ }
+}
diff --git a/src/main/java/Duke/Parser/InputParser.java b/src/main/java/Duke/Parser/InputParser.java
new file mode 100644
index 00000000..0040c451
--- /dev/null
+++ b/src/main/java/Duke/Parser/InputParser.java
@@ -0,0 +1,395 @@
+package Duke.Parser;
+
+import Duke.Checker.InputChecker;
+import Duke.Models.*;
+import Duke.DukeLogic.DukeException;
+import Duke.DukeLogic.TaskList;
+import Duke.DukeLogic.Ui;
+import Duke.Duke;
+import java.time.LocalDate;
+import java.time.LocalTime;
+
+/**
+ * Represents a parser that make sense of inputs and retrieved lines.
+ */
+public class InputParser {
+
+ /**
+ * Reads the input
+ * create a task object based on the input
+ * and adds it into the task list of Duke.
+ * @param input
+ */
+ public static void parseInput(String input) {
+ if (input.trim().equals("list")) {
+ TaskList.printTaskList();
+ } else if (input.startsWith("todo ")) {
+ parseTodoInput(input);
+ } else if (input.startsWith("deadline ")) {
+ parseDeadlineInput(input);
+ } else if (input.startsWith("event ")) {
+ parseEventInput(input);
+ } else if (input.startsWith("done ")) {
+ parseDoneInput(input);
+ } else if (input.startsWith("delete ")) {
+ parseDeleteInput(input);
+ } else if (input.startsWith("find ")) {
+ parseFindInput(input);
+ } else if (input.startsWith("dowithin ")) {
+ parseDoWithinInput(input);
+ } else {
+ System.out.println("☹ OOPS!!! I'm sorry, but I don't know what that means :-(");
+ }
+ }
+
+ /**
+ * If input contains valid information for a "To do" object,
+ * method creates the "to do" Object from the input
+ * and adds it to duke task list
+ * @param input
+ */
+ public static void parseTodoInput(String input) {
+ assert(input.startsWith("todo "));
+ try {
+ if (InputChecker.isValidTodoInput(input)) {
+ String newTask = input.substring(4).trim();
+ Todo newTodo = new Todo(newTask);
+ TaskList.addTaskToList(newTodo);
+ addPriorityToTask(newTodo);
+ Ui.printTaskAdded(newTodo);
+ Ui.printTaskCount();
+ }
+ } catch (DukeException e) {
+ e.printErrMsg();
+ }
+ }
+
+ /**
+ * If input contains valid information for a "deadline" object,
+ * method creates the "deadline" Object from the input
+ * and adds it to duke task list
+ * @param input
+ */
+ public static void parseDeadlineInput(String input) {
+ assert(input.startsWith("deadline "));
+ try {
+ if (InputChecker.isValidDeadlineInput(input)) {
+ Deadline newDeadline = buildDeadline(input);
+ TaskList.addTaskToList(newDeadline);
+ addPriorityToTask(newDeadline);
+ Ui.printTaskAdded(newDeadline);
+ Ui.printTaskCount();
+ }
+ } catch (DukeException e) {
+ e.printErrMsg();
+ }
+ }
+
+ /**
+ * Takes in a valid input with information on a deadline,
+ * parse the parts of the input
+ * and returns a new Deadline object created using the relevant constructor.
+ * @param input
+ * @return Deadline object created from a valid input with deadline information.
+ */
+ public static Deadline buildDeadline(String input) {
+ assert(input.startsWith("deadline "));
+ String[] parts = input.substring(8).split("/by");
+ String[] datetime = parts[1].trim().split(" ");
+ Deadline newDeadline;
+ if (datetime.length > 2) {
+ newDeadline = new Deadline(parts[0].trim(), parts[1].trim());
+ } else {
+ String[] dateDetails = datetime[0].split("/");
+ if (checkDateInput(dateDetails)) {
+ LocalDate taskDate = buildTaskDate(dateDetails);
+ if (datetime.length == 2 && checkTimeInput(datetime[1])) {
+ LocalTime taskTime = buildTaskTime(datetime[1]);
+ newDeadline = new Deadline(parts[0].trim(), taskDate, taskTime);
+ } else {
+ newDeadline = new Deadline(parts[0].trim(), taskDate);
+ }
+ } else {
+ newDeadline = new Deadline(parts[0].trim(), parts[1].trim());
+ }
+ }
+ return newDeadline;
+ }
+
+ /**
+ * If input contains valid information for an "event" object,
+ * method creates the "event" Object from the input
+ * and adds it to duke task list
+ * @param input
+ */
+ public static void parseEventInput(String input) {
+ assert(input.startsWith("event "));
+ try {
+ if (InputChecker.isValidEventInput(input)) {
+ Event newEvent = buildEvent(input);
+ TaskList.addTaskToList(newEvent);
+ addPriorityToTask(newEvent);
+ Ui.printTaskAdded(newEvent);
+ Ui.printTaskCount();
+ }
+ } catch (DukeException e) {
+ e.printErrMsg();
+ }
+ }
+
+ /**
+ * Takes in a valid input with information on an event,
+ * parse the parts of the input
+ * and returns a new Event object created using the relevant constructor.
+ * @param input
+ * @return an Event object created from a valid input with event information.
+ */
+ public static Event buildEvent(String input) {
+ assert(input.startsWith("event "));
+ String[] parts = input.substring(5).split("/at");
+ String[] datetime = parts[1].trim().split(" ");
+ Event newEvent;
+ if (datetime.length > 2) {
+ newEvent = new Event(parts[0].trim(), parts[1].trim());
+ } else {
+ String[] dateDetails = datetime[0].split("/");
+ if (checkDateInput(dateDetails)) {
+ LocalDate taskDate = buildTaskDate(dateDetails);
+ if (datetime.length == 2 && checkTimeInput(datetime[1])) {
+ LocalTime taskTime = buildTaskTime(datetime[1]);
+ newEvent = new Event(parts[0].trim(), taskDate, taskTime);
+ } else {
+ newEvent = new Event(parts[0].trim(), taskDate);
+ }
+ } else {
+ newEvent = new Event(parts[0].trim(), parts[1].trim());
+ }
+ }
+ return newEvent;
+ }
+
+ /**
+ * If input contains valid information for a "dowithin" object,
+ * method creates the "dowithin" Object from the input
+ * and adds it to duke task list
+ * @param input
+ */
+ public static void parseDoWithinInput(String input) {
+ assert(input.startsWith("dowithin "));
+ try {
+ if (InputChecker.isValidDoWithinInput(input)) {
+ DoWithin newDoWithin = buildDoWithin(input);
+ TaskList.addTaskToList(newDoWithin);
+ addPriorityToTask(newDoWithin);
+ Ui.printTaskAdded(newDoWithin);
+ Ui.printTaskCount();
+ }
+ } catch (DukeException e) {
+ e.printErrMsg();
+ }
+ }
+
+ /**
+ * Takes in a valid input with information on
+ * a "dowithin" task that has to be done within a period of time,
+ * parse the parts of the input
+ * and returns a new DoWithin object created using the relevant constructor.
+ * @param input
+ * @return a DoWithin object created from a valid input with dowithin information.
+ */
+ public static DoWithin buildDoWithin(String input) {
+ assert(input.startsWith("dowithin "));
+ String[] parts = input.substring(8).split("/start");
+ String[] startEnd = parts[1].split("/end");
+ String[] startDateTime = startEnd[0].trim().split(" ");
+ String[] endDateTime = startEnd[1].trim().split(" ");
+ DoWithin newDoWithin;
+ LocalDate newStartDate = null, newEndDate = null;
+ LocalTime newStartTime = null, newEndTime = null;
+ if (startDateTime.length > 2 && endDateTime.length > 2) {
+ newDoWithin = new DoWithin(parts[0], startEnd[0], startEnd[1]);
+ } else {
+ if (startDateTime.length < 3) {
+ String[] startDateDetails = startDateTime[0].trim().split("/");
+ if (checkDateInput(startDateDetails)) {
+ newStartDate = buildTaskDate(startDateDetails);
+ if (startDateTime.length == 2 && checkTimeInput(startDateTime[1])) {
+ newStartTime = buildTaskTime(startDateTime[1]);
+ }
+ }
+ }
+ if (endDateTime.length < 3) {
+ String[] endDateDetails = endDateTime[0].trim().split("/");
+ if (checkDateInput(endDateDetails)) {
+ newEndDate = buildTaskDate(endDateDetails);
+ if (endDateTime.length == 2 && checkTimeInput(endDateTime[1])) {
+ newEndTime = buildTaskTime(endDateTime[1]);
+ }
+ }
+ }
+ newDoWithin = new DoWithin(parts[0], startEnd[0], startEnd[1], newStartDate, newStartTime,
+ newEndDate, newEndTime);
+ }
+ return newDoWithin;
+ }
+
+ /**
+ * Takes in a string and checks whether it is in an input representing time.
+ * If it is in valid format, return true.
+ * @param timeInput
+ * @return boolean on whether input is represented in a valid time format.
+ */
+ public static boolean checkTimeInput(String timeInput) {
+ if (timeInput.equals("")) {
+ return false;
+ }
+ try {
+ int time = Integer.parseInt(timeInput);
+ int hour = time/100;
+ int mins = time%100;
+ if (hour < 24 && hour > -1 && mins < 60 && mins > -1) {
+ return true;
+ } else {
+ return false;
+ }
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Takes and parse a string representing time
+ * and return a new LocalTime object
+ * created from the parsed parts of the string.
+ * @param timeInput
+ * @return LocalTime object created from a valid time format input.
+ */
+ public static LocalTime buildTaskTime(String timeInput) {
+ int time = Integer.parseInt(timeInput);
+ int hour = time/100;
+ int mins = time%100;
+ LocalTime taskTime = LocalTime.of(hour, mins);
+ return taskTime;
+ }
+
+ /**
+ * Takes in a string array and checks whether it contains valid strings
+ * that represent date, month and year.
+ * If valid, the method returns true.
+ * @param details
+ * @return boolean whether input contains the correct details to represent date.
+ */
+ public static boolean checkDateInput(String[] details) {
+ if (details.length != 3) {
+ return false;
+ }
+ try {
+ int day = Integer.parseInt(details[0].trim());
+ int month = Integer.parseInt(details[1].trim());
+ int year = Integer.parseInt(details[2].trim());
+ if (day > 31 || day < 1) {
+ return false;
+ } else if (month > 12 || month < 1) {
+ return false;
+ } else if (year < 0) {
+ return false;
+ } else {
+ return true;
+ }
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ }
+
+ /** Takes a string array with details on a specific date
+ * and return a LocalDate object
+ * created using the details.
+ * @param details
+ * @return LocalDate object created from the parts of a valid date format input.
+ */
+ public static LocalDate buildTaskDate(String[] details) {
+ String day = details[0].trim();
+ if (day.length() == 1) {
+ day = "0" + day;
+ }
+ String month = details[1].trim();
+ if (month.length() == 1) {
+ month = "0" + month;
+ }
+ String year = details[2].trim();
+ if (year.length() < 4) {
+ for (int i = year.length(); i < 4; i ++) {
+ year = "0" + year;
+ }
+ }
+ String newDateFormat = year + "-" + month + "-" + day;
+ LocalDate taskDate = LocalDate.parse(newDateFormat);
+ return taskDate;
+ }
+
+ /**
+ * Takes in a task object and request priority level of task from user.
+ * Set the priority level of the task object based on the input from user.
+ * @param newTask
+ */
+ public static void addPriorityToTask(Task newTask) {
+ Ui.requestPriorityLevel();
+ String level = Duke.getInput();
+ if (level.equals("HIGH") || level.equals("MEDIUM")
+ || level.equals("LOW") || level.equals("NIL")) {
+ level = "0";
+ }
+ newTask.setPriority(level);
+ }
+
+ /**
+ * If input contain valid information to identify a task in the list,
+ * mark said task object as completed.
+ * @param input
+ */
+ public static void parseDoneInput(String input) {
+ assert(input.startsWith("done "));
+ try {
+ if (InputChecker.isValidDoneInput(input)) {
+ TaskList.markTaskAtIndex(input);
+ }
+ } catch (DukeException e) {
+ e.printErrMsg();
+ }
+ }
+
+ /**
+ * If input contain valid information to identify a task in the list,
+ * delete said task object.
+ * @param input
+ */
+ public static void parseDeleteInput(String input) {
+ assert(input.startsWith("delete "));
+ try {
+ if (InputChecker.isValidDeleteInput(input)) {
+ TaskList.deleteTaskAtIndex(input);
+ }
+ } catch (DukeException e) {
+ e.printErrMsg();
+ }
+ }
+
+ /**
+ * If input contain a valid keyword to search for task,
+ * print tasks with the keyword in their descriptions.
+ * @param input
+ */
+ public static void parseFindInput(String input) {
+ assert(input.startsWith("find "));
+ String[] parts = input.split(" ");
+ try {
+ if (InputChecker.isValidFindInput(parts)) {
+ TaskList.printTaskWithDesc(parts[1]);
+ }
+ } catch (DukeException e) {
+ e.printErrMsg();
+ }
+ }
+
+}
+
diff --git a/src/test/java/Duke/Checker/FileLineCheckerTest.java b/src/test/java/Duke/Checker/FileLineCheckerTest.java
new file mode 100644
index 00000000..2d68aa7f
--- /dev/null
+++ b/src/test/java/Duke/Checker/FileLineCheckerTest.java
@@ -0,0 +1,34 @@
+package Duke.Checker;
+
+import Duke.DukeLogic.DukeException;
+import org.junit.jupiter.api.Test;
+
+import static Duke.Checker.InputChecker.isValidTodoInput;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static Duke.Checker.FileLineChecker.*;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class FileLineCheckerTest {
+
+ @Test
+ public void checkTodoLineTest() {
+ assertEquals(true, isValidTodoLine(new String[]{"T", "1", "Homework", "MEDIUM"}));
+ assertThrows(AssertionError.class, () -> isValidTodoLine(new String[]{"", "", ""}));
+ }
+
+ @Test
+ public void checkDeadlineLineTest() {
+ assertEquals(true,
+ isValidDeadlineLine(new String[]{"D", "0", "Submit Project", "Sunday", "HIGH"}));
+ assertEquals(false,
+ isValidDeadlineLine(new String[]{"D", "3", "Do task", "Today", "LOW"}));
+ }
+
+ @Test
+ public void checkEventLineTest() {
+ assertEquals(true,
+ isValidEventLine(new String[]{"E", "1", "Party", "Friend's house", "LOW"}));
+ assertEquals(false,
+ isValidEventLine(new String[]{"E", "0", "", "Nowhere", "NIL"}));
+ }
+}
diff --git a/src/test/java/Duke/Checker/InputCheckerTest.java b/src/test/java/Duke/Checker/InputCheckerTest.java
new file mode 100644
index 00000000..2535cf49
--- /dev/null
+++ b/src/test/java/Duke/Checker/InputCheckerTest.java
@@ -0,0 +1,42 @@
+package Duke.Checker;
+
+import Duke.DukeLogic.DukeException;
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static Duke.Checker.InputChecker.*;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class InputCheckerTest {
+
+ @Test
+ public void checkValidTodoTest() throws DukeException {
+ assertEquals(true, isValidTodoInput("todo task 1"));
+ assertThrows(AssertionError.class, () -> isValidTodoInput("tod"));
+ assertThrows(DukeException.class, () -> isValidTodoInput("todo "));
+ }
+
+ @Test
+ public void checkValidDeadlineTest() throws DukeException {
+ assertEquals(true, isValidDeadlineInput("deadline homework /by today"));
+ assertThrows(AssertionError.class, () -> isValidDeadlineInput("deadli"));
+ assertThrows(DukeException.class, () -> isValidDeadlineInput("deadline return book /by"));
+ assertThrows(DukeException.class, () -> isValidDeadlineInput("deadline /by tomorrow"));
+
+ }
+
+ @Test
+ public void checkValidEventTest() throws DukeException {
+ assertEquals(true, isValidEventInput("event assembly /at school"));
+ assertThrows(AssertionError.class, () -> isValidEventInput("eve"));
+ assertThrows(DukeException.class, () -> isValidEventInput("event birthday /at"));
+ assertThrows(DukeException.class, () -> isValidEventInput("event /at home"));
+ }
+
+ @Test
+ public void checkValidFindTest() throws DukeException {
+ assertEquals(true, isValidFindInput(new String[]{"find","task"}));
+ assertThrows(DukeException.class, () -> isValidFindInput(new String[]{"find"}));
+ assertThrows(ArrayIndexOutOfBoundsException.class, () -> isValidFindInput(new String[]{}));
+
+ }
+}
diff --git a/src/test/java/Duke/Models/DeadlineTest.java b/src/test/java/Duke/Models/DeadlineTest.java
new file mode 100644
index 00000000..27a44b45
--- /dev/null
+++ b/src/test/java/Duke/Models/DeadlineTest.java
@@ -0,0 +1,79 @@
+package Duke.Models;
+
+import org.junit.jupiter.api.Test;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class DeadlineTest {
+ Deadline Test1 = new Deadline("Task 1", "tomorrow");
+ Deadline Test2 = new Deadline("Task 2", LocalDate.parse("2010-10-19"));
+ Deadline Test3 = new Deadline("Task 3",
+ LocalDate.parse("2012-01-20"), LocalTime.parse("18:00"));
+
+ @Test
+ public void getAdditionalInfoTest() {
+ assertEquals("tomorrow", Test1.getAdditionalInfo());
+ assertEquals("2010-10-19", Test2.getAdditionalInfo());
+ assertEquals("2012-01-20 18:00", Test3.getAdditionalInfo());
+ }
+
+ @Test
+ public void isCompletedTest() {
+ assertEquals(false, Test1.isCompleted());
+ assertEquals(false, Test2.isCompleted());
+ assertEquals(false, Test3.isCompleted());
+ Test1.markCompleted();
+ Test3.markCompleted();
+ assertEquals(true, Test1.isCompleted());
+ assertEquals(true, Test3.isCompleted());
+ }
+
+ @Test
+ public void getTaskTypeTest(){
+ assertEquals("D", Test1.getTaskType());
+ assertEquals("D", Test2.getTaskType());
+ assertEquals("D", Test3.getTaskType());
+ }
+
+ @Test
+ public void getCompletedSymbolTest() {
+ assertEquals("[D][ ]", Test1.getCompletedSymbol());
+ assertEquals("[D][ ]", Test2.getCompletedSymbol());
+ assertEquals("[D][ ]", Test3.getCompletedSymbol());
+ Test1.markCompleted();
+ Test2.markCompleted();
+ assertEquals("[D][X]", Test1.getCompletedSymbol());
+ assertEquals("[D][X]", Test2.getCompletedSymbol());
+ }
+
+ @Test
+ public void setPriorityTest() {
+ Test1.setPriority("MEDIUM");
+ Test2.setPriority("1");
+ Test3.setPriority("abc");
+ assertEquals("MEDIUM", Test1.getPriority().toString());
+ assertEquals("LOW", Test2.getPriority().toString());
+ assertEquals("NIL", Test3.getPriority().toString());
+ }
+
+ @Test
+ public void getTaskInfoTest() {
+ assertEquals("[D][ ] Task 1 (by: tomorrow) " +
+ "- with NIL priority", Test1.getTaskInfo());
+ Test1.setPriority("LOW");
+ Test2.setPriority("3");
+ Test3.setPriority("NIL");
+ assertEquals("[D][ ] Task 1 (by: tomorrow) " +
+ "- with LOW priority", Test1.getTaskInfo());
+ assertEquals("[D][ ] Task 2 (by: Oct 19 2010) "
+ + "- with HIGH priority", Test2.getTaskInfo());
+ assertEquals("[D][ ] Task 3 (by: Jan 20 2012 18:00) " +
+ "- with NIL priority", Test3.getTaskInfo());
+ Test2.markCompleted();
+ assertEquals("[D][X] Task 2 (by: Oct 19 2010) " +
+ "- with HIGH priority", Test2.getTaskInfo());
+ }
+}
diff --git a/src/test/java/Duke/Models/EventTest.java b/src/test/java/Duke/Models/EventTest.java
new file mode 100644
index 00000000..740e1963
--- /dev/null
+++ b/src/test/java/Duke/Models/EventTest.java
@@ -0,0 +1,80 @@
+package Duke.Models;
+
+import org.junit.jupiter.api.Test;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class EventTest {
+ Event Test1 = new Event("Task 1", "School");
+ Event Test2 = new Event("Task 2", LocalDate.parse("2020-06-15"));
+ Event Test3 = new Event("Task 3",
+ LocalDate.parse("2021-11-14"), LocalTime.parse("23:59"));
+
+ @Test
+ public void getAdditionalInfoTest() {
+ assertEquals("School", Test1.getAdditionalInfo());
+ assertEquals("2020-06-15", Test2.getAdditionalInfo());
+ assertEquals("2021-11-14 23:59", Test3.getAdditionalInfo());
+ }
+
+ @Test
+ public void isCompletedTest() {
+ assertEquals(false, Test1.isCompleted());
+ assertEquals(false, Test2.isCompleted());
+ assertEquals(false, Test3.isCompleted());
+ Test2.markCompleted();
+ Test3.markCompleted();
+ assertEquals(true, Test2.isCompleted());
+ assertEquals(true, Test3.isCompleted());
+ }
+
+ @Test
+ public void getTaskTypeTest(){
+ assertEquals("E", Test1.getTaskType());
+ assertEquals("E", Test2.getTaskType());
+ assertEquals("E", Test3.getTaskType());
+ }
+
+ @Test
+ public void getCompletedSymbolTest() {
+ assertEquals("[E][ ]", Test1.getCompletedSymbol());
+ assertEquals("[E][ ]", Test2.getCompletedSymbol());
+ assertEquals("[E][ ]", Test3.getCompletedSymbol());
+ Test1.markCompleted();
+ Test3.markCompleted();
+ assertEquals("[E][X]", Test1.getCompletedSymbol());
+ assertEquals("[E][X]", Test3.getCompletedSymbol());
+ }
+
+ @Test
+ public void setPriorityTest() {
+ Test1.setPriority("3");
+ Test2.setPriority("1");
+ Test3.setPriority("MEDIUM");
+ assertEquals("HIGH", Test1.getPriority().toString());
+ assertEquals("LOW", Test2.getPriority().toString());
+ assertEquals("MEDIUM", Test3.getPriority().toString());
+ }
+
+ @Test
+ public void getTaskInfoTest() {
+ assertEquals("[E][ ] Task 1 (at: School) " +
+ "- with NIL priority", Test1.getTaskInfo());
+ Test1.setPriority("2");
+ Test2.setPriority("testing");
+ Test3.setPriority("HIGH");
+ assertEquals("[E][ ] Task 1 (at: School) " +
+ "- with MEDIUM priority", Test1.getTaskInfo());
+ assertEquals("[E][ ] Task 2 (at: Jun 15 2020) " +
+ "- with NIL priority", Test2.getTaskInfo());
+ assertEquals("[E][ ] Task 3 (at: Nov 14 2021 23:59) " +
+ "- with HIGH priority", Test3.getTaskInfo());
+ Test1.markCompleted();
+ assertEquals("[E][X] Task 1 (at: School) " +
+ "- with MEDIUM priority", Test1.getTaskInfo());
+ }
+}
+
diff --git a/src/test/java/Duke/Models/TaskTest.java b/src/test/java/Duke/Models/TaskTest.java
new file mode 100644
index 00000000..af40fb9f
--- /dev/null
+++ b/src/test/java/Duke/Models/TaskTest.java
@@ -0,0 +1,105 @@
+package Duke.Models;
+
+import Duke.DukeLogic.DukeException;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class TaskTest {
+ Task Test1 = new Task("Task 1");
+ Task Test2 = new Task("Task 2");
+
+ @Test
+ public void getDescriptionTest() {
+ assertEquals("Task 1", Test1.getDescription());
+ assertEquals("Task 2", Test2.getDescription());
+ try {
+ Task Test3 = new Task("Task 3", "0");
+ assertEquals("Task 3", Test3.getDescription());
+ } catch (DukeException e) {
+ System.out.println("Task creation failed");
+ }
+ }
+
+ @Test
+ public void completedTest() {
+ assertEquals(false, Test1.isCompleted());
+ assertEquals(false, Test2.isCompleted());
+
+ Test2.markCompleted();
+ assertEquals(true, Test2.isCompleted());
+
+ try {
+ Task Test3 = new Task("Task 3", "1");
+ assertEquals(true, Test3.isCompleted());
+ } catch (DukeException e) {
+ System.out.println("Task creation failed");
+ }
+
+ try {
+ Task Test4 = new Task("Task 4", "0");
+ assertEquals(false, Test4.isCompleted());
+ Test4.markCompleted();
+ assertEquals(true, Test4.isCompleted());
+ } catch (DukeException e) {
+ System.out.println("Task creation failed");
+ }
+ }
+
+ @Test
+ public void getCompleteSymbolTest() {
+ assertEquals("[ ]", Test1.getCompletedSymbol());
+ assertEquals("[ ]", Test2.getCompletedSymbol());
+
+ Test1.markCompleted();
+ assertEquals("[X]", Test1.getCompletedSymbol());
+
+ try {
+ Task Test3 = new Task("Task 3", "0");
+ assertEquals("[ ]", Test3.getCompletedSymbol());
+ Test3.markCompleted();
+ assertEquals("[X]", Test3.getCompletedSymbol());
+ } catch (DukeException e) {
+ System.out.println("Task creation failed");
+ }
+
+ try {
+ Task Test4 = new Task("Task 4", "0");
+ assertEquals("[ ]", Test4.getCompletedSymbol());
+ } catch (DukeException e) {
+ System.out.println("Task creation failed");
+ }
+ }
+
+ @Test
+ public void setPriorityTest() {
+ Test1.setPriority("1");
+ Test2.setPriority("123");
+ assertEquals("LOW", Test1.getPriority().toString());
+ assertEquals("NIL", Test2.getPriority().toString());
+ }
+
+ @Test
+ public void getTaskInfoTest() {
+ assertEquals("[ ] Task 1", Test1.getTaskInfo());
+ assertEquals("[ ] Task 2", Test2.getTaskInfo());
+ Test2.markCompleted();
+ assertEquals("[X] Task 2", Test2.getTaskInfo());
+
+ try {
+ Task Test3 = new Task("Task 3", "0");
+ assertEquals("[ ] Task 3", Test3.getTaskInfo());
+ Test3.markCompleted();
+ assertEquals("[X] Task 3", Test3.getTaskInfo());
+ } catch (DukeException e) {
+ System.out.println("Task creation failed");
+ }
+
+ try {
+ Task Test4 = new Task("Task 4", "1");
+ assertEquals("[X] Task 4", Test4.getTaskInfo());
+ } catch (DukeException e) {
+ System.out.println("Task creation failed");
+ }
+ }
+}
diff --git a/src/test/java/Duke/Models/TodoTest.java b/src/test/java/Duke/Models/TodoTest.java
new file mode 100644
index 00000000..3103ed50
--- /dev/null
+++ b/src/test/java/Duke/Models/TodoTest.java
@@ -0,0 +1,59 @@
+package Duke.Models;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class TodoTest {
+ Todo Test1 = new Todo("Task 1");
+ Todo Test2 = new Todo("Task 2");
+ Todo Test3 = new Todo("Task 3");
+
+ @Test
+ public void getTaskTypeTest(){
+ assertEquals("T", Test1.getTaskType());
+ assertEquals("T", Test2.getTaskType());
+ assertEquals("T", Test3.getTaskType());
+ }
+
+ @Test
+ public void getCompletedSymbolTest() {
+ assertEquals("[T][ ]", Test1.getCompletedSymbol());
+ assertEquals("[T][ ]", Test2.getCompletedSymbol());
+ assertEquals("[T][ ]", Test3.getCompletedSymbol());
+ Test2.markCompleted();
+ Test3.markCompleted();
+ assertEquals("[T][X]", Test2.getCompletedSymbol());
+ assertEquals("[T][X]", Test3.getCompletedSymbol());
+ }
+
+ @Test
+ public void setPriorityTest() {
+ Test1.setPriority("2");
+ Test2.setPriority("random");
+ Test3.setPriority("HIGH");
+ assertEquals("MEDIUM", Test1.getPriority().toString());
+ assertEquals("NIL", Test2.getPriority().toString());
+ assertEquals("HIGH", Test3.getPriority().toString());
+ }
+
+ @Test
+ public void getTaskInfoTest() {
+ assertEquals("[T][ ] Task 1 " +
+ "- with NIL priority", Test1.getTaskInfo());
+ Test1.setPriority("HIGH");
+ Test2.setPriority("0");
+ Test3.setPriority("LOW");
+ assertEquals("[T][ ] Task 1 " +
+ "- with HIGH priority", Test1.getTaskInfo());
+ assertEquals("[T][ ] Task 2 " +
+ "- with NIL priority", Test2.getTaskInfo());
+ assertEquals("[T][ ] Task 3 " +
+ "- with LOW priority", Test3.getTaskInfo());
+
+ Test3.markCompleted();
+ Test3.setPriority("");
+ assertEquals("[T][X] Task 3 " +
+ "- with NIL priority", Test3.getTaskInfo());
+ }
+}
diff --git a/text-ui-test/runtest.bat b/text-ui-test/runtest.bat
index 08737446..cecfdb11 100644
--- a/text-ui-test/runtest.bat
+++ b/text-ui-test/runtest.bat
@@ -15,7 +15,7 @@ IF ERRORLEVEL 1 (
REM no error here, errorlevel == 0
REM run the program, feed commands from input.txt file and redirect the output to the ACTUAL.TXT
-java -classpath ..\bin Duke < input.txt > ACTUAL.TXT
+java -classpath ..\bin Duke.Duke < input.txt > ACTUAL.TXT
REM compare the output to the expected output
FC ACTUAL.TXT EXPECTED.TXT
diff --git a/text-ui-test/runtest.sh b/text-ui-test/runtest.sh
old mode 100644
new mode 100755