+ * This method extracts the content from the provided input string, converts it to lowercase, and searches
+ * for matching tasks within the task list. The results are then displayed to the user.
+ *
+ * @param input The input string containing the keyword for searching tasks.
+ */
+ private String findAction(String input) {
+ String keyword = parser.extractContent(input).toLowerCase();
+ String results = tasks.search(keyword);
+ return "Here are the matching tasks in your list:\n\t" + results;
+ }
+
+ /**
+ * Handles the case where the user inputs an unknown command.
+ *
+ * @throws DevonUnknownCommandException If the command is unknown.
+ */
+ private void unknownAction() throws DevonUnknownCommandException {
+ throw new DevonUnknownCommandException();
+ }
+
+ /**
+ * Adds a task to the task list and displays a confirmation message.
+ *
+ * @param task The task to add to the list.
+ */
+ private String addToList(Task task) {
+ tasks.addTask(task);
+ return "\t" + "Got it. I've added this task:\n\t\t"
+ + task + "\n\tNow you have "
+ + tasks.getNumberOfTasks()
+ + " tasks in the list.";
+ }
+
+ /**
+ * Prints the current task list to the user.
+ */
+ private String getListAsString() {
+ return tasks.getListAsString();
+ }
+
+ /**
+ * Marks a task as done and displays a confirmation message.
+ *
+ * @param taskIndex The index of the task to mark as done.
+ */
+ private String markAsDone(int taskIndex) {
+ return tasks.markAsDone(taskIndex);
+ }
+
+ /**
+ * Marks a task as undone and displays a confirmation message.
+ *
+ * @param taskIndex The index of the task to mark as undone.
+ */
+ private String markAsUndone(int taskIndex) {
+ return tasks.markAsUndone(taskIndex);
+ }
+
+ /**
+ * Deletes a task from the list and displays a confirmation message.
+ *
+ * @param taskIndex The index of the task to delete.
+ */
+ private String deleteTask(int taskIndex) {
+ String textResponse = tasks.removeTask(taskIndex);
+ return textResponse + "\n\tNow you have " + tasks.getNumberOfTasks() + " tasks in the list.";
+ }
+
+ /**
+ * Main method to start the Devon bot.
+ *
+ * @param args Command-line arguments (not used).
+ */
+ public static void main(String[] args) {
+ Devon bot = new Devon();
+ bot.start();
+ }
+}
diff --git a/src/main/java/devon/DevonException.java b/src/main/java/devon/DevonException.java
new file mode 100644
index 0000000000..68990955d5
--- /dev/null
+++ b/src/main/java/devon/DevonException.java
@@ -0,0 +1,19 @@
+package devon;
+
+
+/**
+ * Represents a base exception for the Devon application.
+ * This is an abstract class that serves as a common parent for all exceptions related to Devon.
+ */
+public abstract class DevonException extends Exception {
+
+ /**
+ * Returns a default string representation of the exception.
+ *
+ * @return A string indicating an error occurred: "OOPS!!!".
+ */
+ @Override
+ public String toString() {
+ return "OOPS!!!";
+ }
+}
diff --git a/src/main/java/devon/DevonInvalidDateTimeException.java b/src/main/java/devon/DevonInvalidDateTimeException.java
new file mode 100644
index 0000000000..aa161ab20b
--- /dev/null
+++ b/src/main/java/devon/DevonInvalidDateTimeException.java
@@ -0,0 +1,22 @@
+package devon;
+
+/**
+ * Exception thrown when there is an invalid date-time format in the Devon application.
+ * This exception is a specific type of {@link DevonException} used to indicate errors
+ * related to date-time parsing.
+ */
+public class DevonInvalidDateTimeException extends DevonException {
+
+ /**
+ * Returns a detailed string representation of the exception, including the specific
+ * error message related to invalid date-time formats.
+ *
+ * @return A string indicating the error: "OOPS!!! Invalid date-time format for '/from' or '/to'.
+ * Please use 'yyyy-MM-dd HHmm'."
+ */
+ @Override
+ public String toString() {
+ return super.toString() + " Invalid date-time format for '/from' or '/to'. Please use 'yyyy-MM-dd HHmm'.";
+ }
+}
+
diff --git a/src/main/java/devon/DevonInvalidDeadlineException.java b/src/main/java/devon/DevonInvalidDeadlineException.java
new file mode 100644
index 0000000000..24bacef80c
--- /dev/null
+++ b/src/main/java/devon/DevonInvalidDeadlineException.java
@@ -0,0 +1,20 @@
+package devon;
+
+/**
+ * Exception thrown when there is an invalid deadline format in the Devon application.
+ * This exception is a specific type of {@link DevonException} used to indicate errors
+ * related to deadline parsing.
+ */
+public class DevonInvalidDeadlineException extends DevonException {
+
+ /**
+ * Returns a detailed string representation of the exception, including the specific
+ * error message related to invalid deadline formats.
+ *
+ * @return A string indicating the error: "OOPS!!! Deadline is invalid! Usage: deadline [task] /by [deadline]".
+ */
+ @Override
+ public String toString() {
+ return super.toString() + " Deadline is invalid! Usage: deadline [task] /by [deadline]";
+ }
+}
diff --git a/src/main/java/devon/DevonInvalidEventException.java b/src/main/java/devon/DevonInvalidEventException.java
new file mode 100644
index 0000000000..0ae5244b70
--- /dev/null
+++ b/src/main/java/devon/DevonInvalidEventException.java
@@ -0,0 +1,21 @@
+package devon;
+
+/**
+ * Exception thrown when there is an invalid event format in the Devon application.
+ * This exception is a specific type of {@link DevonException} used to indicate errors
+ * related to event parsing.
+ */
+public class DevonInvalidEventException extends DevonException {
+
+ /**
+ * Returns a detailed string representation of the exception, including the specific
+ * error message related to invalid event formats.
+ *
+ * @return A string indicating the error:
+ * "OOPS!!! Event is invalid! Usage: event [task] /from [start_time] /to [end_time]".
+ */
+ @Override
+ public String toString() {
+ return super.toString() + " Event is invalid! Usage: event [task] /from [start_time] /to [end_time]";
+ }
+}
diff --git a/src/main/java/devon/DevonInvalidTaskNumberException.java b/src/main/java/devon/DevonInvalidTaskNumberException.java
new file mode 100644
index 0000000000..60583d3ac2
--- /dev/null
+++ b/src/main/java/devon/DevonInvalidTaskNumberException.java
@@ -0,0 +1,20 @@
+package devon;
+
+/**
+ * Exception thrown when a task number is invalid or not found in the Devon application.
+ * This exception is a specific type of {@link DevonException} used to indicate errors
+ * related to task number handling.
+ */
+public class DevonInvalidTaskNumberException extends DevonException {
+
+ /**
+ * Returns a detailed string representation of the exception, including the specific
+ * error message related to invalid task numbers.
+ *
+ * @return A string indicating the error: "OOPS!!! Task not found!".
+ */
+ @Override
+ public String toString() {
+ return super.toString() + " Task not found!";
+ }
+}
diff --git a/src/main/java/devon/DevonReadDatabaseException.java b/src/main/java/devon/DevonReadDatabaseException.java
new file mode 100644
index 0000000000..86798e0b2c
--- /dev/null
+++ b/src/main/java/devon/DevonReadDatabaseException.java
@@ -0,0 +1,20 @@
+package devon;
+
+/**
+ * Exception thrown when there is an error reading from the database in the Devon application.
+ * This exception is a specific type of {@link DevonException} used to indicate issues
+ * related to database reading operations.
+ */
+public class DevonReadDatabaseException extends DevonException {
+
+ /**
+ * Returns a detailed string representation of the exception, including the specific
+ * error message related to database reading failures.
+ *
+ * @return A string indicating the error: "OOPS!!! Unable to read database!".
+ */
+ @Override
+ public String toString() {
+ return super.toString() + " Unable to read database!";
+ }
+}
diff --git a/src/main/java/devon/DevonUnknownCommandException.java b/src/main/java/devon/DevonUnknownCommandException.java
new file mode 100644
index 0000000000..5abef95e71
--- /dev/null
+++ b/src/main/java/devon/DevonUnknownCommandException.java
@@ -0,0 +1,20 @@
+package devon;
+
+/**
+ * Exception thrown when an unknown or unrecognized command is encountered in the Devon application.
+ * This exception is a specific type of {@link DevonException} used to indicate errors
+ * related to invalid commands input by the user.
+ */
+public class DevonUnknownCommandException extends DevonException {
+
+ /**
+ * Returns a detailed string representation of the exception, including the specific
+ * error message related to unknown commands.
+ *
+ * @return A string indicating the error: "OOPS!!! Unknown command!".
+ */
+ @Override
+ public String toString() {
+ return super.toString() + " Unknown command!";
+ }
+}
diff --git a/src/main/java/devon/DialogBox.java b/src/main/java/devon/DialogBox.java
new file mode 100644
index 0000000000..2906f78cbb
--- /dev/null
+++ b/src/main/java/devon/DialogBox.java
@@ -0,0 +1,60 @@
+package devon;
+
+import java.io.IOException;
+import java.util.Collections;
+
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import javafx.fxml.FXML;
+import javafx.fxml.FXMLLoader;
+import javafx.geometry.Pos;
+import javafx.scene.Node;
+import javafx.scene.control.Label;
+import javafx.scene.image.Image;
+import javafx.scene.image.ImageView;
+import javafx.scene.layout.HBox;
+
+/**
+ * Represents a dialog box consisting of an ImageView to represent the speaker's face
+ * and a label containing text from the speaker.
+ */
+public class DialogBox extends HBox {
+ @FXML
+ private Label dialog;
+ @FXML
+ private ImageView displayPicture;
+
+ private DialogBox(String text, Image img) {
+ try {
+ FXMLLoader fxmlLoader = new FXMLLoader(MainWindow.class.getResource("/view/DialogBox.fxml"));
+ fxmlLoader.setController(this);
+ fxmlLoader.setRoot(this);
+ fxmlLoader.load();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ dialog.setText(text);
+ displayPicture.setImage(img);
+ }
+
+ /**
+ * Flips the dialog box such that the ImageView is on the left and text on the right.
+ */
+ private void flip() {
+ ObservableList tmp = FXCollections.observableArrayList(this.getChildren());
+ Collections.reverse(tmp);
+ getChildren().setAll(tmp);
+ setAlignment(Pos.TOP_LEFT);
+ }
+
+ public static DialogBox getUserDialog(String text, Image img) {
+ return new DialogBox(text, img);
+ }
+
+ public static DialogBox getDevonDialog(String text, Image img) {
+ var db = new DialogBox(text, img);
+ db.flip();
+ return db;
+ }
+}
diff --git a/src/main/java/devon/Event.java b/src/main/java/devon/Event.java
new file mode 100644
index 0000000000..1934cae824
--- /dev/null
+++ b/src/main/java/devon/Event.java
@@ -0,0 +1,70 @@
+package devon;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+
+/**
+ * Represents an "Event" task in the Devon application.
+ * An "Event" task has a description and a time range indicating the start and end times of the event.
+ */
+public class Event extends Task {
+ private LocalDateTime from;
+ private LocalDateTime to;
+
+ /**
+ * Constructs a new "Event" task with the specified description, start time, and end time.
+ * The task is initially marked as not done.
+ *
+ * @param description A brief description of the "Event" task.
+ * @param from The start time of the event.
+ * @param to The end time of the event.
+ */
+ public Event(String description, LocalDateTime from, LocalDateTime to) {
+ super(description);
+ this.from = from;
+ this.to = to;
+ }
+
+ /**
+ * Provides a string representation of the "Event" task in a format suitable for database storage.
+ *
+ * @return A string representing the "Event" task in a format suitable for database storage,
+ * with the format "Event|<status>|<description>|<from>|<to>",
+ * where <status> is 1 if done, 0 if not done, <from> is the start time of the event,
+ * and <to> is the end time of the event.
+ */
+ @Override
+ public String dbReadableFormat() {
+ return String.format("Event|%d|%s|%s|%s", this.isDone ? 1 : 0, this.description, this.from, this.to);
+ }
+
+ /**
+ * Converts the start date and time of the event to a formatted string.
+ *
+ * @return A string representing the start date and time of the event in the format "MMM d yyyy, h:mm a".
+ */
+ private String fromToString() {
+ return from.format(DateTimeFormatter.ofPattern("MMM d yyyy, h:mm a"));
+ }
+
+ /**
+ * Converts the end date and time of the event to a formatted string.
+ *
+ * @return A string representing the end date and time of the event in the format "MMM d yyyy, h:mm a".
+ */
+ private String toToString() {
+ return to.format(DateTimeFormatter.ofPattern("MMM d yyyy, h:mm a"));
+ }
+
+ /**
+ * Returns a string representation of the "Event" task for display purposes.
+ *
+ * @return A string representing the "Event" task, including its status icon, description, start time, and end time,
+ * with the format "[E][status] description (from: start_time to: end_time)",
+ * where [status] is "X" if done, " " if not done.
+ */
+ @Override
+ public String toString() {
+ return "[E]" + super.toString() + " (from: " + fromToString() + " to: " + toToString() + ")";
+ }
+}
diff --git a/src/main/java/devon/Launcher.java b/src/main/java/devon/Launcher.java
new file mode 100644
index 0000000000..7c4fc4e8ab
--- /dev/null
+++ b/src/main/java/devon/Launcher.java
@@ -0,0 +1,12 @@
+package devon;
+
+import javafx.application.Application;
+
+/**
+ * A launcher class to workaround classpath issues.
+ */
+public class Launcher {
+ public static void main(String[] args) {
+ Application.launch(Main.class, args);
+ }
+}
diff --git a/src/main/java/devon/Main.java b/src/main/java/devon/Main.java
new file mode 100644
index 0000000000..e5b1869fd6
--- /dev/null
+++ b/src/main/java/devon/Main.java
@@ -0,0 +1,30 @@
+package devon;
+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 Devon devon = new Devon();
+
+ @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().setDevon(devon); // inject the Devon instance
+ stage.show();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/src/main/java/devon/MainWindow.java b/src/main/java/devon/MainWindow.java
new file mode 100644
index 0000000000..a6aa2406d6
--- /dev/null
+++ b/src/main/java/devon/MainWindow.java
@@ -0,0 +1,55 @@
+package devon;
+
+import javafx.fxml.FXML;
+import javafx.scene.control.Button;
+import javafx.scene.control.ScrollPane;
+import javafx.scene.control.TextField;
+import javafx.scene.image.Image;
+import javafx.scene.layout.AnchorPane;
+import javafx.scene.layout.VBox;
+/**
+ * Controller for the main GUI.
+ */
+public class MainWindow extends AnchorPane {
+ @FXML
+ private ScrollPane scrollPane;
+ @FXML
+ private VBox dialogContainer;
+ @FXML
+ private TextField userInput;
+ @FXML
+ private Button sendButton;
+
+ private Devon devon;
+
+ private Image userImage = new Image(this.getClass().getResourceAsStream("/images/DaUser.png"));
+ private Image devonImage = new Image(this.getClass().getResourceAsStream("/images/DaDevon.png"));
+
+ @FXML
+ public void initialize() {
+ scrollPane.vvalueProperty().bind(dialogContainer.heightProperty());
+ }
+
+ /** Injects the Devon instance */
+ public void setDevon(Devon devon) {
+ this.devon = devon;
+ devon.start();
+ dialogContainer.getChildren().add(DialogBox.getDevonDialog(devon.introduction(), devonImage));
+ }
+
+ /**
+ * Creates two dialog boxes, one echoing user input and the other containing Duke's reply and then appends them to
+ * the dialog container. Clears the user input after processing.
+ */
+ @FXML
+ private void handleUserInput() {
+ String input = userInput.getText();
+ String response = devon.getResponse(input);
+ dialogContainer.getChildren().addAll(
+ DialogBox.getUserDialog(input, userImage),
+ DialogBox.getDevonDialog(response, devonImage)
+ );
+ userInput.clear();
+ }
+}
+
diff --git a/src/main/java/devon/Parser.java b/src/main/java/devon/Parser.java
new file mode 100644
index 0000000000..190cf1485a
--- /dev/null
+++ b/src/main/java/devon/Parser.java
@@ -0,0 +1,94 @@
+package devon;
+
+import java.util.Arrays;
+
+/**
+ * Parses user input to extract commands and relevant details for task management.
+ * Handles extraction of different task-related information from user input strings.
+ */
+public class Parser {
+ public Parser() { }
+
+ /**
+ * Extracts the command from a user input message.
+ *
+ * @param msg The input message containing the command.
+ * @return The command extracted from the input message.
+ */
+ protected String extractCommand(String msg) {
+ String[] parts = msg.split(" ");
+ return parts[0];
+ }
+
+ /**
+ * Extracts the content from a user input message after the command.
+ *
+ * @param input The input message containing the command and content.
+ * @return The content of the message after the command, or "Error" if no content is found.
+ */
+ protected String extractContent(String input) {
+ String[] parts = input.split(" ");
+ return parts.length > 1
+ ? String.join(" ", Arrays.copyOfRange(parts, 1, parts.length))
+ : "Error";
+ }
+
+ /**
+ * Extracts the task index from the user input.
+ *
+ * @param input The input message containing the task index.
+ * @return The task index extracted from the input.
+ * @throws NumberFormatException If the task index is not a valid integer.
+ */
+ protected int extractTaskIndex(String input) throws NumberFormatException {
+ return Integer.parseInt(extractContent(input));
+ }
+
+ /**
+ * Extracts the description for a Todo task from the user input.
+ *
+ * @param input The input message containing the Todo task description.
+ * @return The description of the Todo task.
+ */
+ protected String extractTodo(String input) {
+ return extractContent(input).trim();
+ }
+
+ /**
+ * Extracts the description and deadline from the user input for a Deadline task.
+ *
+ * @param input The input message containing the Deadline task details.
+ * @return An array containing the description and deadline for the Deadline task.
+ * @throws DevonInvalidDeadlineException If the input does not contain the "/by" keyword.
+ */
+ protected String[] extractDeadline(String input) throws DevonInvalidDeadlineException {
+ String content = extractContent(input);
+ if (!content.contains("/by")) {
+ throw new DevonInvalidDeadlineException();
+ }
+ String[] parts = content.split("/by", 2);
+ String description = parts[0].trim();
+ String by = parts[1].trim();
+ return new String[]{ description, by };
+ }
+
+ /**
+ * Extracts the description, start date and time, and end date and time from the user input for an Event task.
+ *
+ * @param input The input message containing the Event task details.
+ * @return An array containing the description, start date and time, and end date and time for the Event task.
+ * @throws DevonInvalidEventException If the input does not contain both "/from" and "/to" keywords.
+ */
+ protected String[] extractEvent(String input) throws DevonInvalidEventException {
+ String content = extractContent(input);
+ if (!(content.contains("/from") && content.contains("/to"))) {
+ throw new DevonInvalidEventException();
+ }
+ String[] partsFrom = content.split("/from", 2);
+ String[] partsTo = partsFrom[1].split("/to", 2);
+ String description = partsFrom[0].trim();
+ String from = partsTo[0].trim();
+ String to = partsTo[1].trim();
+ return new String[]{ description, from, to };
+ }
+}
diff --git a/src/main/java/devon/Storage.java b/src/main/java/devon/Storage.java
new file mode 100644
index 0000000000..0149f705e1
--- /dev/null
+++ b/src/main/java/devon/Storage.java
@@ -0,0 +1,80 @@
+package devon;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.Scanner;
+
+/**
+ * Handles loading and saving tasks to and from a file-based database.
+ * Manages the task database file operations, including creation and data formatting.
+ */
+public class Storage {
+ protected static final String DIRECTORY_PATH = "./data";
+ protected static final String DB_PATH = String.valueOf(Paths.get(Storage.DIRECTORY_PATH, "devon_tasks.txt"));
+ protected static final String DB_DELIMITER = "\\|";
+
+ protected static final DateTimeFormatter DATE_TIME_FORMATTER_FOR_EXTERNAL_INPUT =
+ DateTimeFormatter.ofPattern("yyyy-MM-dd HHmm");
+ protected static final DateTimeFormatter DATE_TIME_FORMATTER_FOR_DB =
+ DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm");
+
+ /**
+ * Constructs a Storage object.
+ */
+ public Storage() { }
+
+ /**
+ * Loads tasks from the database file.
+ * If the file does not exist, it creates a new task database file.
+ *
+ * @return An ArrayList of strings representing the tasks.
+ */
+ protected ArrayList loadTasksFromDatabase() {
+ ArrayList tasks = new ArrayList<>();
+ try {
+ Scanner fileReader = new Scanner(new File(Storage.DB_PATH));
+ while (fileReader.hasNextLine()) {
+ tasks.add(fileReader.nextLine());
+ }
+ fileReader.close();
+ } catch (FileNotFoundException e) {
+ this.createTaskDatabase();
+ }
+ return tasks;
+ }
+
+ /**
+ * Creates a new task database file and directory if they do not exist.
+ */
+ private void createTaskDatabase() {
+ new File(Storage.DIRECTORY_PATH).mkdir();
+ try {
+ new File(Storage.DB_PATH).createNewFile();
+ } catch (IOException e) {
+ System.out.println("Error: Cannot create database!");
+ }
+ }
+
+ /**
+ * Saves the tasks from a TaskList to the database file.
+ *
+ * @param tasks The TaskList object containing tasks to be saved.
+ * @throws IOException If there is an error writing to the database file.
+ */
+ protected void saveTasksToDatabase(TaskList tasks) throws IOException {
+ FileWriter filewriter = new FileWriter(Storage.DB_PATH);
+ BufferedWriter bufferedWriter = new BufferedWriter(filewriter);
+ for (int i = 0; i < tasks.getNumberOfTasks(); i++) {
+ bufferedWriter.write(tasks.getTask(i).dbReadableFormat());
+ bufferedWriter.newLine();
+ }
+ bufferedWriter.close();
+ filewriter.close();
+ }
+}
diff --git a/src/main/java/devon/Task.java b/src/main/java/devon/Task.java
new file mode 100644
index 0000000000..b057c94ccf
--- /dev/null
+++ b/src/main/java/devon/Task.java
@@ -0,0 +1,94 @@
+package devon;
+
+/**
+ * Represents an abstract task in the Devon application.
+ * A task has a description and a status indicating whether it is done or not.
+ * Subclasses must provide their specific format for database storage.
+ */
+public abstract class Task {
+ protected String description;
+ protected boolean isDone;
+
+ /**
+ * Constructs a new Task with the specified description.
+ * The task is initially marked as not done.
+ *
+ * @param description A brief description of the task.
+ */
+ public Task(String description) {
+ this.description = description;
+ this.isDone = false;
+ }
+
+ /**
+ * Returns the status icon of the task.
+ *
+ * @return A string representing the task status; "X" if the task is done, otherwise a space.
+ */
+ public String getStatusIcon() {
+ return (isDone ? "X" : " "); // mark done task with X
+ }
+
+ /**
+ * Returns the description of the task.
+ *
+ * @return The description of the task.
+ */
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * Marks the task as done and returns a confirmation message.
+ *
+ * @return A string confirming that the task has been marked as done.
+ */
+ public String markAsDone() {
+ isDone = true;
+ return "\tNice! I've marked this task as done:\n\t\t" + this;
+ }
+
+ /**
+ * Marks the task as done without returning a message.
+ */
+ public void markAsDoneSilently() {
+ isDone = true;
+ }
+
+ /**
+ * Marks the task as not done and returns a confirmation message.
+ *
+ * @return A string confirming that the task has been marked as not done.
+ */
+ public String markAsUndone() {
+ isDone = false;
+ return "\tOK, I've marked this task as not done yet:\n\t\t" + this;
+ }
+
+ /**
+ * Returns a message announcing the deletion of the task, but does not actually delete the task from the TaskList.
+ *
+ * @return A string confirming that the task has been removed.
+ */
+ public String announceDeletion() {
+ return "\tNoted. I've removed this task:\n\t\t" + this;
+ }
+
+ /**
+ * Provides a string representation of the task in a format suitable for database storage.
+ * Subclasses must implement this method to return their specific format.
+ *
+ * @return A string representing the task in a format suitable for database storage.
+ */
+ public abstract String dbReadableFormat();
+
+ /**
+ * Returns a string representation of the task for display purposes.
+ *
+ * @return A string representing the task, including its status icon and description.
+ */
+ @Override
+ public String toString() {
+ return String.format("[%s] %s", getStatusIcon(), getDescription());
+ }
+}
diff --git a/src/main/java/devon/TaskList.java b/src/main/java/devon/TaskList.java
new file mode 100644
index 0000000000..537c1d1d3e
--- /dev/null
+++ b/src/main/java/devon/TaskList.java
@@ -0,0 +1,160 @@
+package devon;
+
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+
+/**
+ * Represents a list of tasks in the Devon application.
+ * Provides methods to manage and manipulate the list of tasks, including adding, removing, and marking tasks.
+ */
+public class TaskList {
+ private final ArrayList tasks;
+
+ /**
+ * Constructs an empty task list.
+ */
+ public TaskList() {
+ this.tasks = new ArrayList<>();
+ }
+
+ /**
+ * Returns the number of tasks in the task list.
+ *
+ * @return The number of tasks in the list.
+ */
+ public int getNumberOfTasks() {
+ return tasks.size();
+ }
+
+ /**
+ * Initializes the task list with tasks loaded from a database.
+ *
+ * @param stringListOfTasks A list of strings representing tasks in a database format.
+ * @throws DevonReadDatabaseException If there is an error reading or parsing the task data from the database.
+ */
+ public void initialiseLoadTasks(ArrayList stringListOfTasks) throws DevonReadDatabaseException {
+ for (String line : stringListOfTasks) {
+ String[] fields = line.split(Storage.DB_DELIMITER);
+ Task newTask;
+
+ switch (fields[0]) {
+ case "Deadline":
+ newTask = new Deadline(
+ fields[2],
+ LocalDateTime.parse(fields[3], Storage.DATE_TIME_FORMATTER_FOR_DB)
+ );
+ break;
+ case "Event":
+ newTask = new Event(
+ fields[2],
+ LocalDateTime.parse(fields[3], Storage.DATE_TIME_FORMATTER_FOR_DB),
+ LocalDateTime.parse(fields[4], Storage.DATE_TIME_FORMATTER_FOR_DB)
+ );
+ break;
+ case "Todo":
+ newTask = new Todo(fields[2]);
+ break;
+ default:
+ throw new DevonReadDatabaseException();
+ }
+
+ boolean toBeMarkedAsDone = Integer.parseInt(fields[1]) == 1;
+ if (toBeMarkedAsDone) {
+ newTask.markAsDoneSilently();
+ }
+
+ this.addTask(newTask);
+ }
+ }
+
+ /**
+ * Adds a task to the task list.
+ *
+ * @param task The task to be added.
+ */
+ protected void addTask(Task task) {
+ this.tasks.add(task);
+ }
+
+ /**
+ * Retrieves a task from the task list based on the task number.
+ *
+ * @param taskNumber The index of the task to retrieve (0-based index).
+ * @return The task at the specified index.
+ */
+ protected Task getTask(int taskNumber) {
+ return this.tasks.get(taskNumber);
+ }
+
+ /**
+ * Removes a task from the task list based on the task number.
+ *
+ * @param taskNumber The index of the task to be removed (0-based index).
+ * @return A message indicating the task that was removed.
+ */
+ protected String removeTask(int taskNumber) {
+ String textResponse = getTask(taskNumber).announceDeletion();
+ this.tasks.remove(taskNumber);
+ return textResponse;
+ }
+
+ /**
+ * Marks a task as done based on the task number.
+ *
+ * @param taskNumber The index of the task to be marked as done (0-based index).
+ * @return A message indicating the task that was marked as done.
+ */
+ protected String markAsDone(int taskNumber) {
+ return getTask(taskNumber).markAsDone();
+ }
+
+ /**
+ * Marks a task as undone based on the task number.
+ *
+ * @param taskNumber The index of the task to be marked as undone (0-based index).
+ * @return A message indicating the task that was marked as not done.
+ */
+ protected String markAsUndone(int taskNumber) {
+ return getTask(taskNumber).markAsUndone();
+ }
+
+ /**
+ * Returns a string representation of all tasks in the list.
+ *
+ * @return A string listing all tasks with their indices and details.
+ */
+ protected String getListAsString() {
+ StringBuilder s = new StringBuilder("\t" + "Here are the tasks in your list:");
+
+ for (int i = 0; i < this.getNumberOfTasks(); i++) {
+ Task current = this.getTask(i);
+ String formattedEntry = String.format("\n\t" + "%d. %s", i + 1, current);
+ s.append(formattedEntry);
+ }
+
+ return s.toString();
+ }
+
+ /**
+ * Searches for tasks that contain a specific keyword and returns the matching tasks as a formatted string.
+ *
+ * This method iterates through the list of tasks, checks if each task's description contains the specified
+ * keyword (case-insensitive), and collects the matching tasks. The tasks are then formatted and returned as
+ * a string with each task on a new line, prefixed by its position in the list.
+ *
+ * @param query The keyword to search for within the task descriptions.
+ * @return A formatted string of matching tasks, each task on a new line prefixed by its position in the list.
+ */
+ protected String search(String query) {
+ ArrayList matchingTasks = new ArrayList<>();
+
+ for (int i = 0; i < getNumberOfTasks(); i++) {
+ Task task = getTask(i);
+ if (task.getDescription().toLowerCase().contains(query)) {
+ matchingTasks.add((i + 1) + ". " + task);
+ }
+ }
+
+ return String.join("\n\t", matchingTasks);
+ }
+}
diff --git a/src/main/java/devon/Todo.java b/src/main/java/devon/Todo.java
new file mode 100644
index 0000000000..26c0fa78b9
--- /dev/null
+++ b/src/main/java/devon/Todo.java
@@ -0,0 +1,41 @@
+package devon;
+
+/**
+ * Represents a "To-Do" task in the Devon application.
+ * A "To-Do" task has a description and a status indicating whether it is done or not.
+ */
+public class Todo extends Task {
+
+ /**
+ * Constructs a new "To-Do" task with the specified description.
+ * The task is initially marked as not done.
+ *
+ * @param description A brief description of the "To-Do" task.
+ */
+ public Todo(String description) {
+ super(description);
+ }
+
+ /**
+ * Provides a string representation of the "To-Do" task in a format suitable for database storage.
+ *
+ * @return A string representing the "To-Do" task in a format suitable for database storage,
+ * with the format "Todo|<status>|<description>",
+ * where <status> is 1 if done, 0 if not done.
+ */
+ @Override
+ public String dbReadableFormat() {
+ return String.format("Todo|%d|%s", this.isDone ? 1 : 0, this.description);
+ }
+
+ /**
+ * Returns a string representation of the "To-Do" task for display purposes.
+ *
+ * @return A string representing the "To-Do" task, including its status icon and description,
+ * with the format "[T][status] description", where [status] is "X" if done, " " if not done.
+ */
+ @Override
+ public String toString() {
+ return "[T]" + super.toString();
+ }
+}
diff --git a/src/main/java/devon/Ui.java b/src/main/java/devon/Ui.java
new file mode 100644
index 0000000000..1f5e1ac746
--- /dev/null
+++ b/src/main/java/devon/Ui.java
@@ -0,0 +1,41 @@
+package devon;
+
+/**
+ * Handles user interface interactions, specifically for displaying information and exceptions to the user.
+ */
+public class Ui {
+ /**
+ * Constructs a Ui object.
+ */
+ public Ui() { }
+
+ /**
+ * Prints a long line of underscores to separate sections of output.
+ */
+ private void printLongLine() {
+ String lineSeparator = "____________________";
+ System.out.println("\t" + lineSeparator);
+ }
+
+ /**
+ * Displays an exception message to the user.
+ *
+ * @param e The exception to be displayed.
+ */
+ protected void displayException(Exception e) {
+ printLongLine();
+ System.out.println("\t" + e);
+ printLongLine();
+ }
+
+ /**
+ * Displays a text message to the user.
+ *
+ * @param text The text message to be displayed.
+ */
+ protected void displayText(String text) {
+ printLongLine();
+ System.out.println(text);
+ printLongLine();
+ }
+}
diff --git a/src/main/resources/images/DaDevon.png b/src/main/resources/images/DaDevon.png
new file mode 100644
index 0000000000..c5bd6c15bc
Binary files /dev/null and b/src/main/resources/images/DaDevon.png differ
diff --git a/src/main/resources/images/DaUser.png b/src/main/resources/images/DaUser.png
new file mode 100644
index 0000000000..5fa00c965d
Binary files /dev/null and b/src/main/resources/images/DaUser.png differ
diff --git a/src/main/resources/view/DialogBox.fxml b/src/main/resources/view/DialogBox.fxml
new file mode 100644
index 0000000000..fe672c90ea
--- /dev/null
+++ b/src/main/resources/view/DialogBox.fxml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml
new file mode 100644
index 0000000000..001ee36210
--- /dev/null
+++ b/src/main/resources/view/MainWindow.fxml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/test/java/devon/DeadlineTest.java b/src/test/java/devon/DeadlineTest.java
new file mode 100644
index 0000000000..e64ec9ebd8
--- /dev/null
+++ b/src/test/java/devon/DeadlineTest.java
@@ -0,0 +1,48 @@
+package devon;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+
+import org.junit.jupiter.api.Test;
+
+public class DeadlineTest {
+ @Test
+ public void dbReadableFormat_undone_success() {
+ Deadline deadline = new Deadline(
+ "desc",
+ LocalDateTime.parse("2024-01-01 1234", DateTimeFormatter.ofPattern("yyyy-MM-dd HHmm"))
+ );
+ assertEquals("Deadline|0|desc|2024-01-01T12:34", deadline.dbReadableFormat());
+ }
+
+ @Test
+ public void toString_undone_success() {
+ Deadline deadline = new Deadline(
+ "desc",
+ LocalDateTime.parse("2024-01-01 1234", DateTimeFormatter.ofPattern("yyyy-MM-dd HHmm"))
+ );
+ assertEquals("[D][ ] desc (by: Jan 1 2024, 12:34 pm)", deadline.toString());
+ }
+
+ @Test
+ public void dbReadableFormat_done_success() {
+ Deadline deadline = new Deadline(
+ "desc",
+ LocalDateTime.parse("2024-01-01 1234", DateTimeFormatter.ofPattern("yyyy-MM-dd HHmm"))
+ );
+ deadline.markAsDone();
+ assertEquals("Deadline|1|desc|2024-01-01T12:34", deadline.dbReadableFormat());
+ }
+
+ @Test
+ public void toString_done_success() {
+ Deadline deadline = new Deadline(
+ "desc",
+ LocalDateTime.parse("2024-01-01 1234", DateTimeFormatter.ofPattern("yyyy-MM-dd HHmm"))
+ );
+ deadline.markAsDone();
+ assertEquals("[D][X] desc (by: Jan 1 2024, 12:34 pm)", deadline.toString());
+ }
+}
diff --git a/src/test/java/devon/TodoTest.java b/src/test/java/devon/TodoTest.java
new file mode 100644
index 0000000000..ec685a6846
--- /dev/null
+++ b/src/test/java/devon/TodoTest.java
@@ -0,0 +1,33 @@
+package devon;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+public class TodoTest {
+ @Test
+ public void dbReadableFormat_undone_success() {
+ Todo todo = new Todo("desc");
+ assertEquals("Todo|0|desc", todo.dbReadableFormat());
+ }
+
+ @Test
+ public void toString_undone_success() {
+ Todo todo = new Todo("desc");
+ assertEquals("[T][ ] desc", todo.toString());
+ }
+
+ @Test
+ public void dbReadableFormat_done_success() {
+ Todo todo = new Todo("desc");
+ todo.markAsDone();
+ assertEquals("Todo|1|desc", todo.dbReadableFormat());
+ }
+
+ @Test
+ public void toString_done_success() {
+ Todo todo = new Todo("desc");
+ todo.markAsDone();
+ assertEquals("[T][X] desc", todo.toString());
+ }
+}
diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT
index 657e74f6e7..6a30b6a609 100644
--- a/text-ui-test/EXPECTED.TXT
+++ b/text-ui-test/EXPECTED.TXT
@@ -1,7 +1,40 @@
-Hello from
- ____ _
-| _ \ _ _| | _____
-| | | | | | | |/ / _ \
-| |_| | |_| | < __/
-|____/ \__,_|_|\_\___|
-
+ ____________________
+ Hello! I'm Devon.
+ What can I do for you?
+ ____________________
+ ____________________
+ Got it. I've added this task:
+ [T][ ] email Dean
+ Now you have 1 tasks in the list.
+ ____________________
+ ____________________
+ Got it. I've added this task:
+ [D][ ] submit CS2103T quiz (by: Thursday 8pm)
+ Now you have 2 tasks in the list.
+ ____________________
+ ____________________
+ Got it. I've added this task:
+ [E][ ] NUS Hackathon (from: Sat 8am to: 4pm)
+ Now you have 3 tasks in the list.
+ ____________________
+ ____________________
+ Nice! I've marked this task as done:
+ [T][X] email Dean
+ ____________________
+ ____________________
+ Nice! I've marked this task as done:
+ [E][X] NUS Hackathon (from: Sat 8am to: 4pm)
+ ____________________
+ ____________________
+ OK, I've marked this task as not done yet:
+ [T][ ] email Dean
+ ____________________
+ ____________________
+ Here are the tasks in your list:
+ 1. [T][ ] email Dean
+ 2. [D][ ] submit CS2103T quiz (by: Thursday 8pm)
+ 3. [E][X] NUS Hackathon (from: Sat 8am to: 4pm)
+ ____________________
+ ____________________
+ Bye. Hope to see you again soon!
+ ____________________
diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt
index e69de29bb2..eb05e9839e 100644
--- a/text-ui-test/input.txt
+++ b/text-ui-test/input.txt
@@ -0,0 +1,8 @@
+todo email Dean
+deadline submit CS2103T quiz /by Thursday 8pm
+event NUS Hackathon /from Sat 8am /to 4pm
+mark 1
+mark 3
+unmark 1
+list
+bye
\ No newline at end of file
diff --git a/text-ui-test/runtest.bat b/text-ui-test/runtest.bat
index 0873744649..68d68f1997 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 Devon < 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
index c9ec870033..ad51440d22
--- a/text-ui-test/runtest.sh
+++ b/text-ui-test/runtest.sh
@@ -20,14 +20,10 @@ then
fi
# 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
-
-# convert to UNIX format
-cp EXPECTED.TXT EXPECTED-UNIX.TXT
-dos2unix ACTUAL.TXT EXPECTED-UNIX.TXT
+java -classpath ../bin Devon < input.txt > ACTUAL.TXT
# compare the output to the expected output
-diff ACTUAL.TXT EXPECTED-UNIX.TXT
+diff ACTUAL.TXT EXPECTED.TXT
if [ $? -eq 0 ]
then
echo "Test result: PASSED"
@@ -35,4 +31,4 @@ then
else
echo "Test result: FAILED"
exit 1
-fi
\ No newline at end of file
+fi