diff --git a/.gitignore b/.gitignore
index 2873e189e..09df8809c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,3 +15,4 @@ bin/
/text-ui-test/ACTUAL.TXT
text-ui-test/EXPECTED-UNIX.TXT
+*.class
diff --git a/docs/README.md b/docs/README.md
index 8077118eb..08eea6b9b 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,29 +1,108 @@
-# User Guide
+# DOM User Guide
+Dom is a desktop program to store a list of tasks for the user via a Command Line Interface(CLI)
+
+## Quick Start
+1. Ensure that you have JAVA '11' or above installed in your computer.
+2. Download the latest 'ip.jar' from [here](https://github.com/bljhty/ip/releases)
+3. Copy downloaded 'ip.jar' file to the desired home folder of your choice
+4. open a command terminal and 'cd' to the folder mentioned in ster 3
+5. type in the following to run the application
+
+```
+java -jar ip.jar
+```
+
+6. if successful, the following greeting should appear:
+```
+Hello from
+ ____
+ | _ \ ___ _ __ ___
+ | | | |/ _ \| '_ ` _ \
+ | |_| | (_) | | | | | |
+ |____/ \___/|_| |_| |_|
+
+____________________________________________________________
+Hello! I'm Dom
+What can I do for you?
+```
+7. Type in desired command to start using the program.
## Features
+### Adding a todo task: `todo`
+Adds a task of type todo to the list.
+
Todo contains a description.
-### Feature-ABC
+Format: `todo DESCRIPTION`
+
Example: `todo read book`
-Description of the feature.
+### Adding an event: `event`
+Adds a task of type event to the list.
+
Event contains a description, from and to.
-### Feature-XYZ
+Format: `event DESCRIPTION /from FROM /to TO`
+
Example: `event CS2113 tutorial /from 4 April 2023 4pm /to 6pm`
-Description of the feature.
-## Usage
+### Adding a deadline: `deadline`
+Adds a task deadline to the list.
+
Deadline contains description and by.
-### `Keyword` - Describe action
+Format: `deadline DESCRIPTION /by BY`
+
Example: `deadline CS2113 iP /by 3 March 11.59pm`
-Describe the action and its outcome.
+### Viewing full list of tasks: `list`
+Shows a list of all tasks recorded in the list.
-Example of usage:
+Format: `list`
-`keyword (optional arguments)`
+### Mark task as done: `mark`
+Marks task as done based on the task number indicated.
-Expected outcome:
+Format: `mark TASK_NUMBER`
+- TASK_NUMBER must be an integer that is within the list size
-Description of the outcome.
+Example: `mark 2`
-```
-expected output
-```
+### Mark task as not done: `unmark`
+Marks task as not done based on the task number indicated.
+
+Format: `unmark TASK_NUMBER`
+- TASK_NUMBER must be an integer that is within the list size
+
+Example: `unmark 3`
+
+### Delete task: `delete`
+Deletes the task based on the task number indicated.
+
+Format: `delete TASK_NUMBER`
+- TASK_NUMBER must be an integer that is within the list size
+
+Example: `delete 1`
+
+### Find task(s): `find`
+Searches the tasks in the list, returning the tasks containing the keyword in their description.
+
+Format: `find KEYWORD`
+
Example: `find CS2113`
+
+### Exiting DOM: `bye`
+Exits and closes the program.
+
+Format: `bye`
+
+## FAQ
+**Q**: Can I edit the file manually from the save.txt file?
+
**A**: It is not advised to do so, as formatting of the save.txt file incorrectly will cause the program to crash.
+
+## Command Summary
+| **Command** | **Format, Examples** |
+|-------------|------------------------------------------------------------------------------------------------------|
+| todo | `todo DESCRIPTION`
e.g. `todo read book` |
+| event | `event DESCRIPTION /from FROM /to TO`
e.g. `event CS2113 tutorial /from 4 April 2023 4pm /to 6pm` |
+| deadline | `deadline DESCRIPTION /by BY`
e.g. `deadline CS2113 iP /by 3 March 11.59pm` |
+| list | `list` |
+| mark | `mark TASK_NUMBER`
e.g. `mark 2` |
+| unmark | `unmark TASK_NUMBER`
e.g. `unmark 3` |
+| delete | `delete TASK_NUMBER`
e.g. `delete 1` |
+| find | `find KEYWORD`
e.g. `find CS2113` |
+| bye | `bye` |
diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java
index 5d313334c..125c584ce 100644
--- a/src/main/java/Duke.java
+++ b/src/main/java/Duke.java
@@ -1,10 +1,284 @@
+import java.io.IOException;
+import java.util.ArrayList;
+
+import storage.Storage;
+
+import tasks.Deadline;
+import tasks.Event;
+import tasks.Task;
+import tasks.Todo;
+import tasks.DukeException;
+import tasks.TaskList;
+import ui.Ui;
+import tasks.Parser;
+
+
+/**
+ * Represents the main logic for the Duke application.
+ * Manages the initialization, execution, and storage of tasks.
+ */
public class Duke {
+ private static final String lineDivider = "____________________________________________________________";
+ private Storage storage;
+ private TaskList tasks;
+ private Ui ui;
+
+ /*
+ * Initializes a Duke object with the given file path.
+ *
+ * @param filePath The file path to the file containing the tasks.
+ * @throws DukeException If an error occurs while loading the tasks from the file.
+ *
+ */
+ public Duke(String filePath) {
+ ui = new Ui();
+ storage = new Storage(filePath);
+ try {
+ tasks = new TaskList(storage.load());
+ } catch (DukeException e) {
+ ui.showErrorMsg(e.getMessage());
+ tasks = new TaskList();
+ }
+ }
+
+ /*
+ * Runs the Duke application.
+ *
+ * @throws DukeException If an error occurs while running the application.
+ */
+ public void run() {
+ Ui.greet();
+ boolean isExit = false;
+ while (!isExit) {
+ try {
+ String fullCommand = ui.readCommand();
+ String action = Parser.parse(fullCommand);
+ String[] parts = action.split("-", 2);
+ switch(parts[0]) {
+ case "exit":
+ isExit = true;
+ ui.showLineDivider();
+ break;
+ case "list":
+ tasks.listTasks();
+ ui.showLineDivider();
+ break;
+ case "markAsDone":
+ handleMarkAsDone(parts[1]);
+ break;
+ case "markAsNotDone":
+ handleMarkAsNotDone(parts[1]);
+ break;
+ case "delete":
+ handleDelete(parts[1]);
+ break;
+ case "addTodo":
+ handleAddTodo(parts[1]);
+ break;
+ case "addDeadline":
+ handleAddDeadline(parts[1]);
+ break;
+ case "addEvent":
+ handleAddEvent(parts[1]);
+ break;
+ case "find":
+ handleFind(parts[1]);
+ break;
+ default:
+ throw new DukeException("Unknown action");
+ }
+ } catch (DukeException e) {
+ ui.showError(e.getMessage());
+ }
+ }
+ ui.showGoodbye();
+ }
+
+ /*
+ * Handles the markAsDone command.
+ * also checks if the task number is valid.
+ *
+ * @param part The part of the command after the markAsDone keyword.
+ * @throws DukeException If an error occurs while marking the task as done.
+ */
+ private void handleMarkAsDone(String part) {
+ try {
+ int doneIndex = Integer.parseInt(part) - 1;
+ tasks.markTaskAsDone(doneIndex);
+ ui.showUnmarkedTask(tasks.getTaskIndex(doneIndex));
+ storage.save(tasks.getTasks());
+ } catch (NumberFormatException e) {
+ ui.showErrorMsg("Error: ☹ OOPS!!! Task number must be an integer.\n");
+ ui.showErrorMsg("Please enter command in the format: mark ");
+ } catch (IOException e) {
+ System.out.println("An error occurred while saving tasks to the file.");
+ } catch (IndexOutOfBoundsException e) {
+ ui.showErrorMsg("Error: ☹ OOPS!!! Invalid task number.");
+ }
+ ui.showLineDivider();
+ }
+
+ /*
+ * Handles the markAsNotDone command.
+ * also checks if the task number is valid.
+ *
+ * @param part The part of the command after the markAsNotDone keyword.
+ * @throws DukeException If an error occurs while marking the task as not done.
+ * @throws IOException If an error occurs while saving the tasks to the file.
+ * @throws IndexOutOfBoundsException If the task number is invalid.
+ */
+ private void handleMarkAsNotDone(String part) {
+ try {
+ int undoneIndex = Integer.parseInt(part) - 1;
+ tasks.getTaskIndex(undoneIndex).markAsDone(false);
+ ui.showUnmarkedTask(tasks.getTaskIndex(undoneIndex));
+ storage.save(tasks.getTasks());
+ } catch (NumberFormatException e) {
+ ui.showErrorMsg("Error: ☹ OOPS!!! Task number must be an integer.\n");
+ ui.showErrorMsg("Please enter command in the format: unmark ");
+ } catch (IndexOutOfBoundsException e) {
+ ui.showErrorMsg("Error: ☹ OOPS!!! Invalid task number.");
+ } catch (IOException e) {
+ System.out.println("An error occurred while saving tasks to the file.");
+ }
+ ui.showLineDivider();
+ }
+
+ /*
+ * Handles the delete command.
+ * also checks if the task number is valid.
+ *
+ * @param part The part of the command after the delete keyword.
+ * @throws DukeException If an error occurs while deleting the task.
+ * @throws IOException If an error occurs while saving the tasks to the file.
+ * @throws IndexOutOfBoundsException If the task number is invalid.
+ * @throws NumberFormatException If the task number is not an integer.q
+ */
+ private void handleDelete(String part) {
+ try {
+ int deleteIndex = Integer.parseInt(part) - 1;
+ if (deleteIndex < 0 || deleteIndex >= tasks.getTaskCount()) {
+ System.out.println("☹ OOPS!!! Invalid task number.");
+ }
+ Task taskToDelete = tasks.getTaskIndex(deleteIndex);
+ tasks.deleteTask(deleteIndex);
+ storage.save(tasks.getTasks());
+ ui.showDeletedTask(taskToDelete, tasks.getTaskCount());
+ } catch (IOException e) {
+ System.out.println("An error occurred while saving tasks to the file.");
+ } catch (NumberFormatException e) {
+ ui.showErrorMsg("Error: ☹ OOPS!!! Task number must be an integer.\n");
+ ui.showErrorMsg("Please enter command in the format: delete ");
+ } catch (IndexOutOfBoundsException e) {
+ ui.showErrorMsg("Error: ☹ OOPS!!! Invalid task number.");
+ }
+ ui.showLineDivider();
+ }
+
+ /*
+ * Handles the addTodo command.
+ * checks if the description is empty.
+ *
+ * @param part The part of the command after the addTodo keyword.
+ * @throws DukeException If an error occurs while adding the todo.
+ * @throws IOException If an error occurs while saving the tasks to the file.
+ */
+ private void handleAddTodo(String part) {
+ if (part.trim().isEmpty()) {
+ ui.showErrorMsg("☹ OOPS!!! The description of a todo cannot be empty.\n" + lineDivider);
+ return;
+ }
+ Task newTodo = new Todo(part);
+ tasks.addTask(newTodo);
+ ui.showAddedTask(newTodo, tasks.getTaskCount());
+ try {
+ storage.save(tasks.getTasks());
+ } catch (IOException e) {
+ System.out.println("An error occurred while saving tasks to the file.");
+ }
+ ui.showLineDivider();
+ }
+
+ /*
+ * Handles the addDeadline command.
+ * checks if description or deadline is empty.
+ *
+ * @param part The part of the command after the addDeadline keyword.
+ * @throws DukeException If an error occurs while adding the deadline.
+ * @throws IOException If an error occurs while saving the tasks to the file.
+ */
+ private void handleAddDeadline(String part) {
+ String[] deadlineParts = part.split("/by", 2);
+ if (deadlineParts.length < 2 || deadlineParts[1].trim().isEmpty() || deadlineParts[0].trim().isEmpty()) {
+ ui.showErrorMsg("☹ OOPS!!! The description of a deadline cannot be empty. \n" + lineDivider);
+ return;
+ }
+ Task newDeadline = new Deadline(deadlineParts[0].trim(), deadlineParts[1].trim());
+ tasks.addTask(newDeadline);
+ ui.showAddedTask(newDeadline, tasks.getTaskCount());
+ try {
+ storage.save(tasks.getTasks());
+ } catch (IOException e) {
+ System.out.println("An error occurred while saving tasks to the file.");
+ }
+ ui.showLineDivider();
+ }
+
+ /*
+ * Handles the addEvent command.
+ * checks if description, start time or end time is empty.
+ *
+ * @param part The part of the command after the addEvent keyword.
+ * @throws DukeException If an error occurs while adding the event.
+ */
+ private void handleAddEvent(String part) {
+ String[] eventParts = part.split("/from", 2);
+ if (eventParts.length < 2 || eventParts[1].trim().isEmpty() || eventParts[0].trim().isEmpty()) {
+ ui.showErrorMsg("☹ OOPS!!! The description of an event cannot be empty.\n" + lineDivider);
+ return;
+ }
+
+ String[] fromToParts = eventParts[1].split("/to", 2);
+ if (fromToParts.length < 2 || fromToParts[0].trim().isEmpty() || fromToParts[1].trim().isEmpty()) {
+ ui.showErrorMsg("☹ OOPS!!! The start or end time for an event cannot be empty.\n" + lineDivider);
+ return;
+ }
+
+ Task newEvent = new Event(eventParts[0].trim(), fromToParts[0].trim(), fromToParts[1].trim());
+ tasks.addTask(newEvent);
+ ui.showAddedTask(newEvent, tasks.getTaskCount());
+ try {
+ storage.save(tasks.getTasks());
+ } catch (IOException e) {
+ System.out.println("An error occurred while saving tasks to the file.");
+ }
+ ui.showLineDivider();
+ }
+
+ /*
+ * Handles the find command.
+ * checks if the keyword is empty.
+ *
+ * @param part The part of the command after the find keyword.
+ * @throws DukeException If an error occurs while finding the tasks.
+ */
+ private void handleFind(String part) {
+ String keyword = part.trim();
+ if (keyword.isEmpty()) {
+ ui.showErrorMsg("☹ OOPS!!! The keyword for find cannot be empty.\n" + lineDivider);
+ return;
+ }
+
+ ArrayList foundTasks = tasks.findTasks(keyword);
+ if (foundTasks.isEmpty()) {
+ ui.showErrorMsg("☹ OOPS!!! No matching tasks found with the given keyword.\n" + lineDivider);
+ return;
+ }
+ ui.showFoundTasks(foundTasks);
+ ui.showLineDivider();
+ }
+
public static void main(String[] args) {
- String logo = " ____ _ \n"
- + "| _ \\ _ _| | _____ \n"
- + "| | | | | | | |/ / _ \\\n"
- + "| |_| | |_| | < __/\n"
- + "|____/ \\__,_|_|\\_\\___|\n";
- System.out.println("Hello from\n" + logo);
+ new Duke("data/tasks.txt").run();
}
}
diff --git a/src/main/java/storage/Storage.java b/src/main/java/storage/Storage.java
new file mode 100644
index 000000000..930aeea4a
--- /dev/null
+++ b/src/main/java/storage/Storage.java
@@ -0,0 +1,97 @@
+package storage;
+
+import tasks.Task;
+import tasks.Todo;
+import tasks.Deadline;
+import tasks.Event;
+import tasks.DukeException;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Scanner;
+
+/**
+ * Represents the storage of tasks.
+ */
+public class Storage {
+
+ private String filePath;
+
+ /**
+ * Initializes a new Storage instance with the given file path.
+ *
+ * @param filePath The path of the file to store task data.
+ */
+ public Storage(String filePath) {
+ this.filePath = filePath;
+ }
+
+ /**
+ * Loads the tasks from the file.
+ *
+ * @return The list of tasks.
+ * @throws DukeException If the file is not found or the data format is incorrect.
+ */
+ public ArrayList load() throws DukeException {
+ File file = new File(filePath);
+ ArrayList tasks = new ArrayList<>();
+
+ try {
+ Scanner scanner = new Scanner(file);
+ while (scanner.hasNext()) {
+ String data = scanner.nextLine();
+ String[] parts = data.split(" \\| ");
+
+ switch (parts[0]) {
+ case "T":
+ Todo todo = new Todo(parts[2]);
+ if (parts[1].equals("1")) {
+ todo.markAsDone(true);
+ }
+ tasks.add(todo);
+ break;
+ case "D":
+ Deadline deadline = new Deadline(parts[2], parts[3]);
+ if (parts[1].equals("1")) {
+ deadline.markAsDone(true);
+ }
+ tasks.add(deadline);
+ break;
+ case "E":
+ if (parts.length < 5) {
+ throw new DukeException("Stored Event task format is incorrect");
+ }
+ Event event = new Event(parts[2], parts[3], parts[4]);
+ if (parts[1].equals("1")) {
+ event.markAsDone(true);
+ }
+ tasks.add(event);
+ break;
+ default:
+ System.out.println("Invalid task type");
+ }
+ }
+ scanner.close();
+ } catch (IOException e) {
+ System.out.println("Error loading file");
+ }
+
+ return tasks;
+ }
+
+ /**
+ * Saves the tasks to the file.
+ *
+ * @param tasks The list of tasks to be saved.
+ * @throws IOException If there is an error writing to the file.
+ */
+ public void save(ArrayList tasks) throws IOException {
+ FileWriter fw = new FileWriter(filePath);
+ for (Task task : tasks) {
+ fw.write(task.toData() + "\n");
+ }
+ fw.close();
+ }
+}
diff --git a/src/main/java/tasks/Deadline.java b/src/main/java/tasks/Deadline.java
new file mode 100644
index 000000000..6d9d7afe8
--- /dev/null
+++ b/src/main/java/tasks/Deadline.java
@@ -0,0 +1,70 @@
+package tasks;
+
+/**
+ * Represents a 'Deadline' task in the Duke application.
+ *
+ * @see Task
+ */
+public class Deadline extends Task{
+ protected String by;
+
+ /*
+ * Initializes a new Deadline with the given description and deadline.
+ *
+ * @param description The description of the deadline.
+ * @param by The deadline of the deadline.
+ */
+ public Deadline(String description, String by) {
+ super(description);
+ if (description == null || description.trim().isEmpty()) {
+ throw new IllegalArgumentException("description cannot be empty");
+ }
+ this.by = by;
+ }
+
+ /*
+ * Returns the deadline of the deadline.
+ *
+ * @return The deadline of the deadline.
+ */
+ public String getBy() {
+ return this.by;
+ }
+
+ /*
+ * Converts the Deadline task object to a data representation for saving to a file.
+ *
+ * @return The data representation of the Deadline task.
+ */
+ public static Task dataToTask(String taskData) {
+ int firstSplitIndex = taskData.indexOf("|");
+ int secondSplitIndex = taskData.indexOf("|", firstSplitIndex + 1);
+ boolean isDone = taskData.substring(0, firstSplitIndex - 1).equals("1");
+ String desc = taskData.substring(firstSplitIndex + 2, secondSplitIndex - 1);
+ String by = taskData.substring(secondSplitIndex + 2);
+ Deadline newDeadline = new Deadline(desc, by);
+ newDeadline.isDone = isDone;
+ return newDeadline;
+ }
+
+ /*
+ * Converts the Deadline task object to a data representation for saving to a file.
+ *
+ * @return The data representation of the Deadline task.
+ */
+ @Override
+ public String toData() {
+ String done = String.valueOf(this.isDone ? 1 : 0);
+ return "D | " + done + " | " + this.description + " | " + this.by;
+ }
+
+ /*
+ * Returns the string representation of the Deadline task.
+ *
+ * @return The string representation of the Deadline task.
+ */
+ @Override
+ public String toString() {
+ return "[D]" + super.toString() + " (by: " + this.by + ")";
+ }
+}
diff --git a/src/main/java/tasks/DukeException.java b/src/main/java/tasks/DukeException.java
new file mode 100644
index 000000000..8f1089040
--- /dev/null
+++ b/src/main/java/tasks/DukeException.java
@@ -0,0 +1,22 @@
+package tasks;
+
+/**
+ * Represents an exception that is thrown when an error occurs in the Duke application.
+ */
+public class DukeException extends Exception {
+ public DukeException() {
+ super();
+ }
+
+ public DukeException(String message) {
+ super(message);
+ }
+
+ public DukeException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public DukeException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/src/main/java/tasks/Event.java b/src/main/java/tasks/Event.java
new file mode 100644
index 000000000..f31c315b8
--- /dev/null
+++ b/src/main/java/tasks/Event.java
@@ -0,0 +1,80 @@
+package tasks;
+
+/**
+ * Represents an 'Event' task in the Duke application.
+ */
+public class Event extends Task {
+ protected String from;
+ protected String to;
+
+ /*
+ * Initializes a new Event with the given description, start time, and end time.
+ *
+ * @param description The description of the event.
+ * @param from The start time of the event.
+ * @param to The end time of the event.
+ */
+ public Event(String description, String from, String to) {
+ super(description);
+ this.from = from;
+ this.to = to;
+ }
+
+ /*
+ * Returns the start time of the event.
+ *
+ * @return The start time of the event.
+ */
+ public String getFrom() {
+ return this.from;
+ }
+
+ /*
+ * Returns the end time of the event.
+ *
+ * @return The end time of the event.
+ */
+ public String getTo() {
+ return this.to;
+ }
+
+ /*
+ * Converts the Event task object to a data representation for saving to a file.
+ *
+ * @return The data representation of the Event task.
+ */
+ @Override
+ public String toData() {
+ String done = String.valueOf(this.isDone ? 1 : 0);
+ return "E | " + done + " | " + this.description + " | " + this.from + " | " + this.to;
+ }
+
+ /*
+ * Returns the string representation of the Event task.
+ *
+ * @return The string representation of the Event task.
+ */
+ @Override
+ public String toString() {
+ return "[E]" + super.toString() + "(from: " + this.from + " to: " + this.to + ")";
+ }
+
+ /*
+ * Returns a new Event task based on the given task data.
+ *
+ * @param taskData The task data to be converted.
+ * @return The new Event task.
+ */
+ public static Task dataToTask(String taskData) {
+ int firstSplitIndex = taskData.indexOf("|");
+ int secondSplitIndex = taskData.indexOf("|", firstSplitIndex + 1);
+ int thirdSplitIndex = taskData.indexOf("|", secondSplitIndex + 1);
+ boolean isDone = taskData.substring(0, firstSplitIndex - 1).equals("1");
+ String desc = taskData.substring(firstSplitIndex + 2, secondSplitIndex - 1);
+ String from = taskData.substring(secondSplitIndex + 2, thirdSplitIndex - 1);
+ String to = taskData.substring(thirdSplitIndex + 2);
+ Event newEvent = new Event(desc, from, to);
+ newEvent.isDone = isDone;
+ return newEvent;
+ }
+}
diff --git a/src/main/java/tasks/Parser.java b/src/main/java/tasks/Parser.java
new file mode 100644
index 000000000..ac6ea7905
--- /dev/null
+++ b/src/main/java/tasks/Parser.java
@@ -0,0 +1,51 @@
+package tasks;
+
+/**
+ * Represents a parser that parses user commands.
+ */
+
+public class Parser {
+ /**
+ * Parses the user command into actionable instructions.
+ *
+ * @param fullCommand The entire command entered by the user.
+ * @return A string representation of the action to be taken.
+ * @throws DukeException If the command is invalid or incomplete.
+ */
+
+ public static String parse(String fullCommand) throws DukeException {
+ String trimmedCommand = fullCommand.trim(); // Remove leading and trailing whitespaces
+ String commandLowerCase = trimmedCommand.toLowerCase(); // Convert command to lowercase for case-insensitive matching
+
+ if (commandLowerCase.equals("bye")) {
+ return "exit";
+ } else if (commandLowerCase.equals("list")) {
+ return "list";
+ } else if (commandLowerCase.startsWith("mark ")) {
+ return "markAsDone-" + trimmedCommand.substring(5);
+ } else if (commandLowerCase.startsWith("unmark ")) {
+ return "markAsNotDone-" + trimmedCommand.substring(7);
+ } else if (commandLowerCase.startsWith("delete")) {
+ if (commandLowerCase.length() <= 6 || commandLowerCase.substring(6).trim().isEmpty()) {
+ throw new DukeException("☹ OOPS!!! Please provide a task number to delete.");
+ }
+ return "delete-" + trimmedCommand.substring(7);
+ } else if (commandLowerCase.startsWith("todo")) {
+ if (commandLowerCase.length() <= 4 || commandLowerCase.substring(4).trim().isEmpty()) {
+ throw new DukeException("☹ OOPS!!! The description of a todo cannot be empty.");
+ }
+ return "addTodo-" + trimmedCommand.substring(5);
+ } else if (commandLowerCase.startsWith("deadline ")) {
+ // Additional parsing for deadline command
+ return "addDeadline-" + trimmedCommand.substring(9);
+ } else if (commandLowerCase.startsWith("event ")) {
+ // Additional parsing for event command
+ return "addEvent-" + trimmedCommand.substring(6);
+ } else if (commandLowerCase.startsWith("find ")) {
+ // Additional parsing for find command
+ return "find-" + trimmedCommand.substring(5);
+ } else {
+ throw new DukeException("Unknown command");
+ }
+ }
+}
diff --git a/src/main/java/tasks/Task.java b/src/main/java/tasks/Task.java
new file mode 100644
index 000000000..913662d31
--- /dev/null
+++ b/src/main/java/tasks/Task.java
@@ -0,0 +1,64 @@
+package tasks;
+
+/**
+ * Represents a task in the Duke application.
+ */
+public abstract class Task {
+ protected String description;
+ protected boolean isDone;
+
+
+ /*
+ * Initializes a new Task with the given description.
+ *
+ * @param description The description of the task.
+ * @param isDone The status of the task.
+ */
+ public Task(String description) {
+ this.description = description;
+ this.isDone = false;
+ }
+
+ /*
+ * Returns the status icon of the task.
+ *
+ * @return The status icon of the task.
+ */
+ public String getStatusIcon() {
+ return (isDone ? "[X]" : "[ ]");
+ }
+
+ /*
+ * Marks the task as done.
+ *
+ * @param isDone The status of the task.
+ */
+ public void markAsDone(boolean isDone) {
+ this.isDone = isDone;
+ }
+
+ /*
+ * Returns the string representation of the task.
+ *
+ * @return The string representation of the task.
+ */
+ public String toString() {
+ return getStatusIcon() + " " + this.description;
+ }
+
+ /*
+ * Returns the description of the task.
+ *
+ * @return The description of the task.
+ */
+ public String getDescription() {
+ return this.description;
+ }
+
+ /*
+ * Converts the task object to a data representation for saving to a file.
+ *
+ * @return The data representation of the task.
+ */
+ public abstract String toData();
+}
diff --git a/src/main/java/tasks/TaskList.java b/src/main/java/tasks/TaskList.java
new file mode 100644
index 000000000..dfbcda51f
--- /dev/null
+++ b/src/main/java/tasks/TaskList.java
@@ -0,0 +1,122 @@
+package tasks;
+
+import java.util.ArrayList;
+
+/**
+ * Represents a list of tasks.
+ */
+public class TaskList {
+ private ArrayList tasks;
+
+ /**
+ * Initializes a new TaskList instance with an empty list of tasks.
+ */
+ public TaskList() {
+ this.tasks = new ArrayList<>();
+ }
+
+ /**
+ * Initializes a new TaskList instance with the specified list of tasks.
+ *
+ * @param tasks The list of tasks.
+ */
+ public TaskList(ArrayList tasks) {
+ this.tasks = tasks;
+ }
+
+ /**
+ * Adds a task to the list.
+ *
+ * @param task The task to be added.
+ */
+ public void addTask(Task task) {
+ this.tasks.add(task);
+ }
+
+ /**
+ * Removes a task from the list based on its index.
+ *
+ * @param index The index of the task to be removed.
+ */
+ public void deleteTask(int index) {
+ this.tasks.remove(index);
+ }
+
+ /**
+ * Returns the task at the specified index.
+ *
+ * @param index The index of the task to be returned.
+ * @return The task at the specified index.
+ */
+ public Task getTaskIndex(int index) {
+ return this.tasks.get(index);
+ }
+
+ /**
+ * Returns the number of tasks in the list.
+ *
+ * @return The number of tasks in the list.
+ */
+ public int getTaskCount() {
+ return this.tasks.size();
+ }
+
+ /**
+ * Marks a specified task as done.
+ *
+ * @param index The index of the task to be marked as done.
+ */
+ public void markTaskAsDone(int index) {
+ Task task = tasks.get(index);
+ task.markAsDone(true);
+ }
+
+ /**
+ * Marks a specified task as undone.
+ *
+ * @param index The index of the task to be marked as undone.
+ */
+ public void markTaskAsUndone(int index) {
+ Task task = tasks.get(index);
+ task.markAsDone(false);
+ }
+
+ /**
+ * Prints the list of tasks.
+ */
+ public void listTasks() {
+ if (tasks.size() == 0) {
+ System.out.println("ERROR: ☹ OOPS!!! No tasks is available.");
+ return;
+ }
+ System.out.println("Here are the tasks in your list:");
+ for (int i = 0; i < tasks.size(); i++) {
+ System.out.println((i + 1) + "." + tasks.get(i));
+ }
+ }
+
+ /**
+ * Returns the list of tasks.
+ *
+ * @return The list of tasks.
+ */
+ public ArrayList getTasks() {
+ return this.tasks;
+ }
+
+ /**
+ * Finds tasks that contain the specified keyword.
+ *
+ * @param keyword The keyword to be searched for.
+ * @return The list of tasks that contain the specified keyword.
+ */
+ public ArrayList findTasks(String keyword) {
+ ArrayList foundTasks = new ArrayList<>();
+ for (Task task : tasks) {
+ if (task.getDescription().contains(keyword)) {
+ foundTasks.add(task);
+ }
+ }
+ return foundTasks;
+ }
+}
diff --git a/src/main/java/tasks/Todo.java b/src/main/java/tasks/Todo.java
new file mode 100644
index 000000000..1ec2f0b58
--- /dev/null
+++ b/src/main/java/tasks/Todo.java
@@ -0,0 +1,46 @@
+package tasks;
+
+/**
+ * Represents a 'Todo' task in the Duke application.
+ */
+public class Todo extends Task {
+ public Todo (String description) {
+ super(description);
+ }
+
+ /**
+ * Returns a new Todo task based on the given task data.
+ *
+ * @param taskData The task data to be converted.
+ * @return The new Todo task.
+ */
+ public static Task dataToTask(String taskData) {
+ int firstSplitIndex = taskData.indexOf("|");
+ boolean isDone = taskData.substring(0, firstSplitIndex - 1).equals("1");
+ String desc = taskData.substring(firstSplitIndex + 2);
+ Todo newTodo = new Todo(desc);
+ newTodo.isDone = isDone;
+ return newTodo;
+ }
+
+ /**
+ * Converts the Todo task object to a data representation for saving to a file.
+ *
+ * @return The data representation of the Todo task.
+ */
+ @Override
+ public String toData() {
+ String done = String.valueOf(this.isDone ? 1 : 0);
+ return "T | " + done + " | " + this.description;
+ }
+
+ /**
+ * Returns the string representation of the Todo task.
+ *
+ * @return The string representation of the Todo task.
+ */
+ @Override
+ public String toString() {
+ return "[T]" + super.toString();
+ }
+}
diff --git a/src/main/java/ui/Ui.java b/src/main/java/ui/Ui.java
new file mode 100644
index 000000000..1d55b3c79
--- /dev/null
+++ b/src/main/java/ui/Ui.java
@@ -0,0 +1,205 @@
+package ui;
+
+import java.util.Scanner;
+import java.util.ArrayList;
+import tasks.Task;
+
+/**
+ * Represents the user interface of the Duke application.
+ */
+
+public class Ui {
+ private Scanner scanner;
+
+ private static final String lineDivider = "____________________________________________________________";
+
+ /**
+ * Initializes the Ui with a new Scanner instance.
+ */
+ public Ui() {
+ this.scanner = new Scanner(System.in);
+ }
+
+ /**
+ * Displays the greeting message to the user when the application starts.
+ */
+ public static void greet() {
+ String logo = " ____ \n"
+ + " | _ \\ ___ _ __ ___ \n"
+ + " | | | |/ _ \\| '_ ` _ \\ \n"
+ + " | |_| | (_) | | | | | |\n"
+ + " |____/ \\___/|_| |_| |_|\n";
+ System.out.println("Hello from\n" + logo);
+ String greetings = lineDivider +
+ "\nHello! I'm Dom\n" +
+ "What can I do for you?\n" +
+ lineDivider;
+ System.out.println(greetings);
+ }
+
+ /**
+ * Displays a goodbye message to the user.
+ */
+ public void goodbye() {
+ String goodbye = lineDivider +
+ "\nBye. Hope to see you again soon!\n" +
+ lineDivider;
+ System.out.println(goodbye);
+ }
+
+ /**
+ * Displays an error message when there is a loading error.
+ */
+ public static void showLoadingError() {
+ System.out.println("Error loading file");
+ }
+
+ /**
+ * Displays the specified error message to the user.
+ *
+ * @param errorMsg The error message to display.
+ */
+ public void showErrorMsg(String errorMsg) {
+ System.out.println(errorMsg);
+ }
+
+ /**
+ * Reads a command from the user.
+ *
+ * @return The command entered by the user.
+ */
+ public String readCommand() {
+ return scanner.nextLine();
+ }
+
+ /**
+ * Displays a line divider to the user.
+ */
+ public void showLineDivider() {
+ System.out.println(lineDivider);
+ }
+
+ /**
+ * Lists all tasks currently in the task list.
+ *
+ * @param tasks The list of tasks to be displayed.
+ */
+ public void listTasks(ArrayList tasks) {
+ if (tasks.size() == 0) {
+ System.out.println("You have no tasks in your list");
+ return;
+ }
+ System.out.println("Here are the tasks in your list:");
+ for (int i = 0; i < tasks.size(); i++) {
+ System.out.println((i + 1) + "." + tasks.get(i));
+ }
+ }
+
+ /**
+ * Displays a message to the user when a task is marked as done.
+ *
+ * @param task The task that was marked as done.
+ */
+ public void showTaskMarked(Task task) {
+ System.out.println("OK, I've marked this task as done:\n" + " "
+ + task.getStatusIcon() + " " + task.getDescription());
+ }
+
+
+ /**
+ * Displays a message to the user when a task is marked as undone.
+ *
+ * @param task The task that was marked as undone.
+ */
+ public void showUnmarkedTask(Task task) {
+ System.out.println(" Nice! I've marked this task as not done:\n" + " " + task.getStatusIcon()
+ + " " + task.getDescription());
+ }
+
+ /**
+ * Displays a message to the user when a task is deleted.
+ *
+ * @param task The task that was deleted.
+ * @param taskCount The total number of tasks in the list after deletion.
+ */
+ public void showInvalidTaskNumberError() {
+ System.out.println("Error: Invalid task number.");
+ }
+
+ /*
+ * Displays a message to the user when a task is deleted.
+ *
+ * @param task The task that was deleted.
+ * @param taskCount The total number of tasks in the list after deletion.
+ *
+ */
+ public void showEmptyTaskNumberError() {
+ System.out.println("Error: ☹ OOPS!!! Task number cannot be empty\n");
+ System.out.println("Please enter command in the format: unmark ");
+ }
+
+ /*
+ * Displays a message to the user when a task is deleted.
+ *
+ * @param task The task that was deleted.
+ * @param taskCount The total number of tasks in the list after deletion.
+ *
+ */
+ public void showInvalidTaskNumberFormatError() {
+ System.out.println("Error: ☹ OOPS!!! Task number must be an integer.\n");
+ System.out.println("Please enter command in the format: unmark ");
+ }
+
+ /**
+ * Displays a message to the user when a task is added.
+ * @param task The task that was added.
+ * @param taskCount The total number of tasks in the list after addition.
+ */
+ public void showAddedTask(Task task, int taskCount) {
+ System.out.println("Got it. I've added this task:");
+ System.out.println(" " + task);
+ System.out.println("Now you have " + taskCount + " tasks in the list.");
+ }
+
+ /**
+ * Displays a message to the user when a task is deleted.
+ * @param task The task that was deleted.
+ * @param taskCount The total number of tasks in the list after deletion.
+ */
+ public void showDeletedTask(Task task, int taskCount) {
+ System.out.println("Noted. I've removed this task:");
+ System.out.println(" " + task);
+ System.out.println("Now you have " + taskCount + " tasks in the list.");
+ }
+
+ /**
+ * Displays a message to the user when a task is found.
+ *
+ * @param task The task that was found.
+ */
+ public void showError(String errorMessage) {
+ System.out.println(errorMessage);
+ }
+
+ /**
+ * Displays a message to the user when a task is found.
+ *
+ * @param task The task that was found.
+ */
+ public void showGoodbye() {
+ System.out.println("Bye. Hope to see you again soon!");
+ }
+
+ /**
+ * Displays a message to the user when a task is found.
+ *
+ * @param task The task that was found.
+ */
+ public void showFoundTasks(ArrayList foundTasks) {
+ System.out.println(lineDivider);
+ System.out.println("Here are the matching tasks in your list:");
+ for (int i = 0; i < foundTasks.size(); i++) {
+ System.out.println((i + 1) + "." + foundTasks.get(i));
+ }
+ }
+}
diff --git a/text-ui-test/runtest.sh b/text-ui-test/runtest.sh
index c9ec87003..15e6ff3ae 100644
--- a/text-ui-test/runtest.sh
+++ b/text-ui-test/runtest.sh
@@ -13,7 +13,7 @@ then
fi
# compile the code into the bin folder, terminates if error occurred
-if ! javac -cp ../src/main/java -Xlint:none -d ../bin ../src/main/java/*.java
+if ! javac -cp /Users/bryanlee/Desktop/NUS/NUS Y3S1/CS2113/ip/src/main/java -Xlint:none -d ../bin /Users/bryanlee/Desktop/NUS/NUS Y3S1/CS2113/ip/src/main/java/Duke.java
then
echo "********** BUILD FAILURE **********"
exit 1
diff --git a/text-ui-test/tempCodeRunnerFile.sh b/text-ui-test/tempCodeRunnerFile.sh
new file mode 100644
index 000000000..e73c4013e
--- /dev/null
+++ b/text-ui-test/tempCodeRunnerFile.sh
@@ -0,0 +1 @@
+/Users/bryanlee/Desktop/NUS/NUS Y3S1/CS2113/ip/src/main/java/Duke.java
\ No newline at end of file