diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml
new file mode 100644
index 0000000000..133b0e7f2b
--- /dev/null
+++ b/.github/workflows/gradle.yml
@@ -0,0 +1,34 @@
+name: Java CI
+
+on: [push, pull_request]
+
+jobs:
+ build:
+ strategy:
+ matrix:
+ platform: [ubuntu-latest, macos-latest, windows-latest]
+ runs-on: ${{ matrix.platform }}
+
+ steps:
+ - name: Set up repository
+ uses: actions/checkout@master
+
+ - name: Set up repository
+ uses: actions/checkout@master
+ with:
+ ref: master
+
+ - name: Merge to master
+ run: git checkout --progress --force ${{ github.sha }}
+
+ - name: Validate Gradle Wrapper
+ uses: gradle/wrapper-validation-action@v1
+
+ - name: Setup JDK 11
+ uses: actions/setup-java@v1
+ with:
+ java-version: '11'
+ java-package: jdk+fx
+
+ - name: Build and check with Gradle
+ run: ./gradlew check
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index f69985ef1f..98c24b7e05 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,5 +13,7 @@ src/main/resources/docs/
*.iml
bin/
+/data/
+/text-ui-test/data/
/text-ui-test/ACTUAL.txt
-text-ui-test/EXPECTED-UNIX.TXT
+/text-ui-test/EXPECTED-UNIX.TXT
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000000..5a3c3737f4
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,61 @@
+plugins {
+ id 'java'
+ id 'application'
+ id 'checkstyle'
+ id 'com.github.johnrengelman.shadow' version '5.1.0'
+}
+
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ String javaFxVersion = '11'
+
+ testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.5.0'
+ testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.5.0'
+ 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'
+}
+
+test {
+ useJUnitPlatform()
+
+ testLogging {
+ events "passed", "skipped", "failed"
+
+ showExceptions true
+ exceptionFormat "full"
+ showCauses true
+ showStackTraces true
+ showStandardStreams = false
+ }
+}
+
+application {
+ mainClassName = "ren.Launcher"
+}
+
+checkstyle {
+ toolVersion = '10.2'
+}
+
+shadowJar {
+ archiveBaseName = "ren"
+ archiveClassifier = null
+}
+
+run{
+ standardInput = System.in
+ enableAssertions = true
+}
diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml
new file mode 100644
index 0000000000..d618671b83
--- /dev/null
+++ b/config/checkstyle/checkstyle.xml
@@ -0,0 +1,434 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/config/checkstyle/suppressions.xml b/config/checkstyle/suppressions.xml
new file mode 100644
index 0000000000..39efb6e4ac
--- /dev/null
+++ b/config/checkstyle/suppressions.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
diff --git a/docs/README.md b/docs/README.md
index 8077118ebe..38d2959619 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,29 +1,235 @@
# User Guide
-## Features
+Ren is a **desktop task manager app optimized for use via a Command Line Interface** (CLI)
+while still having the benefits of a Graphical User Interface (GUI). If you can type fast,
+Ren can manage your tasks faster than traditional GUI apps.
-### Feature-ABC
+--------------------------------------------------------------------------------------------------------------------
-Description of the feature.
+## Quick start
-### Feature-XYZ
+1. Ensure you have Java `11` or above installed in your Computer.
+2. Download the latest `ren.jar` from [here](https://github.com/Eugene-Ong-W-X/ip/releases).
+3. Copy the file to the folder you want to use as the _home folder_ for your Ren Task Manager.
+4. Double-click the file to start the app. The GUI similar to the below should appear in a few seconds.
+ ![Ui](/docs/Ui.png)
+5. Type a command at the bottom and press Enter or click Send to execute it.
+6. Refer to the [Usage](#usage) below for details of each command.
-Description of the feature.
+--------------------------------------------------------------------------------------------------------------------
+
+## Features
+
+### Manage Tasks
+
+3 different types of Tasks. Operations supported
+* add task
+* delete task / empty list
+* mark/unmark task
+
+### Organise Tasks
+
+View your list of Tasks. Sort the list. Find a task from the list.
+
+### Saved Data
+
+Your TaskList data is saved in the hard disk automatically after
+any command that modifies the data. Have no fear of losing your data!
+Have no worries about saving your data manually!
+
+--------------------------------------------------------------------------------------------------------------------
## Usage
-### `Keyword` - Describe action
+
+
+**Notes about the command format:**
+
+* Words in `UPPER_CASE` are the parameters to be supplied by the user.
+
+* Extra parameters for commands that do not take in parameters will be ignored.
+ e.g. if the command specifies `list 123`, it will be interpreted as `list`.
+
+
+
+### Add a Todo Task - `todo`
+
+Adds a Task, that needs to be done, to the TaskList.
+
+Format: `todo DESCRIPTION`
+
+Example Usage
+
+>`todo Watch recorded lecture videos`
+
+Example Outcome
+```
+ Understood. I have added the following task:
+ [T][ ] Watch recorded lecture videos
+ You now have a total of 1 task(s).
+```
+
+### Add a Deadline Task - `deadline`
+
+Adds a Task, that needs to be done by a certain date or time, to the TaskList.
+
+Format: `deadline DESCRIPTION /by DD/MM/YY-HH:MM`
+* Time must be in 24hr format (i.e. 23:59 instead of 11:59 PM)
+
+Example Usage
+
+>`deadline Submit Essay /by 11/9/2022-23:59`
+
+Example Outcome
+```
+ Understood. I have added the following task:
+ [D][ ] Submit Essay (by: Sun, 11 September 2022 11:59 PM)
+ You now have a total of 1 task(s).
+```
+
+### Add an Event Task - `event`
+
+Adds an Event, that takes place between certain dates or time, to the TaskList.
+
+Format: `event DESCRIPTION /at DD/MM/YY-HH:MM ~ DD/MM/YY-HH:MM`
+* Time must be in 24hr format (i.e. 23:59 instead of 11:59 PM)
+
+Example Usage
+
+>`event Recess Week /at 17/9/2022-00:00 ~ 25/9/2022-23:59`
+
+Example Outcome
+```
+ Understood. I have added the following task:
+ [E][ ] Recess Week (at: Sat, 17 September 2022 12:00 AM - Sun, 25 September 2022 11:59 PM)
+ You now have a total of 1 task(s).
+```
+
+### Mark a Task - `mark`
+
+Sets a Task, in the TaskList, as completed.
+
+Format: `mark INDEX`
+* Use index of task from the list of tasks
+
+Example Usage
+
+>`mark 1`
+
+Example Outcome
+```
+ Great job! I will mark the task as completed.
+ [T][X] Watch recorded lecture videos
+```
+
+### Unmark a Task - `unmark`
+
+Sets a Task, in the TaskList, to uncompleted.
+
+Format: `unmark INDEX`
+* Use index of task from the list of tasks
+
+Example Usage
+
+>`unmark 1`
+
+Example Outcome
+```
+ Understood. I will mark the task as uncompleted.
+ [T][ ] Watch recorded lecture videos
+```
+
+### Lists all Tasks - `list`
+
+Displays all Tasks in the TaskList.
+
+Format: `list`
+
+Example Usage
+
+>`list`
+
+Example Outcome
+```
+ Here are your current tasks:
+ 1. [T][ ] Watch recorded lecture videos
+ 2. [T][X] Submit Essay
+```
+
+### Sort Tasks - `sort`
+
+Sorts all Tasks in the TaskList.
+
+Format: `sort ATTRIBUTE`
+* Attribute 1. `type` Sorts in the order of Todo > Deadline > Event
+* Attribute 2. `status` Sorts uncompleted Tasks before completed Tasks
+* Attribute 3. `description` Sorts by description, in lexicographical order
+* Attribute 4. `date` Sorts by date, in chronological order (Todos are last)
+
+Example Usage
+
+>`sort status`
+
+Example Outcome
+```
+ I have finished sorting your list of tasks!
+
+ Here are your current tasks:
+ 1. [T][ ] Watch recorded lecture videos
+ 2. [T][X] Submit Essay
+```
+
+### Find a Task - `find`
+
+Finds a Task from the TaskList.
+
+Format: `find SEARCH_TERM`
+
+Example Usage
+
+>`find video`
+
+Example Outcome
+```
+ I have found these matching tasks:
+ 1. [T][ ] Watch recorded lecture videos
+```
-Describe the action and its outcome.
+### Delete a Task - `delete`
-Example of usage:
+Deletes a Task from the TaskList.
-`keyword (optional arguments)`
+Format: `delete INDEX`
+* Use index of task from the list of tasks
-Expected outcome:
+Example Usage
-Description of the outcome.
+>`delete 1`
+Example Outcome
```
-expected output
+ Understood. I have removed the following task:
+ [T][ ] Watch recorded lecture videos
+ You have a total of 1 task(s) left.
```
+
+### Empty the TaskList - `empty`
+
+Deletes all Tasks from the TaskList.
+
+Format: `empty`
+
+Example Usage
+
+>`empty`
+
+Example Outcome
+```
+ Understood. I have emptied your list of tasks.
+```
+
+### Exit Ren - `bye`
+
+Exits and closes the Ren program.
+
+Format: `bye`
diff --git a/docs/Ui.png b/docs/Ui.png
new file mode 100644
index 0000000000..639e1e1da2
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..f3d88b1c2f
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000000..b7c8c5dbf5
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.2-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
new file mode 100755
index 0000000000..2fe81a7d95
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,183 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=`expr $i + 1`
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000000..62bd9b9cce
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,103 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java
deleted file mode 100644
index 5d313334cc..0000000000
--- a/src/main/java/Duke.java
+++ /dev/null
@@ -1,10 +0,0 @@
-public class Duke {
- public static void main(String[] args) {
- String logo = " ____ _ \n"
- + "| _ \\ _ _| | _____ \n"
- + "| | | | | | | |/ / _ \\\n"
- + "| |_| | |_| | < __/\n"
- + "|____/ \\__,_|_|\\_\\___|\n";
- System.out.println("Hello from\n" + logo);
- }
-}
diff --git a/src/main/java/ren/DialogBox.java b/src/main/java/ren/DialogBox.java
new file mode 100644
index 0000000000..09c32cd08b
--- /dev/null
+++ b/src/main/java/ren/DialogBox.java
@@ -0,0 +1,90 @@
+package ren;
+
+import java.io.IOException;
+import java.util.Collections;
+
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import javafx.fxml.FXML;
+import javafx.fxml.FXMLLoader;
+import javafx.geometry.Pos;
+import javafx.scene.Node;
+import javafx.scene.control.Label;
+import javafx.scene.effect.Reflection;
+import javafx.scene.image.Image;
+import javafx.scene.image.ImageView;
+import javafx.scene.layout.HBox;
+import javafx.scene.shape.Rectangle;
+
+/**
+ * An example of a custom control using FXML.
+ * This control represents a dialog box consisting of an ImageView to represent the speaker's face and a label
+ * containing text from the speaker.
+ */
+public class DialogBox extends HBox {
+ @FXML
+ private Label dialog;
+
+ @FXML
+ private ImageView displayPicture;
+
+ /**
+ * Constructor for a DialogBox.
+ *
+ * @param text The text of the DialogBox.
+ * @param img The image of the DialogBox.
+ */
+ private DialogBox(String text, Image img) {
+ try {
+ FXMLLoader fxmlLoader = new FXMLLoader(MainWindow.class.getResource("/view/DialogBox.fxml"));
+ fxmlLoader.setController(this);
+ fxmlLoader.setRoot(this);
+ fxmlLoader.load();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ dialog.setText(text);
+ displayPicture.setImage(img);
+
+ Rectangle rect = new Rectangle(99, 99);
+ rect.setArcHeight(30);
+ rect.setArcWidth(30);
+ rect.setEffect(new Reflection());
+ displayPicture.setClip(rect);
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * Factory method for a User DialogBox.
+ *
+ * @param text String containing command from User.
+ * @param img Image representing User.
+ * @return DialogBox.
+ */
+ public static DialogBox getUserDialog(String text, Image img) {
+ return new DialogBox(text, img);
+ }
+
+ /**
+ * Factory method for a Ren DialogBox.
+ *
+ * @param text String containing message from Ren.
+ * @param img Image representing Ren.
+ * @return DialogBox.
+ */
+ public static DialogBox getRenDialog(String text, Image img) {
+ var db = new DialogBox(text, img);
+ db.flip();
+ return db;
+ }
+}
diff --git a/src/main/java/ren/Launcher.java b/src/main/java/ren/Launcher.java
new file mode 100644
index 0000000000..97deeb081e
--- /dev/null
+++ b/src/main/java/ren/Launcher.java
@@ -0,0 +1,12 @@
+package ren;
+
+import javafx.application.Application;
+
+/**
+ * A launcher class to workaround classpath issues.
+ */
+public class Launcher {
+ public static void main(String[] args) {
+ Application.launch(Main.class, args);
+ }
+}
diff --git a/src/main/java/ren/Main.java b/src/main/java/ren/Main.java
new file mode 100644
index 0000000000..a390286774
--- /dev/null
+++ b/src/main/java/ren/Main.java
@@ -0,0 +1,33 @@
+package ren;
+
+import java.io.IOException;
+
+import javafx.application.Application;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Scene;
+import javafx.scene.layout.AnchorPane;
+import javafx.stage.Stage;
+
+/**
+ * A GUI for Duke using FXML.
+ */
+public class Main extends Application {
+ /** The Ren bot for this instance of the program. */
+ private final Ren ren = new Ren();
+
+ @Override
+ public void start(Stage stage) {
+ try {
+ FXMLLoader fxmlLoader = new FXMLLoader(Main.class.getResource("/view/MainWindow.fxml"));
+ AnchorPane ap = fxmlLoader.load();
+ Scene scene = new Scene(ap);
+ stage.setScene(scene);
+ fxmlLoader.getController().setRen(ren);
+ stage.setResizable(false);
+ stage.setTitle("Ren");
+ stage.show();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/src/main/java/ren/MainWindow.java b/src/main/java/ren/MainWindow.java
new file mode 100644
index 0000000000..853a4c24ed
--- /dev/null
+++ b/src/main/java/ren/MainWindow.java
@@ -0,0 +1,90 @@
+package ren;
+
+import java.io.InputStream;
+
+import javafx.application.Platform;
+import javafx.fxml.FXML;
+import javafx.scene.control.Button;
+import javafx.scene.control.ScrollPane;
+import javafx.scene.control.TextField;
+import javafx.scene.image.Image;
+import javafx.scene.layout.AnchorPane;
+import javafx.scene.layout.VBox;
+/**
+ * Controller for MainWindow. Provides the layout for the other controls.
+ */
+public class MainWindow extends AnchorPane {
+ @FXML
+ private ScrollPane scrollPane;
+ @FXML
+ private VBox dialogContainer;
+ @FXML
+ private TextField userInput;
+ @FXML
+ private Button sendButton;
+
+ /** The instance of Ren for this program. */
+ private Ren ren;
+
+ /** Profile picture for the user. */
+ private Image userImage;
+
+ /** Profile picture for Ren. */
+ private Image renImage;
+
+ /**
+ * Initializes the GUI. Greets the user.
+ */
+ @FXML
+ public void initialize() {
+ // Credits for Image
+ // https://pixabay.com/vectors/blank-profile-picture-mystery-man-973460/
+ InputStream userImageLoader = this.getClass().getResourceAsStream("/images/user.png");
+ // Credits for Image
+ // https://www.freepik.com/free-photo/robot-with-clipboard_958202.htm#query=robot&position=9&from_view=author
+ InputStream renImageLoader = this.getClass().getResourceAsStream("/images/ren.png");
+ assert userImageLoader != null : "userImage in MainWindow should not be null";
+ assert renImageLoader != null : "renImage in MainWindow should not be null";
+
+ userImage = new Image(userImageLoader);
+ renImage = new Image(renImageLoader);
+
+ String greetings = " Greetings! My name is Ren ^_^\n How may I be of service today?\n";
+ scrollPane.vvalueProperty().bind(dialogContainer.heightProperty());
+ dialogContainer.getChildren().addAll(
+ DialogBox.getRenDialog(greetings, renImage)
+ );
+ }
+
+ /**
+ * Setter method for ren.
+ *
+ * @param ren Instance of Ren.
+ */
+ public void setRen(Ren ren) {
+ this.ren = ren;
+ }
+
+ /**
+ * Creates two dialog boxes, one echoing user input and the other containing Ren's reply and then appends them to
+ * the dialog container. Clears the user input after processing. Exits the program if bye command entered.
+ */
+ @FXML
+ private void handleUserInput() {
+ String input = userInput.getText().trim();
+ String response;
+ try {
+ response = ren.interpret(input);
+ } catch (RenException e) {
+ response = e.toString();
+ }
+ dialogContainer.getChildren().addAll(
+ DialogBox.getUserDialog(input, userImage),
+ DialogBox.getRenDialog(response, renImage)
+ );
+ userInput.clear();
+ if (input.equals("bye")) {
+ Platform.exit();
+ }
+ }
+}
diff --git a/src/main/java/ren/Parser.java b/src/main/java/ren/Parser.java
new file mode 100644
index 0000000000..cd729ff54e
--- /dev/null
+++ b/src/main/java/ren/Parser.java
@@ -0,0 +1,161 @@
+package ren;
+
+/**
+ * Parser interprets user commands and executes them.
+ */
+public class Parser {
+ private final TaskList tasks;
+
+ /**
+ * Constructor for Parser.
+ *
+ * @param tasks The TaskList to execute commands on.
+ */
+ public Parser(TaskList tasks) {
+ this.tasks = tasks;
+ }
+
+ /**
+ * Interprets user commands and executes them.
+ *
+ * @param cmd Command to execute.
+ * @return String containing a message for the user.
+ * @throws RenException If command is invalid.
+ */
+ public String parseCommand(String cmd) throws RenException {
+ String[] firstParse = cmd.split(" ", 2);
+ boolean hasSecondTerm = firstParse.length > 1;
+ switch (firstParse[0]) {
+ case "bye":
+ return " Farewell!\n";
+ case "todo":
+ return parseTodo(hasSecondTerm, firstParse);
+ case "deadline":
+ return parseDeadline(hasSecondTerm, firstParse);
+ case "event":
+ return parseEvent(hasSecondTerm, firstParse);
+ case "delete":
+ return parseDelete(hasSecondTerm, firstParse);
+ case "mark":
+ return parseMark(hasSecondTerm, firstParse);
+ case "unmark":
+ return parseUnmark(hasSecondTerm, firstParse);
+ case "list":
+ return tasks.listTasks();
+ case "find":
+ return parseFind(hasSecondTerm, firstParse);
+ case "empty":
+ return tasks.emptyList();
+ case "sort":
+ return parseSort(hasSecondTerm, firstParse);
+ default:
+ throw new RenException("Please enter a supported command.");
+ }
+ }
+
+ private String parseTodo(boolean hasSecondTerm, String[] firstParse) throws RenException {
+ if (hasSecondTerm) {
+ return tasks.addTask(Ren.TaskType.TODO, firstParse[1], "");
+ } else {
+ throw new RenException("Please provide a description for the todo.");
+ }
+ }
+
+ private String parseDeadline(boolean hasSecondTerm, String[] firstParse) throws RenException {
+ if (hasSecondTerm) {
+ String[] secondParse = firstParse[1].split("/by", 2);
+ if (secondParse.length > 1) {
+ return tasks.addTask(Ren.TaskType.DEADLINE, secondParse[0], secondParse[1]);
+ } else {
+ throw new RenException("Please provide a date/time for the deadline.");
+ }
+ } else {
+ throw new RenException("Please provide a description for the deadline.");
+ }
+ }
+
+ private String parseEvent(boolean hasSecondTerm, String[] firstParse) throws RenException {
+ if (hasSecondTerm) {
+ String[] secondParse = firstParse[1].split("/at", 2);
+ if (secondParse.length > 1) {
+ return tasks.addTask(Ren.TaskType.EVENT, secondParse[0], secondParse[1]);
+ } else {
+ throw new RenException("Please provide a date/time for the event.");
+ }
+ } else {
+ throw new RenException("Please provide a description for the event.");
+ }
+ }
+
+ private String parseDelete(boolean hasSecondTerm, String[] firstParse) throws RenException {
+ try {
+ String secondTerm = hasSecondTerm
+ ? firstParse[1].split(" ", 2)[0]
+ : "0";
+ return tasks.deleteTask(Integer.parseInt(secondTerm));
+ } catch (NumberFormatException e) {
+ // If the second term parsed isn't an integer
+ throw new RenException("Please indicate the task no. in digits.");
+ } catch (RenException f) {
+ // The user entered an invalid number
+ return f.toString();
+ }
+ }
+
+ private String parseMark(boolean hasSecondTerm, String[] firstParse) throws RenException {
+ try {
+ String secondTerm = hasSecondTerm
+ ? firstParse[1].split(" ", 2)[0]
+ : "0";
+ return tasks.updateTask(true, Integer.parseInt(secondTerm));
+ } catch (NumberFormatException e) {
+ // If the second term parsed isn't an integer
+ throw new RenException("Please indicate the task no. in digits.");
+ } catch (RenException f) {
+ // The user entered an invalid number
+ return f.toString();
+ }
+ }
+
+ private String parseUnmark(boolean hasSecondTerm, String[] firstParse) throws RenException {
+ try {
+ String secondTerm = hasSecondTerm
+ ? firstParse[1].split(" ", 2)[0]
+ : "0";
+ return tasks.updateTask(false, Integer.parseInt(secondTerm));
+ } catch (NumberFormatException e) {
+ // If the second term parsed isn't an integer
+ throw new RenException("Please indicate the task no. in digits.");
+ } catch (RenException f) {
+ // The user entered an invalid number
+ return f.toString();
+ }
+ }
+
+ private String parseFind(boolean hasSecondTerm, String[] firstParse) throws RenException {
+ if (hasSecondTerm) {
+ return tasks.findTasks(firstParse[1]);
+ } else {
+ throw new RenException("Please provide a search term.");
+ }
+ }
+
+ private String parseSort(boolean hasSecondTerm, String[] firstParse) throws RenException {
+ if (!hasSecondTerm) {
+ throw new RenException("Please specify how you want the list to be sorted.");
+ }
+ String secondTerm = firstParse[1].split(" ", 2)[0];
+ switch (secondTerm) {
+ case "type":
+ return tasks.sortTasks(TaskList.SortType.TYPE);
+ case "status":
+ return tasks.sortTasks(TaskList.SortType.STATUS);
+ case "description":
+ return tasks.sortTasks(TaskList.SortType.DESCRIPTION);
+ case "date":
+ return tasks.sortTasks(TaskList.SortType.DATE);
+ default:
+ throw new RenException("Please specify how you want the list to be sorted.");
+ }
+ }
+}
diff --git a/src/main/java/ren/Ren.java b/src/main/java/ren/Ren.java
new file mode 100644
index 0000000000..00b54a17e2
--- /dev/null
+++ b/src/main/java/ren/Ren.java
@@ -0,0 +1,38 @@
+package ren;
+
+/**
+ * Ren is a Task Manager program that helps a user keep track of and manage their tasks.
+ */
+public class Ren {
+ /** Parser helps the Ren bot to interpret commands from the user. */
+ private final Parser parser;
+
+ /**
+ * Constructor for a Ren bot.
+ */
+ public Ren() {
+ Storage storage = new Storage("data/list.txt");
+ TaskList tasks = new TaskList(storage);
+ parser = new Parser(tasks);
+ }
+
+ /**
+ * Interprets commands from the user.
+ *
+ * @param input The command from the user.
+ * @return String containing message from Ren after attempting to execute the command.
+ * @throws RenException If the execution failed.
+ */
+ public String interpret(String input) throws RenException {
+ return parser.parseCommand(input);
+ }
+
+ /**
+ * The types of Tasks supported by Ren.
+ */
+ enum TaskType {
+ TODO,
+ DEADLINE,
+ EVENT
+ }
+}
diff --git a/src/main/java/ren/RenException.java b/src/main/java/ren/RenException.java
new file mode 100644
index 0000000000..867c95e285
--- /dev/null
+++ b/src/main/java/ren/RenException.java
@@ -0,0 +1,25 @@
+package ren;
+
+/**
+ * RenExceptions represent errors in user input when using Ren.
+ */
+public class RenException extends Exception {
+ /**
+ * Constructor for a RenException.
+ *
+ * @param message Error Message.
+ */
+ public RenException(String message) {
+ super(message);
+ }
+
+ /**
+ * Returns the string representation of this RenException.
+ *
+ * @return String Representation.
+ */
+ @Override
+ public String toString() {
+ return " >_< Apologies! " + super.getMessage() + "\n";
+ }
+}
diff --git a/src/main/java/ren/Storage.java b/src/main/java/ren/Storage.java
new file mode 100644
index 0000000000..6f85fee3c0
--- /dev/null
+++ b/src/main/java/ren/Storage.java
@@ -0,0 +1,203 @@
+package ren;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Scanner;
+
+import ren.task.Deadline;
+import ren.task.Event;
+import ren.task.Task;
+import ren.task.Todo;
+
+/**
+ * Storage handles the reading and writing of Tasks to a File.
+ */
+public class Storage {
+ /** Path of the File. */
+ private final String filePath;
+
+ /** File that will store the list of Tasks. */
+ private final File dataFile;
+
+ /** Array that stores each line of the File as an element. */
+ private final ArrayList dataArray = new ArrayList<>();
+
+ /** Counter for number of lines in the File. */
+ private int counter = 0;
+
+ /** Indicator for whether an error has occurred in the File. */
+ private boolean hasError = false;
+
+ /**
+ * Constructor for Storage.
+ *
+ * @param filePath Path of the File to read from and write to.
+ */
+ public Storage(String filePath) {
+ this.filePath = filePath;
+ this.dataFile = new File(filePath);
+ init();
+ }
+
+ /**
+ * Initialises dataArray using dataFile and creates dataFile if it is missing.
+ */
+ private void init() {
+ if (!dataFile.exists()) {
+ try {
+ // Check if the parent directory exists
+ if (!dataFile.getParentFile().exists()) {
+ dataFile.getParentFile().mkdir();
+ }
+ dataFile.createNewFile();
+ } catch (IOException e) {
+ this.hasError = true;
+ }
+ } else {
+ try {
+ Scanner dataSource = new Scanner(dataFile);
+ while (dataSource.hasNext()) {
+ dataArray.add(dataSource.nextLine());
+ counter++;
+ }
+ dataSource.close();
+ } catch (FileNotFoundException e) {
+ this.hasError = true;
+ }
+ }
+ }
+
+ /**
+ * Resets the File to a blank File.
+ */
+ private void newFile() {
+ try {
+ dataFile.delete();
+ dataFile.createNewFile();
+ } catch (IOException e) {
+ this.hasError = true;
+ }
+ }
+
+ /**
+ * Overwrites the File with a new list of Tasks.
+ */
+ private void writeToFile() {
+ // Disable writing to File if error occurred during creation of file or reading of file.
+ if (hasError) {
+ return;
+ }
+ try {
+ FileWriter writer = new FileWriter(filePath);
+ StringBuilder contents = new StringBuilder(dataArray.get(0));
+ for (int i = 1; i < counter; i++) {
+ contents.append(System.lineSeparator());
+ contents.append(dataArray.get(i));
+ }
+ writer.write(contents.toString());
+ writer.close();
+ } catch (IOException e) {
+ this.hasError = true;
+ }
+ }
+
+ /**
+ * Appends a new Task's information to the File.
+ */
+ private void appendToFile() {
+ // Disable writing to File if error occurred during creation of file or reading of file.
+ if (hasError) {
+ return;
+ }
+ try {
+ FileWriter writer = new FileWriter(filePath, true);
+ StringBuilder contents;
+ if (counter == 1) {
+ contents = new StringBuilder(dataArray.get(0));
+ } else {
+ contents = new StringBuilder(System.lineSeparator());
+ contents.append(dataArray.get(counter - 1));
+ }
+ writer.write(contents.toString());
+ writer.close();
+ } catch (IOException e) {
+ this.hasError = true;
+ }
+ }
+
+ /**
+ * Returns a list of Tasks read from File.
+ *
+ * @return ArrayList of Tasks.
+ */
+ public ArrayList load() {
+ ArrayList tasks = new ArrayList<>();
+ for (int i = 0; i < this.counter; i++) {
+ // Split the task information into individual terms
+ String[] data = dataArray.get(i).split("\\|");
+ String taskType = data[0];
+ Task newTask = null;
+ switch (taskType) {
+ case "T":
+ newTask = Todo.readData(data);
+ break;
+ case "D":
+ newTask = Deadline.readData(data);
+ break;
+ case "E":
+ newTask = Event.readData(data);
+ break;
+ default:
+ }
+ assert newTask != null : "newTask in load in Storage should not be null";
+
+ tasks.add(newTask);
+ }
+ return tasks;
+ }
+
+ /**
+ * Writes a new Task to File.
+ *
+ * @param newTask Task to be written.
+ */
+ public void addTask(Task newTask) {
+ dataArray.add(newTask.writeData());
+ counter++;
+ appendToFile();
+ }
+
+ /**
+ * Modifies a Task in File.
+ *
+ * @param newTask Task that was modified.
+ * @param index Index of the modified Task.
+ */
+ public void updateTask(Task newTask, int index) {
+ dataArray.set(index, newTask.writeData());
+ writeToFile();
+ }
+
+ /**
+ * Removes a Task from File.
+ *
+ * @param index Index of the Task to be removed.
+ */
+ public void deleteTask(int index) {
+ dataArray.remove(index);
+ counter--;
+ writeToFile();
+ }
+
+ /**
+ * Removes all Tasks from File.
+ */
+ public void emptyList() {
+ dataArray.clear();
+ counter = 0;
+ newFile();
+ }
+}
diff --git a/src/main/java/ren/TaskList.java b/src/main/java/ren/TaskList.java
new file mode 100644
index 0000000000..dab8ff154f
--- /dev/null
+++ b/src/main/java/ren/TaskList.java
@@ -0,0 +1,195 @@
+package ren;
+
+import java.util.ArrayList;
+
+import ren.task.Deadline;
+import ren.task.Event;
+import ren.task.Task;
+import ren.task.Todo;
+
+/**
+ * TaskList contains a list of Tasks as well methods to add, delete, update tasks.
+ */
+public class TaskList {
+ /** ArrayList to store all Tasks. */
+ private final ArrayList tasks;
+
+ /** Storage to synchronize all changes with. */
+ private final Storage storage;
+
+ /**
+ * Constructor for a TaskList.
+ *
+ * @param storage Storage to read and write all Tasks to.
+ */
+ public TaskList(Storage storage) {
+ this.tasks = storage.load();
+ this.storage = storage;
+ }
+
+ /**
+ * Adds a Task to the TaskList.
+ *
+ * @param type The type of the new Task.
+ * @param task The information of the new Task.
+ * @param dateTime The date and time information of the new Task.
+ * @return String containing a message for the user.
+ * @throws RenException If task or dateTime is invalid.
+ */
+ public String addTask(Ren.TaskType type, String task, String dateTime) throws RenException {
+ Task newTask = null;
+ switch (type) {
+ case TODO:
+ newTask = new Todo(task);
+ break;
+ case DEADLINE:
+ newTask = new Deadline(task, dateTime);
+ break;
+ case EVENT:
+ newTask = new Event(task, dateTime);
+ break;
+ default:
+ }
+ assert newTask != null : "newTask in addTask in TaskList should not be null";
+
+ tasks.add(newTask);
+ storage.addTask(newTask);
+ return " Understood. I have added the following task:\n"
+ + " " + newTask
+ + " You now have a total of " + tasks.size() + " task(s).\n";
+ }
+
+ /**
+ * Removes a Task from the TaskList.
+ *
+ * @param taskNum The index of the Task to remove.
+ * @return String containing a message for the user.
+ * @throws RenException If taskNum is invalid.
+ */
+ public String deleteTask(int taskNum) throws RenException {
+ if (taskNum <= tasks.size() && taskNum > 0) {
+ Task removedTask = tasks.remove(taskNum - 1);
+ storage.deleteTask(taskNum - 1);
+ return " Understood. I have removed the following task:\n"
+ + " " + removedTask
+ + " You have a total of " + tasks.size() + " task(s) left.\n";
+ } else if (tasks.size() == 0) {
+ throw new RenException("You have no tasks to delete.");
+ } else {
+ throw new RenException("Please indicate a task no. between 1 to " + tasks.size() + ".");
+ }
+ }
+
+ /**
+ * Updates a Task in the TaskList.
+ *
+ * @param status The new status of the Task.
+ * @param taskNum The index of the Task to update.
+ * @return String containing a message for the user.
+ * @throws RenException If taskNum is invalid.
+ */
+ public String updateTask(boolean status, int taskNum) throws RenException {
+ if (taskNum <= tasks.size() && taskNum > 0) {
+ Task selectedTask = tasks.get(taskNum - 1);
+ String message = selectedTask.setDone(status);
+ storage.updateTask(selectedTask, taskNum - 1);
+ return message;
+ } else if (tasks.size() == 0) {
+ throw new RenException("You have no tasks to mark or unmark.");
+ } else {
+ throw new RenException("Please indicate a task no. between 1 to " + tasks.size() + ".");
+ }
+ }
+
+ /**
+ * Lists all Tasks in the TaskList.
+ *
+ * @return String containing the list.
+ */
+ public String listTasks() {
+ if (tasks.size() == 0) {
+ return " You have not added any tasks!\n";
+ }
+
+ StringBuilder result = new StringBuilder(" Here are your current tasks:\n");
+ for (int i = 0; i < tasks.size(); i++) {
+ result.append(" ").append(i + 1).append(". ").append(tasks.get(i).toString());
+ }
+ return result.toString();
+ }
+
+ /**
+ * Sorts the tasks according to sortType.
+ * For type, the order is Todo, Deadline, Event.
+ * For status, unmarked tasks are before marked tasks.
+ * For description, tasks are sorted lexicographically, ignoring case differences.
+ * For date, Todo are sorted last while Deadline and Event are sorted chronologically.
+ *
+ * @param sortType The order to sort the tasks by.
+ * @return String containing the sorted list of tasks.
+ */
+ public String sortTasks(SortType sortType) {
+ switch (sortType) {
+ case TYPE:
+ tasks.sort(Task::compareType);
+ break;
+ case STATUS:
+ tasks.sort(Task::compareStatus);
+ break;
+ case DESCRIPTION:
+ tasks.sort(Task::compareDescription);
+ break;
+ case DATE:
+ tasks.sort(Task::compareDate);
+ break;
+ default:
+ break;
+ }
+ storage.emptyList();
+ tasks.forEach(storage::addTask);
+ return " I have finished sorting your list of tasks!\n\n" + listTasks();
+ }
+
+ /**
+ * Searches TaskList for Tasks matching a search term.
+ *
+ * @param term The Search Term to match Tasks with.
+ * @return String containing the list of matching Tasks.
+ */
+ public String findTasks(String term) {
+ if (tasks.size() == 0) {
+ return " Apologies! I have not found any matching tasks.\n";
+ }
+
+ int index = 1;
+ StringBuilder result = new StringBuilder(" I have found these matching tasks:\n");
+ for (Task taskToCheck : tasks) {
+ if (taskToCheck.isMatch(term)) {
+ result.append(" ").append(index).append(". ").append(taskToCheck);
+ index++;
+ }
+ }
+ return index != 1 ? result.toString() : " Apologies! I have not found any matching tasks.\n";
+ }
+
+ /**
+ * Removes all Tasks from the TaskList.
+ *
+ * @return String containing a message for the user.
+ */
+ public String emptyList() {
+ tasks.clear();
+ storage.emptyList();
+ return " Understood. I have emptied your list of tasks.";
+ }
+
+ /**
+ * The types of sorting supported by Ren.
+ */
+ enum SortType {
+ TYPE,
+ STATUS,
+ DESCRIPTION,
+ DATE
+ }
+}
diff --git a/src/main/java/ren/TimeStamp.java b/src/main/java/ren/TimeStamp.java
new file mode 100644
index 0000000000..706e8616da
--- /dev/null
+++ b/src/main/java/ren/TimeStamp.java
@@ -0,0 +1,73 @@
+package ren;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+
+/**
+ * TimeStamp stores the date and time information of Deadline Tasks and Events.
+ */
+public class TimeStamp {
+ private final LocalDateTime timestamp;
+
+ private TimeStamp(LocalDateTime timestamp) {
+ this.timestamp = timestamp;
+ }
+
+ /**
+ * Factory method for TimeStamps.
+ *
+ * @param dateTime Date and Time information in the format of (dd/MM/yyyy-HH:mm).
+ * @return TimeStamp.
+ * @throws RenException If dateTime is not formatted correctly.
+ */
+ public static TimeStamp of(String dateTime) throws RenException {
+ // format of the date and time information in the input
+ DateTimeFormatter format = DateTimeFormatter.ofPattern("d/M/yyyy-H:mm");
+ try {
+ LocalDateTime timestamp = LocalDateTime.parse(dateTime.strip(), format);
+ return new TimeStamp(timestamp);
+ } catch (DateTimeParseException e) {
+ throw new RenException("Please indicate date and time properly. (20/8/2022-15:37)");
+ }
+ }
+
+ /**
+ * Factory method for TimeStamps, meant for reading from File.
+ *
+ * @param dateTime Date and Time information in the format of (dd/MM/yyyy-HH:mm).
+ * @return TimeStamp.
+ */
+ public static TimeStamp fromFile(String dateTime) {
+ // format of the date and time information in the input
+ DateTimeFormatter format = DateTimeFormatter.ofPattern("E, d MMMM yyyy h:mm a");
+ try {
+ LocalDateTime timestamp = LocalDateTime.parse(dateTime.strip(), format);
+ return new TimeStamp(timestamp);
+ } catch (DateTimeParseException e) {
+ System.out.println("fromFile failed");
+ return new TimeStamp(LocalDateTime.now());
+ }
+ }
+
+ /**
+ * Compares this TimeStamp to another TimeStamp.
+ *
+ * @param other The other TimeStamp to compare with.
+ * @return -1 if this TimeStamp is earlier. 1 if the other TimeStamp is earlier. 0 if both are the same.
+ */
+ public int compareTo(TimeStamp other) {
+ return this.timestamp.compareTo(other.timestamp);
+ }
+
+ /**
+ * Returns String Representation of a TimeStamp.
+ *
+ * @return String Representation.
+ */
+ @Override
+ public String toString() {
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("E, d MMMM yyyy h:mm a");
+ return " " + timestamp.format(formatter);
+ }
+}
diff --git a/src/main/java/ren/task/Deadline.java b/src/main/java/ren/task/Deadline.java
new file mode 100644
index 0000000000..360cac361c
--- /dev/null
+++ b/src/main/java/ren/task/Deadline.java
@@ -0,0 +1,100 @@
+package ren.task;
+
+import ren.RenException;
+import ren.TimeStamp;
+
+/**
+ * Deadline Task represents an action that needs to be done by a certain date or time.
+ */
+public class Deadline extends Task {
+ protected TimeStamp dateTime;
+
+ /**
+ * Constructor for a Deadline Task.
+ *
+ * @param description the description of the Task
+ * @param dateTime the deadline of the Task
+ */
+ public Deadline(String description, String dateTime) throws RenException {
+ super(description);
+ this.dateTime = TimeStamp.of(dateTime);
+ }
+
+ private Deadline(String description, TimeStamp dateTime) {
+ super(description);
+ this.dateTime = dateTime;
+ }
+
+ /**
+ * Returns a Deadline Task constructed with data read from File.
+ *
+ * @param data Data of a Deadline Task.
+ * @return Deadline Task or null if data is corrupted.
+ */
+ public static Deadline readData(String[] data) {
+ // Check if data is complete
+ assert data.length == 4 : "data[] in readData in Deadline should be of size 4";
+
+ Deadline newDeadline = new Deadline(data[2], TimeStamp.fromFile(data[3]));
+ if (data[1].equals("X")) {
+ newDeadline.setDone(true);
+ }
+ return newDeadline;
+ }
+
+ /**
+ * Returns the Deadline Task information for writing to a File.
+ *
+ * @return String with Deadline Task information.
+ */
+ @Override
+ public String writeData() {
+ String symbol = this.isDone ? "X" : " ";
+ return "D|" + symbol + "|" + this.description + "|" + this.dateTime.toString();
+ }
+
+ /**
+ * Compares this Deadline to another Task by their type of task.
+ *
+ * @param other The task to compare with.
+ * @return -1 if other is an Event, 1 if the other task is a Todo, 0 if the other task is a Deadline.
+ */
+ @Override
+ public int compareType(Task other) {
+ if (other instanceof Todo) {
+ return 1;
+ } else if (other instanceof Deadline) {
+ return 0;
+ }
+ return -1;
+ }
+
+ /**
+ * Compares this Deadline to another Task by their date.
+ *
+ * @param other The task to compare with.
+ * @return -1 if this task should be sorted first, 1 if the other task should be sorted first, 0 otherwise.
+ */
+ @Override
+ public int compareDate(Task other) {
+ if (other instanceof Todo) {
+ return -1;
+ } else if (other instanceof Deadline) {
+ return this.dateTime.compareTo(((Deadline) other).dateTime);
+ } else if (other instanceof Event) {
+ return this.dateTime.compareTo(((Event) other).start);
+ }
+ return 0;
+ }
+
+ /**
+ * Returns the string representation of this Deadline Task.
+ *
+ * @return String Representation.
+ */
+ @Override
+ public String toString() {
+ String symbol = this.isDone ? "X" : " ";
+ return "[D][" + symbol + "] " + this.description + "(by:" + this.dateTime + ")\n";
+ }
+}
diff --git a/src/main/java/ren/task/Event.java b/src/main/java/ren/task/Event.java
new file mode 100644
index 0000000000..029cfcfdd5
--- /dev/null
+++ b/src/main/java/ren/task/Event.java
@@ -0,0 +1,104 @@
+package ren.task;
+
+import ren.RenException;
+import ren.TimeStamp;
+
+/**
+ * Event Task represents an event that takes place between certain dates/time.
+ */
+public class Event extends Task {
+ protected TimeStamp start;
+ protected TimeStamp end;
+
+ /**
+ * Constructor for an Event Task.
+ *
+ * @param description the description of the Event
+ * @param dateTime the start and end dates/time of the Event
+ */
+ public Event(String description, String dateTime) throws RenException {
+ super(description);
+ String[] duration = dateTime.split(" ~ ", 2);
+ this.start = TimeStamp.of(duration[0]);
+ this.end = TimeStamp.of(duration[1]);
+ }
+
+ private Event(String description, TimeStamp start, TimeStamp end) {
+ super(description);
+ this.start = start;
+ this.end = end;
+ }
+
+ /**
+ * Returns an Event constructed with data read from File.
+ *
+ * @param data Data of an Event.
+ * @return Event or null if data is corrupted.
+ */
+ public static Event readData(String[] data) {
+ // Check if data is complete
+ assert data.length == 5 : "data[] in readData in Event should be of size 5";
+
+ Event newEvent = new Event(data[2], TimeStamp.fromFile(data[3]), TimeStamp.fromFile(data[4]));
+ if (data[1].equals("X")) {
+ newEvent.setDone(true);
+ }
+ return newEvent;
+ }
+
+ /**
+ * Returns the Event information for writing to a File.
+ *
+ * @return String with Event information.
+ */
+ @Override
+ public String writeData() {
+ String symbol = this.isDone ? "X" : " ";
+ return "E|" + symbol + "|" + this.description + "|" + this.start + "|" + this.end;
+ }
+
+ /**
+ * Compares this Event to another Task by their type of task.
+ *
+ * @param other The task to compare with.
+ * @return 1 if the other task is not an Event, 0 if the other task is an Event.
+ */
+ @Override
+ public int compareType(Task other) {
+ if (other instanceof Todo) {
+ return 1;
+ } else if (other instanceof Deadline) {
+ return 1;
+ }
+ return 0;
+ }
+
+ /**
+ * Compares this Event to another Task by their date.
+ *
+ * @param other The task to compare with.
+ * @return -1 if this task should be sorted first, 1 if the other task should be sorted first, 0 otherwise.
+ */
+ @Override
+ public int compareDate(Task other) {
+ if (other instanceof Todo) {
+ return -1;
+ } else if (other instanceof Deadline) {
+ return this.start.compareTo(((Deadline) other).dateTime);
+ } else if (other instanceof Event) {
+ return this.start.compareTo(((Event) other).start);
+ }
+ return 0;
+ }
+
+ /**
+ * Returns the string representation of this Event.
+ *
+ * @return String Representation.
+ */
+ @Override
+ public String toString() {
+ String symbol = this.isDone ? "X" : " ";
+ return "[E][" + symbol + "] " + this.description + "(at:" + this.start + " -" + this.end + ")\n";
+ }
+}
diff --git a/src/main/java/ren/task/Task.java b/src/main/java/ren/task/Task.java
new file mode 100644
index 0000000000..672544e94f
--- /dev/null
+++ b/src/main/java/ren/task/Task.java
@@ -0,0 +1,91 @@
+package ren.task;
+
+/**
+ * Parent Class for all Tasks.
+ */
+public abstract class Task {
+ protected String description;
+ protected boolean isDone;
+
+ /**
+ * Constructor for a Task.
+ *
+ * @param description Description of the Task.
+ */
+ public Task(String description) {
+ this.description = description;
+ this.isDone = false;
+ }
+
+ /**
+ * Returns the Task information for writing to a File.
+ *
+ * @return String with Task information.
+ */
+ public abstract String writeData();
+
+ /**
+ * Compares this Task to another Task by their type of task.
+ *
+ * @param other The task to compare with.
+ * @return -1 if this task should be sorted first, 1 if the other task should be sorted first, 0 otherwise.
+ */
+ public abstract int compareType(Task other);
+
+ /**
+ * Compares this Task to another Task by their date.
+ *
+ * @param other The task to compare with.
+ * @return -1 if this task should be sorted first, 1 if the other task should be sorted first, 0 otherwise.
+ */
+ public abstract int compareDate(Task other);
+
+ /**
+ * Compares this Task to another Task by their description.
+ *
+ * @param other The task to compare with.
+ * @return -1 if this task should be sorted first, 1 if the other task should be sorted first, 0 otherwise.
+ */
+ public int compareDescription(Task other) {
+ return this.description.compareToIgnoreCase(other.description);
+ }
+
+ /**
+ * Compares this Task to another Task by their status.
+ *
+ * @param other The task to compare with.
+ * @return -1 if this task should be sorted first, 1 if the other task should be sorted first, 0 otherwise.
+ */
+ public int compareStatus(Task other) {
+ if (!this.isDone && other.isDone) {
+ return -1;
+ } else if (this.isDone && !other.isDone) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * Sets the completion status of this task.
+ *
+ * @param isDone New completion status of the task.
+ * @return String with message for user.
+ */
+ public String setDone(boolean isDone) {
+ this.isDone = isDone;
+ return isDone
+ ? " Great job! I will mark the task as completed.\n" + " " + this
+ : " Understood. I will mark the task as uncompleted.\n" + " " + this;
+ }
+
+ /**
+ * Checks if the description of this task contains a search term.
+ *
+ * @param term The Search Term.
+ * @return true if it contains the search term, false otherwise.
+ */
+ public boolean isMatch(String term) {
+ return this.description.contains(term);
+ }
+}
diff --git a/src/main/java/ren/task/Todo.java b/src/main/java/ren/task/Todo.java
new file mode 100644
index 0000000000..304b89e2f9
--- /dev/null
+++ b/src/main/java/ren/task/Todo.java
@@ -0,0 +1,76 @@
+package ren.task;
+
+/**
+ * Todo Task represents an action that needs to be done.
+ */
+public class Todo extends Task {
+ /**
+ * Constructor for a Todo Task.
+ *
+ * @param description Description of the Task.
+ */
+ public Todo(String description) {
+ super(description);
+ }
+
+ /**
+ * Returns a Todo Task constructed with data read from File.
+ *
+ * @param data Data of a Todo Task.
+ * @return Todo Task or null if data is corrupted.
+ */
+ public static Todo readData(String[] data) {
+ // Check if data is complete
+ assert data.length == 3 : "data[] in readData in Todo should be of size 3";
+
+ Todo newTodo = new Todo(data[2]);
+ if (data[1].equals("X")) {
+ newTodo.setDone(true);
+ }
+ return newTodo;
+ }
+
+ /**
+ * Returns the Todo Task information for writing to a File.
+ *
+ * @return String with Todo Task information.
+ */
+ @Override
+ public String writeData() {
+ String symbol = this.isDone ? "X" : " ";
+ return "T|" + symbol + "|" + this.description;
+ }
+
+ /**
+ * Compares this Todo to another Task by their type of task.
+ *
+ * @param other The task to compare with.
+ * @return -1 if the other task is not a Todo, 0 otherwise.
+ */
+ @Override
+ public int compareType(Task other) {
+ return (other instanceof Todo) ? 0 : -1;
+ }
+
+ /**
+ * Compares this Todo to another Task by their date.
+ *
+ * @param other The task to compare with.
+ * @return 1 if the other task is not a Todo, 0 otherwise.
+ */
+ @Override
+ public int compareDate(Task other) {
+ return (other instanceof Todo) ? 0 : 1;
+ }
+
+ /**
+ * Returns the string representation of this Todo Task.
+ *
+ * @return String Representation.
+ */
+ @Override
+ public String toString() {
+ String symbol = this.isDone ? "X" : " ";
+ return "[T][" + symbol + "] " + this.description + "\n";
+ }
+}
diff --git a/src/main/resources/images/ren.png b/src/main/resources/images/ren.png
new file mode 100644
index 0000000000..1ffcc4df13
Binary files /dev/null and b/src/main/resources/images/ren.png differ
diff --git a/src/main/resources/images/user.png b/src/main/resources/images/user.png
new file mode 100644
index 0000000000..09a7f13418
Binary files /dev/null and b/src/main/resources/images/user.png differ
diff --git a/src/main/resources/view/DialogBox.fxml b/src/main/resources/view/DialogBox.fxml
new file mode 100644
index 0000000000..babfb938ac
--- /dev/null
+++ b/src/main/resources/view/DialogBox.fxml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml
new file mode 100644
index 0000000000..c058f2c766
--- /dev/null
+++ b/src/main/resources/view/MainWindow.fxml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/view/style.css b/src/main/resources/view/style.css
new file mode 100644
index 0000000000..f3297709cf
--- /dev/null
+++ b/src/main/resources/view/style.css
@@ -0,0 +1,23 @@
+.root {
+ -fx-background-color: #708090;
+}
+
+.scroll-pane {
+ -fx-background-color: #708090;
+}
+
+.scroll-pane .corner {
+ -fx-background-color: #708090;
+}
+
+.scroll-pane .scroll-bar:vertical {
+ -fx-background-color: #708090;
+}
+
+.scroll-pane .scroll-bar:horizontal {
+ -fx-background-color: #708090;
+}
+
+.label .text {
+ -fx-fill: #000000;
+}
\ No newline at end of file
diff --git a/src/test/java/ren/ParserTest.java b/src/test/java/ren/ParserTest.java
new file mode 100644
index 0000000000..2f3d279127
--- /dev/null
+++ b/src/test/java/ren/ParserTest.java
@@ -0,0 +1,219 @@
+package ren;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import org.junit.jupiter.api.Test;
+
+public class ParserTest {
+ @Test
+ public void parseCommand_bye_success() {
+ try {
+ Parser parser = new Parser(new TaskListStub());
+ assertEquals(" Farewell!\n", parser.parseCommand("bye"));
+ } catch (RenException e) {
+ fail();
+ }
+ }
+
+ @Test
+ public void parseCommand_todo_success() {
+ try {
+ Parser parser = new Parser(new TaskListStub());
+ assertEquals("", parser.parseCommand("todo test"));
+ } catch (RenException e) {
+ fail();
+ }
+ }
+
+ @Test
+ public void parseCommand_todo_exceptionThrown() {
+ try {
+ Parser parser = new Parser(new TaskListStub());
+ parser.parseCommand("todo");
+ fail();
+ } catch (RenException e) {
+ assertEquals("Please provide a description for the todo.", e.getMessage());
+ }
+ }
+
+ @Test
+ public void parseCommand_deadline_success() {
+ try {
+ Parser parser = new Parser(new TaskListStub());
+ assertEquals("", parser.parseCommand("deadline test /by test"));
+ } catch (RenException e) {
+ fail();
+ }
+ }
+
+ @Test
+ public void parseCommand_deadline_exceptionThrown() {
+ try {
+ Parser parser = new Parser(new TaskListStub());
+ parser.parseCommand("deadline");
+ fail();
+ } catch (RenException e) {
+ assertEquals("Please provide a description for the deadline.", e.getMessage());
+ }
+ }
+
+ @Test
+ public void parseCommand_deadline2_exceptionThrown() {
+ try {
+ Parser parser = new Parser(new TaskListStub());
+ parser.parseCommand("deadline test");
+ fail();
+ } catch (RenException e) {
+ assertEquals("Please provide a date/time for the deadline.", e.getMessage());
+ }
+ }
+
+ @Test
+ public void parseCommand_event_success() {
+ try {
+ Parser parser = new Parser(new TaskListStub());
+ assertEquals("", parser.parseCommand("event test /at test"));
+ } catch (RenException e) {
+ fail();
+ }
+ }
+
+ @Test
+ public void parseCommand_event_exceptionThrown() {
+ try {
+ Parser parser = new Parser(new TaskListStub());
+ parser.parseCommand("event");
+ fail();
+ } catch (RenException e) {
+ assertEquals("Please provide a description for the event.", e.getMessage());
+ }
+ }
+
+ @Test
+ public void parseCommand_event2_exceptionThrown() {
+ try {
+ Parser parser = new Parser(new TaskListStub());
+ parser.parseCommand("event test");
+ fail();
+ } catch (RenException e) {
+ assertEquals("Please provide a date/time for the event.", e.getMessage());
+ }
+ }
+
+ @Test
+ public void parseCommand_delete_success() {
+ try {
+ Parser parser = new Parser(new TaskListStub());
+ assertEquals("", parser.parseCommand("delete 2"));
+ } catch (RenException e) {
+ fail();
+ }
+ }
+
+ @Test
+ public void parseCommand_delete_exceptionThrown() {
+ try {
+ Parser parser = new Parser(new TaskListStub());
+ parser.parseCommand("delete one");
+ fail();
+ } catch (RenException e) {
+ assertEquals("Please indicate the task no. in digits.", e.getMessage());
+ }
+ }
+
+ @Test
+ public void parseCommand_mark_success() {
+ try {
+ Parser parser = new Parser(new TaskListStub());
+ assertEquals("", parser.parseCommand("mark 2"));
+ } catch (RenException e) {
+ fail();
+ }
+ }
+
+ @Test
+ public void parseCommand_mark_exceptionThrown() {
+ try {
+ Parser parser = new Parser(new TaskListStub());
+ parser.parseCommand("unmark one");
+ fail();
+ } catch (RenException e) {
+ assertEquals("Please indicate the task no. in digits.", e.getMessage());
+ }
+ }
+
+ @Test
+ public void parseCommand_unmark_success() {
+ try {
+ Parser parser = new Parser(new TaskListStub());
+ assertEquals("", parser.parseCommand("unmark 2"));
+ } catch (RenException e) {
+ fail();
+ }
+ }
+
+ @Test
+ public void parseCommand_unmark_exceptionThrown() {
+ try {
+ Parser parser = new Parser(new TaskListStub());
+ parser.parseCommand("unmark one");
+ fail();
+ } catch (RenException e) {
+ assertEquals("Please indicate the task no. in digits.", e.getMessage());
+ }
+ }
+
+ @Test
+ public void parseCommand_list_success() {
+ try {
+ Parser parser = new Parser(new TaskListStub());
+ assertEquals("", parser.parseCommand("list"));
+ } catch (RenException e) {
+ fail();
+ }
+ }
+
+ @Test
+ public void parseCommand_find_success() {
+ try {
+ Parser parser = new Parser(new TaskListStub());
+ assertEquals("", parser.parseCommand("find test"));
+ } catch (RenException e) {
+ fail();
+ }
+ }
+
+ @Test
+ public void parseCommand_find_exceptionThrown() {
+ try {
+ Parser parser = new Parser(new TaskListStub());
+ parser.parseCommand("find");
+ fail();
+ } catch (RenException e) {
+ assertEquals("Please provide a search term.", e.getMessage());
+ }
+ }
+
+ @Test
+ public void parseCommand_empty_success() {
+ try {
+ Parser parser = new Parser(new TaskListStub());
+ assertEquals("", parser.parseCommand("empty"));
+ } catch (RenException e) {
+ fail();
+ }
+ }
+
+ @Test
+ public void parseCommand_blah_exceptionThrown() {
+ try {
+ Parser parser = new Parser(new TaskListStub());
+ parser.parseCommand("blah");
+ fail();
+ } catch (RenException e) {
+ assertEquals("Please enter a supported command.", e.getMessage());
+ }
+ }
+}
+
diff --git a/src/test/java/ren/RenExceptionTest.java b/src/test/java/ren/RenExceptionTest.java
new file mode 100644
index 0000000000..6f62dafe24
--- /dev/null
+++ b/src/test/java/ren/RenExceptionTest.java
@@ -0,0 +1,18 @@
+package ren;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+public class RenExceptionTest {
+ @Test
+ public void toString_message_success() {
+ String message = "Test";
+ assertEquals(" >_< Apologies! Test\n", new RenException(message).toString());
+ }
+
+ @Test
+ public void toString_empty_success() {
+ assertEquals(" >_< Apologies! \n", new RenException("").toString());
+ }
+}
diff --git a/src/test/java/ren/TaskListStub.java b/src/test/java/ren/TaskListStub.java
new file mode 100644
index 0000000000..0197944271
--- /dev/null
+++ b/src/test/java/ren/TaskListStub.java
@@ -0,0 +1,43 @@
+package ren;
+
+/**
+ * Stub for TaskList.
+ */
+public class TaskListStub extends TaskList {
+ /**
+ * Constructor for a TaskListStub.
+ */
+ public TaskListStub() {
+ super(new Storage("data/list_test.txt"));
+ }
+
+ @Override
+ public String addTask(Ren.TaskType type, String task, String dateTime) {
+ return "";
+ }
+
+ @Override
+ public String deleteTask(int taskNum) {
+ return "";
+ }
+
+ @Override
+ public String updateTask(boolean status, int taskNum) {
+ return "";
+ }
+
+ @Override
+ public String listTasks() {
+ return "";
+ }
+
+ @Override
+ public String findTasks(String term) {
+ return "";
+ }
+
+ @Override
+ public String emptyList() {
+ return "";
+ }
+}
diff --git a/src/test/java/ren/TimeStampTest.java b/src/test/java/ren/TimeStampTest.java
new file mode 100644
index 0000000000..b5ca26cf55
--- /dev/null
+++ b/src/test/java/ren/TimeStampTest.java
@@ -0,0 +1,34 @@
+package ren;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import org.junit.jupiter.api.Test;
+
+public class TimeStampTest {
+ @Test
+ public void of_dateTime_success() {
+ try {
+ TimeStamp test = TimeStamp.of("20/8/2022-22:04");
+ assertEquals(" Sat, 20 August 2022 10:04 PM", test.toString());
+ } catch (RenException e) {
+ fail();
+ }
+ }
+
+ @Test
+ public void of_invalidDateTime_exceptionThrown() {
+ try {
+ TimeStamp.of("lmao");
+ fail();
+ } catch (RenException e) {
+ assertEquals("Please indicate date and time properly. (20/8/2022-15:37)", e.getMessage());
+ }
+ }
+
+ @Test
+ public void fromFile_dateTime_success() {
+ TimeStamp test = TimeStamp.fromFile("Sat, 20 August 2022 10:04 PM");
+ assertEquals(" Sat, 20 August 2022 10:04 PM", test.toString());
+ }
+}
diff --git a/src/test/java/ren/task/DeadlineTest.java b/src/test/java/ren/task/DeadlineTest.java
new file mode 100644
index 0000000000..a71cd56b57
--- /dev/null
+++ b/src/test/java/ren/task/DeadlineTest.java
@@ -0,0 +1,37 @@
+package ren.task;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import org.junit.jupiter.api.Test;
+
+import ren.RenException;
+
+public class DeadlineTest {
+ @Test
+ public void toString_description_success() {
+ try {
+ Deadline test = new Deadline("test", "20/8/2022-11:11");
+ assertEquals("[D][ ] test(by: Sat, 20 August 2022 11:11 AM)\n", test.toString());
+ } catch (RenException e) {
+ fail();
+ }
+ }
+
+ @Test
+ public void writeData_description_success() {
+ try {
+ Deadline test = new Deadline("test", "20/8/2022-11:11");
+ assertEquals("D| |test| Sat, 20 August 2022 11:11 AM", test.writeData());
+ } catch (RenException e) {
+ fail();
+ }
+ }
+
+ @Test
+ public void readData_description_success() {
+ Deadline test = Deadline.readData(new String[] {"D", " ", "test", "Sat, 20 August 2022 11:11 AM"});
+ assert test != null;
+ assertEquals("D| |test| Sat, 20 August 2022 11:11 AM", test.writeData());
+ }
+}
diff --git a/src/test/java/ren/task/EventTest.java b/src/test/java/ren/task/EventTest.java
new file mode 100644
index 0000000000..985f710603
--- /dev/null
+++ b/src/test/java/ren/task/EventTest.java
@@ -0,0 +1,41 @@
+package ren.task;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import org.junit.jupiter.api.Test;
+
+import ren.RenException;
+
+public class EventTest {
+ @Test
+ public void toString_description_success() {
+ try {
+ Event test = new Event("test", "20/8/2022-11:11 ~ 20/8/2022-11:11");
+ String expected = "[E][ ] test(at: Sat, 20 August 2022 11:11 AM - Sat, 20 August 2022 11:11 AM)\n";
+ assertEquals(expected, test.toString());
+ } catch (RenException e) {
+ fail();
+ }
+ }
+
+ @Test
+ public void writeData_description_success() {
+ try {
+ Event test = new Event("test", "20/8/2022-11:11 ~ 20/8/2022-11:11");
+ String expected = "E| |test| Sat, 20 August 2022 11:11 AM| Sat, 20 August 2022 11:11 AM";
+ assertEquals(expected, test.writeData());
+ } catch (RenException e) {
+ fail();
+ }
+ }
+
+ @Test
+ public void readData_description_success() {
+ String[] input = {"E", " ", "test", "Sat, 20 August 2022 11:11 AM", "Sat, 20 August 2022 11:11 AM"};
+ Event test = Event.readData(input);
+ assert test != null;
+ String expected = "E| |test| Sat, 20 August 2022 11:11 AM| Sat, 20 August 2022 11:11 AM";
+ assertEquals(expected, test.writeData());
+ }
+}
diff --git a/src/test/java/ren/task/TodoTest.java b/src/test/java/ren/task/TodoTest.java
new file mode 100644
index 0000000000..4f2ded8c15
--- /dev/null
+++ b/src/test/java/ren/task/TodoTest.java
@@ -0,0 +1,24 @@
+package ren.task;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+public class TodoTest {
+ @Test
+ public void toString_description_success() {
+ assertEquals("[T][ ] test\n", new Todo("test").toString());
+ }
+
+ @Test
+ public void writeData_description_success() {
+ assertEquals("T| |test", new Todo("test").writeData());
+ }
+
+ @Test
+ public void readData_description_success() {
+ Todo test = Todo.readData(new String[] {"T", " ", "test"});
+ assert test != null;
+ assertEquals("T| |test", test.writeData());
+ }
+}
diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT
index 657e74f6e7..736be26b02 100644
--- a/text-ui-test/EXPECTED.TXT
+++ b/text-ui-test/EXPECTED.TXT
@@ -1,7 +1,269 @@
-Hello from
- ____ _
-| _ \ _ _| | _____
-| | | | | | | |/ / _ \
-| |_| | |_| | < __/
-|____/ \__,_|_|\_\___|
+=======================================================================================
+ Greetings! My name is Ren ^_^
+ How may I be of service today?
+
+=======================================================================================
+=======================================================================================
+
+ Here are your current tasks:
+ 1.[T][ ] todoTest
+ 2.[D][ ] deadlineTest (by: Sat, 8 January 2000 12:00 PM)
+ 3.[E][ ] eventTest (at: Sat, 8 January 2000 12:00 PM - Sat, 20 August 2022 5:59 PM)
+
+=======================================================================================
+=======================================================================================
+
+ Understood. I have emptied your list of tasks.
+=======================================================================================
+=======================================================================================
+
+ You have not added any tasks!
+
+=======================================================================================
+=======================================================================================
+
+ Understood. I have emptied your list of tasks.
+=======================================================================================
+=======================================================================================
+
+ You have not added any tasks!
+
+=======================================================================================
+=======================================================================================
+
+ ☹ Apologies! You have no tasks to mark or unmark.
+
+=======================================================================================
+=======================================================================================
+
+ ☹ Apologies! You have no tasks to mark or unmark.
+
+=======================================================================================
+=======================================================================================
+
+ ☹ Apologies! You have no tasks to delete.
+
+=======================================================================================
+=======================================================================================
+
+ ☹ Apologies! Please provide a description for the todo.
+
+=======================================================================================
+=======================================================================================
+
+ Understood. I have added the following task:
+ [T][ ] todoTest
+ You now have a total of 1 task(s).
+
+=======================================================================================
+=======================================================================================
+
+ ☹ Apologies! Please provide a description for the deadline.
+
+=======================================================================================
+=======================================================================================
+
+ ☹ Apologies! Please provide a date/time for the deadline.
+
+=======================================================================================
+=======================================================================================
+
+ ☹ Apologies! Please indicate date and time properly. (20/8/2022-15:37)
+
+=======================================================================================
+=======================================================================================
+
+ Understood. I have added the following task:
+ [D][ ] deadlineTest (by: Sat, 8 January 2000 12:00 PM)
+ You now have a total of 2 task(s).
+
+=======================================================================================
+=======================================================================================
+
+ ☹ Apologies! Please provide a description for the event.
+
+=======================================================================================
+=======================================================================================
+
+ ☹ Apologies! Please provide a date/time for the event.
+
+=======================================================================================
+=======================================================================================
+
+ ☹ Apologies! Please indicate date and time properly. (20/8/2022-15:37)
+
+=======================================================================================
+=======================================================================================
+
+ Understood. I have added the following task:
+ [E][ ] eventTest (at: Sat, 8 January 2000 12:00 PM - Sat, 20 August 2022 5:59 PM)
+ You now have a total of 3 task(s).
+
+=======================================================================================
+=======================================================================================
+
+ Here are your current tasks:
+ 1.[T][ ] todoTest
+ 2.[D][ ] deadlineTest (by: Sat, 8 January 2000 12:00 PM)
+ 3.[E][ ] eventTest (at: Sat, 8 January 2000 12:00 PM - Sat, 20 August 2022 5:59 PM)
+
+=======================================================================================
+=======================================================================================
+
+ ☹ Apologies! Please indicate a task no. between 1 to 3.
+
+=======================================================================================
+=======================================================================================
+
+ ☹ Apologies! Please indicate a task no. between 1 to 3.
+
+=======================================================================================
+=======================================================================================
+
+ ☹ Apologies! Please indicate a task no. between 1 to 3.
+
+=======================================================================================
+=======================================================================================
+
+ ☹ Apologies! Please indicate the task no. in digits.
+
+=======================================================================================
+=======================================================================================
+
+ Great job! I will mark the task as completed.
+ [D][X] deadlineTest (by: Sat, 8 January 2000 12:00 PM)
+
+=======================================================================================
+=======================================================================================
+
+ Great job! I will mark the task as completed.
+ [E][X] eventTest (at: Sat, 8 January 2000 12:00 PM - Sat, 20 August 2022 5:59 PM)
+
+=======================================================================================
+=======================================================================================
+
+ Great job! I will mark the task as completed.
+ [D][X] deadlineTest (by: Sat, 8 January 2000 12:00 PM)
+
+=======================================================================================
+=======================================================================================
+
+ Here are your current tasks:
+ 1.[T][ ] todoTest
+ 2.[D][X] deadlineTest (by: Sat, 8 January 2000 12:00 PM)
+ 3.[E][X] eventTest (at: Sat, 8 January 2000 12:00 PM - Sat, 20 August 2022 5:59 PM)
+
+=======================================================================================
+=======================================================================================
+
+ ☹ Apologies! Please indicate a task no. between 1 to 3.
+
+=======================================================================================
+=======================================================================================
+
+ ☹ Apologies! Please indicate a task no. between 1 to 3.
+
+=======================================================================================
+=======================================================================================
+
+ ☹ Apologies! Please indicate a task no. between 1 to 3.
+
+=======================================================================================
+=======================================================================================
+
+ ☹ Apologies! Please indicate the task no. in digits.
+
+=======================================================================================
+=======================================================================================
+
+ Understood. I will mark the task as uncompleted.
+ [D][ ] deadlineTest (by: Sat, 8 January 2000 12:00 PM)
+
+=======================================================================================
+=======================================================================================
+
+ Understood. I will mark the task as uncompleted.
+ [E][ ] eventTest (at: Sat, 8 January 2000 12:00 PM - Sat, 20 August 2022 5:59 PM)
+
+=======================================================================================
+=======================================================================================
+
+ Understood. I will mark the task as uncompleted.
+ [D][ ] deadlineTest (by: Sat, 8 January 2000 12:00 PM)
+
+=======================================================================================
+=======================================================================================
+
+ Here are your current tasks:
+ 1.[T][ ] todoTest
+ 2.[D][ ] deadlineTest (by: Sat, 8 January 2000 12:00 PM)
+ 3.[E][ ] eventTest (at: Sat, 8 January 2000 12:00 PM - Sat, 20 August 2022 5:59 PM)
+
+=======================================================================================
+=======================================================================================
+
+ ☹ Apologies! Please enter a supported command.
+
+=======================================================================================
+=======================================================================================
+
+ ☹ Apologies! Please indicate a task no. between 1 to 3.
+
+=======================================================================================
+=======================================================================================
+
+ ☹ Apologies! Please indicate a task no. between 1 to 3.
+
+=======================================================================================
+=======================================================================================
+
+ ☹ Apologies! Please indicate a task no. between 1 to 3.
+
+=======================================================================================
+=======================================================================================
+
+ ☹ Apologies! Please indicate the task no. in digits.
+
+=======================================================================================
+=======================================================================================
+
+ Understood. I have removed the following task:
+ [E][ ] eventTest (at: Sat, 8 January 2000 12:00 PM - Sat, 20 August 2022 5:59 PM)
+ You have a total of 2 task(s) left.
+
+=======================================================================================
+=======================================================================================
+
+ ☹ Apologies! Please indicate a task no. between 1 to 2.
+
+=======================================================================================
+=======================================================================================
+
+ Here are your current tasks:
+ 1.[T][ ] todoTest
+ 2.[D][ ] deadlineTest (by: Sat, 8 January 2000 12:00 PM)
+
+=======================================================================================
+=======================================================================================
+
+ ☹ Apologies! Please indicate a task no. between 1 to 2.
+
+=======================================================================================
+=======================================================================================
+
+ ☹ Apologies! Please indicate a task no. between 1 to 2.
+
+=======================================================================================
+=======================================================================================
+
+ Understood. I have added the following task:
+ [E][ ] eventTest (at: Sat, 8 January 2000 12:00 PM - Sat, 20 August 2022 5:59 PM)
+ You now have a total of 3 task(s).
+
+=======================================================================================
+=======================================================================================
+
+ Farewell!
+
+=======================================================================================
diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt
index e69de29bb2..b1fc3a9667 100644
--- a/text-ui-test/input.txt
+++ b/text-ui-test/input.txt
@@ -0,0 +1,47 @@
+list
+empty
+list
+empty
+list
+mark 0
+unmark 0
+delete 0
+todo
+todo todoTest
+deadline
+deadline deadlineTest
+deadline deadlineTest /by 99/99/9999-99:99
+deadline deadlineTest /by 08/01/2000-12:00
+event
+event eventTest
+event eventTest /at 00/00/0000-00:00 ~ 99/99/9999-99:99
+event eventTest /at 08/01/2000-12:00 ~ 20/8/2022-17:59
+list
+mark
+mark -1
+mark 99
+mark three
+mark 2
+mark 3 please
+mark 2
+list
+unmark
+unmark -1
+unmark 99
+unmark three
+unmark 2
+unmark 3 please
+unmark 2
+list
+blah
+delete
+delete -1
+delete 99
+delete three
+delete 3
+delete 3
+list
+mark 3
+unmark 3
+event eventTest /at 08/01/2000-12:00 ~ 20/8/2022-17:59
+bye
\ No newline at end of file
diff --git a/text-ui-test/runtest.bat b/text-ui-test/runtest.bat
index 0873744649..140a9bac99 100644
--- a/text-ui-test/runtest.bat
+++ b/text-ui-test/runtest.bat
@@ -7,7 +7,12 @@ REM delete output from previous run
if exist ACTUAL.TXT del ACTUAL.TXT
REM compile the code into the bin folder
-javac -cp ..\src\main\java -Xlint:none -d ..\bin ..\src\main\java\*.java
+javac -cp ..\src\main\java -Xlint:none -d ..\bin ..\src\main\java\ren\*.java
+IF ERRORLEVEL 1 (
+ echo ********** BUILD FAILURE **********
+ exit /b 1
+)
+javac -cp ..\src\main\java -Xlint:none -d ..\bin ..\src\main\java\ren\task\*.java
IF ERRORLEVEL 1 (
echo ********** BUILD FAILURE **********
exit /b 1
@@ -15,7 +20,7 @@ IF ERRORLEVEL 1 (
REM no error here, errorlevel == 0
REM run the program, feed commands from input.txt file and redirect the output to the ACTUAL.TXT
-java -classpath ..\bin Duke < input.txt > ACTUAL.TXT
+java -classpath ..\bin ren.Ren < input.txt > ACTUAL.TXT
REM compare the output to the expected output
FC ACTUAL.TXT EXPECTED.TXT
diff --git a/text-ui-test/runtest.sh b/text-ui-test/runtest.sh
old mode 100644
new mode 100755
index c9ec870033..926f95e305
--- a/text-ui-test/runtest.sh
+++ b/text-ui-test/runtest.sh
@@ -13,14 +13,19 @@ then
fi
# compile the code into the bin folder, terminates if error occurred
-if ! javac -cp ../src/main/java -Xlint:none -d ../bin ../src/main/java/*.java
+if ! javac -cp ../src/main/java -Xlint:none -d ../bin ../src/main/java/ren/*.java
+then
+ echo "********** BUILD FAILURE **********"
+ exit 1
+fi
+if ! javac -cp ../src/main/java -Xlint:none -d ../bin ../src/main/java/ren/task/*.java
then
echo "********** BUILD FAILURE **********"
exit 1
fi
# run the program, feed commands from input.txt file and redirect the output to the ACTUAL.TXT
-java -classpath ../bin Duke < input.txt > ACTUAL.TXT
+java -classpath ../bin ren.Ren < input.txt > ACTUAL.TXT
# convert to UNIX format
cp EXPECTED.TXT EXPECTED-UNIX.TXT