diff --git a/README.md b/README.md
index 8715d4d915..0f2208ab64 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 0000000000..1eb4a90733
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,67 @@
+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.Launcher"
+}
+
+shadowJar {
+ archiveBaseName = "duke"
+ archiveClassifier = null
+}
+
+checkstyle {
+ toolVersion = '8.29'
+}
+
+run{
+ standardInput = System.in
+}
+
+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'
+}
diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml
new file mode 100644
index 0000000000..7951f3a6c4
--- /dev/null
+++ b/config/checkstyle/checkstyle.xml
@@ -0,0 +1,398 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/config/checkstyle/suppressions.xml b/config/checkstyle/suppressions.xml
new file mode 100644
index 0000000000..135ea49ee0
--- /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..5dec41ce08 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,29 +1,208 @@
# User Guide
+Duke helps you keep track of your todos, deadlines and events.
+
## Features
-### Feature-ABC
+### 1. Keep track of your tasks with Duke's todo list!
+
+Duke supports 3 types of tasks:
+A. **todo**: Tasks to be completed in your own time.
+B. **deadline**: Tasks to be completed by a deadline.
+C. **event**: Events that occur on an event date.
+
+
+### 2. Mark tasks as done
+
+Checks off the tasks that you have marked as done.
+
+
+### 3. Delete tasks
+
+Delete the tasks that are no longer relevant.
+
+
+### 4. List
+
+Lists all your current tasks.
+
-Description of the feature.
+### 5. Search among tasks
+
+Search for tasks containing a keyword of your choice.
+
+
+### 6. Save
+
+Automatically save task to your hard disk.
+
+
+### 7. Exit
+
+Close the program with a single command.
-### Feature-XYZ
-Description of the feature.
## Usage
-### `Keyword` - Describe action
+### 1A. `todo ` - Add a todo task
+
+Add a todo task to the current task list.
+
+_Example of usage:_
+
+`todo read book`
+
+_Expected outcome:_
+
+Update the current task list with your new todo task.
-Describe the action and its outcome.
+```
+Got it. I've added this task:
+ [T][ ] read book
+Now you have 1 task in the list.
+```
+
+
+### 1B. `deadline /by ` - Add a deadline task
+
+Add a deadline task to the current task list.
+
+_Example of usage:_
+
+`deadline finish iP /by 2021-09-17`
+
+_Expected outcome:_
+
+Add a deadline task and reflects its respective deadline in MMM dd yyyy format.
+
+```
+Got it. I've added this task:
+ [D][ ] finish iP (by: Sep 17 2021)
+Now you have 2 tasks in the list.
+```
-Example of usage:
-`keyword (optional arguments)`
+### 1C. `event /at ` - Add an event task
-Expected outcome:
+Add an event task to the current task list.
-Description of the outcome.
+_Example of usage:_
+
+`event party /at 2021-09-18`
+
+_Expected outcome:_
+
+Add a event task and reflects its event date in MMM dd yyyy format.
```
-expected output
+Got it. I've added this task:
+ [E][ ] party (at: Sep 18 2021)
+Now you have 3 tasks in the list.
+```
+
+
+### 2. `done ` - Mark tasks as done
+
+Check task off in the task list when it is done. Only able to input valid task numbers. Able to check multiple tasks off at once by entering multiple task numbers, each separated by a whitespace.
+
+_Example of usage:_
+
+`done 1 2`
+
+_Expected outcome:_
+
+Mark tasks indicated by the task numbers given as done.
+
```
+Nice! I've marked these tasks as done:
+ [T][X] read book
+ [D][X] finish iP (by: Sep 17 2021)
+```
+
+
+### 3. `delete ` - Delete tasks of your choice
+
+Delete tasks off the task list. Only able to input valid task numbers. Able to delete multiple tasks at once by entering multiple task numbers, each separated by a whitespace.
+
+_Example of usage:_
+
+`delete 1 3`
+
+_Expected outcome:_
+
+Delete tasks at the given task numbers.
+
+```
+Noted. I've removed these tasks:
+ [T][X] read book
+ [E][ ] party (at: Sep 18 2021)
+Now you have 1 tasks in the list.
+```
+
+
+### 4. `list` - Display current task list
+
+Display current task list.
+
+_Example of usage:_
+
+`list`
+
+_Expected outcome:_
+
+If the task list is empty:
+```
+There are currently no tasks in your list.
+```
+
+If the task list contains tasks:
+```
+Here are the tasks in your list:
+1. [T][X] read book
+2. [D][X] finish iP (by: Sep 17 2021)
+3. [E][ ] party (at: Sep 18 2021)
+```
+
+
+
+### 5. `find ` - Search for tasks containing keyword
+
+Display tasks containing any keyword, phrase or letter of the user's choice.
+
+_Example of usage:_
+
+`find book`
+
+_Expected outcome:_
+
+Search for tasks containing the keyword 'book'.
+
+```
+Here are tasks that contain the keyword 'book':
+1. [T][X] read book
+```
+
+
+### 6. Save tasks to disk
+
+Automatically save the tasks to disk.
+
+
+### 7. `bye` - Close the application
+
+Terminate and close the application.
+
+_Example of usage:_
+
+`bye`
+
+_Expected outcome:_
+
+Terminate and close the application.
+
+
+
+## Acknowledgements:
+* Original Duke template forked from Prof Damith CS2103T Github
+* GUI Design based on JavaFX tutorials
diff --git a/docs/Ui.png b/docs/Ui.png
new file mode 100644
index 0000000000..52a459a40a
Binary files /dev/null and b/docs/Ui.png differ
diff --git a/duke.txt b/duke.txt
new file mode 100644
index 0000000000..eef26a1ab5
--- /dev/null
+++ b/duke.txt
@@ -0,0 +1,3 @@
+T | 0 | read book
+D | 0 | finish ip | Sep 17 2021
+E | 0 | post ip partyy | Sep 18 2021
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000000..f3d88b1c2f
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..b7c8c5dbf5
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.2-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
new file mode 100755
index 0000000000..2fe81a7d95
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,183 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=`expr $i + 1`
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000000..62bd9b9cce
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,103 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/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/META-INF/MANIFEST.MF b/src/main/java/META-INF/MANIFEST.MF
new file mode 100644
index 0000000000..6e864153e8
--- /dev/null
+++ b/src/main/java/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Main-Class: duke.Duke
+
diff --git a/src/main/java/duke/DialogBox.java b/src/main/java/duke/DialogBox.java
new file mode 100644
index 0000000000..df7be98f3f
--- /dev/null
+++ b/src/main/java/duke/DialogBox.java
@@ -0,0 +1,66 @@
+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;
+import javafx.scene.shape.Rectangle;
+
+/**
+ * An example of a custom control using FXML.
+ * This control represents a dialog box consisting of an ImageView to represent the speaker's face and a label
+ * containing text from the speaker.
+ */
+public class DialogBox extends HBox {
+ @FXML
+ private Label dialog;
+ @FXML
+ private ImageView displayPicture;
+
+ private DialogBox(String text, Image img) {
+ try {
+ FXMLLoader fxmlLoader = new FXMLLoader(MainWindow.class.getResource("/view/DialogBox.fxml"));
+ fxmlLoader.setController(this);
+ fxmlLoader.setRoot(this);
+ fxmlLoader.load();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ // Adapted the following snippet from https://github.com/royleochan/ip/
+ Rectangle clip = new Rectangle(displayPicture.getFitWidth(), displayPicture.getFitHeight());
+ clip.setArcWidth(100);
+ clip.setArcHeight(100);
+ displayPicture.setClip(clip);
+ dialog.setText(text);
+ displayPicture.setImage(img);
+ }
+
+ /**
+ * Flips the dialog box such that the ImageView is on the left and text on the right.
+ */
+ private void flip() {
+ ObservableList tmp = FXCollections.observableArrayList(this.getChildren());
+ Collections.reverse(tmp);
+ getChildren().setAll(tmp);
+ setAlignment(Pos.TOP_LEFT);
+ }
+
+ public static DialogBox getUserDialog(String text, Image img) {
+ return new DialogBox(text, img);
+ }
+
+ public static DialogBox getDukeDialog(String text, Image img) {
+ var db = new DialogBox(text, img);
+ db.flip();
+ return db;
+ }
+}
diff --git a/src/main/java/duke/Duke.java b/src/main/java/duke/Duke.java
new file mode 100644
index 0000000000..2b51c49971
--- /dev/null
+++ b/src/main/java/duke/Duke.java
@@ -0,0 +1,81 @@
+package duke;
+import java.io.IOException;
+import java.util.Scanner;
+
+import duke.command.Command;
+import duke.task.TaskList;
+
+/**
+ * Main Duke class.
+ */
+public class Duke {
+
+ private Storage storage;
+ private TaskList tasks;
+ private Ui ui;
+
+ public Duke() {
+ }
+
+ /**
+ * Constructor for Duke.
+ *
+ * @param filePath Path of the .txt file the tasks are stored in.
+ */
+ public Duke(String filePath) throws IOException, DukeException {
+ ui = new Ui();
+ storage = new Storage(filePath);
+ try {
+ System.out.println(storage.load());
+ tasks = storage.load();
+ } catch (DukeException e) {
+ ui.getErrorMessage(e);
+ tasks = new TaskList();
+ } catch (IOException e) {
+ ui.getErrorMessage(e);
+ }
+ }
+
+ /**
+ * Runs the Duke program.
+ */
+ public void run() {
+ ui.showWelcome();
+ boolean isExit = false;
+ Scanner scanner = new Scanner(System.in);
+
+ while (!isExit) {
+ try {
+ Command c = Parser.parse(new Input(scanner.nextLine().trim()));
+ c.execute(tasks, ui, storage);
+ isExit = c.isExit();
+ } catch (DukeException e) {
+ ui.getErrorMessage(e);
+ }
+ }
+ scanner.close();
+ }
+
+ /**
+ * Main argument for Duke.
+ *
+ * @param args The command line arguments.
+ */
+ public static void main(String[] args) throws IOException, DukeException {
+ new Duke("duke.txt").run();
+ }
+
+ /**
+ * You should have your own function to generate a response to user input.
+ * Replace this stub with your completed method.
+ */
+ String getResponse(String input) {
+ try {
+ Command c = Parser.parse(new Input(input));
+ return c.execute(tasks, ui, storage);
+ } catch (DukeException e) {
+ return ui.getErrorMessage(e);
+ }
+ }
+
+}
diff --git a/src/main/java/duke/DukeException.java b/src/main/java/duke/DukeException.java
new file mode 100644
index 0000000000..c3608d4cac
--- /dev/null
+++ b/src/main/java/duke/DukeException.java
@@ -0,0 +1,16 @@
+package duke;
+
+/**
+ * DukeException class to handle errors in Duke.
+ */
+public class DukeException extends Exception {
+
+ /**
+ * Constructor for DukeException.
+ *
+ * @param message Error message.
+ */
+ public DukeException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/duke/Input.java b/src/main/java/duke/Input.java
new file mode 100644
index 0000000000..b4774fc66e
--- /dev/null
+++ b/src/main/java/duke/Input.java
@@ -0,0 +1,163 @@
+package duke;
+
+import java.util.ArrayList;
+
+/**
+ * Input class to handle inputs from the user.
+ */
+public class Input {
+
+ private String input;
+
+ /**
+ * Constructor for Input.
+ *
+ * @param input User input.
+ */
+ public Input(String input) {
+ this.input = input;
+ }
+
+ /**
+ * Gets the description of the task.
+ *
+ * @param tag Represents the type of task in the user input.
+ * @return Description of the task.
+ */
+ public String getDescription(String tag) {
+ if (tag.equals("todo")) {
+ return input.replaceFirst("^todo", "");
+ } else if (tag.equals("deadline")) {
+ return this.input.replaceFirst("^deadline", "").split(" /")[0];
+ } else if (tag.equals("event")) {
+ return this.input.replaceFirst("^event", "").split(" /")[0];
+ }
+ return "";
+ }
+
+ /**
+ * Gets the date of Deadline and Event tasks.
+ *
+ * @param tag Represents the type of task in the user input.
+ * @return Date of the task.
+ */
+ public String getDate(String tag) {
+ if (tag.equals("deadline")) {
+ return this.input.substring(input.indexOf("/by") + 4);
+ } else if (tag.equals("event")) {
+ return this.input.substring(input.indexOf("/at") + 4);
+ }
+ return "";
+ }
+
+ /**
+ * Handles the deletion and marking as done of multiple tasks at once.
+ *
+ * @param tag Represents the type of operation to be done.
+ * @param lsSize Size of the current list.
+ * @return An ArrayList of indexes representing the tasks to be marked as done or deleted.
+ * @throws DukeException If the task number is invalid and the task does not exist in the list.
+ */
+ public ArrayList getIndexArray(String tag, int lsSize) throws DukeException {
+ if (tag.equals("delete")) {
+ return generateIndexArray(lsSize, this.input.substring(7).split(" "));
+ } else if (tag.equals("done")) {
+ return generateIndexArray(lsSize, this.input.substring(5).split(" "));
+ } else {
+ return new ArrayList<>();
+ }
+ }
+
+ /**
+ * Generates an array of indexes that represent the tasks to be deleted or marked as done.
+ *
+ * @param lsSize Size of current list.
+ * @param indexes Array of task numbers representing tasks to be deleted or marked as done.
+ * @return An ArrayList of indexes representing the tasks to be marked as done or deleted.
+ * @throws DukeException If the task number is invalid and the task does not exist in the list.
+ */
+ @SafeVarargs
+ public final ArrayList generateIndexArray(int lsSize, String... indexes) throws DukeException {
+ ArrayList arr = new ArrayList<>();
+ for (String i : indexes) {
+ int index = Integer.parseInt(i) - 1;
+ if (index < 0 || index >= lsSize) {
+ throw new DukeException("Item does not exist in the list.");
+ }
+ arr.add(index);
+ }
+ return arr;
+ }
+
+ /**
+ * Gets the keyword from the user input.
+ *
+ * @return The keyword to be searched.
+ */
+ public String getKeyword() {
+ return this.input.substring(5);
+ }
+
+ /**
+ * Checks if the user input contains the keyword.
+ *
+ * @param keyword The keyword to be searched.
+ * @return Boolean value representing whether the input contains the keyword.
+ */
+ public boolean checkIfContains(String keyword) {
+ return this.input.contains(keyword);
+ }
+
+ /**
+ * Checks if the user input has the delete or done command word only, without the task number.
+ *
+ * @param tag Represents whether the command is a delete or mark as done command.
+ * @return Boolean value representing whether the input only has the command word.
+ */
+ public boolean hasCommandWordOnly(String tag) {
+ if (tag.equals("delete")) {
+ return (this.input.equals("delete") || input.equals("delete "));
+ } else if (tag.equals("done")) {
+ return (equals("done") || input.equals("done "));
+ }
+ return false;
+ }
+
+ /**
+ * Checks if the Input object is equal to another object.
+ *
+ * @param o Object for comparison.
+ * @return Boolean value representing whether the input Strings are equal.
+ */
+ @Override
+ public boolean equals(Object o) {
+ return this.input.equals(o);
+ }
+
+ /**
+ * Checks the type of command the user input represents.
+ *
+ * @return A String tag corresponding to the type of command represented by the user command.
+ */
+ public String checkType() {
+ if (input.equals("bye")) {
+ return "bye";
+ } else if (input.equals("list")) {
+ return "list";
+ } else if (input.startsWith("done")) {
+ return "done";
+ } else if (input.startsWith("delete")) {
+ return "delete";
+ } else if (input.startsWith("find")) {
+ return "find";
+ } else if (input.startsWith("todo")) {
+ return "todo";
+ } else if (input.startsWith("deadline")) {
+ return "deadline";
+ } else if (input.startsWith("event")) {
+ return "event";
+ } else {
+ return "error";
+ }
+ }
+}
diff --git a/src/main/java/duke/Launcher.java b/src/main/java/duke/Launcher.java
new file mode 100644
index 0000000000..b1b599d530
--- /dev/null
+++ b/src/main/java/duke/Launcher.java
@@ -0,0 +1,12 @@
+package duke;
+
+import javafx.application.Application;
+
+/**
+ * A launcher class to work around classpath issues.
+ */
+public class Launcher {
+ public static void main(String[] args) {
+ Application.launch(Main.class, args);
+ }
+}
diff --git a/src/main/java/duke/Main.java b/src/main/java/duke/Main.java
new file mode 100644
index 0000000000..bf01031a00
--- /dev/null
+++ b/src/main/java/duke/Main.java
@@ -0,0 +1,34 @@
+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("duke.txt");
+
+ public Main() throws IOException, DukeException {
+ }
+
+ @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..37541b96e0
--- /dev/null
+++ b/src/main/java/duke/MainWindow.java
@@ -0,0 +1,65 @@
+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 Ui ui = new Ui();
+
+ private Image userImage = new Image(this.getClass().getResourceAsStream("/images/DaUser.jpg"));
+ private Image dukeImage = new Image(this.getClass().getResourceAsStream("/images/DaDuke.jpg"));
+
+ /**
+ * Initialises the MainWindow.
+ */
+ @FXML
+ public void initialize() {
+ // Adapted the following snippet from https://github.com/gordonlzy/ip/
+ scrollPane.setStyle("-fx-background: transparent; -fx-background-color: transparent;");
+ scrollPane.vvalueProperty().bind(dialogContainer.heightProperty());
+ sendButton.setStyle("-fx-background-color: #2b2b2b; -fx-text-fill: white");
+ dialogContainer.setStyle("-fx-background-color: #2b2b2b; -fx-text-fill: white");
+ dialogContainer.getChildren().addAll(DialogBox.getDukeDialog(ui.showWelcome(), dukeImage));
+ }
+
+ /**
+ * Setter for Duke.
+ *
+ * @param d Instance of Duke used.
+ */
+ 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/Parser.java b/src/main/java/duke/Parser.java
new file mode 100644
index 0000000000..7611fa16c0
--- /dev/null
+++ b/src/main/java/duke/Parser.java
@@ -0,0 +1,48 @@
+package duke;
+
+import duke.command.Command;
+import duke.command.DeadlineCommand;
+import duke.command.DeleteCommand;
+import duke.command.DoneCommand;
+import duke.command.ErrorCommand;
+import duke.command.EventCommand;
+import duke.command.ExitCommand;
+import duke.command.FindCommand;
+import duke.command.ListCommand;
+import duke.command.TodoCommand;
+
+/**
+ * Class for parsing the user commands.
+ */
+public class Parser {
+
+ /**
+ * Returns commands according to the uder input.
+ *
+ * @param input The user input.
+ * @return The respective commands.
+ * @throws DukeException If the user input is invalid.
+ */
+ public static Command parse(Input input) throws DukeException {
+ String tag = input.checkType();
+ if (tag.equals("bye")) {
+ return new ExitCommand();
+ } else if (tag.equals("list")) {
+ return new ListCommand();
+ } else if (tag.equals("done")) {
+ return new DoneCommand(input);
+ } else if (tag.equals("delete")) {
+ return new DeleteCommand(input);
+ } else if (tag.equals("find")) {
+ return new FindCommand(input);
+ } else if (tag.equals("todo")) {
+ return new TodoCommand(input);
+ } else if (tag.equals("deadline")) {
+ return new DeadlineCommand(input);
+ } else if (tag.equals("event")) {
+ return new EventCommand(input);
+ } else {
+ return new ErrorCommand();
+ }
+ }
+}
diff --git a/src/main/java/duke/Storage.java b/src/main/java/duke/Storage.java
new file mode 100644
index 0000000000..b543419874
--- /dev/null
+++ b/src/main/java/duke/Storage.java
@@ -0,0 +1,176 @@
+package duke;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.Scanner;
+
+import duke.task.Deadline;
+import duke.task.Event;
+import duke.task.Task;
+import duke.task.TaskList;
+import duke.task.Todo;
+
+
+/**
+ * Storage class for saving the tasks to the hard disk.
+ */
+public class Storage {
+ private String filePath;
+ private PrintWriter writer;
+ private TaskList ls;
+
+ /**
+ * Constructor for Storage.
+ *
+ * @param filePath Path for the .txt file that the tasks are stored in.
+ */
+ public Storage(String filePath) throws IOException, DukeException {
+ this.filePath = filePath;
+ this.ls = this.load();
+ }
+
+ /**
+ * Rewrites the duke.txt file according to the updated TaskList.
+ *
+ * @param ls The current TaskList.
+ */
+ public void rewriteFile(TaskList ls) {
+ this.ls = ls;
+ try {
+ FileWriter fw = new FileWriter(filePath, false);
+ writer = new PrintWriter(fw);
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ for (int i = 0; i < ls.getSize(); i++) {
+ Task task = ls.getTask(i);
+ String taskString = task.toString();
+ String desc = task.getDesc();
+ String addOns = task.additionalDates();
+ if (taskString.startsWith("\t[T]")) {
+ writer.println("T" + (task.isDone() ? " | 1 | " : " | 0 | ") + desc);
+ } else if (taskString.startsWith("\t[D]")) {
+ writer.println("D" + (ls.getTask(i).isDone() ? " | 1 | " : " | 0 | ")
+ + desc + " | " + addOns);
+ } else {
+ writer.println("E" + (task.isDone() ? " | 1 | " : " | 0 | ")
+ + desc + " | " + addOns);
+ }
+ }
+ writer.close();
+ }
+
+ /**
+ * Loads the saved tasks in the hard disk into a TaskList object.
+ *
+ * @return A TaskList object representative of the current tasks.
+ * @throws IOException If there are issues with reading the file in the hard disk.
+ * @throws DukeException If the user input is invalid.
+ */
+ public TaskList load() throws IOException, DukeException {
+ TaskList ls = new TaskList();
+ File data = new File(filePath);
+ data.createNewFile();
+ Scanner s = new Scanner(data);
+ while (s.hasNext()) {
+ ls.addTask(parseTask(s.nextLine()));
+ }
+ return ls;
+ }
+
+ /**
+ * Parses the String representation of the tasks and returns their Task representation.
+ *
+ * @param input String representation of the task.
+ * @return Task representation of the task.
+ * @throws DukeException If the user input is invalid.
+ */
+ public Task parseTask(String input) throws DukeException {
+ String doneString = input.substring(4, 5);
+ boolean state = getDoneState(doneString);
+ if (input.startsWith("T")) {
+ String taskDesc = input.substring(7);
+ Todo tTask = new Todo(taskDesc);
+ if (state) {
+ tTask.setDone();
+ }
+ return tTask;
+ } else if (input.startsWith("D")) {
+ String taskDesc = getDesc(input.substring(7));
+ String taskDate = getDate(input);
+ Deadline dTask = new Deadline(taskDesc, taskDate);
+ if (state) {
+ dTask.setDone();
+ }
+ return dTask;
+ } else {
+ String taskDesc = getDesc(input.substring(7));
+ String taskDate = getDate(input);
+ Event eTask = new Event(taskDesc, taskDate);
+ if (state) {
+ eTask.setDone();
+ }
+ return eTask;
+ }
+ }
+
+ /**
+ * Gets done state of task.
+ *
+ * @param input String representation of done state of task.
+ * @return Boolean representation of done state of task.
+ */
+ public boolean getDoneState(String input) {
+ return input.equals("1");
+ }
+
+ /**
+ * Gets description of task.
+ *
+ * @param input User input.
+ * @return The String representation of the task description.
+ */
+ public String getDesc(String input) {
+ int endIndex = 0;
+ int count = 0;
+ for (int i = 0; i < input.length(); i++) {
+ if (input.charAt(i) == '|') {
+ count++;
+ if (count == 1) {
+ endIndex = i;
+ }
+ }
+ }
+ return input.substring(0, endIndex);
+ }
+
+ /**
+ * Gets the date of the tasks for Deadlines and Events.
+ *
+ * @param input String representation of the task.
+ * @return String representation of the date of the task in yyyy-mm-dd format.
+ */
+ public String getDate(String input) {
+ int endIndex = 0;
+ int count = 0;
+ for (int i = 0; i < input.length(); i++) {
+ if (input.charAt(i) == '|') {
+ count++;
+ if (count == 3) {
+ endIndex = i + 2;
+ }
+ }
+ }
+ String oldDate = input.substring(endIndex);
+ String newDate = LocalDate.parse(oldDate, DateTimeFormatter.ofPattern("MMM dd yyyy"))
+ .format(DateTimeFormatter.ofPattern("uuuu-MM-dd"));
+ return newDate;
+ }
+}
diff --git a/src/main/java/duke/Ui.java b/src/main/java/duke/Ui.java
new file mode 100644
index 0000000000..c909735941
--- /dev/null
+++ b/src/main/java/duke/Ui.java
@@ -0,0 +1,155 @@
+package duke;
+
+import duke.task.Find;
+import duke.task.Task;
+import duke.task.TaskList;
+
+
+/**
+ * Class that encapsulates the Ui in Duke.
+ */
+public class Ui {
+
+ /**
+ * Constructor for Ui class.
+ */
+ public Ui() {
+ }
+
+ /**
+ * Returns welcome statement.
+ *
+ * @return Welcome statement.
+ */
+ public String showWelcome() {
+ return ("Hello! I'm Duke :-)\n" + "What can I do for you today?");
+ }
+
+ /**
+ * Returns closing statement.
+ *
+ * @return Closing statement.
+ */
+ public String goodbye() {
+ return ("Bye. Hope to see you again soon!");
+ }
+
+ /**
+ * Returns Ui for adding task to list.
+ *
+ * @param task Task to be added.
+ * @param size Updated number of items on the TaskList.
+ * @return Ui for adding task to list.
+ */
+ public String addTaskToList(Task task, int size) {
+ String taskToString = task.toString();
+ return ("Got it. I've added this task: \n" + taskToString
+ + "\nNow you have " + size + " tasks in the list.");
+ }
+
+ /**
+ * Prints Ui for setting task as done.
+ *
+ * @param tasklist TaskList of tasks to be set as done.
+ */
+ public String setTaskAsDone(TaskList tasklist) {
+ assert !tasklist.equals(null) : "task does not exist";
+ String result = "Nice! I've marked these tasks as done: \n";
+ for (int i = 0; i < tasklist.getSize(); i++) {
+ Task task = tasklist.getTask(i);
+ result += task.toString() + "\n";
+ }
+ return result;
+ }
+
+ /**
+ * Prints Ui for removing task from the list.
+ *
+ * @param tasklist TaskList of tasks to be removed.
+ * @param newListSize New number of items on the TaskList.
+ */
+ public String removeTaskFromList(TaskList tasklist, int newListSize) {
+ assert !tasklist.equals(null) : "task does not exist";
+ String result = "Noted. I've removed these tasks: \n";
+ for (int i = 0; i < tasklist.getSize(); i++) {
+ Task task = tasklist.getTask(i);
+ result += task.toString() + "\n";
+ }
+ result += "Now you have " + newListSize + " tasks in the list.";
+ return result;
+ }
+
+ /**
+ * Prints the current TaskList.
+ *
+ * @param taskList The current TaskList.
+ */
+ public String printTaskList(TaskList taskList) {
+ if (taskList.getSize() == 0) {
+ return "There are currently no tasks in your list.";
+ } else {
+ String ls = "Here are the tasks in your list:\n";
+ for (int i = 0; i < taskList.getSize(); i++) {
+ ls += (i + 1) + "." + taskList.getTask(i).toString() + "\n";
+ }
+ return ls;
+ }
+ }
+
+ /**
+ * Searches for the keyword among current Tasks.
+ *
+ * @param ls Current TaskList.
+ * @param word Keyword.
+ * @param find Current Find object.
+ * @return String representation of the Tasks that include the keyword.
+ */
+ public String printListWithKeyword(TaskList ls, String word, Find find) {
+ String intro = "Here are tasks that contain the keyword '" + word + "':\n";
+ String result = "";
+ int count = 1;
+ for (int i = 0; i < ls.getSize(); i++) {
+ Task task = ls.getTask(i);
+ if (task.getDesc().toLowerCase().contains(word.toLowerCase())) {
+ find.setFound();
+ result += count + "." + ls.getTask(i).toString() + "\n";
+ count++;
+ }
+ }
+
+ if (result.equals("")) {
+ return "There were no tasks that contained your keyword '" + word + "' :-(";
+ } else {
+ return intro + result;
+ }
+ }
+
+ /**
+ * Prints a message when all current Tasks do not contain the keyword provided.
+ *
+ * @param word Keyword.
+ */
+ public String noResultsFound(String word) {
+ return ("There were no tasks that included your keyword: " + word + ".");
+ }
+
+ /**
+ * Prints DukeException errors.
+ *
+ * @param e Error message.
+ */
+ public String getErrorMessage(Exception e) {
+ return e.getMessage();
+ }
+
+ /**
+ * Returns String representation of Task status.
+ *
+ * @param status Boolean representation of Task status, true for done and false for undone.
+ * @return String representation of Task status, "X" for done and " " for undone.
+ */
+ public String taskStatusIcon(boolean status) {
+ return (status ? "X" : " ");
+ }
+
+}
diff --git a/src/main/java/duke/command/Command.java b/src/main/java/duke/command/Command.java
new file mode 100644
index 0000000000..d7c6e7eb9c
--- /dev/null
+++ b/src/main/java/duke/command/Command.java
@@ -0,0 +1,29 @@
+package duke.command;
+import duke.DukeException;
+import duke.Storage;
+import duke.Ui;
+import duke.task.TaskList;
+
+/**
+ * Abstract class to encapsulate commands.
+ */
+public abstract class Command {
+
+
+ /**
+ * Executes the command.
+ *
+ * @param ls Current list.
+ * @param ui Current Ui.
+ * @param storage Current version of the saved tasks in the hard disk.
+ * @throws DukeException If input is invalid.
+ */
+ public abstract String execute(TaskList ls, Ui ui, Storage storage) throws DukeException;
+
+ /**
+ * Signals to the system whether the command is an exit command.
+ *
+ * @return True if command is "bye".
+ */
+ public abstract boolean isExit();
+}
diff --git a/src/main/java/duke/command/DeadlineCommand.java b/src/main/java/duke/command/DeadlineCommand.java
new file mode 100644
index 0000000000..b6178eb703
--- /dev/null
+++ b/src/main/java/duke/command/DeadlineCommand.java
@@ -0,0 +1,54 @@
+package duke.command;
+import duke.DukeException;
+import duke.Input;
+import duke.Storage;
+import duke.Ui;
+import duke.task.Deadline;
+import duke.task.TaskList;
+
+/**
+ * Command to create Deadline tasks.
+ */
+public class DeadlineCommand extends Command {
+ private String taskDesc;
+ private String deadline;
+
+ /**
+ * Constructor for DeadlineCommand.
+ *
+ * @param input User's input.
+ */
+ public DeadlineCommand(Input input) {
+ this.taskDesc = input.getDescription("deadline");
+ if (input.checkIfContains("/by")) {
+ this.deadline = input.getDate("deadline");
+ }
+ }
+
+ /**
+ * Creates a new Deadline object and adds it to the current list.
+ *
+ * @param ls Current list.
+ * @param ui Current Ui.
+ * @param storage Current version of the saved tasks in the hard disk.
+ * @throws DukeException If Deadline object is invalid due to invalid input.
+ */
+ @Override
+ public String execute(TaskList ls, Ui ui, Storage storage) throws DukeException {
+ Deadline dTask = new Deadline(taskDesc, deadline);
+ ls.addTask(dTask);
+ storage.rewriteFile(ls);
+ return ui.addTaskToList(dTask, ls.getSize());
+ }
+
+ /**
+ * Signals to the system that the command is not an exit command.
+ *
+ * @return False.
+ */
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+
+}
diff --git a/src/main/java/duke/command/DeleteCommand.java b/src/main/java/duke/command/DeleteCommand.java
new file mode 100644
index 0000000000..2dde9ea12f
--- /dev/null
+++ b/src/main/java/duke/command/DeleteCommand.java
@@ -0,0 +1,72 @@
+package duke.command;
+import java.util.ArrayList;
+
+import duke.DukeException;
+import duke.Input;
+import duke.Storage;
+import duke.Ui;
+import duke.task.Task;
+import duke.task.TaskList;
+
+/**
+ * Command to delete tasks.
+ */
+public class DeleteCommand extends Command {
+ private Input input;
+ private ArrayList taskNumberList;
+
+ /**
+ * Constructor for DeleteCommand.
+ *
+ * @param input User input.
+ * @throws DukeException If input is invalid.
+ */
+ public DeleteCommand(Input input) throws DukeException {
+ this.input = input;
+ if (input.hasCommandWordOnly("delete")) {
+ throw new DukeException("A number must follow after the command word 'delete'.");
+ }
+ }
+
+ /**
+ * Removes task from current list.
+ *
+ * @param ls Current list.
+ * @param ui Current Ui.
+ * @param storage Current version of the saved tasks in the hard disk.
+ * @throws DukeException If input is invalid.
+ */
+ @Override
+ public String execute(TaskList ls, Ui ui, Storage storage) throws DukeException {
+
+ try {
+ this.taskNumberList = input.getIndexArray("delete", ls.getSize());
+ } catch (NumberFormatException e) {
+ throw new DukeException("OOPS! Please enter a valid task number.");
+ }
+
+ TaskList tasksToBeRemoved = new TaskList();
+ for (int taskNumber : this.taskNumberList) {
+ Task task = ls.getTask(taskNumber);
+ tasksToBeRemoved.addTask(task);
+ }
+
+ for (int i = 0; i < tasksToBeRemoved.getSize(); i++) {
+ Task task = tasksToBeRemoved.getTask(i);
+ ls.removeTask(task);
+ storage.rewriteFile(ls);
+ }
+
+ return ui.removeTaskFromList(tasksToBeRemoved, ls.getSize());
+ }
+
+ /**
+ * Signals to the system that the command is not an exit command.
+ *
+ * @return False.
+ */
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+}
diff --git a/src/main/java/duke/command/DoneCommand.java b/src/main/java/duke/command/DoneCommand.java
new file mode 100644
index 0000000000..3616ceebf8
--- /dev/null
+++ b/src/main/java/duke/command/DoneCommand.java
@@ -0,0 +1,73 @@
+package duke.command;
+import java.util.ArrayList;
+
+import duke.DukeException;
+import duke.Input;
+import duke.Storage;
+import duke.Ui;
+import duke.task.Task;
+import duke.task.TaskList;
+
+/**
+ * Command to mark tasks as done.
+ */
+public class DoneCommand extends Command {
+ private Input input;
+ private ArrayList taskNumberList;
+
+ /**
+ * Constructor for DoneCommand.
+ *
+ * @param input User input.
+ * @throws DukeException If input is invalid.
+ */
+ public DoneCommand(Input input) throws DukeException {
+ this.input = input;
+ if (input.hasCommandWordOnly("done")) {
+ throw new DukeException("A number must follow after the command word 'done'.");
+ }
+ }
+
+ /**
+ * Marks item as done.
+ *
+ * @param ls Current list.
+ * @param ui Current Ui.
+ * @param storage Current version of the saved tasks in the hard disk.
+ * @throws DukeException If input is invalid.
+ */
+ @Override
+ public String execute(TaskList ls, Ui ui, Storage storage) throws DukeException {
+
+ try {
+ this.taskNumberList = input.getIndexArray("done", ls.getSize());
+ } catch (NumberFormatException e) {
+ throw new DukeException("OOPS! Please enter a valid task number.");
+ }
+
+ TaskList tasksToBeSetAsDone = new TaskList();
+
+ for (int taskNumber : this.taskNumberList) {
+ Task task = ls.getTask(taskNumber);
+ tasksToBeSetAsDone.addTask(task);
+ }
+
+ for (int i = 0; i < tasksToBeSetAsDone.getSize(); i++) {
+ Task task = tasksToBeSetAsDone.getTask(i);
+ task.setDone();
+ storage.rewriteFile(ls);
+ }
+
+ return ui.setTaskAsDone(tasksToBeSetAsDone);
+ }
+
+ /**
+ * Signals to the system that the command is not an exit command.
+ *
+ * @return False.
+ */
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+}
diff --git a/src/main/java/duke/command/ErrorCommand.java b/src/main/java/duke/command/ErrorCommand.java
new file mode 100644
index 0000000000..abe4939721
--- /dev/null
+++ b/src/main/java/duke/command/ErrorCommand.java
@@ -0,0 +1,34 @@
+package duke.command;
+import duke.DukeException;
+import duke.Storage;
+import duke.Ui;
+import duke.task.TaskList;
+
+/**
+ * Command to throw an DukeException error is the user input is not a valid command.
+ */
+public class ErrorCommand extends Command {
+
+ /**
+ * Throws a DukeException error if the user input is not a valid command.
+ *
+ * @param ls Current list.
+ * @param ui Current Ui.
+ * @param storage Current version of the saved tasks in the hard disk.
+ * @throws DukeException If user input is not a valid command.
+ */
+ @Override
+ public String execute(TaskList ls, Ui ui, Storage storage) throws DukeException {
+ throw new DukeException("☹ OOPS!!! I'm sorry, but I don't know what that means :-(");
+ }
+
+ /**
+ * Signals to the system that the command is not an exit command.
+ *
+ * @return False.
+ */
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+}
diff --git a/src/main/java/duke/command/EventCommand.java b/src/main/java/duke/command/EventCommand.java
new file mode 100644
index 0000000000..78642d1d37
--- /dev/null
+++ b/src/main/java/duke/command/EventCommand.java
@@ -0,0 +1,55 @@
+package duke.command;
+import duke.DukeException;
+import duke.Input;
+import duke.Storage;
+import duke.Ui;
+import duke.task.Event;
+import duke.task.TaskList;
+
+/**
+ * Command to create Event tasks.
+ */
+public class EventCommand extends Command {
+ private String taskDesc = "";
+ private String eventDate = "";
+
+ /**
+ * Constructor for EventCommand.
+ *
+ * @param input User input.
+ */
+ public EventCommand(Input input) throws DukeException {
+ this.taskDesc = input.getDescription("event");
+ if (input.checkIfContains("/at")) {
+ this.eventDate = input.getDate("event");
+ } else {
+ throw new DukeException("☹ OOPS!!! Please use the /at command to include the time of event.");
+ }
+ }
+
+ /**
+ * Creates a new Event object and adds it to the current list.
+ *
+ * @param ls Current list.
+ * @param ui Current Ui.
+ * @param storage Current version of the saved tasks in the hard disk.
+ * @throws DukeException If user input is invalid.
+ */
+ @Override
+ public String execute(TaskList ls, Ui ui, Storage storage) throws DukeException {
+ Event eTask = new Event(taskDesc, eventDate);
+ ls.addTask(eTask);
+ storage.rewriteFile(ls);
+ return ui.addTaskToList(eTask, ls.getSize());
+ }
+
+ /**
+ * Signals to the system that the command is not an exit command.
+ *
+ * @return False.
+ */
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+}
diff --git a/src/main/java/duke/command/ExitCommand.java b/src/main/java/duke/command/ExitCommand.java
new file mode 100644
index 0000000000..5243f7ef25
--- /dev/null
+++ b/src/main/java/duke/command/ExitCommand.java
@@ -0,0 +1,35 @@
+package duke.command;
+import duke.DukeException;
+import duke.Storage;
+import duke.Ui;
+import duke.task.TaskList;
+
+/**
+ * Command to signal for the system to exit.
+ */
+public class ExitCommand extends Command {
+
+ /**
+ * Exits the program.
+ *
+ * @param ls Current list.
+ * @param ui Current Ui.
+ * @param storage Current version of the saved tasks in the hard disk.
+ * @throws DukeException If user input is invalid.
+ */
+ @Override
+ public String execute(TaskList ls, Ui ui, Storage storage) throws DukeException {
+ System.exit(0);
+ return ui.goodbye();
+ }
+
+ /**
+ * Signals to the system that the command is an exit command.
+ *
+ * @return True.
+ */
+ @Override
+ public boolean isExit() {
+ return true;
+ }
+}
diff --git a/src/main/java/duke/command/FindCommand.java b/src/main/java/duke/command/FindCommand.java
new file mode 100644
index 0000000000..41a3bf6a20
--- /dev/null
+++ b/src/main/java/duke/command/FindCommand.java
@@ -0,0 +1,47 @@
+package duke.command;
+import duke.DukeException;
+import duke.Input;
+import duke.Storage;
+import duke.Ui;
+import duke.task.Find;
+import duke.task.TaskList;
+
+/**
+ * Command to signal the system to search for given keyword.
+ */
+public class FindCommand extends Command {
+ private String keyword;
+
+ /**
+ * Constructor for FindCommand.
+ *
+ * @param input The user input.
+ */
+ public FindCommand(Input input) {
+ this.keyword = input.getKeyword();
+ }
+
+ /**
+ * Searches for the keyword in the current tasks.
+ *
+ * @param ls Current TaskList.
+ * @param ui Current Ui.
+ * @param storage Current version of the saved tasks in the hard disk.
+ * @throws DukeException If user input is invalid
+ */
+ @Override
+ public String execute(TaskList ls, Ui ui, Storage storage) throws DukeException {
+ Find f = new Find(keyword, ls);
+ return f.findWord();
+ }
+
+ /**
+ * Signals to the system that the command is not an exit command.
+ *
+ * @return False.
+ */
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+}
diff --git a/src/main/java/duke/command/ListCommand.java b/src/main/java/duke/command/ListCommand.java
new file mode 100644
index 0000000000..61b40e284f
--- /dev/null
+++ b/src/main/java/duke/command/ListCommand.java
@@ -0,0 +1,35 @@
+package duke.command;
+import duke.DukeException;
+import duke.Storage;
+import duke.Ui;
+import duke.task.TaskList;
+
+/**
+ * Command to print current list.
+ */
+public class ListCommand extends Command {
+
+ /**
+ * Prints current list.
+ *
+ * @param ls Current list.
+ * @param ui Current Ui.
+ * @param storage Current version of the saved tasks in the hard disk.
+ * @throws DukeException If user input is invalid.
+ */
+ @Override
+ public String execute(TaskList ls, Ui ui, Storage storage) throws DukeException {
+ return ui.printTaskList(ls);
+ }
+
+ /**
+ * Signals to the system that the command is not an exit command.
+ *
+ * @return False.
+ */
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+
+}
diff --git a/src/main/java/duke/command/TodoCommand.java b/src/main/java/duke/command/TodoCommand.java
new file mode 100644
index 0000000000..5d6862a7b1
--- /dev/null
+++ b/src/main/java/duke/command/TodoCommand.java
@@ -0,0 +1,49 @@
+package duke.command;
+import duke.DukeException;
+import duke.Input;
+import duke.Storage;
+import duke.Ui;
+import duke.task.TaskList;
+import duke.task.Todo;
+
+/**
+ * Command to create Todo tasks.
+ */
+public class TodoCommand extends Command {
+ private String taskDesc;
+
+ /**
+ * Constructor for TodoCommand.
+ *
+ * @param input User input.
+ */
+ public TodoCommand(Input input) {
+ this.taskDesc = input.getDescription("todo");
+ }
+
+ /**
+ * Creates a new Todo object and adds it to the current list.
+ *
+ * @param ls Current list.
+ * @param ui Current Ui.
+ * @param storage Current version of the saved tasks in the hard disk.
+ * @throws DukeException If the user input is invalid.
+ */
+ @Override
+ public String execute(TaskList ls, Ui ui, Storage storage) throws DukeException {
+ Todo tTask = new Todo(taskDesc);
+ ls.addTask(tTask);
+ storage.rewriteFile(ls);
+ return ui.addTaskToList(tTask, ls.getSize());
+ }
+
+ /**
+ * Signals to the system that the command is not an exit command.
+ *
+ * @return False.
+ */
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+}
diff --git a/src/main/java/duke/task/Deadline.java b/src/main/java/duke/task/Deadline.java
new file mode 100644
index 0000000000..8389acd474
--- /dev/null
+++ b/src/main/java/duke/task/Deadline.java
@@ -0,0 +1,56 @@
+package duke.task;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+
+import duke.DukeException;
+
+/**
+ * Class that encapsulates Deadline tasks.
+ */
+public class Deadline extends Task {
+
+ protected String deadline;
+
+ /**
+ * Constructor for Deadline.
+ *
+ * @param description Task description.
+ * @param deadline Date of deadline.
+ * @throws DukeException If user input is not valid.
+ */
+ public Deadline(String description, String deadline) throws DukeException {
+ super(description);
+ if (deadline.equals("") || deadline.equals(" ")) {
+ throw new DukeException("☹ OOPS!!! The deadline of this task must be indicated.");
+ } else {
+ try {
+ LocalDate localDate = LocalDate.parse(deadline);
+ this.deadline = localDate.format(DateTimeFormatter.ofPattern("MMM dd yyyy"));
+ } catch (DateTimeParseException e) {
+ throw new DukeException("duke.task.Deadline should be in a yyyy-mm-dd format.");
+ }
+ }
+ }
+
+ /**
+ * Returns a String representation of the Deadline task.
+ *
+ * @return String representation of the Deadline task.
+ */
+ @Override
+ public String toString() {
+ return "\t[D]" + super.toString() + " (by: " + this.deadline + ")";
+ }
+
+ /**
+ * Returns a String representation of the date of the deadline.
+ *
+ * @return A String representation of the date of the deadline.
+ */
+ @Override
+ public String additionalDates() {
+ return this.deadline;
+ }
+}
diff --git a/src/main/java/duke/task/Event.java b/src/main/java/duke/task/Event.java
new file mode 100644
index 0000000000..13a37ffcdb
--- /dev/null
+++ b/src/main/java/duke/task/Event.java
@@ -0,0 +1,56 @@
+package duke.task;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+
+import duke.DukeException;
+
+/**
+ * Class that encapsulates Event tasks.
+ */
+public class Event extends Task {
+
+ protected String at;
+
+ /**
+ * Constructor for Event.
+ *
+ * @param description Description of Event task.
+ * @param at Date of event.
+ * @throws DukeException If user inpyt is invalid.
+ */
+ public Event(String description, String at) throws DukeException {
+ super(description);
+ if (at.equals("") || at.equals(" ")) {
+ throw new DukeException("☹ OOPS!!! The time of the event must be indicated.");
+ } else {
+ try {
+ LocalDate localDate = LocalDate.parse(at);
+ this.at = localDate.format(DateTimeFormatter.ofPattern("MMM dd yyyy"));
+ } catch (DateTimeParseException e) {
+ throw new DukeException("duke.task.Event date should be in a yyyy-mm-dd format.");
+ }
+ }
+ }
+
+ /**
+ * Returns a String representation of the Event task.
+ *
+ * @return A String representation of the Event task
+ */
+ @Override
+ public String toString() {
+ return "\t[E]" + super.toString() + " (at: " + at + ")";
+ }
+
+ /**
+ * Returns a String representation of the date of the event.
+ *
+ * @return A String representation of the date of the event.
+ */
+ @Override
+ public String additionalDates() {
+ return this.at;
+ }
+}
diff --git a/src/main/java/duke/task/Find.java b/src/main/java/duke/task/Find.java
new file mode 100644
index 0000000000..56f9827a20
--- /dev/null
+++ b/src/main/java/duke/task/Find.java
@@ -0,0 +1,49 @@
+package duke.task;
+
+import duke.Ui;
+
+/**
+ * Class that encapsulates searches for keywords among current Tasks.
+ */
+public class Find {
+ private boolean isFound;
+ private String word;
+ private TaskList ls;
+ private String result;
+ private Ui ui = new Ui();
+
+ /**
+ * Constructor for Find.
+ *
+ * @param word Keyword.
+ * @param ls Current TaskList.
+ */
+ public Find(String word, TaskList ls) {
+ this.isFound = false;
+ this.word = word;
+ this.ls = ls;
+ }
+
+ /**
+ * Prints out the list of Tasks that include the keyword.
+ */
+ public String findWord() {
+ if (!this.isFound) {
+ return this.result = ui.printListWithKeyword(ls, word, this);
+ }
+
+ if (this.result.isEmpty()) {
+ return ui.noResultsFound(word);
+ } else {
+ return this.result;
+ }
+ }
+
+ /**
+ * Signals that there are Tasks with the keyword by setting the isFound boolean to true.
+ */
+ public void setFound() {
+ this.isFound = true;
+ }
+
+}
diff --git a/src/main/java/duke/task/Task.java b/src/main/java/duke/task/Task.java
new file mode 100644
index 0000000000..523ae39347
--- /dev/null
+++ b/src/main/java/duke/task/Task.java
@@ -0,0 +1,79 @@
+package duke.task;
+import duke.DukeException;
+import duke.Ui;
+
+/**
+ * Class that encapsulates Tasks.
+ */
+public class Task {
+ protected String description;
+ protected boolean isDone;
+ protected Ui ui = new Ui();
+
+ /**
+ * Constructor for Task.
+ *
+ * @param description Task description.
+ */
+ public Task(String description) throws DukeException {
+ if (description.equals("") || description.equals(" ")) {
+ throw new DukeException("☹ OOPS!!! The description of an event cannot be empty.");
+ } else {
+ this.description = description.substring(1);
+ }
+ this.isDone = false;
+ }
+
+ /**
+ * Represents undone tasks with a space and done tasks with an "X".
+ *
+ * @return " " for undone tasks and "X" for done tasks.
+ */
+ public String getStatusIcon() {
+ return ui.taskStatusIcon(isDone);
+ }
+
+ /**
+ * Returns a String representation of the task.
+ *
+ * @return A String representation of the task.
+ */
+ @Override
+ public String toString() {
+ return ("[" + getStatusIcon() + "] " + description);
+ }
+
+ /**
+ * Returns the task description.
+ *
+ * @return Task description
+ */
+ public String getDesc() {
+ return this.description;
+ }
+
+ /**
+ * Sets tasks as done by setting the isDone boolean to true.
+ */
+ public void setDone() {
+ this.isDone = true;
+ }
+
+ /**
+ * Checks the status of the task by checking the status of the isDone boolean.
+ *
+ * @return True of the task is done, false if the task is undone.
+ */
+ public boolean isDone() {
+ return this.isDone;
+ }
+
+ /**
+ * Returns additional dates (if any).
+ *
+ * @return Additional dates.
+ */
+ public String additionalDates() {
+ return "";
+ }
+}
diff --git a/src/main/java/duke/task/TaskList.java b/src/main/java/duke/task/TaskList.java
new file mode 100644
index 0000000000..790ef8634d
--- /dev/null
+++ b/src/main/java/duke/task/TaskList.java
@@ -0,0 +1,54 @@
+package duke.task;
+import java.util.ArrayList;
+
+/**
+ * Class that encapsulates Tasklists.
+ */
+public class TaskList {
+ private ArrayList tasklist;
+
+ /**
+ * Constructor for TaskList.
+ */
+ public TaskList() {
+ this.tasklist = new ArrayList();
+ }
+
+ /**
+ * Adds task to current TaskList.
+ *
+ * @param task Task to be added to current TaskList.
+ */
+ public void addTask(Task task) {
+ this.tasklist.add(task);
+ }
+
+ /**
+ * Removes task from current TaskList.
+ *
+ * @param task Task to be removed from current TaskList.
+ */
+ public void removeTask(Task task) {
+ this.tasklist.remove(task);
+ }
+
+ /**
+ * Retrieves task from current TaskList by their index.
+ *
+ * @param index Index of the task to be retrieved.
+ * @return Task at the index.
+ */
+ public Task getTask(int index) {
+ return this.tasklist.get(index);
+ }
+
+ /**
+ * Returns the number of items in the current TaskList.
+ *
+ * @return Number of items in the current TaskList.
+ */
+ public int getSize() {
+ return this.tasklist.size();
+ }
+
+}
diff --git a/src/main/java/duke/task/Todo.java b/src/main/java/duke/task/Todo.java
new file mode 100644
index 0000000000..b0e18c97ac
--- /dev/null
+++ b/src/main/java/duke/task/Todo.java
@@ -0,0 +1,28 @@
+package duke.task;
+import duke.DukeException;
+
+/**
+ * Class that encapsulates Todo tasks.
+ */
+public class Todo extends Task {
+
+ /**
+ * Constructor for Todo.
+ *
+ * @param description Task description.
+ * @throws DukeException If user input is invalid.
+ */
+ public Todo(String description) throws DukeException {
+ super(description);
+ }
+
+ /**
+ * Returns a String representation of the Todo task.
+ *
+ * @return A String representation of the Todo task.
+ */
+ @Override
+ public String toString() {
+ return ("\t[T]" + super.toString());
+ }
+}
diff --git a/src/main/resources/images/DaDuke.jpg b/src/main/resources/images/DaDuke.jpg
new file mode 100644
index 0000000000..f932fe9ba1
Binary files /dev/null and b/src/main/resources/images/DaDuke.jpg differ
diff --git a/src/main/resources/images/DaUser.jpg b/src/main/resources/images/DaUser.jpg
new file mode 100644
index 0000000000..c46070e114
Binary files /dev/null and b/src/main/resources/images/DaUser.jpg differ
diff --git a/src/main/resources/view/DialogBox.fxml b/src/main/resources/view/DialogBox.fxml
new file mode 100644
index 0000000000..e47ebc5fe5
--- /dev/null
+++ b/src/main/resources/view/DialogBox.fxml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml
new file mode 100644
index 0000000000..832f84382e
--- /dev/null
+++ b/src/main/resources/view/MainWindow.fxml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/java/duke/task/DeadlineTest.java b/src/test/java/duke/task/DeadlineTest.java
new file mode 100644
index 0000000000..2b79ab30e6
--- /dev/null
+++ b/src/test/java/duke/task/DeadlineTest.java
@@ -0,0 +1,37 @@
+package duke.task;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+import duke.DukeException;
+
+
+/**
+ * Tests the functionality of the Deadline class.
+ */
+public class DeadlineTest {
+
+ /**
+ * Tests the creating of a Deadline task.
+ *
+ * @throws DukeException If the user input is invalid.
+ */
+ @Test
+ public void testCreateDeadline() throws DukeException {
+ Task dTask = new Deadline("read book", "2001-07-07");
+ assertEquals("\t[D][ ] read book (by: Jul 07 2001)", dTask.toString());
+ }
+
+ /**
+ * Tests the markAsDone() method in the Deadline class.
+ *
+ * @throws DukeException If the user input is invalid.
+ */
+ @Test
+ public void testMarkAsDone() throws DukeException {
+ Task dTask = new Deadline("read book", "2001-07-07");
+ dTask.setDone();
+ assertEquals(true, dTask.isDone());
+ }
+}
diff --git a/src/test/java/duke/task/TaskListTest.java b/src/test/java/duke/task/TaskListTest.java
new file mode 100644
index 0000000000..372d1bec88
--- /dev/null
+++ b/src/test/java/duke/task/TaskListTest.java
@@ -0,0 +1,41 @@
+package duke.task;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+import duke.DukeException;
+
+/**
+ * Tests the functionality of the TaskList class.
+ */
+public class TaskListTest {
+
+ /**
+ * Tests the addTask(Task task) method in the TaskList class.
+ *
+ * @throws DukeException If the user input is invalid.
+ */
+ @Test
+ public void testAddTask() throws DukeException {
+ Task dTask = new Deadline("read book", "2001-07-07");
+ TaskList ls = new TaskList();
+ ls.addTask(dTask);
+ assertEquals("\t[D][ ] read book (by: Jul 07 2001)", ls.getTask(0).toString());
+ }
+
+ /**
+ * Tests the getSize() method in the TaskList class.
+ *
+ * @throws DukeException If the user input is invalid.
+ */
+ @Test
+ public void testGetSize() throws DukeException {
+ Task dTask = new Deadline("read book", "2001-07-07");
+ TaskList ls = new TaskList();
+ ls.addTask(dTask);
+ ls.addTask(dTask);
+ ls.addTask(dTask);
+ assertEquals(3, ls.getSize());
+ }
+}
diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT
index 657e74f6e7..e2f4cf7f93 100644
--- a/text-ui-test/EXPECTED.TXT
+++ b/text-ui-test/EXPECTED.TXT
@@ -5,3 +5,19 @@ Hello from
| |_| | |_| | < __/
|____/ \__,_|_|\_\___|
+Hello! I'm duke.Duke
+What can I do for you?
+Got it. I've added this task:
+ [T][ ] borrow book
+Now you have 1 tasks in the list.
+Got it. I've added this task:
+ [D][ ] return book (by: Sunday)
+Now you have 2 tasks in the list.
+Got it. I've added this task:
+ [E][ ] project meeting (at: Mon 2-4pm)
+Now you have 3 tasks in the list.
+Here are the tasks in your list:
+1. [T][ ] borrow book
+2. [D][ ] return book (by: Sunday)
+3. [E][ ] project meeting (at: Mon 2-4pm)
+Bye. Hope to see you again soon!
diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt
index e69de29bb2..2c9ef2e1fe 100644
--- a/text-ui-test/input.txt
+++ b/text-ui-test/input.txt
@@ -0,0 +1,5 @@
+todo borrow book
+deadline return book /by Sunday
+event project meeting /at Mon 2-4pm
+list
+bye
diff --git a/text-ui-test/runtest.bat b/text-ui-test/runtest.bat
index 0873744649..62752b8814 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
index c9ec870033..b36790c82c
--- a/text-ui-test/runtest.sh
+++ b/text-ui-test/runtest.sh
@@ -22,12 +22,8 @@ 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
+diff ACTUAL.TXT EXPECTED.TXT
if [ $? -eq 0 ]
then
echo "Test result: PASSED"
@@ -35,4 +31,4 @@ then
else
echo "Test result: FAILED"
exit 1
-fi
\ No newline at end of file
+fi