diff --git a/README.md b/README.md index 90aa7f092a..e798f87451 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,37 @@ -# Duke project template - -This is a project template for a greenfield Java project. It's named after the Java mascot _Duke_. Given below are instructions on how to use it. - -## Setting up in Intellij - -Prerequisites: JDK 17, update Intellij to the most recent version. - -1. Open Intellij (if you are not in the welcome screen, click `File` > `Close Project` to close the existing project first) -1. Open the project into Intellij as follows: - 1. Click `Open`. - 1. Select the project directory, and click `OK`. - 1. If there are any further prompts, accept the defaults. -1. Configure the project to use **JDK 17** (not other versions) as explained in [here](https://www.jetbrains.com/help/idea/sdk.html#set-up-jdk).
- In the same dialog, set the **Project language level** field to the `SDK default` option. -3. After that, locate the `src/main/java/Duke.java` file, right-click it, and choose `Run Duke.main()` (if the code editor is showing compile errors, try restarting the IDE). If the setup is correct, you should see something like the below as the output: - ``` - Hello from - ____ _ - | _ \ _ _| | _____ - | | | | | | | |/ / _ \ - | |_| | |_| | < __/ - |____/ \__,_|_|\_\___| - ``` +# KieTwoForOne + +> “Your mind is for having ideas, not holding them.” – David Allen ([source](https://www.zen-tools.net/getting-things-done.html)) + +KieTwoForOne _frees your mind_ from having to remember things you need to do. It's, + +- text-based +- easy to learn +- ~~FAST~~ **SUPER FAST** to use + +All you need to do is, + +1. download the jar file. +2. double-click it. +3. add your tasks. +4. let it manage your tasks for you 😉 + +And it is **FREE**! + +Features: +- [X] Managing tasks +- [X] Managing deadlines +- [X] Managing Events +- [X] Adding to tasks + +If you are a Java programmer, you can also use it to practice Java. Here's the `main` method 😀: + +```Java +public class Main { + public static void main(String[] args) { + Application.launch(MainApp.class, args); + } +} +``` + + + diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000000..d17cbe0dd1 --- /dev/null +++ b/build.gradle @@ -0,0 +1,61 @@ +plugins { + id 'checkstyle' + id 'java' + id 'application' + id 'com.github.johnrengelman.shadow' version '7.1.2' +} + +checkstyle { + toolVersion = '10.2' +} + +repositories { + mavenCentral() +} + +dependencies { + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.10.0' + testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.10.0' + + String javaFxVersion = '17.0.7' + 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 = 'kietwoforone.gui.Launcher' +} + +shadowJar { + archiveBaseName = "kietwoforone" + archiveFileName = "kietwoforone.jar" + archiveClassifier = null +} + +run { + standardInput = System.in +} diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml new file mode 100644 index 0000000000..fa4ecf021a --- /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..6423654db1 --- /dev/null +++ b/config/checkstyle/suppressions.xml @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/data/tasks.txt b/data/tasks.txt new file mode 100644 index 0000000000..50db8cb6c9 Binary files /dev/null and b/data/tasks.txt differ diff --git a/docs/README.md b/docs/README.md index 47b9f984f7..2d640fd208 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,30 +1,269 @@ -# Duke User Guide +# KieTwoForOne User Guide -// Update the title above to match the actual product name +![Screenshot of KieTwoForOne chatbot UI](Ui.png) -// Product screenshot goes here +## WELCOME TO THE FUTURE! -// Product intro goes here +_KIETWOFORONE IS YOU ALL-ENCOMPASSING FAST TRACKING CHATBOT!!!_ -## Adding deadlines +From marking tasks as complete or incomplete to filtering by date, KieTwoForOne does it **ALL**! -// Describe the action and its outcome. +## How do I access the chatbot? -// Give examples of usage +Users can download the JAR file under the v0.2 release found on this page. After downloading the file, users can open the file by navigating to the location where the JAR file has been downloaded and running the following command on their terminal. -Example: `keyword (optional arguments)` +`java -jar kietwoforone.java` -// A description of the expected outcome goes here +## Features -``` -expected output -``` +KieTwoForOne's features are listed as follows! -## Feature ABC +### Adding tasks -// Feature details +Users can make use of KieTwoForOne to add three types of tasks, namely Todos, Deadline and Events. Once the task is +added successfully, KieTwoForOne will display the task added to the user and inform the user on the total number of +tasks present in the list. +#### Todo Tasks -## Feature XYZ +Todo tasks are tasks with no specified deadline. Users can add Todo tasks to KieTwoForOne by inputting the following +command: -// Feature details \ No newline at end of file +> todo (task) + +For example if the user had inputted the following command: + +> todo task + +The chatbot will provide the following response if it has successfully added the task to the task list. + +> Got it. I've added this task: +> +> [T] [ ] task [] +> +> Now you have 2 tasks in the list. + +#### Deadline Tasks + +Deadline tasks are tasks with one specified deadline. Users can add Deadline tasks to KieTwoForOne by inputting the +following command: + +> deadline (task) /by (date in YYYY-MM-DD) (time in 24hr clock) + +For example if the user had inputted the following command: + +> deadline task /by 2024-12-05 1800 + +The chatbot will provide the following response if it has successfully added the task to the task list. + +> Got it. I've added this task: +> +> [D] [ ] task (by: Dec 5 2024 1800) [] +> +> Now you have 2 tasks in the list. + +#### Event Tasks + +Event tasks are tasks with a specified start and end time. Users can add Event tasks to KieTwoForOne by inputting the +following command: + +> event (task) /from (date in YYYY-MM-DD) (time in 24hr clock) /to (date in YYYY-MM-DD) (time in 24hr clock) + +For example if the user had inputted the following command: + +> event task /from 2024-12-05 1800 /to 2024-12-06 1800 [] + +The chatbot will provide the following response if it has successfully added the task to the task list. + +> Got it. I've added this task: +> +> [E] [ ] task (from: Dec 5 2024 1800 to: Dec 6 2024 1800) [] +> +> Now you have 3 tasks in the list. + +### Listing Tasks + +Users can make use of KieTwoForOne to list the tasks that they added to the chatbot. + +This is done by inputting the following command> + +> list + +Using the previous inputs from the adding tasks section calling this command would display the following + +> Here are the tasks in your list: +> 1. [T] [ ] task ] [] +> 2. [D] [ ] task (by: Dec 5 2024 1800) [] +> 3. [E] [ ] task (from: Dec 5 2024 1800 to: Dec 6 2024 1800) [] + +### Marking and Unmarking tasks + +Users can make use of KieTwoForOne to mark certain tasks as complete or incomplete. This would mark an X at the Task to +indicate that the task has been completed and leave it empty if it is incomplete. + +#### Marking tasks + +Users can make use of KieTwoForOne to mark tasks as complete by inputting the following command: + +> mark (index) + +Index refers to the position of the task in the task list. For example calling the following input: + +> mark 1 + +Would yield the following response from the chatbot. + +> Nice! I've marked this task as done: +> +> [T] [X] task [] + +#### Unmarking tasks + +Users can also unmark tasks that are incomplete by inputting the following command: + +> unmark (index) + +Index again refers to the position of the task in the task list. For example calling the following input on the same +task list. + +> unmark 1 + +Would yield the following response from the chatbot. + +> OK. I've marked this task as incomplete: +> +> [T] [ ] task [] + +### Deleting tasks + +Users can also delete tasks from the task list by inputting the following command: + +> delete (index) + +Index refers to the position of the task in the task list. For example inputting the following command on the previous +task list: + +> delete 1 + +Would yield the following response from the chatbot: + +>Noted. I've removed the task: +> +>[T] [ ] task [] +> +>Now you have 2 tasks in the list. + +The chatbot will inform you of the number of tasks remaining in the task list. Inputting the following: + +> list + +Would yield the following output: + +>Here are the tasks in your list: +> +> 1. [D] [ ] task (by: Dec 5 2024 1800) [] +> 2. [E] [ ] task (from: Dec 5 2024 1800 to: Dec 6 2024 1800) [] + +We can see that the task Todo task has been deleted and there are 2 tasks left. + +### Filer tasks by date + +Users can filter the tasks by date by inputting the following command: + +> date (date in YYYY-MM-DD) + +Deadline tasks are only kept if the deadline lies on the date inputted while event tasks are kept if the date lies +between the start and end date inclusive + +For example using the above task list, inputting the following: + +> date 2024-12-05 + +Would yield the following output: + +> Here are the tasks occurring on this date: +> 1. [D] [ ] task (by: Dec 5 2024 1800) [] +> 2. [E] [ ] task (from: Dec 5 2024 1800 to: Dec 6 2024 1800) [] + +Since the date inputted lies wihtin both the deadline and event duration. + +On the other hand inputting the following: + +> date 2024-12-06 + +Would yield the following output: + +> Here are the tasks occurring on this date: +> 1. [E] [ ] task (from: Dec 5 2024 1800 to: Dec 6 2024 1800) [] + +Since the date does not lie within the deadline but lies within the event duration. + +### Filter tasks by word + +Users can make use of the find functionality in our chatbot to filter tasks by words using the following input + +> find (word) + +Given the following task list: + +> Here are the tasks in your list: +> 1. [D] [ ] task (by: Dec 5 2024 1800) [] +> 2. [E] [ ] task (from: Dec 5 2024 1800 to: Dec 6 2024 1800) [] +> 3. [T] [ ] do work [] +> 4. [T] [ ] do work tomorrow [] +> 5. [T] [ ] do poetry [] + +Users can input a word from the sentence such as: + +> find do + +And yield the following output: + +> Here are the matching tasks in your list: +> 1. [T] [ ] do work [] +> 2. [T] [ ] do work tomorrow [] +> 3. [T] [ ] do poetry [] + +They may also input more than one word: + +> find do work + +And yield the following output: + +> Here are the matching tasks in your list: +> 1. [T] [ ] do work [] +> 2. [T] [ ] do work tomorrow [] + +They may even also input part of a word: + +> find ta + +And yield the following output: + +> Here are the matching tasks in your list: +> 1. [D] [ ] task (by: Dec 5 2024 1800) [] +> 2. [E] [ ] task (from: Dec 5 2024 1800 to: Dec 6 2024 1800) [] + +### Tagging tasks + +Users can also use the tag function to tag certain tasks as they see fit using the following command + +> tag (index) /(tag) + +Index refers to the position of the task in the task list and the tag being the tag u would like to use. + +For example inputting the following on the previous task list: + +> tag 5 /fun + +Will yield the following output: + +> OK. I've tagged the following task: +> +> [T] [ ] do poetry [fun] + +### Exiting the application + +User can input the following command to exit and close the application + +> bye diff --git a/docs/Ui.png b/docs/Ui.png new file mode 100644 index 0000000000..ede969663f 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..033e24c4cd 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..66c01cfeba --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.2-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 0000000000..fcb6fca147 --- /dev/null +++ b/gradlew @@ -0,0 +1,248 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original 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 POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# 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 ;; #( + MSYS* | 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 + if ! command -v java >/dev/null 2>&1 + then + 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 +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# 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"' + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000000..6689b85bee --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,92 @@ +@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=. +@rem This is normally unused +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% equ 0 goto execute + +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 execute + +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 + +: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 %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 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! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +: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/kietwoforone/KieTwoForOne.java b/src/main/java/kietwoforone/KieTwoForOne.java new file mode 100644 index 0000000000..68634365f8 --- /dev/null +++ b/src/main/java/kietwoforone/KieTwoForOne.java @@ -0,0 +1,89 @@ +package kietwoforone; + +import kietwoforone.commands.Command; +import kietwoforone.exceptions.KieTwoForOneException; +import kietwoforone.parser.Parser; +import kietwoforone.storage.Storage; +import kietwoforone.tasks.Task; +import kietwoforone.tasks.TaskList; +import kietwoforone.ui.UI; +import java.util.ArrayList; + +/** + * Represents the chatbot and its interactions with the user. + */ +public class KieTwoForOne { + + private static TaskList tasks = new TaskList(new ArrayList<> (100)); + private static UI ui = new UI(); + private static Storage storage = new Storage(); + + /** + * Runs the chatbot by initially displaying the welcome message and setting isExit as false. + * When the user keys in a command, the command is read and the parsed to execute the appropriate command. + * Incorrect inputs throws a KieTwoForOne exception and the chatbot will display the error message. + * Lastly the separation line is drawn to separate the different commands. + * If isExit is true, function exits. + */ + public static void run() { + UI.showWelcome(); + boolean isExit = false; + while (!isExit) { + try { + String fullCommand = UI.readCommand(); + UI.showLine(); + Command c = Parser.parse(fullCommand); + c.execute(tasks, ui, storage); + isExit = c.isExit(); + } catch (KieTwoForOneException e) { + ui.showErrorMessage(e); + } finally { + ui.showLine(); + } + } + } + + /** + * Loads the saved tasks from the text file and adds it to the task list ad then runs the chatbot. + * Throws a KieTwoForOne exception when the file does not exist. + * + * @param args + */ + public static void main(String[] args) { + try { + storage.loadFile(tasks.getTaskList()); + } catch (KieTwoForOneException e) { + System.out.println(e.getMessage()); + } + KieTwoForOne.run(); + } + + public String getResponse(String input) { + assert input != null: "Input cannot be null."; + try { + Command c = Parser.parse(input); + c.execute(tasks, ui, storage); + return ui.getResponse(); + } catch (KieTwoForOneException e) { + return e.getMessage(); + } + } + + /** + * Returns the TaskList object + * + * @return TaskList + */ + public TaskList getTasks() { + return this.tasks; + } + + /** + * Returns the Storage object + * + * @return Storage + */ + public static Storage getStorage() { + return storage; + } +} diff --git a/src/main/java/kietwoforone/commands/AddCommand.java b/src/main/java/kietwoforone/commands/AddCommand.java new file mode 100644 index 0000000000..46dee76882 --- /dev/null +++ b/src/main/java/kietwoforone/commands/AddCommand.java @@ -0,0 +1,70 @@ +package kietwoforone.commands; + +import kietwoforone.exceptions.KieTwoForOneException; +import kietwoforone.storage.Storage; +import kietwoforone.tasks.Task; +import kietwoforone.tasks.TaskList; +import kietwoforone.ui.UI; + +/** + * Represents the command called when a new task is added to the task list. + */ +public class AddCommand extends Command { + + private Task task; + + /** + * Constructor for the AddCommand Object. + * + * @param task + */ + public AddCommand(Task task) { + assert task != null: "Task cannot be null."; + this.task = task; + } + + /** + * Returns the command to add a task to the tasklist. + * Throws a KieTwoForOne exception when there is no file to save date. + * + * @param tasks + * @param ui + * @param storage + * @throws KieTwoForOneException + */ + @Override + public void execute(TaskList tasks, UI ui, Storage storage) throws KieTwoForOneException { + assert tasks != null: "TaskList cannot be null."; + assert ui != null: "UI cannot be null."; + assert storage != null: "Storage cannot be null."; + tasks.addTasks(this.task); + ui.showAddTasks(tasks.getTaskList(), this.task); + try { + storage.saveFile(tasks.getTaskList()); + } catch (KieTwoForOneException e) { + throw new KieTwoForOneException(e.getMessage()); + } + } + + /** + * Returns a boolean to determine whether to close the chatbot. + * True if the command closes the chatbot and false otherwise. + * + * @return Boolean to determine whether to close the chatbot. + */ + @Override + public boolean isExit() { + return false; + } + + /** + * Returns the string representation of the AddCommand object. + * + * @return String representation of the AddCommand object. + */ + @Override + public String toString() { + return "Task added"; + } + +} diff --git a/src/main/java/kietwoforone/commands/ByeCommand.java b/src/main/java/kietwoforone/commands/ByeCommand.java new file mode 100644 index 0000000000..c18fac1ec0 --- /dev/null +++ b/src/main/java/kietwoforone/commands/ByeCommand.java @@ -0,0 +1,45 @@ +package kietwoforone.commands; + +import kietwoforone.storage.Storage; +import kietwoforone.tasks.TaskList; +import kietwoforone.ui.UI; + +/** + * Represents the command called when the user exits the chatbot. + */ +public class ByeCommand extends Command { + + /** + * Returns the command to exit the user out of the chatbot. + * + * @param tasks + * @param ui + * @param storage + */ + @Override + public void execute(TaskList tasks, UI ui, Storage storage) { + ui.showBye(); + } + + /** + * Returns a boolean to determine whether to close the chatbot. + * True if the command closes the chatbot and false otherwise. + * + * @return Boolean to determine whether to close the chatbot. + */ + @Override + public boolean isExit() { + return true; + } + + /** + * Returns the string representation of the ByeCommand object. + * + * @return String representation of the ByeCommand object. + */ + @Override + public String toString() { + return "Bye"; + } + +} diff --git a/src/main/java/kietwoforone/commands/Command.java b/src/main/java/kietwoforone/commands/Command.java new file mode 100644 index 0000000000..2b8bda2a62 --- /dev/null +++ b/src/main/java/kietwoforone/commands/Command.java @@ -0,0 +1,32 @@ +package kietwoforone.commands; + +import kietwoforone.exceptions.KieTwoForOneException; +import kietwoforone.storage.Storage; +import kietwoforone.tasks.TaskList; +import kietwoforone.ui.UI; + +/** + * Represents any command the user can call in the chatbot. + */ +public abstract class Command { + + /** + * Returns any command that user can input. + * Throws a KieTwoForOne exception if the user puts an invalid/incomplete input. + * + * @param tasks + * @param ui + * @param storage + * @throws KieTwoForOneException + */ + public abstract void execute(TaskList tasks, UI ui, Storage storage) throws KieTwoForOneException; + + /** + * Abstract method that returns a boolean to determine whether to close the chatbot. + * True if the command closes the chatbot and false otherwise. + * + * @return Boolean to determine whether to close the chatbot. + */ + public abstract boolean isExit(); + +} diff --git a/src/main/java/kietwoforone/commands/DeleteCommand.java b/src/main/java/kietwoforone/commands/DeleteCommand.java new file mode 100644 index 0000000000..37838bd14a --- /dev/null +++ b/src/main/java/kietwoforone/commands/DeleteCommand.java @@ -0,0 +1,71 @@ +package kietwoforone.commands; + +import kietwoforone.exceptions.KieTwoForOneException; +import kietwoforone.storage.Storage; +import kietwoforone.tasks.Task; +import kietwoforone.tasks.TaskList; +import kietwoforone.ui.UI; + +/** + * Represents the command called when the user deletes a task from the task list. + */ +public class DeleteCommand extends Command { + + int position; + + /** + * Constructor for the DeleteCommand object. + * + * @param position + */ + public DeleteCommand(int position) { + assert position >= 0: "Array position cannot be less than 0."; + this.position = position; + } + + /** + * Returns the command that deletes a task from the task list. + * Throws a KieTwoForOne exception when the index specified does not hold a task. + * Throws a KieTwoForOne exception when saving to a file that doesn't exist. + * + * @param tasks + * @param ui + * @param storage + * @throws KieTwoForOneException + */ + @Override + public void execute(TaskList tasks, UI ui, Storage storage) throws KieTwoForOneException { + assert tasks != null: "TaskList cannot be null."; + assert ui != null: "UI cannot be null."; + assert storage != null: "Storage cannot be null."; + Task deletedTask = tasks.deleteTask(this.position); + ui.showDeleteTask(tasks.getTaskList(), deletedTask); + try { + storage.saveFile(tasks.getTaskList()); + } catch (KieTwoForOneException e) { + throw new KieTwoForOneException(e.getMessage()); + } + } + + /** + * Returns a boolean to determine whether to close the chatbot. + * True if the command closes the chatbot and false otherwise. + * + * @return Boolean to determine whether to close the chatbot. + */ + @Override + public boolean isExit() { + return false; + } + + /** + * Returns the string representation of the DeleteCommand object. + * + * @return String representation of the DeleteCommand object. + */ + @Override + public String toString() { + return "Task deleted"; + } + +} diff --git a/src/main/java/kietwoforone/commands/FindDateCommand.java b/src/main/java/kietwoforone/commands/FindDateCommand.java new file mode 100644 index 0000000000..3a42b12cca --- /dev/null +++ b/src/main/java/kietwoforone/commands/FindDateCommand.java @@ -0,0 +1,67 @@ +package kietwoforone.commands; + +import kietwoforone.exceptions.KieTwoForOneException; +import kietwoforone.storage.Storage; +import kietwoforone.tasks.TaskList; +import kietwoforone.ui.UI; + +/** + * Represents the command called when the user filters the task by date. + */ +public class FindDateCommand extends Command { + + private String date; + + /** + * Constructor for the FindDateCommand object. + * + * @param date + */ + public FindDateCommand(String date) { + assert date != null: "Date cannot be null."; + this.date = date; + } + + /** + * Returns the command to filter the tasks by date. + * Throws a KieTwoForOne exception when user inputs an incorrect date format. + * + * @param tasks + * @param ui + * @param storage + * @throws KieTwoForOneException + */ + @Override + public void execute(TaskList tasks, UI ui, Storage storage) throws KieTwoForOneException { + assert tasks != null: "TaskList cannot be null."; + assert ui != null: "UI cannot be null."; + assert storage != null: "Storage cannot be null."; + try { + ui.showSameDate(tasks.getTaskList(), this.date); + } catch (KieTwoForOneException e) { + throw new KieTwoForOneException(e.getMessage()); + } + } + + /** + * Returns a boolean to determine whether to close the chatbot. + * True if the command closes the chatbot and false otherwise. + * + * @return Boolean to determine whether to close the chatbot. + */ + @Override + public boolean isExit() { + return false; + } + + /** + * Returns the string representation of the FindDateCommand object. + * + * @return String representation of the FindDateCommand object. + */ + @Override + public String toString() { + return "Date found"; + } + +} diff --git a/src/main/java/kietwoforone/commands/FindWordCommand.java b/src/main/java/kietwoforone/commands/FindWordCommand.java new file mode 100644 index 0000000000..de6f919c64 --- /dev/null +++ b/src/main/java/kietwoforone/commands/FindWordCommand.java @@ -0,0 +1,55 @@ +package kietwoforone.commands; + +import kietwoforone.exceptions.KieTwoForOneException; +import kietwoforone.storage.Storage; +import kietwoforone.tasks.TaskList; +import kietwoforone.ui.UI; + +/** + * Represents the command called when the user filters the task list by word. + */ +public class FindWordCommand extends Command { + + private String keyword; + + /** + * Constructor for a new FindWordCommand object. + * + * @param keyword + */ + public FindWordCommand(String keyword) { + assert keyword != null: "Keyword cannot be null."; + this.keyword = keyword; + } + + /** + * Returns the command to filter the task list by the keyword. + * + * @param tasks + * @param ui + * @param storage + */ + @Override + public void execute(TaskList tasks, UI ui, Storage storage) { + assert tasks != null: "TaskList cannot be null."; + assert ui != null: "UI cannot be null."; + ui.showMatchingTask(tasks.getTaskList(), this.keyword); + } + + /** + * Returns a boolean to determine whether to close the chatbot. + * True if the command closes the chatbot and false otherwise. + * + * @return Boolean to determine whether to close the chatbot. + */ + @Override + public boolean isExit() { + return false; + } + + @Override + public String toString() { + return "Matching tasks listed"; + } + +} diff --git a/src/main/java/kietwoforone/commands/ListCommand.java b/src/main/java/kietwoforone/commands/ListCommand.java new file mode 100644 index 0000000000..4a3bec8138 --- /dev/null +++ b/src/main/java/kietwoforone/commands/ListCommand.java @@ -0,0 +1,47 @@ +package kietwoforone.commands; + +import kietwoforone.tasks.TaskList; +import kietwoforone.ui.UI; +import kietwoforone.storage.Storage; + +/** + * Represent the command called when the user wants to list the tasks in the task list. + */ +public class ListCommand extends Command { + + /** + * Returns the command to display the task list. + * + * @param tasks + * @param ui + * @param storage + */ + @Override + public void execute(TaskList tasks, UI ui, Storage storage) { + assert tasks != null: "TaskList cannot be null."; + assert ui != null: "UI cannot be null."; + ui.showTaskList(tasks.getTaskList()); + } + + /** + * Returns a boolean to determine whether to close the chatbot. + * True if the command closes the chatbot and false otherwise. + * + * @return Boolean to determine whether to close the chatbot. + */ + @Override + public boolean isExit() { + return false; + } + + /** + * Returns the string representation of the ListCommand object. + * + * @return String representation of the ListCommand object. + */ + @Override + public String toString() { + return "Tasks listed"; + } + +} diff --git a/src/main/java/kietwoforone/commands/MarkCommand.java b/src/main/java/kietwoforone/commands/MarkCommand.java new file mode 100644 index 0000000000..e7074239b5 --- /dev/null +++ b/src/main/java/kietwoforone/commands/MarkCommand.java @@ -0,0 +1,70 @@ +package kietwoforone.commands; + +import kietwoforone.exceptions.KieTwoForOneException; +import kietwoforone.storage.Storage; +import kietwoforone.tasks.TaskList; +import kietwoforone.ui.UI; + +/** + * Represents the command called when the user marks a task as complete. + */ +public class MarkCommand extends Command { + + private int position; + + /** + * Constructor for the MarkCommand object. + * + * @param position + */ + public MarkCommand(int position) { + assert position >= 0: "Invalid position inputted"; + this.position = position; + } + + /** + * Returns the command to mark the task at the position specified by the user. + * Throws a KieTwoForOne exception when there is no task at the specified position. + * Throws a KieTwoForOne exception when the file being saved to does not exist. + * + * @param tasks + * @param ui + * @param storage + * @throws KieTwoForOneException + */ + @Override + public void execute(TaskList tasks, UI ui, Storage storage) throws KieTwoForOneException { + assert tasks != null: "TaskList cannot be null."; + assert ui != null: "UI cannot be null."; + assert storage != null: "Storage cannot be null."; + String markedTask = tasks.markTask(position); + ui.showMarkTask(markedTask); + try { + storage.saveFile(tasks.getTaskList()); + } catch (KieTwoForOneException e) { + throw new KieTwoForOneException(e.getMessage()); + } + } + + /** + * Returns a boolean to determine whether to close the chatbot. + * True if the command closes the chatbot and false otherwise. + * + * @return Boolean to determine whether to close the chatbot. + */ + @Override + public boolean isExit() { + return false; + } + + /** + * Returns the string representation of the MarkCommand object. + * + * @return String representation of the MarkCommand object. + */ + @Override + public String toString() { + return "Task marked"; + } + +} diff --git a/src/main/java/kietwoforone/commands/TagCommand.java b/src/main/java/kietwoforone/commands/TagCommand.java new file mode 100644 index 0000000000..0245cb1c1d --- /dev/null +++ b/src/main/java/kietwoforone/commands/TagCommand.java @@ -0,0 +1,61 @@ +package kietwoforone.commands; + +import kietwoforone.exceptions.KieTwoForOneException; +import kietwoforone.storage.Storage; +import kietwoforone.tasks.TaskList; +import kietwoforone.ui.UI; + +/** + * Represents the command called when the user adds a tag to a task. + */ +public class TagCommand extends Command { + + private int position; + private String tag; + + /** + * Constructor for the TagCommand object. + * + * @param position + * @param tag + */ + public TagCommand(int position, String tag) { + this.position = position; + this.tag = tag; + } + + /** + * Returns the command to tag the task at the position specified by the user. + * Throws a KieTwoForOne exception when there is no task at the specified position. + * Throws a KieTwoForOne exception when the file being saved to does not exist. + * + * @param tasks + * @param ui + * @param storage + * @throws KieTwoForOneException + */ + @Override + public void execute(TaskList tasks, UI ui, Storage storage) throws KieTwoForOneException { + assert tasks != null: "TaskList cannot be null."; + assert ui != null: "UI cannot be null."; + assert storage != null: "Storage cannot be null."; + String taggedTask = tasks.addTag(this.position, this.tag); + ui.showTaggedTask(taggedTask); + try { + storage.saveFile(tasks.getTaskList()); + } catch (KieTwoForOneException e) { + throw new KieTwoForOneException(e.getMessage()); + } + } + + /** + * Returns a boolean to determine whether to close the chatbot. + * True if the command closes the chatbot and false otherwise. + * + * @return Boolean to determine whether to close the chatbot. + */ + @Override + public boolean isExit() { + return false; + } +} diff --git a/src/main/java/kietwoforone/commands/UnmarkCommand.java b/src/main/java/kietwoforone/commands/UnmarkCommand.java new file mode 100644 index 0000000000..a90be9ad39 --- /dev/null +++ b/src/main/java/kietwoforone/commands/UnmarkCommand.java @@ -0,0 +1,70 @@ +package kietwoforone.commands; + +import kietwoforone.exceptions.KieTwoForOneException; +import kietwoforone.storage.Storage; +import kietwoforone.tasks.TaskList; +import kietwoforone.ui.UI; + +/** + * Represents the command called when the user wishes to unmark an incomplete task. + */ +public class UnmarkCommand extends Command { + + private int position; + + /** + * Constructor for the UnmarkCommand object. + * + * @param position + */ + public UnmarkCommand(int position) { + assert position >= 0: "Position must be greater than 0."; + this.position = position; + } + + /** + * Returns the command to unmark the task at the position specified by the user. + * Throws a KieTwoForOne exception when there is no task at the specified position. + * Throws a KieTwoForOne exception when the file to be saved to does not exist. + * + * @param tasks + * @param ui + * @param storage + * @throws KieTwoForOneException + */ + @Override + public void execute(TaskList tasks, UI ui, Storage storage) throws KieTwoForOneException { + assert tasks != null: "TaskList cannot be null."; + assert ui != null: "UI cannot be null."; + assert storage != null: "Storage cannot be null."; + String unmarkedTask = tasks.unmarkTask(this.position); + ui.showUnmarkTask(unmarkedTask); + try { + storage.saveFile(tasks.getTaskList()); + } catch (KieTwoForOneException e) { + throw new KieTwoForOneException(e.getMessage()); + } + } + + /** + * Returns a boolean to determine whether to close the chatbot. + * True if the command closes the chatbot and false otherwise. + * + * @return Boolean to determine whether to close the chatbot. + */ + @Override + public boolean isExit() { + return false; + } + + /** + * Returns the string representation of the UnmarkCommand object + * + * @return String representation of the UnmarkCommand object. + */ + @Override + public String toString() { + return "Task unmarked"; + } + +} diff --git a/src/main/java/kietwoforone/exceptions/KieTwoForOneException.java b/src/main/java/kietwoforone/exceptions/KieTwoForOneException.java new file mode 100644 index 0000000000..32b18e79b2 --- /dev/null +++ b/src/main/java/kietwoforone/exceptions/KieTwoForOneException.java @@ -0,0 +1,17 @@ +package kietwoforone.exceptions; + +/** + * Represents any exception thrown when the user keys invalid/incomplete inputs. + */ +public class KieTwoForOneException extends Exception { + + /** + * Constructor for the KieTwoForOneException object. + * + * @param message + */ + public KieTwoForOneException(String message) { + super(message); + } + +} diff --git a/src/main/java/kietwoforone/gui/DialogBox.java b/src/main/java/kietwoforone/gui/DialogBox.java new file mode 100644 index 0000000000..35b4fbd253 --- /dev/null +++ b/src/main/java/kietwoforone/gui/DialogBox.java @@ -0,0 +1,80 @@ +package kietwoforone.gui; + +import java.io.IOException; +import java.util.Collections; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.geometry.Pos; +import javafx.scene.Node; +import javafx.scene.control.Label; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.layout.HBox; + +/** + * Represents a dialog box consisting of an ImageView to represent the speaker's face + * and a label containing text from the speaker. + */ +public class DialogBox extends HBox { + @FXML + private Label dialog; + @FXML + private ImageView displayPicture; + + private DialogBox(String text, Image img) { + assert text != null: "Text cannot be null."; + assert img != null: "Image cannot be null."; + try { + FXMLLoader fxmlLoader = new FXMLLoader(MainWindow.class.getResource("/view/DialogBox.fxml")); + fxmlLoader.setController(this); + fxmlLoader.setRoot(this); + fxmlLoader.load(); + } catch (IOException e) { + e.printStackTrace(); + } + + dialog.setText(text); + displayPicture.setImage(img); + } + + /** + * Flips the dialog box such that the ImageView is on the left and text on the right. + */ + private void flip() { + ObservableList tmp = FXCollections.observableArrayList(this.getChildren()); + Collections.reverse(tmp); + getChildren().setAll(tmp); + setAlignment(Pos.TOP_LEFT); + } + + /** + * Returns a DialogBox instance when for the user's inputs. + * + * @param text + * @param img + * @return + */ + public static DialogBox getUserDialog(String text, Image img) { + assert text != null: "Text cannot be null."; + assert img != null: "Image cannot be null."; + return new DialogBox(text, img); + } + + /** + * Returns a DialogBox instance when the chatbot responds to the user's input. + * + * @param text + * @param img + * @return + */ + public static DialogBox getKieDialog(String text, Image img) { + assert text != null: "Text cannot be null."; + assert img != null: "Image cannot be null."; + var db = new DialogBox(text, img); + db.flip(); + return db; + } +} diff --git a/src/main/java/kietwoforone/gui/Launcher.java b/src/main/java/kietwoforone/gui/Launcher.java new file mode 100644 index 0000000000..140be92839 --- /dev/null +++ b/src/main/java/kietwoforone/gui/Launcher.java @@ -0,0 +1,13 @@ +package kietwoforone.gui; + +import javafx.application.Application; +import kietwoforone.gui.Main; + +/** + * 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/kietwoforone/gui/Main.java b/src/main/java/kietwoforone/gui/Main.java new file mode 100644 index 0000000000..26af2b10f2 --- /dev/null +++ b/src/main/java/kietwoforone/gui/Main.java @@ -0,0 +1,42 @@ +package kietwoforone.gui; + +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; +import kietwoforone.KieTwoForOne; + +/** + * A GUI for KieTwoForOne using FXML. + */ +public class Main extends Application { + + private KieTwoForOne kie = new KieTwoForOne(); + + /** + * Sets up the primary stage and initializes the user interface when the application is launched. + * + * @param stage the primary stage for this application, onto which + * the application scene can be set. + * Applications may create other stages, if needed, but they will not be + * primary stages. + */ + @Override + public void start(Stage stage) { + assert stage != null: "Stage cannot be null"; + try { + FXMLLoader fxmlLoader = new FXMLLoader(Main.class.getResource("/view/MainWindow.fxml")); + AnchorPane ap = fxmlLoader.load(); + Scene scene = new Scene(ap); + stage.setTitle("KieTwoForOne"); + stage.setScene(scene); + fxmlLoader.getController().setKie(kie); + stage.show(); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/kietwoforone/gui/MainWindow.java b/src/main/java/kietwoforone/gui/MainWindow.java new file mode 100644 index 0000000000..c27fc02949 --- /dev/null +++ b/src/main/java/kietwoforone/gui/MainWindow.java @@ -0,0 +1,73 @@ +package kietwoforone.gui; + +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; +import kietwoforone.KieTwoForOne; +import kietwoforone.exceptions.KieTwoForOneException; +import kietwoforone.storage.Storage; +import kietwoforone.ui.UI; + +/** + * Controller for the main GUI. + */ +public class MainWindow extends AnchorPane { + @FXML + private ScrollPane scrollPane; + @FXML + private VBox dialogContainer; + @FXML + private TextField userInput; + @FXML + private Button sendButton; + + private KieTwoForOne kie; + + private Image userImage = new Image(this.getClass().getResourceAsStream("/images/userImage.png")); + private Image chatbotImage = new Image(this.getClass().getResourceAsStream("/images/chatbotImage.png")); + + @FXML + public void initialize() { + scrollPane.vvalueProperty().bind(dialogContainer.heightProperty()); + dialogContainer.getChildren().addAll( + DialogBox.getKieDialog("Hello! I'm KieTwoForOne.\n" + "What can I do for you?", chatbotImage) + ); + } + + /** + * Injects the KieTwoForOne instance and loads data from the tasks.txt file. + */ + public void setKie(KieTwoForOne k) { + kie = k; + try { + Storage storage = kie.getStorage(); + storage.loadFile(kie.getTasks().getTaskList()); + } catch (KieTwoForOneException e) { + dialogContainer.getChildren().addAll( + DialogBox.getKieDialog(e.getMessage(), chatbotImage)); + } + } + + /** + * Creates two dialog boxes, one echoing user input and the other containing KieTwoForOne's reply and then appends them to + * the dialog container. Clears the user input after processing. + */ + @FXML + private void handleUserInput() { + String input = userInput.getText(); + String response = kie.getResponse(input); + dialogContainer.getChildren().addAll( + DialogBox.getUserDialog(input, userImage), + DialogBox.getKieDialog(response, chatbotImage) + ); + if (response == "Bye. Hope to see you again soon!") { + Platform.exit(); + } + userInput.clear(); + } +} diff --git a/src/main/java/kietwoforone/parser/Parser.java b/src/main/java/kietwoforone/parser/Parser.java new file mode 100644 index 0000000000..bf411eac2a --- /dev/null +++ b/src/main/java/kietwoforone/parser/Parser.java @@ -0,0 +1,156 @@ +package kietwoforone.parser; + +import kietwoforone.commands.*; +import kietwoforone.exceptions.KieTwoForOneException; +import kietwoforone.tasks.Deadline; +import kietwoforone.tasks.Event; +import kietwoforone.tasks.Todo; +import java.time.DateTimeException; + +/** + * Represents the logic through which the chatbot understands and carries out instructions + * input by the user. + */ +public class Parser { + + /** + * Represents the valid commands that can be inputed by the user. + */ + public enum Instructions { + LIST, MARK, UNMARK, BYE, TODO, EVENT, DEADLINE, DELETE, DATE, FIND, TAG + } + + /** + * Throws a KieTwoForOne exception when the user only inputs a command that is + * not "list" or "bye" with no additional details. + * + * @param input + * @return Boolean + * @throws KieTwoForOneException + */ + public static boolean isCompleteInput(String[] input) throws KieTwoForOneException { + assert input != null: "Input array cannot be null."; + if (input.length < 2 && !input[0].equalsIgnoreCase("list") && + !input[0].equalsIgnoreCase("bye")) { + throw new KieTwoForOneException("Your instruction is incomplete!"); + } + return true; + } + + /** + * Throws a KieTwoForOne exception when the user adds an incorrect event with missing details. + * + * @param input + * @return Boolean + * @throws KieTwoForOneException + */ + public static boolean isCompleteEventInput(String[] input) throws KieTwoForOneException { + assert input != null: "Input array cannot be null."; + if (input.length != 3) { + throw new KieTwoForOneException("Please input a start and end time!"); + } + return true; + } + + /** + * Throws a KieTwoForOne exception when the user adds an incorrect deadline with missing details. + * + * @param input + * @return Boolean + * @throws KieTwoForOneException + */ + public static boolean isCompleteDeadlineInput(String[] input) throws KieTwoForOneException { + assert input != null: "Input array cannot be null."; + if (input.length != 2) { + throw new KieTwoForOneException("Please input a deadline!"); + } + return true; + } + + /** + * Returns a new command object depending on the specified keywords in the command string. + * Throws a KieTwoForOne exception when an invalid command string is inputted by the user. + * + * @param command + * @return Command + * @throws KieTwoForOneException + */ + public static Command parse(String command) throws KieTwoForOneException { + assert command != null: "Command cannot be null"; + String[] instruction = command.split(" ", 2); + String[] taskDetails = new String[0]; + + try { + Instructions.valueOf(instruction[0].toUpperCase()); + isCompleteInput(instruction); + } catch (IllegalArgumentException e) { + throw new KieTwoForOneException("Invalid input!"); + } catch (KieTwoForOneException e) { + throw new KieTwoForOneException(e.getMessage()); + } + + if (instruction.length > 1) { + taskDetails = instruction[1].split(" /", 0); + } + + switch (Instructions.valueOf(instruction[0].toUpperCase())) { + case LIST: + return new ListCommand(); + case BYE: + return new ByeCommand(); + case MARK: + return new MarkCommand(Integer.valueOf(instruction[1])); + case UNMARK: + return new UnmarkCommand(Integer.valueOf(instruction[1])); + case TAG: + try { + return new TagCommand(Integer.valueOf(taskDetails[0]), taskDetails[1]); + } catch (NumberFormatException e) { + throw new KieTwoForOneException("Please input your instruction in the format: tag (index) /(tag)"); + } catch (ArrayIndexOutOfBoundsException e) { + throw new KieTwoForOneException("Please input a valid index and tag!"); + } + case TODO: + return new AddCommand(new Todo(instruction[1])); + case EVENT: + try { + isCompleteEventInput(taskDetails); + } catch (KieTwoForOneException e) { + throw new KieTwoForOneException(e.getMessage()); + } + try { + return new AddCommand(new Event(taskDetails[0], taskDetails[1], taskDetails[2])); + } catch (DateTimeException e) { + throw new KieTwoForOneException("Date must be valid and in the form YYYY-MM-DD!"); + } catch (ArrayIndexOutOfBoundsException e) { + throw new KieTwoForOneException("Please input a date and time for your start and end time!"); + } + case DEADLINE: + try { + isCompleteDeadlineInput(taskDetails); + } catch (KieTwoForOneException e) { + throw new KieTwoForOneException(e.getMessage()); + } + try { + return new AddCommand(new Deadline(taskDetails[0], taskDetails[1])); + } catch (DateTimeException e) { + throw new KieTwoForOneException("Date must be valid and in the form YYYY-MM-DD!"); + } catch (ArrayIndexOutOfBoundsException e) { + throw new KieTwoForOneException("Please input a date and time for your deadline!"); + } + case DELETE: + return new DeleteCommand(Integer.valueOf(instruction[1])); + case DATE: + try { + return new FindDateCommand(instruction[1]); + } catch (IndexOutOfBoundsException e) { + throw new KieTwoForOneException("Please input a valid date!"); + } + case FIND: + return new FindWordCommand(instruction[1]); + default: + throw new KieTwoForOneException("Invalid input!"); + } + } + +} diff --git a/src/main/java/kietwoforone/storage/Storage.java b/src/main/java/kietwoforone/storage/Storage.java new file mode 100644 index 0000000000..9d3eb5638a --- /dev/null +++ b/src/main/java/kietwoforone/storage/Storage.java @@ -0,0 +1,78 @@ +package kietwoforone.storage; + +import kietwoforone.exceptions.KieTwoForOneException; +import kietwoforone.tasks.Task; +import java.io.EOFException; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.io.ObjectInputStream; +import java.util.ArrayList; + +/** + * Represents the methods used to store and load date inputted by the user into a text file. + */ +public class Storage { + + private static String filePath = "data/tasks.txt"; + + /** + * Saves data to a file by writing the task objects to tasks.txt file. + * Throws a KieTwoForOneException exception when the file being saved to does not exist. + * + * @param tasks + * @throws KieTwoForOneException + */ + public static void saveFile(ArrayList tasks) throws KieTwoForOneException { + assert tasks != null: "TaskList cannot be null."; + try { + ObjectOutputStream fileSaver = new ObjectOutputStream(new FileOutputStream(filePath)); + for (int i = 0; i < tasks.size(); i++) { + fileSaver.writeObject(tasks.get(i)); + } + fileSaver.close(); + } catch (IOException e) { + throw new KieTwoForOneException("File not found!"); + } + } + + /** + * Loads data from the tasks.txt file and adds the created objects to the task list. + * If the directory containing the tasks.txt file does not exist, create the directory and the file, + * Throws a KieTwoForOneException exception when the file being loaded from does not exist. + * Throws a KieTwoForOneException exception when the object being loaded is not a Task object. + * + * @param tasks + * @throws KieTwoForOneException + */ + public static void loadFile(ArrayList tasks) throws KieTwoForOneException { + assert tasks != null: "TaskList cannot be null."; + try { + File file = new File(filePath); + File folder = file.getParentFile(); + if (folder != null && !folder.exists()) { + folder.mkdirs(); + } + if (!file.exists()) { + file.createNewFile(); + } + ObjectInputStream fileLoader = new ObjectInputStream(new FileInputStream(filePath)); + while (true) { + try { + Task newTask = (Task) fileLoader.readObject(); + tasks.add(newTask); + } catch (EOFException e) { + break; + } + } + fileLoader.close(); + } catch (IOException e) { + throw new KieTwoForOneException("File not found! Creating new storage file."); + } catch (ClassNotFoundException e) { + throw new KieTwoForOneException("Not a task!"); + } + } + +} diff --git a/src/main/java/kietwoforone/tasks/Deadline.java b/src/main/java/kietwoforone/tasks/Deadline.java new file mode 100644 index 0000000000..9a041c05cd --- /dev/null +++ b/src/main/java/kietwoforone/tasks/Deadline.java @@ -0,0 +1,119 @@ +package kietwoforone.tasks; + +import kietwoforone.exceptions.KieTwoForOneException; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; + +/** + * Represents a deadline task that has a deadline time. + */ +public class Deadline extends Task { + + private LocalDate deadlineDate; + private String deadlineTime; + private Task task; + private String tag = ""; + + /** + * Constructor for a new Deadline object. + * + * @param description + * @param deadline + */ + public Deadline(String description, String deadline) { + super(description); + assert description != null: "Description cannot be null"; + assert deadline != null: "Deadline cannot be null"; + this.task = new Task(this.description); + String[] time = deadline.replaceFirst("by ", "").split(" ", 2); + this.deadlineDate = LocalDate.parse(time[0]); + this.deadlineTime = time[1]; + } + + /** + * Returns the date in the form of a string of the format "MMM d yyyy". + * + * @return date + */ + public String getDate() { + return this.deadlineDate.format(DateTimeFormatter.ofPattern("MMM d yyyy")); + } + + /** + * Returns a string representation of a deadline that has been marked. + * + * @return marked deadline + */ + @Override + public String markTask() { + return String.format("[D] %s (by: %s %s)", this.task.markTask(), + this.getDate(), this.deadlineTime); + } + + /** + * Returns a string representation of a deadline that has been unmarked. + * + * @return unmarked deadline + */ + @Override + public String unmarkTask() { + return String.format("[D] %s (by: %s %s)", this.task.unmarkTask(), + this.getDate(), this.deadlineTime); + } + + /** + * Returns a boolean value when comparing the deadline with the date string inputed. + * Returns true if the dates are the same, and false otherwise. + * Throws a KieTwoForOne exception when the users input an incorrect format for the date. + * + * @param date + * @return Boolean + * @throws KieTwoForOneException + */ + @Override + public boolean compareDate(String date) throws KieTwoForOneException { + assert date != null: "Date cannot be null"; + try { + return this.deadlineDate.equals(LocalDate.parse(date)); + } catch (DateTimeParseException e) { + throw new KieTwoForOneException("Date must be valid and in the form YYYY-MM-DD!"); + } + } + + /** + * Compares the details of the Deadline with the specified string input. + * Returns true if the task contains the keyword, false otherwise. + * + * @param keyword + * @return Boolean. + */ + @Override + public boolean compareString(String keyword) { + assert keyword != null: "Keyword cannot be null."; + return this.task.compareString(keyword); + } + + /** + * Returns the string representation of task with a tag. + * + * @param tag + * @return + */ + @Override + public String addTag(String tag) { + this.tag = tag; + return this.toString(); + } + + /** + * Returns the string representation of the Deadline object. + * + * @return String representation of the Deadline event. + */ + @Override + public String toString() { + return String.format("[D] %s (by: %s %s) [%s]", this.task.toString(), this.getDate(), this.deadlineTime, this.tag); + } + +} diff --git a/src/main/java/kietwoforone/tasks/Event.java b/src/main/java/kietwoforone/tasks/Event.java new file mode 100644 index 0000000000..7870334cca --- /dev/null +++ b/src/main/java/kietwoforone/tasks/Event.java @@ -0,0 +1,133 @@ +package kietwoforone.tasks; + +import kietwoforone.exceptions.KieTwoForOneException; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; + +/** + * Represents an event that has a start and end time. + */ +public class Event extends Task{ + + private LocalDate startDate; + private String startTime; + private LocalDate endDate; + private String endTime; + private Task task; + private String tag = ""; + /** + * Constructor for a new Event object. + * + * @param description + * @param start + * @param end + */ + public Event(String description, String start, String end) { + super(description); + assert description != null: "Description cannot be null."; + assert start != null: "Start time cannot be null."; + assert end != null: "End time cannot be null."; + this.task = new Task(this.description); + + String[] beginning = start.replaceFirst("from ", "").split(" ", 2); + this.startDate = LocalDate.parse(beginning[0]); + this.startTime = beginning[1]; + + String[] ending = end.replaceFirst("to ", "").split(" ", 2); + this.endDate = LocalDate.parse(ending[0]); + this.endTime = ending[1]; + } + + /** + * Returns a string representation of the start date in the format "MMM d yyyy". + * + * @return Start date + */ + public String getStartDate() { + return this.startDate.format(DateTimeFormatter.ofPattern("MMM d yyyy")); + } + + /** + * Returns a string representation of the end date in the format "MMMM d yyyy". + * + * @return End date. + */ + public String getEndDate() { + return this.endDate.format(DateTimeFormatter.ofPattern("MMM d yyyy")); + } + + /** + * Returns a String representation of a marked event. + * + * @return Marked event. + */ + @Override + public String markTask() { + return String.format("[E] %s (from: %s %s to: %s %s)", this.task.markTask(), + this.getStartDate(), this.startTime, this.getEndDate(), this.endTime); + } + + /** + * Return a String representation of an unmarked event. + * + * @return Unmarked event. + */ + @Override + public String unmarkTask() { + return String.format("[E] %s (from: %s %s to: %s %s)", this.task.unmarkTask() , + this.getStartDate(), this.startTime, this.getEndDate(), this.endTime); + } + + /** + * Returns a boolean value when comparing the deadline with the date string inputed. + * Returns true if the date is within the start and end date inclusive, and false otherwise. + * Throws a KieTwoForOne exception when the users input an incorrect format for the date. + * + * @param date + * @return Boolean + * @throws KieTwoForOneException + */ + @Override + public boolean compareDate(String date) throws KieTwoForOneException { + assert date != null: "Date cannot be null."; + LocalDate newDate; + try { + newDate = LocalDate.parse(date); + } catch (DateTimeParseException e) { + throw new KieTwoForOneException("Date must be valid and in the form YYYY-MM-DD!"); + } + return (this.startDate.isBefore(newDate) && this.endDate.isAfter(newDate)) || + this.startDate.equals(newDate) || this.endDate.equals(newDate); + } + + /** + * Compares the details of the Event with the specified string input. + * Returns true if the task contains the keyword, false otherwise. + * + * @param keyword + * @return Boolean. + */ + @Override + public boolean compareString(String keyword) { + assert keyword != null: "Keyword cannot be null"; + return this.task.compareString(keyword); + } + + @Override + public String addTag(String tag) { + this.tag = tag; + return this.toString(); + } + + /** + * Returns the string representation of the Event object. + * + * @return String representation of the Event object. + */ + @Override + public String toString() { + return String.format("[E] %s (from: %s %s to: %s %s) [%s]", this.task.toString(), this.getStartDate(), this.startTime, this.getEndDate(), this.endTime, this.tag); + } + +} diff --git a/src/main/java/kietwoforone/tasks/Task.java b/src/main/java/kietwoforone/tasks/Task.java new file mode 100644 index 0000000000..88eb3200e4 --- /dev/null +++ b/src/main/java/kietwoforone/tasks/Task.java @@ -0,0 +1,111 @@ +package kietwoforone.tasks; + +import kietwoforone.exceptions.KieTwoForOneException; +import java.io.Serializable; + +/** + * Represents a general task. + * Implements Serializable so that to allow saving and loading from txt file. + */ +public class Task implements Serializable { + + protected String description; + protected boolean isDone; + private String tag = ""; + + /** + * Constructor for a new Task object. + * + * @param description + */ + public Task(String description) { + assert description != null: "Description cannot be null."; + this.description = description; + this.isDone = false; + } + + /** + * Checks if isDone is true, marks the task with an "X". + * Marks the task with " " otherwise. + * + * @return Mark or blank space + */ + public String getStatusIcon() { + return (isDone ? "X" : " "); + } + + /** + * Sets isDone to true. + */ + public void setTrue() { + this.isDone = true; + } + + /** + * Sets isDone to false. + */ + public void setFalse() { + this.isDone = false; + } + + /** + * Sets isDone to true and returns the string representation of the task. + * + * @return String representation of the marked task. + */ + public String markTask() { + this.setTrue(); + return this.toString(); + } + + /** + * Sets isDone to false and returns the string representation of the task. + * + * @return + */ + public String unmarkTask() { + this.setFalse(); + return this.toString(); + } + + /** + * Compares the date of the task with the specified string input. + * Since this is a general task with no date return false. + * + * @param date + * @return False + * @throws KieTwoForOneException + */ + public boolean compareDate(String date) throws KieTwoForOneException { + assert date != null: "Date cannot be null."; + return false; + } + + /** + * Compares the details of the task with the specified string input. + * Returns true if the task contains the keyword, false otherwise. + * + * @param keyword + * @return Boolean. + */ + public boolean compareString(String keyword) { + assert keyword != null: "Keyword cannot be null."; + return this.description.contains(keyword); + } + + public String addTag(String tag) { + this.tag = tag; + return this.toString(); + } + + /** + * Returns the string representation of the task, including its status and the description of the task. + * + * @return String representation of the task. + */ + @Override + public String toString() { + return String.format("[%s] %s", this.getStatusIcon(), this.description); + } + +} diff --git a/src/main/java/kietwoforone/tasks/TaskList.java b/src/main/java/kietwoforone/tasks/TaskList.java new file mode 100644 index 0000000000..9b9cd755fc --- /dev/null +++ b/src/main/java/kietwoforone/tasks/TaskList.java @@ -0,0 +1,99 @@ +package kietwoforone.tasks; + +import kietwoforone.exceptions.KieTwoForOneException; +import java.util.ArrayList; + +/** + * Represents and ArrayList of Tasks. + */ +public class TaskList { + + private ArrayList tasks; + + /** + * Constructor for a TaskList object. + * + * @param tasks + */ + public TaskList(ArrayList tasks) { + assert tasks != null: "Tasks cannot be null."; + this.tasks = tasks; + } + + /** + * Returns the ArrayList. + * + * @return Task ArrayList. + */ + public ArrayList getTaskList() { + return this.tasks; + } + + /** + * Adds a new task to the ArrayList. + * + * @param newTask + */ + public void addTasks(Task newTask) { + this.tasks.add(newTask); + } + + /** + * Removes a task from the ArrayList and returns the task. + * + * @param position + * @return Task. + * @throws KieTwoForOneException + */ + public Task deleteTask(int position) throws KieTwoForOneException { + assert position >= 0: "Array position cannot be less than 0."; + try { + return this.tasks.remove(position - 1); + } catch (IndexOutOfBoundsException e) { + throw new KieTwoForOneException(String.format("No task at index %d!", position)); + } + } + + /** + * Returns a task string with the task marked. + * + * @param position + * @return Task string. + * @throws KieTwoForOneException + */ + public String markTask(int position) throws KieTwoForOneException { + assert position >= 0: "Array position cannot be less than 0."; + try { + return this.tasks.get(position - 1).markTask(); + } catch (IndexOutOfBoundsException e) { + throw new KieTwoForOneException("Task does not exist!"); + } + } + + /** + * Returns a task string with the task unmarked. + * + * @param position + * @return Task string. + * @throws KieTwoForOneException + */ + public String unmarkTask(int position) throws KieTwoForOneException { + assert position >= 0: "Array position cannot be less than 0."; + try { + return this.tasks.get(position - 1).unmarkTask(); + } catch (IndexOutOfBoundsException e) { + throw new KieTwoForOneException("Task does not exist!"); + } + } + + public String addTag(int position, String tag) throws KieTwoForOneException { + assert position >= 0: "Array position cannot be less than 0."; + assert tag != null: "Tag cannot be null"; + try { + return tasks.get(position - 1).addTag(tag); + } catch (IndexOutOfBoundsException e) { + throw new KieTwoForOneException("Task does not exist!"); + } + } + +} diff --git a/src/main/java/kietwoforone/tasks/Todo.java b/src/main/java/kietwoforone/tasks/Todo.java new file mode 100644 index 0000000000..9dccb2828f --- /dev/null +++ b/src/main/java/kietwoforone/tasks/Todo.java @@ -0,0 +1,83 @@ +package kietwoforone.tasks; + +/** + * Represents a task with no deadline. + */ +public class Todo extends Task { + + private Task task; + private String tag = ""; + + /** + * Constructor for new Todo object. + * + * @param description + */ + public Todo(String description) { + super(description); + assert description != null: "Description cannot be null."; + this.task = new Task(this.description); + } + + /** + * Returns a Todo string that is marked. + * + * @return Todo string. + */ + @Override + public String markTask() { + return String.format("[T] %s", this.task.markTask()); + } + + /** + * Returns a Todo string that is unmarked. + * + * @return Todo string. + */ + @Override + public String unmarkTask() { + return String.format("[T] %s", this.task.unmarkTask()); + } + + /** + * Returns a boolean. + * + * @param date + * @return False. + */ + @Override + public boolean compareDate(String date) { + assert date != null: "Date cannot be null."; + return false; + } + + /** + * Compares the details of the Todo with the specified string input. + * Returns true if the task contains the keyword, false otherwise. + * + * @param keyword + * @return Boolean. + */ + @Override + public boolean compareString(String keyword) { + assert keyword != null: "Keyword cannot be null"; + return this.task.compareString(keyword); + } + + @Override + public String addTag(String tag) { + this.tag = tag; + return this.toString(); + } + + /** + * Returns the string representation of Todo object. + * + * @return String representation of the Todo object. + */ + @Override + public String toString() { + return String.format("[T] %s [%s]", this.task.toString(), this.tag); + } + +} diff --git a/src/main/java/kietwoforone/ui/UI.java b/src/main/java/kietwoforone/ui/UI.java new file mode 100644 index 0000000000..2dde046679 --- /dev/null +++ b/src/main/java/kietwoforone/ui/UI.java @@ -0,0 +1,200 @@ +package kietwoforone.ui; + +import kietwoforone.exceptions.KieTwoForOneException; +import kietwoforone.tasks.Task; +import java.util.ArrayList; +import java.util.Scanner; + +/** + * Represents what the chatbot displays depending on user input. + */ +public class UI { + + private static String separationLine = "_________________________________________"; + private static String chatBotName = "KieTwoForOne"; + private static Scanner scanner; + private static String response; + /** + * Constructor for UI Object. + * Initialises a scanner to read keyboard input. + */ + public UI() { + this.scanner = new Scanner(System.in); + } + + /** + * Returns the string obtained from the input keyed in by the user. + * + * @return User input. + */ + public static String readCommand() { + return scanner.nextLine(); + } + + /** + * Prints the separation line. + */ + public static void showLine() { + System.out.println(separationLine); + } + + /** + * Prints the welcome message. + */ + public static void showWelcome() { + showLine(); + response = "Hello! I'm " + chatBotName + "." + "\n" + "What can I do for you?"; + System.out.println(response); + showLine(); + } + + /** + * Prints the message displayed when a task is added to the ArrayList. + * + * @param tasks + * @param newTask + */ + public void showAddTasks(ArrayList tasks, Task newTask) { + assert tasks != null: "Tasks cannot be null."; + assert newTask != null: "NewTask cannot be null."; + response = "Got it. I've added this task:\n" + " " + newTask.toString() + "\n" + + String.format("Now you have %d tasks in the list.", tasks.size()); + System.out.println(response); + } + + /** + * Prints the message displayed when a task is deleted from the ArrayList. + * + * @param tasks + * @param removedTask + */ + public void showDeleteTask(ArrayList tasks, Task removedTask) { + assert tasks != null: "Tasks cannot be null."; + assert removedTask != null: "RemovedTask cannot be null."; + response = "Noted. I've removed the task:\n" + " " + removedTask.toString() + "\n" + + String.format("Now you have %d tasks in the list.", tasks.size()); + System.out.println(response); + } + + /** + * Prints the task list. + * + * @param tasks + */ + public void showTaskList(ArrayList tasks) { + assert tasks != null: "Tasks cannot be null."; + String currString = "Here are the tasks in your list:\n"; + for (int i = 0; i < tasks.size(); i++) { + currString = currString + String.format("%d. %s\n", i + 1, tasks.get(i).toString()); + } + response = currString; + System.out.print(response); + } + + /** + * Prints the message when a task is marked. + * + * @param task + */ + public void showMarkTask(String task) { + assert task != null: "Task cannot be null."; + response = "Nice! I've marked this task as done:\n" + " " + task; + System.out.println(response); + } + + /** + * Prints the message when a task is unmarked. + * + * @param task + */ + public void showUnmarkTask(String task) { + assert task != null: "Task cannot be null."; + response = "OK. I've marked this task as incomplete:\n" + " " + task; + System.out.println(response); + } + + public void showTaggedTask(String task) { + assert task != null: "Task cannot be null."; + response = "OK. I've tagged the following task:\n" + " " + task; + System.out.println(response); + } + + /** + * Prints all the tasks where the inputted date lies within. + * + * @param tasks + * @param date + * @throws KieTwoForOneException + */ + public void showSameDate(ArrayList tasks, String date) throws KieTwoForOneException { + assert tasks != null: "Tasks cannot be null."; + assert date != null: "Date cannot be null."; + ArrayList taskList = new ArrayList<>(100); + for (int i = 0; i < tasks.size(); i++) { + Task currTask = tasks.get(i); + if (currTask.compareDate(date)) { + taskList.add(currTask); + } + } + String currString = "Here are the tasks occurring on this date:\n"; + for (int i = 0; i < taskList.size(); i++) { + currString = currString + String.format("%d. %s\n", i + 1, taskList.get(i).toString()); + } + response = currString; + System.out.print(response); + } + + /** + * Prints all the tasks that contains the keyword. + * + * @param tasks + * @param keyword + * @throws KieTwoForOneException + */ + public void showMatchingTask(ArrayList tasks, String keyword) { + assert tasks != null: "Tasks cannot be null."; + assert keyword != null: "Keyword cannot be null."; + ArrayList taskList= new ArrayList<>(100); + for (int i = 0; i < tasks.size(); i++) { + Task currTask = tasks.get(i); + if (currTask.compareString(keyword)) { + taskList.add(currTask); + } + } + String currString = "Here are the matching tasks in your list:\n"; + for (int i = 0; i < taskList.size(); i++) { + currString = currString + String.format("%d. %s\n", i + 1, taskList.get(i).toString()); + } + response = currString; + System.out.print(response); + } + + /** + * Prints the error message when an exception is thrown. + * + * @param e + */ + public void showErrorMessage(KieTwoForOneException e) { + assert e != null: "e cannot be null."; + response = e.getMessage(); + System.out.println(response); + } + + /** + * Prints the message when the user exits the chatbot. + */ + public void showBye() { + response = "Bye. Hope to see you again soon!"; + System.out.println(response); + } + + /** + * Returns the chatbot response to the user's input. + * + * @return + */ + public String getResponse() { + return response; + } + +} diff --git a/src/main/resources/images/chatbotImage.png b/src/main/resources/images/chatbotImage.png new file mode 100644 index 0000000000..79f67456af Binary files /dev/null and b/src/main/resources/images/chatbotImage.png differ diff --git a/src/main/resources/images/userImage.png b/src/main/resources/images/userImage.png new file mode 100644 index 0000000000..2a0a2ee52c Binary files /dev/null and b/src/main/resources/images/userImage.png differ diff --git a/src/main/resources/view/DialogBox.fxml b/src/main/resources/view/DialogBox.fxml new file mode 100644 index 0000000000..fe672c90ea --- /dev/null +++ b/src/main/resources/view/DialogBox.fxml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml new file mode 100644 index 0000000000..2386792878 --- /dev/null +++ b/src/main/resources/view/MainWindow.fxml @@ -0,0 +1,44 @@ + + + + + + + + + + + +