diff --git a/README.md b/README.md index 0dd8f7fb21..9d95025bce 100644 --- a/README.md +++ b/README.md @@ -6,13 +6,16 @@ This is a project template for a greenfield Java project. It's named after the J Prerequisites: JDK 11, 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. Open Intellij (if you are not in the welcome screen, click `File` > `Close Project` to close the existing project dialog first) +1. Set up the correct JDK version, as follows: + 1. Click `Configure` > `Structure for New Projects` and then `Project Settings` > `Project` > `Project SDK` + 1. If JDK 11 is listed in the drop down, select it. If it is not, click `New...` and select the directory where you installed JDK 11 + 1. Click `OK` +1. Import the project into Intellij as follows: + 1. Click `Open or Import`. + 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 11** (not other versions) as explained in [here](https://www.jetbrains.com/help/idea/sdk.html#set-up-jdk). -1. After that, locate the `src/main/java/Duke.java` file, right-click it, and choose `Run Duke.main()`. If the setup is correct, you should see something like the below: +1. After the importing is complete, locate the `src/main/java/Duke.java` file, right-click it, and choose `Run Duke.main()`. If the setup is correct, you should see something like the below: ``` Hello from ____ _ diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000000..d24e14cd79 --- /dev/null +++ b/build.gradle @@ -0,0 +1,61 @@ +plugins { + id 'java' + id 'application' + id 'checkstyle' + id 'com.github.johnrengelman.shadow' version '5.1.0' +} + +repositories { + mavenCentral() +} + +dependencies { + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.5.0' + testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.5.0' + + String javaFxVersion = '11' + + 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 = "Launcher" +} + +shadowJar { + archiveBaseName = "blue" + archiveClassifier = null +} + +checkstyle { + toolVersion = '8.29' +} + +run{ + standardInput = System.in +} diff --git a/data/duke.txt b/data/duke.txt new file mode 100644 index 0000000000..999751b9df --- /dev/null +++ b/data/duke.txt @@ -0,0 +1,8 @@ +TODO[X] watch lecture +TODO[X] eat dinner +DDLN[ ] assignment (by: 2021-02-23) +TODO[ ] buy groceries +EVNT[ ] ice skating (at: 2022-03-15) +DDLN[ ] lecture quiz (by: 2021-04-01) +TODO[X] complete homework +TODO[ ] som diff --git a/docs/README.md b/docs/README.md index fd44069597..f997f7f7a3 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,20 +1,224 @@ # User Guide +Duke is a Command Line Interface (CLI) style desktop task +list application for managing your tasks, events and deadlines. + ## Features ++ Viewing help : `help` ++ Adding a to do task : `todo` ++ Adding a deadline : `deadline` ++ Adding an event : `event` ++ Listing all tasks : `list` ++ Completing a task : `done` ++ Deleting a task : `delete` ++ Sorting task list : `sort` ++ Locating a task : `find` ++ Exiting the program : `bye` + +## Viewing help : `help` +Shows complete list of commands. -### Feature 1 -Description of feature. +### Usage +Format: `help` -## Usage -### `Keyword` - Describe action +## Adding a to do task : `todo` +Adds a generic to do task to the task list. -Describe action and its outcome. +### Usage +Format: `todo {TASK_NAME}` ++ adds`TASK_NAME` to the task list as a to do task. Example of usage: -`keyword (optional arguments)` +`todo watch lecture videos` + +Expected outcome: + +`TODO[ ] watch lecture videos` will be added to the task list. + +## Adding a deadline : `deadline` +Adds a deadline to the task list. + +### Usage +Format: `deadline {DEADLINE_NAME} /by {YYYY-MM-DD}` ++ adds`DEADLINE_NAME` to the task list as a deadline. + +Example of usage: + +`deadline assignment /by 2021-02-20` + +Expected outcome: + +`DDLN[ ] assignment (by: Feb 20 2021)` will be added to the task list. + +## Adding a event : `event` +Adds an event to the task list. + +### Usage +Format: `event {EVENT_NAME} /at {YYYY-MM-DD}` ++ adds`EVENT_NAME` to the task list as an event. + +Example of usage: + +`event career fair /at 2021-04-02` + +Expected outcome: + +`EVNT[ ] career fair (at: Apr 2 2021)` will be added to the task list. + +## Listing all tasks : `list` +Displays all tasks in the task list. + +### Usage +Format: `list` + +Example of usage: + +`list` Expected outcome: -`outcome` + 1. TODO[ ] watch lecture videos + 2. DDLN[ ] assignment (by: Feb 20 2021) + 3. EVNT[ ] career fair (at: Apr 2 2021) + +## Completing a task : `done` +Marks a task as done. + +### Usage +Format: `done {INDEX}` ++ Marks the task corresponding to INDEX according to + the list as done. ++ List can be accessed with `list` command. + +Example of usage: + +`list` + + 1. TODO[ ] watch lecture videos + 2. DDLN[ ] assignment (by: Feb 20 2021) + 3. EVNT[ ] career fair (at: Apr 2 2021) + +`done 2` + +Expected outcome: + +`list` + + 1. TODO[ ] watch lecture videos + 2. DDLN[X] assignment (by: Feb 20 2021) + 3. EVNT[ ] career fair (at: Apr 2 2021) + +The second task on the list `2. DDLN[X] assignment (by: Feb 20 2021)` +will be marked as done. + +## + Deleting a task : `delete` +Deletes a task from the list. + +### Usage +Format: `delete {INDEX}` ++ Deletes the task corresponding to INDEX according to + the list ++ List can be accessed with `list` command. + +Example of usage: + +`list` + + 1. TODO[ ] watch lecture videos + 2. DDLN[X] assignment (by: Feb 20 2021) + 3. EVNT[ ] career fair (at: Apr 2 2021) + +`delete 2` + +Expected outcome: + + 1. TODO[ ] watch lecture videos + 2. EVNT[ ] career fair (at: Apr 2 2021) + +`DDLN[X] assignment (by: Feb 20 2021)` has been deleted +permanently from the task list. +## Sorting task list : `sort` +Sorts the task list by date. + +### Usage +Format: `sort` ++ Tasks are sorted by date. ++ Deadlines and events will be displayed in order of + increasing due date. ++ To do tasks without dates will be arranged below + deadlines and events ++ Sorting is for viewing only; the new indices of the + tasks will not apply when using `done` and `delete` + commands + +Example of usage: + +`list` + + 1. TODO[ ] watch lecture videos + 2. DDLN[X] assignment (by: Feb 20 2021) + 3. EVNT[ ] career fair (at: Apr 2 2021) + +`sort` + +Expected outcome: + +`list` + + 1. DDLN[X] assignment (by: Feb 20 2021) + 2. EVNT[ ] career fair (at: Apr 2 2021) + 3. TODO[ ] watch lecture videos + +`1. DDLN[X] assignment (by: Feb 20 2021)` and +`2. EVNT[ ] career fair (at: Apr 2 2021)`are sorted by date, while +`3. TODO[ ] watch lecture videos` has no date, and is sorted to +the bottom by default. + +## Locating a task : `find` +Searches and displays tasks. + +### Usage +Format: `find {KEYWORD}` ++ Tasks in the list that contain words that match the + KEYWORD will be displayed. ++ Task names or descriptions, types of task or dates can +all be used as keywords. ++ Keywords are case-sensitive and need to match the way the + list displays tasks, or the right results may not be retrieved. ++ Indices displayed in the results list are for viewing only, + and will not apply when using `done` and `delete` + commands +Example of usage: + +`list` + + 1. TODO[ ] watch lecture videos + 2. DDLN[X] assignment (by: Feb 20 2021) + 3. EVNT[ ] career fair (at: Apr 2 2021) + 4. DDLN[ ] study for quiz (by: Mar 15 2021) + +`find DDLN` + +Expected outcome: + + Here are the matching tasks in your list: + + 1. DDLN[X] assignment (by: Feb 20 2021) + 2. DDLN[ ] study for quiz (by: Mar 15 2021) + + Found 2 result(s). + +All tasks with `DDLN` in the list will be displayed. + + +## Exiting the program : `bye` +Exits program. + +### Usage +Format: `bye` + +## Acknowledgements +### External Libraries used: ++ javafx-sdk-11.0.2 \ No newline at end of file diff --git a/docs/README.md.orig b/docs/README.md.orig new file mode 100644 index 0000000000..4a6065776e --- /dev/null +++ b/docs/README.md.orig @@ -0,0 +1,228 @@ +# User Guide + +Duke is a Command Line Interface (CLI) style desktop task +list application for managing your tasks, events and deadlines. + +## Features ++ Viewing help : `help` ++ Adding a to do task : `todo` ++ Adding a deadline : `deadline` ++ Adding an event : `event` ++ Listing all tasks : `list` ++ Completing a task : `done` ++ Deleting a task : `delete` ++ Sorting task list : `sort` ++ Locating a task : `find` ++ Exiting the program : `bye` + +## Viewing help : `help` +Shows complete list of commands. + +### Usage +Format: `help` + + +## Adding a to do task : `todo` +Adds a generic to do task to the task list. + +### Usage +Format: `todo {TASK_NAME}` ++ adds`TASK_NAME` to the task list as a to do task. + +Example of usage: + +`todo watch lecture videos` + +Expected outcome: + +`TODO[ ] watch lecture videos` will be added to the task list. + +## Adding a deadline : `deadline` +Adds a deadline to the task list. + +### Usage +Format: `deadline {DEADLINE_NAME} /by {YYYY-MM-DD}` ++ adds`DEADLINE_NAME` to the task list as a deadline. + +Example of usage: + +`deadline assignment /by 2021-02-20` + +Expected outcome: + +`DDLN[ ] assignment (by: Feb 20 2021)` will be added to the task list. + +## Adding a event : `event` +Adds an event to the task list. + +### Usage +Format: `event {EVENT_NAME} /at {YYYY-MM-DD}` ++ adds`EVENT_NAME` to the task list as an event. + +Example of usage: + +`event career fair /at 2021-04-02` + +Expected outcome: + +`EVNT[ ] career fair (at: Apr 2 2021)` will be added to the task list. + +## Listing all tasks : `list` +Displays all tasks in the task list. + +### Usage +Format: `list` + +Example of usage: + +`list` + +Expected outcome: + + 1. TODO[ ] watch lecture videos + 2. DDLN[ ] assignment (by: Feb 20 2021) + 3. EVNT[ ] career fair (at: Apr 2 2021) + +## Completing a task : `done` +Marks a task as done. + +### Usage +Format: `done {INDEX}` ++ Marks the task corresponding to INDEX according to + the list as done. ++ List can be accessed with `list` command. + +Example of usage: + +`list` + + 1. TODO[ ] watch lecture videos + 2. DDLN[ ] assignment (by: Feb 20 2021) + 3. EVNT[ ] career fair (at: Apr 2 2021) + +`done 2` + +Expected outcome: + +`list` + + 1. TODO[ ] watch lecture videos + 2. DDLN[X] assignment (by: Feb 20 2021) + 3. EVNT[ ] career fair (at: Apr 2 2021) + +The second task on the list `2. DDLN[X] assignment (by: Feb 20 2021)` +will be marked as done. + +## + Deleting a task : `delete` +Deletes a task from the list. + +### Usage +Format: `delete {INDEX}` ++ Deletes the task corresponding to INDEX according to + the list ++ List can be accessed with `list` command. + +Example of usage: + +`list` + + 1. TODO[ ] watch lecture videos + 2. DDLN[X] assignment (by: Feb 20 2021) + 3. EVNT[ ] career fair (at: Apr 2 2021) + +`delete 2` + +Expected outcome: + + 1. TODO[ ] watch lecture videos + 2. EVNT[ ] career fair (at: Apr 2 2021) + +`DDLN[X] assignment (by: Feb 20 2021)` has been deleted +permanently from the task list. +## Sorting task list : `sort` +Sorts the task list by date. + +### Usage +Format: `sort` ++ Tasks are sorted by date. ++ Deadlines and events will be displayed in order of + increasing due date. ++ To do tasks without dates will be arranged below + deadlines and events ++ Sorting is for viewing only; the new indices of the + tasks will not apply when using `done` and `delete` + commands + +Example of usage: + +`list` + + 1. TODO[ ] watch lecture videos + 2. DDLN[X] assignment (by: Feb 20 2021) + 3. EVNT[ ] career fair (at: Apr 2 2021) + +`sort` + +Expected outcome: + +`list` + + 1. DDLN[X] assignment (by: Feb 20 2021) + 2. EVNT[ ] career fair (at: Apr 2 2021) + 3. TODO[ ] watch lecture videos + +`1. DDLN[X] assignment (by: Feb 20 2021)` and +`2. EVNT[ ] career fair (at: Apr 2 2021)`are sorted by date, while +`3. TODO[ ] watch lecture videos` has no date, and is sorted to +the bottom by default. + +## Locating a task : `find` +Searches and displays tasks. + +### Usage +Format: `find {KEYWORD}` ++ Tasks in the list that contain words that match the + KEYWORD will be displayed. ++ Task names or descriptions, types of task or dates can +all be used as keywords. ++ Keywords are case-sensitive and need to match the way the + list displays tasks, or the right results may not be retrieved. ++ Indices displayed in the results list are for viewing only, + and will not apply when using `done` and `delete` + commands +Example of usage: + +`list` + + 1. TODO[ ] watch lecture videos + 2. DDLN[X] assignment (by: Feb 20 2021) + 3. EVNT[ ] career fair (at: Apr 2 2021) + 4. DDLN[ ] study for quiz (by: Mar 15 2021) + +`find DDLN` + +Expected outcome: + + Here are the matching tasks in your list: + + 1. DDLN[X] assignment (by: Feb 20 2021) + 2. DDLN[ ] study for quiz (by: Mar 15 2021) + + Found 2 result(s). + +All tasks with `DDLN` in the list will be displayed. + + +## Exiting the program : `bye` +Exits program. + +### Usage +Format: `bye` + +## Acknowledgements +### External Libraries used: +<<<<<<< HEAD ++ javafx-sdk-11.0.2 +======= ++ javafx-sdk-11.0.2 +>>>>>>> b7d0bbeddb9d3c6f43a441c6dd2cc4a23f5bb526 diff --git a/docs/Ui.png b/docs/Ui.png new file mode 100644 index 0000000000..d877602e87 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..c4192631f2 --- /dev/null +++ b/docs/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-cayman \ 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..f3d88b1c2f Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000..b7c8c5dbf5 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100644 index 0000000000..2fe81a7d95 --- /dev/null +++ b/gradlew @@ -0,0 +1,183 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000000..62bd9b9cce --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,103 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/src/META-INF/MANIFEST.MF b/src/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..f578f7960d --- /dev/null +++ b/src/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: Launcher + diff --git a/src/main/data/duke.txt b/src/main/data/duke.txt new file mode 100644 index 0000000000..8c54c72511 --- /dev/null +++ b/src/main/data/duke.txt @@ -0,0 +1,9 @@ +TODO[ ] rea +EVNT[ ] play with bingrae (at: 2000-09-08) +TODO[ ] yoz +DDLN[ ] deadline hello (by: 2000-09-08) +EVNT[ ] event y (at: 2003-08-15) +TODO[X] yellow +TODO[ ] check check +EVNT[X] event yoooz (at: 2000-04-03) +DDLN[X] shit (by: 2021-03-09) diff --git a/src/main/java/Blue.java b/src/main/java/Blue.java new file mode 100644 index 0000000000..16e5125ce3 --- /dev/null +++ b/src/main/java/Blue.java @@ -0,0 +1,173 @@ +//package duke; + +import javafx.application.Application; +import javafx.scene.Scene; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.control.ScrollPane; +import javafx.scene.control.TextField; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.Region; +import javafx.scene.layout.VBox; +import javafx.stage.Stage; + +import java.util.Scanner; + +public class Blue extends Application { + private ScrollPane scrollPane; + private VBox dialogContainer; + private TextField userInput; + private Button sendButton; + private Scene scene; + private Image user = new Image(this.getClass().getResourceAsStream("/images/DaUser.png")); + private Image duke = new Image(this.getClass().getResourceAsStream("/images/DaDuke.png")); + + public static void main(String[] args) { + Parser parser = createParser(); + Ui.printWelcome(); + parser.parseAll(); + } + + static Parser createParser() { + // Set up data files + String path = "./data/duke.txt"; + String directory = "./data"; + Storage storage = new Storage(path, directory); + + // Initialise TaskList from file + TaskList taskList = new TaskList(storage); + + // Parse input + Parser parser = new Parser(taskList); + return parser; + } + + @Override + public void start(Stage stage) { + //Step 1. Setting up required components + + //The container for the content of the chat to scroll. + scrollPane = new ScrollPane(); + dialogContainer = new VBox(); + scrollPane.setContent(dialogContainer); + + userInput = new TextField(); + sendButton = new Button("Send"); + + AnchorPane mainLayout = new AnchorPane(); + mainLayout.getChildren().addAll(scrollPane, userInput, sendButton); + + scene = new Scene(mainLayout); + + stage.setScene(scene); + stage.show(); + + // more code to be added here later + //Step 2. Formatting the window to look as expected + stage.setTitle("Duke"); + stage.setResizable(false); + stage.setMinHeight(600.0); + stage.setMinWidth(400.0); + + mainLayout.setPrefSize(400.0, 600.0); + + scrollPane.setPrefSize(385, 535); + scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER); + scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.ALWAYS); + + scrollPane.setVvalue(1.0); + scrollPane.setFitToWidth(true); + + // You will need to import `javafx.scene.layout.Region` for this. + dialogContainer.setPrefHeight(Region.USE_COMPUTED_SIZE); + + userInput.setPrefWidth(325.0); + + sendButton.setPrefWidth(55.0); + + AnchorPane.setTopAnchor(scrollPane, 1.0); + + AnchorPane.setBottomAnchor(sendButton, 1.0); + AnchorPane.setRightAnchor(sendButton, 1.0); + + AnchorPane.setLeftAnchor(userInput , 1.0); + AnchorPane.setBottomAnchor(userInput, 1.0); + + Label introText = new Label(Ui.welcomeString()); + dialogContainer.getChildren().addAll( + DialogBox.getDukeDialog(introText, new ImageView(duke)) + ); + userInput.clear(); + // more code to be added here later + //Step 3. Add functionality to handle user input. + sendButton.setOnMouseClicked((event) -> { + handleUserInput(); + }); + + userInput.setOnAction((event) -> { + handleUserInput(); + }); + + //Scroll down to the end every time dialogContainer's height changes. + dialogContainer.heightProperty().addListener((observable) -> scrollPane.setVvalue(1.0)); + } + /** + * Iteration 1: + * Creates a label with the specified text and adds it to the dialog container. + * @param text String containing text to add + * @return a label with the specified text that has word wrap enabled. + */ + private Label getDialogLabel(String text) { + // You will need to import `javafx.scene.control.Label`. + Label textToAdd = new Label(text); + textToAdd.setWrapText(true); + + return textToAdd; + } + /** + * Iteration 2: + * 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. + */ + + private void handleUserInput() { + Label userText = new Label(userInput.getText()); + Label dukeText = new Label(getResponse(userInput.getText())); + assert user != null : "Error: User image does not exist"; + assert duke != null : "Error: Duke image does not exist"; + dialogContainer.getChildren().addAll( + DialogBox.getUserDialog(userText, new ImageView(user)), + DialogBox.getDukeDialog(dukeText, new ImageView(duke)) + ); + userInput.clear(); + } + /* + private void handleUserInput() { + String input = userInput.getText(); + String response = getResponse(input); + dialogContainer.getChildren().addAll( + DialogBox.getUserDialog(input, user), + DialogBox.getDukeDialog(response, duke) + ); + userInput.clear(); + }*/ + + + /** + * You should have your own function to generate a response to user input. + * Replace this stub with your completed method. + */ + public String getResponse(String input) { + Parser parser = createParser(); + if (!input.equals("bye")) { + return parser.parseLine(input); + } else { + System.exit(0); + return Ui.byeString(); + } + } +} + + diff --git a/src/main/java/Deadline.java b/src/main/java/Deadline.java new file mode 100644 index 0000000000..7fd301ba44 --- /dev/null +++ b/src/main/java/Deadline.java @@ -0,0 +1,58 @@ +//package duke; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; + +public class Deadline extends TimedTask { + + Deadline() { + this.isDone = false; + } + + static Deadline parseInput(String input) throws DukeIncompleteCommandException, + DateTimeParseException { + + Deadline deadline = new Deadline(); + input = input.substring(8).trim(); + + if (input.equals("")) { + throw new DukeIncompleteCommandException(); + } + + String[] inputs = input.split("/by"); + + if (!input.contains("/by") || inputs.length < 2) { + throw new DukeIncompleteCommandException("Oh no! Please enter an due date. :P\n"); + } + deadline.task = inputs[0].trim(); + deadline.isDone = false; + deadline.date = LocalDate.parse(inputs[1].trim()); + + return deadline; + } + + static Deadline fileReader(String line) { + Deadline deadline = new Deadline(); + if (line.charAt(5) == 'X') { + deadline.isDone = true; + } else { + deadline.isDone = false; + } + String[] lines = line.substring(7).trim().split("by: "); + deadline.task = lines[0].substring(0, lines[0].length() - 2).trim(); + String dateString = lines[1].substring(0, lines[1].length() - 1); + deadline.date = LocalDate.parse(dateString.subSequence(0, dateString.length())); + return deadline; + } + + @Override + public String toString() { + return String.format("DDLN%s (by: %s)", super.toString(), + date.format(DateTimeFormatter.ofPattern("MMM d yyyy"))); + } + + public String toFileString() { + return String.format("DDLN%s (by: %s)", super.toString(), date); + } +} diff --git a/src/main/java/DialogBox.java b/src/main/java/DialogBox.java new file mode 100644 index 0000000000..b264da45c6 --- /dev/null +++ b/src/main/java/DialogBox.java @@ -0,0 +1,56 @@ +//package duke; + +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 { + private Label text; + private ImageView displayPicture; + + public DialogBox(Label l, ImageView iv) { + text = l; + displayPicture = iv; + + text.setWrapText(true); + displayPicture.setFitWidth(100.0); + displayPicture.setFitHeight(100.0); + + this.setAlignment(Pos.TOP_RIGHT); + this.getChildren().addAll(text, displayPicture); + } + + /** + * 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(Label l, ImageView iv) { + return new DialogBox(l, iv); + } + + public static DialogBox getDukeDialog(Label l, ImageView iv) { + var db = new DialogBox(l, iv); + db.flip(); + return db; + } +} 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/DukeIncompleteCommandException.java b/src/main/java/DukeIncompleteCommandException.java new file mode 100644 index 0000000000..54327a82e9 --- /dev/null +++ b/src/main/java/DukeIncompleteCommandException.java @@ -0,0 +1,25 @@ +//package duke; + +public class DukeIncompleteCommandException extends Exception { + public String message; + + DukeIncompleteCommandException() { + this.message = "Oh no! Task cannot be empty. ):\n"; + } + + DukeIncompleteCommandException(String message) { + this.message = message; + } + /** + * Returns an incomplete command error message. + * @return Error message in the form of a string. + */ + @Override + public String getMessage() { + return message; + } + + public String toString() { + return message; + } +} diff --git a/src/main/java/DukeInvalidCommandException.java b/src/main/java/DukeInvalidCommandException.java new file mode 100644 index 0000000000..ca7c03c19b --- /dev/null +++ b/src/main/java/DukeInvalidCommandException.java @@ -0,0 +1,21 @@ +//package duke; + +public class DukeInvalidCommandException extends Exception { + public String message = "Oh no! I don't know what that means. ):\nType 'help' for more details."; + + DukeInvalidCommandException() { + } + + /** + * Returns an invalid command error message. + * @return Error message in the form of a string. + */ + @Override + public String getMessage() { + return message; + } + + public String toString() { + return message; + } +} diff --git a/src/main/java/Event.java b/src/main/java/Event.java new file mode 100644 index 0000000000..afd00d3b90 --- /dev/null +++ b/src/main/java/Event.java @@ -0,0 +1,62 @@ +//package duke; + +import java.time.format.DateTimeFormatter; + +public class Event extends TimedTask { + + public Event() { + this.isDone = false; + } + + /* + static Event parseInput(String input) throws DukeIncompleteCommandException, + DateTimeParseException { + + Event event = new Event(); + input = input.substring(5).trim(); + + if (input.equals("")) { + throw new DukeIncompleteCommandException(); + } + + String[] inputs = input.split("/at"); + + if (!input.contains("/at") || inputs.length < 2) { + throw new DukeIncompleteCommandException("Oh no! Please enter an event date. :P\n"); + } + + event.task = inputs[0].trim(); + event.isDone = false; + event.date = LocalDate.parse(inputs[1].trim()); + return event; + } + + static Event fileReader(String line) { + Event event = new Event(); + if (line.charAt(5) == 'X') { + event.isDone = true; + } else { + event.isDone = false; + } + String[] lines = line.substring(7).trim().split("at: "); + event.task = lines[0].substring(0, lines[0].length() - 2).trim(); + String dateString = lines[1].substring(0, lines[1].length() - 1); + event.date = LocalDate.parse(dateString.subSequence(0, dateString.length())); + return event; + } + + */ + @Override + public String toString() { + return String.format("EVNT%s (at: %s)", super.toString(), + date.format(DateTimeFormatter.ofPattern("MMM d yyyy"))); + } + /** + * Creates a string for saving in the file. + * @return String to be saved in file. + */ + @Override + public String toFileString() { + return String.format("EVNT%s (at: %s)", super.toString(), date); + } +} diff --git a/src/main/java/Launcher.java b/src/main/java/Launcher.java new file mode 100644 index 0000000000..058d60acca --- /dev/null +++ b/src/main/java/Launcher.java @@ -0,0 +1,13 @@ +//package duke; + +import javafx.application.Application; + +/** + * A launcher class to workaround classpath issues. + */ +public class Launcher { + public static void main(String[] args) { + Application.launch(Blue.class, args); + } +} + diff --git a/src/main/java/MainWindow.java b/src/main/java/MainWindow.java new file mode 100644 index 0000000000..d0142d047e --- /dev/null +++ b/src/main/java/MainWindow.java @@ -0,0 +1,51 @@ +public class MainWindow { +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/DaUser.png")); + private Image dukeImage = new Image(this.getClass().getResourceAsStream("/images/DaDuke.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() { + String input = userInput.getText(); + String response = duke.getResponse(input); + dialogContainer.getChildren().addAll( + DialogBox.getUserDialog(input, userImage), + DialogBox.getDukeDialog(response, dukeImage) + ); + userInput.clear(); + } + } +} diff --git a/src/main/java/Parser.java b/src/main/java/Parser.java new file mode 100644 index 0000000000..b931b07b3e --- /dev/null +++ b/src/main/java/Parser.java @@ -0,0 +1,206 @@ +//package duke; + +import java.time.LocalDate; +import java.time.format.DateTimeParseException; +import java.util.Scanner; + +/** + * Class containing methods for parsing user input and + * file contents. + */ +public class Parser { + TaskList taskList; + + Parser(TaskList taskList) { + this.taskList = taskList; + } + + /** + * Parses all user input and processes each line and + * command. + */ + + void parseAll() { + Scanner sc = new Scanner(System.in); + String line = sc.nextLine(); + while (!line.equals("bye")) { + System.out.println(parseLine(line)); + line = sc.nextLine(); + } + System.exit(0); + sc.close(); + } + + /** + * Parses each line of user input and determines if it + * is a general command or a command for adding tasks. + * @param input Each line of user input + */ + String parseLine(String input) { + try { + if (input.equals("list") || input.contains("done") + || input.contains("delete") || input.contains("find") + || input.equals("sort") || input.equals("help")) { + return parseGeneralCommand(input); + } else if (input.contains("todo") || + input.contains("deadline") || + input.contains("event")) { + return taskList.add(parseAddTaskCommand(input)); + } else { + throw new DukeInvalidCommandException(); + } + } catch (DukeInvalidCommandException e) { + return (e.getMessage()); + } catch (DukeIncompleteCommandException e) { + return e.getMessage(); + } catch (DateTimeParseException e) { + return Ui.dateFormatErrorString(); + } + } + + /** + * Parses commands like "list", "done" and "delete". + * @param input User input that are general commands. + * @throws DukeIncompleteCommandException If "done" or "delete" + * commands do not state an index. + */ + + String parseGeneralCommand(String input) throws DukeIncompleteCommandException { + String[] inputs; + int index; + if (input.equals("list")) { + return taskList.listString(); + } else if (input.contains("done")) { + inputs = input.split(" "); + assert (inputs[1] != "") : "No index found!"; + index = Integer.parseInt(inputs[1]) - 1; + return taskList.markDone(index); + } else if (input.contains("delete")) { + inputs = input.split(" "); + assert (inputs[1] != "") : "No index found!"; + index = Integer.parseInt(inputs[1]) - 1; + return taskList.delete(index); + } else if (input.contains("find")) { + inputs = input.split(" "); + assert (inputs[1] != "") : "No index found!"; + String keyword = inputs[1]; + return taskList.find(keyword); + } else if (input.equals("sort")) { + return taskList.sortByDate(); + } else { + return Ui.helpString(); + } + } + + /** + * Parses commands for adding tasks like "todo", "event" and "deadline". + * @param input User input that are task adding commands. + * @return Task that corresponds to the input. + * @throws DukeIncompleteCommandException If no task has been specified. + * @throws DateTimeParseException If date is not in the YYYY-MM-DD format. + */ + public static Task parseAddTaskCommand(String input) throws DukeIncompleteCommandException, + DateTimeParseException { + + if (input.contains("todo")) { + return parseAddToDoCommand(input); + } else { + return parseAddTimedTaskCommand(input); + } + } + /** + * Parses commands for adding todo tasks. + * @param input User input that are task adding commands. + * @return Task that corresponds to the input. + * @throws DukeIncompleteCommandException If no task has been specified. + */ + public static Task parseAddToDoCommand(String input) throws DukeIncompleteCommandException { + + assert (input.contains(" ")) : "Space between command and task needed!"; + String editedInput = input.substring(4).trim(); + // Catch incomplete commands + if (editedInput.equals("")) { + throw new DukeIncompleteCommandException(); + } + Task task = new ToDo(editedInput); + return task; + } + /** + * Parses commands for adding timed tasks like events and deadlines. + * @param input User input that are task adding commands. + * @return Task that corresponds to the input. + * @throws DukeIncompleteCommandException If no task has been specified. + * @throws DateTimeParseException If date is not in the YYYY-MM-DD format. + */ + public static Task parseAddTimedTaskCommand(String input) throws DukeIncompleteCommandException, + DateTimeParseException { + // Get rid of command string and get date regex + assert (input.contains(" ")): "Space between command and task needed!"; + TimedTask timedTask = new TimedTask(); + String editedInput; + String regex; + if (input.contains("deadline")) { + editedInput = input.substring(8).trim(); + regex = "/by"; + timedTask = new Deadline(); + } else { + editedInput = input.substring(5).trim(); + regex = "/at"; + timedTask = new Event(); + } + // Catch incomplete commands + if (editedInput.equals("")) { + throw new DukeIncompleteCommandException(); + } + String[] editedInputs = editedInput.split(regex); + timedTask.task = editedInputs[0].trim(); + try { + timedTask.date = LocalDate.parse(editedInputs[1].trim()); + return timedTask; + } catch (DateTimeParseException e) { + throw e; + } + } + + + /** + * Parses data stored in file. + * @param line Each line stored in data.txt. + * @return Task that corresponds to each line in data.txt. + */ + static Task parseFile(String line) { + if (line.contains("TODO")) { + Task task = new ToDo(); + if (line.charAt(5) == 'X') { + task.isDone = true; + } else { + task.isDone = false; + } + task.task = line.substring(7).trim(); + return task; + } else { + TimedTask timedTask; + String regex; + if (line.contains("DDLN")) { + timedTask = new Deadline(); + regex = "by: "; + } else { + timedTask = new Event(); + regex = "at: "; + } + if (line.charAt(5) == 'X') { + timedTask.isDone = true; + } else { + timedTask.isDone = false; + } + String[] lines = line.substring(7).trim().split(regex); + timedTask.task = lines[0].substring(0, lines[0].length() - 2).trim(); + String dateString = lines[1].substring(0, lines[1].length() - 1); + timedTask.date = LocalDate.parse(dateString.subSequence(0, dateString.length())); + return timedTask; + } + } + +} + + diff --git a/src/main/java/Printer.java b/src/main/java/Printer.java new file mode 100644 index 0000000000..f3292c4d86 --- /dev/null +++ b/src/main/java/Printer.java @@ -0,0 +1,84 @@ +import java.util.ArrayList; + +/** + * Class containing methods for printing different messages + * according to the situation. + */ +public class Printer { + /** + * Prints welcome message. + */ + static void printWelcome() { + String logo = " ____ _ \n" + + "| _ \\ _ _| | _____ \n" + + "| | | | | | | |/ / _ \\\n" + + "| |_| | |_| | < __/\n" + + "|____/ \\__,_|_|\\_\\___|\n"; + System.out.println(logo); + System.out.println("Hello! I'm Duke! \n" + + "What would you like to do today? \n" + + "***********************************"); + } + + /** + * Prints farewell message upon exit. + */ + static void printBye() { + System.out.println("Bye! Stay on task!"); + } + + /** + * Prints text accompanying a "done" command. + * @param task Task that has been marked as done. + */ + static void printDone(Task task) { + System.out.println("Good job! I've marked this task as done:\n " + + task + "\n"); + } + + /** + * Prints text accompanying a "delete" command. + * @param task Task that has been deleted. + * @param size Size of remaining taskList. + */ + static void printDelete(Task task, int size) { + System.out.println("Alright, I've deleted this task:\n " + + task); + + System.out.println("Now you have " + size + + " task(s) in the list. \n"); + } + + /** + * Prints text accompanying "add" command. + * @param task Task that has been added. + * @param size Size of new taskList. + */ + + static void printAdd(Task task, int size) { + System.out.println("Alright! I've added this task: \n " + + task + "\nNow you have " + size + + " task(s) in the list. \n"); + } + + /** + * Prints error message for IndexOutOfBoundsExceptions + */ + static void printIndexError() { + System.out.println("Oh no! This task does not exist. D:\n"); + } + + /** + * Prints error message when date is typed in the wrong format. + */ + static void printDateFormatError() { + System.out.println("Oh no! " + + " Please key in the date in the format YYYY-MM-DD.\n"); + } + static void printFind(ArrayList tasks) { + System.out.println("Here are the matching tasks in your list:\n"); + TaskList.displayTabbedList(tasks); + System.out.println("Found " + tasks.size() + " result(s).\n"); + } +} + diff --git a/src/main/java/Storage.java b/src/main/java/Storage.java new file mode 100644 index 0000000000..fbca7577e2 --- /dev/null +++ b/src/main/java/Storage.java @@ -0,0 +1,86 @@ +//package duke; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Scanner; + +/** + * Class with methods for saving and loading files. + */ +public class Storage { + String path; + String directory; + + Storage() { + } + + Storage(String path, String directory) { + this.path = path; + this.directory = directory; + } + + /** + * Loads data from files if there is data to load. + * @return A list of tasks according to the saved data. + */ + ArrayList loadFile() { + // String path = "./data/duke.txt"; + // String directory = "./data"; + File d = new File(directory); + File f = new File(path); + ArrayList list = new ArrayList<>(); + if (!d.exists()) { + d.mkdirs(); + } + try { + f.createNewFile(); + Scanner sc = new Scanner(f); + while (sc.hasNext()) { + try { + String line = sc.nextLine(); + Task task = Parser.parseFile(line); + list.add(task); + } catch (Exception e) { + } + } + } catch (IOException e) { + System.out.println(e.getMessage()); + } + return list; + } + + /** + * Saves individual tasks to the existing data file. + * @param input String to be stored in the file. + */ + void saveFile(String input) { + // String path = "./data/duke.txt"; + try { + FileWriter fw = new FileWriter(path, true); + fw.write(input + System.lineSeparator()); + fw.close(); + } catch (IOException e) { + System.out.println("An error has occurred, unable to save."); + e.getMessage(); + } + } + + /** + * Overwrites file with new list of task if changes are made to the list. + * @param list New list of tasks to be saved. + */ + void saveFile(ArrayList list) { + // String path = "./data/duke.txt"; + try { + FileWriter fw = new FileWriter(path); + for (Task t : list) { + fw.write(t.toFileString() + System.lineSeparator()); + } + fw.close(); + } catch (Exception e) { + System.out.println(e.getMessage()); + } + } +} diff --git a/src/main/java/Task.java b/src/main/java/Task.java new file mode 100644 index 0000000000..9a8c9fdcf8 --- /dev/null +++ b/src/main/java/Task.java @@ -0,0 +1,60 @@ +//package duke; + +import java.time.LocalDate; +public class Task { + String task; + boolean isDone; + LocalDate date; + + Task() { + } + + Task(String task) { + this.task = task.trim(); + isDone = false; + } + + boolean isTimedTask() { + return false; + } + + /** + * Marks task as done. + * @return Task that has been marked as done. + */ + public Task markDone() { + isDone = true; + return this; + } + + /* + static Task parseInput(String input) throws DukeIncompleteCommandException { + Task task; + if (input.contains("todo")) { + task = ToDo.parseInput(input); + } else if (input.contains("deadline")) { + task = Deadline.parseInput(input); + } else { + task = Event.parseInput(input); + } + return task; + } + */ + + @Override + public String toString() { + if (isDone) { + return "[X] " + task; + } else { + return "[ ] " + task; + } + } + + /** + * Creates a string for saving in the file. + * @return String to be saved in file. + */ + public String toFileString() { + return toString(); + } +} diff --git a/src/main/java/TaskComparator.java b/src/main/java/TaskComparator.java new file mode 100644 index 0000000000..477b53e2a0 --- /dev/null +++ b/src/main/java/TaskComparator.java @@ -0,0 +1,16 @@ +import java.util.Comparator; + +public class TaskComparator implements Comparator { + @Override + public int compare(Task t1, Task t2) { + if (t1.isTimedTask() && !t2.isTimedTask()) { + return -1; + } else if (!t1.isTimedTask() && t2.isTimedTask()) { + return 1; + } else if (t1.isTimedTask() && t1.isTimedTask()) { + return t1.date.compareTo(t2.date); + } else { + return 0; + } + } +} diff --git a/src/main/java/TaskList.java b/src/main/java/TaskList.java new file mode 100644 index 0000000000..524c70228c --- /dev/null +++ b/src/main/java/TaskList.java @@ -0,0 +1,116 @@ +//package duke; + +import java.util.ArrayList; + +public class TaskList { + ArrayList list; + Storage storage; + + TaskList(Storage storage) { + this.storage = storage; + this.list = this.storage.loadFile(); + } + + TaskList(ArrayList list, Storage storage) { + this.list = list; + this.storage = storage; + } + + /** + * Loads saved data into list attribute. + */ + void loadList() { + this.list = this.storage.loadFile(); + } + + /** + * Prints out task list. + */ + void displayList() { + for (int i = 0; i < list.size(); i++) { + System.out.printf("%d. %s%n", i + 1, list.get(i).toString()); + } + System.out.println("\n"); + } + static void displayTabbedList(ArrayList tasks) { + for(int i = 0; i < tasks.size(); i++) { + System.out.printf(" %d. %s%n",i + 1, tasks.get(i).toString()); + } + System.out.println("\n"); + } + public String listString() { + String listString = ""; + for(int i = 0; i < list.size(); i++) { + listString += String.format("%d. %s%n",i + 1, list.get(i).toString()); + } + return listString; + } + static String tabbedListString(ArrayList tasks) { + String tabbedList = ""; + for(int i = 0; i < tasks.size(); i++) { + tabbedList += String.format(" %d. %s%n",i + 1, tasks.get(i).toString()); + } + return tabbedList; + } + /** + * Marks the task specified as done. + * + * @param index Index of task to mark. + */ + String markDone(int index) { + try { + Task task = list.get(index); + task.markDone(); + storage.saveFile(list); + return Ui.doneString(task); + // Ui.printDone(task); + } catch (IndexOutOfBoundsException e) { + return Ui.indexErrorString(); + } + } + + /** + * Deletes the task specified. + * @param index Index of task to delete. + */ + String delete(int index) { + try { + Task task = list.get(index); + list.remove(index); + storage.saveFile(list); + return Ui.deleteString(task, list.size()); + } catch (IndexOutOfBoundsException e) { + return Ui.indexErrorString(); + } + + } + + /** + * Adds the task specified to the list. + * @param task Task to be added to list. + */ + String add(Task task) { + list.add(task); + storage.saveFile(task.toFileString()); + return Ui.addString(task, list.size()); + } + + String find(String keyword) { + ArrayList newList = new ArrayList<>(); + for (int i = 0; i < list.size(); i++) { + String taskString = list.get(i).toString(); + if (taskString.contains(keyword)) { + newList.add(list.get(i)); + } + } + return Ui.findString(newList); + } + + String sortByDate() { + ArrayList sortedList = new ArrayList<>(list); + sortedList.sort(new TaskComparator()); + return Ui.sortString(sortedList); + } + + +} diff --git a/src/main/java/TimedTask.java b/src/main/java/TimedTask.java new file mode 100644 index 0000000000..018c704e73 --- /dev/null +++ b/src/main/java/TimedTask.java @@ -0,0 +1,29 @@ +//package duke; + +import java.time.LocalDate; + +public class TimedTask extends Task { + + TimedTask() { + } + + @Override + boolean isTimedTask() { + return true; + } + /* + static T fileReader(String line, String regex) { + TimedTask t = new T<>(); + if (line.charAt(5) == 'X') { + t.isDone = true; + } else { + t.isDone = false; + } + String[] lines = line.substring(7).trim().split(regex); + t.task = lines[0].substring(0, lines[0].length() - 2).trim(); + t.date = lines[1].substring(0, lines[1].length() - 1); + return t; + } + */ + +} diff --git a/src/main/java/ToDo.java b/src/main/java/ToDo.java new file mode 100644 index 0000000000..5536098b60 --- /dev/null +++ b/src/main/java/ToDo.java @@ -0,0 +1,41 @@ +//package duke; + + + +public class ToDo extends Task { + ToDo() { + } + + public ToDo(String task) { + super(task); + } + + /* + static ToDo parseInput(String input) throws DukeIncompleteCommandException { + ToDo todo = new ToDo(); + input = input.substring(4).trim(); + if (input.equals("")) { + throw new DukeIncompleteCommandException(); + } + todo.task = input.trim(); + todo.isDone = false; + return todo; + } + + static ToDo fileReader(String line) { + ToDo todo = new ToDo(); + if (line.charAt(5) == 'X') { + todo.isDone = true; + } else { + todo.isDone = false; + } + todo.task = line.substring(7).trim(); + return todo; + } + */ + + @Override + public String toString() { + return "TODO" + super.toString(); + } +} diff --git a/src/main/java/Ui.java b/src/main/java/Ui.java new file mode 100644 index 0000000000..43886c9717 --- /dev/null +++ b/src/main/java/Ui.java @@ -0,0 +1,102 @@ +//package duke; + +import java.util.ArrayList; + +/** + * Class containing methods for printing different messages + * according to the situation. + */ +public class Ui { + /** + * Prints welcome message. + */ + static void printWelcome() { + String logo = " ____ _ \n" + + "| _ \\ _ _| | _____ \n" + + "| | | | | | | |/ / _ \\\n" + + "| |_| | |_| | < __/\n" + + "|____/ \\__,_|_|\\_\\___|\n"; + System.out.println(logo); + System.out.println("Hello! I'm Blue! \n" + + "What would you like to do today? \n" + + "***********************************"); + } + static String welcomeString() { + return "Hello! I'm Blue! \n" + + "What would you like to do today? \n" + + "Type 'help' to view the list of commands."; + } + + /** + * Returns farewell message upon exit. + */ + static String byeString() { + return ("Bye! Stay on task!"); + } + + static String helpString() { + return "List of commands: \n\n" + + "Viewing help : help\n" + + "Adding a to do task : todo {TASK_NAME}\n" + + "Adding a deadline : deadline {DEADLINE_NAME} /by {YYYY-MM-DD}'\n" + + "Adding a event : event {EVENT_NAME} /at {YYYY-MM-DD}'\n" + + "Listing all tasks : list\n" + + "Completing a task : done {INDEX}\n" + + "Deleting a task : delete {INDEX}\n" + + "Sorting task list : sort\n" + + "Locating a task : find {KEYWORD}\n" + + "Exiting the program : bye\n\n" + + "Visit the user guide (https://andrea-twl.github.io/ip/) for more details.\n"; + } + /** + * Returns String accompanying a "done" command. + * @param task Task that has been marked as done. + */ + static String doneString(Task task) { + return ("Good job! I've marked this task as done:\n " + task + "\n"); + } + + /** + * Prints text accompanying a "delete" command. + * @param task Task that has been deleted. + * @param size Size of remaining taskList. + */ + static String deleteString(Task task, int size) { + return ("Alright, I've deleted this task:\n " + + task + + "\nNow you have " + size + " task(s) in the list.\n"); + } + + /** + * Prints text accompanying "add" command. + * @param task Task that has been added. + * @param size Size of new taskList. + */ + static String addString(Task task, int size) { + return ("Alright! I've added this task: \n " + + task + + "\nNow you have " + size + " task(s) in the list. \n"); + } + + /** + * Prints error message for IndexOutOfBoundsExceptions + */ + static String indexErrorString() { + return ("Oh no! This task does not exist. D:\n"); + } + + /** + * Prints error message when date is typed in the wrong format. + */ + static String dateFormatErrorString() { + return ("Oh no! Please key in the date in the format YYYY-MM-DD.\n"); + } + static String findString(ArrayList tasks) { + return "Here are the matching tasks in your list:\n" + + TaskList.tabbedListString(tasks) + + "Found " + tasks.size() + " result(s).\n"; + } + static String sortString(ArrayList tasks) { + return "I've sorted your list by date:\n" + TaskList.tabbedListString(tasks); + } +} diff --git a/src/main/resources/images/DaDuke.png b/src/main/resources/images/DaDuke.png new file mode 100644 index 0000000000..013bbcf1b8 Binary files /dev/null and b/src/main/resources/images/DaDuke.png differ diff --git a/src/main/resources/images/DaUser.png b/src/main/resources/images/DaUser.png new file mode 100644 index 0000000000..aee7f99a75 Binary files /dev/null and b/src/main/resources/images/DaUser.png differ diff --git a/src/main/resources/view/DialogBox.fxml b/src/main/resources/view/DialogBox.fxml new file mode 100644 index 0000000000..c9521bd5af --- /dev/null +++ b/src/main/resources/view/DialogBox.fxml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml new file mode 100644 index 0000000000..6f988e7544 --- /dev/null +++ b/src/main/resources/view/MainWindow.fxml @@ -0,0 +1,19 @@ + + + + + + + + + + + +