diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml new file mode 100644 index 0000000000..47972eb0ac --- /dev/null +++ b/.github/workflows/gradle.yml @@ -0,0 +1,50 @@ +name: Java CI + +on: [push, pull_request] + +jobs: + build: + strategy: + matrix: + platform: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{ matrix.platform }} + + steps: + - name: Set up repository + uses: actions/checkout@master + + - name: Set up repository + uses: actions/checkout@master + with: + ref: master + + - name: Merge to master + run: git checkout --progress --force ${{ github.sha }} + + - name: Validate Gradle Wrapper + uses: gradle/wrapper-validation-action@v1 + + - name: Setup JDK 11 + uses: actions/setup-java@v1 + with: + java-version: "11" + java-package: jdk+fx + + - name: Build and check with Gradle + run: ./gradlew check + + # - name: Perform IO redirection test (*NIX) + # if: runner.os == 'Linux' + # working-directory: ${{ github.workspace }}/text-ui-test + # run: ./runtest.sh + + # - name: Perform IO redirection test (MacOS) + # if: always() && runner.os == 'macOS' + # working-directory: ${{ github.workspace }}/text-ui-test + # run: ./runtest.sh + + # - name: Perform IO redirection test (Windows) + # if: always() && runner.os == 'Windows' + # working-directory: ${{ github.workspace }}/text-ui-test + # shell: cmd + # run: runtest.bat diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000..a882e57307 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "java.configuration.updateBuildConfiguration": "automatic", + "java.checkstyle.configuration": "${workspaceFolder}\\config\\checkstyle\\checkstyle.xml" +} diff --git a/README.md b/README.md index 8715d4d915..a1dd614ac8 100644 --- a/README.md +++ b/README.md @@ -13,12 +13,4 @@ Prerequisites: JDK 11, update Intellij to the most recent version. 1. If there are any further prompts, accept the defaults. 1. Configure the project to use **JDK 11** (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 - ____ _ - | _ \ _ _| | _____ - | | | | | | | |/ / _ \ - | |_| | |_| | < __/ - |____/ \__,_|_|\_\___| - ``` +1. After that, locate the `src/main/java/Launcher.java` file, right-click it, and choose `Run Launcher.main()` (if the code editor is showing compile errors, try restarting the IDE). diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000000..507751640a --- /dev/null +++ b/build.gradle @@ -0,0 +1,63 @@ +plugins { + id 'java' + id 'application' + id 'checkstyle' + id 'org.openjfx.javafxplugin' version '0.0.10' + id 'com.github.johnrengelman.shadow' version '5.1.0' +} + +repositories { + mavenCentral() +} + +dependencies { + testImplementation('org.junit.platform:junit-platform-launcher:1.5.2') + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.6.0' + testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.6.0' +} + +jar { + manifest { + attributes( + 'Main-Class': 'duke.Launcher' + ) + } +} + +javafx { + version = "11" + modules = [ 'javafx.controls', 'javafx.fxml' ] +} + +test { + useJUnitPlatform() + + testLogging { + events "passed", "skipped", "failed" + + showExceptions true + exceptionFormat "full" + showCauses true + showStackTraces true + showStandardStreams = false + } +} + +application { + mainClassName = "duke.Launcher" +} + +shadowJar { + archiveBaseName = "duke" + archiveClassifier = null +} + +checkstyle { + configFile = file('./config/checkstyle/checkstyle.xml') + toolVersion = '8.29' +} + +run{ + standardInput = System.in + enableAssertions = true +} \ No newline at end of file diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml new file mode 100644 index 0000000000..e31724b2e4 --- /dev/null +++ b/config/checkstyle/checkstyle.xml @@ -0,0 +1,398 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/README.md b/docs/README.md index 8077118ebe..f146b049bb 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,29 +1,370 @@ -# User Guide +# -## Features +# **User Guide** _for Jin Lin's Duke_ -### Feature-ABC +#### _Duke manages your list of tasks for you. Be it to-dos, deadlines or events, Duke's got you._ -Description of the feature. +# -### Feature-XYZ +## **Capabilities** -Description of the feature. +# -## Usage +### Track your list of tasks with their details! -### `Keyword` - Describe action +- Note down the tasks that you need to do and the relevant date and time applicable to them! -Describe the action and its outcome. + The types of tasks supported are: -Example of usage: + - To-dos _(with no date time details)_ + - Deadlines _(with details on the date and time of the deadline)_ + - Events (with details on the date and time of the event) -`keyword (optional arguments)` +- Manage the individual tasks in the list of tasks through the following actions: + - Marking the task as completed + - Updating the task description + - Updating the task date and line _(if applicable)_ + - Deleting the task +- Store the tasks in a local file to be loaded the next time the application is launched. -Expected outcome: +# -Description of the outcome. +## **Usage** + +# + +### **`list` - shows the list of tasks** + +_Prints out the list of tasks recorded. The message is limited to the first 4 tasks recorded._ + +#### **Example of usage:** + +`list` + +#### **Example outcome:** + +A message listing the list of tasks. + +If there are no tasks, this will be expected: + +``` +There are currently no elements in the list! +``` + +otherwise, this will be expected: + +``` +These are the tasks currently stored in the list: +1. Todo - Cut hair +2. Deadline - CS2103/T iP (by Feb 18 2022 11.59pm) +3. ... +``` + +# + +### **`todo (description)` - logs a new To-do** + +_Records a new To-do task to the list of tasks._ + +#### **Example of usage:** + +`todo Cut hair` + +#### **Example outcome:** + +A new To-do task to be added to the list of tasks. + +This will be expected: ``` -expected output +Got it. I've added this task: +Todo - Cut hair +Now you have 2 tasks in the list. + +> list + +These are the tasks currently stored in the list: +1. ... +2. Todo - Cut Hair ``` + +# + +### **`deadline (description) /by (date) (time)` - logs a new Deadline** + +_Records a new Deadline task to the list of tasks._ + +_Date input has to be in the format: yyyy-mm-dd e.g. 2022-04-18._ + +_Time input has to be in the 24-hour format: hh:mm e.g. 19:33._ + +#### **Example of usage:** + +`deadline Homework Submission /by 2022-01-13 23:59` + +#### **Example outcome:** + +A new Deadline task to be added to the list of tasks. + +This will be expected: + +``` +Got it. I've added this task: +Deadline - Homework Submission (by Jan 13 2022 11.59pm) +Now you have 1 tasks in the list. + +> list + +These are the tasks currently stored in the list: +1. Deadline - Homework Submission (by Jan 13 2022 11.59pm) +``` + +# + +### **`event (description) /at (date) (time)` - logs a new Event** + +_Records a new Event task to the list of tasks._ + +_Date input has to be in the format: yyyy-mm-dd e.g. 2022-04-18._ + +_Time input has to be in the 24-hour format: hh:mm e.g. 19:33._ + +#### **Example of usage:** + +`event Module Briefing /at 2022-02-21 09:00` + +#### **Example outcome:** + +A new Event task to be added to the list of tasks. + +This will be expected: + +``` +Got it. I've added this task: +Event - Module Briefing (at Feb 21 2022 09.00am) +Now you have 1 tasks in the list. + +> list + +These are the tasks currently stored in the list: +1. Event - Module Briefing (at Feb 21 2022 09.00am) +``` + +# + +### **`delete (index)` - deletes the indexed task** + +_Deletes the task at the given index._ + +#### **Example of usage:** + +`delete 1` + +#### **Example outcome:** + +The task at index 1 will be deleted. + +This will be expected: + +``` +Noted. I've removed this task: +Event - Module Briefing (at Feb 21 2022 09.00am) +Now you have 0 tasks in the list. + +> list + +There are currently no elements in the list! +``` + +# + +### **`mark (index)` - marks the indexed task as done** + +_Marks the task at the given index as Done._ + +#### **Example of usage:** + +`mark 1` + +#### **Example outcome:** + +The task at index 1 will be marked as done. + +If the task has been previously marked as done, this will be expected: + +``` +The task is already marked. + +> list + +These are the tasks currently stored in the list: +1. !!!DONE!!! Event - Module Briefing (at Feb 21 2022 09.00am) +``` + +else, this will be expected: + +``` +Nice! I've marked this task as done: +!!!DONE!!! Event - Module Briefing (at Feb 21 2022 09.00am) + +> list + +These are the tasks currently stored in the list: +1. !!!DONE!!! Event - Module Briefing (at Feb 21 2022 09.00am) +``` + +# + +### **`unmark (index)` - marks the indexed task as not yet done** + +_Marks the task at the given index as not yet done._ + +#### **Example of usage:** + +`unmark 1` + +#### **Example outcome:** + +The task at index 1 will be marked as not yet done. + +If the task has been previously marked as not yet done, this will be expected: + +``` +The task is already unmarked. + +> list + +These are the tasks currently stored in the list: +1. Event - Module Briefing (at Feb 21 2022 09.00am) +``` + +else, this will be expected: + +``` +Nice! I've marked this task as done: +Event - Module Briefing (at Feb 21 2022 09.00am) + +> list + +These are the tasks currently stored in the list: +1. Event - Module Briefing (at Feb 21 2022 09.00am) +``` + +# + +### **`clone (index)` - clones the indexed task** + +_Clones the task at the given index._ + +#### **Example of usage:** + +`clone 1` + +#### **Example outcome:** + +The task at index 1 will be cloned. + +This will be expected: + +``` +I have cloned the task and added it to the end of the task list! This is the cloned task: +Todo - Walk the dog + +> list + +These are the tasks currently stored in the list: +1. Todo - Walk the dog +2. Event - Module Briefing (at Feb 21 2022 09.00am) +3. Todo - Walk the dog +``` + +# + +### **`update (what to update) (index) (new value)` - updates the indexed task** + +_Updates the specified detail of the task at the given index to the new value._ + +_If date is to be updated, new value must follow the format: yyyy-mm-dd e.g. 2019-04-20._ + +_If time is to be updated, new value must follow the format: hh:mm e.g. 23:59._ + +#### **Example of usage:** + +`update time 1 23:00` + +#### **Example outcome:** + +The time of the task at index 1 will be updated. + +This will be expected: + +``` +I have updated the task as per your request! This is the updated task: +Event - Module Briefing (at Feb 21 2022 11.00pm) + +> list + +These are the tasks currently stored in the list: +1. Event - Module Briefing (at Feb 21 2022 11.00am) +``` + +# + +### **`find (keyword)` - searches for keyword** + +_Searches the list of tasks for tasks that contains the keyword._ + +#### **Example of usage:** + +`find Bidding` + +#### **Example outcome:** + +The tasks that contains the keyword will be filtered out. + +If there are no tasks that contains the keyword, this will be expected: + +``` +There's nothing that contains the keyword! + +> list + +These are the tasks currently stored in the list: +1. Event - Module Briefing (at Feb 21 2022 11.00am) +2. Deadline - Module Bidding (by August 20 2022, 12:00pm) +``` + +else, this will be expected: + +``` +These are the matching tasks: +1. Deadline - Module Bidding (by: Aug 20 2022 12:00pm) + +> list + +These are the tasks currently stored in the list: +1. Event - Module Briefing (at Feb 21 2022 11.00am) +2. Deadline - Module Bidding (by Aug 20 2022 12:00pm) +``` + +# + +### **`bye` - exits program** + +_Exits the program._ + +#### **Example of usage:** + +`bye` + +#### **Example outcome:** + +A farewell message will be printed. + +This will be expected: + +``` +Sayonara!! Hope to see you again soon heh! :-) +``` + +# diff --git a/docs/Ui.png b/docs/Ui.png new file mode 100644 index 0000000000..7759176161 Binary files /dev/null and b/docs/Ui.png differ diff --git a/docs/_config.yml b/docs/_config.yml new file mode 100644 index 0000000000..259a24e4d2 --- /dev/null +++ b/docs/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-tactile \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000..7454180f2a 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..84d1f85fd6 --- /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-7.3.1-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 0000000000..1b6c787337 --- /dev/null +++ b/gradlew @@ -0,0 +1,234 @@ +#!/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/master/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 + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${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 "$*" +} >&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 + 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" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + 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 + +# 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 \ + "$@" + +# 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..107acd32c4 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@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 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%"=="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/duke/Duke.java b/src/main/java/duke/Duke.java new file mode 100644 index 0000000000..d1abefb398 --- /dev/null +++ b/src/main/java/duke/Duke.java @@ -0,0 +1,56 @@ +package duke; + +import duke.admin.Parser; +import duke.admin.Storage; +import duke.admin.TaskList; +import duke.admin.Ui; +import duke.commands.Command; +import duke.exceptions.DukeException; +/** + * Duke class is the main class of the program. + */ +public class Duke { + private static final String DEFAULT_FILE_PATH = "./dukeSaveLog.txt"; + private Storage storage; + private TaskList tasks; + + /** + * Constructor of Duke that uses a default file path as the specified location of the storage file. + */ + public Duke() { + storage = new Storage(DEFAULT_FILE_PATH); + try { + tasks = new TaskList(storage.load()); + } catch (DukeException e) { + tasks = new TaskList(); + } + } + + /** + * Constructor of Duke that takes in a file path specifying the location of the + * storage file. + * @param filePath string specifying location of storage file + */ + public Duke(String filePath) { + assert filePath != null; + storage = new Storage(filePath); + try { + tasks = new TaskList(storage.load()); + } catch (DukeException e) { + tasks = new TaskList(); + } + } + + public String getResponse(String fullCommand) throws DukeException { + assert fullCommand != null; + try { + Command c = Parser.parse(fullCommand); + String response = c.execute(tasks, storage); + + return response; + } catch (DukeException e) { + return Ui.showErrorMessage(e.getMessage()); + } + + } +} diff --git a/src/main/java/duke/Launcher.java b/src/main/java/duke/Launcher.java new file mode 100644 index 0000000000..e4ef6b4628 --- /dev/null +++ b/src/main/java/duke/Launcher.java @@ -0,0 +1,12 @@ +package duke; + +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/duke/Main.java b/src/main/java/duke/Main.java new file mode 100644 index 0000000000..dddda9aa4d --- /dev/null +++ b/src/main/java/duke/Main.java @@ -0,0 +1,33 @@ +package duke; + +import java.io.IOException; + +import duke.controllers.MainWindow; +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 { + + private Duke duke = new Duke(); + + @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().setDuke(duke); + stage.setTitle("Duke"); + stage.show(); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/duke/admin/Parser.java b/src/main/java/duke/admin/Parser.java new file mode 100644 index 0000000000..43e5a11cf1 --- /dev/null +++ b/src/main/java/duke/admin/Parser.java @@ -0,0 +1,66 @@ +package duke.admin; + +import duke.commands.AddCommand; +import duke.commands.ChangeMarkCommand; +import duke.commands.CloneCommand; +import duke.commands.Command; +import duke.commands.DeleteCommand; +import duke.commands.ExitCommand; +import duke.commands.FindCommand; +import duke.commands.ListCommand; +import duke.commands.UpdateCommand; +import duke.exceptions.DukeException; + +/** + * Parser class parses the command passed in as a String and represents it as a + * Command that the program can manage. + */ +public class Parser { + private static String description; + + /** + * Returns the relevent Command object to be executed from the string input. + * @param fullCommand the command as a String + * @return Command object that triggers an action from the program based on the + * command + * @throws DukeException exception thrown when command is invalid or improper + */ + public static Command parse(String fullCommand) throws DukeException { + String action = fullCommand.split(" ", 2)[0]; + + if (fullCommand.split(" ", 2).length > 1) { + description = fullCommand.split(" ", 2)[1]; + } + + try { + switch (action) { + case "list": //List tasks function + return new ListCommand(); + case "mark": //Mark tasks function + return new ChangeMarkCommand(description, true); + case "unmark": //Unmark tasks function + return new ChangeMarkCommand(description, false); + case "todo": //Add To Do task function + return new AddCommand("T", description); + case "deadline": //Add Deadline task function + return new AddCommand("D", description); + case "event": //Add Event task function + return new AddCommand("E", description); + case "delete": //Deletes task function + return new DeleteCommand(description); + case "bye": //Exit function + return new ExitCommand(); + case "find": //Find task function + return new FindCommand(description); + case "update": //Update task function + return new UpdateCommand(description); + case "clone": //Clone task function + return new CloneCommand(description); + default: + throw new DukeException(DukeException.INVALID_COMMAND); + } + } catch (NullPointerException ne) { + throw new DukeException(DukeException.INVALID_FORMAT); + } + } +} diff --git a/src/main/java/duke/admin/Storage.java b/src/main/java/duke/admin/Storage.java new file mode 100644 index 0000000000..875d4df208 --- /dev/null +++ b/src/main/java/duke/admin/Storage.java @@ -0,0 +1,357 @@ +package duke.admin; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; + +import duke.exceptions.DukeException; +import duke.tasks.Task; + +/** + * Storage class manages the storage file and the actions on the storage file. + */ +public class Storage { + private static String buffer = " xxx "; + private File storageFile; + + /** + * Constructor for Storage that takes in a file path that points to the storage + * file + * @param filePath + */ + public Storage(String filePath) { + this.storageFile = new File(filePath); + } + + /** + * Reads the content of the storage file and passes the data to be processed by + * the program. + * @return an array of Strings each representing the tasks stored in the storage + * file + * @throws DukeException exception thrown when file cannot be found or is + * corrupted + */ + public String[] load() throws DukeException { + try { + BufferedReader storageFileReader = new BufferedReader(new FileReader(this.storageFile)); + String fileContent = ""; + String dataLine = storageFileReader.readLine(); + + while (dataLine != null) { + fileContent = fileContent + dataLine + System.lineSeparator(); + + dataLine = storageFileReader.readLine(); + } + String[] tasksArr = fileContent.split(System.lineSeparator()); + storageFileReader.close(); + + return tasksArr; + } catch (FileNotFoundException e) { + throw new DukeException("File not found!! :-("); + } catch (IOException e) { + throw new DukeException("Issue reading file!! D:"); + } + } + + /** + * Reflects the addition of the task in the storage list. + * @param task the task that has just been added to the task list + * @throws DukeException exception thrown when there is an error accessing or + * writing to the storage file + */ + public void updateAfterAdd(Task task, String description) throws DukeException { + try { + int isMarked = task.isDone() ? 1 : 0; // isMarked is the integer representation of task.isDone(); + + FileWriter storageFileWriter = new FileWriter(storageFile, true); + storageFileWriter.write(task.getType() + buffer + isMarked + buffer + description + "\n"); + storageFileWriter.close(); + } catch (IOException e) { + throw new DukeException("Cannot update addition in save file!! :-("); + } + } + + /** + * Updates the storage file after details of tasks have been updated. + * @param typeOfTask type of task that has been updated + * @param typeOfUpdate what details has been updated: description, date or time + * @param index index of task that has been updated + * @param updateValue value to be updated to + * @throws DukeException exception thrown when there is an error accessing or writing to the storage file + */ + public void updateAfterEdits(String typeOfTask, String typeOfUpdate, int index, String updateValue) + throws DukeException { + try { + int dataLineCounter = 0; //initializing the counter + BufferedReader storageFileReader = new BufferedReader(new FileReader(storageFile)); + String contentToBeWritten = ""; + String dataLine = storageFileReader.readLine(); + + while (dataLine != null) { + if (dataLineCounter == index) { + if (typeOfTask.equals("D") || typeOfTask.equals("E")) { + dataLine = updateNonToDoTask(typeOfUpdate, dataLine, updateValue); + } else if (typeOfTask.equals("T")) { + dataLine = updateToDoTask(typeOfUpdate, dataLine, updateValue); + } + } + + contentToBeWritten = contentToBeWritten + dataLine + System.lineSeparator(); + dataLine = storageFileReader.readLine(); + dataLineCounter++; + } + + FileWriter storageFileWriter = new FileWriter(storageFile); + storageFileWriter.write(contentToBeWritten); + + storageFileReader.close(); + storageFileWriter.close(); + } catch (FileNotFoundException e) { + throw new DukeException("Save file not found!! :-("); + } catch (IOException e) { + throw new DukeException("Cannot update edits in save file!! D:"); + } + } + + /** + * Updates the non-"To Do" tasks stored in the storage file + * @param typeOfUpdate whether description, date or time should be updated + * @param original original To Do task to be updated + * @param updateValue value to be updated to + * @return updated non-"To Do" task to be stored formatted as a String + */ + private String updateNonToDoTask(String typeOfUpdate, String original, String updateValue) { + String updatedString = ""; + + switch (typeOfUpdate) { + case "description": + updatedString = updateDescription(original, updateValue); + break; + case "date": + updatedString = updateDate(original, updateValue); + break; + case "time": + updatedString = updateTime(original, updateValue); + break; + default: + break; + } + + return updatedString; + } + + /** + * Updates the To Do tasks stored in the storage file + * @param typeOfUpdate should be description only + * @param original original To Do task to be updated + * @param updateValue value to be updated to + * @return updated To Do task to be stored formatted as a String + */ + private String updateToDoTask(String typeOfUpdate, String original, String updateValue) { + String updatedString = ""; + + switch (typeOfUpdate) { + case "description": + updatedString = updateToDoDescription(original, updateValue); + break; + default: + break; + } + + return updatedString; + } + + /** + * Replaces the description in the original task stored in the storage file + * @param original original task stored in the storage file + * @param updateValue description to be updated to + * @return updated task to be stored formatted as a String + */ + private String updateDescription(String original, String updateValue) { + int numOfCharsBeforeDescription = 12; + int numOfCharsAfterDescription = 21; + + String prefix = original.substring(0, numOfCharsBeforeDescription); + String suffix = original.substring(original.length() - numOfCharsAfterDescription); + + return prefix + updateValue + suffix; + } + + /** + * Replaces the description of the to do task stored within the storage file + * @param original original task stored in the storage file + * @param updateValue description to be updated to + * @return updated task to be stored formatted as a String + */ + private String updateToDoDescription(String original, String updateValue) { + int numOfCharsBeforeDescription = 12; + + String prefix = original.substring(0, numOfCharsBeforeDescription); + + return prefix + updateValue; + } + + /** + * Replaces the date of the tasks stored within the storage file + * @param original original task stored in the storage file + * @param updateValue date to be updated to + * @return updated task to be stored formatted as a String + */ + private String updateDate(String original, String updateValue) { + int numOfCharsFromDate = 16; + int numOfCharsAfterDate = 6; + int indexOfDate = original.length() - numOfCharsFromDate; + + String dateToBeUpdated = original.substring(indexOfDate, original.length() - numOfCharsAfterDate); + + return original.replaceFirst(dateToBeUpdated, updateValue); + } + + /** + * Replaces the time of the tasks stored within the storage file + * @param original original task stored in the storage file + * @param updateValue time to be updated to + * @return updated task to be stored formatted as a String + */ + private String updateTime(String original, String updateValue) { + int numOfCharsFromTime = 5; + + String timeToBeUpdated = original.substring(original.length() - numOfCharsFromTime); + + return original.replaceFirst(timeToBeUpdated, updateValue); + } + + /** + * Updates the storage list after a clone action has been performed. + * @param index index of task to be cloned + * @throws DukeException exception thrown when there is an error accessing or writing to the storage file + */ + public void updateAfterClone(int index) throws DukeException { + try { + int dataLineCounter = 0; + BufferedReader storageFileReader = new BufferedReader(new FileReader(storageFile)); + String contentToBeWritten = ""; + String dataLine = storageFileReader.readLine(); + String clone = ""; + + while (dataLine != null) { + if (dataLineCounter == index) { + clone = clone + dataLine; + } + + contentToBeWritten = contentToBeWritten + dataLine + System.lineSeparator(); + dataLine = storageFileReader.readLine(); + dataLineCounter++; + } + + contentToBeWritten = contentToBeWritten + clone + System.lineSeparator(); + FileWriter storageFileWriter = new FileWriter(storageFile); + storageFileWriter.write(contentToBeWritten); + + storageFileReader.close(); + storageFileWriter.close(); + } catch (FileNotFoundException e) { + throw new DukeException("Save file not found!! :-("); + } catch (IOException e) { + throw new DukeException("Cannot update clone action in save file!! D:"); + } + } + + /** + * Reflects the deleted task in the storage list. + * @param index the index of the task that has just been deleted + * @throws DukeException exception thrown when there is an error accessing or + * writing to the storage file + */ + public void updateAfterDelete(int index) throws DukeException { + try { + int dataLineCounter = 0; //initializing the counter + BufferedReader storageFileReader = new BufferedReader(new FileReader(storageFile)); + String contentToBeWritten = ""; + String dataLine = storageFileReader.readLine(); + + while (dataLine != null) { + if (dataLineCounter != index) { + contentToBeWritten = contentToBeWritten + dataLine + System.lineSeparator(); + } + + dataLine = storageFileReader.readLine(); + dataLineCounter++; + } + + FileWriter storageFileWriter = new FileWriter(storageFile); + storageFileWriter.write(contentToBeWritten); + + storageFileReader.close(); + storageFileWriter.close(); + } catch (FileNotFoundException e) { + throw new DukeException("Save file not found!! :-("); + } catch (IOException e) { + throw new DukeException("Cannot update deletion in save file!! D:"); + } + } + + /** + * Updates the storage file when there has just been a change to whether a task + * has been marked as done or not yet done. + * @param index index of the task which the action is performed on + * @param toMark true if the action marked the task as done, otherwise + * false + * @throws DukeException exception thrown when there is an error accessing or + * writing to the storage file + */ + public void updateAfterChangeMark(int index, boolean toMark) throws DukeException { + try { + int dataLineCounter = 0; //initializing the counter + BufferedReader storageFileReader = new BufferedReader(new FileReader(storageFile)); + String contentToBeWritten = ""; + String dataLine = storageFileReader.readLine(); + + while (dataLine != null) { + if (dataLineCounter == index) { + if (dataLine.charAt(6) == '1') { + if (!(toMark)) { + dataLine = dataLine.replaceFirst("xxx 1 xxx", "xxx 0 xxx"); + } + } else { + if (toMark) { + dataLine = dataLine.replaceFirst("xxx 0 xxx", "xxx 1 xxx"); + } + } + } + + contentToBeWritten = contentToBeWritten + dataLine + System.lineSeparator(); + dataLine = storageFileReader.readLine(); + dataLineCounter++; + } + + FileWriter storageFileWriter = new FileWriter(storageFile); + storageFileWriter.write(contentToBeWritten); + + storageFileReader.close(); + storageFileWriter.close(); + } catch (FileNotFoundException e) { + throw new DukeException("Save file not found!! :-("); + } catch (IOException e) { + throw new DukeException("Cannot update edits in save file!! D:"); + } + } + + /** + * Wipes the storage file of all its data. + * @throws DukeException exception thrown when there is an error accessing or + * writing to the storage file + */ + public void resetFile() throws DukeException { + try { + FileWriter storageFileWriter = new FileWriter(storageFile, false); + storageFileWriter.write(""); + storageFileWriter.close(); + } catch (IOException e) { + throw new DukeException("Cannot reset the save file!! D:"); + } + } +} diff --git a/src/main/java/duke/admin/TaskList.java b/src/main/java/duke/admin/TaskList.java new file mode 100644 index 0000000000..ec33f7fa7c --- /dev/null +++ b/src/main/java/duke/admin/TaskList.java @@ -0,0 +1,205 @@ +package duke.admin; + +import java.util.ArrayList; + +import duke.exceptions.DukeException; +import duke.tasks.Deadline; +import duke.tasks.Event; +import duke.tasks.Task; +import duke.tasks.ToDo; +import duke.tasks.Trigger; + +/** + * TaskList class manages the task list and the actions that can be performed on + * the task list. + */ +public class TaskList { + private static final String BUFFER = " xxx "; + private static final Trigger noChangeTrigger = new Trigger("n0 cH4Ng3$ d#t3CtEd"); + private ArrayList tasks; + + /** + * Constructor for TaskList that takes in data from the storage file and + * constructs a task list from there. + * @param tasksArr data read from the storage file + * @throws DukeException exception when data is corrupted or task list cannot be + * created as intended + */ + public TaskList(String[] tasksArr) throws DukeException { + try { + tasks = new ArrayList<>(); + + for (String task : tasksArr) { + String[] taskDetails = task.split(BUFFER); + + String type = taskDetails[0]; + Boolean isMarked = (Integer.parseInt(taskDetails[1]) > 0); + String description = taskDetails[2]; + + switch (type) { + case "T": + tasks.add(new ToDo(description, isMarked)); + break; + case "D": + tasks.add(new Deadline(description, isMarked)); + break; + case "E": + tasks.add(new Event(description, isMarked)); + break; + default: + break; + } + } + } catch (Exception e) { + throw new DukeException(""); + } + } + + /** + * Constructor for TaskList when storage file data is unavailable. + */ + public TaskList() { + tasks = new ArrayList<>(); + } + + /** + * Adds the task specified to the task list. + * @param task the task to be added + */ + public void add(Task task) { + tasks.add(task); + } + + /** + * Deletes the task indexed by the index specified. + * @param index the index of the task that is to be deleted + * @return the task after it is deleted from the task list + */ + public Task delete(int index) { + assert index <= tasks.size(); + Task deletedTask = tasks.get(index); + tasks.remove(index); + + return deletedTask; + } + + /** + * Prints out all the tasks in the task list and their index. + * @return task list as a string + */ + public String list() { + String listString = ""; + + for (int i = 1; i <= tasks.size(); i++) { + Task task = tasks.get(i - 1); + String listElement = i + ". " + task.toString(); + + listString = listString + listElement + System.lineSeparator(); + } + + return listString; + } + + /** + * Prints out all the tasks in the task list that contains the keyword. + * @param keyword keyword to be contained by the tasks + * @return result as a string + */ + public String find(String keyword) { + ArrayList searchResults = new ArrayList<>(); + + for (int i = 0; i < tasks.size(); i++) { + Task task = tasks.get(i); + String taskAsString = task.toString().toLowerCase(); + if (taskAsString.contains(keyword.toLowerCase())) { + searchResults.add(task); + } + } + + String searchResultString = ""; + + for (int j = 1; j <= searchResults.size(); j++) { + Task matchedTask = searchResults.get(j - 1); + String result = j + ". " + matchedTask.toString(); + searchResultString = searchResultString + result + System.lineSeparator(); + } + + if (searchResultString.equals("")) { + return "There's nothing that contains the keyword!"; + } else { + return "These are the matching tasks:\n" + searchResultString; + } + } + + /** + * Changes the mark of the task if the command requests for a change in mark and returns the task that has been + * changed. If not, return a trigger task to trigger the system to inform the user that the command does not + * change the task. + * @param index index of task to be marked + * @param toMark if the command wishes the indexed task to be marked or not + * @return the changed task or a trigger task + */ + public Task changeMark(int index, boolean toMark) { + assert index <= tasks.size(); + + Task taskToChange = tasks.get(index); + boolean isMarked = taskToChange.isDone(); + boolean isChangingMark = isMarked ^ toMark; + + if (isChangingMark) { + taskToChange.toggleMark(); + return taskToChange; + } else { + return noChangeTrigger; + } + + } + + /** + * Returns the number of tasks in the task list. + * @return number of tasks in the task list + */ + public int getNumberOfTasks() { + return tasks.size(); + } + + /** + * Updates the details of the indexed task and returns the resulting task. + * @param type details to be updated: description, date or time + * @param index index of task to be updated + * @param updateValue value to be updated to + * @return task that has been updated + * @throws DukeException when there is an error trying to update the date or time value + */ + public Task update(String type, int index, String updateValue) throws DukeException { + Task task = tasks.get(index); + + switch (type) { + case "description": + task.updateDescription(updateValue); + break; + case "date": + task.updateDate(updateValue); + break; + case "time": + task.updateTime(updateValue); + break; + default: + throw new DukeException(DukeException.INVALID_FORMAT); + } + + return task; + } + + /** + * Clones the indexed task and adds to the the end of the task list. Returns the cloned task at the end. + * @param index index of the task to be cloned + * @return the task that is cloned + */ + public Task clone(int index) { + Task taskToBeCloned = tasks.get(index); + tasks.add(taskToBeCloned); + + return taskToBeCloned; + } +} diff --git a/src/main/java/duke/admin/Ui.java b/src/main/java/duke/admin/Ui.java new file mode 100644 index 0000000000..81e3515502 --- /dev/null +++ b/src/main/java/duke/admin/Ui.java @@ -0,0 +1,133 @@ +package duke.admin; + +import duke.tasks.Task; +import duke.tasks.Trigger; + +/** + * Ui is a class that manages the bulk of the user interaction required by the + * program. + */ +public class Ui { + private static final Trigger noChangeTrigger = new Trigger("n0 cH4Ng3$ d#t3CtEd"); + + /** + * Prints out a welcome message when the user boots the program. + */ + public static String showWelcomeMessage() { + return "Welcome to Duke, your friendly task manager!\n What do you want to do today?"; + } + + /** + * Prints out a farewell message when the user leaves the + * program. + */ + public static String showGoodByeMessage() { + return "Sayonara!! Hope to see you again soon hehe! :-)"; + } + + /** + * Prints out a message to let the user know what task has + * been added and how many tasks there are currently in the task list. + * @param task the task that has just been added into the task list + * @param tasks the list of task being managed by Duke + */ + public static String showAddedMessage(Task task, TaskList tasks) { + return "Got it. I've added this task:\n" + task.toString() + + "\nNow you have " + tasks.getNumberOfTasks() + " tasks in the list."; + } + + /** + * Prints out a message to let the user know what task has + * been deleted and how many tasks there are remaining in the task list. + * @param task the task that has just been deleted into the task list + * @param tasks the list of task being managed by Duke + */ + public static String showDeletedMessage(Task task, TaskList tasks) { + return "Noted. I've removed this task:\n" + task.toString() + + "\nNow you have " + tasks.getNumberOfTasks() + " tasks in the list."; + } + + /** + * Returns a string to inform the user that the mark is changed. + * @param task task to be changed + * @param toMark if the command wishes for the task to be marked or not. + * @return a string to inform the user that the mark is changed. + */ + public static String showChangeMarkMessage(Task task, boolean toMark) { + if (task.toString().equals(noChangeTrigger.toString())) { + String message = "The task is already "; + String messageSuffix = toMark ? "marked." : "unmarked."; + + return message + messageSuffix; + } else { + String taskDescription = task.toString(); + String messagePrefix = toMark ? "Nice! I've marked this task as done:\n" + : "OK, I've marked this task as not done yet:\n"; + + return messagePrefix + taskDescription; + } + } + + /** + * Prints out the error message to the user to let the user + * know why the program cannot run as intended + * @param errorMessage a message describing the fault + */ + public static String showErrorMessage(String errorMessage) { + return "Uh oh... We ran into an error: " + errorMessage; + } + + /** + * Returns the list of tasks as a String to be printed out in response to user's request to print the task list. + * @param tasks list of tasks to be printed + * @return the list of tasks as a String with Ui features included + */ + public static String listTasks(TaskList tasks) { + String listResult = tasks.list(); + + if (listResult.equals("")) { + return "There are currently no elements in the list!"; + } else { + String listResultPrefix = "These are the tasks currently stored in the list:\n"; + + return listResultPrefix + listResult; + } + } + + /** + * Returns a string of tasks containing the keyword. + * @param tasks list of tasks to be searched + * @param keyword the keyword that the results have to contain + * @return the list of matching tasks as a String to be printed + */ + public static String findTasks(TaskList tasks, String keyword) { + String findResult = tasks.find(keyword); + + if (findResult.equals("")) { + return "No results containing the keyword found!"; + } else { + String findResultPrefix = "Here are the matching tasks in your list:" + System.lineSeparator(); + + return findResultPrefix + findResult; + } + } + + /** + * Returns a string informing user that update has been completed. + * @param task task to be updated + * @return a string informing user that the task is updated and shows the user the updated task + */ + public static String showUpdatedMessage(Task task) { + return "I have updated the task as per your request! This is the updated task:\n" + task.toString(); + } + + /** + * Returns a string informing user that the cloning action is completed. + * @param task the cloned task + * @return a string detailing the cloned task and that cloning is complete. + */ + public static String showClonedMessage(Task task) { + return "I have cloned the task and added it to the end of the task list! This is the cloned task:\n" + + task.toString(); + } +} diff --git a/src/main/java/duke/commands/AddCommand.java b/src/main/java/duke/commands/AddCommand.java new file mode 100644 index 0000000000..3fa0dc6b6f --- /dev/null +++ b/src/main/java/duke/commands/AddCommand.java @@ -0,0 +1,70 @@ +package duke.commands; + +import duke.admin.Storage; +import duke.admin.TaskList; +import duke.admin.Ui; +import duke.exceptions.DukeException; +import duke.tasks.Deadline; +import duke.tasks.Event; +import duke.tasks.Task; +import duke.tasks.ToDo; + +/** + * AddCommand is a Command that adds a task that is either a ToDo task, Deadline + * task or Event task to the program. + */ + +public class AddCommand extends Command { + private String description; + private String type; + private Task taskToBeAdded; + + /** + * Constructor for AddCommand which takes in the type of task and description of + * task that is to be added. + * @param type type of task to be added + * @param description description of task to be added + */ + public AddCommand(String type, String description) { + assert type != null; + assert description != null; + this.type = type; + this.description = description; + } + + /** + * Adds the task to TaskList, updates the storage file and notifies the user + * when it's done + * @param tasks task list local to user + * @param storage storage instance local to user + */ + @Override + public String execute(TaskList tasks, Storage storage) throws DukeException { + switch (type) { + case "T": + taskToBeAdded = new ToDo(description); + break; + case "D": + taskToBeAdded = new Deadline(description); + break; + case "E": + taskToBeAdded = new Event(description); + break; + default: + break; + } + + tasks.add(taskToBeAdded); + storage.updateAfterAdd(taskToBeAdded, description); + + return Ui.showAddedMessage(taskToBeAdded, tasks); + } + + /** + * Checks if this is an exit command, and only returns true for an exit command. + */ + @Override + public boolean isExit() { + return false; + } +} diff --git a/src/main/java/duke/commands/ChangeMarkCommand.java b/src/main/java/duke/commands/ChangeMarkCommand.java new file mode 100644 index 0000000000..51a6091e4a --- /dev/null +++ b/src/main/java/duke/commands/ChangeMarkCommand.java @@ -0,0 +1,44 @@ +package duke.commands; + +import duke.admin.Storage; +import duke.admin.TaskList; +import duke.admin.Ui; +import duke.exceptions.DukeException; +import duke.tasks.Task; + +/** + * ChangeMarkCommand changes the marked status of a task, either by marking it or unmarking it. + */ +public class ChangeMarkCommand extends Command { + private String description; + private boolean isMarkRequest; + + /** + * Constructor for ChangeMarkCommand + * @param index index of task to be changed + * @param isMarkRequest whether the command wants to mark the task or unmark the task + */ + public ChangeMarkCommand(String description, boolean isMarkRequest) { + this.description = description; + this.isMarkRequest = isMarkRequest; + } + + @Override + public String execute(TaskList tasks, Storage storage) throws DukeException { + int index = Integer.parseInt(description) - 1; + + if (index >= tasks.getNumberOfTasks() || index < 0) { + throw new DukeException(DukeException.INVALID_FORMAT); + } + + Task task = tasks.changeMark(index, isMarkRequest); + storage.updateAfterChangeMark(index, isMarkRequest); + + return Ui.showChangeMarkMessage(task, isMarkRequest); + } + + @Override + public boolean isExit() { + return false; + } +} diff --git a/src/main/java/duke/commands/CloneCommand.java b/src/main/java/duke/commands/CloneCommand.java new file mode 100644 index 0000000000..16d3abd8ef --- /dev/null +++ b/src/main/java/duke/commands/CloneCommand.java @@ -0,0 +1,41 @@ +package duke.commands; + +import duke.admin.Storage; +import duke.admin.TaskList; +import duke.admin.Ui; +import duke.exceptions.DukeException; +import duke.tasks.Task; + +/** + * CloneCommand clones the indexed task specified in the command. + */ +public class CloneCommand extends Command { + private String description; + + /** + * Constructor for CloneCommand that takes in a description. + * @param description of clone command. + */ + public CloneCommand(String description) { + this.description = description; + } + + @Override + public String execute(TaskList tasks, Storage storage) throws DukeException { + int index = Integer.parseInt(description) - 1; + + if (index >= tasks.getNumberOfTasks() || index < 0) { + throw new DukeException(DukeException.INVALID_FORMAT); + } + + Task task = tasks.clone(index); + storage.updateAfterClone(index); + + return Ui.showClonedMessage(task); + } + + @Override + public boolean isExit() { + return false; + } +} diff --git a/src/main/java/duke/commands/Command.java b/src/main/java/duke/commands/Command.java new file mode 100644 index 0000000000..0002116483 --- /dev/null +++ b/src/main/java/duke/commands/Command.java @@ -0,0 +1,16 @@ +package duke.commands; + +import duke.admin.Storage; +import duke.admin.TaskList; +import duke.exceptions.DukeException; + +/** + * Command is an abstract class that specifies 2 methods that has to be + * implemented by all the different commands, namely isExit and execute. + */ +public abstract class Command { + + public abstract boolean isExit(); + + public abstract String execute(TaskList tasks, Storage storage) throws DukeException; +} diff --git a/src/main/java/duke/commands/DeleteCommand.java b/src/main/java/duke/commands/DeleteCommand.java new file mode 100644 index 0000000000..3fd66cbfcb --- /dev/null +++ b/src/main/java/duke/commands/DeleteCommand.java @@ -0,0 +1,52 @@ +package duke.commands; + +import duke.admin.Storage; +import duke.admin.TaskList; +import duke.admin.Ui; +import duke.exceptions.DukeException; +import duke.tasks.Task; + +/** + * DeleteCommand is a Command that deletes the task at the index specified from + * the program. + */ +public class DeleteCommand extends Command { + private String description; + + /** + * Constructor for DeleteCommand that takes in the description containing index + * of the task to be deleted from the program. + * @param description description of delete command + */ + public DeleteCommand(String description) { + this.description = description; + } + + /** + * Deletes the indexed task from task list and storage file and updates the user + * when the task is deleted. + * @param tasks task list local to user + * @param storage storage instance local to user + */ + @Override + public String execute(TaskList tasks, Storage storage) throws DukeException { + int index = Integer.parseInt(description) - 1; + + if (index >= tasks.getNumberOfTasks() || index < 0) { + throw new DukeException(DukeException.INVALID_FORMAT); + } + + Task deletedTask = tasks.delete(index); + storage.updateAfterDelete(index); + + return Ui.showDeletedMessage(deletedTask, tasks); + } + + /** + * Checks if this is an exit command, and only returns true for an exit command. + */ + @Override + public boolean isExit() { + return false; + } +} diff --git a/src/main/java/duke/commands/ExitCommand.java b/src/main/java/duke/commands/ExitCommand.java new file mode 100644 index 0000000000..cfae7a4e79 --- /dev/null +++ b/src/main/java/duke/commands/ExitCommand.java @@ -0,0 +1,30 @@ +package duke.commands; + +import duke.admin.Storage; +import duke.admin.TaskList; +import duke.admin.Ui; + +/** + * ExitCommand is a Command that terminates the program. + */ +public class ExitCommand extends Command { + + /** + * Checks if this is an exit command, and only returns yes for an + * exit command. + */ + @Override + public boolean isExit() { + return true; + } + + /** + * Prints a farewell message to the user and exits the program. + * @param tasks task list local to user + * @param storage storage instance local to user + */ + @Override + public String execute(TaskList tasks, Storage storage) { + return Ui.showGoodByeMessage(); + } +} diff --git a/src/main/java/duke/commands/FindCommand.java b/src/main/java/duke/commands/FindCommand.java new file mode 100644 index 0000000000..2b1bda7bb5 --- /dev/null +++ b/src/main/java/duke/commands/FindCommand.java @@ -0,0 +1,31 @@ +package duke.commands; + +import duke.admin.Storage; +import duke.admin.TaskList; +import duke.exceptions.DukeException; + +public class FindCommand extends Command { + private String keyword; + + /** + * Constructor for FindCommand that takes in a keyword to be used to filter the matching results. + * @param keyword + */ + public FindCommand(String keyword) { + this.keyword = keyword; + } + + @Override + public String execute(TaskList tasks, Storage storage) throws DukeException { + if (keyword == null) { + throw new DukeException(DukeException.INVALID_FORMAT); + } + + return tasks.find(keyword); + } + + @Override + public boolean isExit() { + return false; + } +} diff --git a/src/main/java/duke/commands/ListCommand.java b/src/main/java/duke/commands/ListCommand.java new file mode 100644 index 0000000000..5d88810acb --- /dev/null +++ b/src/main/java/duke/commands/ListCommand.java @@ -0,0 +1,31 @@ +package duke.commands; + +import duke.admin.Storage; +import duke.admin.TaskList; +import duke.admin.Ui; + +/** + * ListCommand is a Command that triggers the program to print out the task list + * with proper indexing according to chronological order of when the task was + * added into the task list. + */ +public class ListCommand extends Command { + + /** + * Lists out the tasks stored in the tasks list. + * @param tasks task list local to user + * @param storage storage instance local to user + */ + @Override + public String execute(TaskList tasks, Storage storage) { + return Ui.listTasks(tasks); + } + + /** + * Checks if this is an exit command, and only returns true for an exit command. + */ + @Override + public boolean isExit() { + return false; + } +} diff --git a/src/main/java/duke/commands/UpdateCommand.java b/src/main/java/duke/commands/UpdateCommand.java new file mode 100644 index 0000000000..9a93b0d82d --- /dev/null +++ b/src/main/java/duke/commands/UpdateCommand.java @@ -0,0 +1,41 @@ +package duke.commands; + +import duke.admin.Storage; +import duke.admin.TaskList; +import duke.admin.Ui; +import duke.exceptions.DukeException; +import duke.tasks.Task; + +public class UpdateCommand extends Command { + private int index; + private String typeOfUpdate; + private String updateValue; + + /** + * Constructor for UpdateCommand + * @param description description of the command that contains the type of update, index of task to be updated, + * and value to be updated to + */ + public UpdateCommand(String description) { + String[] splitDescription = description.split(" "); + assert splitDescription.length >= 3; + + this.typeOfUpdate = splitDescription[0]; + this.index = Integer.parseInt(splitDescription[1]) - 1; + this.updateValue = splitDescription[2]; + } + + @Override + public boolean isExit() { + return false; + } + + @Override + public String execute(TaskList tasks, Storage storage) throws DukeException { + Task taskToBeUpdated = tasks.update(typeOfUpdate, index, updateValue); + String typeOfTask = taskToBeUpdated.getType(); + storage.updateAfterEdits(typeOfTask, typeOfUpdate, index, updateValue); + + return Ui.showUpdatedMessage(taskToBeUpdated); + } +} diff --git a/src/main/java/duke/controllers/DialogBox.java b/src/main/java/duke/controllers/DialogBox.java new file mode 100644 index 0000000000..cb2ea03e55 --- /dev/null +++ b/src/main/java/duke/controllers/DialogBox.java @@ -0,0 +1,61 @@ +package duke.controllers; + +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; + +/** + * 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; + + private DialogBox(String text, Image img) { + try { + FXMLLoader fxmlLoader = new FXMLLoader(MainWindow.class.getResource("/view/DialogBox.fxml")); + fxmlLoader.setController(this); + fxmlLoader.setRoot(this); + fxmlLoader.load(); + } catch (IOException e) { + e.printStackTrace(); + } + + dialog.setText(text); + displayPicture.setImage(img); + } + + /** + * Flips the dialog box such that the ImageView is on the left and text on the right. + */ + private void flip() { + ObservableList tmp = FXCollections.observableArrayList(this.getChildren()); + Collections.reverse(tmp); + getChildren().setAll(tmp); + setAlignment(Pos.TOP_LEFT); + } + + public static DialogBox getUserDialog(String text, Image img) { + return new DialogBox(text, img); + } + + public static DialogBox getDukeDialog(String text, Image img) { + var db = new DialogBox(text, img); + db.flip(); + return db; + } +} diff --git a/src/main/java/duke/controllers/MainWindow.java b/src/main/java/duke/controllers/MainWindow.java new file mode 100644 index 0000000000..26ad1c1844 --- /dev/null +++ b/src/main/java/duke/controllers/MainWindow.java @@ -0,0 +1,57 @@ +package duke.controllers; + +import duke.Duke; +import duke.admin.Ui; +import duke.exceptions.DukeException; +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; + + private Duke duke; + + private Image userImage = new Image(this.getClass().getResourceAsStream("/images/Aladdin.png")); + private Image dukeImage = new Image(this.getClass().getResourceAsStream("/images/Genie.png")); + + @FXML + public void initialize() { + scrollPane.vvalueProperty().bind(dialogContainer.heightProperty()); + } + + public void setDuke(Duke d) { + duke = d; + } + + /** + * Creates two dialog boxes, one echoing user input and the other containing Duke's reply and then appends them to + * the dialog container. Clears the user input after processing. + */ + @FXML + private void handleUserInput() { + try { + String input = userInput.getText(); + String response = duke.getResponse(input); + dialogContainer.getChildren().addAll( + DialogBox.getUserDialog(input, userImage), + DialogBox.getDukeDialog(response, dukeImage)); + userInput.clear(); + } catch (DukeException e) { + System.out.println(Ui.showErrorMessage(e.getMessage())); + } + } +} diff --git a/src/main/java/duke/data/duke.txt b/src/main/java/duke/data/duke.txt new file mode 100644 index 0000000000..2f7a1de882 --- /dev/null +++ b/src/main/java/duke/data/duke.txt @@ -0,0 +1,2 @@ +E xxx 0 xxx Module Briefing /at 2022-02-21 23:00 +D xxx 0 xxx Module Bidding /by 2022-08-20 12:00 diff --git a/src/main/java/duke/exceptions/DukeException.java b/src/main/java/duke/exceptions/DukeException.java new file mode 100644 index 0000000000..0c17b0e7ab --- /dev/null +++ b/src/main/java/duke/exceptions/DukeException.java @@ -0,0 +1,18 @@ +package duke.exceptions; + +/** + * DukeException is an Exception unique to Duke. + */ +public class DukeException extends Exception { + + public static final String INVALID_COMMAND = "Command was invalid or does not exist!"; + public static final String INVALID_FORMAT = "Details input are invalid or in the wrong format!"; + + /** + * Constructs a DukeException instance with the inputted error message + * @param errorMessage message describing the fault + */ + public DukeException(String errorMessage) { + super("What went wrong: " + errorMessage); + } +} diff --git a/src/main/java/duke/tasks/Deadline.java b/src/main/java/duke/tasks/Deadline.java new file mode 100644 index 0000000000..ff4162a04c --- /dev/null +++ b/src/main/java/duke/tasks/Deadline.java @@ -0,0 +1,122 @@ +package duke.tasks; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; + +import duke.exceptions.DukeException; + +/** + * Deadline is a Task that should be done by a certain date and time. + */ +public class Deadline extends Task { + private static String type = "D"; + private String printedString; + private String descriptionWithoutDateTime; + private LocalDate deadlineDate; + private LocalTime deadlineTime; + + /** + * Constructor for Deadline that takes in a description of the Deadline task and + * whether it is marked as done. + * @param description the description of the deadline task, containing the date + * and time of deadline + * @param isDone true if the deadline task has been marked as done + * @throws DukeException exception thrown when the deadline is invalid due to + * missing deadline or improper input format + */ + public Deadline(String description, boolean isDone) throws DukeException { + this.isDone = isDone; + this.description = description; + try { + String[] splitDescription = description.split("/by "); + descriptionWithoutDateTime = splitDescription[0]; + boolean containsDateTimeSpecification = splitDescription.length > 1; + + if (containsDateTimeSpecification) { + String dateAndTime = splitDescription[1]; + String[] splitDateTime = dateAndTime.split(" "); + boolean containsTimeSpecification = splitDateTime.length > 1; + + if (containsTimeSpecification) { + this.deadlineDate = LocalDate.parse(splitDateTime[0]); + this.deadlineTime = LocalTime.parse(splitDateTime[1]); + + updatePrintedString(); + } else { + throw new DukeException(DukeException.INVALID_FORMAT); + } + } else { + throw new DukeException(DukeException.INVALID_FORMAT); + } + } catch (DateTimeParseException e) { + throw new DukeException(DukeException.INVALID_FORMAT); + } + } + + /** + * Constructor for Deadline that takes in a description of the Deadline task + * @param description the description of the deadline task, containing the date + * and time of deadline + * @throws DukeException exception thrown when the deadline is invalid due to + * missing deadline or improper input format + */ + + public Deadline(String description) throws DukeException { + this(description, false); + } + + private void updatePrintedString() { + assert descriptionWithoutDateTime != null; + assert deadlineDate != null; + assert deadlineTime != null; + + this.printedString = descriptionWithoutDateTime + " (by: " + + this.deadlineDate.format(DateTimeFormatter.ofPattern("MMM dd yyyy")) + " " + + this.deadlineTime.format(DateTimeFormatter.ofPattern("hh:mma")) + ")"; + } + + @Override + public void updateDate(String dateString) throws DukeException { + try { + this.deadlineDate = LocalDate.parse(dateString); + updatePrintedString(); + } catch (DateTimeParseException e) { + throw new DukeException("Unable to update deadline date due to improper input"); + } + } + + @Override + public void updateTime(String timeString) throws DukeException { + try { + this.deadlineTime = LocalTime.parse(timeString); + updatePrintedString(); + } catch (DateTimeParseException e) { + throw new DukeException("Unable to update deadline time due to improper input"); + } + } + + @Override + public void updateDescription(String newDescription) { + this.descriptionWithoutDateTime = newDescription; + updatePrintedString(); + } + + /** + * Returns the String representation of deadline task. + * @return string representation of deadline task + */ + @Override + public String toString() { + assert this.printedString != null; + + return this.isDone ? "!!!DONE!!! Deadline - " + this.printedString + : "Deadline - " + this.printedString; + } + + @Override + public String getType() { + return type; + } +} diff --git a/src/main/java/duke/tasks/Event.java b/src/main/java/duke/tasks/Event.java new file mode 100644 index 0000000000..0e67e898bc --- /dev/null +++ b/src/main/java/duke/tasks/Event.java @@ -0,0 +1,121 @@ +package duke.tasks; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; + +import duke.exceptions.DukeException; + +/** + * Event is a Task that should be attended at a certain date and time. + */ +public class Event extends Task { + private static String type = "E"; + private String printedString; + private String descriptionWithoutDateTime; + private LocalDate eventDate; + private LocalTime eventTime; + + /** + * Constructor for Event that takes in a description of the Event task and + * whether it is marked as done. + * @param description the description of the event task, containing the date and + * time of the event + * @param isDone true if the event task has been marked as done + * @throws DukeException exception thrown when the event datetime is invalid due + * to missing input or improper input format + */ + public Event(String description, boolean isDone) throws DukeException { + this.description = description; + this.isDone = isDone; + try { + String[] splitDescription = description.split("/at "); + descriptionWithoutDateTime = splitDescription[0]; + boolean containsDateTimeSpecification = splitDescription.length > 1; + + if (containsDateTimeSpecification) { + String dateAndTime = splitDescription[1]; + String[] splitDateTime = dateAndTime.split(" "); + boolean containsTimeSpecification = splitDateTime.length > 1; + + if (containsTimeSpecification) { + this.eventDate = LocalDate.parse(splitDateTime[0]); + this.eventTime = LocalTime.parse(splitDateTime[1]); + + updatePrintedString(); + } else { + throw new DukeException(DukeException.INVALID_FORMAT); + } + } else { + throw new DukeException(DukeException.INVALID_FORMAT); + } + } catch (DateTimeParseException e) { + throw new DukeException(DukeException.INVALID_FORMAT); + } + } + + /** + * Constructor for Event that takes in a description of the Event task. + * @param description the description of the event task, containing the date and + * time of the event + * @throws DukeException exception thrown when the event datetime is invalid due + * to missing input or improper input format + */ + public Event(String description) throws DukeException { + this(description, false); + } + + private void updatePrintedString() { + assert descriptionWithoutDateTime != null; + assert eventDate != null; + assert eventTime != null; + + this.printedString = descriptionWithoutDateTime + " (at: " + + this.eventDate.format(DateTimeFormatter.ofPattern("MMM dd yyyy")) + " " + + this.eventTime.format(DateTimeFormatter.ofPattern("hh:mma")) + ")"; + } + + @Override + public void updateDate(String dateString) throws DukeException { + try { + this.eventDate = LocalDate.parse(dateString); + updatePrintedString(); + } catch (DateTimeParseException e) { + throw new DukeException("Unable to update deadline date due to improper input"); + } + } + + @Override + public void updateTime(String timeString) throws DukeException { + try { + this.eventTime = LocalTime.parse(timeString); + updatePrintedString(); + } catch (DateTimeParseException e) { + throw new DukeException("Unable to update deadline time due to improper input"); + } + } + + @Override + public void updateDescription(String newDescription) { + this.descriptionWithoutDateTime = newDescription; + updatePrintedString(); + } + + /** + * Returns the String representation of event task. + * @return string representation of event task + */ + @Override + public String toString() { + assert this.printedString != null; + return this.isDone ? "!!!DONE!!! Event - " + this.printedString + : "Event - " + this.printedString; + } + + @Override + public String getType() { + return type; + } + +} diff --git a/src/main/java/duke/tasks/Task.java b/src/main/java/duke/tasks/Task.java new file mode 100644 index 0000000000..b8b3347375 --- /dev/null +++ b/src/main/java/duke/tasks/Task.java @@ -0,0 +1,37 @@ +package duke.tasks; + +import duke.exceptions.DukeException; + +/** + * Task is a class that manages the actions that a Task can do such as marking + * itself as done etc. + */ +public abstract class Task { + protected String type; + protected String description; + protected boolean isDone; + + /** + * Toggles the isDone value. + */ + public void toggleMark() { + this.isDone = !(this.isDone); + } + + /** + * Returns a boolean value depending on whether the task is done, + * if it is then the method returns true. + * @return true if the task has been marked as done + */ + public boolean isDone() { + return isDone; + } + + public abstract String getType(); + + public abstract void updateDescription(String description); + + public abstract void updateDate(String newDate) throws DukeException; + + public abstract void updateTime(String newTime) throws DukeException; +} diff --git a/src/main/java/duke/tasks/ToDo.java b/src/main/java/duke/tasks/ToDo.java new file mode 100644 index 0000000000..2c9a988498 --- /dev/null +++ b/src/main/java/duke/tasks/ToDo.java @@ -0,0 +1,60 @@ +package duke.tasks; + +import duke.exceptions.DukeException; + +/** + * ToDo is a Task with no specific start or end date or time. + */ +public class ToDo extends Task { + private static String type = "T"; + + /** + * Constructor for ToDo task takes in the description of the task and whether it + * has been marked as done. + * @param description the description of the ToDo task + * @param isDone true if the task has been marked as done + */ + public ToDo(String description, boolean isDone) { + this.description = description; + this.isDone = isDone; + } + + /** + * Constructor for ToDo task takes in the description of the task. + * @param description the description of the ToDo task + */ + public ToDo(String description) { + this(description, false); + } + + /** + * Returns the String representation of todo task. + * @return string representation of todo task + */ + @Override + public String toString() { + assert this.description != null; + return this.isDone ? "!!!DONE!!! To do - " + this.description + : "To do - " + this.description; + } + + @Override + public void updateDate(String newDate) throws DukeException { + throw new DukeException("No date value for Todo task"); + } + + @Override + public void updateTime(String newTime) throws DukeException { + throw new DukeException("No time value for Todo task"); + } + + @Override + public String getType() { + return type; + } + + @Override + public void updateDescription(String description) { + this.description = description; + } +} diff --git a/src/main/java/duke/tasks/Trigger.java b/src/main/java/duke/tasks/Trigger.java new file mode 100644 index 0000000000..ad2889710a --- /dev/null +++ b/src/main/java/duke/tasks/Trigger.java @@ -0,0 +1,59 @@ +package duke.tasks; + +import duke.exceptions.DukeException; + +/** + * Trigger is a Task to trigger a message. + */ +public class Trigger extends Task { + private static String type = "TRG"; + + /** + * Constructor for Trigger task takes in the description of the task and whether it + * has been marked as done. + * @param description the description of the Trigger task + * @param isDone true if the task has been marked as done + */ + public Trigger(String description, boolean isDone) { + this.description = description; + this.isDone = isDone; + } + + /** + * Constructor for Trigger task takes in the description of the task. + * @param description the description of the Trigger task + */ + public Trigger(String description) { + this(description, false); + } + + /** + * Returns the String representation of todo task. + * @return string representation of todo task + */ + @Override + public String toString() { + assert this.description != null; + return this.description; + } + + @Override + public void updateDate(String newDate) throws DukeException { + System.out.println("No date value for Todo task"); + } + + @Override + public void updateTime(String newTime) throws DukeException { + System.out.println("No time value for Todo task"); + } + + @Override + public String getType() { + return type; + } + + @Override + public void updateDescription(String description) { + this.description = description; + } +} diff --git a/src/main/resources/images/Aladdin.png b/src/main/resources/images/Aladdin.png new file mode 100644 index 0000000000..60add44011 Binary files /dev/null and b/src/main/resources/images/Aladdin.png differ diff --git a/src/main/resources/images/Genie.png b/src/main/resources/images/Genie.png new file mode 100644 index 0000000000..b8bfac6325 Binary files /dev/null and b/src/main/resources/images/Genie.png differ diff --git a/src/main/resources/view/DialogBox.fxml b/src/main/resources/view/DialogBox.fxml new file mode 100644 index 0000000000..b0ca251c38 --- /dev/null +++ b/src/main/resources/view/DialogBox.fxml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml new file mode 100644 index 0000000000..69fbe766e3 --- /dev/null +++ b/src/main/resources/view/MainWindow.fxml @@ -0,0 +1,19 @@ + + + + + + + + + + + +