diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml
new file mode 100644
index 0000000000..c6b7ee2b1e
--- /dev/null
+++ b/.github/workflows/gradle.yml
@@ -0,0 +1,50 @@
+name: Java CI
+
+on: [push, pull_request]
+
+jobs:
+ build:
+ strategy:
+ matrix:
+ platform: [ubuntu-latest, macos-latest, windows-latest]
+ runs-on: ${{ matrix.platform }}
+
+ steps:
+ - name: Set up repository
+ uses: actions/checkout@master
+
+ - name: Set up repository
+ uses: actions/checkout@master
+ with:
+ ref: master
+
+ - name: Merge to master
+ run: git checkout --progress --force ${{ github.sha }}
+
+ - name: Validate Gradle Wrapper
+ uses: gradle/wrapper-validation-action@v1
+
+ - name: Setup JDK 11
+ uses: actions/setup-java@v1
+ with:
+ java-version: '11'
+ java-package: jdk+fx
+
+ - name: Build and check with Gradle
+ run: ./gradlew check
+
+ - name: Perform IO redirection test (*NIX)
+ if: runner.os == 'Linux'
+ working-directory: ${{ github.workspace }}/text-ui-test
+ run: ./runtest.sh
+
+ - name: Perform IO redirection test (MacOS)
+ if: always() && runner.os == 'macOS'
+ working-directory: ${{ github.workspace }}/text-ui-test
+ run: ./runtest.sh
+
+ - name: Perform IO redirection test (Windows)
+ if: always() && runner.os == 'Windows'
+ working-directory: ${{ github.workspace }}/text-ui-test
+ shell: cmd
+ run: runtest.bat
diff --git a/README.md b/README.md
index 9d95025bce..d0047b4e52 100644
--- a/README.md
+++ b/README.md
@@ -1,26 +1,34 @@
-# Duke project template
+# Duke project
+Duke Application, made by [Stephen Tan](https://www.linkedin.com/in/stephen-tan-hin-khai/) under
+the module CS2103T 2020/21 Sem 1.
+This is a greenfield Java project. It's named after the Java mascot _Duke_.
-This is a project template for a greenfield Java project. It's named after the Java mascot _Duke_. Given below are instructions on how to use it.
+Please check [here](docs/README.md) for the User Guide
-## Setting up in Intellij
+## Pre-Requisites for use
+Install Java 11
+## Getting Started
+To execute the program, or start the Duke Chatbot:
-Prerequisites: JDK 11, update Intellij to the most recent version.
+Double click the jar file, or enter by command prompt
+```
+java -jar duke.jar
+```
+If you prefer a Command Line Interface, type:
+```
+java -jar duke.jar -cli
+```
+Then, type the help command to get help on what Duke can do.
+```
+help
+```
-1. Open Intellij (if you are not in the welcome screen, click `File` > `Close Project` to close the existing project dialog first)
-1. Set up the correct JDK version, as follows:
- 1. Click `Configure` > `Structure for New Projects` and then `Project Settings` > `Project` > `Project SDK`
- 1. If JDK 11 is listed in the drop down, select it. If it is not, click `New...` and select the directory where you installed JDK 11
- 1. Click `OK`
-1. Import the project into Intellij as follows:
- 1. Click `Open or Import`.
- 1. Select the project directory, and click `OK`
- 1. If there are any further prompts, accept the defaults.
-1. After the importing is complete, locate the `src/main/java/Duke.java` file, right-click it, and choose `Run Duke.main()`. If the setup is correct, you should see something like the below:
- ```
- Hello from
- ____ _
- | _ \ _ _| | _____
- | | | | | | | |/ / _ \
- | |_| | |_| | < __/
- |____/ \__,_|_|\_\___|
- ```
+![Duke UI](docs/Ui.png)
+
+## Acknowledgements
+In this project, I acknowledge referencing [here](https://github.com/JoeyChenSmart/ip)
+for inspiration for the functional software pattern used.
+
+Use of Gradle project Wrapper.
+
+Initial Template is taken from [Prof Damith](https://github.com/damithc)
\ No newline at end of file
diff --git a/_config.yml b/_config.yml
new file mode 100644
index 0000000000..9da9a0291e
--- /dev/null
+++ b/_config.yml
@@ -0,0 +1 @@
+theme: jekyll-theme-dinky
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000000..b80ce25da9
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,60 @@
+plugins {
+ id 'java'
+ id 'application'
+ id 'checkstyle'
+ id 'com.github.johnrengelman.shadow' version '5.1.0'
+}
+
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ String javaFxVersion = '11.0.1'
+ 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'
+ 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.ChatbotApplication"
+}
+
+shadowJar {
+ archiveBaseName = "duke"
+ archiveClassifier = null
+}
+
+checkstyle {
+ toolVersion = '8.29'
+}
+
+run{
+ enableAssertions(true)
+ standardInput = System.in
+}
diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml
new file mode 100644
index 0000000000..bb15f3a86b
--- /dev/null
+++ b/config/checkstyle/checkstyle.xml
@@ -0,0 +1,403 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/config/checkstyle/suppressions.xml b/config/checkstyle/suppressions.xml
new file mode 100644
index 0000000000..39efb6e4ac
--- /dev/null
+++ b/config/checkstyle/suppressions.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
diff --git a/config/travis/check-eof-newline.sh b/config/travis/check-eof-newline.sh
new file mode 100755
index 0000000000..b771f3988d
--- /dev/null
+++ b/config/travis/check-eof-newline.sh
@@ -0,0 +1,18 @@
+#!/bin/sh
+# Checks that all text files end with a newline.
+
+ret=0
+
+# Preserve filename with spaces by only splitting on newlines.
+IFS='
+'
+
+for filename in $(git grep --cached -I -l -e '' -- ':/'); do
+ if [ "$(tail -c 1 "./$filename")" != '' ]; then
+ line="$(wc -l "./$filename" | cut -d' ' -f1)"
+ echo "ERROR:$filename:$line: no newline at EOF."
+ ret=1
+ fi
+done
+
+exit $ret
diff --git a/config/travis/check-line-endings.sh b/config/travis/check-line-endings.sh
new file mode 100755
index 0000000000..3de67ea87f
--- /dev/null
+++ b/config/travis/check-line-endings.sh
@@ -0,0 +1,19 @@
+#!/bin/sh
+# Checks for prohibited line endings.
+# Prohibited line endings: \r\n
+
+git grep --cached -I -n --no-color -P '\r$' -- ':/' |
+awk '
+ BEGIN {
+ FS = ":"
+ OFS = ":"
+ ret = 0
+ }
+ {
+ ret = 1
+ print "ERROR", $1, $2, " prohibited \\r\\n line ending, use \\n instead."
+ }
+ END {
+ exit ret
+ }
+'
diff --git a/config/travis/check-trailing-whitespace.sh b/config/travis/check-trailing-whitespace.sh
new file mode 100755
index 0000000000..33841caa81
--- /dev/null
+++ b/config/travis/check-trailing-whitespace.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+# Checks for trailing whitespace
+
+git grep --cached -I -n --no-color -P '[ \t]+$' -- ':/' |
+awk '
+ BEGIN {
+ FS = ":"
+ OFS = ":"
+ ret = 0
+ }
+ {
+ # Only warn for markdown files (*.md) to accomodate text editors
+ # which do not properly handle trailing whitespace.
+ # (e.g. GitHub web editor)
+ if ($1 ~ /\.md$/) {
+ severity = "WARN"
+ } else {
+ severity = "ERROR"
+ ret = 1
+ }
+ print severity, $1, $2, " trailing whitespace."
+ }
+ END {
+ exit ret
+ }
+'
diff --git a/config/travis/run-checks.sh b/config/travis/run-checks.sh
new file mode 100755
index 0000000000..7aad1e9622
--- /dev/null
+++ b/config/travis/run-checks.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+# Runs all check-* scripts, and returns a non-zero exit code if any of them fail.
+
+dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) &&
+ret=0 &&
+for checkscript in "$dir"/check-*; do
+ if ! "$checkscript"; then
+ ret=1
+ fi
+done
+exit $ret
diff --git a/docs/README.md b/docs/README.md
index fd44069597..69a0847fba 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,20 +1,136 @@
# User Guide
+![Duke UI](Ui.png)
+This is a user guide for the usage of Duke Application, made by
+[Stephen Tan](https://www.linkedin.com/in/stephen-tan-hin-khai/) under
+the module CS2103T 2020/21 Sem 1.
-## Features
+Duke Chatbot 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.
-### Feature 1
-Description of feature.
+## Pre-Requisites for use
+Install Java 11
+## Getting Started
+To execute the program, or start the Duke Chatbot:
+Double click the jar file, or enter by command prompt
+```
+java -jar duke.jar
+```
+If you prefer a Command Line Interface, type:
+```
+java -jar duke.jar -cli
+```
+Then, type the help command to get help on what Duke can do.
+```
+help
+```
-## Usage
+## Features supported
+Scope of features supported.
+1. Create new tasks
+2. View a list of tasks
+3. Use regex to find tasks by matching substring
+4. Get help about commands from Duke
+5. Update a task as done
+6. Delete tasks by given index
+7. GUI or CLI interface
+8. User Feedback for bad input
+9. Saves and loads tasks upon execution of program
-### `Keyword` - Describe action
+## Command Usage
-Describe action and its outcome.
-
-Example of usage:
-
-`keyword (optional arguments)`
-
-Expected outcome:
-
-`outcome`
+### Adding and Scheduling Tasks:
+You can schedule tasks on the chatbot.
+There are up to 3 kinds of tasks supported.
+#### 1. Todo tasks
+##### command:
+```
+todo
+```
+Todo tasks have no deadline and thus have no requirements on completion
+##### Expected outcome:
+In the system will register a `todo task`
+##### Upon wrong input:
+Duke will indicate a bad command input was given and return a specific
+error relating to the bad input
+#### 2. Deadline Tasks
+##### command:
+```
+deadline -by dd-MM-YYYY
+```
+Deadline tasks have a deadline and will indicate how close the given date is
+from the deadline using the current time. For deadline tasks, there is limited
+level of autocorrection Hence you can just specify a day > the current day's date
+Eg: if date is 12 September 2020
+```
+deadline example task -by 13
+```
+##### Expected outcome:
+In the system will register a `deadline due by 14-9-2020`
+##### Upon bad input
+Duke will indicate that datetime is invalid, or an invalid input was given
+to the description field
+#### 3. Event Tasks
+##### command:
+```
+event -at dd-MM-YYYY
+```
+Event tasks have a date of event and will indicate how close the given date is from the
+event using the current time. For event tasks, there is limited level of autocorrection
+Hence you can just specify a day > the current day's date
+Eg: if date is 12 September 2020
+```
+event example task -by 13
+```
+##### Expected outcome:
+In the system will register a `event due by 14-9-2020`
+##### Upon bad input
+Duke will indicate that datetime is invalid, or an invalid input was given
+to the description field
+### Listing Tasks:
+You can list all tasks registered in the system with the command
+```
+list
+```
+### Search for Task:
+You can find all tasks registered in the system by description using a
+ substring or a regex expression.
+```
+find
+```
+### Mark task done:
+You can mark a task done by using the index of the task referenced from before.
+```
+done
+```
+##### Expected outcome:
+Duke will mark the task as done
+##### Upon bad input
+Duke will indicate that the input is either out-of-index or not a number.
+### Delete Task
+You can delete a task by using the index of the task referenced from before.
+```
+delete
+```
+##### Expected outcome:
+Duke will delete the task
+##### Upon bad input
+Duke will indicate that the input is either out-of-index or not a number.
+### Help
+You can get help about Duke by issuing the command
+```
+help
+```
+You can also get specific help about certain commands through adding the
+specific command itself.
+```
+help
+```
+##### Expected outcome:
+Duke will respond with a help message.
+##### Upon wrong input:
+Duke will respond with the all help Duke message.
+### End Application
+You can end the execution of the application by issuing the command `bye`
+```
+bye
+```
\ No newline at end of file
diff --git a/docs/Ui.png b/docs/Ui.png
new file mode 100644
index 0000000000..3b3b13fdff
Binary files /dev/null and b/docs/Ui.png differ
diff --git a/docs/_config.yml b/docs/_config.yml
new file mode 100644
index 0000000000..9da9a0291e
--- /dev/null
+++ b/docs/_config.yml
@@ -0,0 +1 @@
+theme: jekyll-theme-dinky
\ No newline at end of file
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..68dde2fd46
--- /dev/null
+++ b/src/main/java/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Main-Class: duke.ChatbotApplication
+
diff --git a/src/main/java/duke/ChatbotApplication.java b/src/main/java/duke/ChatbotApplication.java
new file mode 100644
index 0000000000..3d1a001706
--- /dev/null
+++ b/src/main/java/duke/ChatbotApplication.java
@@ -0,0 +1,38 @@
+package duke;
+
+import duke.ui.CommandLineInterface;
+import duke.ui.MainLauncher;
+import duke.ui.UserInterface;
+import javafx.application.Application;
+
+
+/**
+ * Front End Facing Script for the UI of duke.ChatbotApplication
+ */
+public class ChatbotApplication {
+ private final Duke dukeProgram;
+ /**
+ * Constructor class of the duke.ChatbotApplication
+ * @param ui the path to read a file from.
+ */
+ public ChatbotApplication(UserInterface ui) {
+ dukeProgram = new Duke(ui);
+ }
+
+ /**
+ * Entry point for application
+ * @param args args
+ */
+ public static void main(String[] args) {
+ //args[0] = "cli";
+ if (args.length > 0 && args[0].equals("-cli")) {
+ UserInterface ui = new CommandLineInterface();
+ Duke duke = new Duke(ui);
+ while (ui.isRunning()) {
+ duke.nextIteration();
+ }
+ } else {
+ Application.launch(MainLauncher.class, args);
+ }
+ }
+}
diff --git a/src/main/java/duke/Duke.java b/src/main/java/duke/Duke.java
new file mode 100644
index 0000000000..51b6004f79
--- /dev/null
+++ b/src/main/java/duke/Duke.java
@@ -0,0 +1,82 @@
+package duke;
+
+import java.util.Optional;
+import java.util.regex.Matcher;
+
+import duke.command.CommandEnums;
+import duke.exceptions.DukeCommandException;
+import duke.exceptions.DukeException;
+import duke.exceptions.DukeIoException;
+import duke.tasks.TaskManager;
+import duke.tasks.TextParser;
+import duke.ui.UserInterface;
+
+
+
+/**
+ * Backend Object Class for the duke.Duke Chatbot Interface
+ */
+public class Duke {
+ private static final TextParser textParser = new TextParser();
+ private final TaskManager taskManager;
+ private final UserInterface ui;
+ /**
+ * Constructor for the duke.Duke Chatbot, if is old initialisation, will read from txt file
+ * Eles it will initialise a new TaskManager class
+ * @param ui The user interface to use.
+ */
+
+ public Duke(UserInterface ui) {
+ assert ui != null : "ui will never be null";
+ TaskManager list1;
+ try {
+ list1 = new TaskManager(System.getProperty("user.dir"));
+ } catch (DukeIoException e) {
+ list1 = new TaskManager(System.getProperty("user.dir"), true);
+ }
+ taskManager = list1;
+ this.ui = ui;
+ this.ui.start("Friend");
+ }
+ private void parseRun(String input) throws DukeException {
+ for (CommandEnums cmd : CommandEnums.values()) {
+ Optional maybeMatcher = cmd.matcher(input);
+ if (maybeMatcher.isEmpty()) {
+ continue;
+ }
+ Matcher match = maybeMatcher.get();
+ if (!match.find()) {
+ throw cmd.commandError(input);
+ }
+ int count = match.groupCount();
+ String[] arguments = new String[count];
+ for (int i = 1; i <= count; i++) {
+ arguments[i - 1] = match.group(i);
+ }
+ cmd.execute(taskManager, ui, arguments);
+ return;
+ }
+ throw new DukeCommandException(input);
+ }
+
+ /**
+ * Runs the Next Iteration of Duke
+ * To be invoked to iterate the continuation of Duke Application flow.
+ */
+ public void nextIteration() {
+ String input = textParser.cleanInput(ui.nextLine());
+ try {
+ parseRun(input);
+ } catch (DukeException e) {
+ ui.systemMessage(e.toString());
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "Duke{"
+ + "taskManager=" + taskManager
+ + ", ui=" + ui
+ + '}';
+ }
+}
diff --git a/src/main/java/duke/command/CommandEnums.java b/src/main/java/duke/command/CommandEnums.java
new file mode 100644
index 0000000000..471f62b8dc
--- /dev/null
+++ b/src/main/java/duke/command/CommandEnums.java
@@ -0,0 +1,72 @@
+package duke.command;
+
+import java.util.Optional;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import duke.exceptions.DukeCommandException;
+import duke.exceptions.DukeException;
+import duke.tasks.TaskManager;
+import duke.ui.UserInterface;
+
+public enum CommandEnums {
+ TODO(CommandList.TODO , "(.*)" , "todo"),
+ DEADLINE(CommandList.DEADLINE, "(.*) -by (.*)", "deadline"),
+ EVENT(CommandList.EVENT , "(.*) -at (.*)" , "event"),
+ HELP(CommandList.HELP, "(.*)", "help"),
+ LIST(CommandList.LIST, "" , "list"),
+ FIND(CommandList.FIND, "(.*)" , "find"),
+ DELETE(CommandList.DELETE , "(\\d+)" , "delete"),
+ DONE(CommandList.DONE, "(\\d+)" , "done"),
+ BYE(CommandList.BYE , "" , "bye");
+ private final CommandExecuter executer;
+ private final Pattern format;
+ private final String name;
+
+ /**
+ * Constructs a CommandEnums which contains the static functional interface for interacting in application
+ * @param executer Functional interface for a specific command
+ * @param formatString Regex format for a command
+ * @param name name of command to match.
+ */
+ CommandEnums(CommandExecuter executer, String formatString, String name) {
+ this.executer = executer;
+ format = Pattern.compile(formatString);
+ this.name = name;
+ }
+
+ /**
+ * Parse string output to return a optional.of(regex.Matcher) if there is a match
+ * Otherwise, returns Optional.empty(). Matcher is configured to return the arguments to the Command
+ * in its Capture Groups.
+ * @param rawInput The user input
+ * @return Optional regex Match if exists, else empty
+ */
+ public Optional matcher(String rawInput) {
+ if (!rawInput.startsWith(name)) {
+ return Optional.empty();
+ }
+ Matcher matcher = format.matcher(rawInput.substring(name.length()).trim());
+ return Optional.of(matcher);
+ }
+
+ /**
+ * Executes the task using the taskmanager and user interface
+ * @param taskManager stored taskManager in parser
+ * @param ui User Interface
+ * @param args String argument
+ * @throws DukeException an Exception in the internal commandlist
+ */
+ public void execute(TaskManager taskManager, UserInterface ui, String[] args) throws DukeException {
+ executer.run(taskManager, ui , args);
+ }
+
+ /**
+ * Input given that is known to be wrong
+ * @param input userinput
+ * @return Error Message
+ */
+ public DukeCommandException commandError(String input) {
+ return new DukeCommandException(input);
+ }
+}
diff --git a/src/main/java/duke/command/CommandExecuter.java b/src/main/java/duke/command/CommandExecuter.java
new file mode 100644
index 0000000000..7d04591abe
--- /dev/null
+++ b/src/main/java/duke/command/CommandExecuter.java
@@ -0,0 +1,10 @@
+package duke.command;
+
+import duke.exceptions.DukeException;
+import duke.tasks.TaskManager;
+import duke.ui.UserInterface;
+
+@FunctionalInterface
+interface CommandExecuter {
+ void run(TaskManager taskManager, UserInterface ui, String[] arguments) throws DukeException;
+}
diff --git a/src/main/java/duke/command/CommandHelp.java b/src/main/java/duke/command/CommandHelp.java
new file mode 100644
index 0000000000..612cabd5a8
--- /dev/null
+++ b/src/main/java/duke/command/CommandHelp.java
@@ -0,0 +1,52 @@
+package duke.command;
+
+/**
+ * Command is a Storage for the Enumerations of each type of command that is valid in Duke Chatbot.
+ */
+public enum CommandHelp {
+ //random string as this is the default enum.
+ ERROR("asjdbaksjfbanfjknjkdfnskasd", "This is a invalid command"),
+ BYE("bye", "close the application"),
+ HELP("help", "Get help for a specific command via help -cmd command"),
+ LIST("list" , "list the current list of Tasks and their statuses"),
+ DONE("done" , "set a task as done via index: done 1 to mark Task 1 as done"),
+ DELETE("delete", "delete a task via index: delete 1 to delete first task"),
+ TODO("todo", "schedule a untimed task"),
+ DEADLINE("deadline", "schedule a timed deadline task, please structure with "
+ + "[deadline -by dd-MM-YYYY]"),
+ EVENT("event", "schedule a timed event task, please structure with "
+ + "[event -at dd-MM-YYYY]"),
+ SEARCH("find", "find on description only"),
+ BLANK("", "This is a invalid command");
+ private final String commandEncoding;
+ private final String helpMsg;
+
+ /**
+ * Constructs the enumeration for help commands
+ * @param code key word of command
+ * @param help Help message to display
+ */
+ CommandHelp(String code, String help) {
+ commandEncoding = code;
+ helpMsg = help;
+ }
+ /**
+ * Getter for command that is encoded in a Command
+ * @return exact text that represents a command in Duke
+ */
+ public String getCode() {
+ return commandEncoding;
+ }
+ public String toString() {
+ return "- " + commandEncoding + " to " + helpMsg + "\n";
+ }
+
+ /**
+ * Checks for equivilence between the code of the command and the other
+ * @param other Other command
+ * @return boolean indicating that the command is the same.
+ */
+ public boolean equals(CommandHelp other) {
+ return other.getCode().equals(getCode());
+ }
+}
diff --git a/src/main/java/duke/command/CommandList.java b/src/main/java/duke/command/CommandList.java
new file mode 100644
index 0000000000..f90ce41031
--- /dev/null
+++ b/src/main/java/duke/command/CommandList.java
@@ -0,0 +1,78 @@
+package duke.command;
+
+import duke.exceptions.DukeNoInputException;
+import duke.tasks.Deadline;
+import duke.tasks.Event;
+import duke.tasks.TextParser;
+import duke.tasks.ToDo;
+
+
+
+/**
+ * CommandList to store commands supported by duke
+ * Software pattern referenced from https://github.com/JoeyChenSmart/ip
+ * Implementation is done by myself with reference to this example
+ */
+class CommandList {
+ private static final String ALLHELP = "\t Need some help huh?\n"
+ + "Heres a list of my commands!\n"
+ + "- 'bye' to close the application\n"
+ + "- 'list' to list the current list of duke.tasks and their statuses\n"
+ + "- 'done' to set a task as done\n"
+ + "- 'find' to find a task using regex or a query text string\n"
+ + "- 'todo' to list a untimed task\n"
+ + "- 'deadline' to list a timed deadline task, please structure with "
+ + "[deadline -by dd-MM-YYYY]\n"
+ + "- 'event' to list a timed event task, please structure with "
+ + "[event -at dd-MM-YYYY]\n"
+ + "- 'help' to list these commands again help for specific commands\n";
+ private static final TextParser PARSER = new TextParser();
+ static final CommandExecuter DEADLINE = (taskManager, ui, arguments) -> {
+ if (arguments.length == 0) {
+ throw new DukeNoInputException();
+ }
+ assert arguments.length == 2 : "The length of the argument should always only be 2"
+ + " because of -by seperator";
+ ui.systemMessage(taskManager.add(Deadline.createNewDeadline(arguments[0] , arguments[1])));
+ };
+ static final CommandExecuter TODO = (taskManager, ui, arguments) -> {
+ if (arguments[0].isEmpty()) {
+ throw new DukeNoInputException();
+ }
+ assert arguments.length == 1 : "The length of the argument should always only be 1";
+ ui.systemMessage(taskManager.add(new ToDo(arguments[0])));
+ };
+ static final CommandExecuter EVENT = (taskManager, ui, arguments) -> {
+ if (arguments.length == 0) {
+ throw new DukeNoInputException();
+ }
+ assert arguments.length == 2 : "The length of the argument should always only be 2"
+ + " because of -at seperator";
+ ui.systemMessage(taskManager.add(Event.createNewEvent(arguments[0], arguments[1])));
+ };
+ static final CommandExecuter FIND = (taskManager, ui, arguments) -> {
+ ui.systemMessage(taskManager.findTasks(arguments[0]));
+ };
+ static final CommandExecuter BYE = (taskManager, ui, arguments) -> {
+ taskManager.saveTasks();
+ ui.close();
+ };
+ static final CommandExecuter DELETE = (taskManager, ui, arguments) -> {
+ System.out.print(arguments[0]);
+ ui.systemMessage(taskManager.deleteTask(arguments[0]));
+ };
+ static final CommandExecuter DONE = (taskManager, ui, arguments) -> {
+ ui.systemMessage(taskManager.doTask(arguments[0]));
+ };
+ static final CommandExecuter LIST = (taskManager, ui, arguments) -> {
+ ui.systemMessage(taskManager.listTasks());
+ };
+ static final CommandExecuter HELP = (taskManager, ui, arguments) -> {
+ CommandHelp commandHelp = PARSER.parseHelpCommand(arguments[0]);
+ String message = commandHelp.toString();
+ if (commandHelp.equals(CommandHelp.ERROR) || commandHelp.equals(CommandHelp.BLANK)) {
+ message = ALLHELP;
+ }
+ ui.systemMessage(message);
+ };
+}
diff --git a/src/main/java/duke/exceptions/DukeBlankCommandException.java b/src/main/java/duke/exceptions/DukeBlankCommandException.java
new file mode 100644
index 0000000000..f12c56d1a3
--- /dev/null
+++ b/src/main/java/duke/exceptions/DukeBlankCommandException.java
@@ -0,0 +1,14 @@
+package duke.exceptions;
+
+/**
+ * Error Type of a Blank Command in Duke Application.
+ */
+public class DukeBlankCommandException extends DukeException {
+ /**
+ * Constructor for DukeBlankCommandException for a blank command
+ * @param s String form of the bad input
+ */
+ public DukeBlankCommandException(String s) {
+ super(s, 4);
+ }
+}
diff --git a/src/main/java/duke/exceptions/DukeCommandException.java b/src/main/java/duke/exceptions/DukeCommandException.java
new file mode 100644
index 0000000000..4c8d9fa250
--- /dev/null
+++ b/src/main/java/duke/exceptions/DukeCommandException.java
@@ -0,0 +1,14 @@
+package duke.exceptions;
+
+/**
+ * Error Type of a Invalid Command in Duke Application.
+ */
+public class DukeCommandException extends DukeException {
+ /**
+ * Constructor class for DukeCommandException
+ * @param badCommand the command that is unrecognisable by Duke Application
+ */
+ public DukeCommandException(String badCommand) {
+ super(badCommand , 1);
+ }
+}
diff --git a/src/main/java/duke/exceptions/DukeDateTimeException.java b/src/main/java/duke/exceptions/DukeDateTimeException.java
new file mode 100644
index 0000000000..e46b7793dd
--- /dev/null
+++ b/src/main/java/duke/exceptions/DukeDateTimeException.java
@@ -0,0 +1,29 @@
+package duke.exceptions;
+
+/**
+ * Error Type of a DateTimeError in Duke Application.
+ * This error is thrown if the DateTime does not match or is incompatible with the DateTimeFormatter
+ */
+public class DukeDateTimeException extends DukeException {
+ /**
+ * Constructor class for DukeDateTimeException
+ * @param cmd the invalid command
+ */
+ public DukeDateTimeException(String cmd) {
+ super(cmd, 3);
+ }
+
+ /**
+ * Takes in the given bad input and the code
+ * @return String
+ */
+ public String message(String s) {
+ StringBuilder b = new StringBuilder();
+ b.append("Oops you did not mark your datetime! Not sure what you mean by:\n");
+ b.append(badCommand).append("\n");
+ b.append(s);
+ b.append(": ").append(code.toString()).append("\n");
+ b.append("Heres a tip, use the 'help' command to learn about my commands!\n");
+ return b.toString();
+ }
+}
diff --git a/src/main/java/duke/exceptions/DukeException.java b/src/main/java/duke/exceptions/DukeException.java
new file mode 100644
index 0000000000..4ba954d555
--- /dev/null
+++ b/src/main/java/duke/exceptions/DukeException.java
@@ -0,0 +1,49 @@
+package duke.exceptions;
+
+/**
+ * DukeException is a classification of errors that pertain to any running problems within Duke Class applications
+ * Some errors that may occur in the hierarchy of data flow:
+ * 1. FileRead Error (WIP): For handling stored memory and I/O errors
+ * 2. Bad Command Given: When a Command that is unknown is given
+ * 3. No Input given: When in the flow for a given command, no description is detected
+ * 4. Bad Date Given: For handling datetime parsing errors.
+ * 5. Blank Command Given: For handling when a Blank command is given to a input.
+ * 6. Index Error: When a invalid index is given
+ * 7. UnknownException: For handling anything exceptionally unexpected
+ */
+public abstract class DukeException extends Exception {
+
+ protected final String badCommand;
+ protected final ErrorEncode code;
+
+ /**
+ * Constructor class for a Generic DukeException for any errortype encountered in Duke
+ * @param badCommand The command or user input that is causing the error
+ * @param code The enumeration to encode the error message.
+ */
+ protected DukeException(String badCommand, int code) {
+ this.badCommand = badCommand;
+ this.code = ErrorEncode.parseCode(code);
+ }
+
+ /**
+ * Returns the template user error message of the DukeException class
+ * @param s String Message to wrap generic error Message
+ * @return String message to be printed out to player.
+ */
+ public String message(String s) {
+ StringBuilder b = new StringBuilder();
+ b.append("Oops you used a invalid command! Not sure what you mean by:\n");
+ b.append(badCommand).append("\n");
+ b.append(s);
+ b.append(": ").append(code.toString()).append("\n");
+ b.append("Heres a tip, use the 'help' command to learn about my commands!\n");
+ return b.toString();
+ }
+
+ @Override
+ public String toString() {
+
+ return message(super.toString());
+ }
+}
diff --git a/src/main/java/duke/exceptions/DukeIndexException.java b/src/main/java/duke/exceptions/DukeIndexException.java
new file mode 100644
index 0000000000..22d1bed7a7
--- /dev/null
+++ b/src/main/java/duke/exceptions/DukeIndexException.java
@@ -0,0 +1,31 @@
+package duke.exceptions;
+
+/**
+ * Error type for Index Errors that appear when trying to perform a command.
+ */
+public class DukeIndexException extends DukeException {
+ private int size;
+ /**
+ * Constructs a DukeIndexException to indicate that the given command or index is invalid
+ * @param cmd Command or Index that is invalid
+ * @param size The actual or given size of the list
+ */
+ public DukeIndexException(String cmd, int size) {
+ super(cmd, 5);
+ this.size = size;
+ }
+
+ /**
+ * Takes in the given bad input and the code
+ * @return String message
+ */
+ public String message(String s) {
+ StringBuilder b = new StringBuilder("");
+ b.append("Oops you requested for a index ourside the list range or a non numeric index:\n");
+ b.append(badCommand).append("\n");
+ b.append(s);
+ b.append(": ").append(code.toString()).append(" out of ").append(size).append("\n");
+ b.append("Heres a tip, use the 'list' command to see the current duke.tasks!\n");
+ return b.toString();
+ }
+}
diff --git a/src/main/java/duke/exceptions/DukeIoException.java b/src/main/java/duke/exceptions/DukeIoException.java
new file mode 100644
index 0000000000..3cc451acd4
--- /dev/null
+++ b/src/main/java/duke/exceptions/DukeIoException.java
@@ -0,0 +1,14 @@
+package duke.exceptions;
+
+/**
+ * Error type for I/O that appear when trying to perform a read or write command.
+ */
+public class DukeIoException extends DukeException {
+ /**
+ * Constructor for I/O exception class.
+ * @param badCommand the part of the I/O process that is causing an error
+ */
+ public DukeIoException(String badCommand) {
+ super(badCommand, 0);
+ }
+}
diff --git a/src/main/java/duke/exceptions/DukeNoInputException.java b/src/main/java/duke/exceptions/DukeNoInputException.java
new file mode 100644
index 0000000000..072fa1218f
--- /dev/null
+++ b/src/main/java/duke/exceptions/DukeNoInputException.java
@@ -0,0 +1,14 @@
+package duke.exceptions;
+
+/**
+ * Error Type of a Invalid Description for a task, or if it is not given.
+ */
+public class DukeNoInputException extends DukeException {
+ /**
+ * Constructs class for a DukeNoInputException
+ * No input is given to this command hence returning the no input exception
+ */
+ public DukeNoInputException() {
+ super("", 2);
+ }
+}
diff --git a/src/main/java/duke/exceptions/DukeUnknownException.java b/src/main/java/duke/exceptions/DukeUnknownException.java
new file mode 100644
index 0000000000..7b7691e618
--- /dev/null
+++ b/src/main/java/duke/exceptions/DukeUnknownException.java
@@ -0,0 +1,14 @@
+package duke.exceptions;
+
+/**
+ * Exception Class to encapsulate any exceptional error that does not fit any of the previous criterion
+ */
+public class DukeUnknownException extends DukeException {
+ /**
+ * Constructor class for the DukeUnknownException
+ * @param message message or input that caused this unexpected error.
+ */
+ public DukeUnknownException(String message) {
+ super(message, 99);
+ }
+}
diff --git a/src/main/java/duke/exceptions/ErrorEncode.java b/src/main/java/duke/exceptions/ErrorEncode.java
new file mode 100644
index 0000000000..d8e8f8872d
--- /dev/null
+++ b/src/main/java/duke/exceptions/ErrorEncode.java
@@ -0,0 +1,51 @@
+package duke.exceptions;
+/**
+ * Enum to encapsulate all generic messages for specific error types
+ */
+public enum ErrorEncode {
+ FileRead(0, "A I/O and Filereading Error has occured."),
+ BadCommandGiven(1, "I cannot understand what that command means."),
+ NoInputGiven(2, "There was no Input given for the task."),
+ BadDateGiven(3, "You did not give a valid date!"),
+ BlankCommand(4, "No input command was given"),
+ IndexError(5, "The index of the task is out of bounds"),
+ UnknownError(99, "Something Exceptionally unexpected has happened!"
+ + " We will shut the application down");
+
+ private final int code;
+ private final String description;
+
+ ErrorEncode(int code, String description) {
+ this.code = code;
+ this.description = description;
+ }
+
+ /**
+ * Parse an error code into the Enum List for the given error message
+ * @param code int code of error
+ * @return ErrorEncode Enumeration of the type of code
+ */
+ public static ErrorEncode parseCode(int code) {
+ ErrorEncode e = UnknownError;
+ for (ErrorEncode i : ErrorEncode.values()) {
+ if (i.getCode() == code) {
+ e = i;
+ break;
+ }
+ }
+ return e;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public int getCode() {
+ return code;
+ }
+
+ @Override
+ public String toString() {
+ return getDescription();
+ }
+}
diff --git a/src/main/java/duke/tasks/Deadline.java b/src/main/java/duke/tasks/Deadline.java
new file mode 100644
index 0000000000..d08cda2665
--- /dev/null
+++ b/src/main/java/duke/tasks/Deadline.java
@@ -0,0 +1,75 @@
+package duke.tasks;
+
+
+import duke.exceptions.DukeDateTimeException;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeParseException;
+
+/**
+ * The Event class contains the information that is entered by the user into the
+ * Duke Chatbot Command Line Interface.
+ */
+public class Deadline extends TimedTask {
+ /**
+ * Constructor for Deadline Class without done status
+ * To be used for creating new duke.tasks by the end user
+ * @param desc the description of the task
+ * @param date the date on which the task is due
+ * @throws DukeDateTimeException if the fields for the date are not matching autocorrection cases
+ */
+ private Deadline(String desc, String date) throws DukeDateTimeException {
+ super(desc, date);
+ }
+ /**
+ * Constructor for Deadline class with done status
+ * To be used by the I/O manager to read duke.tasks and populate the system at runtime.
+ * @param desc the description of the task.
+ * @param date the date on which the task is due.
+ * @param b the Done Status of the Task.
+ * @throws DukeDateTimeException if the fields for the date are not matching autocorrection cases
+ */
+ Deadline(String desc , String date , Boolean b) throws DukeDateTimeException {
+ super(desc, date, b);
+ }
+ /**
+ * Static Factory method Creates new Deadlines from user input, and adds additional constraint on user
+ * input in order to input a valid new event
+ * @param desc Deadline description
+ * @param date date or partial date for
+ * @return Deadline created from a validated input date.
+ * @throws DukeDateTimeException if the date given by user is before, or is autocorrected before date
+ */
+ public static Deadline createNewDeadline(String desc, String date) throws DukeDateTimeException {
+ try {
+ if (NOW.format(FMAT).length() > date.length()) {
+ date = date + NOW.format(FMAT).substring(date.length());
+ }
+ LocalDateTime dateby = LocalDateTime.parse(date, FMAT);
+ if (dateby.toLocalDate().isBefore(NOW)){
+ throw new DukeDateTimeException("The date you entered "+ dateby.toLocalDate() + " is before today:"
+ + NOW + " or is not in the correct format!");
+ }
+ return new Deadline(desc, date);
+ } catch (DateTimeParseException e) {
+ throw new DukeDateTimeException("The String you entered does not meet the "
+ + "required format of 'yyyy-MM-dd' ");
+ }
+
+
+ }
+ @Override
+ public String toString() {
+ return "[D]" + super.toString() + " (by: " + getDateby() + ") You have "
+ + (timeLeft() > -1 ? timeLeft() + " days left till its due!"
+ : "this task due since " + timeLeft() * -1 + " days ago!");
+ }
+ /**
+ * Returns a String Representation of the Deadline object class to write to text file.
+ * @return the saved task to write to a text file
+ */
+ @Override
+ public String saveTask() {
+ return "D" + SEPERATOR + super.saveTask();
+ }
+}
diff --git a/src/main/java/duke/tasks/Event.java b/src/main/java/duke/tasks/Event.java
new file mode 100644
index 0000000000..7db05ae990
--- /dev/null
+++ b/src/main/java/duke/tasks/Event.java
@@ -0,0 +1,76 @@
+package duke.tasks;
+
+import duke.exceptions.DukeDateTimeException;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeParseException;
+
+/**
+ * The Event class contains the information that is entered by the user into the
+ * Duke Chatbot Command Line Interface.
+ */
+public class Event extends TimedTask {
+ /**
+ * Constructs Event Class without done status
+ * To be used for creating new Tasks by the end user
+ * @param desc the description of the task
+ * @param date the date on which the task is due
+ * @throws DukeDateTimeException Throws Exception if the given date is parsed and returns invalid
+ */
+ private Event(String desc, String date) throws DukeDateTimeException {
+ super(desc, date);
+ }
+
+ /**
+ * Constructs Event class with done status
+ * To be used by the I/O manager to read duke.tasks and populate the system at runtime
+ * @param desc the description of the task
+ * @param date the date on which the task is due
+ * @param b the Done Status of the Task
+ * @throws DukeDateTimeException if the fields for the date are not matching autocorrection cases
+ */
+ Event(String desc, String date, Boolean b) throws DukeDateTimeException {
+ super(desc, date, b);
+ }
+
+ @Override
+ public String toString() {
+ return "[E]" + super.toString() + " (at: " + getDateby() + ") You have "
+ + (timeLeft() > -1 ? timeLeft() + " days left till the event!"
+ : "had this event " + timeLeft() * -1 + " days ago!");
+ }
+
+ /**
+ * Static Factory method Creates new events from user input, and adds additional constraint on user
+ * input in order to input a valid new event
+ * @param desc Event description
+ * @param date date or partial date for
+ * @return Event created from a validated input date.
+ * @throws DukeDateTimeException if the date given by user is before, or is autocorrected before date
+ */
+ public static Event createNewEvent(String desc, String date) throws DukeDateTimeException {
+ try {
+ if (NOW.format(FMAT).length() > date.length()) {
+ date = date + NOW.format(FMAT).substring(date.length());
+ }
+ LocalDateTime dateby = LocalDateTime.parse(date, FMAT);
+ if (dateby.toLocalDate().isBefore(NOW)){
+ throw new DukeDateTimeException("The date you entered " + dateby.toLocalDate() + "is before today:"
+ + NOW + " or is " + "not in the correct format!");
+ }
+ return new Event(desc, date);
+ } catch (DateTimeParseException e) {
+ throw new DukeDateTimeException("The String you entered does not meet the "
+ + "required format of 'yyyy-MM-dd' ");
+ }
+
+ }
+ /**
+ * Returns a String Representation of the Event object class to write to text file.
+ * @return the saved task to write to a text file
+ */
+ @Override
+ public String saveTask() {
+ return "E" + SEPERATOR + super.saveTask();
+ }
+}
diff --git a/src/main/java/duke/tasks/Task.java b/src/main/java/duke/tasks/Task.java
new file mode 100644
index 0000000000..b1cf0c2689
--- /dev/null
+++ b/src/main/java/duke/tasks/Task.java
@@ -0,0 +1,66 @@
+package duke.tasks;
+
+/**
+ * The Task Class is a Abstract Base class for any extending class that acts as
+ * a Task object for the Duke Chatbot
+ */
+abstract class Task {
+ // SEPERATION Attribute is used to encode the different attributes of the Task Class
+ public static final String SEPERATOR = "#sep#";
+ protected final String description;
+ protected boolean isDoneTask;
+
+ /**
+ * Constructor for a Task
+ * @param description String description of the task
+ * @param done Done Status of the task
+ */
+ protected Task(String description, boolean done) {
+ this.description = description;
+ isDoneTask = done;
+ }
+
+ /**
+ * Returns the check for if the task is already done.
+ * @return Boolean representing whether the task is done
+ */
+ public boolean done() {
+ return isDoneTask;
+ }
+
+ /**
+ * Mark a generic Task object as done
+ */
+ public void doTask() {
+ isDoneTask = true;
+ }
+
+ /**
+ * Get the description of the task
+ * @return description of task
+ */
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * Checklist icon for displaying in the toString Representation.
+ * @return either done or not done
+ */
+ public String statusIcon() {
+ return isDoneTask ? "[\u2713] " : "[\u2718] ";
+ }
+
+ @Override
+ public String toString() {
+ return statusIcon() + getDescription();
+ }
+
+ /**
+ * Takes done status and attributes to encode the String
+ * @return a encoded string version of task for writing to text file.
+ */
+ public String saveTask() {
+ return isDoneTask + SEPERATOR + description;
+ }
+}
diff --git a/src/main/java/duke/tasks/TaskIoParser.java b/src/main/java/duke/tasks/TaskIoParser.java
new file mode 100644
index 0000000000..55e24326f1
--- /dev/null
+++ b/src/main/java/duke/tasks/TaskIoParser.java
@@ -0,0 +1,106 @@
+package duke.tasks;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Scanner;
+
+import duke.exceptions.DukeDateTimeException;
+import duke.exceptions.DukeIoException;
+
+
+/**
+ * Class to perform reading and writing operations on the task list itself.
+ */
+public class TaskIoParser {
+ private static final String SAVEFILE = "save.txt";
+ private final File saveFile;
+
+ TaskIoParser(String path) {
+ Path taskFile = Paths.get(path, "src", "save");
+ this.saveFile = new File(taskFile.toString());
+ }
+
+ /**
+ * Load duke.tasks from a text file into memory
+ * @return A read text file of duke.tasks to Tasklist
+ * @throws DukeIoException if there is an error in reading a file from disk
+ */
+ public List loadTaskList() throws DukeIoException {
+ List tasks = new ArrayList<>();
+ try {
+ Scanner sc = new Scanner(this.saveFile.toPath().resolve(SAVEFILE));
+ String currentLine = "";
+ String[] spl;
+ while (sc.hasNext()) {
+ try {
+ currentLine = sc.nextLine();
+ spl = currentLine.split(Task.SEPERATOR);
+ switch (spl[0]) {
+ case "T":
+ tasks.add(new ToDo(spl[2], Boolean.parseBoolean(spl[1])));
+ break;
+ case "D":
+ tasks.add(new Deadline(spl[2], spl[3], Boolean.parseBoolean(spl[1])));
+ break;
+ case "E":
+ tasks.add(new Event(spl[2], spl[3], Boolean.parseBoolean(spl[1])));
+ break;
+ default:
+ continue;
+ }
+ } catch (DukeDateTimeException ignored) {
+ // ignored as if the error occurs, we just do not parse that command
+ }
+ }
+ sc.close();
+ return tasks;
+ } catch (IOException fileException) {
+ throw new DukeIoException("Oops we couldnt read any file,"
+ + " hence we will start from a new save file");
+ }
+
+ }
+
+ /**
+ * For initialising new TaskList
+ * @return returns a new List of Tasks
+ */
+ public List loadNewTaskList() {
+ return new ArrayList<>();
+ }
+
+ /**
+ * Writes the task list into a textfile
+ * @param taskList the given list of duke.tasks.
+ * @throws DukeIoException If there is a IO error in creating or writing to the file.
+ */
+ public void writeTask(List taskList) throws DukeIoException {
+ if (!saveFile.exists()) {
+ try {
+ Files.createDirectories(Path.of(saveFile.getPath()));
+ Files.createFile(Path.of(saveFile.getPath()).resolve(SAVEFILE));
+ } catch (IOException e) {
+ throw new DukeIoException("Could not save the file due to directory not created");
+ }
+ }
+
+ try {
+ FileWriter fw = new FileWriter(Path.of(saveFile.getPath()).resolve(SAVEFILE).toFile());
+ String linesep = System.lineSeparator();
+ for (Task t : taskList) {
+ fw.write(t.saveTask());
+ fw.write(linesep);
+ }
+ fw.close();
+ } catch (IOException e) {
+ throw new DukeIoException(e.toString());
+ }
+
+ }
+}
diff --git a/src/main/java/duke/tasks/TaskManager.java b/src/main/java/duke/tasks/TaskManager.java
new file mode 100644
index 0000000000..a0fafee7d3
--- /dev/null
+++ b/src/main/java/duke/tasks/TaskManager.java
@@ -0,0 +1,162 @@
+package duke.tasks;
+
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.regex.Pattern;
+
+import duke.exceptions.DukeCommandException;
+import duke.exceptions.DukeIndexException;
+import duke.exceptions.DukeIoException;
+
+/**
+ * TaskManager is a class to handle where Tasks are CRUD.
+ */
+public class TaskManager {
+ private final List taskList;
+ private final TaskIoParser ioParser;
+
+ /**
+ * Constructs TaskManager for the Duke Application with loading from the save file
+ * @param path File Path from Main Class
+ * @throws DukeIoException If no loaded save is read
+ */
+ public TaskManager(String path) throws DukeIoException {
+ ioParser = new TaskIoParser(path);
+ taskList = ioParser.loadTaskList();
+ }
+
+ /**
+ * Constructs TaskManger for the first time.
+ * @param path File Path from Main Class
+ * @param isNew Boolean to indicate that the TaskManager is first initialised.
+ */
+ public TaskManager(String path, boolean isNew) {
+ assert isNew : "isNew is to allow for polymorphism for the case where taskmanager is new";
+ ioParser = new TaskIoParser(path);
+ taskList = ioParser.loadNewTaskList();
+ }
+ /**
+ * Indicates that a task is done
+ * @param index index of the list as displayed from the application
+ * @return String representation of indicating the task is done.
+ * @throws DukeCommandException if a illegal index is given
+ * @throws DukeIndexException If a given index is out of bounds
+ */
+ public String doTask(String index) throws DukeCommandException, DukeIndexException {
+ try {
+ int i = Integer.parseInt(index) - 1;
+ //0 indexing
+ getTask(i).doTask();
+ return "\tNice! I've marked this task as done: \n\t" + getTask(i) + "\n";
+ } catch (IllegalArgumentException e) {
+ throw new DukeCommandException(index);
+ } catch (IndexOutOfBoundsException e) {
+ throw new DukeIndexException(index, taskList.size());
+ }
+
+ }
+
+ /**
+ * Deletes task
+ * @param index index of the list as displayed from the application
+ * @return String representation of the confirmation of deletion of task
+ * @throws DukeCommandException if a illegal index is given
+ * @throws DukeIndexException if a given index is out of bounds
+ */
+ public String deleteTask(String index) throws DukeCommandException, DukeIndexException {
+ try {
+ int i = Integer.parseInt(index) - 1;
+ //0 indexing
+ Task t = getTask(i);
+ taskList.remove(i);
+ return new StringBuilder().append("\tNoted! I've removed this task from your list: \n\t")
+ .append(t)
+ .append("\n\tNow you have ")
+ .append(taskList.size())
+ .append("tasks in the list.\n").toString();
+ } catch (IllegalArgumentException e) {
+ throw new DukeCommandException(index);
+ } catch (IndexOutOfBoundsException e) {
+ throw new DukeIndexException(index, taskList.size());
+ }
+
+ }
+
+ /**
+ * Get task from the internal list
+ * @param index index of the internal list
+ * @return Task the task at that index
+ */
+ private Task getTask(int index) {
+ return taskList.get(index);
+ }
+
+ /**
+ * generic polymorphic data flow for adding a task to the runtime database
+ * @param t task
+ * @return String to be wrapped and printed
+ */
+ public String add(Task t) {
+ taskList.add(t);
+ return echo(t);
+ }
+
+ /**
+ * Returns string builder of the task
+ *
+ * @param task Task
+ * @return String echow when task is completed
+ */
+ private String echo(Task task) {
+ return new StringBuilder().append("\tGot it. I've added this task:\n\t ")
+ .append(task).append("\n\tNow you have ")
+ .append(taskList.size())
+ .append(" tasks in the list.\n")
+ .toString();
+ }
+
+ /**
+ * Message Passing for Tasks
+ * @throws DukeIoException if the task cannot be read to the file
+ */
+ public void saveTasks() throws DukeIoException {
+ ioParser.writeTask(taskList);
+ }
+ /**
+ * Parses the current list and prints the output
+ * @return String representation of the Task List
+ */
+ public String listTasks() {
+ return findTasks("");
+ }
+ /**
+ * Regex pattern string search
+ * @param pattern Regex Pattern or substring of description of any task in the list
+ * @return String representation of duke.tasks that match the given pattern
+ */
+ public String findTasks(String pattern) {
+ StringBuilder sb = new StringBuilder("");
+ if (taskList.size() > 0) {
+ Pattern stringPattern = Pattern.compile(pattern);
+ AtomicInteger index = new AtomicInteger();
+ sb.append(taskList.stream()
+ .filter(task -> {
+ index.incrementAndGet();
+ return stringPattern.matcher(task.getDescription()).find();
+ }).map(task -> String.format("\t%d. %s\n", index.get(), task))
+ .reduce("" , (accumulate, next) -> accumulate + next));
+ if (sb.toString().isEmpty()) {
+ sb.append("\tCannot find a valid task in your list");
+ }
+ } else {
+ sb.append("\tThere are no tasks in your list!\n");
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public String toString() {
+ return "TaskManager: \n" + listTasks();
+ }
+}
diff --git a/src/main/java/duke/tasks/TextParser.java b/src/main/java/duke/tasks/TextParser.java
new file mode 100644
index 0000000000..c3ee3e0786
--- /dev/null
+++ b/src/main/java/duke/tasks/TextParser.java
@@ -0,0 +1,38 @@
+package duke.tasks;
+
+import java.util.Arrays;
+import java.util.Optional;
+
+import duke.command.CommandHelp;
+
+/**
+ * TextParser to handle parsing of commands and possible cleaning.
+ */
+public class TextParser {
+ public TextParser(){
+ }
+ /**
+ * Parse String Input into the Command Parser to return a Enum of the command encoded.
+ *
+ * @param cmd the string command for the Duke Application
+ * @return Command Enumeration
+ */
+ public CommandHelp parseHelpCommand(String cmd) {
+ String cleaned = cmd.toLowerCase();
+ Optional given = Arrays.stream(CommandHelp.values())
+ .filter(commandHelp -> commandHelp.getCode().equals(cleaned))
+ .findFirst();
+ given = given.isEmpty() ? Optional.of(CommandHelp.ERROR) : given;
+ return given.get();
+ }
+ /**
+ * inputs string, processes and cleans the text for the chatbot
+ * via adding a ending token seperator
+ * @param userInput Direct user input of the string
+ * @return Cleaned user input
+ */
+ public String cleanInput(String userInput) {
+ return userInput.strip();
+ }
+
+}
diff --git a/src/main/java/duke/tasks/TimedTask.java b/src/main/java/duke/tasks/TimedTask.java
new file mode 100644
index 0000000000..34f745b794
--- /dev/null
+++ b/src/main/java/duke/tasks/TimedTask.java
@@ -0,0 +1,89 @@
+package duke.tasks;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.Period;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeFormatterBuilder;
+import java.time.format.DateTimeParseException;
+import java.time.temporal.ChronoField;
+
+import duke.exceptions.DukeDateTimeException;
+/**
+ * TimedTask is a abstract class that inherits from the base Task
+ * class, to add a new field of datetime into this child class which implements such functionalities
+ */
+abstract class TimedTask extends Task {
+ protected static final DateTimeFormatter FMAT;
+ protected static final LocalDate NOW = LocalDateTime.now().toLocalDate();
+ static {
+ DateTimeFormatterBuilder dateTimeFormatterBuilder = new DateTimeFormatterBuilder();
+ dateTimeFormatterBuilder.appendPattern("dd-MM-yyyy");
+ dateTimeFormatterBuilder.parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0);
+ dateTimeFormatterBuilder.parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0);
+ dateTimeFormatterBuilder.parseDefaulting(ChronoField.HOUR_OF_DAY, 0);
+ dateTimeFormatterBuilder.parseDefaulting(ChronoField.YEAR_OF_ERA, LocalDateTime.now().getYear());
+ dateTimeFormatterBuilder.parseDefaulting(ChronoField.MONTH_OF_YEAR, LocalDateTime.now().getMonthValue());
+ dateTimeFormatterBuilder.parseDefaulting(ChronoField.DAY_OF_MONTH, LocalDateTime.now().getDayOfMonth());
+ FMAT = dateTimeFormatterBuilder
+ .toFormatter();
+ }
+ protected final LocalDateTime dateby;
+ /**
+ * Constructor for a TimedTask for use by implementing subclasses, in particular to populate
+ * Tasks that have been read from a text save file.
+ * @param desc Description of a task
+ * @param date String that is extracted from user input to be parsed into the constructor for a TimedTask object
+ * @param done Done Status of a task
+ * @throws DukeDateTimeException if the fields for the date are not matching autocorrection cases
+ */
+ protected TimedTask(String desc, String date, Boolean done) throws DukeDateTimeException {
+ super(desc, done);
+ try {
+ if (NOW.format(FMAT).length() > date.length()) {
+ date = date + NOW.format(FMAT).substring(date.length());
+ }
+ dateby = LocalDateTime.parse(date, FMAT);
+ } catch (DateTimeParseException e) {
+ throw new DukeDateTimeException("The String you entered does not meet the "
+ + "required format of 'yyyy-MM-dd' ");
+ }
+
+ }
+
+ /**
+ * Constructor for a TimedTask for use by implementing subclasses, in particular to create
+ * a new TimedTask class
+ * @param desc Description of a task
+ * @param date String that is extracted from user input to be parsed into the constructor for
+ * a TimedTask object
+ * @throws DukeDateTimeException if the fields for the date are not matching autocorrection cases
+ */
+ protected TimedTask(String desc, String date) throws DukeDateTimeException {
+ this(desc, date, false);
+ }
+ /**
+ * Performs a DateTime Arithmetric calculation with the current time of execution of the program
+ * in order to display a countdown of days in the Task
+ * @return integer representing the number of days left or past since the task was due.
+ */
+ protected int timeLeft() {
+ return Period.between(NOW, LocalDate.from(dateby)).getDays();
+ }
+
+ /**
+ * Get the dateby for the set task
+ * @return dateby for the registered task
+ */
+ public String getDateby() {
+ return dateby.toLocalDate().format(FMAT);
+ }
+ /**
+ * Returns a String Representation of the Event object class to write to text file.
+ * @return the saved task to write to a text file
+ */
+ @Override
+ public String saveTask() {
+ return super.saveTask() + SEPERATOR + getDateby();
+ }
+}
diff --git a/src/main/java/duke/tasks/ToDo.java b/src/main/java/duke/tasks/ToDo.java
new file mode 100644
index 0000000000..9f3bd5effd
--- /dev/null
+++ b/src/main/java/duke/tasks/ToDo.java
@@ -0,0 +1,37 @@
+package duke.tasks;
+
+/**
+ * ToDo is a Implementation of the Task Class with no additional fields
+ * This is the class that stores todo duke.tasks in the Duke program
+ */
+public class ToDo extends Task {
+ /**
+ * Constructs the ToDo Class for use when creating new duke.tasks
+ * by end user.
+ * @param desc Description of task
+ */
+ public ToDo(String desc) {
+ super(desc, false);
+ }
+ /**
+ * Constructs the ToDo Class for use when populating the list of duke.tasks
+ * recorded by the save text file
+ * @param desc Description of task
+ * @param done Done Status of task
+ */
+ ToDo(String desc, Boolean done) {
+ super(desc, done);
+ }
+
+ @Override
+ public String toString() {
+ return "[T]" + super.toString();
+ }
+ /**
+ * Returns a String Representation of the ToDo object class to write to text file.
+ * @return the saved task to write to a text file
+ */
+ public String saveTask() {
+ return "T" + SEPERATOR + super.saveTask();
+ }
+}
diff --git a/src/main/java/duke/ui/CommandLineInterface.java b/src/main/java/duke/ui/CommandLineInterface.java
new file mode 100644
index 0000000000..744709e24a
--- /dev/null
+++ b/src/main/java/duke/ui/CommandLineInterface.java
@@ -0,0 +1,75 @@
+package duke.ui;
+
+import java.util.Scanner;
+
+/**
+ * Class to handle Commandline interface, implements UserInterface contract.
+ */
+public class CommandLineInterface implements UserInterface {
+ private static final String logo = "\tHello from\n"
+ + " ____ _ \n"
+ + "| _ \\ _ _| | _____ \n"
+ + "| | | | | | | |/ / _ \\\n"
+ + "| |_| | |_| | < __/\n"
+ + "|____/ \\__,_|_|\\_\\___|\n"
+ + "\tHello! %s I'm Duke\n\tWhat can I do for you "
+ + "\n";
+ private static final String goodbye = "Bye %s! Hope to see you again soon!\n";
+ private static final String linebreaker = "_".repeat(30) + "\n";
+ private boolean isChatbotRunning;
+ private String userName;
+ private final Scanner scanner;
+
+ /**
+ * Constructs the CommandLineInterface UI for use without a GUI
+ */
+ public CommandLineInterface() {
+ scanner = new Scanner(System.in);
+ isChatbotRunning = false;
+ }
+ @Override
+ public boolean isRunning() {
+ return isChatbotRunning;
+ }
+ /**
+ * Greeting from Duke Bot and set username of user
+ * @param userName Name of the user
+ */
+ @Override
+ public void start(String userName) {
+ assert !isChatbotRunning : "CommandLineInterface should only start once";
+ isChatbotRunning = true;
+ userName = userName;
+ systemMessage(String.format(logo, userName));
+ }
+
+ @Override
+ public void close() {
+ assert isChatbotRunning : "CommandLineInterface should only end once";
+ scanner.close();
+ isChatbotRunning = false;
+ systemMessage(String.format(goodbye, userName));
+ }
+
+ @Override
+ public String nextLine() {
+ return scanner.nextLine();
+ }
+
+ /**
+ * Pass Message into the System for display as text
+ * @param message from duke to human
+ */
+ @Override
+ public void systemMessage(String message) {
+ System.out.print("\t" + linebreaker + indent(message) + linebreaker);
+ }
+ /**
+ * Indents text
+ * @param s text to indent
+ * @return indented text
+ */
+ private String indent(String s) {
+ return " " + s.replace("\n", "\n\t");
+ }
+}
diff --git a/src/main/java/duke/ui/GuiHelper.java b/src/main/java/duke/ui/GuiHelper.java
new file mode 100644
index 0000000000..024a13bdd1
--- /dev/null
+++ b/src/main/java/duke/ui/GuiHelper.java
@@ -0,0 +1,82 @@
+package duke.ui;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * Container for messages from {@link duke.Duke} to the {@link MainLauncher}
+ */
+public class GuiHelper implements UserInterface {
+ private boolean isChatbotRunning = true;
+ private String userName;
+ private String userInput;
+ private List commandOutput;
+ private boolean isNotConsumed = false;
+ /**
+ * Constructs helper class for GUI Interfacing
+ */
+ public GuiHelper() {
+ commandOutput = new ArrayList<>();
+ }
+
+ @Override
+ public void start(String userName) {
+ assert !isChatbotRunning : "GuiHelper should start only once";
+ isChatbotRunning = true;
+ userName = userName;
+ }
+ @Override
+ public boolean isRunning() {
+ return isChatbotRunning;
+ }
+
+ @Override
+ public void close() {
+ assert isChatbotRunning : "GuiHelper should only close once";
+ isChatbotRunning = false;
+ commandOutput.add("Goodbye " + userName + " my friend!");
+ }
+
+ @Override
+ public String nextLine() {
+ return userInput;
+ }
+
+ @Override
+ public void systemMessage(String message) {
+ isNotConsumed = true;
+ commandOutput.add(message);
+ }
+
+ /**
+ * Consumes command and returns output from Duke command if any.
+ * Otherwise returns Optional.empty()
+ * If a output is returned, mark as "consumed"
+ * and subsequent calls to consumeCommandOutput returns Optional.empty()
+ * until a new {@link duke.command.CommandEnums} from Duke is run
+ * @return Output from Duke Command if it is first invocation, else optional.empty.
+ */
+ public Optional> consumeCommandOutput() {
+ if (isNotConsumed) {
+ List result = commandOutput;
+ isNotConsumed = false;
+ commandOutput = new ArrayList<>();
+ return Optional.of(result);
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ /**
+ * Set user input
+ * @param input userInput
+ */
+ public void setUserInput(String input) {
+ userInput = input;
+ }
+
+ public String toString() {
+ return "GUI";
+ }
+}
diff --git a/src/main/java/duke/ui/MainLauncher.java b/src/main/java/duke/ui/MainLauncher.java
new file mode 100644
index 0000000000..c4ba649d63
--- /dev/null
+++ b/src/main/java/duke/ui/MainLauncher.java
@@ -0,0 +1,32 @@
+package duke.ui;
+
+import java.io.IOException;
+
+import duke.ui.graphics.MainWindow;
+import javafx.application.Application;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Scene;
+import javafx.scene.layout.AnchorPane;
+import javafx.stage.Stage;
+
+
+
+/**
+ * Main class for GUI
+ */
+public class MainLauncher extends Application {
+ @Override
+ public void start(Stage stage) {
+ try {
+ FXMLLoader fxmlLoader = new FXMLLoader(MainWindow.class.getResource("/view/MainWindow.fxml"));
+ AnchorPane ap = fxmlLoader.load();
+ Scene scene = new Scene(ap);
+ stage.setScene(scene);
+ stage.setTitle("Duke Chatbot");
+ fxmlLoader.getController().setup();
+ stage.show();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/src/main/java/duke/ui/UserInterface.java b/src/main/java/duke/ui/UserInterface.java
new file mode 100644
index 0000000000..3cd15ad58f
--- /dev/null
+++ b/src/main/java/duke/ui/UserInterface.java
@@ -0,0 +1,12 @@
+package duke.ui;
+
+/**
+ * Class to define the UI Operations
+ */
+public interface UserInterface {
+ public boolean isRunning();
+ public void start(String username);
+ public void close();
+ public String nextLine();
+ public void systemMessage(String message);
+}
diff --git a/src/main/java/duke/ui/graphics/DialogBox.java b/src/main/java/duke/ui/graphics/DialogBox.java
new file mode 100644
index 0000000000..f5c6ad86fb
--- /dev/null
+++ b/src/main/java/duke/ui/graphics/DialogBox.java
@@ -0,0 +1,76 @@
+package duke.ui.graphics;
+
+import java.io.IOException;
+import java.net.URL;
+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;
+
+
+
+/**
+ * Dialog box contains messages from duke and user
+ */
+public class DialogBox extends HBox {
+ @FXML
+ private Label dialog;
+ @FXML
+ private ImageView displayPicture;
+
+ private DialogBox(String text, Image img) {
+ try {
+ URL file = MainWindow.class.getResource("/view/DialogBox.fxml");
+ FXMLLoader fxmlLoader = new FXMLLoader(file);
+ fxmlLoader.setController(this);
+ fxmlLoader.setRoot(this);
+ fxmlLoader.load();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ dialog.setMinHeight(Label.USE_PREF_SIZE);
+ 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(getChildren());
+ Collections.reverse(tmp);
+ getChildren().setAll(tmp);
+ setAlignment(Pos.TOP_LEFT);
+ }
+
+ /**
+ * Constructs a Dialog box for the user
+ * @param text text to input
+ * @param img image for user
+ * @return Dialog box for GUI
+ */
+ public static DialogBox getUserDialog(String text, Image img) {
+ return new DialogBox(text, img);
+ }
+
+ /**
+ * Constructs a dialog box for the duke response
+ * @param text text output
+ * @param img image for duke
+ * @return Dialog box for GUI
+ */
+ 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/ui/graphics/MainWindow.java b/src/main/java/duke/ui/graphics/MainWindow.java
new file mode 100644
index 0000000000..5a94cac264
--- /dev/null
+++ b/src/main/java/duke/ui/graphics/MainWindow.java
@@ -0,0 +1,84 @@
+package duke.ui.graphics;
+
+import duke.Duke;
+import duke.ui.GuiHelper;
+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;
+import javafx.stage.Stage;
+
+public class MainWindow extends AnchorPane {
+ @FXML
+ private ScrollPane scrollPane;
+ @FXML
+ private VBox dialogContainer;
+ @FXML
+ private TextField userInput;
+ @FXML
+ private Button sendButton;
+
+ private GuiHelper guiHelper;
+ private Duke duke;
+
+ private final Image userImg = new Image(getClass().getResourceAsStream("/images/caocao.gif"));
+ private final Image dukeImg = new Image(getClass().getResourceAsStream("/images/trump.gif"));
+
+ /**
+ * Substitute constructor for GUI
+ */
+ public void setup() {
+ guiHelper = new GuiHelper();
+ duke = new Duke(guiHelper);
+ greeting();
+ }
+
+ /**
+ * Initialise the Mainwindow.
+ */
+ @FXML
+ public void initialize() {
+ scrollPane.vvalueProperty().bind(dialogContainer.heightProperty());
+ }
+ /**
+ * 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();
+ guiHelper.setUserInput(input);
+ userMessage(input);
+ duke.nextIteration();
+ if (!guiHelper.isRunning()) {
+ closeWindow();
+ }
+ guiHelper.consumeCommandOutput().ifPresent((message) -> {
+ message.forEach(this::dukeMessage);
+ });
+ userInput.clear();
+ }
+
+ private void greeting() {
+ final String welcomeMessage = "Hello Friend! I'm Duke, how may I help you!";
+ dukeMessage(welcomeMessage);
+ }
+
+ private void closeWindow() {
+ Stage stage = (Stage) sendButton.getScene().getWindow();
+ stage.close();
+ }
+
+ private void userMessage(String message) {
+ dialogContainer.getChildren().add(DialogBox.getUserDialog(message , userImg));
+ }
+
+ private void dukeMessage(String message) {
+ dialogContainer.getChildren().add(DialogBox.getDukeDialog(message, dukeImg));
+ }
+
+}
+
diff --git a/src/main/resources/images/DaDuke.png b/src/main/resources/images/DaDuke.png
new file mode 100644
index 0000000000..d893658717
Binary files /dev/null and b/src/main/resources/images/DaDuke.png differ
diff --git a/src/main/resources/images/DaUser.png b/src/main/resources/images/DaUser.png
new file mode 100644
index 0000000000..3c82f45461
Binary files /dev/null and b/src/main/resources/images/DaUser.png differ
diff --git a/src/main/resources/images/caocao.gif b/src/main/resources/images/caocao.gif
new file mode 100644
index 0000000000..ec7da83dda
Binary files /dev/null and b/src/main/resources/images/caocao.gif differ
diff --git a/src/main/resources/images/trump.gif b/src/main/resources/images/trump.gif
new file mode 100644
index 0000000000..47a52d4cf4
Binary files /dev/null and b/src/main/resources/images/trump.gif differ
diff --git a/src/main/resources/view/DialogBox.fxml b/src/main/resources/view/DialogBox.fxml
new file mode 100644
index 0000000000..4d7524da9b
--- /dev/null
+++ b/src/main/resources/view/DialogBox.fxml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml
new file mode 100644
index 0000000000..153572f0fc
--- /dev/null
+++ b/src/main/resources/view/MainWindow.fxml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/save/save.txt b/src/save/save.txt
new file mode 100644
index 0000000000..de04cb7a8a
--- /dev/null
+++ b/src/save/save.txt
@@ -0,0 +1,2 @@
+D#sep#false#sep#asdqfe#sep#21-09-2020
+E#sep#false#sep#asdat#sep#13-09-2020
diff --git a/src/test/java/duke/tasks/DeadlineTest.java b/src/test/java/duke/tasks/DeadlineTest.java
new file mode 100644
index 0000000000..1daab2e590
--- /dev/null
+++ b/src/test/java/duke/tasks/DeadlineTest.java
@@ -0,0 +1,37 @@
+package duke.tasks;
+
+import duke.exceptions.DukeDateTimeException;
+import org.junit.jupiter.api.Test;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+/**
+ * Class to test Deadline and TimedTask class
+ */
+public class DeadlineTest {
+ @Test
+ public void testDeadlineClassConstructor_invalidString_throwsDukeDateTimeException() {
+ assertThrows(DukeDateTimeException.class, () -> Deadline.createNewDeadline("description", "fail"));
+ }
+
+
+ @Test
+ public void testDeadlineConstructor_autoCorrectsBlankDateField_CorrectstoCurrentDate() {
+ try {
+ Deadline deadline = Deadline.createNewDeadline("random_desc", "");
+ assertEquals(LocalDate.now().format(DateTimeFormatter.ofPattern("dd-MM-YYYY")),deadline.getDateby());
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Test
+ public void testTimeLeftArithmetric_noInput_zeroReturned() throws Exception {
+ Deadline deadline = Deadline.createNewDeadline("random_desc", "");
+ assertEquals(deadline.timeLeft(), 0);
+ }
+}
diff --git a/src/test/java/duke/tasks/TextParserTest.java b/src/test/java/duke/tasks/TextParserTest.java
new file mode 100644
index 0000000000..35aefdb2d3
--- /dev/null
+++ b/src/test/java/duke/tasks/TextParserTest.java
@@ -0,0 +1,31 @@
+package duke.tasks;
+
+import duke.command.CommandHelp;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * Class to teast TextParser and all public test
+ */
+public class TextParserTest {
+ /**
+ * Tests TextParser and ensures that it returns a error if a invalid command is given as input
+ */
+ @Test
+ public void parseCommand_givenUnknownCommand_thenAssertThrowsException() {
+ TextParser textParser = new TextParser();
+ assertEquals(CommandHelp.ERROR, textParser.parseHelpCommand("hello"));
+ }
+
+ /**
+ * Tests that when a expected command is given, the correct enumeration is returned.
+ */
+ @Test
+ public void parseCommand_givenSeperationToken_AssertTokenSeperatesString() {
+ TextParser textParser = new TextParser();
+ String test = "todo";
+ CommandHelp expectedResult = CommandHelp.TODO;
+ assertEquals(expectedResult, textParser.parseHelpCommand(test));
+ }
+}
diff --git a/src/test/java/duke/tasks/ToDoTest.java b/src/test/java/duke/tasks/ToDoTest.java
new file mode 100644
index 0000000000..640f5b7fa8
--- /dev/null
+++ b/src/test/java/duke/tasks/ToDoTest.java
@@ -0,0 +1,30 @@
+package duke.tasks;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class ToDoTest {
+ public final ToDo toDo = new ToDo("test task");
+
+ @Test
+ void testDescription_isEquivalent() {
+ assertEquals("test task", toDo.getDescription());
+ }
+
+ @Test
+ void testStatusIcon_matchesExpectedStatus() {
+ assertEquals("[\u2718] ", toDo.statusIcon());
+ }
+
+ @Test
+ void testSaveTaskEncoding_matchesExpectedStatus() {
+ assertEquals("T#sep#false#test task#sep#", toDo.saveTask());
+ }
+
+ @Test
+ void testMarkDoneTask_changesOutput() {
+ toDo.doTask();
+ assertEquals("[\u2713] test task", toDo.toString());
+ }
+}
diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT
index 657e74f6e7..adde0b7ed7 100644
--- a/text-ui-test/EXPECTED.TXT
+++ b/text-ui-test/EXPECTED.TXT
@@ -5,3 +5,77 @@ Hello from
| |_| | |_| | < __/
|____/ \__,_|_|\_\___|
+ _________________________________________________
+
+ Hello! I'm duke.Duke
+ What can I do for you
+
+ _________________________________________________
+
+####################################################################################################
+ Got it. I've added this task:
+ [T][✘] taskAbc 1231
+ Now you have 1 duke.tasks in the list.
+####################################################################################################
+
+####################################################################################################
+ Here are the duke.tasks in your list:
+ 1. [T][✘] taskAbc 1231
+####################################################################################################
+
+####################################################################################################
+ Got it. I've added this task:
+ [D][✘] null (by: null)
+ Now you have 2 duke.tasks in the list.
+####################################################################################################
+
+####################################################################################################
+ Got it. I've added this task:
+ [D][✘] Say Hi (by: tofu)
+ Now you have 3 duke.tasks in the list.
+####################################################################################################
+
+####################################################################################################
+ Oops you used a invalid commandHelp! Not sure what you mean... by:
+ abc 123
+ Heres a tip, use the 'help' commandHelp to learn about my commands!
+####################################################################################################
+
+####################################################################################################
+ Nice! I've marked this task as done:
+ [T][✓] taskAbc 1231
+####################################################################################################
+
+####################################################################################################
+ Nice! I've marked this task as done:
+ [D][✓] null (by: null)
+####################################################################################################
+
+####################################################################################################
+ Nice! I've marked this task as done:
+ [D][✓] Say Hi (by: tofu)
+####################################################################################################
+
+####################################################################################################
+ Nice! I've marked this task as done:
+ [D][✓] Say Hi (by: tofu)
+####################################################################################################
+
+####################################################################################################
+ Here are the duke.tasks in your list:
+ 1. [T][✓] taskAbc 1231
+ 2. [D][✓] null (by: null)
+ 3. [D][✓] Say Hi (by: tofu)
+####################################################################################################
+
+####################################################################################################
+ Got it. I've added this task:
+ [E][✘] hello friends /by (at: TIME)
+ Now you have 4 duke.tasks in the list.
+####################################################################################################
+
+ _________________________________________________
+
+ Bye. Hope to see you again soon!
+ _________________________________________________
+
diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt
index e69de29bb2..e8cb892e9d 100644
--- a/text-ui-test/input.txt
+++ b/text-ui-test/input.txt
@@ -0,0 +1,12 @@
+todo taskAbc 1231
+list
+deadline Say Hi \by tofu
+deadline Say Hi /by tofu
+abc 123
+done 1
+done 2
+done 3
+done 3
+list
+event hello friends /by /by TIME
+bye
\ No newline at end of file
diff --git a/text-ui-test/runtest.sh b/text-ui-test/runtest.sh
old mode 100644
new mode 100755
index e169618a34..28424ec34d
--- a/text-ui-test/runtest.sh
+++ b/text-ui-test/runtest.sh
@@ -13,14 +13,14 @@ then
fi
# compile the code into the bin folder, terminates if error occurred
-if ! javac -cp ../src -Xlint:none -d ../bin ../src/main/java/Duke.java
+if ! javac -cp ../src -Xlint:none -d ../bin ../src/main/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
+java -Dfile.encoding=UTF-8 -classpath ../bin Main < input.txt > ACTUAL.TXT
# convert to UNIX format
cp EXPECTED.TXT EXPECTED-UNIX.TXT