diff --git a/-diff b/-diff
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/Duke.gradle b/Duke.gradle
new file mode 100644
index 0000000000..e5b8ce7e49
--- /dev/null
+++ b/Duke.gradle
@@ -0,0 +1,16 @@
+plugins {
+ id 'java'
+ id 'application'
+}
+
+group 'seedu.duke'
+version '0.1.0'
+
+repositories {
+ mavenCentral()
+}
+
+application {
+ // Change this to your main class.
+ mainClassName = "seedu.duke.Duke"
+}
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000000..57da0fa3b3
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,63 @@
+plugins {
+ id 'java'
+ id 'application'
+ id 'com.github.johnrengelman.shadow' version '5.1.0'
+ id 'checkstyle'
+}
+
+
+
+
+shadowJar {
+ archiveBaseName = "duke"
+ archiveVersion = "0.2.1"
+ archiveClassifier = null
+ archiveAppendix = null
+}
+
+group 'seedu.duke'
+version '0.2.1'
+
+repositories {
+ mavenCentral()
+}
+
+checkstyle {
+ toolVersion = '8.23'
+}
+
+
+application {
+ mainClassName = 'Launcher'
+ run {
+ standardInput = System.in
+ }
+}
+
+
+dependencies {
+ String javaFxVersion = '11.0.2'
+ 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'
+ implementation group: 'org.openjfx', name: 'javafx-media', version: javaFxVersion, classifier: 'win'
+ implementation group: 'org.openjfx', name: 'javafx-media', version: javaFxVersion, classifier: 'mac'
+ implementation group: 'org.openjfx', name: 'javafx-media', version: javaFxVersion, classifier: 'linux'
+ testImplementation 'org.junit.jupiter:junit-jupiter:5.5.0'
+ }
+
+test {
+ useJUnitPlatform();
+}
+
+
+
diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml
new file mode 100644
index 0000000000..b1a57ba6c0
--- /dev/null
+++ b/config/checkstyle/checkstyle.xml
@@ -0,0 +1,257 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/duke.txt b/data/duke.txt
new file mode 100644
index 0000000000..189d9fc5a5
--- /dev/null
+++ b/data/duke.txt
@@ -0,0 +1,3 @@
+1 / ToDo / 0 / read book
+2 / Deadline / 1 / homework / 12th of DECEMBER 1212, 12:12pm
+3 / Event / 0 / jogging with friends / Wednesday
diff --git a/diff b/diff
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/docs/README.md b/docs/README.md
index fd44069597..4176e6a514 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,20 +1,128 @@
# User Guide
-## Features
+[1. Introduction](README.md#Introduction)
+[2. Quick Start](README.md#Quick-Start)
+[3. Features](README.md#Features)
+[4. FAQ](README.md#FAQ)
+[5. Command Summary](README.md#Command-Summary)
-### Feature 1
-Description of feature.
+## 1. Introduction
-## Usage
+Duke is for those who prefer to use a desktop app for managing tasks.
+More importantly, DukeBot is optimized for those who prefer to work with a Command Line Interface (CLI) while still having the benefits of a Graphical User Interface (GUI). Enjoy!
-### `Keyword` - Describe action
-Describe action and its outcome.
+## 2. Quick Start
+1. Ensure you have Java 11 or above installed in your Computer.
-Example of usage:
+2. Download the latest duke-0.2.1.jar [here](https://github.com/qweiping31415/duke/releases)
-`keyword (optional arguments)`
+![Ui.png](Ui.png)
-Expected outcome:
+## 3. Features
-`outcome`
+### 3.1. Viewing help
+Shows a simple user guide to command formats.
+Format: `help`
+
+
+### 3.2. Adding a task: `todo`, `deadline`, `event`
+Adds a task to the list
+
+
+##### 3.2.1. Adding a ToDo Task: `todo`
+Adds a ToDo task to the list
+Format: `todo TASK_DESCRIPTION`
+
+Examples:
+* `todo homework`
+* `todo read book`
+
+##### 3.2.2. Adding a Deadline Task: `deadline`
+Adds a Deadline task to the list
+Format: `deadline TASK_DESCRIPTION /by DUE_DATETIME`
+
+Examples:
+* `deadline do homework /by 3pm`
+* `deadline do homework /by Monday`
+
+> ###### **Formatted date and time**:
+> If `DUE_DATETIME` is entered in the `DD/MM/YYYY HHMM` format, it will automatically be converted to a more readable format.
+>
+>Example:
+>`deadline do homework /by 12/12/1212 1212`
+
+##### 3.2.3. Adding a Event Task: `event`
+Adds an Event task to Duke
+Format: `event TASK_DESCRIPTION /by DUE_DATETIME`
+
+Example:
+* `event consultation /at 3pm`
+* `event consultation /at Monday`
+
+> ###### **Formatted date and time**:
+> If `DUE_DATETIME` is entered in the `DD/MM/YYYY HHMM` format, it will automatically be converted to a more readable format.
+>
+>Example:
+>`event consultation /at 12/12/1212 1212`
+
+
+### 3.3. Finding tasks by keyword: `find`
+Finds tasks whose descriptions contain an exact match of the given keyword.
+
+Format: `find KEYWORD`
+Example:
+* `find homework`
+
+
+### 3.4. Marking a task as done: `done`
+Marks the specified task in the list as done.
+
+Format: `done INDEX`
+* Marks the tasks at the specified INDEX.
+* The index refers to the index number shown in the displayed task list.
+* The index must be a positive integer 1, 2, 3, …
+
+
+### 3.5. Deleting a task: `delete`
+Deletes the specified task from the list.
+Format: `delete INDEX`
+* Deletes the tasks at the specified INDEX.
+* The index refers to the index number shown in the displayed task list.
+* The index must be a positive integer 1, 2, 3, …
+
+
+### 3.6. Listing all tasks : `list`
+Shows a list of all tasks in the list.
+Format: `list`
+
+
+### 3.7. Exiting the program: `bye`
+Exits the program.
+Format: `bye`
+
+
+## 4. FAQ
+* None
+
+
+## 5. Command Summary
+* Add : `CODE_NAME TASK_DESCRIPTION [ADDITIONAL_KEYWORD] [DUE_DATETIME]`
+e.g. `todo homework`
+e.g. `deadline do homework /by 3pm`
+e.g. `event consultation /at 12/12/1212 1212`
+
+* Find : `find KEYWORD`
+e.g. `find homework`
+
+* Delete : `delete INDEX`
+e.g. `delete 3`
+
+* Done : `done INDEX`
+e.g. `done 3`
+
+* List : `list`
+
+* Help : `help`
+
+* Exit : `bye`
diff --git a/docs/Ui.png b/docs/Ui.png
new file mode 100644
index 0000000000..24ebc02260
Binary files /dev/null and b/docs/Ui.png differ
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000000..87b738cbd0
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..4b7e1f3d38
--- /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-5.5.1-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
new file mode 100755
index 0000000000..af6708ff22
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+## 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"'
+
+# 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, switch paths to Windows format before running java
+if $cygwin ; 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=$((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"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000000..6d57edc706
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,84 @@
+@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 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"
+
+@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/settings.gradle b/settings.gradle
new file mode 100644
index 0000000000..d1e92fe5db
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1 @@
+rootProject.name = 'duke'
diff --git a/src/main/java/DialogBox.java b/src/main/java/DialogBox.java
new file mode 100644
index 0000000000..49eda03543
--- /dev/null
+++ b/src/main/java/DialogBox.java
@@ -0,0 +1,105 @@
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import javafx.geometry.Pos;
+import javafx.scene.Node;
+import javafx.scene.control.Label;
+import javafx.scene.image.ImageView;
+import javafx.scene.layout.*;
+
+import javafx.scene.image.Image;
+import javafx.scene.image.ImageView;
+import javafx.scene.layout.HBox;
+import javafx.scene.paint.Color;
+
+import javafx.fxml.FXML;
+import javafx.fxml.FXMLLoader;
+
+import java.io.IOException;
+
+import javafx.geometry.Insets;
+import java.util.Collections;
+
+/**
+ * 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.
+ */
+
+//Some design for DialogBox.fxml
+//adapted from https://github.com/calvincxz/duke/blob/master/src/main/resources/view/DialogBox.fxml
+public class DialogBox extends HBox {
+
+ @FXML
+ private Label dialog;
+ @FXML
+ private ImageView displayPicture;
+
+ private static final Color DIALOGBOX_BACKGROUND_RED = Color.rgb(200,0,0);
+ private static final Color DIALOGBOX_BACKGROUND_BLUE = Color.rgb(0,0,200);
+ private static final Color DIALOGBOX_BACKGROUND_GREEN = Color.rgb(0,150,0);
+
+ private static final CornerRadii DIALOGBOX_BACKGROUND_RADII = new CornerRadii(10);
+
+ private static final Insets DIALOGBOX_BACKGROUND_INSET = new Insets(0,0,0,0);
+
+ private DialogBox(String text, Image img) {
+ try {
+ FXMLLoader fxmlLoader = new FXMLLoader(MainWindow.class.getResource("/view/DialogBox.fxml"));
+ fxmlLoader.setController(this);
+ fxmlLoader.setRoot(this);
+ fxmlLoader.load();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ dialog.setText(text);
+ displayPicture.setImage(img);
+ }
+
+ /**
+ * Flips the dialog box such that the ImageView is on the left and text on the right.
+ */
+ private void flip() {
+ ObservableList tmp = FXCollections.observableArrayList(this.getChildren());
+ Collections.reverse(tmp);
+ getChildren().setAll(tmp);
+ setAlignment(Pos.TOP_LEFT);
+ }
+
+ // sets the background to a blue rounded corner box
+ public static DialogBox getUserDialog(String text, Image img) {
+ DialogBox user = new DialogBox(text, img);
+ user.setBackground(
+ new Background(
+ new BackgroundFill(
+ DIALOGBOX_BACKGROUND_BLUE,
+ DIALOGBOX_BACKGROUND_RADII,
+ DIALOGBOX_BACKGROUND_INSET)));
+ return user;
+ }
+
+ //sets the background to green or red depending on error status
+ public static DialogBox getDukeDialog(String text, Image img, int errorStatus) {
+ DialogBox duke = new DialogBox(text, img);
+
+ Color[] colours = new Color[]{DIALOGBOX_BACKGROUND_GREEN,
+ DIALOGBOX_BACKGROUND_RED};
+ Color correctColor = colours[errorStatus];
+ duke.setBackground(
+ new Background(
+ new BackgroundFill(
+ correctColor,
+ DIALOGBOX_BACKGROUND_RADII,
+ DIALOGBOX_BACKGROUND_INSET)));
+
+
+ duke.flip();
+
+ return duke;
+ }
+
+
+
+
+
+}
\ No newline at end of file
diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java
index 5d313334cc..d98b68e31a 100644
--- a/src/main/java/Duke.java
+++ b/src/main/java/Duke.java
@@ -1,10 +1,49 @@
+import duke.commands.Command;
+import duke.commands.CommandType;
+
+import duke.core.Storage;
+import duke.core.Ui;
+import duke.core.TaskList;
+import duke.core.Parser;
+
+import duke.errors.DukeException;
+import duke.tasks.Task;
+
+import java.io.IOException;
+
+
+/**
+ * The driver class to run user interface of Duke. Duke provides commands to add different tasks,
+ * list out tasks, marking tasks as done, deleting tasks and storing the tasks into a file for
+ * retrieval after reboot
+ */
public class Duke {
- public static void main(String[] args) {
- String logo = " ____ _ \n"
- + "| _ \\ _ _| | _____ \n"
- + "| | | | | | | |/ / _ \\\n"
- + "| |_| | |_| | < __/\n"
- + "|____/ \\__,_|_|\\_\\___|\n";
- System.out.println("Hello from\n" + logo);
+
+ private Ui ui;
+ private TaskList taskList;
+
+
+ /**
+ * Initialises a new Duke application.
+ * @throws DukeException Thrown when parts of the command cannot be executed.
+ */
+ Duke() throws DukeException {
+ Storage storage = Storage.createStorageIfRequired();
+ this.taskList = new TaskList(storage.load(), storage);
+ Task.setTaskList(taskList);
+ this.ui = new Ui();
}
+
+
+ Response getResponse(String input) {
+ try {
+ Command c = Parser.parseCommand(input);
+ return new Response(c.execute(taskList, ui),false);
+ } catch (IllegalArgumentException | DukeException | IOException error2) {
+ return new Response(ui.printErrorMessage(error2),true);
+ }
+ }
+
+
}
+
diff --git a/src/main/java/Launcher.java b/src/main/java/Launcher.java
new file mode 100644
index 0000000000..11dbf00c62
--- /dev/null
+++ b/src/main/java/Launcher.java
@@ -0,0 +1,10 @@
+import javafx.application.Application;
+
+/**
+ * A launcher class to workaround classpath issues.
+ */
+public class Launcher {
+ public static void main(String[] args) {
+ Application.launch(Main.class, args);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/Main.java b/src/main/java/Main.java
new file mode 100644
index 0000000000..35a848aded
--- /dev/null
+++ b/src/main/java/Main.java
@@ -0,0 +1,35 @@
+import duke.errors.DukeException;
+import javafx.application.Application;
+import javafx.stage.Stage;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Scene;
+import javafx.scene.layout.AnchorPane;
+
+import java.io.IOException;
+
+/**
+ * A GUI for Duke using FXML.
+ */
+public class Main extends Application {
+
+ @Override
+ public void start(Stage stage) throws DukeException {
+ try {
+ Duke duke = new Duke();
+
+ 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/MainWindow.java b/src/main/java/MainWindow.java
new file mode 100644
index 0000000000..95766d6d94
--- /dev/null
+++ b/src/main/java/MainWindow.java
@@ -0,0 +1,75 @@
+import javafx.application.Platform;
+import javafx.fxml.FXML;
+import javafx.scene.control.Button;
+import javafx.scene.control.Dialog;
+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 duke.core.Ui;
+
+
+/**
+ * Controller for MainWindow. Provides the layout for the other controls.
+ */
+
+//Some design for MainWindow.fxml
+//adapted from https://github.com/calvincxz/duke/blob/master/src/main/resources/view/MainWindow.fxml
+public class MainWindow extends AnchorPane {
+ @FXML
+ private ScrollPane scrollPane;
+ @FXML
+ private VBox dialogContainer;
+ @FXML
+ private TextField userInput;
+ @FXML
+ private Button sendButton;
+
+ private Duke duke;
+
+ private Image userImage = new Image(this.getClass().getResourceAsStream("/images/DaUser.png"));
+ private Image dukeImage = new Image(this.getClass().getResourceAsStream("/images/DaDuke.png"));
+
+ private Ui ui = new Ui();
+
+ @FXML
+ public void initialize() {
+ scrollPane.vvalueProperty().bind(dialogContainer.heightProperty());
+ }
+
+
+ public void setDuke(Duke d) {
+ duke = d;
+ dialogContainer.getChildren().add(DialogBox.getDukeDialog(ui.printWelcomeMessage(), dukeImage, 0));
+ }
+
+ /**
+ * Creates two dialog boxes, one echoing user input and the other containing Duke's reply and then appends them to
+ * the dialog container. Clears the user input after processing.
+ */
+ @FXML
+ private void handleUserInput() {
+ String input = userInput.getText();
+ Response response = duke.getResponse(input);
+ int mode = booleanToInteger(response.getErrorStatus());
+ dialogContainer.getChildren().addAll(
+ DialogBox.getUserDialog(input, userImage),
+ DialogBox.getDukeDialog(response.getMessage(), dukeImage,mode)
+ );
+ if (input.equals("bye")) {
+ Platform.exit();
+ System.exit(0);
+ }
+ userInput.clear();
+ }
+
+ private int booleanToInteger(boolean isTrue) {
+ if (isTrue) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+}
diff --git a/src/main/java/Response.java b/src/main/java/Response.java
new file mode 100644
index 0000000000..2f66a8dbf9
--- /dev/null
+++ b/src/main/java/Response.java
@@ -0,0 +1,42 @@
+/**
+ * Represents a response from Duke due to some user input.
+ * A response provides the getter methods to its message and error status
+ */
+class Response {
+
+ private String message;
+ private boolean isError;
+
+ /**
+ * Initialises a Response that has a default isDone field of false.
+ *
+ * @param message Message to be displayed
+ * @param isError Error status tagged to the message
+ */
+ Response(String message, boolean isError) {
+ this.message = message;
+ this.isError = isError;
+ }
+
+ /**
+ * Returns the message to be shown to the user
+ *
+ * @return Message to be displayed
+ */
+ String getMessage() {
+ return message;
+ }
+
+ /**
+ * Returns the error status tagged to the message
+ *
+ * @return Error status
+ */
+ boolean getErrorStatus() {
+ return isError;
+ }
+
+
+
+
+}
diff --git a/src/main/java/duke/commands/AddDeadlineCommand.java b/src/main/java/duke/commands/AddDeadlineCommand.java
new file mode 100755
index 0000000000..491b0469ba
--- /dev/null
+++ b/src/main/java/duke/commands/AddDeadlineCommand.java
@@ -0,0 +1,54 @@
+package duke.commands;
+
+import java.io.IOException;
+
+
+import duke.core.TaskList;
+import duke.core.Ui;
+
+import duke.errors.DukeAssertions;
+import duke.tasks.Deadline;
+
+
+/**
+ * Represents a command which contains an execute method that adds a deadline task to the task list.
+ * The AddDeadlineCommand object requires the parameters of the task that is to be
+ * added to the list.
+ */
+public class AddDeadlineCommand extends Command {
+
+ private String description;
+ private String date;
+
+ /**
+ * Initialises the add command which contains the parameters of the task to be created
+ *
+ * @param description deadline description
+ * @param date date description
+ */
+ public AddDeadlineCommand(String description, String date) {
+ super(CommandType.COMMAND_ADD_DEADLINE);
+ this.description = description;
+ this.date = date;
+
+ DukeAssertions.assertNotNull(description,date);
+ }
+
+ /**
+ * Adds the deadline task to the task list and prints the result.
+ *
+ * @param taskList The main task list of the application.
+ * @param ui The main user interface of the application.
+ * @throws IOException Thrown when the new task cannot be added to the file.
+ */
+ @Override
+ public String execute(TaskList taskList, Ui ui) throws IOException {
+ Deadline task = new Deadline(this.description,this.date);
+
+ DukeAssertions.assertNotNull(taskList,ui);
+
+ taskList.addToList(task);
+ return ui.printAddMessage(task, taskList);
+ }
+
+}
diff --git a/src/main/java/duke/commands/AddEventCommand.java b/src/main/java/duke/commands/AddEventCommand.java
new file mode 100755
index 0000000000..4994a52703
--- /dev/null
+++ b/src/main/java/duke/commands/AddEventCommand.java
@@ -0,0 +1,56 @@
+package duke.commands;
+
+import java.io.IOException;
+
+
+import duke.core.TaskList;
+import duke.core.Ui;
+
+
+import duke.errors.DukeAssertions;
+import duke.tasks.Event;
+
+
+/**
+ * Represents a command which contains an execute method that adds a event task to the task list.
+ * The AddEventCommand object requires the parameters of the task that is to be
+ * added to the list.
+ */
+public class AddEventCommand extends Command{
+
+ private String description;
+ private String date;
+
+
+ /**
+ * Initialises the add command which contains the parameters of the task to be created
+ * Constructor to creating a command for adding an event task
+ * @param description event description
+ * @param date date description
+ */
+ public AddEventCommand(String description, String date) {
+ super(CommandType.COMMAND_ADD_EVENT);
+ this.description = description;
+ this.date = date;
+
+ DukeAssertions.assertNotNull(description,date);
+ }
+
+ /**
+ * Adds the event task to the task list and prints the result.
+ *
+ * @param taskList The main task list of the application.
+ * @param ui The main user interface of the application.
+ * @throws IOException Thrown when the new task cannot be added to the file.
+ */
+ public String execute(TaskList taskList, Ui ui) throws IOException {
+ Event task = new Event(this.description,this.date);
+
+ DukeAssertions.assertNotNull(taskList,ui);
+
+ taskList.addToList(task);
+ return ui.printAddMessage(task, taskList);
+ }
+
+
+}
diff --git a/src/main/java/duke/commands/AddToDoCommand.java b/src/main/java/duke/commands/AddToDoCommand.java
new file mode 100755
index 0000000000..ed2fbcc5d8
--- /dev/null
+++ b/src/main/java/duke/commands/AddToDoCommand.java
@@ -0,0 +1,47 @@
+package duke.commands;
+
+import java.io.IOException;
+
+import duke.core.TaskList;
+import duke.core.Ui;
+
+import duke.errors.DukeAssertions;
+import duke.tasks.ToDo;
+
+
+/**
+ * Represents a command which contains an execute method that adds a to-do task to the task list.
+ * The AddToDoCommand object requires the parameters of the task that is to be
+ * added to the list.
+ */
+public class AddToDoCommand extends Command{
+
+ private String [] tokens;
+
+ /**
+ * Initialises the add command which contains the parameters of the task to be created
+ *
+ * @param tokens user input split by space, required for creating a to-do task
+ */
+ public AddToDoCommand(String [] tokens) {
+ super(CommandType.COMMAND_ADD_TODO);
+ this.tokens = tokens;
+
+ }
+
+ /**
+ * Adds the to-do task to the task list and prints the result.
+ *
+ * @param taskList The main task list of the application.
+ * @param ui The main user interface of the application.
+ * @throws IOException Thrown when the new task cannot be added to the file.
+ */
+ @Override
+ public String execute(TaskList taskList, Ui ui) throws IOException {
+ ToDo task = ToDo.createToDo(tokens);
+ DukeAssertions.assertNotNull(taskList,ui);
+ taskList.addToList(task);
+ return ui.printAddMessage(task, taskList);
+ }
+
+}
diff --git a/src/main/java/duke/commands/Command.java b/src/main/java/duke/commands/Command.java
new file mode 100755
index 0000000000..57bd8a46db
--- /dev/null
+++ b/src/main/java/duke/commands/Command.java
@@ -0,0 +1,52 @@
+package duke.commands;
+
+import java.io.IOException;
+
+import duke.errors.DukeAssertions;
+import duke.errors.DukeException;
+
+import duke.core.TaskList;
+import duke.core.Ui;
+
+/**
+ * Base class for the other commands. Specifies the abstract method that is required to be implemented
+ * by the children commands.
+ */
+public abstract class Command {
+ private CommandType commandType;
+
+ /**
+ * Initialises the command of a specific command type
+ *
+ * @param commandType The type of command to be created.
+ */
+ public Command(CommandType commandType) {
+ this.commandType = commandType;
+
+ DukeAssertions.assertNotNull(commandType);
+ }
+
+
+ /**
+ * getter for type of command
+ *
+ * @return type of command
+ */
+ public CommandType getCommandType() {
+ return commandType;
+ }
+
+ /**
+ * Executes what the command is suppose to do.
+ *
+ * @param taskList The main task list of the application.
+ * @param ui The main user interface of the application.
+ * @throws DukeException Occurs when parts of the command cannot be executed.
+ * @throws IOException Thrown when the file update fails.
+ */
+ public abstract String execute(TaskList taskList, Ui ui) throws DukeException, IOException;
+
+}
+
+
+
diff --git a/src/main/java/duke/commands/CommandType.java b/src/main/java/duke/commands/CommandType.java
new file mode 100644
index 0000000000..fb321e615b
--- /dev/null
+++ b/src/main/java/duke/commands/CommandType.java
@@ -0,0 +1,18 @@
+package duke.commands;
+
+/**
+ * Represents the different command types
+ */
+public enum CommandType {
+ COMMAND_ADD_TODO,
+ COMMAND_ADD_EVENT,
+ COMMAND_ADD_DEADLINE,
+ COMMAND_SHOW_LIST,
+ COMMAND_DELETE_TASK,
+ COMMAND_DONE_TASK,
+ COMMAND_NULL,
+ COMMAND_EXIT,
+ COMMAND_FIND_TASK,
+ COMMAND_HELP
+}
+
diff --git a/src/main/java/duke/commands/DeleteCommand.java b/src/main/java/duke/commands/DeleteCommand.java
new file mode 100755
index 0000000000..95e534339e
--- /dev/null
+++ b/src/main/java/duke/commands/DeleteCommand.java
@@ -0,0 +1,71 @@
+package duke.commands;
+
+import java.io.IOException;
+
+import duke.core.TaskList;
+import duke.core.Ui;
+
+import duke.errors.DukeAssertions;
+import duke.errors.DukeException;
+import duke.errors.DukeExceptionType;
+
+import duke.tasks.Task;
+
+/**
+ * Represents a command which contains an execute method that deletes a task to the task list.
+ * The DeleteCommand object requires the task number of the task in the list that is to be
+ * deleted from the list.
+ */
+
+public class DeleteCommand extends Command {
+
+ private int index;
+
+
+ /**
+ * Initialises the command which contains the index of the task to be deleted
+ * @param index The index of the task to be deleted
+ */
+ private DeleteCommand(int index){
+ super(CommandType.COMMAND_DELETE_TASK);
+ this.index = index;
+
+ assert index >= 0;
+ }
+
+ /**
+ * Service for creating a delete command that checks for number formatting errors
+ * @param tokens User input split by space, required for creating a delete command
+ * @throws DukeException Thrown when the parameters does not specify the index of the task
+ */
+ public static DeleteCommand createDeleteIfValid(String [] tokens) throws DukeException {
+ try {
+ int index = Integer.parseInt(tokens[1])-1;
+ return new DeleteCommand(index);
+ } catch (NumberFormatException error) {
+ throw new DukeException("Must be integer", DukeExceptionType.NOT_INTEGER);
+ }
+ }
+
+ /**
+ * Deletes the specified task from the task list and prints the result.
+ *
+ * @param taskList The main task list of the application.
+ * @param ui The main user interface of the application.
+ * @throws IOException Thrown when the task cannot be removed from the file.
+ */
+ @Override
+ public String execute(TaskList taskList, Ui ui) throws IOException {
+ assert ui != null;
+ try {
+ assert taskList != null;
+
+ Task task = taskList.getTaskAt(index+1);
+ taskList.removeFromList(task);
+ return ui.printDeletion(task, taskList);
+ } catch (IndexOutOfBoundsException error3) {
+ return ui.printOneLine(new DukeException("No such task", DukeExceptionType.MISSING_TASK).getMessage());
+ }
+ }
+
+}
diff --git a/src/main/java/duke/commands/DoneCommand.java b/src/main/java/duke/commands/DoneCommand.java
new file mode 100755
index 0000000000..ede8cc78cd
--- /dev/null
+++ b/src/main/java/duke/commands/DoneCommand.java
@@ -0,0 +1,83 @@
+package duke.commands;
+
+import java.io.IOException;
+import java.util.List;
+
+
+import duke.core.TaskList;
+import duke.core.Ui;
+
+import duke.errors.DukeException;
+import duke.errors.DukeExceptionType;
+
+import duke.tasks.Task;
+
+
+/**
+ * Represents a command which contains an execute method that marks a task in the task list as done.
+ * The DoneCommand object requires the task number of the task that is to be marked in the list.
+ */
+public class DoneCommand extends Command{
+
+ private int index;
+
+ /**
+ * Initialises the command which contains the index of the task
+ * to be marked as done
+ *
+ * @param index the index of the task to be deleted
+ */
+ private DoneCommand(int index){
+ super(CommandType.COMMAND_DONE_TASK);
+ this.index = index;
+ assert index >= 0;
+ }
+
+
+ /**
+ * Service for creating a done command that checks for number formatting errors
+ *
+ * @param tokens User input split by space, required for creating a done command
+ * @throws DukeException Thrown when the parameters does not specify the index of the task
+ */
+ public static DoneCommand createDoneIfValid(String [] tokens) throws DukeException {
+ try {
+ int index = Integer.parseInt(tokens[1])-1;
+ return new DoneCommand(index);
+ } catch (NumberFormatException error) {
+ throw new DukeException("Must be integer", DukeExceptionType.NOT_INTEGER);
+ }
+ }
+
+ /**
+ * Executes by marking a particular task as done and prints to the user
+ *
+ * @param taskList The main task list of the application.
+ * @param ui The main user interface of the application.
+ * @throws IOException Thrown when the file update fails.
+ */
+ @Override
+ public String execute(TaskList taskList, Ui ui) throws IOException {
+ assert ui != null;
+ try {
+ assert taskList != null;
+
+ Task task = taskList.getTaskAt(index+1);
+ boolean isDoneBefore = task.setDone();
+ if (isDoneBefore) {
+ throw new IllegalArgumentException("Task has already been done");
+ }
+ taskList.setDoneInList(this.index+1);
+
+ List inst = List.of("Nice! I've marked this task as done: ",
+ " "+task.toString());
+ return ui.printInput(inst);
+
+ } catch (IndexOutOfBoundsException error3) {
+ return ui.printOneLine(new DukeException("No such task", DukeExceptionType.MISSING_TASK).getMessage());
+ } catch (IllegalArgumentException error2) {
+ return ui.printOneLine(new DukeException(error2.getMessage(), DukeExceptionType.TASK_ALREADY_DONE).getMessage());
+ }
+ }
+
+}
diff --git a/src/main/java/duke/commands/ExitCommand.java b/src/main/java/duke/commands/ExitCommand.java
new file mode 100755
index 0000000000..40e7480c20
--- /dev/null
+++ b/src/main/java/duke/commands/ExitCommand.java
@@ -0,0 +1,31 @@
+package duke.commands;
+
+import duke.core.TaskList;
+import duke.core.Ui;
+
+/**
+ * Represents a command which contains an execute method to exit the application.
+ */
+public class ExitCommand extends Command {
+
+ /**
+ * Initialises the exit command
+ */
+ public ExitCommand(){
+ super(CommandType.COMMAND_EXIT);
+ }
+
+ /**
+ * Prints the exit message.
+ *
+ * @param taskList The main task list of the application.
+ * @param ui The main user interface of the application.
+ */
+ @Override
+ public String execute(TaskList taskList, Ui ui) {
+ assert ui != null;
+ return ui.printByeMessage();
+ }
+
+}
+
diff --git a/src/main/java/duke/commands/FindCommand.java b/src/main/java/duke/commands/FindCommand.java
new file mode 100644
index 0000000000..d105a26f37
--- /dev/null
+++ b/src/main/java/duke/commands/FindCommand.java
@@ -0,0 +1,62 @@
+package duke.commands;
+
+import java.util.List;
+
+import duke.core.TaskList;
+import duke.core.Ui;
+import duke.errors.DukeAssertions;
+import duke.errors.DukeException;
+import duke.errors.DukeExceptionType;
+import duke.tasks.Task;
+
+
+
+/**
+ * Represents a command which contains an execute method that finds tasks with the matching keyword.
+ */
+public class FindCommand extends Command {
+
+ private String keyword;
+
+
+
+
+ /**
+ * Initialises the find command which contains the keyword
+ * where the tasks will be searched against
+ *
+ * @param keyword Keyword to be searched against
+ */
+ private FindCommand(String keyword) {
+ super(CommandType.COMMAND_FIND_TASK);
+ this.keyword = keyword;
+ assert keyword != null;
+ }
+
+ /**
+ * Service for creating a find command that checks for multiple keywords
+ * @param tokens User input split by space, required for creating a find command
+ * @throws DukeException Thrown when the parameters contains multiple keywords
+ */
+ public static FindCommand createFindCommandIfValid(String[] tokens) throws DukeException{
+ if (tokens.length > 2) {
+ throw new DukeException("Must be a single keyword", DukeExceptionType.NOT_SINGLE_WORD);
+ }
+ return new FindCommand(tokens[1]);
+ }
+
+ /**
+ * Executes by storing all tasks with descriptions containing the keyword
+ * and prints to the user
+ * @param taskList The main task list of the application.
+ * @param ui The main user interface of the application.
+ */
+ public String execute(TaskList taskList, Ui ui) {
+ DukeAssertions.assertNotNull(taskList, ui);
+ List resultList = taskList.findTasks(this.keyword);
+ return ui.printFindResults(resultList);
+ }
+
+
+}
+
diff --git a/src/main/java/duke/commands/HelpCommand.java b/src/main/java/duke/commands/HelpCommand.java
new file mode 100644
index 0000000000..398dfed80e
--- /dev/null
+++ b/src/main/java/duke/commands/HelpCommand.java
@@ -0,0 +1,29 @@
+package duke.commands;
+import duke.core.TaskList;
+import duke.core.Ui;
+import duke.errors.DukeAssertions;
+
+/**
+ * Represents a command which contains an execute method that shows a help page.
+ */
+public class HelpCommand extends Command {
+
+ /**
+ * Initialises the list command
+ */
+ public HelpCommand(){
+ super(CommandType.COMMAND_HELP);
+ }
+
+ /**
+ * Shows a help page to the user
+ *
+ * @param taskList The main task list of the application.
+ * @param ui The main user interface of the application.
+ */
+ @Override
+ public String execute(TaskList taskList, Ui ui) {
+ DukeAssertions.assertNotNull(taskList, ui);
+ return ui.printHelpMessage();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/duke/commands/ListCommand.java b/src/main/java/duke/commands/ListCommand.java
new file mode 100755
index 0000000000..d32fc574c0
--- /dev/null
+++ b/src/main/java/duke/commands/ListCommand.java
@@ -0,0 +1,32 @@
+package duke.commands;
+
+
+import duke.core.TaskList;
+import duke.core.Ui;
+import duke.errors.DukeAssertions;
+
+
+/**
+ * Represents a command which contains an execute method that lists the tasks in the task list.
+ */
+public class ListCommand extends Command {
+
+ /**
+ * Initialises the list command
+ */
+ public ListCommand(){
+ super(CommandType.COMMAND_SHOW_LIST);
+ }
+
+ /**
+ * Lists all the tasks in the task list and prints them out.
+ *
+ * @param taskList The main task list of the application.
+ * @param ui The main user interface of the application.
+ */
+ @Override
+ public String execute(TaskList taskList, Ui ui) {
+ DukeAssertions.assertNotNull(taskList, ui);
+ return ui.printNumberList(taskList);
+ }
+}
diff --git a/src/main/java/duke/commands/NullCommand.java b/src/main/java/duke/commands/NullCommand.java
new file mode 100755
index 0000000000..94f10c090a
--- /dev/null
+++ b/src/main/java/duke/commands/NullCommand.java
@@ -0,0 +1,35 @@
+package duke.commands;
+
+
+import duke.core.TaskList;
+import duke.core.Ui;
+
+import duke.errors.DukeException;
+import duke.errors.DukeExceptionType;
+
+/**
+ * Represents a command that is invalid
+ */
+public class NullCommand extends Command{
+
+ /**
+ * Initialises the null command
+ */
+ public NullCommand(){
+ super(CommandType.COMMAND_NULL);
+ }
+
+ /**
+ * Throws an exception which tells
+ * the user that the input command is not valid
+ *
+ * @param taskList The main task list of the application.
+ * @param ui The main user interface of the application.
+ * @throws DukeException Thrown when the command does not exist
+ */
+ @Override
+ public String execute(TaskList taskList, Ui ui) throws DukeException {
+ throw new DukeException("Invalid Command! Please try again.", DukeExceptionType.INVALID_COMMAND);
+ }
+
+}
diff --git a/src/main/java/duke/core/Parser.java b/src/main/java/duke/core/Parser.java
new file mode 100644
index 0000000000..0c3c2783ed
--- /dev/null
+++ b/src/main/java/duke/core/Parser.java
@@ -0,0 +1,254 @@
+package duke.core;
+
+import duke.commands.*;
+
+import duke.errors.DukeAssertions;
+import duke.errors.DukeException;
+import duke.errors.DukeExceptionType;
+
+import java.util.List;
+import java.lang.StringBuilder;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.time.LocalDateTime;
+import java.util.Arrays;
+
+/**
+ * Represents a class that takes in user inputs and translates them into different commands.
+ */
+public class Parser {
+
+ private static String[] responses = new String[]{"/by","/at"};
+ private static int[] startingIndex = new int[]{9,6};
+
+ /**
+ * Takes in user input and convert it into a command which performs a set of
+ * instructions on the task list and ui.
+ *
+ * @param input String that contains user input
+ * @return A command that execute a set of instructions.
+ * @throws IllegalArgumentException Thrown when the length of the command is not sufficient
+ * @throws DukeException Thrown when exceptions occur due to non-length checks
+ */
+ public static Command parseCommand(String input) throws DukeException, IllegalArgumentException {
+ String[] tokens = input.split(" ");
+ if (tokens.length == 0) {
+ return new NullCommand();
+ } else if (tokens[0].equals("bye")) {
+ return new ExitCommand();
+ } else if (tokens[0].equals("list")) {
+ return new ListCommand();
+ } else if (tokens[0].equals("help")) {
+ return new HelpCommand();
+ }
+ checkValidLength(tokens);
+
+ if (tokens[0].equals("done")) {
+ return DoneCommand.createDoneIfValid(tokens);
+ } else if (tokens[0].equals("delete")) {
+ return DeleteCommand.createDeleteIfValid(tokens);
+ } else if (tokens[0].equals("find")) {
+ return FindCommand.createFindCommandIfValid(tokens);
+ } else {
+ return Parser.createAddCommandIfValid(tokens, input);
+ }
+ }
+
+ /**
+ * Throws an exception which tells
+ * the user that the input command is not valid, by checking the length of command
+ * the user that the input lacks information based on each case
+ *
+ * @param tokens User input split by space
+ * @throws IllegalArgumentException Thrown when the length of the command is not sufficient
+ */
+ private static void checkValidLength(String[] tokens) throws IllegalArgumentException {
+ DukeAssertions.assertArrayNotEmpty(tokens);
+ List group1 = List.of("todo", "deadline", "event");
+ List group2 = List.of("done", "delete");
+ if (tokens.length == 1 && group1.contains(tokens[0])) {
+ throw new IllegalArgumentException(String.format("OOPS!!! The description of a %s cannot be empty.",tokens[0]));
+ } else if (tokens.length == 1 && group2.contains(tokens[0])) {
+ throw new IllegalArgumentException(String.format("OOPS!!! %s command requires integer.",tokens[0]));
+ } else if (tokens.length == 1 && tokens[0].equals("find")) {
+ throw new IllegalArgumentException(String.format("OOPS!!! %s command requires keyword input.",tokens[0]));
+ }
+ }
+
+ // helper method to check if user input can still be a valid to-do, deadline or event task
+ private static Command createAddCommandIfValid(String[] tokens, String fullCommand) throws DukeException, IllegalArgumentException {
+ DukeAssertions.assertArrayNotEmpty(tokens);
+
+ List validCommands = List.of("todo", "deadline", "event");
+
+ if (!validCommands.contains(tokens[0])) {
+ throw new DukeException("Command doesn't exist", DukeExceptionType.INVALID_COMMAND);
+ }
+ if (tokens[0].equals("todo")) {
+ return new AddToDoCommand(tokens);
+ } else if (tokens[0].equals("deadline")) {
+ return createDateCommandIfValid(tokens,fullCommand,0);
+ } else if (tokens[0].equals("event")) {
+ return createDateCommandIfValid(tokens,fullCommand,1);
+ } else {
+ return new NullCommand();
+ }
+ }
+
+ /**
+ * Takes in a string and tries to parse input string as Date and Time in dd/MM/yyyy HHmm,
+ * converting it into a more readable format
+ * eg. dd/MM/yyyy HHmm(e.g. 11/12/1111 1111 -> 11th of DECEMBER 1111, 11:11am)
+ *
+ * @param dateTimeString String to be parsed, and converted, if possible
+ * @return The formatted date and time, if it can be formatted
+ * @throws DukeException Thrown when the input cannot be formatted
+ */
+
+ //@@author qweiping31415-reused
+ //Reused from https://github.com/briyanii/duke/blob/master/src/main/java/duke/command/Parser.java
+ // with minor modifications
+ public static String parseDateTime(String dateTimeString) throws DukeException {
+ assert dateTimeString != null;
+ try {
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HHmm");
+ LocalDateTime dateAndTime = LocalDateTime.parse(dateTimeString, formatter);
+
+ int year = dateAndTime.getYear();
+ String month = dateAndTime.getMonth().toString();
+ int day = dateAndTime.getDayOfMonth();
+ int hour = dateAndTime.getHour();
+ int minute = dateAndTime.getMinute();
+
+ StringBuilder dateTime = new StringBuilder();
+
+ dateTime.append(getFormattedDay(day));
+ dateTime.append(" of ");
+ dateTime.append(month);
+ dateTime.append(" ");
+ dateTime.append(year);
+ dateTime.append(", ");
+ dateTime.append((hour > 12 ? hour - 12 : hour == 0 ? 12 : hour));
+ if (minute != 0) {
+ dateTime.append(":");
+ dateTime.append(minute);
+ }
+ if (hour < 12) {
+ dateTime.append("am");
+ } else {
+ dateTime.append("pm");
+ }
+
+ return dateTime.toString();
+ } catch (DateTimeParseException exception) {
+ throw new DukeException(dateTimeString + " is not in valid dd/MM/yyyy HHmm format.",
+ DukeExceptionType.INVALID_DATE_TIME_FORMAT);
+ }
+ }
+
+ // helper method to attach a prefix to a day
+ private static String getFormattedDay(int day) {
+ assert day > 1;
+ int remainderHundred = day % 100;
+ if (remainderHundred > 9 && remainderHundred < 21) {
+ return day + "th";
+ } else {
+ int remainderTen = day % 10;
+ switch (remainderTen) {
+ case 1:
+ return day + "st";
+ case 2:
+ return day + "nd";
+ case 3:
+ return day + "rd";
+ default:
+ return day + "th";
+ }
+ }
+ }
+ //@@author
+
+
+
+ // helper method to check if the given date and time of a deadline or event task
+ // can be recognised as a DateTime format.
+ private static Command createDateCommandIfValid(String[] tokens, String fullCommand, int mode)
+ throws DukeException, IllegalArgumentException {
+ DukeAssertions.assertArrayNotEmpty(tokens);
+ assert fullCommand != null;
+
+ List lst = Arrays.asList(tokens);
+ String key = responses[mode];
+ if (!lst.contains(key)) {
+ throw new IllegalArgumentException("Missing deadline keyword!!");
+ }
+
+ int index = lst.indexOf(key);
+ checkTaskDescription(index);
+ String[] datedTaskSplit = fullCommand.split(" " + key + " ");
+ checkDeadline(datedTaskSplit);
+ String description = datedTaskSplit[0].substring(startingIndex[mode]);
+ String dateTime = datedTaskSplit[1];
+ if (isDate(dateTime)) {
+ String dateTimeString = tokens[index + 1] + " " + tokens[index + 2];
+ dateTime = Parser.parseDateTime(dateTimeString);
+ }
+
+ return createDateCommand(mode, description, dateTime);
+ }
+
+ //helper method to create the correct kind of DateTime Command
+ private static Command createDateCommand(int mode, String description, String correctDate) {
+ if (mode==0) {
+ return new AddDeadlineCommand(description,correctDate);
+ } else {
+ return new AddEventCommand(description,correctDate);
+ }
+ }
+
+ //helper method to verify that the date is actually in dd/MM/yyyy HHmm format
+ private static boolean isDate(String dateDescription){
+ assert dateDescription != null;
+ String[] dateSplit = dateDescription.split(" ");
+ if (dateSplit.length != 2){
+ return false;
+ } else if (!dateSplit[0].contains("/") ||
+ checkSlashCount(dateSplit[0])) {
+ return false;
+ }
+ return true;
+ }
+
+ private static boolean checkSlashCount(String str) {
+ return str.chars()
+ .filter(ch -> ch == '/')
+ .count() != 2;
+ }
+
+ //helper method to check if the datetime task can have a description
+ private static void checkTaskDescription(int index) throws IllegalArgumentException {
+ if (index -1 <=0) {
+ throw new IllegalArgumentException("Please input task description for DateCommand");
+ }
+
+ }
+
+ //helper method to check if the datetime task is given a deadline
+ private static void checkDeadline(String [] datedTaskSplit) throws IllegalArgumentException {
+ if (datedTaskSplit.length > 2) {
+ throw new IllegalArgumentException("Multiple keyword detected!!");
+ }
+ if (datedTaskSplit.length < 2) {
+ throw new IllegalArgumentException("Please insert a due date!!!");
+ }
+ }
+
+
+
+}
+
+
+
+
+
+
diff --git a/src/main/java/duke/core/Storage.java b/src/main/java/duke/core/Storage.java
new file mode 100644
index 0000000000..3ec1af2f21
--- /dev/null
+++ b/src/main/java/duke/core/Storage.java
@@ -0,0 +1,160 @@
+package duke.core;
+
+import java.io.*;
+import java.nio.file.Paths;
+import java.util.Scanner;
+import java.util.ArrayList;
+
+import duke.tasks.Task;
+import duke.tasks.ToDo;
+import duke.tasks.Deadline;
+import duke.tasks.Event;
+
+import duke.errors.DukeException;
+import duke.errors.DukeExceptionType;
+
+
+
+
+/**
+ * Represents the storage of the application. Provides methods that create a storage,
+ * overwrite the contents of the file and loading data from the file.
+ */
+public class Storage{
+
+ private File file;
+
+ /**
+ * Initialises the Storage with the file
+ *
+ * @param file File Object
+ */
+ private Storage(File file) {
+ this.file = file;
+ }
+
+ /**
+ * Creates a Storage for the application
+ * It creates a new directory with a new text file if there is no existing file
+ *
+ * @return Storage for the application
+ */
+ public static Storage createStorageIfRequired() {
+ String path = System.getProperty("user.home");
+ path += File.separator + "DukeData";
+ File customDir = new File(path);
+ if (!customDir.exists()) {
+ customDir.mkdirs();
+ }
+
+ path += File.separator + "data.txt";
+ File file = new File(path);
+
+ if (!file.exists()) {
+ try {
+ file.createNewFile();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ return new Storage(file);
+ }
+
+
+
+ /**
+ * Reads the data stored in the file, after which the date would be used
+ * to generate a ArrayList that would be returned.
+ *
+ * @return An ArrayList of tasks.
+ * @throws DukeException Thrown when the file does not exist.
+ */
+ public ArrayList load() throws DukeException {
+ ArrayList taskList = new ArrayList<>();
+ try {
+ Scanner sc = new Scanner(this.file);
+ while (sc.hasNext()) {
+ String line = sc.nextLine();
+ taskList.add(formatFileToTask(line));
+ }
+ return taskList;
+ } catch (FileNotFoundException e) {
+ throw new DukeException("Load failed", DukeExceptionType.FILE_NOT_FOUND);
+ }
+ }
+
+
+ /**
+ * Overwrites the data in the file by writing to the file.
+ *
+ * @param taskList Current task list stored in the application.
+ * @throws IOException Thrown when writing to file fails.
+ */
+ void overwriteStorage(ArrayList taskList) throws IOException{
+ assert (this.file != null);
+
+ FileWriter fw = new FileWriter(this.file);
+ for (Task task: taskList){
+ switch (task.getType()) {
+ case TODO_TASK:
+ fw.write(Task.getTaskID(task) + " / " +
+ "ToDo" + " / " +
+ task.getStorageStatusIcon() + " / " +
+ task.getDescription() +
+ System.lineSeparator());
+ break;
+ case DEADLINE_TASK:
+ fw.write(Task.getTaskID(task) + " / " +
+ "Deadline" + " / " +
+ task.getStorageStatusIcon() + " / " +
+ task.getDescription() + " / " +
+ ((Deadline) task).getDate() +
+ System.lineSeparator());
+ break;
+ case EVENT_TASK:
+ fw.write(Task.getTaskID(task) + " / " +
+ "Event" + " / " +
+ task.getStorageStatusIcon() + " / " +
+ task.getDescription() + " / " +
+ ((Event) task).getDate() +
+ System.lineSeparator());
+ break;
+ default:
+ }
+ }
+ fw.close();
+ }
+
+ //helper method to convert the written format of the task in the file
+ //into a Task to be loaded back into storage
+ private Task formatFileToTask(String line) throws DukeException {
+ assert line != null;
+ String[] tokens = line.split(" / ");
+ switch(tokens[1]){
+ case "ToDo":
+ ToDo toDoTask = new ToDo(tokens[3]);
+ if (tokens[2].equals("1")){
+ toDoTask.setDone();
+ }
+ return toDoTask;
+ case "Deadline":
+ Deadline deadlineTask = new Deadline(tokens[3], tokens[4]);
+ if (tokens[2].equals("1")){
+ deadlineTask.setDone();
+ }
+ return deadlineTask;
+ case "Event":
+ Event eventTask = new Event(tokens[3], tokens[4]);
+ if (tokens[2].equals("1")){
+ eventTask.setDone();
+ }
+ return eventTask;
+ default:
+ throw new DukeException("Unknown task detected? Something is wrong.",
+ DukeExceptionType.TASK_NOT_FOUND);
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/src/main/java/duke/core/TaskList.java b/src/main/java/duke/core/TaskList.java
new file mode 100644
index 0000000000..01378a6760
--- /dev/null
+++ b/src/main/java/duke/core/TaskList.java
@@ -0,0 +1,139 @@
+package duke.core;
+
+import java.util.List;
+import java.io.IOException;
+import java.util.ArrayList;
+
+import duke.tasks.Task;
+
+/**
+ * Represents the task list used to store the tasks. Provides methods to add a task to the list,
+ * getting a List, getting a task using the index and removing
+ * a task based on the index specified, getting the size of the list,
+ * setting the done status of a task and getting the ID of a task
+ */
+public class TaskList {
+
+ private ArrayList taskList;
+ private Storage storage;
+
+ /**
+ * Initialises the TaskList, creates an ArrayList to store the tasks,
+ * and holds a reference to the main storage
+ *
+ * @param storage The main storage of the application.
+ */
+ public TaskList(Storage storage){
+ this.taskList = new ArrayList<>();
+ this.storage = storage;
+ }
+
+ /**
+ * Initialises the TaskList, with a given ArrayList to store the tasks,
+ * and holds a reference to the main storage
+ *
+ * @param list List to initialise with
+ * @param storage The main storage of the application.
+ */
+ public TaskList(ArrayList list, Storage storage){
+ this.taskList = list;
+ this.storage = storage;
+ }
+
+
+ /**
+ * Adds a task to the task list.
+ *
+ * @param task Task to be added.
+ * @throws IOException Thrown when writing to file fails.
+ */
+ public void addToList(Task task) throws IOException {
+ this.taskList.add(task);
+ this.storage.overwriteStorage(taskList);
+
+ }
+
+
+ /**
+ * Returns a ArrayList of type Task.
+ *
+ * @return An ArrayList of tasks.
+ */
+ public ArrayList getList(){
+ return this.taskList;
+ }
+
+
+ /**
+ * Returns a task at specified index.
+ *
+ * @param index Index of task to be retrieved.
+ * @return Task of the corresponding index.
+ */
+ public Task getTaskAt(int index){
+ return this.taskList.get(index - 1);
+ }
+
+
+ /**
+ * Removes a task from the list using its reference
+ *
+ * @param task Task to be removed.
+ * @throws IOException Thrown when writing to file fails.
+ */
+ public void removeFromList(Task task) throws IOException {
+ boolean isRemoved = this.taskList.remove(task);
+ if (isRemoved) {
+ this.storage.overwriteStorage(taskList);
+ }
+ }
+
+
+ /**
+ * Sets task at specified index to done
+ *
+ * @param index Index of task to be set as done
+ * @throws IOException Thrown when writing to file fails.
+ */
+ public void setDoneInList(int index) throws IOException {
+ this.taskList.get(index - 1).setDone();
+ this.storage.overwriteStorage(taskList);
+ }
+
+ /**
+ * Returns the number of tasks in current list
+ *
+ * @return Number of tasks in current list
+ */
+ int getNumTasks(){
+ return this.taskList.size();
+ }
+
+ /**
+ * Returns the position of the specified task in current list
+ *
+ * @return Position of the specified task in current list
+ */
+ public int getTaskID(Task task) {
+ return taskList.indexOf(task) + 1;
+ }
+
+
+ /**
+ * Returns a list of tasks containing the specific keyword
+ *
+ * @return List of tasks containing the specific keyword
+ */
+ public List findTasks(String word) {
+ ArrayList lst= new ArrayList<>();
+ for (Task task: taskList) {
+ if (task.getDescription().contains(word)) {
+ lst.add(task);
+ }
+ }
+ return lst;
+ }
+
+
+}
+
diff --git a/src/main/java/duke/core/Ui.java b/src/main/java/duke/core/Ui.java
new file mode 100644
index 0000000000..f0cb77abdd
--- /dev/null
+++ b/src/main/java/duke/core/Ui.java
@@ -0,0 +1,225 @@
+package duke.core;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Scanner;
+
+import duke.tasks.Task;
+
+
+/**
+ * Represents the user interface of the application. Provides methods for reading input
+ * and printing output to the console.
+ */
+public class Ui {
+
+ private Scanner sc = new Scanner(System.in);
+
+
+ /**
+ * Checks if there are inputs to be read
+ *
+ * @return True if there are inputs, false otherwise
+ */
+ public boolean hasInputs() {
+ return sc.hasNextLine();
+ }
+
+
+ /**
+ * Reads from standard input and returns it.
+ *
+ * @return String which consists of user input.
+ */
+ public String readCommand() {
+ return sc.nextLine();
+ }
+
+ /**
+ * Prints the welcome message of the application.
+ *
+ * @return Welcome message.
+ */
+ public String printWelcomeMessage() {
+ List start = new ArrayList<>();
+ start.add("Hello! I'm Duke");
+ start.add("What can I do for you?");
+ return this.printInput(start);
+ }
+
+ /**
+ * Prints a message when a task is added.
+ *
+ * @param task Task that was added.
+ * @param taskList Task list where the Task is stored.
+
+ */
+ public String printAddMessage(Task task, TaskList taskList) {
+ StringBuilder builder = new StringBuilder();
+
+ appendWithNewline(builder, "Got it. I've added this task: ");
+ appendWithNewline(builder, task.toString());
+ appendWithNewline(builder,
+ String.format("Now you have %d tasks in the list.", taskList.getNumTasks()));
+
+ return builder.toString();
+
+
+ }
+
+ /**
+ * Prints out the message when a task is deleted.
+ *
+ * @param task Task that was deleted.
+ * @param taskList Task list that the Task was removed from.
+ * @return String message to be printed.
+ */
+ public String printDeletion(Task task, TaskList taskList) {
+
+ StringBuilder builder = new StringBuilder();
+ appendWithNewline(builder, "Noted. I've removed this task: ");
+ appendWithNewline(builder, task.toString());
+ appendWithNewline(builder,
+ String.format("Now you have %d tasks in the list.", taskList.getNumTasks()));
+
+ return builder.toString();
+
+ }
+
+
+ /**
+ * Prints a list of string in the correct format
+ *
+ * @param start List of Strings which needs to be printed sequentially
+ * @return String message to be printed.
+ */
+ public String printInput(List start) {
+ StringBuilder builder = new StringBuilder();
+ for (String input : start) {
+ appendWithNewline(builder, input);
+ }
+
+ return builder.toString();
+
+ }
+
+ /**
+ * Prints a single line of input in the correct format
+ *
+ * @param input String to be printed
+ * @return String message to be printed.
+ */
+ public String printOneLine(String input) {
+ StringBuilder builder = new StringBuilder();
+ appendWithNewline(builder, input);
+
+ return builder.toString();
+
+ }
+
+ /**
+ * Prints the list of tasks.
+ *
+ * @param taskList List of tasks stored in the application
+ * @return String message to be printed.
+ */
+ public String printNumberList(TaskList taskList) {
+
+ StringBuilder builder = new StringBuilder();
+ appendWithNewline(builder, "Here are the tasks in your list:");
+ for (int i = 0; i < taskList.getNumTasks(); i++) {
+ appendWithNewline(builder,
+ String.format("%d.%s", i + 1, taskList.getTaskAt(i + 1)));
+ }
+
+ return builder.toString();
+ }
+
+
+ /**
+ * Prints the list of tasks.
+ *
+ * @param lst List of tasks with matching keyword
+ * @return String message to be printed.
+ */
+ public String printFindResults(List lst) {
+
+ StringBuilder builder = new StringBuilder();
+
+
+ builder.append("Here are the matching tasks in your list:");
+ builder.append("\n");
+ for (int i = 0; i < lst.size(); i++) {
+ appendWithNewline(builder,
+ String.format("%d.%s", i + 1, lst.get(i)));
+ }
+
+ return builder.toString();
+ }
+
+
+ /**
+ * Prints the exit message.
+ *
+ * @return String message to be printed.
+ */
+ public String printByeMessage() {
+
+ return this.printOneLine("Bye. Hope to see you again soon!");
+ }
+
+
+ /**
+ * Prints out the exception.
+ *
+ * @param e Exception object whose message is to be printed out
+ */
+ public String printErrorMessage(Exception e) {
+ return this.printOneLine(e.getMessage());
+ }
+
+ //helper method to attach a newline after adding a message to the same StringBuilder
+ private void appendWithNewline(StringBuilder builder, String msg) {
+ builder.append(msg);
+ builder.append("\n");
+ }
+
+ /**
+ * Prints out a mini user manual to help the user
+ *
+ * @return String message to be printed.
+ */
+ public String printHelpMessage() {
+ return printInput(List.of(
+ "List of commands with format:",
+ "",
+ " todo TASK_DESCRIPTION : Adds a todo task to list",
+ " deadline TASK_DESCRIPTION /by dd/mm/yyyy hhmm : Adds a deadline to list " +
+ " with a recognisable datetime format",
+ " event TASK_DESCRIPTION /at dd/mm/yyyy hhmm : Adds a deadline to list" +
+ " with a recognisable datetime format",
+ " deadline TASK_DESCRIPTION /by DUE_DATE : To add deadline to list with any due date format",
+ " event TASK_DESCRIPTION /at DUE_DATE : To add event to list with any due date format",
+ " e.g. todo report",
+ " e.g. deadline report /by 12/12/1212 2312",
+ " e.g. event meetup /at 12/12/1212 2312",
+ " e.g. deadline report /by Monday",
+ " e.g. event meetup /at Monday",
+ "",
+ " find KEYWORD : Find tasks with the word in the description/name",
+ "",
+ " done [TASK_NUMBER] : Checks task at a particular index as done",
+ " delete [TASK_NUMBER] : Deletes task at a particular index",
+ "",
+ " list : Shows all undeleted tasks",
+ " bye : Exit the program",
+ "",
+ " Note: bracketed inputs like [TASK_NUMBER] denote that only integer inputs are accepted"));
+ }
+
+
+}
+
+
+
+
diff --git a/src/main/java/duke/errors/DukeAssertions.java b/src/main/java/duke/errors/DukeAssertions.java
new file mode 100644
index 0000000000..434a972f8a
--- /dev/null
+++ b/src/main/java/duke/errors/DukeAssertions.java
@@ -0,0 +1,25 @@
+package duke.errors;
+
+/**
+ * Represents an helper class to assert null checks for any number of objects
+ */
+public class DukeAssertions {
+
+ /**
+ * Asserts that each object of any given number is not null
+ */
+ public static void assertNotNull(Object ... items) {
+ for (Object item : items) {
+ assert item != null;
+ }
+ }
+
+ /**
+ * Asserts that each String of any given number is not null
+ */
+ public static void assertArrayNotEmpty(String [] items) {
+ for (String item : items) {
+ assert item != null;
+ }
+ }
+}
diff --git a/src/main/java/duke/errors/DukeException.java b/src/main/java/duke/errors/DukeException.java
new file mode 100644
index 0000000000..1e470be6a1
--- /dev/null
+++ b/src/main/java/duke/errors/DukeException.java
@@ -0,0 +1,44 @@
+package duke.errors;
+
+
+/**
+ * Represents an Exception class in Duke.
+ */
+public class DukeException extends Exception {
+
+ private DukeExceptionType type;
+
+ public DukeException(String error, DukeExceptionType type){
+ super(error);
+ this.type = type;
+
+ assert type != null;
+ }
+
+ /**
+ * Specifies the message to be printed based on different Exception types
+ * @return String which is the message of the exception.
+ */
+ public String getMessage() {
+ switch (type) {
+ case INVALID_COMMAND:
+ return "OOPS!!! I'm sorry, but I don't know what that means :-(";
+ case NOT_INTEGER:
+ return "Invalid input, must be an integer!!";
+ case MISSING_TASK:
+ return "No such task";
+ case INVALID_DATE_TIME_FORMAT:
+ return super.getMessage();
+ case FILE_NOT_FOUND:
+ return "File not found!!";
+ case TASK_NOT_FOUND:
+ return "Task not found!!";
+ case NOT_SINGLE_WORD:
+ return "Must be a single keyword";
+ case TASK_ALREADY_DONE:
+ return "Task already done!";
+ default:
+ return "Unknown error! Please try again.";
+ }
+ }
+}
diff --git a/src/main/java/duke/errors/DukeExceptionType.java b/src/main/java/duke/errors/DukeExceptionType.java
new file mode 100644
index 0000000000..ffe239fa05
--- /dev/null
+++ b/src/main/java/duke/errors/DukeExceptionType.java
@@ -0,0 +1,15 @@
+package duke.errors;
+
+/**
+ * Represents different Exception types
+ */
+public enum DukeExceptionType {
+ INVALID_COMMAND,
+ NOT_INTEGER,
+ MISSING_TASK,
+ FILE_NOT_FOUND,
+ TASK_NOT_FOUND,
+ NOT_SINGLE_WORD,
+ TASK_ALREADY_DONE,
+ INVALID_DATE_TIME_FORMAT
+}
diff --git a/src/main/java/duke/tasks/Deadline.java b/src/main/java/duke/tasks/Deadline.java
new file mode 100644
index 0000000000..6cb5566421
--- /dev/null
+++ b/src/main/java/duke/tasks/Deadline.java
@@ -0,0 +1,44 @@
+package duke.tasks;
+
+/**
+ * Represents a deadline task in the application.
+ * A deadline provides the getter methods to its date.
+ */
+public class Deadline extends Task {
+
+ private String date;
+
+ /**
+ * Initialises an deadline task with the description and date and time of the deadline task.
+ *
+ * @param description Deadline description
+ * @param date Date description
+ */
+ public Deadline(String description, String date) {
+ super(description, TaskType.DEADLINE_TASK);
+ this.date = date;
+
+ assert date != null;
+ }
+
+ /**
+ * Returns a string containing the date and time of a deadline task
+ *
+ * @return String containing the date and time of a deadline task.
+ */
+ public String getDate() {
+ assert date != null;
+ return this.date;
+ }
+
+ /**
+ * Returns A string that includes the task type, description and date of the deadline task.
+ *
+ * @return String that includes the task type, description and date of the deadline task.
+ */
+ @Override
+ public String toString() {
+ return String.format("[D][%s] %s(by: %s)", getStatusIcon(),
+ getDescription(), getDate());
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/duke/tasks/Event.java b/src/main/java/duke/tasks/Event.java
new file mode 100644
index 0000000000..c41564ebbb
--- /dev/null
+++ b/src/main/java/duke/tasks/Event.java
@@ -0,0 +1,44 @@
+package duke.tasks;
+
+
+/**
+ * Represents an event task in the application.
+ * An event provides the getter methods to its date.
+ */
+public class Event extends Task {
+
+ private String date;
+
+ /**
+ * Initialises an deadline task with the description and date and time of the event task.
+ *
+ * @param description Event description
+ * @param date Date description
+ */
+ public Event(String description, String date) {
+ super(description, TaskType.EVENT_TASK);
+ this.date = date;
+ assert date != null;
+ }
+
+ /**
+ * Returns a string containing the date and time of a event task
+ *
+ * @return String containing the date and time of a event task.
+ */
+ public String getDate() {
+ assert date != null;
+ return this.date;
+ }
+
+ /**
+ * Returns A string that includes the task type, description and date of the event task.
+ *
+ * @return String that includes the task type, description and date of the event task.
+ */
+ @Override
+ public String toString() {
+ return String.format("[E][%s] %s(at: %s)", getStatusIcon(),
+ getDescription(), getDate());
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/duke/tasks/Task.java b/src/main/java/duke/tasks/Task.java
new file mode 100644
index 0000000000..4ab9db22eb
--- /dev/null
+++ b/src/main/java/duke/tasks/Task.java
@@ -0,0 +1,128 @@
+package duke.tasks;
+
+import duke.core.TaskList;
+import duke.errors.DukeAssertions;
+
+
+/**
+ * Represents a task in the application. A task has two private fields, the description of the task and
+ * the state of completion of the task. The type of task is package-private and taskList is a
+ * class attribute. The Task class provides the getters to type, description, completion status,
+ * the task list as well as getting the icon (tick and cross, or 1 and 0)
+ * which corresponds to the isDone field. Task class also
+ * supports a setDone method which sets isDone field to true and a setTaskList method which sets
+ * taskList field to the main task list of the application.
+ */
+public abstract class Task {
+ private String description;
+ private boolean isDone;
+ private TaskType type;
+
+ public static TaskList taskList;
+
+ /**
+ * Initialises a Task that has a default isDone field of false.
+ *
+ * @param description Description of the task.
+ * @param type Type of the task.
+ */
+ public Task(String description, TaskType type) {
+ this.description = description;
+ this.type = type;
+ this.isDone = false;
+
+ DukeAssertions.assertNotNull(description, type);
+
+ }
+
+ /**
+ * Returns the type of Task
+ *
+ * @return TaskType
+ */
+ public TaskType getType() {
+ assert type != null;
+ return type;
+ }
+
+ /**
+ * Returns a tick or a cross depending on the field isDone.
+ *
+ * @return Icon which shows a tick or a cross.
+ */
+ public String getStatusIcon() {
+ return (isDone ? "\u2713" : "\u2718");
+ }
+
+
+ /**
+ * Returns a 1 or 0 depending on the field isDone, used for Storage purposes.
+ *
+ * @return String number which is 1 or 0.
+ */
+ public String getStorageStatusIcon() {
+ return (isDone ? "1" : "0");
+ }
+
+ /**
+ * Returns the description of the task.
+ *
+ * @return String that represents the description of the task.
+ */
+ public String getDescription() {
+ assert description != null;
+ return description;
+ }
+
+ /**
+ * Returns a string that includes the status icon and the description of the task.
+ *
+ * @return String with status icon and description of task.
+ */
+ public String toString() {
+ return String.format("[%s] %s", getStatusIcon(), getDescription());
+ }
+
+
+ /**
+ * Returns the type of Task
+ *
+ * @return Completion status of the task
+ */
+ public boolean getDoneStatus() {
+ return isDone;
+ }
+
+
+ /**
+ * Sets the boolean isDone to true and returns the previous status of the task
+ *
+ * @return Previous boolean value of isDone.
+ */
+ public boolean setDone() {
+ if (!isDone) {
+ this.isDone = true;
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * Initialises a reference to the task list of the application in the Task class
+ */
+ public static void setTaskList(TaskList taskList) {
+ Task.taskList = taskList;
+ assert taskList != null;
+ }
+
+ /**
+ * Returns the position of the specified task in current list
+ *
+ * @return Position of the specified task in current list
+ */
+ public static int getTaskID(Task task) {
+ assert taskList != null;
+ return Task.taskList.getTaskID(task);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/duke/tasks/TaskType.java b/src/main/java/duke/tasks/TaskType.java
new file mode 100644
index 0000000000..6e5f088508
--- /dev/null
+++ b/src/main/java/duke/tasks/TaskType.java
@@ -0,0 +1,11 @@
+package duke.tasks;
+
+
+/**
+ * Represents different task types
+ */
+public enum TaskType {
+ EVENT_TASK,
+ TODO_TASK,
+ DEADLINE_TASK
+}
diff --git a/src/main/java/duke/tasks/ToDo.java b/src/main/java/duke/tasks/ToDo.java
new file mode 100644
index 0000000000..5b01d3592e
--- /dev/null
+++ b/src/main/java/duke/tasks/ToDo.java
@@ -0,0 +1,49 @@
+package duke.tasks;
+
+import java.lang.StringBuilder;
+
+
+/**
+ * Represents a Todo task in the application.
+ * A Todo provides a factory method to create itself.
+ */
+public class ToDo extends Task {
+
+ /**
+ * Initialises a todo task with the description of the task.
+ *
+ * @param description ToDo description
+ */
+ public ToDo(String description) {
+ super(description, TaskType.TODO_TASK);
+ }
+
+
+ /**
+ * Creates a ToDo task from the parameters provided for a ToDo task
+ *
+ * @return A ToDo task
+ * @param tokens User input split by space
+ */
+ public static ToDo createToDo(String [] tokens) {
+ StringBuilder builder = new StringBuilder();
+ for (int i = 1; i < tokens.length - 1 ; i++) {
+ builder.append(tokens[i]);
+ builder.append(" ");
+ }
+ builder.append(tokens[tokens.length-1]);
+ return new ToDo(builder.toString());
+ }
+
+
+ /**
+ * Returns A string that includes the type task and the toString of Task.
+ *
+ * @return String that adds the type of the task to the toString method of Task.
+ */
+ @Override
+ public String toString() {
+ return String.format("[T][%s] %s", getStatusIcon(), getDescription());
+ }
+
+}
\ No newline at end of file
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/view/DialogBox.fxml b/src/main/resources/view/DialogBox.fxml
new file mode 100644
index 0000000000..6fa12924bc
--- /dev/null
+++ b/src/main/resources/view/DialogBox.fxml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml
new file mode 100644
index 0000000000..804cf60670
--- /dev/null
+++ b/src/main/resources/view/MainWindow.fxml
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/test/java/duke/core/ParserTest.java b/src/test/java/duke/core/ParserTest.java
new file mode 100644
index 0000000000..52fc13503f
--- /dev/null
+++ b/src/test/java/duke/core/ParserTest.java
@@ -0,0 +1,127 @@
+package duke.core;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.fail;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import duke.core.Parser;
+import duke.errors.DukeException;
+
+import duke.commands.Command;
+import duke.commands.ExitCommand;
+import duke.commands.ListCommand;
+import duke.commands.DoneCommand;
+import duke.commands.DeleteCommand;
+import duke.commands.NullCommand;
+import duke.commands.AddToDoCommand;
+import duke.commands.AddDeadlineCommand;
+import duke.commands.AddEventCommand;
+
+/**
+ * Class to test certain functionality of the Parser class
+ */
+class ParserTest {
+
+ /**
+ * Test that the parseDateTime method can convert into
+ * formatted date and time when a valid input is given
+ */
+ @Test
+ void parseDateTime_validInput_success() {
+ try {
+ assertEquals("2nd of DECEMBER 1212, 12pm",
+ Parser.parseDateTime("02/12/1212 1200"));
+ assertEquals("12th of DECEMBER 1212, 12:13am",
+ Parser.parseDateTime("12/12/1212 0013"));
+ assertEquals("23rd of FEBRUARY 2019, 11:12pm",
+ Parser.parseDateTime("23/02/2019 2312"));
+ } catch (DukeException e) {
+ fail("Should not have thrown exception");
+ }
+ }
+
+ /**
+ * Test that the parseDateTime method throws an exception
+ * when it tries to parse and format an invalid input
+ */
+ @Test
+ void parseDateTime_invalidInput_exceptionThrown() {
+ assertThrows(DukeException.class, () -> Parser.parseDateTime("1212"));
+ assertThrows(DukeException.class, () -> Parser.parseDateTime("12/12/1212"));
+ assertThrows(DukeException.class, () -> Parser.parseDateTime("12pm"));
+ assertThrows(DukeException.class, () -> Parser.parseDateTime("Hello"));
+ }
+
+ /**
+ * Test that the parseDateTime method throws an exception when the input
+ * is potentially valid but cannot be formatted to the expected format
+ */
+ @Test
+ void parseDateTime_validLookingInvalidNumbers_exceptionThrown() {
+ assertThrows(DukeException.class, () -> Parser.parseDateTime("0/12/1212 1212"));
+ assertThrows(DukeException.class, () -> Parser.parseDateTime("-1/12/1212 1212"));
+ assertThrows(DukeException.class, () -> Parser.parseDateTime("/12/1212 1212"));
+ assertThrows(DukeException.class, () -> Parser.parseDateTime("99/12/1212 1212"));
+ assertThrows(DukeException.class, () -> Parser.parseDateTime("12/13/1212 1212"));
+ assertThrows(DukeException.class, () -> Parser.parseDateTime("12/12/999 1212"));
+ assertThrows(DukeException.class, () -> Parser.parseDateTime("12/12/0 1212"));
+ assertThrows(DukeException.class, () -> Parser.parseDateTime("12/12/0 00"));
+ }
+
+
+ /**
+ * Test that the parseCommand method throws the expected Exceptions when the it tries to parse a input
+ * as a command, but the required arguments are not provided
+ */
+ @Test
+ void parseCommand_missingParameter_exceptionThrown() {
+ assertThrows(IllegalArgumentException.class, () -> Parser.parseCommand("todo "));
+
+ assertThrows(IllegalArgumentException.class, () -> Parser.parseCommand("deadline "));
+ assertThrows(IllegalArgumentException.class, () -> Parser.parseCommand("deadline task"));
+ assertThrows(DukeException.class, () -> Parser.parseCommand("deadline /by time"));
+
+ assertThrows(IllegalArgumentException.class, () -> Parser.parseCommand("event "));
+ assertThrows(IllegalArgumentException.class, () -> Parser.parseCommand("event task "));
+ assertThrows(DukeException.class, () -> Parser.parseCommand("event /at time"));
+
+ assertThrows(IllegalArgumentException.class, () -> Parser.parseCommand("event task /at "));
+
+ assertThrows(IllegalArgumentException.class, () -> Parser.parseCommand("done "));
+ assertThrows(IllegalArgumentException.class, () -> Parser.parseCommand("delete "));
+ }
+
+ /**
+ * Test that the parseCommand method properly still return an valid AddCommand
+ * even when the deadline is not in recognisable dateTime format
+ */
+ @Test
+ void parseCommand_nonParseableTimeForDeadlineOrEvent_success() {
+ try {
+ assertTrue(Parser.parseCommand("deadline task /by time") instanceof AddDeadlineCommand);
+ assertTrue(Parser.parseCommand("event task /at time") instanceof AddEventCommand);
+ } catch (DukeException ex) {
+ fail("Should return without formatting the time parameter into dd/MM/yyyy HHmm");
+ }
+ }
+
+ /**
+ * Test that the parseCommand method ignores extra input when attempting to parse commands
+ * which do not require arguments.
+ */
+ @Test
+ void parseCommand_extraArgumentForNoArgumentCommands_success() {
+ try {
+ assertTrue(Parser.parseCommand("list 1") instanceof ListCommand);
+ assertTrue(Parser.parseCommand("bye 1") instanceof ExitCommand);
+ assertTrue(Parser.parseCommand("list a") instanceof ListCommand);
+ assertTrue(Parser.parseCommand("bye a") instanceof ExitCommand);
+ } catch (DukeException ex) {
+ fail("Should return a command ignoring the extra arguments");
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/duke/core/TaskListTest.java b/src/test/java/duke/core/TaskListTest.java
new file mode 100644
index 0000000000..d37f3458b6
--- /dev/null
+++ b/src/test/java/duke/core/TaskListTest.java
@@ -0,0 +1,24 @@
+package duke.core;
+
+import duke.errors.DukeException;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.fail;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.ArrayList;
+import duke.tasks.Task;
+
+import duke.commands.Command;
+
+
+
+
+class TaskListTest {
+
+}
\ No newline at end of file
diff --git a/tutorials/gradleTutorial.md b/tutorials/gradleTutorial.md
index 08292b118d..335012a526 100644
--- a/tutorials/gradleTutorial.md
+++ b/tutorials/gradleTutorial.md
@@ -36,7 +36,7 @@ As a developer, you write a _build file_ that describes the project. A build fil
mainClassName = "seedu.duke.Duke"
}
```
-1. To check if Gradle has been added to the project correctly, open a terminal window, navigate to the root directory of your project and run the command `gradlew run`. This should result in Gradle running the main method of your project.
+1. To check if Gradle has been added to the project correctly, open a terminal window, navigate to the root directory of your project and run the command `./gradlew run`. This should result in Gradle running the main method of your project.
:bulb: Simply run the command `gradlew {taskName}` in the terminal and Gradle will run the task! Here are some example commands:
* `gradlew tasks` (or `gradlew tasks --all`): shows a list of tasks available
diff --git a/tutorials/javaFxTutorialPart2.md b/tutorials/javaFxTutorialPart2.md
index f24a0cd6ad..c714f75ea0 100644
--- a/tutorials/javaFxTutorialPart2.md
+++ b/tutorials/javaFxTutorialPart2.md
@@ -49,6 +49,7 @@ import javafx.scene.layout.VBox;
import javafx.stage.Stage;
+
public class Duke extends Application {
private ScrollPane scrollPane;
diff --git a/tutorials/javaFxTutorialPart3.md b/tutorials/javaFxTutorialPart3.md
index a9e1bdddd3..2251efc04b 100644
--- a/tutorials/javaFxTutorialPart3.md
+++ b/tutorials/javaFxTutorialPart3.md
@@ -19,6 +19,7 @@ public void start(Stage stage) {
//Step 2 code here
//Step 3. Add functionality to handle user input.
+
sendButton.setOnMouseClicked((event) -> {
dialogContainer.getChildren().add(getDialogLabel(userInput.getText()));
userInput.clear();