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 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/java/duke/TaskListTest.java b/src/test/java/duke/TaskListTest.java
new file mode 100644
index 0000000000..9b727ade58
--- /dev/null
+++ b/src/test/java/duke/TaskListTest.java
@@ -0,0 +1,17 @@
+package duke;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import org.junit.jupiter.api.Test;
+
+public class TaskListTest {
+
+ @Test
+ public void AddingTask() {
+ TaskList taskList = new TaskList();
+ ToDo toDo = new ToDo("test");
+ taskList.addTask(toDo);
+ assertEquals(taskList.isEmpty(), false);
+ assertEquals(taskList.getSize(), 1);
+ }
+}
diff --git a/src/test/java/duke/ToDoTest.java b/src/test/java/duke/ToDoTest.java
new file mode 100644
index 0000000000..518537fc16
--- /dev/null
+++ b/src/test/java/duke/ToDoTest.java
@@ -0,0 +1,25 @@
+package duke;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import org.junit.jupiter.api.Test;
+
+public class ToDoTest {
+
+ @Test
+ public void addToDo() {
+ ToDo toDo = new ToDo("testing ToDo");
+ assertNotNull(toDo);
+ assertEquals(toDo.getName(), "testing ToDo");
+ }
+
+ @Test
+ public void checkMarkAndUnmark() {
+ ToDo toDo = new ToDo("testing ToDo");
+ assertEquals(toDo.isDone(), false);
+ toDo.setMarked();
+ assertEquals(toDo.isDone(), true);
+ toDo.setUnmarked();
+ assertEquals(toDo.isDone(), false);
+ }
+}
diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT
index 657e74f6e7..b14df6442e 100644
--- a/text-ui-test/EXPECTED.TXT
+++ b/text-ui-test/EXPECTED.TXT
@@ -1,7 +1 @@
-Hello from
- ____ _
-| _ \ _ _| | _____
-| | | | | | | |/ / _ \
-| |_| | |_| | < __/
-|____/ \__,_|_|\_\___|
-
+Hi
diff --git a/text-ui-test/runtest.bat b/text-ui-test/runtest.bat
index 0873744649..62752b8814 100644
--- a/text-ui-test/runtest.bat
+++ b/text-ui-test/runtest.bat
@@ -15,7 +15,7 @@ IF ERRORLEVEL 1 (
REM no error here, errorlevel == 0
REM run the program, feed commands from input.txt file and redirect the output to the ACTUAL.TXT
-java -classpath ..\bin Duke < input.txt > ACTUAL.TXT
+java -classpath ..\bin duke.Duke < input.txt > ACTUAL.TXT
REM compare the output to the expected output
FC ACTUAL.TXT EXPECTED.TXT
diff --git a/text-ui-test/runtest.sh b/text-ui-test/runtest.sh
old mode 100644
new mode 100755