diff --git a/.gitignore b/.gitignore
index 99712178bf..c2f5aa01a2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,6 @@ src/main/resources/docs/
.DS_Store
*.iml
bin/
+
+# Data files
+/data/
diff --git a/README.md b/README.md
deleted file mode 100644
index 84755485a7..0000000000
--- a/README.md
+++ /dev/null
@@ -1,39 +0,0 @@
-# Setting up
-
-**Prerequisites**
-
-* JDK 11
-* Recommended: IntelliJ IDE
-* Fork this repo to your GitHub account and clone the fork to your computer
-
-**Importing the project into IntelliJ**
-
-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.
- * Click `Configure` > `Structure for new Projects` (in older versions of Intellij:`Configure` > `Project Defaults` > `Project Structure`).
- * 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.
- * Click `OK`.
-1. Click `Import Project`.
-1. Locate the project directory and click `OK`.
-1. Select `Create project from existing sources` and click `Next`.
-1. Rename the project if you want. Click `Next`.
-1. Ensure that your src folder is checked. Keep clicking `Next`.
-1. Click `Finish`.
-
-# Tutorials
-
-Duke Increment | Tutorial
----------------|---------------
-`A-Gradle` | [Gradle Tutorial](tutorials/gradleTutorial.md)
-`A-TextUiTesting` | [Text UI Testing Tutorial](tutorials/textUiTestingTutorial.md)
-`Level-10` | JavaFX tutorials: → [Part 1: Introduction to JavaFX][fx1] → [Part 2: Creating a GUI for Duke][fx2] → [Part 3: Interacting with the user][fx3] → [Part 4: Introduction to FXML][fx4]
-
-[fx1]:
-[fx2]:
-[fx3]:
-[fx4]:
-
-# Feedback, Bug Reports
-
-* If you have feedback or bug reports, please post in [se-edu/duke issue tracker](https://github.com/se-edu/duke/issues).
-* We welcome pull requests too.
\ No newline at end of file
diff --git a/_config.yml b/_config.yml
new file mode 100644
index 0000000000..c4192631f2
--- /dev/null
+++ b/_config.yml
@@ -0,0 +1 @@
+theme: jekyll-theme-cayman
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000000..cce62d89a3
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,59 @@
+plugins {
+ id 'java'
+ id 'application'
+ id 'com.github.johnrengelman.shadow' version '5.1.0'
+ id 'org.openjfx.javafxplugin' version '0.0.8'
+}
+
+version '0.1.0'
+
+repositories {
+ mavenCentral()
+}
+
+javafx {
+ version = "12.0.2"
+ modules = [ 'javafx.controls', 'javafx.fxml' ]
+}
+
+application {
+ mainClassName = 'dose.Launcher'
+}
+
+run {
+ standardInput = System.in
+}
+
+dependencies {
+ String javaFxVersion = '12'
+ 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'
+ implementation group: 'org.openjfx', name: 'javafx-web', version: javaFxVersion, classifier: 'win'
+ implementation group: 'org.openjfx', name: 'javafx-web', version: javaFxVersion, classifier: 'mac'
+ implementation group: 'org.openjfx', name: 'javafx-web', version: javaFxVersion, classifier: 'linux'
+ testImplementation 'org.junit.jupiter:junit-jupiter:5.5.0'
+}
+
+test {
+ useJUnitPlatform()
+}
+
+shadowJar {
+ archiveBaseName = "dose"
+ archiveVersion = "1.3"
+ archiveClassifier = null
+ archiveAppendix = null
+}
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/docs/README.md b/docs/README.md
index fd44069597..c46cf18e9c 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,20 +1,185 @@
# User Guide
+**Dose** is a customisable personal assistant for managing your deadlines, events and todos.
+Made for users who are comfortable with Command-Line based interactions,
+Dose helps you get your tasks out of your head as quickly as possible without getting in the way.
+Stop fiddling with bells and whistles of other todo-list apps -- Dose's pretty GUI is just a bonus!
+
+Experiencing a caffeinated rush and needing a dose of calm?
+Stuck in a rut and wishing for a dose of motivation? Dose has your back.
+Put your tasks in Dose and never forget about them ever again!
## Features
-### Feature 1
-Description of feature.
+### Add todos, events and deadlines
+To add a todo, event or deadline to Dose, simply type the type of the task,
+followed by its description and deadline (if any). Dose will update your list of tasks accordingly.
+
+
+
+### View tasks in task list
+Keep track of the tasks you've added.
+See each task's description, deadline, priority and tags (if any) in a compact list view.
+
+
+
+### Save task list to disk
+Dose is capable of saving your list of tasks to a small, portable text file.
+Upon the next startup of Dose, your tasks will be instantly re-imported -- no sweat!
+
+
+
+### Complete and delete tasks
+Done with a task? Mistakenly added a task you no longer need? Dose will take care of it.
+
+
+
+### Tag and prioritise tasks
+Need some organisation for your tasks? Dose is here to help.
+(Sorting tasks by tag and priority is coming soon!)
+
+
+
+### Find tasks matching your query
+Looking for a specific task? Simply search for it.
+
+
+
+### Snooze a task
+Too many deadlines at your back? Have yourself a dose of calm by postponing non-urgent tasks.
+
+
+
+## Commands
+1. [todo](#todo)
+2. [deadline](#deadline)
+3. [event](#event)
+4. [list](#list)
+5. [save](#save)
+6. [done](#done)
+7. [delete](#delete)
+8. [tag](#tag)
+9. [priority](#priority)
+10. [find](#find)
+11. [snooze](#snooze)
+12. [bye](#bye)
+
+### `todo`
+Creates a new todo.
+
+**Usage:** `todo [description]`
+
+**Parameters:**
+
+`[description]` Description of the todo (required).
+
+**Expected outcome:**
+A new todo with the provided description is created.
+
+### `deadline`
+Creates a new task with a deadline.
+
+**Usage:** `deadline [description] /by [deadline]`
+
+**Parameters:**
+
+`[description]` Description of the task (required).
+
+`[deadline]` Deadline of the task, in ISO format.
+
+**Expected outcome:**
+A new deadline task with the provided description and deadline is created.
+
+### `event`
+Creates a new event at a specified time.
+
+**Usage:** `event [description] /at [time]`
+
+**Parameters:**
+
+`[description]` Description of the event (required).
+
+`[time]` Time of the event, in ISO format.
+
+**Expected outcome:**
+A new event with the provided description and date is created.
+
+### `list`
+Displays the tasks in the task list.
+
+**Usage:** `list`
+
+**Expected outcome**: The current state of the task list is displayed.
+
+### `save`
+Saves the tasks in the task list to the disk.
+
+**Usage:** `save`
+
+**Expected outcome:** The current state of the task list is saved to the disk.
+
+### `done`
+Marks the task as complete.
+
+**Usage:** `done [taskId]`
+
+**Parameters:**
+`[taskId]` ID of the task in the list (required). Use `list` to get this, if need be.
+
+### `delete`
+Deletes the task.
+
+**Usage:** `delete [taskId]`
+
+**Parameters:**
+`[taskId]` ID of the task in the list (required). Use `list` to get this, if need be.
+
+**Expected outcome:** The task is deleted from the list.
+
+### `tag`
+Adds a tag to a task.
+
+**Usage:** `tag [taskId] [tag]`
+
+**Parameters:**
+`[taskId]` ID of the task in the list (required). Use `list` to get this, if need be.
+`[tag]` Tag to be added to the task.
+
+**Expected outcome:** The tag is added to the task.
+
+### `priority`
+Adds a priority to a task.
+
+**Usage:** `priority [taskId] [priority]`
+
+**Parameters:**
+`[taskId]` ID of the task in the list (required). Use `list` to get this, if need be.
+`[priority]` Priority to be added to the task. Can be `low`, `medium` or `high`.
+
+**Expected outcome:** The tag is added to the task.
+
+### `find`
+Find tasks matching a query.
+
+**Usage:** `find [keyword]`
+
+**Parameters:**
+`[keyword]` Keyword to be used in searching for tasks.
+
+**Expected outcome:** Displays the list of tasks matching the query, if any.
-## Usage
+### `snooze`
+Snoozes a given task. That is, postpones its deadline by one day.
-### `Keyword` - Describe action
+**Usage:** `snooze [taskId]`
-Describe action and its outcome.
+**Parameters:**
+`[taskId]` ID of the task in the list (required). Use `list` to get this, if need be.
-Example of usage:
+**Expected outcome:** The deadline (of tasks with deadlines) or time (of events) is postponed by one day.
-`keyword (optional arguments)`
+### `bye`
+Saves the tasks in the task list to the disk, then exits Dose.
-Expected outcome:
+**Usage:** `bye`
-`outcome`
+**Expected outcome:** The current state of the task list is saved to the disk. Duke can be safely exited.
diff --git a/docs/Ui.png b/docs/Ui.png
new file mode 100644
index 0000000000..ea24f42a11
Binary files /dev/null and b/docs/Ui.png differ
diff --git a/docs/images/add_tasks.PNG b/docs/images/add_tasks.PNG
new file mode 100644
index 0000000000..165f866b5f
Binary files /dev/null and b/docs/images/add_tasks.PNG differ
diff --git a/docs/images/done_delete_tasks.PNG b/docs/images/done_delete_tasks.PNG
new file mode 100644
index 0000000000..62648a767d
Binary files /dev/null and b/docs/images/done_delete_tasks.PNG differ
diff --git a/docs/images/find_tasks.PNG b/docs/images/find_tasks.PNG
new file mode 100644
index 0000000000..a8c19a7018
Binary files /dev/null and b/docs/images/find_tasks.PNG differ
diff --git a/docs/images/list_tasks.PNG b/docs/images/list_tasks.PNG
new file mode 100644
index 0000000000..0f8b415fc3
Binary files /dev/null and b/docs/images/list_tasks.PNG differ
diff --git a/docs/images/save_tasks.PNG b/docs/images/save_tasks.PNG
new file mode 100644
index 0000000000..bd1f109fe2
Binary files /dev/null and b/docs/images/save_tasks.PNG differ
diff --git a/docs/images/snooze_tasks.PNG b/docs/images/snooze_tasks.PNG
new file mode 100644
index 0000000000..a931c208c7
Binary files /dev/null and b/docs/images/snooze_tasks.PNG differ
diff --git a/docs/images/tag_priority_tasks.PNG b/docs/images/tag_priority_tasks.PNG
new file mode 100644
index 0000000000..a568c069cf
Binary files /dev/null and b/docs/images/tag_priority_tasks.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..b3338b157c
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Thu Sep 05 23:32:25 SGT 2019
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.5.1-all.zip
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStorePath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
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..b930e6f5be
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1 @@
+rootProject.name = 'dose'
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/dose/Dose.java b/src/main/java/dose/Dose.java
new file mode 100644
index 0000000000..6692cb4326
--- /dev/null
+++ b/src/main/java/dose/Dose.java
@@ -0,0 +1,24 @@
+package dose;
+
+import dose.util.cli.CliDose;
+
+public interface Dose {
+
+ /**
+ * Runs Dose.
+ */
+ void run();
+
+ /**
+ * Creates and runs a new instance of Dose.
+ * Invoked when Dose is run from the CLI.
+ * @param args Arguments supplied by the user.
+ */
+ static void main(String[] args) {
+ // running Dose from the CLI should instantiate CliDose, as usual
+ Dose dose = new CliDose();
+ dose.run();
+ }
+
+ void initializeStorage();
+}
diff --git a/src/main/java/dose/Launcher.java b/src/main/java/dose/Launcher.java
new file mode 100644
index 0000000000..806a8d1ad7
--- /dev/null
+++ b/src/main/java/dose/Launcher.java
@@ -0,0 +1,12 @@
+package dose;
+
+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/dose/Main.java b/src/main/java/dose/Main.java
new file mode 100644
index 0000000000..526c39ca0f
--- /dev/null
+++ b/src/main/java/dose/Main.java
@@ -0,0 +1,50 @@
+package dose;
+
+import dose.util.gui.GuiDose;
+import dose.util.gui.MainWindow;
+import javafx.application.Application;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Scene;
+import javafx.scene.image.Image;
+import javafx.scene.layout.AnchorPane;
+import javafx.stage.Stage;
+
+/**
+ * A GUI for Dose using FXML.
+ */
+public class Main extends Application {
+
+ /** Image to be used as application icon for Dose.*/
+ private Image doseIcon = new Image(getClass().getResourceAsStream("/images/coffee.png"));
+
+ /** Underlying instance of Dose created when application runs.*/
+ private GuiDose dose = new GuiDose();
+
+ @Override
+ public void start(Stage stage) {
+ try {
+ // create new scene using MainWindow fxml
+ FXMLLoader fxmlLoader = new FXMLLoader(Main.class.getResource("/view/MainWindow.fxml"));
+ AnchorPane ap = fxmlLoader.load();
+ Scene scene = new Scene(ap);
+
+ // set the scene to display on the stage
+ stage.setScene(scene);
+
+ // set underlying instance of Dose
+ fxmlLoader.getController().setDose(dose);
+
+ // set title and icon for the window
+ stage.setTitle("Dose");
+ stage.getIcons().add(doseIcon);
+
+ // display the window
+ stage.show();
+
+ // initialize Dose
+ dose.run();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/dose/command/AddDeadlineCommand.java b/src/main/java/dose/command/AddDeadlineCommand.java
new file mode 100644
index 0000000000..587661e529
--- /dev/null
+++ b/src/main/java/dose/command/AddDeadlineCommand.java
@@ -0,0 +1,25 @@
+package dose.command;
+
+import dose.task.DeadlineTask;
+import dose.task.Task;
+import dose.util.exception.DoseException;
+
+/**
+ * Represents a command to create and add a DeadlineTask.
+ */
+public class AddDeadlineCommand extends AddTaskCommand {
+ public AddDeadlineCommand(String command) throws DoseException {
+ super(command);
+ s.useDelimiter("/by");
+ setDescription();
+ setDeadlineString();
+ }
+
+ @Override
+ public Task createTask() throws DoseException {
+ assert this.getDescription() != null;
+ assert this.getDeadlineString() != null;
+
+ return new DeadlineTask(getDescription(), getDeadlineString());
+ }
+}
diff --git a/src/main/java/dose/command/AddEventCommand.java b/src/main/java/dose/command/AddEventCommand.java
new file mode 100644
index 0000000000..baf9517871
--- /dev/null
+++ b/src/main/java/dose/command/AddEventCommand.java
@@ -0,0 +1,25 @@
+package dose.command;
+
+import dose.util.exception.DoseException;
+import dose.task.EventTask;
+import dose.task.Task;
+
+/**
+ * Represents a command to create and add an Event task.
+ */
+public class AddEventCommand extends AddTaskCommand {
+ public AddEventCommand(String command) throws DoseException {
+ super(command);
+ s.useDelimiter("/at");
+ setDescription();
+ setDeadlineString();
+ }
+
+ @Override
+ public Task createTask() throws DoseException {
+ assert this.getDescription() != null;
+ assert this.getDeadlineString() != null;
+
+ return new EventTask(getDescription(), getDeadlineString());
+ }
+}
diff --git a/src/main/java/dose/command/AddTaskCommand.java b/src/main/java/dose/command/AddTaskCommand.java
new file mode 100644
index 0000000000..63a948813c
--- /dev/null
+++ b/src/main/java/dose/command/AddTaskCommand.java
@@ -0,0 +1,93 @@
+package dose.command;
+
+import dose.task.Task;
+import dose.task.TaskList;
+import dose.util.Storage;
+import dose.util.Ui;
+import dose.util.UiMessage;
+import dose.util.exception.DoseException;
+import dose.util.exception.ExceptionType;
+import java.util.NoSuchElementException;
+import java.util.Scanner;
+
+/**
+ * Represents a command to add a new task to the list of tasks.
+ */
+public abstract class AddTaskCommand implements Command {
+ Scanner s;
+ private String description;
+ private String deadlineString;
+
+ AddTaskCommand(String fullCommand) {
+ super();
+ this.s = new Scanner(fullCommand);
+ s.next(); // ignore command
+ }
+
+ /**
+ * Returns the description of the task to be added.
+ * @return Description of the task to be added.
+ */
+ String getDescription() {
+ return this.description;
+ }
+
+ /**
+ * Returns the string representing the deadline of the task to be added.
+ * For Deadline tasks, this refers to the expected date of completion.
+ * For Event tasks, this refers to the expected date of occurrence.
+ * @return Deadline of the task to be added.
+ */
+ String getDeadlineString() {
+ return this.deadlineString;
+ }
+
+ /**
+ * Retrieves the description of the task to be added, based on command issued by user.
+ * @throws DoseException Application-specific exception thrown during execution.
+ */
+ void setDescription() throws DoseException {
+ try {
+ this.description = this.s.next().strip();
+ } catch (NoSuchElementException e) {
+ // user input after task type is blank
+ throw new DoseException(ExceptionType.DESCRIPTION_BLANK);
+ }
+ }
+
+ /**
+ * Retrieves the string representing the deadline of the task to be added,
+ * based on command issued by user.
+ * @throws DoseException Application-specific exception thrown during execution.
+ */
+ void setDeadlineString() throws DoseException {
+ try {
+ this.deadlineString = this.s.next().strip();
+ } catch (NoSuchElementException e) {
+ // no deadline entered
+ throw new DoseException(ExceptionType.DEADLINE_BLANK);
+ }
+ }
+
+ /**
+ * Creates the task to be added, based on command issued by the user.
+ * @return Task to be added.
+ */
+ abstract Task createTask() throws DoseException;
+
+ /**
+ * Creates a new Task based on command issued by the user, and adds it to the list of tasks.
+ * @param tasks List of tasks.
+ * @param ui UI to display to the user.
+ * @param storage Object that handles storage of task list to disk.
+ * @throws DoseException Application-specific exception thrown during execution.
+ */
+ @Override
+ public void execute(TaskList tasks, Ui ui, Storage storage) throws DoseException {
+ Task newTask = createTask();
+ tasks.add(newTask);
+
+ String message = UiMessage.TASK_ADDED.getMessage() + " " + UiMessage.HINT_LIST.getMessage();
+ ui.showMessage(message);
+ }
+}
diff --git a/src/main/java/dose/command/AddTodoCommand.java b/src/main/java/dose/command/AddTodoCommand.java
new file mode 100644
index 0000000000..ca94525022
--- /dev/null
+++ b/src/main/java/dose/command/AddTodoCommand.java
@@ -0,0 +1,21 @@
+package dose.command;
+
+import dose.task.Task;
+import dose.task.TodoTask;
+import dose.util.exception.DoseException;
+
+/**
+ * Represents a command to create and add a TodoTask.
+ */
+public class AddTodoCommand extends AddTaskCommand {
+ public AddTodoCommand(String fullCommand) throws DoseException {
+ super(fullCommand);
+ s.useDelimiter("\n"); // no special delimiter required
+ setDescription();
+ }
+
+ @Override
+ public Task createTask() {
+ return new TodoTask(getDescription());
+ }
+}
diff --git a/src/main/java/dose/command/Command.java b/src/main/java/dose/command/Command.java
new file mode 100644
index 0000000000..cb599d99eb
--- /dev/null
+++ b/src/main/java/dose/command/Command.java
@@ -0,0 +1,31 @@
+package dose.command;
+
+import dose.task.TaskList;
+import dose.util.Storage;
+import dose.util.Ui;
+import dose.util.exception.DoseException;
+
+/**
+ * Represents commands requested by user and related operations,
+ * such as executing the command.
+ */
+public interface Command {
+
+ /**
+ * Executes the command, by interacting with tasks and UI.
+ * @param tasks List of tasks.
+ * @param ui UI to display to the user.
+ * @param storage Object that handles storage of task list to disk.
+ * @throws DoseException Application-specific exception thrown during execution.
+ */
+ void execute(TaskList tasks, Ui ui, Storage storage) throws DoseException;
+ // implementation varies for each subclass of Command
+
+ /**
+ * Returns boolean indicating if command entered was "exit", false by default.
+ * @return boolean indicating if command entered was "exit".
+ */
+ default boolean isExit() {
+ return false;
+ }
+}
diff --git a/src/main/java/dose/command/DeleteCommand.java b/src/main/java/dose/command/DeleteCommand.java
new file mode 100644
index 0000000000..49d89a321b
--- /dev/null
+++ b/src/main/java/dose/command/DeleteCommand.java
@@ -0,0 +1,35 @@
+package dose.command;
+
+import dose.task.Task;
+import dose.task.TaskList;
+import dose.util.Storage;
+import dose.util.Ui;
+import dose.util.UiMessage;
+import dose.util.exception.DoseException;
+
+public class DeleteCommand extends ModifyTaskCommand {
+
+ public DeleteCommand(String fullCommand) {
+ super(fullCommand);
+ }
+
+ /**
+ * Deletes a task, based on command issued by the user.
+ * @param tasks List of tasks.
+ * @param ui UI to display to the user.
+ * @param storage Object that handles storage of task list to disk.
+ * @throws DoseException Application-specific exception thrown during execution.
+ */
+ @Override
+ public void execute(TaskList tasks, Ui ui, Storage storage) throws DoseException {
+ Task task = getTaskById(tasks);
+ String taskDeletedMessage = UiMessage.TASK_DELETED.getMessage() + task.toString();
+
+ tasks.deleteTask(task);
+
+ ui.showMessage(taskDeletedMessage);
+ String message = UiMessage.TASKS_STATUS_FRONT.getMessage()
+ + tasks.getSize() + UiMessage.TASKS_STATUS_BACK.getMessage();
+ ui.showMessage(message);
+ }
+}
diff --git a/src/main/java/dose/command/DoneCommand.java b/src/main/java/dose/command/DoneCommand.java
new file mode 100644
index 0000000000..5d4970fea7
--- /dev/null
+++ b/src/main/java/dose/command/DoneCommand.java
@@ -0,0 +1,34 @@
+package dose.command;
+
+import dose.task.Task;
+import dose.task.TaskList;
+import dose.util.Storage;
+import dose.util.Ui;
+import dose.util.UiMessage;
+import dose.util.exception.DoseException;
+
+/**
+ * Represents a request from the user to mark a given task as done.
+ */
+public class DoneCommand extends ModifyTaskCommand {
+
+ public DoneCommand(String fullCommand) {
+ super(fullCommand);
+ }
+
+ /**
+ * Marks the task in the command issued by the user as done.
+ * @param tasks List of tasks.
+ * @param ui UI to display to the user.
+ * @param storage Object that handles storage of task list to disk.
+ * @throws DoseException Application-specific exception thrown during execution.
+ */
+ @Override
+ public void execute(TaskList tasks, Ui ui, Storage storage) throws DoseException {
+ Task task = getTaskById(tasks);
+ task.markAsDone();
+
+ String message = UiMessage.TASK_DONE.getMessage() + task.toString();
+ ui.showMessage(message);
+ }
+}
diff --git a/src/main/java/dose/command/ExitCommand.java b/src/main/java/dose/command/ExitCommand.java
new file mode 100644
index 0000000000..560de6f5e1
--- /dev/null
+++ b/src/main/java/dose/command/ExitCommand.java
@@ -0,0 +1,35 @@
+package dose.command;
+
+import dose.task.TaskList;
+import dose.util.Storage;
+import dose.util.Ui;
+import dose.util.UiMessage;
+
+/**
+ * Exits the application.
+ */
+public class ExitCommand implements Command {
+
+ /**
+ * Exits the application.
+ * @param tasks List of tasks.
+ * @param ui UI to display to the user.
+ * @param storage Object that handles storage of task list to disk.
+ */
+ @Override
+ public void execute(TaskList tasks, Ui ui, Storage storage) {
+ // todo: in future, "lock down" ui and actually exit Dose when this command is called?
+ // todo: call this command when the "close" button is pressed by user
+ storage.save(tasks);
+ ui.showMessage(UiMessage.GOODBYE);
+ }
+
+ /**
+ * Returns boolean indicating if command entered was "exit", true in this case.
+ * @return boolean indicating if command entered was "exit".
+ */
+ @Override
+ public boolean isExit() {
+ return true;
+ }
+}
diff --git a/src/main/java/dose/command/FindCommand.java b/src/main/java/dose/command/FindCommand.java
new file mode 100644
index 0000000000..f8a05b3302
--- /dev/null
+++ b/src/main/java/dose/command/FindCommand.java
@@ -0,0 +1,51 @@
+package dose.command;
+
+import dose.task.TaskList;
+import dose.util.Storage;
+import dose.util.Ui;
+import dose.util.UiMessage;
+import dose.util.exception.DoseException;
+import dose.util.exception.ExceptionType;
+import java.util.InputMismatchException;
+import java.util.Scanner;
+
+public class FindCommand implements Command {
+ private Scanner s;
+
+ /**
+ * Constructs a new FindCommand, given the full command issued by the user.
+ * @param fullCommand Full command issued by the user.
+ */
+ public FindCommand(String fullCommand) {
+ super();
+ this.s = new Scanner(fullCommand);
+ }
+
+ /**
+ * Finds a list of tasks matching the keyword issued by the user.
+ * @param tasks List of tasks.
+ * @param ui UI to display to the user.
+ * @param storage Object that handles storage of task list to disk.
+ * @throws DoseException Application-specific exception thrown during execution.
+ */
+ @Override
+ public void execute(TaskList tasks, Ui ui, Storage storage) throws DoseException {
+ try {
+ // first, try to get keyword
+ s.next(); // command (find), to be ignored
+ String keyword = s.nextLine();
+
+ // if ok, find and return matching tasks
+ TaskList matchingTasks = tasks.findTasks(keyword);
+ if (matchingTasks.isEmpty()) {
+ throw new DoseException(ExceptionType.NO_MATCHING_TASKS);
+ } else {
+ ui.showMessage(UiMessage.MATCHING_TASKS);
+ ui.showTasks(matchingTasks);
+ }
+ } catch (InputMismatchException e) {
+ // user input after "find" is empty
+ throw new DoseException(ExceptionType.KEYWORD_BLANK);
+ }
+ }
+}
diff --git a/src/main/java/dose/command/HelpCommand.java b/src/main/java/dose/command/HelpCommand.java
new file mode 100644
index 0000000000..2d9a28153c
--- /dev/null
+++ b/src/main/java/dose/command/HelpCommand.java
@@ -0,0 +1,20 @@
+package dose.command;
+
+import dose.task.TaskList;
+import dose.util.Storage;
+import dose.util.Ui;
+import dose.util.UiMessage;
+
+public class HelpCommand implements Command {
+
+ /**
+ * Shows a list of commands available in the application.
+ * @param tasks List of tasks.
+ * @param ui UI to display to the user.
+ * @param storage Object that handles storage of task list to disk.
+ */
+ @Override
+ public void execute(TaskList tasks, Ui ui, Storage storage) {
+ ui.showMessage(UiMessage.getHelpMessage());
+ }
+}
diff --git a/src/main/java/dose/command/ListCommand.java b/src/main/java/dose/command/ListCommand.java
new file mode 100644
index 0000000000..e76accfdec
--- /dev/null
+++ b/src/main/java/dose/command/ListCommand.java
@@ -0,0 +1,27 @@
+package dose.command;
+
+import dose.task.TaskList;
+import dose.util.Storage;
+import dose.util.Ui;
+import dose.util.UiMessage;
+import dose.util.exception.DoseException;
+import dose.util.exception.ExceptionType;
+
+public class ListCommand implements Command {
+
+ /**
+ * Lists all tasks currently in the list.
+ * @param tasks List of tasks.
+ * @param ui UI to display to the user.
+ * @param storage Object that handles storage of task list to disk.
+ * @throws DoseException Application-specific exception thrown during execution.
+ */
+ @Override
+ public void execute(TaskList tasks, Ui ui, Storage storage) throws DoseException {
+ if (tasks.isEmpty()) {
+ throw new DoseException(ExceptionType.TASK_LIST_EMPTY);
+ }
+ ui.showMessage(UiMessage.TASK_LIST);
+ ui.showTasks(tasks);
+ }
+}
diff --git a/src/main/java/dose/command/ModifyTaskCommand.java b/src/main/java/dose/command/ModifyTaskCommand.java
new file mode 100644
index 0000000000..d5f4dd5764
--- /dev/null
+++ b/src/main/java/dose/command/ModifyTaskCommand.java
@@ -0,0 +1,38 @@
+package dose.command;
+
+import dose.task.Task;
+import dose.task.TaskList;
+import dose.util.exception.DoseException;
+import dose.util.exception.ExceptionType;
+import java.util.InputMismatchException;
+import java.util.NoSuchElementException;
+import java.util.Scanner;
+
+public abstract class ModifyTaskCommand implements Command{
+ Scanner s;
+
+ public ModifyTaskCommand(String fullCommand) {
+ super();
+ s = new Scanner(fullCommand);
+ }
+
+ public Task getTaskById(TaskList tasks) throws DoseException {
+ int taskId;
+ Task task;
+
+ try {
+ // first, try to get taskId
+ s.next(); // command, to be ignored
+ taskId = s.nextInt();
+ task = tasks.getTask(taskId);
+
+ return task;
+ } catch (InputMismatchException | IndexOutOfBoundsException e) {
+ // user input after "tag" is not an int, or is an invalid task ID
+ throw new DoseException(ExceptionType.INVALID_ID);
+ } catch (NoSuchElementException e) {
+ // user input after "tag" is blank
+ throw new DoseException(ExceptionType.ID_BLANK);
+ }
+ }
+}
diff --git a/src/main/java/dose/command/PriorityCommand.java b/src/main/java/dose/command/PriorityCommand.java
new file mode 100644
index 0000000000..3bd5d0c852
--- /dev/null
+++ b/src/main/java/dose/command/PriorityCommand.java
@@ -0,0 +1,51 @@
+package dose.command;
+
+import static dose.task.TaskPriority.getTaskPriority;
+
+import dose.task.Task;
+import dose.task.TaskList;
+import dose.task.TaskPriority;
+import dose.util.Storage;
+import dose.util.Ui;
+import dose.util.UiMessage;
+import dose.util.exception.DoseException;
+import dose.util.exception.ExceptionType;
+import java.util.NoSuchElementException;
+
+/**
+ * Represents a command to add a priority to a task.
+ */
+public class PriorityCommand extends ModifyTaskCommand {
+ public PriorityCommand(String fullCommand) {
+ super(fullCommand);
+ }
+
+ /**
+ * Adds a priority to the task, based on command issued by the user.
+ * @param tasks List of tasks.
+ * @param ui UI to display to the user.
+ * @param storage Object that handles storage of task list to disk.
+ * @throws DoseException Application-specific exception thrown during execution.
+ */
+ @Override
+ public void execute(TaskList tasks, Ui ui, Storage storage) throws DoseException {
+ Task task = getTaskById(tasks);
+
+ // then, try to get priority
+ try {
+ String priorityString = s.next().strip();
+ TaskPriority priority = getTaskPriority(priorityString);
+
+ // if ok, add priority to task
+ task.addPriority(priority);
+
+ // todo: handle invalid priority string (currently, it just removes priority since null)
+
+ String message = UiMessage.TASK_PRIORITISED.getMessage() + task.toString();
+ ui.showMessage(message);
+ } catch (NoSuchElementException e) {
+ // user input after taskId is blank
+ throw new DoseException(ExceptionType.TAG_BLANK);
+ }
+ }
+}
diff --git a/src/main/java/dose/command/SaveCommand.java b/src/main/java/dose/command/SaveCommand.java
new file mode 100644
index 0000000000..025aa7973d
--- /dev/null
+++ b/src/main/java/dose/command/SaveCommand.java
@@ -0,0 +1,20 @@
+package dose.command;
+
+import dose.task.TaskList;
+import dose.util.Storage;
+import dose.util.Ui;
+import dose.util.exception.DoseException;
+
+public class SaveCommand implements Command {
+
+ /**
+ * Saves the current status of the task list to the disk.
+ * @param tasks List of tasks.
+ * @param ui UI to display to the user.
+ * @param storage Object that handles storage of task list to disk.
+ */
+ @Override
+ public void execute(TaskList tasks, Ui ui, Storage storage) throws DoseException {
+ storage.save(tasks);
+ }
+}
diff --git a/src/main/java/dose/command/SnoozeCommand.java b/src/main/java/dose/command/SnoozeCommand.java
new file mode 100644
index 0000000000..cb41127aa6
--- /dev/null
+++ b/src/main/java/dose/command/SnoozeCommand.java
@@ -0,0 +1,32 @@
+package dose.command;
+
+import dose.task.Task;
+import dose.task.TaskList;
+import dose.util.Storage;
+import dose.util.Ui;
+import dose.util.UiMessage;
+import dose.util.exception.DoseException;
+
+/**
+ * Represents a command to postpone ("snooze") a task.
+ */
+public class SnoozeCommand extends ModifyTaskCommand {
+
+ public SnoozeCommand(String fullCommand) {
+ super(fullCommand);
+ }
+
+ /**
+ * Snoozes the task in the command issued by the user.
+ * @param tasks List of tasks.
+ * @param ui UI to display to the user.
+ * @param storage Object that handles storage of task list to disk.
+ * @throws DoseException Application-specific exception thrown during execution.
+ */
+ @Override
+ public void execute(TaskList tasks, Ui ui, Storage storage) throws DoseException {
+ Task task = getTaskById(tasks);
+ task.snooze();
+ ui.showMessage(UiMessage.TASK_SNOOZED);
+ }
+}
diff --git a/src/main/java/dose/command/TagCommand.java b/src/main/java/dose/command/TagCommand.java
new file mode 100644
index 0000000000..312301f1fa
--- /dev/null
+++ b/src/main/java/dose/command/TagCommand.java
@@ -0,0 +1,44 @@
+package dose.command;
+
+import dose.task.Task;
+import dose.task.TaskList;
+import dose.util.Storage;
+import dose.util.Ui;
+import dose.util.UiMessage;
+import dose.util.exception.DoseException;
+import dose.util.exception.ExceptionType;
+import java.util.NoSuchElementException;
+
+/**
+ * Represents a command to add a tag to a task.
+ */
+public class TagCommand extends ModifyTaskCommand {
+ public TagCommand(String fullCommand) {
+ super(fullCommand);
+ }
+
+ /**
+ * Adds a tag to the task, based on command issued by the user.
+ * @param tasks List of tasks.
+ * @param ui UI to display to the user.
+ * @param storage Object that handles storage of task list to disk.
+ * @throws DoseException Application-specific exception thrown during execution.
+ */
+ @Override
+ public void execute(TaskList tasks, Ui ui, Storage storage) throws DoseException {
+ Task task = getTaskById(tasks);
+
+ // then, try to get tag
+ try {
+ String tag = s.next().strip();
+
+ // if ok, add tag to task
+ task.addTag(tag);
+ String message = UiMessage.TASK_TAGGED.getMessage() + task.toString();
+ ui.showMessage(message);
+ } catch (NoSuchElementException e) {
+ // user input after taskId is blank
+ throw new DoseException(ExceptionType.TAG_BLANK);
+ }
+ }
+}
diff --git a/src/main/java/dose/task/DeadlineTask.java b/src/main/java/dose/task/DeadlineTask.java
new file mode 100644
index 0000000000..231aeae5a1
--- /dev/null
+++ b/src/main/java/dose/task/DeadlineTask.java
@@ -0,0 +1,42 @@
+package dose.task;
+
+import static dose.util.DateTime.parseDate;
+import static dose.util.DateTime.snoozeDate;
+
+import dose.util.exception.DoseException;
+import java.util.Date;
+
+/**
+ * Represents a Deadline, a type of Task that has an expected date of completion.
+ */
+public class DeadlineTask extends Task {
+ private Date by;
+
+ /**
+ * Constructs a new Deadline, with the specified description and date of completion.
+ * @param description Description of the Deadline.
+ * @param byString String representing expected date of completion of the Deadline.
+ */
+ public DeadlineTask(String description, String byString) throws DoseException {
+ super(description);
+ this.type = TaskType.DEADLINE;
+
+ this.by = parseDate(byString);
+ }
+
+ /**
+ * Returns the String representation of a Deadline for display purposes.
+ * Adds the deadline of the Deadline to the String representation
+ * provided by the Task class.
+ * @return String representation of a Deadline for display purposes.
+ */
+ @Override
+ public String toString() {
+ return super.toString() + " (by: " + by + ")";
+ }
+
+ @Override
+ public void snooze() {
+ this.by = snoozeDate(this.by);
+ }
+}
diff --git a/src/main/java/dose/task/EventTask.java b/src/main/java/dose/task/EventTask.java
new file mode 100644
index 0000000000..c7bb779262
--- /dev/null
+++ b/src/main/java/dose/task/EventTask.java
@@ -0,0 +1,42 @@
+package dose.task;
+
+import static dose.util.DateTime.parseDate;
+import static dose.util.DateTime.snoozeDate;
+
+import dose.util.exception.DoseException;
+import java.util.Date;
+
+/**
+ * Represents an Event, a type of Task that has an expected date of occurrence.
+ */
+public class EventTask extends Task {
+ private Date at;
+
+ /**
+ * Constructs a new Event, with the specified description and date of occurrence.
+ * @param description Description of the Event.
+ * @param atString String representing expected date of occurrence of the Event.
+ */
+ public EventTask(String description, String atString) throws DoseException {
+ super(description);
+ this.type = TaskType.EVENT;
+
+ this.at = parseDate(atString);
+ }
+
+ /**
+ * Returns the String representation of an Event for display purposes.
+ * Adds the date of occurrence of the Event to the String representation
+ * provided by the Task class.
+ * @return String representation of a Event for display purposes.
+ */
+ @Override
+ public String toString() {
+ return super.toString() + " (at: " + at + ")";
+ }
+
+ @Override
+ public void snooze() {
+ this.at = snoozeDate(this.at);
+ }
+}
diff --git a/src/main/java/dose/task/Task.java b/src/main/java/dose/task/Task.java
new file mode 100644
index 0000000000..eb304e8c74
--- /dev/null
+++ b/src/main/java/dose/task/Task.java
@@ -0,0 +1,98 @@
+package dose.task;
+
+import java.io.Serializable;
+
+/**
+ * Represents a task, the building block of a TaskList object.
+ */
+public abstract class Task implements Serializable {
+ private String description;
+ private Boolean isDone = false;
+ TaskType type;
+ private String tag;
+ private TaskPriority priority;
+
+ /**
+ * Constructs a Task, with the specified description.
+ * @param description Description of the Task.
+ */
+ public Task(String description) {
+ this.description = description;
+ }
+
+ /**
+ * Returns the String representation of a Task for display purposes.
+ * @return String representation of a Task for display purposes.
+ */
+ @Override
+ public String toString() {
+ String typeInitial = "[" + this.type.getTaskTypeInitial() + "] ";
+ String status = "[" + this.getStatusIcon() + "] ";
+
+ assert this.description != null;
+ String toString = typeInitial + status + this.description;
+
+ if (this.hasTag()) {
+ toString = toString + " (tag: " + this.getTag() + ")";
+ }
+
+ if (this.hasPriority()) {
+ toString = toString + " (priority: " + this.getPriority() + ")";
+ }
+ return toString;
+ }
+
+ /**
+ * Returns the status icon corresponding to the status of the task (done or not done).
+ * @return Status icon corresponding to the status of the task (done or not done).
+ */
+ private String getStatusIcon() {
+ return (isDone ? "Y" : "N"); // return Y or N
+ }
+
+ /**
+ * Marks the task as done.
+ */
+ public void markAsDone() {
+ this.isDone = true;
+ }
+
+ /**
+ * Postpones the deadline of the given task by 1 day if no other argument is specified.
+ */
+ public abstract void snooze();
+
+ /**
+ * Adds a tag to the given task.
+ * Currently supports only one tag per task.
+ * To override existing tag, use tag command.
+ * @param tag Tag to be added to the given task.
+ */
+ public void addTag(String tag) {
+ this.tag = tag;
+ }
+
+ private String getTag() {
+ return this.tag;
+ }
+
+ private boolean hasTag() {
+ return this.getTag() != null;
+ }
+
+ /**
+ * Adds a priority to the given task.
+ * @param priority Priority to be added to the given task.
+ */
+ public void addPriority(TaskPriority priority) {
+ this.priority = priority;
+ }
+
+ private TaskPriority getPriority() {
+ return this.priority;
+ }
+
+ private boolean hasPriority() {
+ return this.getPriority() != null;
+ }
+}
diff --git a/src/main/java/dose/task/TaskList.java b/src/main/java/dose/task/TaskList.java
new file mode 100644
index 0000000000..bef0d3f755
--- /dev/null
+++ b/src/main/java/dose/task/TaskList.java
@@ -0,0 +1,120 @@
+package dose.task;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+
+/**
+ * Represents the list of tasks in the application.
+ */
+public class TaskList implements Serializable {
+
+ /** ArrayList containing tasks in the TaskList. */
+ private ArrayList tasks;
+
+ /**
+ * Constructs a new TaskList containing the tasks in the list provided.
+ * @param tasks ArrayList containing the tasks to be included in the new TaskList.
+ */
+ public TaskList(ArrayList tasks) {
+ this.tasks = tasks;
+ }
+
+ /**
+ * Constructs a new, empty TaskList.
+ */
+ public TaskList() {
+ tasks = new ArrayList<>();
+ }
+
+ /**
+ * Returns ArrayList containing the tasks in the TaskList.
+ * @return ArrayList containing the tasks in the TaskList.
+ */
+ public ArrayList getTasks() {
+ assert this.tasks != null;
+ return this.tasks;
+ }
+
+ /**
+ * Returns true if the TaskList does not contain any tasks.
+ * @return True if the TaskList does not contain any tasks.
+ */
+ public boolean isEmpty() {
+ return tasks.isEmpty();
+ }
+
+ /**
+ * Adds a new task to the list.
+ * @param task New task to be added to the list.
+ */
+ public void add(Task task) {
+ tasks.add(task);
+ assert !this.isEmpty();
+ }
+
+ /**
+ * Prints the list of tasks to the UI.
+ */
+ public void printList() {
+ for (Task task : tasks) {
+ int i = getId(task);
+ String s = task.toString();
+ System.out.println(i + ". " + s);
+ }
+ }
+
+ /**
+ * Returns the ID of the given task, in the given TaskList.
+ * Helper function for printList() method.
+ * @param task Task whose ID is required.
+ * @return ID of the given task.
+ */
+ public int getId(Task task) {
+ int id = this.tasks.indexOf(task) + 1;
+ // getId should only ever be called on tasks that are present in the list
+ assert id > 1;
+ return this.tasks.indexOf(task) + 1;
+ }
+
+ /**
+ * Returns the task with the given ID in this TaskList.
+ * @param taskId ID of the task.
+ * @return Task with the given ID.
+ * @throws IndexOutOfBoundsException Exception thrown when the given ID is not present in
+ * the TaskList.
+ */
+ public Task getTask(int taskId) throws IndexOutOfBoundsException {
+ return tasks.get(taskId - 1);
+ }
+
+ public int getSize() {
+ return tasks.size();
+ }
+
+ /**
+ * Deletes the given task from the TaskList.
+ * @param taskToDelete Task to be deleted from the TaskList.
+ */
+ public void deleteTask(Task taskToDelete) {
+ assert tasks.contains(taskToDelete);
+ tasks.remove(taskToDelete);
+ }
+
+ /**
+ * Returns a TaskList containing tasks that match the given keyword.
+ * @param keyword Keyword to be used to find tasks.
+ * @return TaskList containing tasks that match the given keyword.
+ */
+ public TaskList findTasks(String keyword) {
+ // is it possible to implement without creating temporary TaskList object?
+ // so that the numbering of the tasks based on the original list can be preserved
+ TaskList matchingTasks = new TaskList();
+ for (Task task : tasks) {
+ if (task.toString().contains(keyword)) {
+ matchingTasks.add(task);
+ }
+ }
+ return matchingTasks;
+ }
+
+}
diff --git a/src/main/java/dose/task/TaskPriority.java b/src/main/java/dose/task/TaskPriority.java
new file mode 100644
index 0000000000..9a1127c7a0
--- /dev/null
+++ b/src/main/java/dose/task/TaskPriority.java
@@ -0,0 +1,25 @@
+package dose.task;
+
+public enum TaskPriority {
+ LOW,
+ MEDIUM,
+ HIGH;
+
+ /**
+ * Returns the TaskPriority, given a String describing its priority.
+ * @param taskPriorityString String describing the priority.
+ * @return TaskPriority, based on String provided.
+ */
+ public static TaskPriority getTaskPriority(String taskPriorityString) {
+ switch (taskPriorityString) {
+ case "low":
+ return TaskPriority.LOW;
+ case "medium":
+ return TaskPriority.MEDIUM;
+ case "high":
+ return TaskPriority.HIGH;
+ default:
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/dose/task/TaskType.java b/src/main/java/dose/task/TaskType.java
new file mode 100644
index 0000000000..bb43787467
--- /dev/null
+++ b/src/main/java/dose/task/TaskType.java
@@ -0,0 +1,55 @@
+package dose.task;
+
+/**
+ * Represents the possible types of Tasks.
+ */
+public enum TaskType {
+ DEADLINE("by", "D"),
+ EVENT("at", "E"),
+ TODO("\n", "T"),
+ INVALID(null, null);
+
+ private final String delimiter;
+ private final String initial;
+
+ TaskType(String delimiter, String initial) {
+ this.delimiter = delimiter;
+ this.initial = initial;
+ }
+
+ /**
+ * Returns the delimiter used to separate the description of a Task from its additional details.
+ * Possible usage: in an alternative implementation of AddTaskCommand.
+ * @return Delimiter used to separate the description of a Task from its additional details.
+ */
+ public String getDelimiter() {
+ return this.delimiter;
+ }
+
+ /**
+ * Returns the TaskType of the task, given a String describing its type.
+ * @param taskTypeString String describing the type of the Task.
+ * @return TaskType of the task.
+ */
+ public static TaskType getTaskType(String taskTypeString) {
+ switch (taskTypeString) {
+ case "todo":
+ return TaskType.TODO;
+ case "event":
+ return TaskType.EVENT;
+ case "deadline":
+ return TaskType.DEADLINE;
+ default:
+ return TaskType.INVALID;
+ }
+ }
+
+ /**
+ * Returns the initial representing the TaskType.
+ * Possible usage: for icons representing the TaskType in UI.
+ * @return Initial representing the TaskType.
+ */
+ public String getTaskTypeInitial() {
+ return this.initial;
+ }
+}
diff --git a/src/main/java/dose/task/TodoTask.java b/src/main/java/dose/task/TodoTask.java
new file mode 100644
index 0000000000..9978b83b2c
--- /dev/null
+++ b/src/main/java/dose/task/TodoTask.java
@@ -0,0 +1,17 @@
+package dose.task;
+
+public class TodoTask extends Task {
+ public TodoTask(String description) {
+ super(description);
+ this.type = TaskType.TODO;
+ }
+
+ /**
+ * Snoozes ("postpones") the given task.
+ * Not applicable to TodoTasks.
+ */
+ @Override
+ public void snooze() {
+ // do nothing
+ }
+}
diff --git a/src/main/java/dose/util/DateTime.java b/src/main/java/dose/util/DateTime.java
new file mode 100644
index 0000000000..43e6e6fb04
--- /dev/null
+++ b/src/main/java/dose/util/DateTime.java
@@ -0,0 +1,30 @@
+package dose.util;
+
+import dose.util.exception.DoseException;
+import dose.util.exception.ExceptionType;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+//import dose.util.Ui;
+
+public class DateTime {
+ private static SimpleDateFormat dateFormatter = new SimpleDateFormat("dd/MM/yyyy HHmm");
+
+ public static Date parseDate(String date) throws DoseException {
+ try {
+ return dateFormatter.parse(date);
+ } catch (ParseException e) {
+ // deadline entered in wrong format
+ throw new DoseException(ExceptionType.INVALID_DATE);
+ }
+ }
+
+ public static Date snoozeDate(Date date) {
+ Calendar c = Calendar.getInstance();
+ c.setTime(date);
+ c.add(Calendar.DATE, 1);
+ date = c.getTime();
+ return date;
+ }
+}
diff --git a/src/main/java/dose/util/Parser.java b/src/main/java/dose/util/Parser.java
new file mode 100644
index 0000000000..ff7b57388e
--- /dev/null
+++ b/src/main/java/dose/util/Parser.java
@@ -0,0 +1,44 @@
+package dose.util;
+
+import dose.command.*;
+import dose.util.exception.DoseException;
+import dose.util.exception.ExceptionType;
+import java.util.Scanner;
+
+public class Parser {
+
+ public static Command parse(String fullCommand) throws DoseException {
+ Scanner commandReader = new Scanner(fullCommand);
+ String command = commandReader.next();
+
+ if (command.equals("bye")) {
+ return new ExitCommand();
+ } else if (command.equals("list")) {
+ return new ListCommand();
+ } else if (command.equals("done")) {
+ return new DoneCommand(fullCommand);
+ } else if (command.equals("delete")) {
+ return new DeleteCommand(fullCommand);
+ } else if (command.equals("find")) {
+ return new FindCommand(fullCommand);
+ } else if (command.equals("todo")) {
+ return new AddTodoCommand(fullCommand);
+ } else if (command.equals("event")) {
+ return new AddEventCommand(fullCommand);
+ } else if (command.equals("deadline")) {
+ return new AddDeadlineCommand(fullCommand);
+ } else if (command.equals("snooze")) {
+ return new SnoozeCommand(fullCommand);
+ } else if (command.equals("tag")) {
+ return new TagCommand(fullCommand);
+ } else if (command.equals("priority")) {
+ return new PriorityCommand(fullCommand);
+ } else if (command.equals("save")) {
+ return new SaveCommand(); // disregard any input after "save"
+ } else if (command.equals("help")) {
+ return new HelpCommand(); // disregard any input after "help" (for now)
+ } else {
+ throw new DoseException(ExceptionType.INVALID_COMMAND);
+ }
+ }
+}
diff --git a/src/main/java/dose/util/Storage.java b/src/main/java/dose/util/Storage.java
new file mode 100644
index 0000000000..b2271f6319
--- /dev/null
+++ b/src/main/java/dose/util/Storage.java
@@ -0,0 +1,90 @@
+package dose.util;
+
+import dose.task.TaskList;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+public class Storage {
+ private String filePath;
+ private Ui ui;
+
+ public Storage(String filePath, Ui ui) {
+ this.filePath = filePath;
+ this.ui = ui;
+ }
+
+ private boolean hasDirectory() {
+ // check if parent directory exists. if not, create it
+ File file = new File(this.filePath);
+ if (!file.getParentFile().exists()) {
+ file.getParentFile().mkdirs();
+ return false;
+ } else {
+ return true;
+ }
+ }
+ private boolean hasFile() {
+ File file = new File(this.filePath);
+
+ // check if file exists
+ if (!file.exists()) {
+ ui.showMessage(UiMessage.TASKS_NOT_FOUND);
+ return false;
+ }
+
+ return true;
+ }
+
+ public TaskList load() {
+ TaskList tasks = new TaskList();
+ // if directory does not exist, or file does not exist, no need to load from file
+ if (!hasDirectory() || !hasFile()) {
+ return tasks;
+ }
+
+ try {
+ FileInputStream fis = new FileInputStream(filePath);
+ ObjectInputStream ois = new ObjectInputStream(fis);
+
+ tasks = (TaskList) ois.readObject();
+
+ ois.close();
+ fis.close();
+
+ String message = UiMessage.TASKS_IMPORTED.getMessage() + this.filePath;
+ ui.showMessage(message);
+ } catch (Exception e) {
+ // temporary haxx
+ e.printStackTrace();
+ }
+
+ return tasks;
+ }
+
+ public void save(TaskList tasks) {
+ // first, check if task list is empty. if so, do not save
+ if (tasks.isEmpty()) {
+ ui.showMessage(UiMessage.TASKS_NOT_SAVED);
+ return;
+ }
+
+ try {
+ FileOutputStream fos = new FileOutputStream(this.filePath);
+ ObjectOutputStream oos = new ObjectOutputStream(fos);
+
+ oos.writeObject(tasks);
+
+ oos.close();
+ fos.close();
+
+ String message = UiMessage.TASKS_SAVED.getMessage() + this.filePath;
+ ui.showMessage(message);
+ } catch (Exception e) {
+ // temporary haxx
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/src/main/java/dose/util/Ui.java b/src/main/java/dose/util/Ui.java
new file mode 100644
index 0000000000..2a6dde4b6d
--- /dev/null
+++ b/src/main/java/dose/util/Ui.java
@@ -0,0 +1,39 @@
+package dose.util;
+
+import dose.task.TaskList;
+import dose.util.exception.DoseException;
+
+/**
+ * Encapsulates methods related to displaying responses from Dose in the UI.
+ */
+public interface Ui {
+ /**
+ * Returns the command entered by the user.
+ * @return Command entered by the user.
+ */
+ String readCommand();
+
+ /**
+ * Displays the required message in the UI, given the type of message required.
+ * @param uiMessage Enum indicating type of message required to be displayed.
+ */
+ void showMessage(UiMessage uiMessage);
+
+ /**
+ * Displays the required message in the UI, given the message required as a String.
+ * @param message String representing message to be displayed.
+ */
+ void showMessage(String message);
+
+ /**
+ * Displays the required error message in the UI, given the type of exception thrown.
+ * @param exception Exception thrown by the application.
+ */
+ void showError(DoseException exception);
+
+ /**
+ * Displays a list of tasks in the UI.
+ * @param tasks Tasks to be shown in the UI.
+ */
+ void showTasks(TaskList tasks);
+}
diff --git a/src/main/java/dose/util/UiMessage.java b/src/main/java/dose/util/UiMessage.java
new file mode 100644
index 0000000000..d943cb8d63
--- /dev/null
+++ b/src/main/java/dose/util/UiMessage.java
@@ -0,0 +1,64 @@
+package dose.util;
+
+/**
+ * Represents different types of events that need to be displayed to the user within the UI.
+ */
+public enum UiMessage {
+ // greetings
+ WELCOME("Hello! What can I do for you?"),
+ GOODBYE("Bye. Hope to see you again soon!"),
+ // storage
+ TASKS_IMPORTED("Success! Your tasks have been imported from: "),
+ TASKS_SAVED("Success! Your tasks have been saved to: "),
+ TASKS_NOT_SAVED("Your task list is empty! Adios :)"),
+ TASKS_NOT_FOUND("Existing tasks file not found! Starting dose afresh..."),
+ // commands
+ TASK_ADDED("Okay! I've added the task."),
+ TASK_DONE("Nice! I've marked this task as done: "),
+ TASK_SNOOZED("Okay! The task has been snoozed by 1 day."),
+ TASK_DELETED("Noted. I've removed this task: "),
+ MATCHING_TASKS("Here are the matching tasks in your list..."),
+ TASK_TAGGED("Nice! I've added a tag to this task: "),
+ TASK_PRIORITISED("Sweet! I've added a priority to this task: "),
+ // helpers
+ TASK_LIST("Here are the tasks in your list: "),
+ TASKS_STATUS_FRONT("Now you have "),
+ TASKS_STATUS_BACK(" items in this list."),
+ HINT_TODO("Use todo to add a new todo."),
+ HINT_DEADLINE("Use deadline /by [deadline] to add a new task with a deadline."),
+ HINT_EVENT("Use event /at [time] to add a new event at a time."),
+ HINT_DONE("Use done [taskId] to mark a task as done."),
+ HINT_DELETE("Use delete [taskId] to remove a task from the list."),
+ HINT_SAVE("Use save to save your tasks to disk."),
+ HINT_LIST("Use list to see all your tasks!"),
+ // todo: add tag, priority, find, snooze
+ HELP("Here are the things I can do...");
+
+ private final String message;
+ //private ArrayList helpMessages = new ArrayList<>();
+
+ UiMessage(String message) {
+ this.message = message;
+ }
+
+ /**
+ * Returns a message relating to the event, to be displayed in the UI.
+ * @return Message relating to the event, to be displayed in the UI.
+ */
+ public String getMessage() {
+ return this.message;
+ }
+
+ public static String getHelpMessage() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(HELP.getMessage()).append("\n");
+ sb.append(HINT_TODO.getMessage()).append("\n");
+ sb.append(HINT_DEADLINE.getMessage()).append("\n");
+ sb.append(HINT_EVENT.getMessage()).append("\n");
+ sb.append(HINT_DONE.getMessage()).append("\n");
+ sb.append(HINT_DELETE.getMessage()).append("\n");
+ sb.append(HINT_SAVE.getMessage()).append("\n");
+ sb.append(HINT_LIST.getMessage()).append("\n");
+ return sb.toString();
+ }
+}
diff --git a/src/main/java/dose/util/cli/Cli.java b/src/main/java/dose/util/cli/Cli.java
new file mode 100644
index 0000000000..175f69260b
--- /dev/null
+++ b/src/main/java/dose/util/cli/Cli.java
@@ -0,0 +1,52 @@
+package dose.util.cli;
+
+import dose.task.TaskList;
+import dose.util.Ui;
+import dose.util.UiMessage;
+import dose.util.exception.DoseException;
+import java.util.Scanner;
+
+/**
+ * Handles Dose's UI when in CLI mode.
+ */
+public class Cli implements Ui {
+ private Scanner in;
+
+ public Cli() {
+ this.in = new Scanner(System.in);
+ }
+
+ @Override
+ public String readCommand() {
+ return in.nextLine();
+ }
+
+ @Override
+ public void showMessage(UiMessage uiMessage) {
+ System.out.println(uiMessage.getMessage());
+ }
+
+ @Override
+ public void showMessage(String message) {
+ System.out.println(message);
+ }
+
+ @Override
+ public void showError(DoseException exception) {
+ System.out.println(exception.getMessage());
+ }
+
+ @Override
+ public void showTasks(TaskList tasks) {
+ System.out.println("Here are the matching tasks in your list:");
+ tasks.printList();
+ }
+
+ /**
+ * Displays a divider line in the UI.
+ */
+ public void showLine() {
+ String line = "____________________________________________________________";
+ System.out.println(line);
+ }
+}
diff --git a/src/main/java/dose/util/cli/CliDose.java b/src/main/java/dose/util/cli/CliDose.java
new file mode 100644
index 0000000000..cb98a51910
--- /dev/null
+++ b/src/main/java/dose/util/cli/CliDose.java
@@ -0,0 +1,72 @@
+package dose.util.cli;
+
+import dose.Dose;
+import dose.command.Command;
+import dose.task.TaskList;
+import dose.util.Parser;
+import dose.util.Storage;
+import dose.util.UiMessage;
+import dose.util.exception.DoseException;
+
+public class CliDose implements Dose {
+ Storage storage;
+ TaskList tasks;
+ String filePath = "data/tasks.txt";
+
+ /** CLI implementation of Dose uses a Cli object to represent its UI. */
+ Cli ui;
+
+ /**
+ * Creates a new instance of Dose, with the default filePath.
+ */
+ public CliDose() {
+ ui = new Cli();
+ storage = new Storage(filePath, ui);
+ tasks = new TaskList();
+ }
+
+ /**
+ * Runs Dose from the CLI. All output is displayed in the CLI.
+ */
+ public void run() {
+ ui.showMessage(UiMessage.WELCOME);
+ initializeStorage();
+
+ boolean isExit = false;
+ while (!isExit) {
+ try {
+ String fullCommand = ui.readCommand();
+ ui.showLine(); // show the divider line ("_______")
+ Command c = Parser.parse(fullCommand);
+ c.execute(tasks, ui, storage);
+ isExit = c.isExit();
+ } catch (DoseException e) {
+ ui.showError(e);
+ } finally {
+ ui.showLine();
+ }
+ }
+ }
+
+ /**
+ * Creates and runs a new instance of Dose from the CLI.
+ * @param args Arguments supplied by the user.
+ */
+ public static void main(String[] args) {
+ CliDose duke = new CliDose();
+ duke.run();
+ }
+
+ /**
+ * Attempts to import an existing task list.
+ */
+ public void initializeStorage() {
+ try {
+ TaskList tasksFromFile = storage.load();
+ tasks = tasksFromFile;
+ } catch (Exception e) {
+ // temporary haxx
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/src/main/java/dose/util/exception/DoseException.java b/src/main/java/dose/util/exception/DoseException.java
new file mode 100644
index 0000000000..6561f3db16
--- /dev/null
+++ b/src/main/java/dose/util/exception/DoseException.java
@@ -0,0 +1,15 @@
+package dose.util.exception;
+
+public class DoseException extends Exception {
+ private ExceptionType exceptionType;
+
+ public DoseException(ExceptionType exceptionType) {
+ super();
+ this.exceptionType = exceptionType;
+ }
+
+ @Override
+ public String getMessage() {
+ return this.exceptionType.getMessage();
+ }
+}
diff --git a/src/main/java/dose/util/exception/ExceptionType.java b/src/main/java/dose/util/exception/ExceptionType.java
new file mode 100644
index 0000000000..65e60068e4
--- /dev/null
+++ b/src/main/java/dose/util/exception/ExceptionType.java
@@ -0,0 +1,35 @@
+package dose.util.exception;
+
+/**
+ * Represents different types of Exceptions unique to the application.
+ */
+public enum ExceptionType {
+ // invalid
+ INVALID_ID("Oops! You entered an invalid task ID!"),
+ INVALID_COMMAND("Oops! I don't know what that means. Try using help to see what I can do."),
+ INVALID_DATE("Oops! You did not enter the date in an appropriate format.\n" +
+ "Try: DD/MM/YYYY HHmm instead."),
+ // blank
+ ID_BLANK("Oops! You did not enter a task ID!"),
+ DESCRIPTION_BLANK("Oops! You did not enter a description!"),
+ DEADLINE_BLANK("Oops! You did not enter a deadline!"),
+ KEYWORD_BLANK("Oops! You did not enter a keyword!"),
+ TAG_BLANK("Oops! You did not enter a tag!"),
+ // nothing to return
+ NO_MATCHING_TASKS("There are no tasks matching your query :("),
+ TASK_LIST_EMPTY("The task list is empty.");
+
+ private final String message;
+
+ ExceptionType(String message) {
+ this.message = message;
+ }
+
+ /**
+ * Returns a message describing the Exception, to be displayed in the UI.
+ * @return Message describing the Exception, to be displayed in the UI.
+ */
+ public String getMessage() {
+ return this.message;
+ }
+}
diff --git a/src/main/java/dose/util/gui/ColourScheme.java b/src/main/java/dose/util/gui/ColourScheme.java
new file mode 100644
index 0000000000..5f514da9c4
--- /dev/null
+++ b/src/main/java/dose/util/gui/ColourScheme.java
@@ -0,0 +1,49 @@
+package dose.util.gui;
+
+public enum ColourScheme {
+ MINT("#b2eee6", "#8ad6cc", "#66beb2",
+ "#f99192", "#f97171", "#333333"),
+ GREY("#f4f6f9", "#e5e8ec", "#cbd0d8",
+ "#a9b1bc", "#646c77", "#424953");
+
+ private final String backgroundColour;
+ private final String dukeMessageBoxColour;
+ private final String dukeShadowColour; // also serves as the colour of the taskMessageBox
+ private final String userMessageBoxColour;
+ private final String userShadowColour; // also serves as the colour of the exceptionMessageBox
+ private final String textColour;
+
+ public String getBackgroundColour() {
+ return backgroundColour;
+ }
+
+ public String getDukeMessageBoxColour() {
+ return dukeMessageBoxColour;
+ }
+
+ public String getDukeShadowColour() {
+ return dukeShadowColour;
+ }
+
+ public String getUserMessageBoxColour() {
+ return userMessageBoxColour;
+ }
+
+ public String getUserShadowColour() {
+ return userShadowColour;
+ }
+
+ public String getTextColour() {
+ return textColour;
+ }
+
+ ColourScheme(String backgroundColour, String dukeMessageBoxColour, String dukeShadowColour,
+ String userMessageBoxColour, String userShadowColour, String textColour) {
+ this.backgroundColour = backgroundColour;
+ this.dukeMessageBoxColour = dukeMessageBoxColour;
+ this.dukeShadowColour = dukeShadowColour;
+ this.userMessageBoxColour = userMessageBoxColour;
+ this.userShadowColour = userShadowColour;
+ this.textColour = textColour;
+ }
+}
diff --git a/src/main/java/dose/util/gui/Gui.java b/src/main/java/dose/util/gui/Gui.java
new file mode 100644
index 0000000000..eaec064e16
--- /dev/null
+++ b/src/main/java/dose/util/gui/Gui.java
@@ -0,0 +1,92 @@
+package dose.util.gui;
+
+import dose.task.Task;
+import dose.task.TaskList;
+import dose.util.Ui;
+import dose.util.UiMessage;
+import dose.util.exception.DoseException;
+import dose.util.gui.messagebox.DukeMessageBox;
+import dose.util.gui.messagebox.ExceptionMessageBox;
+import dose.util.gui.messagebox.MessageBox;
+import dose.util.gui.messagebox.TaskMessageBox;
+import dose.util.gui.messagebox.UserMessageBox;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.Queue;
+
+/**
+ * Handles Dose's UI when in GUI mode.
+ */
+public class Gui implements Ui {
+
+ /** Represents the colour scheme in use for the GUI. Hardcoded as MINT for now. */
+ private ColourScheme colourScheme = ColourScheme.MINT;
+
+ /** Represents the queue of messages to be displayed in the GUI. */
+ // todo: change Queue to Stream
+ private Queue messageBoxQueue = new LinkedList<>();
+
+ /**
+ * Not in use for Dose's GUI.
+ * @return Nothing.
+ */
+ @Override
+ public String readCommand() {
+ return null;
+ }
+
+ public void showUserInput(String input) {
+ try {
+ messageBoxQueue.add(new UserMessageBox(input, this.colourScheme));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void showMessage(UiMessage uiMessage) {
+ try {
+ messageBoxQueue.add(new DukeMessageBox(uiMessage.getMessage(), this.colourScheme));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void showMessage(String message) {
+ try {
+ messageBoxQueue.add(new DukeMessageBox(message, this.colourScheme));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void showError(DoseException exception) {
+ try {
+ messageBoxQueue.add(new ExceptionMessageBox(exception.getMessage(), this.colourScheme));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void showTasks(TaskList tasks) {
+ ArrayList listOfTasks = tasks.getTasks();
+ for (Task task : listOfTasks) {
+ showTask(task, tasks);
+ }
+ }
+
+ public void showTask(Task task, TaskList tasks) {
+ String taskString = task.toString();
+ int taskIdString = tasks.getId(task);
+ String toPrint = taskIdString + ". " + taskString;
+ messageBoxQueue.add(new TaskMessageBox(toPrint, this.colourScheme));
+ }
+
+ public Queue getMessageBoxQueue() {
+ return messageBoxQueue;
+ // todo: move clear() to here, makes more sense
+ }
+}
diff --git a/src/main/java/dose/util/gui/GuiDose.java b/src/main/java/dose/util/gui/GuiDose.java
new file mode 100644
index 0000000000..9af13bf9c2
--- /dev/null
+++ b/src/main/java/dose/util/gui/GuiDose.java
@@ -0,0 +1,67 @@
+package dose.util.gui;
+
+import dose.Dose;
+import dose.command.Command;
+import dose.task.TaskList;
+import dose.util.Parser;
+import dose.util.Storage;
+import dose.util.UiMessage;
+import dose.util.exception.DoseException;
+
+public class GuiDose implements Dose {
+ Storage storage;
+ TaskList tasks;
+ String filePath = "/data/tasks.txt";
+
+ /** GUI implementation of Dose uses a Gui object to represent its UI. */
+ Gui ui;
+
+ /**
+ * Creates a new instance of Dose to be run from the GUI, with the default filePath.
+ */
+ public GuiDose() {
+ ui = new Gui();
+ storage = new Storage(filePath, ui);
+ tasks = new TaskList();
+ }
+
+ /**
+ * Get response from Dose to be displayed in the UI.
+ * May not be required after UI implementation is refined.
+ * @return Response from Dose to be displayed in the UI.
+ */
+ public void getResponse(String input) {
+ try {
+ Command c = Parser.parse(input);
+ c.execute(tasks, ui, storage);
+ } catch (DoseException e) {
+ ui.showError(e);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ // dummy implementation
+// return "Dose heard: " + input;
+ //return ui.getResponse();
+ }
+
+ @Override
+ public void run() {
+ // todo: figure out why welcome message doesn't appear until user's first input
+ ui.showMessage(UiMessage.WELCOME);
+ initializeStorage();
+ }
+
+ /**
+ * Attempts to import an existing task list.
+ */
+ public void initializeStorage() {
+ try {
+ TaskList tasksFromFile = storage.load();
+ tasks = tasksFromFile;
+ } catch (Exception e) {
+ // temporary haxx
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/src/main/java/dose/util/gui/MainWindow.java b/src/main/java/dose/util/gui/MainWindow.java
new file mode 100644
index 0000000000..0b393847e6
--- /dev/null
+++ b/src/main/java/dose/util/gui/MainWindow.java
@@ -0,0 +1,77 @@
+package dose.util.gui;
+
+import dose.util.gui.messagebox.MessageBox;
+import java.util.Queue;
+import javafx.fxml.FXML;
+import javafx.scene.control.Button;
+import javafx.scene.control.ScrollPane;
+import javafx.scene.control.TextField;
+import javafx.scene.layout.AnchorPane;
+import javafx.scene.layout.VBox;
+
+/**
+ * Controller for MainWindow. Provides the layout for the other controls.
+ */
+public class MainWindow extends AnchorPane {
+ @FXML
+ private ScrollPane scrollPane;
+ @FXML
+ private VBox messageBoxContainer;
+ @FXML
+ private TextField userInput;
+ @FXML
+ private Button sendButton;
+
+ private GuiDose dose;
+
+ /** Represents the colour scheme in use for the GUI. Hardcoded as MINT for now. */
+ private ColourScheme colourScheme = ColourScheme.MINT;
+
+ @FXML
+ public void initialize() {
+ scrollPane.vvalueProperty().bind(messageBoxContainer.heightProperty());
+ messageBoxContainer.setStyle("-fx-background-color: " + colourScheme.getBackgroundColour());
+ }
+
+ public void setDose(GuiDose d) {
+ dose = d;
+ }
+
+ /**
+ * Creates two dialog boxes, one echoing user input and the other containing Dose's reply and then appends them to
+ * the dialog container. Clears the user input after processing.
+ */
+ @FXML
+ private void handleUserInput() {
+ try {
+ // get and display user input
+ String input = userInput.getText();
+ dose.ui.showUserInput(input);
+
+ dose.getResponse(input);
+
+ // clear user input field
+ userInput.clear();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ try {
+ // get and display output from Dose
+ Queue messageBoxQueue = dose.ui.getMessageBoxQueue();
+ messageBoxContainer.getChildren().addAll(messageBoxQueue);
+ dose.ui.getMessageBoxQueue().clear();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+// /**
+// * Changes the appearance of Dose's GUI to match the given colour scheme.
+// * Not sure why this needs to be static. KIV implementation :(
+// * @param colourScheme Colour scheme for Dose's GUI.
+// */
+// public static void setColourScheme(ColourScheme colourScheme) {
+// this.colourScheme = colourScheme;
+// }
+}
\ No newline at end of file
diff --git a/src/main/java/dose/util/gui/messagebox/DukeMessageBox.java b/src/main/java/dose/util/gui/messagebox/DukeMessageBox.java
new file mode 100644
index 0000000000..50d7ca5346
--- /dev/null
+++ b/src/main/java/dose/util/gui/messagebox/DukeMessageBox.java
@@ -0,0 +1,15 @@
+package dose.util.gui.messagebox;
+
+import dose.util.gui.ColourScheme;
+import javafx.scene.effect.DropShadow;
+import javafx.scene.paint.Color;
+
+public class DukeMessageBox extends MessageBox {
+ public DukeMessageBox(String text, ColourScheme colourScheme) {
+ super(text);
+ messageBox.setStyle("-fx-background-color: " + colourScheme.getDukeMessageBoxColour());
+ getMessage().setStyle("-fx-fill: " + colourScheme.getTextColour());
+ DropShadow dropShadow = new DropShadow(5.0, Color.web(colourScheme.getDukeShadowColour()));
+ messageBox.setEffect(dropShadow);
+ }
+}
diff --git a/src/main/java/dose/util/gui/messagebox/ExceptionMessageBox.java b/src/main/java/dose/util/gui/messagebox/ExceptionMessageBox.java
new file mode 100644
index 0000000000..80881bff69
--- /dev/null
+++ b/src/main/java/dose/util/gui/messagebox/ExceptionMessageBox.java
@@ -0,0 +1,16 @@
+package dose.util.gui.messagebox;
+
+import dose.util.gui.ColourScheme;
+import javafx.scene.effect.DropShadow;
+import javafx.scene.paint.Color;
+
+// todo: custom styling for exceptions, including exclamation mark etc
+public class ExceptionMessageBox extends MessageBox {
+ public ExceptionMessageBox(String text, ColourScheme colourScheme) {
+ super(text);
+ messageBox.setStyle("-fx-background-color: " + colourScheme.getUserShadowColour());
+ getMessage().setStyle("-fx-fill: " + colourScheme.getTextColour());
+ DropShadow dropShadow = new DropShadow(5.0, Color.web(colourScheme.getUserShadowColour()));
+ messageBox.setEffect(dropShadow);
+ }
+}
diff --git a/src/main/java/dose/util/gui/messagebox/MessageBox.java b/src/main/java/dose/util/gui/messagebox/MessageBox.java
new file mode 100644
index 0000000000..c1e1a3670e
--- /dev/null
+++ b/src/main/java/dose/util/gui/messagebox/MessageBox.java
@@ -0,0 +1,53 @@
+package dose.util.gui.messagebox;
+
+import dose.util.gui.MainWindow;
+import java.io.IOException;
+import javafx.fxml.FXML;
+import javafx.fxml.FXMLLoader;
+import javafx.geometry.Pos;
+import javafx.scene.control.Label;
+import javafx.scene.layout.HBox;
+import javafx.scene.layout.VBox;
+
+/**
+ * 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.
+ */
+// todo: replace Label with TextFlow to enable text to wrap multiple lines!
+// todo: still can't get user input to align right :(
+public abstract class MessageBox extends VBox {
+
+ @FXML
+ private Label message;
+
+ @FXML
+ HBox messageBox;
+
+ @FXML
+ VBox messageBoxContainer;
+
+ MessageBox(String text) {
+ try {
+ FXMLLoader fxmlLoader = new FXMLLoader(MainWindow.class.getResource(
+ "/view/MessageBox.fxml"));
+ fxmlLoader.setController(this);
+ fxmlLoader.setRoot(this);
+ fxmlLoader.load();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ // I give up :(
+ messageBoxContainer.setAlignment(Pos.TOP_LEFT);
+ message.setText(text);
+ }
+
+ public Label getMessage() {
+ return message;
+ }
+
+ public void setMessage(Label message) {
+ this.message = message;
+ }
+}
diff --git a/src/main/java/dose/util/gui/messagebox/TaskMessageBox.java b/src/main/java/dose/util/gui/messagebox/TaskMessageBox.java
new file mode 100644
index 0000000000..37f4d6e9d3
--- /dev/null
+++ b/src/main/java/dose/util/gui/messagebox/TaskMessageBox.java
@@ -0,0 +1,16 @@
+package dose.util.gui.messagebox;
+
+import dose.util.gui.ColourScheme;
+import javafx.scene.effect.DropShadow;
+import javafx.scene.paint.Color;
+
+// todo: custom styling for tasks, including status, priority, deadline, tags etc
+public class TaskMessageBox extends MessageBox {
+ public TaskMessageBox(String text, ColourScheme colourScheme) {
+ super(text);
+ messageBox.setStyle("-fx-background-color: " + colourScheme.getDukeShadowColour());
+ getMessage().setStyle("-fx-fill: " + colourScheme.getTextColour());
+ DropShadow dropShadow = new DropShadow(5.0, Color.web(colourScheme.getDukeShadowColour()));
+ messageBox.setEffect(dropShadow);
+ }
+}
diff --git a/src/main/java/dose/util/gui/messagebox/UserMessageBox.java b/src/main/java/dose/util/gui/messagebox/UserMessageBox.java
new file mode 100644
index 0000000000..1d5a5929f4
--- /dev/null
+++ b/src/main/java/dose/util/gui/messagebox/UserMessageBox.java
@@ -0,0 +1,28 @@
+package dose.util.gui.messagebox;
+
+import dose.util.gui.ColourScheme;
+import javafx.geometry.Pos;
+import javafx.scene.effect.DropShadow;
+import javafx.scene.paint.Color;
+
+public class UserMessageBox extends MessageBox {
+ public UserMessageBox(String text, ColourScheme colourScheme) {
+ super(text);
+ messageBox.setStyle("-fx-background-color: " + colourScheme.getUserMessageBoxColour());
+ getMessage().setStyle("-fx-fill: " + colourScheme.getTextColour());
+ DropShadow dropShadow = new DropShadow(5.0, Color.web(colourScheme.getUserShadowColour()));
+ messageBox.setEffect(dropShadow);
+ //flip();
+ messageBoxContainer.setAlignment(Pos.TOP_RIGHT);
+ }
+
+// /**
+// * 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_RIGHT);
+// }
+}
diff --git a/src/main/resources/images/coffee.png b/src/main/resources/images/coffee.png
new file mode 100644
index 0000000000..14f2a825b2
Binary files /dev/null and b/src/main/resources/images/coffee.png differ
diff --git a/src/main/resources/images/kawaii_robot.png b/src/main/resources/images/kawaii_robot.png
new file mode 100644
index 0000000000..74d6c83e3c
Binary files /dev/null and b/src/main/resources/images/kawaii_robot.png differ
diff --git a/src/main/resources/images/kawaii_robot_power.png b/src/main/resources/images/kawaii_robot_power.png
new file mode 100644
index 0000000000..48cb73aaf8
Binary files /dev/null and b/src/main/resources/images/kawaii_robot_power.png differ
diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml
new file mode 100644
index 0000000000..df697b3483
--- /dev/null
+++ b/src/main/resources/view/MainWindow.fxml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/view/MessageBox.fxml b/src/main/resources/view/MessageBox.fxml
new file mode 100644
index 0000000000..d37b71cd21
--- /dev/null
+++ b/src/main/resources/view/MessageBox.fxml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/test/java/TaskTest.java b/src/test/java/TaskTest.java
new file mode 100644
index 0000000000..d755939612
--- /dev/null
+++ b/src/test/java/TaskTest.java
@@ -0,0 +1,32 @@
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import dose.task.DeadlineTask;
+import dose.task.EventTask;
+import dose.task.Task;
+import dose.task.TodoTask;
+import dose.util.exception.DoseException;
+import org.junit.jupiter.api.Test;
+
+public class TaskTest {
+ @Test
+ public void testTodoToString() {
+ Task task = new TodoTask("practice math");
+ assertEquals("[T] [N] practice math",
+ task.toString());
+ }
+
+ @Test
+ public void testDeadlineToString() throws DoseException {
+ Task task = new DeadlineTask("this project", "30/9/2019 2300");
+ assertEquals("[D] [N] this project (by: Mon Sep 30 23:00:00 SGT 2019)",
+ task.toString());
+ }
+
+ @Test
+ public void testEventToString() throws DoseException {
+ Task task = new EventTask("play guitar with friends", "1/10/2019 1900");
+ assertEquals("[E] [N] play guitar with friends (at: Tue Oct 01 19:00:00 SGT 2019)",
+ task.toString());
+ }
+
+}
diff --git a/src/test/java/UiMessageTest.java b/src/test/java/UiMessageTest.java
new file mode 100644
index 0000000000..ba570da8d8
--- /dev/null
+++ b/src/test/java/UiMessageTest.java
@@ -0,0 +1,37 @@
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import dose.util.UiMessage;
+import org.junit.jupiter.api.Test;
+
+public class UiMessageTest {
+ @Test
+ public void testWelcome() {
+ UiMessage uiMessage = UiMessage.WELCOME;
+ assertEquals("Hello! What can I do for you?",
+ uiMessage.getMessage());
+ }
+
+ @Test
+ public void testTaskStatus() {
+ String uiMessageFront = UiMessage.TASKS_STATUS_FRONT.getMessage();
+ int numTasks = 5;
+ String uiMessageBack = UiMessage.TASKS_STATUS_BACK.getMessage();
+ String message = uiMessageFront + numTasks + uiMessageBack;
+ assertEquals("Now you have 5 items in this list.",
+ message);
+ }
+
+ @Test
+ public void testHelpMessage() {
+ String helpMessage = UiMessage.getHelpMessage();
+ assertEquals("Here are the things I can do...\n"
+ + "Use todo to add a new todo.\n"
+ + "Use deadline /by [deadline] to add a new task with a deadline.\n"
+ + "Use event /at [time] to add a new event at a time.\n"
+ + "Use done [taskId] to mark a task as done.\n"
+ + "Use delete [taskId] to remove a task from the list.\n"
+ + "Use save to save your tasks to disk.\n"
+ + "Use list to see all your tasks!\n",
+ helpMessage);
+ }
+}
diff --git a/tutorials/gradleTutorial.md b/tutorials/gradleTutorial.md
index 08292b118d..c8dce655b2 100644
--- a/tutorials/gradleTutorial.md
+++ b/tutorials/gradleTutorial.md
@@ -30,10 +30,10 @@ As a developer, you write a _build file_ that describes the project. A build fil
git checkout master
git merge gradle
```
-1. Open the `build.gradle` file in an editor. Update the following code block to point to the main class (i.e., the one containing the `main` method) of your application. The code below assumes your main class is `seedu.duke.Duke`
+1. Open the `build.gradle` file in an editor. Update the following code block to point to the main class (i.e., the one containing the `main` method) of your application. The code below assumes your main class is `seedu.dose.Dose`
```groovy
application {
- mainClassName = "seedu.duke.Duke"
+ mainClassName = "seedu.dose.Dose"
}
```
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.
@@ -106,7 +106,7 @@ The plugin can be configured by setting some properties. Let's try to produce a
Add the following block to your build file:
```groovy
shadowJar {
- archiveBaseName = "duke"
+ archiveBaseName = "dose"
archiveVersion = "0.1.3"
archiveClassifier = null
archiveAppendix = null
@@ -146,15 +146,15 @@ By convention, java tests belong in `src/test/java` folder. Create a new `test/j
src
├─main
│ └─java
-│ └─seedu/duke/Duke.java
+│ └─seedu/dose/Duke.java
└─test
└─java
- └─seedu/duke/DukeTest.java
+ └─seedu/dose/DoseTest.java
```
If you have imported your Gradle project into IntelliJ IDEA, you will notice that IDEA is able to mark the test directory as the _Test root_ (colored in green by default) automatically.
-You can now write a test (e.g., `test/java/seedu/duke/DukeTest.java`) and run it with `gradlew test`.
+You can now write a test (e.g., `test/java/seedu/dose/DoseTest.java`) and run it with `gradlew test`.
**Resources**:
* [Gradle documentation for JUnit](https://docs.gradle.org/current/userguide/java_testing.html#using_junit5)
diff --git a/tutorials/javaFxTutorialPart3.md b/tutorials/javaFxTutorialPart3.md
index a9e1bdddd3..9ae617a5a6 100644
--- a/tutorials/javaFxTutorialPart3.md
+++ b/tutorials/javaFxTutorialPart3.md
@@ -115,7 +115,7 @@ Image|Filename
public class Duke extends Application {
// ...
private Image user = new Image(this.getClass().getResourceAsStream("/images/DaUser.png"));
- private Image duke = new Image(this.getClass().getResourceAsStream("/images/DaDuke.png"));
+ private Image dose = new Image(this.getClass().getResourceAsStream("/images/DaDuke.png"));
// ...
}
```
@@ -132,7 +132,7 @@ private void handleUserInput() {
Label dukeText = new Label(getResponse(userInput.getText()));
dialogContainer.getChildren().addAll(
new DialogBox(userText, new ImageView(user)),
- new DialogBox(dukeText, new ImageView(duke))
+ new DialogBox(dukeText, new ImageView(dose))
);
userInput.clear();
}
@@ -213,7 +213,7 @@ private void handleUserInput() {
Label dukeText = new Label(getResponse(userInput.getText()));
dialogContainer.getChildren().addAll(
DialogBox.getUserDialog(userText, new ImageView(user)),
- DialogBox.getDukeDialog(dukeText, new ImageView(duke))
+ DialogBox.getDukeDialog(dukeText, new ImageView(dose))
);
userInput.clear();
}
diff --git a/tutorials/javaFxTutorialPart4.md b/tutorials/javaFxTutorialPart4.md
index 1a6e5bc412..a6ba0ba31a 100644
--- a/tutorials/javaFxTutorialPart4.md
+++ b/tutorials/javaFxTutorialPart4.md
@@ -128,7 +128,7 @@ public class MainWindow extends AnchorPane {
@FXML
private Button sendButton;
- private Duke duke;
+ private Duke dose;
private Image userImage = new Image(this.getClass().getResourceAsStream("/images/DaUser.png"));
private Image dukeImage = new Image(this.getClass().getResourceAsStream("/images/DaDuke.png"));
@@ -139,7 +139,7 @@ public class MainWindow extends AnchorPane {
}
public void setDuke(Duke d) {
- duke = d;
+ dose = d;
}
/**
@@ -149,7 +149,7 @@ public class MainWindow extends AnchorPane {
@FXML
private void handleUserInput() {
String input = userInput.getText();
- String response = duke.getResponse(input);
+ String response = dose.getResponse(input);
dialogContainer.getChildren().addAll(
DialogBox.getUserDialog(input, userImage),
DialogBox.getDukeDialog(response, dukeImage)
@@ -186,7 +186,7 @@ import javafx.stage.Stage;
*/
public class Main extends Application {
- private Duke duke = new Duke();
+ private Duke dose = new Duke();
@Override
public void start(Stage stage) {
@@ -195,7 +195,7 @@ public class Main extends Application {
AnchorPane ap = fxmlLoader.load();
Scene scene = new Scene(ap);
stage.setScene(scene);
- fxmlLoader.getController().setDuke(duke);
+ fxmlLoader.getController().setDuke(dose);
stage.show();
} catch (IOException e) {
e.printStackTrace();