diff --git a/DukeySave.txt b/DukeySave.txt new file mode 100644 index 0000000000..10dd14a788 --- /dev/null +++ b/DukeySave.txt @@ -0,0 +1,2 @@ +T,1,eat +D,0,eat,2022-12-12 diff --git a/README.md b/README.md index 8715d4d915..98b9c6c7e6 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,340 @@ -# Duke project template - -This is a project template for a greenfield Java project. It's named after the Java mascot _Duke_. Given below are instructions on how to use it. - -## Setting up in Intellij - -Prerequisites: JDK 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. 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 - ____ _ - | _ \ _ _| | _____ - | | | | | | | |/ / _ \ - | |_| | |_| | < __/ - |____/ \__,_|_|\_\___| - ``` +# User Guide + +## DukeyList +DukeyList a great and easy way to manage and keep track of your tasks! Using DukeyList, you can add and remove tasks, +and mark them as completed. + +### DukeyList is able to handle these types of tasks: +* **ToDos** - Tasks that need to completed +* **Deadlines** - Tasks that need to be completed by a certain date +* **Events** - Tasks that span over a period +* __Loans__ (extension) - Tasks to keep track of debt + +## DukeyList Features + +### Task list +DukeyList keeps track of all your tasks. You can view your tasks, the task status (whether they are complete or not), +the type of tasks and the deadline/dates associated with your tasks. +```ignorelang +1. [T][ ] Buy Fruits +2. [D][X] Submit forms (by 12 Feb 2023) +3. [E][ ] Seminar (15 Feb 2023 to 16 Feb 2023) +4. [L][ ] Gas money: Brother owes $10 to Mother +``` + +### Add Tasks +Add a new task to DukeyList. DukeyList will continue to keep track of the task until it is deleted from the list. + +### Mark/Unmark Tasks +Should you be done with a task, you can mark it as done. DukeyList represents marked tasks with an 'X': +```ignorelang +2. [D][X] Submit forms (by 12 Feb 2023) +``` +Similarly, tasks can be unmarked as well. + +### Delete Tasks +You can delete a task from DukeyList. The task will no longer be shown on the list after deletion. + +### Find Tasks +You can search for tasks using a certain keyword. DukeyList will return you a list of tasks containing +the keyword, if there are any. +```ignorelang +DukeyList found these tasks matching the keyword "buy": +1. [T][ ] Buy Fruits +``` + +### Save +You can save DukeyList locally, meaning that your tasks will be stored even after you exit the DukeyList app. +DukeyList automatically loads up the most recent save whenever it starts. + +## Starting up DukeyList +Once you start up DukeyList, it will automatically load up your most recent save, if any. +Should there be a saved list, you will see the following message: +``` +Saved list loaded: +``` +If there is no previous save, the following message will show instead. This means that you will +start with a new empty list. +``` +DukeyList empty, starting a new list: +``` +You can now start typing commands into DukeyList! + + + +##Using DukeyList +To use DukeyList, type in a `command`, followed by necessary fields and press enter. Each `field` is separated by a '/' +character. The `command` is also followed by the '/' character. +The format and usages of various commands are shown below. Note that commands are not case-sensitive. +Be sure to follow the formats as closely as you can! Using a command with the incorrect format for fields will lead +to __errors__. Invalid commands or fields will also result in __errors__. + + +### `list` - Shows DukeyList tasks +The `list` commands lists out all the tasks in DukeyList. + +**Usage of `list`:** +```ignorelang +list +``` + +**Expected outcome:** +```ignorelang +1. [T][ ] Buy Fruits +2. [D][X] Submit forms (by 12 Feb 2023) +3. [E][ ] Seminar (15 Feb 2023 to 16 Feb 2023) +4. [L][ ] Gas money: Brother owes $10 to Mother +``` +### + +### `todo` - Add a new ToDo +The `todo` command adds a new ToDo to the DukeyList. +A ToDo requires the following `fields`: +1. name + +The **format** for adding a Todo is shown below: +```ignorelang +todo / +``` + +**Example of usage:** + +`todo / Buy Fruits` + +**Expected outcome:** +```ignorelang +DukeyList just added a new todo: +[T][ ] Buy Fruits +DukeyList now has 5 tasks. +``` + +DukeyList will show a confirmation message along with the created ToDo. + +### +### `deadline` - Add a new Deadline +The `deadline` command adds a new Deadline to the DukeyList. +A Deadline requires the following `fields`: +1. name +2. deadline in the form `yyyy-mm-dd` + +The **format** for adding a Deadline is shown below: +```ignorelang +deadline / / +``` + +**Example of usage:** +```ignorelang +deadline / Submit Forms / 2023-02-12 +``` + +**Expected outcome:** +```ignorelang +DukeyList just added a new deadline: +[D][ ] Submit Forms (by 12 Feb 2023) +DukeyList now has 5 tasks. +``` + +DukeyList will show a confirmation message along with the created ToDo. + +### +### `event` - Add a new Event +The `event` command adds a new Event to the DukeyList. +An Event requires the following `fields`: +1. name +2. start date in the form `yyyy-mm-dd` +3. end date in the form `yyyy-mm-dd` + +The **format** for adding an Event is shown below: +```ignorelang +event / / / +``` + +**Example of usage:** +```ignorelang +event / Seminar / 2023-02-15 / 2023-02-16 +``` + +**Expected outcome:** +```ignorelang +DukeyList just added a new event: +[E][ ] Seminar (15 Feb 2023 to 16 Feb 2023) +DukeyList now has 5 tasks. +``` + +DukeyList will show a confirmation message along with the created Event. + +### +### `loan` - Add a new Loan +The `loan` command adds a new Event to the DukeyList. +A Loan requires the following `fields`: +1. name +2. borrower +3. lender +4. amount (must be a __number__) + +The **format** for adding an Event is shown below: +```ignorelang +loan / / / / +``` + +**Example of usage:** +```ignorelang +loan / Gas money / Brother / Mother / $10 +``` + +**Expected outcome:** +```ignorelang +DukeyList just added a new loan: +[L][ ] Gas money: Brother owes $10 to Mother +DukeyList now has 5 tasks. +``` + +DukeyList will show a confirmation message along with the created Loan. + +### +### `mark` - Mark a task +The `mark` command marks a task as completed. +`mark` requires the following `fields`: +1. task number (must be a __number__) + +The **format** for adding an Event is shown below: +```ignorelang +mark / +``` + +**Example of usage:** +```ignorelang +mark / 1 +``` + +**Expected outcome:** +```ignorelang +Task number 1 has been marked as done! +1. [T][X] Buy Fruits +``` + +DukeyList will show a confirmation message along with the marked task. + +### +### `unmark` - Unmark a task +The `unmark` command marks a task as incomplete. +`unmark` requires the following `fields`: +1. task number (must be a __number__) + +The **format** for adding an Event is shown below: +```ignorelang +unmark / +``` + +**Example of usage:** +```ignorelang +unmark / 1 +``` + +**Expected outcome:** +```ignorelang +Task number 1 has been unmarked. +1. [T][ ] Buy Fruits +``` + +DukeyList will show a confirmation message along with the unmarked task. + +### +### `delete` - delete a task +The `delete` command marks a task as incomplete. +`delete` requires the following `fields`: +1. task number (must be a __number__) + +The **format** for adding an Event is shown below: +```ignorelang +delete / +``` + +**Example of usage:** +```ignorelang +delete / 1 +``` + +**Expected outcome:** +```ignorelang +The following task has been removed! +[T][ ] Buy Fruits +DukeyList now has 4 tasks. +``` + +DukeyList will show a confirmation message along with the deleted task. + +### +### `find` - Find tasks +The `find` command finds and shows all tasks with a certain keyword. +`find` requires the following `fields`: +1. keyword + +The **format** for adding an Event is shown below: +```ignorelang +delete / +``` + +**Example of usage:** +```ignorelang +find / buy +``` + +**Expected outcome:** +```ignorelang +DukeyList found the following tasks with the keyword 'buy': +[T][ ] Buy Fruits +[T][X] Buy Water +``` + +DukeyList will show a confirmation message along with the list of tasks with the keyword. + +### +### `clearList` - Clears DukeyList +The `clearList` command deletes all the tasks currently on DukeyList. + +**Usage:** +```ignorelang +clearList +``` + +**Expected outcome:** +```ignorelang +DukeyList cleared! +``` + +DukeyList will show a confirmation message. + +### +### `save` - Save DukeyList +The `save` command saves a copy of the current DukeyList locally. + +**Usage:** +```ignorelang +save +``` + +**Expected outcome:** +```ignorelang +DukeyList saved! +``` + +DukeyList will show a confirmation message. + + +### +### `bye` - Exits DukeyList +The `bye` command autosaves DukeyList and exits the application. + +**Usage:** +```ignorelang +bye +``` + +**Expected outcome:** +```ignorelang +DukeyList saved! Goodbye! Please return to DukeyList soon! +Exiting... +``` + +DukeyList will show a confirmation message. diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000000..96a18499b0 --- /dev/null +++ b/build.gradle @@ -0,0 +1,63 @@ +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 + } +} + +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' +} + +application { + mainClassName = "duke.Duke" +} + +shadowJar { + archiveBaseName = "duke" + archiveClassifier = null +} + +run{ + standardInput = System.in +} diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml new file mode 100644 index 0000000000..65b7ca123e --- /dev/null +++ b/config/checkstyle/checkstyle.xml @@ -0,0 +1,429 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/config/checkstyle/suppressions.xml b/config/checkstyle/suppressions.xml new file mode 100644 index 0000000000..135ea49ee0 --- /dev/null +++ b/config/checkstyle/suppressions.xml @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file diff --git a/docs/README.md b/docs/README.md index 8077118ebe..7282c355f0 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,29 +1,340 @@ # User Guide -## Features +## DukeyList +DukeyList a great and easy way to manage and keep track of your tasks! Using DukeyList, you can add and remove tasks, +and mark them as completed. -### Feature-ABC +### DukeyList is able to handle these types of tasks: +* **ToDos** - Tasks that need to completed +* **Deadlines** - Tasks that need to be completed by a certain date +* **Events** - Tasks that span over a period +* __Loans__ (extension) - Tasks to keep track of debt -Description of the feature. +## DukeyList Features -### Feature-XYZ +### Task list +DukeyList keeps track of all your tasks. You can view your tasks, the task status (whether they are complete or not), +the type of tasks and the deadline/dates associated with your tasks. +```ignorelang +1. [T][ ] Buy Fruits +2. [D][X] Submit forms (by 12 Feb 2023) +3. [E][ ] Seminar (15 Feb 2023 to 16 Feb 2023) +4. [L][ ] Gas money: Brother owes $10 to Mother +``` + +### Add Tasks +Add a new task to DukeyList. DukeyList will continue to keep track of the task until it is deleted from the list. + +### Mark/Unmark Tasks +Should you be done with a task, you can mark it as done. DukeyList represents marked tasks with an 'X': +```ignorelang +2. [D][X] Submit forms (by 12 Feb 2023) +``` +Similarly, tasks can be unmarked as well. + +### Delete Tasks +You can delete a task from DukeyList. The task will no longer be shown on the list after deletion. + +### Find Tasks +You can search for tasks using a certain keyword. DukeyList will return you a list of tasks containing +the keyword, if there are any. +```ignorelang +DukeyList found these tasks matching the keyword "buy": +1. [T][ ] Buy Fruits +``` -Description of the feature. +### Save +You can save DukeyList locally, meaning that your tasks will be stored even after you exit the DukeyList app. +DukeyList automatically loads up the most recent save whenever it starts. -## Usage +## Starting up DukeyList +Once you start up DukeyList, it will automatically load up your most recent save, if any. +Should there be a saved list, you will see the following message: +``` +Saved list loaded: +``` +If there is no previous save, the following message will show instead. This means that you will +start with a new empty list. +``` +DukeyList empty, starting a new list: +``` +You can now start typing commands into DukeyList! -### `Keyword` - Describe action -Describe the action and its outcome. -Example of usage: +## Using DukeyList +To use DukeyList, type in a `command`, followed by necessary fields and press enter. Each `field` is separated by a '/' +character. The `command` is also followed by the '/' character. +The format and usages of various commands are shown below. Note that commands are not case-sensitive. +Be sure to follow the formats as closely as you can! Using a command with the incorrect format for fields will lead +to __errors__. Invalid commands or fields will also result in __errors__. -`keyword (optional arguments)` -Expected outcome: +### `list` - Shows DukeyList tasks +The `list` commands lists out all the tasks in DukeyList. -Description of the outcome. +**Usage of `list`:** +```ignorelang +list +``` + +**Expected outcome:** +```ignorelang +1. [T][ ] Buy Fruits +2. [D][X] Submit forms (by 12 Feb 2023) +3. [E][ ] Seminar (15 Feb 2023 to 16 Feb 2023) +4. [L][ ] Gas money: Brother owes $10 to Mother +``` + + +### `todo` - Add a new ToDo +The `todo` command adds a new ToDo to the DukeyList. +A ToDo requires the following `fields`: +1. name + +The **format** for adding a Todo is shown below: +```ignorelang +todo / +``` + +**Example of usage:** + +`todo / Buy Fruits` + +**Expected outcome:** +```ignorelang +DukeyList just added a new todo: +[T][ ] Buy Fruits +DukeyList now has 5 tasks. +``` + +DukeyList will show a confirmation message along with the created ToDo. + + +### `deadline` - Add a new Deadline +The `deadline` command adds a new Deadline to the DukeyList. +A Deadline requires the following `fields`: +1. name +2. deadline in the form `yyyy-mm-dd` + +The **format** for adding a Deadline is shown below: +```ignorelang +deadline / / +``` +**Example of usage:** +```ignorelang +deadline / Submit Forms / 2023-02-12 ``` -expected output + +**Expected outcome:** +```ignorelang +DukeyList just added a new deadline: +[D][ ] Submit Forms (by 12 Feb 2023) +DukeyList now has 5 tasks. +``` + +DukeyList will show a confirmation message along with the created ToDo. + + +### `event` - Add a new Event +The `event` command adds a new Event to the DukeyList. +An Event requires the following `fields`: +1. name +2. start date in the form `yyyy-mm-dd` +3. end date in the form `yyyy-mm-dd` + +The **format** for adding an Event is shown below: +```ignorelang +event / / / +``` + +**Example of usage:** +```ignorelang +event / Seminar / 2023-02-15 / 2023-02-16 +``` + +**Expected outcome:** +```ignorelang +DukeyList just added a new event: +[E][ ] Seminar (15 Feb 2023 to 16 Feb 2023) +DukeyList now has 5 tasks. +``` + +DukeyList will show a confirmation message along with the created Event. + + +### `loan` - Add a new Loan +The `loan` command adds a new Event to the DukeyList. +A Loan requires the following `fields`: +1. name +2. borrower +3. lender +4. amount (must be a __number__) + +The **format** for adding an Event is shown below: +```ignorelang +loan / / / / +``` + +**Example of usage:** +```ignorelang +loan / Gas money / Brother / Mother / $10 +``` + +**Expected outcome:** +```ignorelang +DukeyList just added a new loan: +[L][ ] Gas money: Brother owes $10 to Mother +DukeyList now has 5 tasks. +``` + +DukeyList will show a confirmation message along with the created Loan. + + +### `mark` - Mark a task +The `mark` command marks a task as completed. +`mark` requires the following `fields`: +1. task number (must be a __number__) + +The **format** for adding an Event is shown below: +```ignorelang +mark / +``` + +**Example of usage:** +```ignorelang +mark / 1 ``` + +**Expected outcome:** +```ignorelang +Task number 1 has been marked as done! +1. [T][X] Buy Fruits +``` + +DukeyList will show a confirmation message along with the marked task. + + +### `unmark` - Unmark a task +The `unmark` command marks a task as incomplete. +`unmark` requires the following `fields`: +1. task number (must be a __number__) + +The **format** for adding an Event is shown below: +```ignorelang +unmark / +``` + +**Example of usage:** +```ignorelang +unmark / 1 +``` + +**Expected outcome:** +```ignorelang +Task number 1 has been unmarked. +1. [T][ ] Buy Fruits +``` + +DukeyList will show a confirmation message along with the unmarked task. + + +### `delete` - delete a task +The `delete` command marks a task as incomplete. +`delete` requires the following `fields`: +1. task number (must be a __number__) + +The **format** for adding an Event is shown below: +```ignorelang +delete / +``` + +**Example of usage:** +```ignorelang +delete / 1 +``` + +**Expected outcome:** +```ignorelang +The following task has been removed! +[T][ ] Buy Fruits +DukeyList now has 4 tasks. +``` + +DukeyList will show a confirmation message along with the deleted task. + + +### `find` - Find tasks +The `find` command finds and shows all tasks with a certain keyword. +`find` requires the following `fields`: +1. keyword + +The **format** for adding an Event is shown below: +```ignorelang +delete / +``` + +**Example of usage:** +```ignorelang +find / buy +``` + +**Expected outcome:** +```ignorelang +DukeyList found the following tasks with the keyword 'buy': +[T][ ] Buy Fruits +[T][X] Buy Water +``` + +DukeyList will show a confirmation message along with the list of tasks with the keyword. + + +### `clearList` - Clears DukeyList +The `clearList` command deletes all the tasks currently on DukeyList. + +**Usage:** +```ignorelang +clearList +``` + +**Expected outcome:** +```ignorelang +DukeyList cleared! +``` + +DukeyList will show a confirmation message. + + +### `save` - Save DukeyList +The `save` command saves a copy of the current DukeyList locally. + +**Usage:** +```ignorelang +save +``` + +**Expected outcome:** +```ignorelang +DukeyList saved! +``` + +DukeyList will show a confirmation message. + + + +### `bye` - Exits DukeyList +The `bye` command autosaves DukeyList and exits the application. + +**Usage:** +```ignorelang +bye +``` + +**Expected outcome:** +```ignorelang +DukeyList saved! Goodbye! Please return to DukeyList soon! +Exiting... +``` + +DukeyList will show a confirmation message. diff --git a/docs/Ui.png b/docs/Ui.png new file mode 100644 index 0000000000..fb1cd56670 Binary files /dev/null and b/docs/Ui.png differ diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000..f3d88b1c2f Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000..b7c8c5dbf5 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 0000000000..2fe81a7d95 --- /dev/null +++ b/gradlew @@ -0,0 +1,183 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000000..62bd9b9cce --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,103 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java deleted file mode 100644 index 5d313334cc..0000000000 --- a/src/main/java/Duke.java +++ /dev/null @@ -1,10 +0,0 @@ -public class Duke { - public static void main(String[] args) { - String logo = " ____ _ \n" - + "| _ \\ _ _| | _____ \n" - + "| | | | | | | |/ / _ \\\n" - + "| |_| | |_| | < __/\n" - + "|____/ \\__,_|_|\\_\\___|\n"; - System.out.println("Hello from\n" + logo); - } -} diff --git a/src/main/java/duke/ActionEnum.java b/src/main/java/duke/ActionEnum.java new file mode 100644 index 0000000000..4d8fd238c6 --- /dev/null +++ b/src/main/java/duke/ActionEnum.java @@ -0,0 +1,23 @@ +package duke; + +/** + * Available actions that can be performed with Duke + */ +public enum ActionEnum { + LIST, + TODO, + DEADLINE, + EVENT, + LOAN, + MARK, + UNMARK, + DELETE, + SAVE, + CLEARLIST, + BYE, + FORCESTOP, + CLEARSAVE, + FIND + + +} diff --git a/src/main/java/duke/Deadline.java b/src/main/java/duke/Deadline.java new file mode 100644 index 0000000000..8569b12bd5 --- /dev/null +++ b/src/main/java/duke/Deadline.java @@ -0,0 +1,115 @@ +package duke; + +import java.time.LocalDate; + +import duke.exceptions.DukeyException; + + +/** + * Deadline is a type of Task which contains a name and a date (in the form of + * a LocalDate) by which the task has to be + * completed. + */ +public class Deadline extends Task { + private static final String TYPE = "[D]"; + private LocalDate deadline; + + /** + * Returns a Deadline with a name, deadline, and sets its status to undone. + * @param name the name of the Deadline + * @param deadline the time which the Deadline must be completed + */ + public Deadline(String name, LocalDate deadline) { + super(name); + this.deadline = deadline; + } + + /** + * Returns a Deadline with a name and sets its status accordingly. + * @param name the name of the Deadline + * @param deadline the time by which the Deadline must be completed + * @param isMarked the status of the Deadline + */ + public Deadline(String name, LocalDate deadline, boolean isMarked) { + super(name, isMarked); + this.deadline = deadline; + } + + /** + * Returns a Deadline created with details taken from user input. + * @param ui object to handle user interactions + * @return Deadline created based on the user's input + * @exception DukeyException on invalid user input + */ + public static Deadline createDeadline(Ui ui, String input) throws DukeyException { + String[] inputArray = input.split("/"); + if (inputArray.length != 3) { + throw new DukeyException("Error! invalid format!"); + } + String deadlineName = ui.readTaskName(inputArray[1]); + LocalDate deadlineTime = ui.readTime(inputArray[2]); + + return new Deadline(deadlineName, deadlineTime); + + } + + /** + * Returns the deadline + * @return deadline of the task + */ + public LocalDate getDeadline() { + return this.deadline; + } + + /** + * Returns a Deadline created from its log string. This method "loads" + * a Deadline from a save. + * @param logStringArray an array of strings where each string is a component of the log string that + * has been split up + * @return Deadline created from its log string. + */ + public static Deadline createDeadlineFromLog(String[] logStringArray) { + String name = logStringArray[2]; + String deadlineString = logStringArray[3]; + boolean isMarked = !logStringArray[1].equals("0"); + LocalDate deadline = LocalDate.now(); + try { + deadline = DukeyTime.getDateFromString(deadlineString); + } catch (DukeyException e) { + e.getMessage(); + } + + return new Deadline(name, deadline, isMarked); + } + + /** + * Returns a message to be printed whenever a Deadline is added. + * @return the message to be printed + */ + @Override + public String getMessageWhenAdded() { + return "DukeyList just added a new deadline:"; + } + + /** + * Returns the log string of a Deadline, which is a string containing details about the task. The log + * string will be used to save the task locally when the task list is saved. + * @return the log string of the Deadline + */ + @Override + public String getLogString() { + return "D" + "," + this.getMarkedStatus() + "," + this.getName() + "," + this.getDeadline(); + } + + + /** + * Returns the string representation of a Deadline + */ + @Override + public String toString() { + if (this.isDone()) { + return TYPE + "[X] " + this.getName() + " (by " + DukeyTime.dateToString(this.deadline) + ")"; + } + return TYPE + "[ ] " + this.getName() + " (by " + DukeyTime.dateToString(this.deadline) + ")"; + } +} diff --git a/src/main/java/duke/DialogBox.java b/src/main/java/duke/DialogBox.java new file mode 100644 index 0000000000..eaeee34896 --- /dev/null +++ b/src/main/java/duke/DialogBox.java @@ -0,0 +1,70 @@ +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.Insets; +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; +import javafx.scene.shape.Circle; + +/** + * 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); + dialog.setWrapText(true); + dialog.setMinHeight(Double.NEGATIVE_INFINITY); + dialog.setPadding(new Insets(10, 25, 10, 25)); + displayPicture.setImage(img); + Circle clip = new Circle(60, 60, 60); + displayPicture.setClip(clip); + } + + /** + * 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) { + assert !text.equals("") && !img.equals(null); + return new DialogBox(text, img); + } + + public static DialogBox getDukeDialog(String text, Image img) { + assert !text.equals("") && !img.equals(null); + var db = new DialogBox(text, img); + db.flip(); + return db; + } +} \ No newline at end of file diff --git a/src/main/java/duke/Duke.java b/src/main/java/duke/Duke.java new file mode 100644 index 0000000000..8becee487c --- /dev/null +++ b/src/main/java/duke/Duke.java @@ -0,0 +1,218 @@ +package duke; + +import java.io.FileNotFoundException; +import javafx.application.Application; +import javafx.scene.image.Image; +import javafx.scene.Scene; +import javafx.scene.control.Button; +import javafx.scene.control.ScrollPane; +import javafx.scene.control.TextField; +import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.Region; +import javafx.scene.layout.VBox; +import javafx.stage.Stage; + +import duke.exceptions.DukeyException; + +/** + * Interactive list which allows users to add Tasks and perform various operations to these Tasks. + */ +public class Duke extends Application { + private static final String FILEPATH = "DukeySave.txt"; + private Ui ui; + private Storage storage; + private TaskList taskList; + private int inputStatus; + private ScrollPane scrollPane; + private VBox dialogContainer; + private TextField userInput; + private Button sendButton; + private Scene scene; + private Image user = new Image(this.getClass().getResourceAsStream("/images/Cap.jpeg")); + private Image duke = new Image(this.getClass().getResourceAsStream("/images/IronMan.jpg")); + + /** + * Returns a new instance of Duke with a Storage, Ui and TaskList. + */ + public Duke() { + this.ui = new Ui(); + this.storage = new Storage(FILEPATH); + this.taskList = new TaskList(ui); + this.inputStatus = 0; + } + + + /** + * Starts Duke + */ + public static void main(String[] args) { + Duke duke = new Duke(); + //duke.initiateDukeyList(); + + } + + + /** + * Initiates the DukeyList by loading a save, then scans for and performs commands from the user. + */ + public String runDuke(String input) { + assert !ui.equals(null) && !storage.equals(null) && !taskList.equals(null); + assert !input.equals(""); + StringBuilder sb = new StringBuilder(); + ActionEnum actionEnum = ActionEnum.BYE; + try { + actionEnum = ui.readCommand(input); + switch (actionEnum) { + case TODO: + sb.append(taskList.addTask(ToDo.createToDo(ui, input))); + break; + case DEADLINE: + sb.append(taskList.addTask(Deadline.createDeadline(ui, input))); + break; + case EVENT: + sb.append(taskList.addTask(Event.createEvent(ui, input))); + break; + case LOAN: + sb.append(taskList.addTask(Loan.createLoan(ui, input))); + break; + case MARK: + sb.append(taskList.mark(input)); + break; + case UNMARK: + sb.append(taskList.unmark(input)); + break; + case DELETE: + sb.append(taskList.delete(input)); + break; + case LIST: + sb.append(taskList.readList()); + break; + case CLEARLIST: + sb.append(taskList.clearList()); + break; + case FIND: + sb.append(taskList.find(input)); + break; + case SAVE: + sb.append(taskList.save(storage)); + break; + case CLEARSAVE: + taskList.clearSave(storage); + break; + } + } catch (DukeyException e) { + sb.append(ui.printExceptionMessage(e)); + } + + return sb.toString(); + + + } + + public String getInstruction() { + return ui.getWelcomeMessage() + ui.printInstruction(); + } + + public String loadDuke() { + StringBuilder sb = new StringBuilder(); + try { + int status = taskList.initiate(storage); + sb.append(ui.printLoadMessage(status)); + } catch (FileNotFoundException e) { + sb.append(ui.printLoadMessage(0)); + } + sb.append(taskList.readList()); + return sb.toString(); + } + + public String exitDuke() { + String response = ""; + try { + response = taskList.save(storage); + } catch (DukeyException e) { + response = ui.printExceptionMessage(e); + } + + return response + " " + ui.getGoodbyeMessage() + '\n' + "Exiting..."; + } + + @Override + public void start(Stage stage) { + //Setting up the required components + 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(); + + //Formatting 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.ALWAYS); + scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.ALWAYS); + scrollPane.setVvalue(1.0); + scrollPane.setFitToWidth(true); + + 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); + + //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 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() { + String userText = userInput.getText(); + String dukeText = getResponse(userText); + dialogContainer.getChildren().addAll( + DialogBox.getUserDialog(userText, user), + DialogBox.getDukeDialog(dukeText, duke) + ); + userInput.clear(); + } + + /** + * Generates a response to user input. + */ + public String getResponse(String input) { + return "DukeyList: " + input; + } + +} diff --git a/src/main/java/duke/DukeyTime.java b/src/main/java/duke/DukeyTime.java new file mode 100644 index 0000000000..4989dd301b --- /dev/null +++ b/src/main/java/duke/DukeyTime.java @@ -0,0 +1,38 @@ +package duke; + +import java.time.DateTimeException; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.format.FormatStyle; + +import duke.exceptions.DukeyException; + +/** + * Handles operations relating to dates + */ +public class DukeyTime { + private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + + /** + * Returns a LocalDate from a String + * @param s the String containing the date + * @throws DukeyException on invalid string input for date + */ + public static LocalDate getDateFromString(String s) throws DukeyException { + LocalDate date = LocalDate.now(); + try { + date = LocalDate.parse(s, formatter); + } catch (DateTimeException e) { + throw new DukeyException("Error! Invalid date!"); + } + return date; + } + + /** + * Returns a string representation of a date + * @param date the date to be represented + */ + public static String dateToString(LocalDate date) { + return date.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)); + } +} diff --git a/src/main/java/duke/Event.java b/src/main/java/duke/Event.java new file mode 100644 index 0000000000..4a5b4d6f35 --- /dev/null +++ b/src/main/java/duke/Event.java @@ -0,0 +1,118 @@ +package duke; + +import java.time.LocalDate; + +import duke.exceptions.DukeyException; + + +/** + * Event is a type of Task which contains a name and two dates (in the form of + * a LocalDate) indicating the starting date and the ending date. + * completed. + */ +public class Event extends Task { + private static final String TYPE = "[E]"; + private final LocalDate start; + private final LocalDate end; + + /** + * Returns an Event with a name, start date and end date, and sets its status to undone. + * @param name the name of the Event + * @param start the starting date of the event + * @param end the ending date of the event + */ + public Event(String name, LocalDate start, LocalDate end) { + super(name); + this.start = start; + this.end = end; + } + + /** + * Returns an Event with a name, start date and end date, and sets its status accordingly. + * @param name the name of the Event + * @param start the starting date of the event + * @param end the ending date of the event + * @param isMarked the status of the event + */ + public Event(String name, LocalDate start, LocalDate end, boolean isMarked) { + super(name, isMarked); + this.start = start; + this.end = end; + } + + /** + * Returns an Event created with details taken from user input. + * @param ui object to handle user interactions + * @return Event created based on the user's input + * @throws DukeyException on invalid String provided for date + */ + public static Event createEvent(Ui ui, String input) throws DukeyException { + String[] inputArray = input.split("/"); + if (inputArray.length != 4) { + throw new DukeyException("Error! Invalid Format!"); + } + String eventName = ui.readTaskName(inputArray[1]); + LocalDate eventStart = ui.readTime(inputArray[2]); + LocalDate eventEnd = ui.readTime(inputArray[3]); + return new Event(eventName, eventStart, eventEnd); + } + + /** + * Returns an Event created from its log string. This method "loads" + * an Event from a save. + * @param logStringArray an array of strings where each string is a component of the log string that + * has been split up + * @return Event created from its log string. + */ + public static Event createEventFromLog(String[] logStringArray) { + String name = logStringArray[2]; + String startString = logStringArray[3]; + String endString = logStringArray[4]; + boolean isMarked = !logStringArray[1].equals("0"); + + LocalDate start = LocalDate.now(); + LocalDate end = LocalDate.now(); + try { + start = DukeyTime.getDateFromString(startString); + end = DukeyTime.getDateFromString(endString); + } catch (DukeyException e) { + e.getMessage(); + } + return new Event(name, start, end, isMarked); + } + + /** + * Returns a message to be printed whenever an Event is added. + * @return the message to be printed + */ + @Override + public String getMessageWhenAdded() { + return "DukeyList just added a new event:"; + } + + /** + * Returns the log string of an Event, which is a string containing details about the task. The log + * string will be used to save the task locally when the task list is saved. + * @return the log string of the Event + */ + @Override + public String getLogString() { + return "E" + "," + getMarkedStatus() + "," + this.getName() + "," + this.start + "," + this.end; + } + + /** + * Returns the string representation of an Event + */ + @Override + public String toString() { + if (this.isDone()) { + return TYPE + "[X] " + this.getName() + " (" + DukeyTime.dateToString(this.start) + + " to " + DukeyTime.dateToString(this.end) + ")"; + } + return TYPE + "[ ] " + this.getName() + " (" + DukeyTime.dateToString(this.start) + + " to " + DukeyTime.dateToString(this.end) + ")"; + } + + + +} diff --git a/src/main/java/duke/Launcher.java b/src/main/java/duke/Launcher.java new file mode 100644 index 0000000000..b8c0f9c373 --- /dev/null +++ b/src/main/java/duke/Launcher.java @@ -0,0 +1,9 @@ +package duke; + +import javafx.application.Application; + +public class Launcher { + public static void main(String[] args) { + Application.launch(Main.class, args); + } +} diff --git a/src/main/java/duke/Loan.java b/src/main/java/duke/Loan.java new file mode 100644 index 0000000000..35c9eccc7d --- /dev/null +++ b/src/main/java/duke/Loan.java @@ -0,0 +1,78 @@ +package duke; + +import duke.exceptions.DukeyException; + +public class Loan extends Task { + private String borrower; + private String lender; + private int amount; + private static final String TYPE = "[L]"; + + public Loan(String name, String borrower, String lender, int amount) { + super(name, false); + this.borrower = borrower; + this.lender = lender; + this.amount = amount; + } + + public Loan(String name, String borrower, String lender, int amount, boolean isDone) { + super(name, isDone); + this.borrower = borrower; + this.lender = lender; + this.amount = amount; + } + + public String getBorrower() { + return this.borrower; + } + + public String getLender() { + return this.lender; + } + + public int getAmount() { + return this.amount; + } + + public static Loan createLoan(Ui ui, String input) throws DukeyException { + String[] inputArray = input.split("/"); + if (inputArray.length != 5) { + throw new DukeyException("Error! Invalid Format!"); + } + String loanName = ui.readTaskName(inputArray[1]); + String borrower = ui.readName(inputArray[2]); + String lender = ui.readName(inputArray[3]); + int amount = ui.readAmount(inputArray[4]); + + return new Loan(loanName, borrower, lender, amount); + } + + public static Loan createLoanFromLog(String[] logStringArray) { + String name = logStringArray[2]; + boolean isMarked = !logStringArray[1].equals("0"); + String borrower = logStringArray[3]; + String lender = logStringArray[4]; + int amount = Integer.parseInt(logStringArray[5]); + + return new Loan(name, borrower, lender, amount, isMarked); + + } + + + @Override + public String getLogString() { + return "L" + "," + getMarkedStatus() + "," + getName() + "," + getBorrower() + "," + getLender() + "," + + getAmount(); + } + + @Override + public String toString() { + if (this.isDone()) { + return TYPE + "[X]" + " " + this.getName() + " : " + this.getBorrower() + " owes " + + this.getAmount() + " to " + this.getLender(); + } + + return TYPE + "[ ]" + " " + this.getName() + " : " + this.getBorrower() + " owes $" + + this.getAmount() + " to " + this.getLender(); + } +} diff --git a/src/main/java/duke/Main.java b/src/main/java/duke/Main.java new file mode 100644 index 0000000000..59ae50a45c --- /dev/null +++ b/src/main/java/duke/Main.java @@ -0,0 +1,31 @@ +package duke; + +import java.io.IOException; + +import javafx.application.Application; +import javafx.fxml.FXMLLoader; +import javafx.scene.Scene; +import javafx.scene.layout.AnchorPane; +import javafx.stage.Stage; + +/** + * A GUI for Duke using FXML. + */ +public class Main extends Application { + + 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.show(); + } catch (IOException e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/src/main/java/duke/MainWindow.java b/src/main/java/duke/MainWindow.java new file mode 100644 index 0000000000..633427c06b --- /dev/null +++ b/src/main/java/duke/MainWindow.java @@ -0,0 +1,78 @@ +package duke; + +import javafx.animation.PauseTransition; +import javafx.application.Platform; +import javafx.fxml.FXML; +import javafx.scene.control.Button; +import javafx.scene.control.ScrollPane; +import javafx.scene.control.TextField; +import javafx.scene.image.Image; +import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.VBox; +import javafx.util.Duration; + +/** + * 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/Cap.jpeg")); + private Image dukeImage = new Image(this.getClass().getResourceAsStream("/images/IronMan.jpg")); + + @FXML + public void initialize() { + scrollPane.vvalueProperty().bind(dialogContainer.heightProperty()); + } + + public void setDuke(Duke d) { + duke = d; + dialogContainer.getChildren().addAll( + DialogBox.getDukeDialog(duke.getInstruction(), dukeImage)); + dialogContainer.getChildren().addAll( + DialogBox.getDukeDialog(duke.loadDuke(), dukeImage)); + + } + + /** + * 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(); + if (input.strip().equals("bye")) { + String byeMessage = duke.exitDuke(); + dialogContainer.getChildren().addAll( + DialogBox.getUserDialog(input, userImage), + DialogBox.getDukeDialog(byeMessage, dukeImage) + ); + userInput.clear(); + PauseTransition pt = new PauseTransition(Duration.seconds(3)); + pt.setOnFinished(e -> { + Platform.exit(); + }); + pt.play(); + + } else { + String response = duke.runDuke(input); + dialogContainer.getChildren().addAll( + DialogBox.getUserDialog(input, userImage) + ); + dialogContainer.getChildren().addAll( + DialogBox.getDukeDialog(response, dukeImage) + ); + userInput.clear(); + } + } + +} \ No newline at end of file diff --git a/src/main/java/duke/Parser.java b/src/main/java/duke/Parser.java new file mode 100644 index 0000000000..dcad4a97a4 --- /dev/null +++ b/src/main/java/duke/Parser.java @@ -0,0 +1,149 @@ +package duke; + +import java.time.LocalDate; + +import duke.exceptions.DukeyException; + +import static java.lang.Integer.parseInt; + +/** + * Analyzes user input. + */ +public class Parser { + + /** + * Returns true if a given user input is empty. + * @param input the user input + */ + public boolean checkIfEmpty(String input) { + return input.strip().equals(""); + } + + /** + * Detects the user's command based on the user's input String, throws an exception if the user's input + * does not match any of the existing commands. + * @param input the user input + * @return the detected action + * @exception DukeyException on invalid user command + */ + public ActionEnum parseCommand(String input) throws DukeyException { + input = input.strip().toLowerCase(); + if (checkIfEmpty(input)) { + throw new DukeyException("Error! Input cannot be empty."); + } + if (input.length() < 3) { + throw new DukeyException("Invalid Command!"); + } + + input = input.substring(0,3); + + if (input.equals("tod")) { + return ActionEnum.TODO; + } + if (input.equals("dea")) { + return ActionEnum.DEADLINE; + } + if (input.equals("eve")) { + return ActionEnum.EVENT; + } + if (input.equals("loa")) { + return ActionEnum.LOAN; + } + if (input.equals("lis")) { + return ActionEnum.LIST; + } + if (input.equals("mar")) { + return ActionEnum.MARK; + } + if (input.equals("unm")) { + return ActionEnum.UNMARK; + } + if (input.equals("del")) { + return ActionEnum.DELETE; + } + if (input.equals("sav")) { + return ActionEnum.SAVE; + } + if (input.equals("cle")) { + return ActionEnum.CLEARLIST; + } + if (input.equals("bye")) { + return ActionEnum.BYE; + } + if (input.equals("for")) { + return ActionEnum.FORCESTOP; + } + if (input.equals("fin")) { + return ActionEnum.FIND; + } + + throw new DukeyException("Error! Invalid command!"); + } + + + /** + * Scans the user's input for a Task name and returns the Task name, throws an exception if the + * user input is empty. + * @param input the user input + * @throws DukeyException on empty input for Task name + */ + public String parseTaskName(String input) throws DukeyException { + input = input.strip(); + if (checkIfEmpty(input)) { + throw new DukeyException("Error! Task name cannot be empty!"); + } + return input; + } + + /** + * Scans the user's input for a date and returns the date in the form of a LocalDate. Throws an exception if the + * user input is empty or if the date is in the incorrect format. + * @param input the user input + * @throws DukeyException on invalid string input for date + */ + public LocalDate parseDate(String input) throws DukeyException { + input = input.strip(); + if (checkIfEmpty(input)) { + throw new DukeyException("Input cannot be empty!"); + } + return DukeyTime.getDateFromString(input); + } + + /** + * Scans the user's input for a keyword and returns the keyword. Throws an exception if the + * user input is empty. + * @param input the user input + * @throws DukeyException on empty user input + */ + public String parseKeyword(String input) throws DukeyException { + input = input.strip(); + if (checkIfEmpty(input)) { + throw new DukeyException("Input cannot be empty!"); + } + return input; + } + + public int parseAmount(String input) throws DukeyException { + input = input.strip(); + int amount; + try { + amount = parseInt(input); + } catch (NumberFormatException e) { + throw new DukeyException("Error! Invalid amount!"); + } + + if (amount < 0) { + throw new DukeyException("Error! Invalid amount!"); + } + + return amount; + } + + public String parseName(String input) throws DukeyException { + input = input.strip(); + if (checkIfEmpty(input)) { + throw new DukeyException("Name cannot be empty!"); + } + return input; + } +} diff --git a/src/main/java/duke/Storage.java b/src/main/java/duke/Storage.java new file mode 100644 index 0000000000..6d4450f395 --- /dev/null +++ b/src/main/java/duke/Storage.java @@ -0,0 +1,89 @@ +package duke; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Iterator; +import java.util.Scanner; + +import duke.exceptions.DukeyException; + + +/** + * Deals with the saving and loading of Tasks onto a hard drive. + */ +public class Storage { + private File file; + private String filePath; + + /** + * Returns a new storage containing a File where data is to be stored. + */ + public Storage(String filePath) { + this.file = new File(filePath); + this.filePath = filePath; + } + + /** + * Appends text to the save File. + * @param textToAdd the text to be added + * @throws IOException on problems with the input text + */ + public void writeToFile(String textToAdd) throws IOException { + FileWriter fw = new FileWriter(this.file, true); + fw.write(textToAdd + "\n"); + fw.close(); + } + + /** + * Clears the save File of any data. + */ + public void clearFile() { + this.file.delete(); + this.file = new File(this.filePath); + } + + /** + * Saves all the Tasks on a TaskList to the hard drive by writing the log strings of each Task + * onto the save File. + * @param taskList the TaskList to be saved + * @throws DukeyException on invalid filepath provided + */ + public void save(TaskList taskList) throws DukeyException { + this.clearFile(); + Iterator it = taskList.getIterator(); + it.forEachRemaining(x -> { + String logString = x.getLogString(); + try { + this.writeToFile(logString); + } catch (IOException e) { + e.getMessage(); + } + }); + + } + + /** + * Loads all the Tasks on the hard drive to a TaskList by creating new Tasks based on the details from + * their log string. + * @param taskList the TaskList on which the Tasks are to be laoded + * @throws FileNotFoundException on missing file + */ + public void load(TaskList taskList) throws FileNotFoundException { + Scanner fileScanner = new Scanner(file); + while (fileScanner.hasNextLine()) { + String taskLogString = fileScanner.nextLine(); + String[] logStringArray = taskLogString.split(","); + if (logStringArray[0].equals("T")) { + taskList.addTaskFromSave(ToDo.createToDoFromLog(logStringArray)); + } else if (logStringArray[0].equals("D")) { + taskList.addTaskFromSave(Deadline.createDeadlineFromLog(logStringArray)); + } else if (logStringArray[0].equals("E")) { + taskList.addTaskFromSave(Event.createEventFromLog(logStringArray)); + } else if (logStringArray[0].equals("L")) { + taskList.addTaskFromSave(Loan.createLoanFromLog(logStringArray)); + } + } + } +} diff --git a/src/main/java/duke/Task.java b/src/main/java/duke/Task.java new file mode 100644 index 0000000000..59974a8f7f --- /dev/null +++ b/src/main/java/duke/Task.java @@ -0,0 +1,95 @@ +package duke; + +/** + * An abstract class depicting something that the user needs to do. It has a name and a status (whether it is + * done or not). + */ +public abstract class Task { + private String name; + private Boolean isDone; + + /** + * Returns a Task with a given name and sets its status to undone. + * @param name the name of the Task + */ + public Task(String name) { + this.name = name; + this.isDone = false; + } + + /** + * Returns a Task with a given name and sets its status accordingly. + * @param name the name of the Task + * @param isDone the status of the Task + */ + public Task(String name, boolean isDone) { + this.name = name; + this.isDone = isDone; + } + + /** + * Returns the name of the Task. + */ + public String getName() { + return this.name; + } + + /** + * Returns true if the Task's status is done. + */ + public Boolean isDone() { + return this.isDone; + } + + /** + * Returns the status of the Task in the form of an int, with 0 and 1 representing undone and done + * respectively. + */ + public int getMarkedStatus() { + return this.isDone() ? 1 : 0; + } + + /** + * Sets the status of a Task to done. + */ + public void setMarked() { + this.isDone = true; + } + + /** + * Sets the status of a Task to undone. + */ + public void setUnmarked() { + this.isDone = false; + } + + /** + * Returns the confirmation message to be printed when a Task gets added to a TaskList. + * @return the message to be printed + */ + public String getMessageWhenAdded() { + return "DukeyList just added a new item: "; + } + + /** + * Returns the log string of a task, which is a String containing details of a Task in a certain format. + * The log string is used to represent a Task when saving Tasks onto the save File. + * @return log string of a Tasks + */ + public abstract String getLogString(); + + + /** + * Returns the string representation of a Task. + */ + @Override + public String toString() { + if (this.isDone()) { + return "[X] " + this.getName(); + } + return "[ ] " + this.name; + } + + + +} diff --git a/src/main/java/duke/TaskList.java b/src/main/java/duke/TaskList.java new file mode 100644 index 0000000000..8cc34f79f6 --- /dev/null +++ b/src/main/java/duke/TaskList.java @@ -0,0 +1,248 @@ +package duke; + +import static java.lang.Integer.parseInt; +import java.io.FileNotFoundException; +import java.util.ArrayList; +import java.util.Iterator; + +import duke.exceptions.DukeyException; + +/** + * Stores and manages Tasks. + */ +public class TaskList { + private ArrayList list; + private Ui ui; + + /** + * Returns an empty TaskList. + */ + public TaskList(Ui ui) { + this.list = new ArrayList<>(); + this.ui = ui; + } + + /** + * Returns an empty TaskList. + */ + public TaskList() { + this.list = new ArrayList<>(); + this.ui = new Ui(); + } + + /** + * Returns true if the TaskList has no Tasks. + */ + public boolean isEmpty() { + return this.list.isEmpty(); + } + + /** + * Returns an iterator of the list of Tasks. + */ + public Iterator getIterator() { + return this.list.iterator(); + } + + /** + * Adds a new Task to the TaskList and prints out a confirmation message for the user. + * @return the confirmation message for the user + */ + public String addTask(Task task) { + assert !task.equals(null); + this.list.add(task); + return ui.printAddedMessage(task) + '\n' + printSize(); + } + + /** + * Adds a new Task to the TaskList without printing anything. This method is used to load Tasks into + * the TaskList from a save File. + */ + public void addTaskFromSave(Task task) { + assert !task.equals(null); + this.list.add(task); + } + + /** + * Prints out all the Tasks in the TaskList. + * @return the string containing the list of tasks + */ + public String readList() { + return ui.readList(this.list); + } + + /** + * Marks a particular Task as done. This method uses the Ui class to read input from the user to get + * details about which Task is to be marked. + * @return the confirmation message for the user + * @throws DukeyException on invalid Task number from user input + */ + public String mark(String input) throws DukeyException { + String[] inputArray = input.split("/"); + if (inputArray.length != 2) { + throw new DukeyException("Error, Invalid Format!"); + } + int taskNumber; + try { + taskNumber = parseInt(inputArray[1].strip()) - 1; + } catch (NumberFormatException e) { + throw new DukeyException("Error! Invalid task number format!"); + } + + if (taskNumber < 0) { + throw new DukeyException("Error! Invalid task number"); + } else if (taskNumber >= this.list.size()) { + throw new DukeyException("Error! DukeyList only contains " + this.list.size() + " tasks"); + } + + Task taskToMark = this.list.get(taskNumber); + taskToMark.setMarked(); + return ui.printMarkedMessage(taskNumber, taskToMark); + } + + /** + * Marks a particular Task as undone. This method uses the Ui class to read input from the user to get + * details about which Task is to be unmarked. + * @return the confirmation message for the user + * @throws DukeyException on invalid task number from user input + */ + public String unmark(String input) throws DukeyException { + String[] inputArray = input.split("/"); + if (inputArray.length != 2) { + throw new DukeyException("Error, Invalid Format!"); + } + int taskNumber; + try { + taskNumber = parseInt(inputArray[1].strip()) - 1; + } catch (NumberFormatException e) { + throw new DukeyException("Error! Invalid task number format!"); + } + + if (taskNumber < 0) { + throw new DukeyException("Error! Invalid task number"); + } else if (taskNumber >= this.list.size()) { + throw new DukeyException("Error! DukeyList only contains " + this.list.size() + " tasks"); + } + Task taskToUnmark = this.list.get(taskNumber); + taskToUnmark.setUnmarked(); + return ui.printUnmarkedMessage(taskNumber, taskToUnmark); + } + + /** + * Deletes a Task from the TaskList. This method uses the Ui class to read input from the user to get + * details about which Task is to be deleted. + * @return the confirmation message for the user + * @throws DukeyException on invalid Task number from user input + */ + public String delete(String input) throws DukeyException { + String[] inputArray = input.split("/"); + if (inputArray.length != 2) { + throw new DukeyException("Error, Invalid Format!"); + } + int taskNumber; + try { + taskNumber = parseInt(inputArray[1].strip()) - 1; + } catch (NumberFormatException e) { + throw new DukeyException("Error! Invalid task number format!"); + } + + if (taskNumber < 0) { + throw new DukeyException("Error! Invalid task number"); + } else if (taskNumber >= this.list.size()) { + throw new DukeyException("Error! DukeyList only contains " + this.list.size() + " tasks"); + } + + Task taskToRemove = list.get(taskNumber); + String response = ui.printDeletedMessage(taskToRemove); + list.remove(taskNumber); + response += '\n' + this.printSize(); + return response; + } + + /** + * Finds Tasks in the TaskList that contains a particular keyword. This method + * uses the Ui class to read input from the user to get the keyword. The found Tasks are then printed out. + * @return the confirmation message for the user + */ + public String find(String input) throws DukeyException { + String[] inputArray = input.split("/"); + if (inputArray.length != 2) { + throw new DukeyException("Error! Invalid format!"); + } + String keyword = ui.readKeyword(inputArray[1]); + String confirmedKeyword = keyword; + ArrayList foundTaskList = new ArrayList<>(); + Iterator it = getIterator(); + + it.forEachRemaining(x -> { + if (x.getName().contains(confirmedKeyword)) { + foundTaskList.add(new TaskNumberPair(x, list.indexOf(x))); + } + }); + + return ui.printFoundTaskList(foundTaskList); + + } + + /** + * Returns the number of Tasks in the TaskList. + */ + public int getSize() { + return list.size(); + } + + /** + * Uses the Ui class to print out the number of Tasks in the TaskList. + * @return the string containing the size + */ + public String printSize() { + return ui.printSize(getSize()); + } + + /** + * Clears the TaskList. + */ + public String clearList() { + this.list.clear(); + return ui.printClearedMessage(); + } + + /** + * Saves the current TaskList to the save File. + * @return the confirmation message for the user + * @param storage deals with the saving and loading of data to the save File. + */ + public String save(Storage storage) throws DukeyException { + storage.save(this); + return ui.printSavedMessage(); + } + + /** + * Loads all the Tasks in the save File to the TaskList. + * @param storage deals with the saving and loading of data to the save File. + * @throws FileNotFoundException on missing save file + */ + public int initiate(Storage storage) throws FileNotFoundException { + assert !storage.equals(null); + storage.load(this); + + if (this.list.isEmpty()) { + return 0; + } else { + return 1; + } + + } + + /** + * Clears the save File. + */ + public void clearSave(Storage storage) { + assert !storage.equals(null); + storage.clearFile(); + ui.printClearedMessage(); + } + + + +} diff --git a/src/main/java/duke/TaskNumberPair.java b/src/main/java/duke/TaskNumberPair.java new file mode 100644 index 0000000000..265f7e4b46 --- /dev/null +++ b/src/main/java/duke/TaskNumberPair.java @@ -0,0 +1,31 @@ +package duke; + +/** + * Pairs a Task with its index number in the TaskList. + */ +public class TaskNumberPair { + private Task task; + private int number; + + /** + * Returns a TaskNumberPair given a Task and its index + */ + public TaskNumberPair(Task task, int number) { + this.task = task; + this.number = number; + } + + /** + * Returns the Task. + */ + public Task getTask() { + return this.task; + } + + /** + * Returns the index of the Task. + */ + public int getNumber() { + return this.number; + } +} diff --git a/src/main/java/duke/ToDo.java b/src/main/java/duke/ToDo.java new file mode 100644 index 0000000000..0872c15560 --- /dev/null +++ b/src/main/java/duke/ToDo.java @@ -0,0 +1,88 @@ +package duke; + +import duke.exceptions.DukeyException; + +/** + * ToDo is a type of Task which only contains a task name. + */ +public class ToDo extends Task { + private static final String TYPE = "[T]"; + + /** + * Returns a ToDo with a name and status set to undone. + * @param name the name of the ToDo + */ + public ToDo(String name) { + super(name); + } + + /** + * Returns a ToDo with a name and sets the status accordingly. + * @param name the name of the ToDo + * @param isMarked the status of the ToDo + */ + public ToDo(String name, boolean isMarked) { + super(name, isMarked); + } + + + /** + * Returns a ToDo created with details taken from user input. + * @param ui object to handle user interactions + * @return ToDo created based on user's input + * @throws DukeyException on invalid user input for ToDo name + */ + public static ToDo createToDo(Ui ui, String input) throws DukeyException { + String[] inputArray = input.split("/"); + if (inputArray.length != 2) { + throw new DukeyException("Error! Invalid Format!"); + } + String toDoName = ui.readTaskName(inputArray[1]); + return new ToDo(toDoName); + } + + /** + * Returns a ToDo created from its log string. This method "loads" + * a ToDo from a save. + * @param logStringArray an array of strings where each string is a component of the log string that + * has been split up + * @return ToDo created from its log string. + */ + public static ToDo createToDoFromLog(String[] logStringArray) { + String name = logStringArray[2]; + boolean isMarked = !logStringArray[1].equals("0"); + return new ToDo(name, isMarked); + } + + /** + * Returns a message to be printed whenever a ToDo is added. + * @return the message to be printed + */ + @Override + public String getMessageWhenAdded() { + return "DukeyList just added a new todo:"; + } + + /** + * Returns the log string of a ToDo, which is a string containing details about the task. The log + * string will be used to save the task locally when the task list is saved. + * @return the log string of the ToDo + */ + @Override + public String getLogString() { + return "T" + "," + getMarkedStatus() + "," + this.getName(); + } + + /** + * Returns a string representation of the ToDo + */ + @Override + public String toString() { + if (this.isDone()) { + return TYPE + "[X]" + " " + this.getName(); + } + + return TYPE + "[ ]" + " " + this.getName(); + } +} + diff --git a/src/main/java/duke/Ui.java b/src/main/java/duke/Ui.java new file mode 100644 index 0000000000..1213c98c70 --- /dev/null +++ b/src/main/java/duke/Ui.java @@ -0,0 +1,166 @@ +package duke; + +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.Iterator; + +import duke.exceptions.DukeyException; + +/** + * Deals with user interactions, namely reading input from the user and printing messages to the user. + */ +public class Ui { + private Parser parser = new Parser(); + + public ActionEnum readCommand(String command) throws DukeyException { + return parser.parseCommand(command); + } + + public String readTaskName(String input) throws DukeyException { + return parser.parseTaskName(input); + } + + public LocalDate readTime(String timeString) throws DukeyException { + return parser.parseDate(timeString); + } + + public String readKeyword(String keyword) throws DukeyException { + return parser.parseKeyword(keyword); + } + + public int readAmount(String input) throws DukeyException { + return parser.parseAmount(input); + } + + public String readName(String input) throws DukeyException { + return parser.parseName(input); + } + + + public String getWelcomeMessage() { + String logo = " ____ _ \n" + + "| _ \\ _ _| | _____ \n" + + "| | | | | | | |/ / _ \\\n" + + "| |_| | |_| | < __/\n" + + "|____/ \\__,_|_|\\_\\___|\n"; + return "Hello from\n" + logo + '\n'; + } + + public String getGoodbyeMessage() { + return "Goodbye!! Please return to Dukey again soon!! :)"; + } + + public String printTask(int taskNumber, Task task) { + return (taskNumber + 1) + ". " + task; + } + + public String printTask(Task task) { + return task.toString(); + } + + public String printEmptyListMessage() { + return "DukeyList: DukeyList is empty!"; + } + + public String readList(ArrayList list) { + StringBuilder sb = new StringBuilder(); + if (list.isEmpty()) { + sb.append(printEmptyListMessage()); + } else { + sb.append("DukeyList:\n"); + Iterator it = list.iterator(); + it.forEachRemaining(x -> sb.append((list.indexOf(x) + 1) + ". " + x.toString() + '\n')); + } + return sb.toString(); + } + + public String printFoundTaskList(ArrayList foundTaskList) { + StringBuilder sb = new StringBuilder(); + if (foundTaskList.isEmpty()) { + sb.append("No tasks found matching this keyword!"); + } else { + sb.append("DukeyList found these tasks matching the keyword:\n"); + Iterator it = foundTaskList.iterator(); + it.forEachRemaining(x -> sb.append((x.getNumber() + 1) + ". " + x.getTask() + "\n")); + } + + return sb.toString(); + } + + public String printAddedMessage(Task task) { + return '\n' + task.getMessageWhenAdded() + "\n" + task; + } + + public String printMarkedMessage(int taskNumber, Task taskToMark) { + String response = ""; + response += "DukeyList: Task number " + (taskNumber + 1) + " has been marked as done!\n"; + response += printTask(taskNumber, taskToMark); + return response; + } + + public String printUnmarkedMessage(int taskNumber, Task taskToUnmark) { + String response = ""; + response += "DukeyList: Task number " + (taskNumber + 1) + " has been unmarked.\n"; + response += printTask(taskNumber, taskToUnmark); + return response; + } + + public String printDeletedMessage(Task taskToDelete) { + String response = ""; + response += "DukeyList: The following item has been removed!\n"; + response += this.printTask(taskToDelete); + return response; + } + + public String printSavedMessage() { + return "DukeList saved!"; + } + + + public String printLoadMessage(int status) { + if (status == 0) { + return "DukeyList is empty, starting a new list.\n"; + } else { + return "Saved list loaded:\n"; + } + } + + public String printClearedMessage() { + return "DukeyList save has been cleared."; + } + + + public String printSize(int size) { + if (size == 1) { + return "DukeyList now has 1 task.\n"; + } else { + return "DukeyList now has " + size + " tasks.\n"; + } + } + + public String printExceptionMessage(DukeyException e) { + return ":(\n" + e.getMessage(); + } + + public String printInstruction() { + StringBuilder sb = new StringBuilder(); + sb.append("DukeyList: Welcome to DukeyList!! To use DukeyList, " + + "type the appropriate command and follow the prompts:\n"); + sb.append("To list: 'list'\n"); + sb.append("To exit: 'bye'\n"); + sb.append("To add a todo: 'todo / '\n"); + sb.append("To add a deadline: 'deadline / / '\n"); + sb.append("To add an event: 'event / / / '\n"); + sb.append("To add a loan: 'loan / / / / "); + sb.append("To mark a task as done: 'mark / '\n"); + sb.append("To unmark a task: 'unmark / '\n"); + sb.append("To delete a task: 'delete / '\n"); + sb.append("To clear the list: 'clearList'\n"); + sb.append("To save the list: 'save'\n"); + sb.append("To find tasks using keywords: 'find / '\n"); + + return sb.toString(); + } + + +} diff --git a/src/main/java/duke/exceptions/DukeyException.java b/src/main/java/duke/exceptions/DukeyException.java new file mode 100644 index 0000000000..d6c5284af9 --- /dev/null +++ b/src/main/java/duke/exceptions/DukeyException.java @@ -0,0 +1,19 @@ +package duke.exceptions; + +/** + * Exceptions that can be thrown by Duke + */ +public class DukeyException extends Exception { + + /** + * Returns a DukeyExecption with a given message. + * @param message the exception message + */ + public DukeyException(String message) { + super(message); + } + + + + +} diff --git a/src/main/resources/images/Cap.jpeg b/src/main/resources/images/Cap.jpeg new file mode 100644 index 0000000000..4de4f9a3f4 Binary files /dev/null and b/src/main/resources/images/Cap.jpeg differ diff --git a/src/main/resources/images/IronMan.jpg b/src/main/resources/images/IronMan.jpg new file mode 100644 index 0000000000..5d53eca971 Binary files /dev/null and b/src/main/resources/images/IronMan.jpg differ diff --git a/src/main/resources/view/DialogBox.fxml b/src/main/resources/view/DialogBox.fxml new file mode 100644 index 0000000000..e433809947 --- /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..832f84382e --- /dev/null +++ b/src/main/resources/view/MainWindow.fxml @@ -0,0 +1,19 @@ + + + + + + + + + + + +