diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
deleted file mode 100644
index 8e359a0145..0000000000
--- a/CONTRIBUTORS.md
+++ /dev/null
@@ -1,9 +0,0 @@
-# Contributors
-
-Display | Name | Github Profile | Homepage
----|:---:|:---:|:---:
-![](https://avatars0.githubusercontent.com/u/22460123?s=100) | Jeffry Lum | [Github](https://github.com/j-lum/) | [Homepage](https://se.kasugano.moe)
-![](https://avatars0.githubusercontent.com/u/1673303?s=100) | Damith C. Rajapakse | [Github](https://github.com/damithc/) | [Homepage](https://www.comp.nus.edu.sg/~damithch/)
-# I would like to join this list. How can I help the project
-
-For more information, please refer to our [contributor's guide](https://oss-generic.github.io/process/).
diff --git a/README.md b/README.md
index 8715d4d915..e39d7c74fc 100644
--- a/README.md
+++ b/README.md
@@ -1,24 +1,35 @@
-# 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.
-
-## Setting up in Intellij
-
-Prerequisites: JDK 11, update Intellij to the most recent version.
-
-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).
- 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
- ____ _
- | _ \ _ _| | _____
- | | | | | | | |/ / _ \
- | |_| | |_| | < __/
- |____/ \__,_|_|\_\___|
- ```
+# User Guide
+![Duke](docs/Ui.png)
+## How to Start
+1. Download the latest version of duke.jar [here](https://github.com/dreammac3816547290/ip/releases).
+2. Open command prompt and navigate to the download folder.
+3. Run `java -jar duke.jar`.
+4. The Duke GUI will open, and you can type in commands in the command box.
+## Command
+### `bye` exits Duke
+> bye
+### `list` lists all tasks
+> list
+### `free` finds the nearest date without task
+> free
+### `mark` marks a task as done
+`> mark `
+> mark 1
+### `unmark` marks a task as not done
+`> unmark `
+> unmark 1
+### `find` finds all tasks which description matches a string
+`> find `
+> find read book
+### `todo` creates a todo task
+`> todo `
+> todo read book at the library
+### `deadline` creates a task with deadline
+`> deadline /by `
+> deadline finish math homework /by 2022-09-19
+### `event` creates a task with time
+`> event /at `
+> event go fishing /at 2022-09-20
+### `delete` deletes a task
+`> delete `
+> delete 1
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000000..e4dfcfa592
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,65 @@
+plugins {
+ id 'java'
+ id 'application'
+ id 'com.github.johnrengelman.shadow' version '5.1.0'
+ id 'checkstyle'
+}
+
+checkstyle {
+ toolVersion = '10.2'
+}
+
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ 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'
+}
+
+
+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.Launcher"
+}
+
+shadowJar {
+ archiveBaseName = "duke"
+ archiveClassifier = null
+}
+
+run {
+ standardInput = System.in
+ enableAssertions = true
+}
\ No newline at end of file
diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml
new file mode 100644
index 0000000000..62281d3553
--- /dev/null
+++ b/config/checkstyle/checkstyle.xml
@@ -0,0 +1,434 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/config/checkstyle/suppressions.xml b/config/checkstyle/suppressions.xml
new file mode 100644
index 0000000000..dcaa1af3c3
--- /dev/null
+++ b/config/checkstyle/suppressions.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/README.md b/docs/README.md
index 8077118ebe..4ca0ebaede 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,29 +1,35 @@
# User Guide
-
-## Features
-
-### Feature-ABC
-
-Description of the feature.
-
-### Feature-XYZ
-
-Description of the feature.
-
-## Usage
-
-### `Keyword` - Describe action
-
-Describe the action and its outcome.
-
-Example of usage:
-
-`keyword (optional arguments)`
-
-Expected outcome:
-
-Description of the outcome.
-
-```
-expected output
-```
+![Duke](Ui.png)
+## How to Start
+1. Download the latest version of duke.jar [here](https://github.com/dreammac3816547290/ip/releases).
+2. Open command prompt and navigate to the download folder.
+3. Run `java -jar duke.jar`.
+4. The Duke GUI will open, and you can type in commands in the command box.
+## Command
+### `bye` exits Duke
+> bye
+### `list` lists all tasks
+> list
+### `free` finds the nearest date without task
+> free
+### `mark` marks a task as done
+`> mark `
+> mark 1
+### `unmark` marks a task as not done
+`> unmark `
+> unmark 1
+### `find` finds all tasks which description matches a string
+`> find `
+> find read book
+### `todo` creates a todo task
+`> todo `
+> todo read book at the library
+### `deadline` creates a task with deadline
+`> deadline /by `
+> deadline finish math homework /by 2022-09-19
+### `event` creates a task with time
+`> event /at `
+> event go fishing /at 2022-09-20
+### `delete` deletes a task
+`> delete `
+> delete 1
\ No newline at end of file
diff --git a/docs/Ui.png b/docs/Ui.png
new file mode 100644
index 0000000000..20f90f24cf
Binary files /dev/null and b/docs/Ui.png differ
diff --git a/duke.txt b/duke.txt
new file mode 100644
index 0000000000..1085f1f505
--- /dev/null
+++ b/duke.txt
@@ -0,0 +1,3 @@
+T, , skip
+T, , meow
+T, , skip
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000000..7454180f2a
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 0000000000..69a9715077
--- /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-7.1-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
new file mode 100644
index 0000000000..744e882ed5
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,185 @@
+#!/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
+ ;;
+ MSYS* | 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..107acd32c4
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,89 @@
+@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 execute
+
+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 execute
+
+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
+
+: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 %*
+
+: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 5d313334cc..0000000000
--- 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/AddCommand.java b/src/main/java/duke/AddCommand.java
new file mode 100644
index 0000000000..1ebf9c4dc5
--- /dev/null
+++ b/src/main/java/duke/AddCommand.java
@@ -0,0 +1,75 @@
+package duke;
+
+import java.util.Objects;
+
+/**
+ * Represents a Command to add a task to Duke, either a ToDo, a Deadline, or an Event.
+ */
+public class AddCommand extends Command {
+ protected char type;
+ protected String description;
+ protected String detail;
+
+ /**
+ * Constructor of AddCommand with type and description.
+ *
+ * @param type Type of Task to add.
+ * @param description Description of Task to add.
+ */
+ public AddCommand(char type, String description) {
+ this.type = type;
+ this.description = description;
+ }
+
+ /**
+ * Constructor of AddCommand with type, description, and detail.
+ *
+ * @param type Type of Task to add.
+ * @param description Description of Task to add.
+ * @param detail Additional detail of Task to add (deadline for Deadline and time for Event).
+ */
+ public AddCommand(char type, String description, String detail) {
+ this(type, description);
+ this.detail = detail;
+ }
+
+ /**
+ * Run the AddCommand, add a Task to Duke.
+ *
+ * @param duke Duke instance to run the AddCommand at.
+ * @throws DukeException If the type does not exist in Duke.
+ */
+ @Override
+ public void run(Duke duke) throws DukeException {
+ Task task;
+ switch (type) {
+ case 'T':
+ task = new ToDo(description);
+ break;
+ case 'D':
+ task = new Deadline(description, detail);
+ break;
+ case 'E':
+ task = new Event(description, detail);
+ break;
+ default:
+ throw new DukeException("");
+ }
+ duke.addTask(task);
+ }
+
+ /**
+ * Checks equality to another Object.
+ *
+ * @param o Other Object.
+ * @return true if equal, false otherwise.
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (!super.equals(o)) {
+ return false;
+ }
+ AddCommand that = (AddCommand) o;
+ return type == that.type && Objects.equals(description, that.description) && Objects.equals(detail, that.detail);
+ }
+}
diff --git a/src/main/java/duke/Command.java b/src/main/java/duke/Command.java
new file mode 100644
index 0000000000..cf84f390fd
--- /dev/null
+++ b/src/main/java/duke/Command.java
@@ -0,0 +1,25 @@
+package duke;
+
+/**
+ * Represents the abstract concept of a Command, which could be run in Duke.
+ */
+public abstract class Command {
+ /**
+ * Abstract method to run the Command.
+ *
+ * @param duke Duke instance to run the Command at.
+ * @throws DukeException If run is unsuccessful.
+ */
+ public abstract void run(Duke duke) throws DukeException;
+
+ /**
+ * Checks equality to another Object.
+ *
+ * @param obj Other Object.
+ * @return true if equal, false otherwise.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ return this == obj || (obj instanceof Command && getClass() == obj.getClass());
+ }
+}
diff --git a/src/main/java/duke/Deadline.java b/src/main/java/duke/Deadline.java
new file mode 100644
index 0000000000..82cc4704a0
--- /dev/null
+++ b/src/main/java/duke/Deadline.java
@@ -0,0 +1,79 @@
+package duke;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.util.Objects;
+
+/**
+ * Represents a Deadline, which is a Task with a deadline.
+ */
+public class Deadline extends Task {
+ protected final LocalDate deadline;
+
+ /**
+ * Constructor of Deadline with description and deadline.
+ *
+ * @param description Description of the Task.
+ * @param deadline Deadline of the Task.
+ * @throws DukeException If date format is incorrect.
+ */
+ public Deadline(String description, String deadline) throws DukeException {
+ super(description);
+ try {
+ this.deadline = LocalDate.parse(deadline);
+ } catch (DateTimeParseException e) {
+ throw new DukeException("Incorrect date format!");
+ }
+ type = 'D';
+ }
+
+ /**
+ * Constructor of Deadline with description, boolean to set the Deadline as done or not done, and deadline.
+ *
+ * @param description Description of the Task.
+ * @param isDone Boolean to set the Task as done or not done.
+ * @param deadline Deadline of the Task.
+ * @throws DukeException If date format is incorrect.
+ */
+ public Deadline(String description, boolean isDone, String deadline) throws DukeException {
+ this(description, deadline);
+ this.isDone = isDone;
+ }
+
+ /**
+ * Returns the String representation of the Deadline for UI.
+ *
+ * @return String representation of the Deadline for UI.
+ */
+ @Override
+ public String toString() {
+ assert deadline != null;
+ return super.toString() + " (by: " + deadline.format(DateTimeFormatter.ofPattern("MMM d yyyy")) + ")";
+ }
+
+ /**
+ * Returns the String representation of the Deadline for Storage.
+ *
+ * @return String representation of the Deadline for Storage.
+ */
+ @Override
+ public String toData() {
+ return super.toData() + ", " + deadline;
+ }
+
+ /**
+ * Checks equality to another Object.
+ *
+ * @param o Other Object.
+ * @return true if equal, false otherwise.
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (!super.equals(o)) {
+ return false;
+ }
+ Deadline deadline1 = (Deadline) o;
+ return Objects.equals(deadline, deadline1.deadline);
+ }
+}
diff --git a/src/main/java/duke/DeleteCommand.java b/src/main/java/duke/DeleteCommand.java
new file mode 100644
index 0000000000..bbf35a100b
--- /dev/null
+++ b/src/main/java/duke/DeleteCommand.java
@@ -0,0 +1,45 @@
+package duke;
+
+import java.util.Objects;
+
+/**
+ * Represents a Command to delete a Task from Duke.
+ */
+public class DeleteCommand extends Command {
+ protected int index;
+
+ /**
+ * Constructor of DeleteCommand with index of Task to delete.
+ *
+ * @param index Index of Task to delete.
+ */
+ public DeleteCommand(int index) {
+ this.index = index;
+ }
+
+ /**
+ * Run the DeleteCommand, delete a Task from Duke.
+ *
+ * @param duke Duke instance to run the Command at.
+ * @throws DukeException If delete is unsuccessful.
+ */
+ @Override
+ public void run(Duke duke) throws DukeException {
+ duke.deleteTask(index);
+ }
+
+ /**
+ * Checks equality to another Object.
+ *
+ * @param o Other Object.
+ * @return true if equal, false otherwise.
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (!super.equals(o)) {
+ return false;
+ }
+ DeleteCommand that = (DeleteCommand) o;
+ return index == that.index;
+ }
+}
diff --git a/src/main/java/duke/DialogBox.java b/src/main/java/duke/DialogBox.java
new file mode 100644
index 0000000000..1b85e1a6a8
--- /dev/null
+++ b/src/main/java/duke/DialogBox.java
@@ -0,0 +1,81 @@
+package duke;
+
+import java.io.IOException;
+import java.util.Collections;
+
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import javafx.fxml.FXML;
+import javafx.fxml.FXMLLoader;
+import javafx.geometry.Pos;
+import javafx.scene.Node;
+import javafx.scene.control.Label;
+import javafx.scene.image.Image;
+import javafx.scene.image.ImageView;
+import javafx.scene.layout.HBox;
+
+/**
+ * An example of a custom control using FXML.
+ * This control represents a dialog box consisting of an ImageView to represent the speaker's face and a label
+ * containing text from the speaker.
+ */
+public class DialogBox extends HBox {
+ @FXML
+ private Label dialog;
+ @FXML
+ private ImageView displayPicture;
+
+ /**
+ * Constructor of DialogBox, with text and image to put inside the DialogBox.
+ *
+ * @param text Text to put in the DialogBox.
+ * @param img Image to put in the DialogBox.
+ */
+ private DialogBox(String text, Image img) {
+ try {
+ FXMLLoader fxmlLoader = new FXMLLoader(MainWindow.class.getResource("/view/DialogBox.fxml"));
+ fxmlLoader.setController(this);
+ fxmlLoader.setRoot(this);
+ fxmlLoader.load();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ dialog.setText(text);
+ displayPicture.setImage(img);
+ }
+
+ /**
+ * Flips the dialog box such that the ImageView is on the left and text on the right.
+ */
+ private void flip() {
+ ObservableList tmp = FXCollections.observableArrayList(this.getChildren());
+ Collections.reverse(tmp);
+ getChildren().setAll(tmp);
+ setAlignment(Pos.TOP_LEFT);
+ }
+
+ /**
+ * Takes user input as text and user image as img and returns user's DialogBox, which is left-aligned.
+ *
+ * @param text User input.
+ * @param img User image.
+ * @return User's DialogBox, left-aligned.
+ */
+ public static DialogBox getUserDialog(String text, Image img) {
+ var db = new DialogBox(text, img);
+ db.flip();
+ return db;
+ }
+
+ /**
+ * Takes output from Duke as text and Duke image as img and returns Duke's DialogBox, which is right-aligned.
+ *
+ * @param text Duke output.
+ * @param img Duke image.
+ * @return Duke's DialogBox, right-aligned.
+ */
+ public static DialogBox getDukeDialog(String text, Image img) {
+ return new DialogBox(text, img);
+ }
+}
diff --git a/src/main/java/duke/Duke.java b/src/main/java/duke/Duke.java
new file mode 100644
index 0000000000..3acdf33225
--- /dev/null
+++ b/src/main/java/duke/Duke.java
@@ -0,0 +1,268 @@
+package duke;
+
+import javafx.application.Application;
+import javafx.scene.Scene;
+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.Region;
+import javafx.scene.layout.VBox;
+import javafx.stage.Stage;
+
+import java.time.LocalDate;
+import java.util.ArrayList;
+
+/**
+ * Represents Duke, a Personal Assistant Chatterbot that helps a person to keep track of various things.
+ */
+public class Duke extends Application {
+ protected Storage storage;
+ protected TaskList tasks;
+ protected Ui ui;
+ protected boolean isActive = true;
+
+ private ScrollPane scrollPane;
+ private VBox dialogContainer;
+ private TextField userInput;
+ private Button sendButton;
+ private Scene scene;
+ private Image user = new Image(this.getClass().getResourceAsStream("/images/User.png"));
+ private Image duke = new Image(this.getClass().getResourceAsStream("/images/Duke.png"));
+
+ /**
+ * Constructor of Duke.
+ */
+ public Duke() {
+ try {
+ storage = new Storage("./duke.txt");
+ tasks = storage.load();
+ ui = new Ui();
+ } catch (DukeException e) {
+ System.out.println(e.getMessage());
+ }
+ }
+
+ /**
+ * Add a Task to Duke.
+ *
+ * @param task Task to add.
+ * @throws DukeException If task description is empty.
+ */
+ public void addTask(Task task) throws DukeException {
+ assert tasks != null;
+ assert task != null;
+ if (task.description.isBlank()) {
+ throw new DukeException("Task description is empty!");
+ }
+ tasks.add(task);
+ ui.addTask(task);
+ ui.infoCount(tasks.size());
+ }
+
+ /**
+ * Print all current tasks to the UI.
+ */
+ public void printTasks() {
+ assert tasks != null;
+ ui.printTasks(tasks);
+ }
+
+ /**
+ * Mark a Task as done or not done.
+ *
+ * @param index Index of Task to mark.
+ * @param isDone Boolean to mark the Task as done or not done.
+ * @throws DukeException If index is out of bound.
+ */
+ public void markTask(int index, boolean isDone) throws DukeException {
+ assert tasks != null;
+ if (index < 0 || index >= tasks.size()) {
+ throw new DukeException("Index out of bound!");
+ }
+ Task task = tasks.get(index);
+ assert task != null;
+ if (isDone) {
+ task.markAsDone();
+ ui.markAsDone(task);
+ } else {
+ task.markNotDone();
+ ui.markNotDone(task);
+ }
+ }
+
+ /**
+ * Delete a Task.
+ *
+ * @param index Index of Task to delete.
+ * @throws DukeException If index is out of bound.
+ */
+ public void deleteTask(int index) throws DukeException {
+ assert tasks != null;
+ if (index < 0 || index >= tasks.size()) {
+ throw new DukeException("Index out of bound!");
+ }
+ Task task = tasks.remove(index);
+ assert task != null;
+ ui.deleteTask(task);
+ ui.infoCount(tasks.size());
+ }
+
+ /**
+ * Print all tasks that matches the keyword to the UI.
+ *
+ * @param keyword Keyword to match.
+ */
+ public void findTasks(String keyword) {
+ assert keyword != null;
+ TaskList matches = (TaskList) tasks.clone();
+ assert matches != null;
+ matches.removeIf(task -> !task.toString().toLowerCase().contains(keyword.toLowerCase()));
+ ui.findTasks(matches);
+ }
+
+ /**
+ * Prints the nearest date without task to the UI.
+ */
+ public void findFreeTimes() {
+ LocalDate now = LocalDate.now();
+ ArrayList distances = new ArrayList<>();
+ tasks.forEach(task -> {
+ if (task.type == 'D') {
+ distances.add(((Deadline) task).deadline.toEpochDay() - now.toEpochDay());
+ } else if (task.type == 'E') {
+ distances.add(((Event) task).time.toEpochDay() - now.toEpochDay());
+ }
+ });
+ distances.sort((a, b) -> (int) (a - b));
+ int counter = 1;
+ for (Long distance : distances) {
+ if (distance > counter) {
+ break;
+ } else if (distance == counter) {
+ counter++;
+ }
+ }
+ ui.findFreeTimes(now.plusDays(counter));
+ }
+
+ /**
+ * Exit the Duke Chatterbot.
+ */
+ public void exit() {
+ assert isActive;
+ isActive = false;
+ ui.close();
+ }
+
+ /**
+ * Clears tasks in Duke and the storage.
+ *
+ * @throws DukeException If tasks in Duke or storage can not be cleared.
+ */
+ public void clear() throws DukeException {
+ tasks.clear();
+ storage.save(tasks);
+ }
+
+ /**
+ * Starts the Duke GUI.
+ *
+ * @param stage Stage to show the GUI elements.
+ */
+ @Override
+ public void start(Stage stage) {
+ // Step 1. Setting up required components
+
+ // The container for the content of the chat to scroll.
+ scrollPane = new ScrollPane();
+ dialogContainer = new VBox();
+ scrollPane.setContent(dialogContainer);
+
+ userInput = new TextField();
+ sendButton = new Button("Send");
+
+ AnchorPane mainLayout = new AnchorPane();
+ mainLayout.getChildren().addAll(scrollPane, userInput, sendButton);
+
+ scene = new Scene(mainLayout);
+
+ stage.setScene(scene);
+ stage.show();
+
+ //Step 2. Formatting the window to look as expected
+ stage.setTitle("Duke");
+ stage.setResizable(false);
+ stage.setMinHeight(600.0);
+ stage.setMinWidth(400.0);
+
+ mainLayout.setPrefSize(400.0, 600.0);
+
+ scrollPane.setPrefSize(385, 535);
+ scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
+ scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.ALWAYS);
+
+ scrollPane.setVvalue(1.0);
+ scrollPane.setFitToWidth(true);
+
+ // You will need to import `javafx.scene.layout.Region` for this.
+ dialogContainer.setPrefHeight(Region.USE_COMPUTED_SIZE);
+
+ userInput.setPrefWidth(325.0);
+
+ sendButton.setPrefWidth(55.0);
+
+ AnchorPane.setTopAnchor(scrollPane, 1.0);
+
+ AnchorPane.setBottomAnchor(sendButton, 1.0);
+ AnchorPane.setRightAnchor(sendButton, 1.0);
+
+ AnchorPane.setLeftAnchor(userInput , 1.0);
+ AnchorPane.setBottomAnchor(userInput, 1.0);
+
+ //Step 3. Add functionality to handle user input.
+ sendButton.setOnMouseClicked((event) -> {
+ handleUserInput();
+ });
+
+ userInput.setOnAction((event) -> {
+ handleUserInput();
+ });
+
+ // Scroll down to the end every time dialogContainer's height changes.
+ dialogContainer.heightProperty().addListener((observable) -> scrollPane.setVvalue(1.0));
+ }
+
+ /**
+ * Creates two dialog boxes, one echoing user input and the other containing Duke's reply and then appends them to
+ * the dialog container. Clears the user input after processing.
+ */
+ private void handleUserInput() {
+ String userText = userInput.getText();
+ String dukeText = getResponse(userInput.getText());
+ dialogContainer.getChildren().addAll(
+ DialogBox.getUserDialog(userText, user),
+ DialogBox.getDukeDialog(dukeText, duke)
+ );
+ userInput.clear();
+ }
+
+ /**
+ * Takes user input and returns Duke's response to the input.
+ *
+ * @param input User input.
+ * @return Duke's response to the input.
+ */
+ protected String getResponse(String input) {
+ assert input != null;
+ try {
+ Command command = Parser.parseInput(input);
+ command.run(this);
+ storage.save(tasks);
+ } catch (DukeException e) {
+ ui.print(e.getMessage());
+ }
+ return ui.collect();
+ }
+}
diff --git a/src/main/java/duke/DukeException.java b/src/main/java/duke/DukeException.java
new file mode 100644
index 0000000000..633d9e88ef
--- /dev/null
+++ b/src/main/java/duke/DukeException.java
@@ -0,0 +1,26 @@
+package duke;
+
+/**
+ * Represents an Exception in Duke.
+ */
+public class DukeException extends Exception {
+ /**
+ * Constructor of DukeException with message.
+ *
+ * @param message Message explaining the Exception.
+ */
+ public DukeException(String message) {
+ super(message);
+ }
+
+ /**
+ * Checks equality to another Object.
+ *
+ * @param obj Other Object.
+ * @return true if equal, false otherwise.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ return this == obj || (obj instanceof DukeException && getMessage().equals(((DukeException) obj).getMessage()));
+ }
+}
diff --git a/src/main/java/duke/Event.java b/src/main/java/duke/Event.java
new file mode 100644
index 0000000000..8599b36d94
--- /dev/null
+++ b/src/main/java/duke/Event.java
@@ -0,0 +1,79 @@
+package duke;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.util.Objects;
+
+/**
+ * Represents an Event, which is a Task with time.
+ */
+public class Event extends Task {
+ protected final LocalDate time;
+
+ /**
+ * Constructor of Event with description and time.
+ *
+ * @param description Description of the Event.
+ * @param time Time of the Event.
+ * @throws DukeException If date format is incorrect.
+ */
+ public Event(String description, String time) throws DukeException {
+ super(description);
+ try {
+ this.time = LocalDate.parse(time);
+ } catch (DateTimeParseException e) {
+ throw new DukeException("Incorrect date format!");
+ }
+ type = 'E';
+ }
+
+ /**
+ * Constructor of Event with description, boolean to set the Event as done or not done, and time.
+ *
+ * @param description Description of the Event.
+ * @param isDone Boolean to set the Event as done or not done.
+ * @param time Time of the Event.
+ * @throws DukeException If date format is incorrect.
+ */
+ public Event(String description, boolean isDone, String time) throws DukeException {
+ this(description, time);
+ this.isDone = isDone;
+ }
+
+ /**
+ * Returns the String representation of the Event for UI.
+ *
+ * @return String representation of the Event for UI.
+ */
+ @Override
+ public String toString() {
+ assert time != null;
+ return super.toString() + " (at: " + time.format(DateTimeFormatter.ofPattern("MMM d yyyy")) + ")";
+ }
+
+ /**
+ * Returns the String representation of the Event for Storage.
+ *
+ * @return String representation of the Event for Storage.
+ */
+ @Override
+ public String toData() {
+ return super.toData() + ", " + time;
+ }
+
+ /**
+ * Checks equality to another Object.
+ *
+ * @param o Other Object.
+ * @return true if equal, false otherwise.
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (!super.equals(o)) {
+ return false;
+ }
+ Event event = (Event) o;
+ return Objects.equals(time, event.time);
+ }
+}
diff --git a/src/main/java/duke/ExitCommand.java b/src/main/java/duke/ExitCommand.java
new file mode 100644
index 0000000000..f35963069d
--- /dev/null
+++ b/src/main/java/duke/ExitCommand.java
@@ -0,0 +1,16 @@
+package duke;
+
+/**
+ * Represents a Command to exit Duke.
+ */
+public class ExitCommand extends Command {
+ /**
+ * Run the ExitCommand, exit Duke.
+ *
+ * @param duke Duke instance to run the ExitCommand at.
+ */
+ @Override
+ public void run(Duke duke) {
+ duke.exit();
+ }
+}
diff --git a/src/main/java/duke/FindCommand.java b/src/main/java/duke/FindCommand.java
new file mode 100644
index 0000000000..0db3e22fef
--- /dev/null
+++ b/src/main/java/duke/FindCommand.java
@@ -0,0 +1,45 @@
+package duke;
+
+import java.util.Objects;
+
+/**
+ * Represents a Command to find tasks in Duke that matches a certain keyword.
+ */
+public class FindCommand extends Command {
+ protected String keyword;
+
+ /**
+ * Constructor of FindCommand with keyword to match.
+ *
+ * @param keyword Keyword to match.
+ */
+ public FindCommand(String keyword) {
+ this.keyword = keyword;
+ }
+
+ /**
+ * Run the FindCommand, print all tasks in Duke that matches the keyword to the UI.
+ *
+ * @param duke Duke instance to run the AddCommand at.
+ */
+ @Override
+ public void run(Duke duke) {
+ duke.findTasks(keyword);
+ }
+
+ /**
+ * Checks equality to another Object.
+ *
+ * @param o Other Object.
+ * @return true if equal, false otherwise.
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (!super.equals(o)) {
+ return false;
+ }
+ FindCommand that = (FindCommand) o;
+ return Objects.equals(keyword, that.keyword);
+ }
+
+}
diff --git a/src/main/java/duke/FreeCommand.java b/src/main/java/duke/FreeCommand.java
new file mode 100644
index 0000000000..da41425571
--- /dev/null
+++ b/src/main/java/duke/FreeCommand.java
@@ -0,0 +1,16 @@
+package duke;
+
+/**
+ * Represents a Command to find free times (the nearest date without task).
+ */
+public class FreeCommand extends Command {
+ /**
+ * Run the FreeCommand, prints the nearest date without task in Duke.
+ *
+ * @param duke Duke instance to run the Command at.
+ */
+ @Override
+ public void run(Duke duke) {
+ duke.findFreeTimes();
+ }
+}
diff --git a/src/main/java/duke/Launcher.java b/src/main/java/duke/Launcher.java
new file mode 100644
index 0000000000..e4ef6b4628
--- /dev/null
+++ b/src/main/java/duke/Launcher.java
@@ -0,0 +1,12 @@
+package duke;
+
+import javafx.application.Application;
+
+/**
+ * A launcher class to workaround classpath issues.
+ */
+public class Launcher {
+ public static void main(String[] args) {
+ Application.launch(Main.class, args);
+ }
+}
diff --git a/src/main/java/duke/ListCommand.java b/src/main/java/duke/ListCommand.java
new file mode 100644
index 0000000000..34639f10b8
--- /dev/null
+++ b/src/main/java/duke/ListCommand.java
@@ -0,0 +1,16 @@
+package duke;
+
+/**
+ * Represents a Command to print all tasks in Duke to the UI.
+ */
+public class ListCommand extends Command {
+ /**
+ * Run the ListCommand, print all tasks in Duke to the UI.
+ *
+ * @param duke Duke instance to run the ListCommand at.
+ */
+ @Override
+ public void run(Duke duke) {
+ duke.printTasks();
+ }
+}
diff --git a/src/main/java/duke/Main.java b/src/main/java/duke/Main.java
new file mode 100644
index 0000000000..43899cf6ce
--- /dev/null
+++ b/src/main/java/duke/Main.java
@@ -0,0 +1,36 @@
+package duke;
+
+import java.io.IOException;
+
+import javafx.application.Application;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Scene;
+import javafx.scene.layout.AnchorPane;
+import javafx.stage.Stage;
+
+/**
+ * A GUI for Duke using FXML.
+ */
+public class Main extends Application {
+
+ private Duke duke = new Duke();
+
+ /**
+ * Starts the Duke GUI.
+ *
+ * @param stage Stage to show the GUI elements.
+ */
+ @Override
+ public void start(Stage stage) {
+ try {
+ FXMLLoader fxmlLoader = new FXMLLoader(Main.class.getResource("/view/MainWindow.fxml"));
+ AnchorPane ap = fxmlLoader.load();
+ Scene scene = new Scene(ap);
+ stage.setScene(scene);
+ fxmlLoader.getController().setDuke(duke);
+ stage.show();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/src/main/java/duke/MainWindow.java b/src/main/java/duke/MainWindow.java
new file mode 100644
index 0000000000..76ff77fd92
--- /dev/null
+++ b/src/main/java/duke/MainWindow.java
@@ -0,0 +1,60 @@
+package duke;
+
+import javafx.fxml.FXML;
+import javafx.scene.control.Button;
+import javafx.scene.control.ScrollPane;
+import javafx.scene.control.TextField;
+import javafx.scene.image.Image;
+import javafx.scene.layout.AnchorPane;
+import javafx.scene.layout.VBox;
+
+/**
+ * Controller for MainWindow. Provides the layout for the other controls.
+ */
+public class MainWindow extends AnchorPane {
+ @FXML
+ private ScrollPane scrollPane;
+ @FXML
+ private VBox dialogContainer;
+ @FXML
+ private TextField userInput;
+ @FXML
+ private Button sendButton;
+
+ private Duke duke;
+
+ private Image userImage = new Image(this.getClass().getResourceAsStream("/images/User.png"));
+ private Image dukeImage = new Image(this.getClass().getResourceAsStream("/images/Duke.png"));
+
+ /**
+ * Initializes the MainWindow.
+ */
+ @FXML
+ public void initialize() {
+ scrollPane.vvalueProperty().bind(dialogContainer.heightProperty());
+ }
+
+ /**
+ * Sets the associated Duke instance.
+ *
+ * @param d Duke instance to be associated.
+ */
+ public void setDuke(Duke d) {
+ duke = d;
+ }
+
+ /**
+ * Creates two dialog boxes, one echoing user input and the other containing Duke's reply and then appends them to
+ * the dialog container. Clears the user input after processing.
+ */
+ @FXML
+ private void handleUserInput() {
+ String input = userInput.getText();
+ String response = duke.getResponse(input);
+ dialogContainer.getChildren().addAll(
+ DialogBox.getUserDialog(input, userImage),
+ DialogBox.getDukeDialog(response, dukeImage)
+ );
+ userInput.clear();
+ }
+}
diff --git a/src/main/java/duke/MarkCommand.java b/src/main/java/duke/MarkCommand.java
new file mode 100644
index 0000000000..99fd4369be
--- /dev/null
+++ b/src/main/java/duke/MarkCommand.java
@@ -0,0 +1,48 @@
+package duke;
+
+import java.util.Objects;
+
+/**
+ * Represents a Command to mark a task as done or not done in Duke.
+ */
+public class MarkCommand extends Command {
+ protected int index;
+ protected boolean isDone;
+
+ /**
+ * Constructor of MarkCommand with index of Task to mark and boolean to mark the Task as done or not done.
+ *
+ * @param index Index of Task to mark.
+ * @param isDone Boolean to mark the Task as done or not done.
+ */
+ public MarkCommand(int index, boolean isDone) {
+ this.index = index;
+ this.isDone = isDone;
+ }
+
+ /**
+ * Run the MarkCommand, mark a task as done or not done in Duke.
+ *
+ * @param duke Duke instance to run the MarkCommand at.
+ * @throws DukeException If mark is unsuccessful.
+ */
+ @Override
+ public void run(Duke duke) throws DukeException {
+ duke.markTask(index, isDone);
+ }
+
+ /**
+ * Checks equality to another Object.
+ *
+ * @param o Other Object.
+ * @return true if equal, false otherwise.
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (!super.equals(o)) {
+ return false;
+ }
+ MarkCommand that = (MarkCommand) o;
+ return index == that.index && isDone == that.isDone;
+ }
+}
diff --git a/src/main/java/duke/Parser.java b/src/main/java/duke/Parser.java
new file mode 100644
index 0000000000..3a2fe681d4
--- /dev/null
+++ b/src/main/java/duke/Parser.java
@@ -0,0 +1,85 @@
+package duke;
+
+/**
+ * Represents a Parser to parse the input and return an Object corresponding to it.
+ */
+public class Parser {
+ /**
+ * Returns a Task which results from parsing the data input from Storage.
+ *
+ * @param input Data input from Storage.
+ * @return Task corresponding to the data input from Storage.
+ * @throws DukeException If storage data can not be parsed.
+ */
+ public static Task parseStorageTask(String input) throws DukeException {
+ String[] inputParts = input.split(", ");
+ String type = inputParts[0];
+ boolean isDone = inputParts[1].equals("X");
+ String description = inputParts[2];
+ switch (type) {
+ case "T":
+ return new ToDo(description, isDone);
+ case "D":
+ return new Deadline(description, isDone, inputParts[3]);
+ case "E":
+ return new Event(description, isDone, inputParts[3]);
+ default:
+ throw new DukeException("Load unsuccessful!");
+ }
+ }
+
+ /**
+ * Returns a Command which results from parsing the user input.
+ *
+ * @param input Text input from user.
+ * @return Command corresponding to the user input.
+ * @throws DukeException If user input is not recognized or has incorrect syntax.
+ */
+ public static Command parseInput(String input) throws DukeException {
+ try {
+ assert input != null;
+ String[] inputParts;
+ inputParts = splitFirst(input, " ");
+ assert inputParts.length <= 2;
+ switch (inputParts[0]) {
+ case "bye":
+ return new ExitCommand();
+ case "list":
+ return new ListCommand();
+ case "free":
+ return new FreeCommand();
+ case "mark":
+ return new MarkCommand(Integer.parseInt(inputParts[1]) - 1, true);
+ case "unmark":
+ return new MarkCommand(Integer.parseInt(inputParts[1]) - 1, false);
+ case "find":
+ return new FindCommand(inputParts[1]);
+ case "todo":
+ return new AddCommand('T', inputParts[1]);
+ case "deadline":
+ inputParts = splitFirst(inputParts[1], " /by ");
+ return new AddCommand('D', inputParts[0], inputParts[1]);
+ case "event":
+ inputParts = splitFirst(inputParts[1], " /at ");
+ return new AddCommand('E', inputParts[0], inputParts[1]);
+ case "delete":
+ return new DeleteCommand(Integer.parseInt(inputParts[1]) - 1);
+ default:
+ throw new DukeException("OOPS!!! I'm sorry, but I don't know what that means :-(");
+ }
+ } catch (IndexOutOfBoundsException e) {
+ throw new DukeException("Incorrect syntax!");
+ }
+ }
+
+ /**
+ * Returns a String array with at most 2 elements, which results from splitting the String once with the substring (regex).
+ *
+ * @param string Original String.
+ * @param regex Substring used to split the String.
+ * @return String array with at most 2 elements, which results from splitting the String once with the substring.
+ */
+ public static String[] splitFirst(String string, String regex) {
+ return string.split(regex, 2);
+ }
+}
diff --git a/src/main/java/duke/Storage.java b/src/main/java/duke/Storage.java
new file mode 100644
index 0000000000..2e9e75965d
--- /dev/null
+++ b/src/main/java/duke/Storage.java
@@ -0,0 +1,60 @@
+package duke;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.Scanner;
+
+/**
+ * Represents a Storage to load and save the data of Duke.
+ */
+public class Storage {
+ protected String path;
+
+ /**
+ * Constructor of Storage with path name to the Storage file.
+ *
+ * @param path Path name to the Storage file.
+ */
+ public Storage(String path) {
+ this.path = path;
+ }
+
+ /**
+ * Returns a TaskList which results from loading the data in the Storage file.
+ *
+ * @return TaskList from the data in the Storage file.
+ * @throws DukeException If storage data can not be parsed.
+ */
+ public TaskList load() throws DukeException {
+ File file = new File(path);
+ TaskList tasks = new TaskList();
+ Scanner sc;
+ try {
+ file.createNewFile();
+ sc = new Scanner(file);
+ } catch (IOException e) {
+ return tasks;
+ }
+ while (sc.hasNext()) {
+ tasks.add(Parser.parseStorageTask(sc.nextLine()));
+ }
+ return tasks;
+ }
+
+ /**
+ * Saves the current Duke data to the Storage file.
+ *
+ * @param tasks TaskList which represents the current Duke data.
+ * @throws DukeException If save is unsuccessful.
+ */
+ public void save(TaskList tasks) throws DukeException {
+ try {
+ FileWriter fw = new FileWriter(path);
+ fw.write(tasks.toData());
+ fw.close();
+ } catch (IOException e) {
+ throw new DukeException("Save unsuccessful! " + e.getMessage());
+ }
+ }
+}
diff --git a/src/main/java/duke/Task.java b/src/main/java/duke/Task.java
new file mode 100644
index 0000000000..ae510d5190
--- /dev/null
+++ b/src/main/java/duke/Task.java
@@ -0,0 +1,94 @@
+package duke;
+
+import java.util.Objects;
+
+/**
+ * Represents a Task with description, boolean to set the Task as done or not done, and type (ToDo = 'T',
+ * Deadline = 'D', Event = 'E').
+ */
+public class Task {
+ protected String description;
+ protected boolean isDone;
+ protected char type;
+
+ /**
+ * Constructor of Task with description.
+ *
+ * @param description Description of the Task.
+ */
+ public Task(String description) {
+ this.description = description;
+ isDone = false;
+ }
+
+ /**
+ * Constructor of Task with description and boolean to set the Task as done or not done.
+ *
+ * @param description Description of the Task.
+ * @param isDone Boolean to set the Task as done or not done.
+ */
+ public Task(String description, boolean isDone) {
+ this(description);
+ this.isDone = isDone;
+ }
+
+ /**
+ * Marks Task as done.
+ */
+ public void markAsDone() {
+ isDone = true;
+ }
+
+ /**
+ * Marks Task as not done.
+ */
+ public void markNotDone() {
+ isDone = false;
+ }
+
+ /**
+ * Returns the status icon of the Task, 'X' if done and ' ' if not done.
+ *
+ * @return Status icon of the Task, 'X' if done and ' ' if not done.
+ */
+ public char getStatusIcon() {
+ return isDone ? 'X' : ' '; // done = X
+ }
+
+ /**
+ * Returns the String representation of the Task for UI.
+ *
+ * @return String representation of the Task for UI.
+ */
+ @Override
+ public String toString() {
+ return String.format("[%c][%c] %s", type, getStatusIcon(), description);
+ }
+
+ /**
+ * Returns the String representation of the Task for Storage.
+ *
+ * @return String representation of the Task for Storage.
+ */
+ public String toData() {
+ return String.format("%c, %c, %s", type, getStatusIcon(), description);
+ }
+
+ /**
+ * Checks equality to another Object.
+ *
+ * @param o Other Object.
+ * @return true if equal, false otherwise.
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Task task = (Task) o;
+ return isDone == task.isDone && type == task.type && Objects.equals(description, task.description);
+ }
+}
diff --git a/src/main/java/duke/TaskList.java b/src/main/java/duke/TaskList.java
new file mode 100644
index 0000000000..f4b4c3df04
--- /dev/null
+++ b/src/main/java/duke/TaskList.java
@@ -0,0 +1,21 @@
+package duke;
+
+import java.util.ArrayList;
+
+/**
+ * Represents a list of tasks.
+ */
+public class TaskList extends ArrayList {
+ /**
+ * Returns the String representation of the TaskList for Storage.
+ *
+ * @return String representation of the TaskList for Storage.
+ */
+ public String toData() {
+ String acc = "";
+ for (Task task : this) {
+ acc += task.toData() + '\n';
+ }
+ return acc;
+ }
+}
diff --git a/src/main/java/duke/ToDo.java b/src/main/java/duke/ToDo.java
new file mode 100644
index 0000000000..b52e61096b
--- /dev/null
+++ b/src/main/java/duke/ToDo.java
@@ -0,0 +1,27 @@
+package duke;
+
+/**
+ * Represents a ToDo, which is a Task.
+ */
+public class ToDo extends Task {
+ /**
+ * Constructor of ToDo with description.
+ *
+ * @param description Description of the ToDo.
+ */
+ public ToDo(String description) {
+ super(description);
+ type = 'T';
+ }
+
+ /**
+ * Constructor of ToDo with description and boolean to set the ToDo as done or not done.
+ *
+ * @param description Description of the ToDo.
+ * @param isDone Boolean to set the ToDo as done or not done.
+ */
+ public ToDo(String description, boolean isDone) {
+ this(description);
+ this.isDone = isDone;
+ }
+}
diff --git a/src/main/java/duke/Ui.java b/src/main/java/duke/Ui.java
new file mode 100644
index 0000000000..384ccbb781
--- /dev/null
+++ b/src/main/java/duke/Ui.java
@@ -0,0 +1,147 @@
+package duke;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.Scanner;
+
+/**
+ * Represents the UI component of Duke.
+ */
+public class Ui {
+ protected Scanner sc = new Scanner(System.in);
+ protected String output = "";
+
+ /**
+ * Prints a String to the UI.
+ *
+ * @param string String to print.
+ */
+ public void print(String string) {
+ output += string + '\n';
+ }
+
+ /**
+ * Prints a greeting message to the UI.
+ */
+ public void greet() {
+ print("Hello! I'm duke.Duke");
+ print("What can I do for you?");
+ }
+
+ /**
+ * Reads a line of user input and returns it.
+ *
+ * @return A line of user input that is read.
+ */
+ public String read() {
+ return sc.nextLine();
+ }
+
+ /**
+ * Prints a response from adding a Task in Duke to the UI.
+ *
+ * @param task Task added in Duke.
+ */
+ public void addTask(Task task) {
+ assert task != null;
+ print("Got it. I've added this task:");
+ print(task.toString());
+ }
+
+ /**
+ * Prints all tasks in the TaskList to the UI.
+ *
+ * @param tasks List of tasks to print.
+ */
+ public void printTasks(TaskList tasks) {
+ assert tasks != null;
+ print("Here are the tasks in your list:");
+ for (int i = 0; i < tasks.size(); i++) {
+ print((i + 1) + ". " + tasks.get(i));
+ }
+ }
+
+ /**
+ * Prints a response from marking a Task as done in Duke to the UI.
+ *
+ * @param task Task marked as done in Duke.
+ */
+ public void markAsDone(Task task) {
+ assert task != null;
+ print("Nice! I've marked this task as done:");
+ print(task.toString());
+ }
+
+ /**
+ * Prints a response from marking a Task as not done in Duke to the UI.
+ *
+ * @param task Task marked as not done in Duke.
+ */
+ public void markNotDone(Task task) {
+ assert task != null;
+ print("OK, I've marked this task as not done yet:");
+ print(task.toString());
+ }
+
+ /**
+ * Prints an information about the current number of tasks in Duke.
+ *
+ * @param count Number of tasks in Duke.
+ */
+ public void infoCount(int count) {
+ assert count >= 0;
+ print("Now you have " + count + " tasks in the list.");
+ }
+
+ /**
+ * Prints a response from deleting a Task in Duke to the UI.
+ *
+ * @param task Task deleted in Duke.
+ */
+ public void deleteTask(Task task) {
+ assert task != null;
+ print("Noted. I've removed this task:");
+ print(task.toString());
+ }
+
+ /**
+ * Print all matching tasks to the UI.
+ *
+ * @param tasks List of matching tasks.
+ */
+ public void findTasks(TaskList tasks) {
+ assert tasks != null;
+ print("Here are the matching tasks in your list:");
+ for (int i = 0; i < tasks.size(); i++) {
+ print((i + 1) + ". " + tasks.get(i));
+ }
+ }
+
+ /**
+ * Prints the nearest date without task to the UI.
+ *
+ * @param date Nearest date without task.
+ */
+ public void findFreeTimes(LocalDate date) {
+ print("The nearest date without task is " + date.format(DateTimeFormatter.ofPattern("MMM d yyyy")) + ".");
+ }
+
+ /**
+ * Collects the output accumulated by the UI, returns it, and resets the UI output.
+ *
+ * @return Output accumulated in the UI.
+ */
+ public String collect() {
+ String temp = output;
+ output = "";
+ return temp;
+ }
+
+ /**
+ * Prints a goodbye message and closes the UI.
+ */
+ public void close() {
+ print("Bye. Hope to see you again soon!");
+ sc.close();
+ }
+}
diff --git a/src/main/resources/images/Duke.png b/src/main/resources/images/Duke.png
new file mode 100644
index 0000000000..961890be4e
Binary files /dev/null and b/src/main/resources/images/Duke.png differ
diff --git a/src/main/resources/images/User.png b/src/main/resources/images/User.png
new file mode 100644
index 0000000000..21882e097d
Binary files /dev/null and b/src/main/resources/images/User.png differ
diff --git a/src/main/resources/view/DialogBox.fxml b/src/main/resources/view/DialogBox.fxml
new file mode 100644
index 0000000000..4eaa0bc9a9
--- /dev/null
+++ b/src/main/resources/view/DialogBox.fxml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml
new file mode 100644
index 0000000000..44d4d7e42c
--- /dev/null
+++ b/src/main/resources/view/MainWindow.fxml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/java/duke/DeadlineTest.java b/src/test/java/duke/DeadlineTest.java
new file mode 100644
index 0000000000..f2b72f655b
--- /dev/null
+++ b/src/test/java/duke/DeadlineTest.java
@@ -0,0 +1,26 @@
+package duke;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.time.format.DateTimeFormatter;
+
+import org.junit.jupiter.api.Test;
+
+public class DeadlineTest {
+ @Test
+ public void deadlineTest() throws DukeException {
+ assertEquals("Sep 16 2022", new Deadline("Deadline", "2022-09-16").deadline
+ .format(DateTimeFormatter.ofPattern("MMM d yyyy")));
+ }
+
+ @Test
+ public void toStringTest() throws DukeException {
+ assertEquals("[D][ ] Deadline (by: Dec 16 2022)",
+ new Deadline("Deadline", "2022-12-16").toString());
+ }
+
+ @Test
+ public void toDataTest() throws DukeException {
+ assertEquals("D, , Deadline, 2022-09-16", new Deadline("Deadline", "2022-09-16").toData());
+ }
+}
diff --git a/src/test/java/duke/DukeTest.java b/src/test/java/duke/DukeTest.java
new file mode 100644
index 0000000000..22da24073a
--- /dev/null
+++ b/src/test/java/duke/DukeTest.java
@@ -0,0 +1,108 @@
+package duke;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+
+public class DukeTest {
+ @Test
+ public void addTest() throws DukeException {
+ Duke duke = new Duke();
+ assertEquals("Got it. I've added this task:\n[D][ ] assignment (by: Sep 19 2022)\n"
+ + "Now you have 1 tasks in the list.\n", duke.getResponse("deadline assignment /by 2022-09-19"));
+ duke.clear();
+ }
+
+ @Test
+ public void exceptionTest1() {
+ assertEquals("Task description is empty!\n", new Duke().getResponse("todo "));
+ }
+
+ @Test
+ public void listTest() throws DukeException {
+ Duke duke = new Duke();
+ duke.getResponse("todo read book");
+ duke.getResponse("todo watch movies");
+ assertEquals("Here are the tasks in your list:\n1. [T][ ] read book\n2. [T][ ] watch movies\n",
+ duke.getResponse("list"));
+ duke.clear();
+ }
+
+ @Test
+ public void markTest() throws DukeException {
+ Duke duke = new Duke();
+ duke.getResponse("todo read book");
+ duke.getResponse("todo watch movies");
+ assertEquals("Nice! I've marked this task as done:\n[T][X] read book\n", duke.getResponse("mark 1"));
+ duke.clear();
+ }
+
+ @Test
+ public void exceptionTest2() throws DukeException {
+ Duke duke = new Duke();
+ duke.getResponse("todo read book");
+ duke.getResponse("todo watch movies");
+ assertEquals("Index out of bound!\n", duke.getResponse("mark 3"));
+ duke.clear();
+ }
+
+ @Test
+ public void deleteTest() throws DukeException {
+ Duke duke = new Duke();
+ duke.getResponse("todo read book");
+ duke.getResponse("todo watch movies");
+ assertEquals("Noted. I've removed this task:\n[T][ ] watch movies\nNow you have 1 tasks in the list.\n",
+ duke.getResponse("delete 2"));
+ duke.clear();
+ }
+
+ @Test
+ public void exceptionTest3() throws DukeException {
+ Duke duke = new Duke();
+ duke.getResponse("todo read book");
+ duke.getResponse("todo watch movies");
+ assertEquals("Index out of bound!\n", duke.getResponse("delete 0"));
+ duke.clear();
+ }
+
+ @Test
+ public void findTest() throws DukeException {
+ Duke duke = new Duke();
+ duke.getResponse("todo read book");
+ duke.getResponse("todo read magazine");
+ duke.getResponse("todo watch movies");
+ assertEquals("Here are the matching tasks in your list:\n1. [T][ ] read book\n2. [T][ ] read magazine\n",
+ duke.getResponse("find read"));
+ duke.clear();
+ }
+
+ @Test
+ public void freeTest() throws DukeException {
+ Duke duke = new Duke();
+ duke.getResponse("todo read book");
+ duke.getResponse("todo read magazine");
+ duke.getResponse("todo watch movies");
+ assertEquals("The nearest date without task is "
+ + LocalDate.now().plusDays(1).format(DateTimeFormatter.ofPattern("MMM d yyyy")) + ".\n",
+ duke.getResponse("free"));
+ duke.clear();
+ }
+
+ @Test
+ public void exitTest() {
+ assertEquals("Bye. Hope to see you again soon!\n", new Duke().getResponse("bye"));
+ }
+
+ @Test
+ public void clearTest() throws DukeException {
+ Duke duke = new Duke();
+ duke.getResponse("todo read book");
+ duke.getResponse("todo read magazine");
+ duke.getResponse("todo watch movies");
+ duke.clear();
+ assertEquals(0, duke.tasks.size());
+ }
+}
diff --git a/src/test/java/duke/EventTest.java b/src/test/java/duke/EventTest.java
new file mode 100644
index 0000000000..3ee94a7810
--- /dev/null
+++ b/src/test/java/duke/EventTest.java
@@ -0,0 +1,26 @@
+package duke;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.time.format.DateTimeFormatter;
+
+import org.junit.jupiter.api.Test;
+
+public class EventTest {
+ @Test
+ public void timeTest() throws DukeException {
+ assertEquals("Dec 31 2022", new Event("Event", "2022-12-31").time
+ .format(DateTimeFormatter.ofPattern("MMM d yyyy")));
+ }
+
+ @Test
+ public void toStringTest() throws DukeException {
+ assertEquals("[E][ ] Event (at: Dec 31 2022)",
+ new Event("Event", "2022-12-31").toString());
+ }
+
+ @Test
+ public void toDataTest() throws DukeException {
+ assertEquals("E, , Event, 2022-12-31", new Event("Event", "2022-12-31").toData());
+ }
+}
diff --git a/src/test/java/duke/ParserTest.java b/src/test/java/duke/ParserTest.java
new file mode 100644
index 0000000000..9e470d959c
--- /dev/null
+++ b/src/test/java/duke/ParserTest.java
@@ -0,0 +1,78 @@
+package duke;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+public class ParserTest {
+ @Test
+ public void exitTest() throws DukeException {
+ assertEquals(new ExitCommand(), Parser.parseInput("bye"));
+ }
+
+ @Test
+ public void listTest() throws DukeException {
+ assertEquals(new ListCommand(), Parser.parseInput("list"));
+ }
+
+ @Test
+ public void freeTest() throws DukeException {
+ assertEquals(new FreeCommand(), Parser.parseInput("free"));
+ }
+
+ @Test
+ public void markTest() throws DukeException {
+ assertEquals(new MarkCommand(99, true), Parser.parseInput("mark 100"));
+ }
+
+ @Test
+ public void unmarkTest() throws DukeException {
+ assertEquals(new MarkCommand(49, false), Parser.parseInput("unmark 50"));
+ }
+
+ @Test
+ public void findTest() throws DukeException {
+ assertEquals(new FindCommand("study"), Parser.parseInput("find study"));
+ }
+
+ @Test
+ public void toDoTest() throws DukeException {
+ assertEquals(new AddCommand('T', "study with friends"),
+ Parser.parseInput("todo study with friends"));
+ }
+
+ @Test
+ public void deadlineTest() throws DukeException {
+ assertEquals(new AddCommand('D', "submit assignment", "2022-09-19"),
+ Parser.parseInput("deadline submit assignment /by 2022-09-19"));
+ }
+
+ @Test
+ public void eventTest() throws DukeException {
+ assertEquals(new AddCommand('E', "go to the beach", "2022-09-30"),
+ Parser.parseInput("event go to the beach /at 2022-09-30"));
+ }
+
+ @Test
+ public void deleteTest() throws DukeException {
+ assertEquals(new DeleteCommand(0), Parser.parseInput("delete 1"));
+ }
+
+ @Test
+ public void exceptionTest1() {
+ try {
+ Parser.parseInput("hello");
+ } catch (DukeException e) {
+ assertEquals(new DukeException("OOPS!!! I'm sorry, but I don't know what that means :-("), e);
+ }
+ }
+
+ @Test
+ public void exceptionTest2() {
+ try {
+ Parser.parseInput("deadline new task");
+ } catch (DukeException e) {
+ assertEquals(new DukeException("Incorrect syntax!"), e);
+ }
+ }
+}
diff --git a/src/test/java/duke/ToDoTest.java b/src/test/java/duke/ToDoTest.java
new file mode 100644
index 0000000000..c194b4ce6d
--- /dev/null
+++ b/src/test/java/duke/ToDoTest.java
@@ -0,0 +1,17 @@
+package duke;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+public class ToDoTest {
+ @Test
+ public void toStringTest() {
+ assertEquals("[T][ ] ToDo", new ToDo("ToDo").toString());
+ }
+
+ @Test
+ public void descriptionTest() {
+ assertEquals("ToDo", new ToDo("ToDo").description);
+ }
+}
diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT
deleted file mode 100644
index 657e74f6e7..0000000000
--- a/text-ui-test/EXPECTED.TXT
+++ /dev/null
@@ -1,7 +0,0 @@
-Hello from
- ____ _
-| _ \ _ _| | _____
-| | | | | | | |/ / _ \
-| |_| | |_| | < __/
-|____/ \__,_|_|\_\___|
-
diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/text-ui-test/runtest.bat b/text-ui-test/runtest.bat
deleted file mode 100644
index 0873744649..0000000000
--- a/text-ui-test/runtest.bat
+++ /dev/null
@@ -1,21 +0,0 @@
-@ECHO OFF
-
-REM create bin directory if it doesn't exist
-if not exist ..\bin mkdir ..\bin
-
-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
-IF ERRORLEVEL 1 (
- echo ********** BUILD FAILURE **********
- exit /b 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
-
-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
deleted file mode 100644
index c9ec870033..0000000000
--- a/text-ui-test/runtest.sh
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/usr/bin/env bash
-
-# create bin directory if it doesn't exist
-if [ ! -d "../bin" ]
-then
- mkdir ../bin
-fi
-
-# delete output from previous run
-if [ -e "./ACTUAL.TXT" ]
-then
- rm ACTUAL.TXT
-fi
-
-# compile the code into the bin folder, terminates if error occurred
-if ! javac -cp ../src/main/java -Xlint:none -d ../bin ../src/main/java/*.java
-then
- echo "********** BUILD FAILURE **********"
- exit 1
-fi
-
-# 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
-
-# convert to UNIX format
-cp EXPECTED.TXT EXPECTED-UNIX.TXT
-dos2unix ACTUAL.TXT EXPECTED-UNIX.TXT
-
-# compare the output to the expected output
-diff ACTUAL.TXT EXPECTED-UNIX.TXT
-if [ $? -eq 0 ]
-then
- echo "Test result: PASSED"
- exit 0
-else
- echo "Test result: FAILED"
- exit 1
-fi
\ No newline at end of file