+ * The Deadline class is a subclass of the Task class and has 2 additional attributes, by and dateby.
+ * By represents the time to complete the task by.
+ * dateBy is by in a Java LocalDate if the formatting of by is correct.
+ *
+ */
+public class Deadline extends Task {
+ protected String by;
+ protected LocalDate dateBy;
+
+ /**
+ * Constructs a new Deadline object.
+ *
+ * @param description description of the deadline task.
+ * @param by when to complete the task by.
+ */
+ public Deadline(String description, String by, String priority) {
+ super(description);
+ this.by = by;
+ this.priority = mapPriority(priority);
+ stringToDate();
+ }
+
+ /**
+ * Checks and converts the by string to a valid Java LocalDate if the format is correct
+ */
+ @Override
+ public void stringToDate() {
+ Pattern datePattern = Pattern.compile(
+ "^((2000|2400|2800|(19|2[0-9])(0[48]|[2468][048]|[13579][26]))-02-29)$"
+ + "|^(((19|2[0-9])[0-9]{2})-02-(0[1-9]|1[0-9]|2[0-8]))$"
+ + "|^(((19|2[0-9])[0-9]{2})-(0[13578]|10|12)-(0[1-9]|[12][0-9]|3[01]))$"
+ + "|^(((19|2[0-9])[0-9]{2})-(0[469]|11)-(0[1-9]|[12][0-9]|30))$");
+ Matcher dateTimeMatcher = datePattern.matcher(this.by);
+
+ if (!dateTimeMatcher.matches()) { // if datetime doesn't match, do nothing
+ this.dateBy = null;
+ } else {
+ Pattern dateTimePattern2 = Pattern.compile(
+ "(\\d{4})-(0[1-9]|[12][0-9]|3[01])-(0[1-9]|1[0-2])"); // YYYY-MM-DD
+ Matcher dateTimePattern2Matcher = dateTimePattern2.matcher(this.by);
+ dateTimePattern2Matcher.matches();
+ int day = Integer.parseInt(dateTimePattern2Matcher.group(3));
+ int month = Integer.parseInt(dateTimePattern2Matcher.group(2));
+ int year = Integer.parseInt(dateTimePattern2Matcher.group(1));
+ this.dateBy = LocalDate.of(year, month, day);
+ }
+ }
+
+ /**
+ * Converts the task to a user-friendly string representation
+ *
+ * @return A string representation of the task
+ */
+ @Override
+ public String toString() {
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMM d yyyy");
+ if (dateBy == null) {
+ return "[D]" + super.toString() + " (by: " + by + ")";
+ } else {
+ String formattedDate = this.dateBy.format(formatter);
+ return "[D]" + super.toString() + " (by: " + formattedDate + ")";
+ }
+ }
+}
diff --git a/src/main/java/Jarvis/Event.java b/src/main/java/Jarvis/Event.java
new file mode 100644
index 0000000000..4d14fa89bc
--- /dev/null
+++ b/src/main/java/Jarvis/Event.java
@@ -0,0 +1,37 @@
+package Jarvis;
+
+/**
+ * Represents an event task with a start time and an end time.
+ *
+ * The Event class is a subclass of the Task class and has 2 additional attributes,
+ * from and to, the represent the start time and end time of the event.
+ *
+ */
+public class Event extends Task {
+ protected String from;
+ protected String to;
+
+ /**
+ * Constructs a new Event object.
+ *
+ * @param description The description of the event.
+ * @param from The starting time of the event.
+ * @param to The ending time of the event.
+ */
+ public Event(String description, String from, String to, String priority) {
+ super(description);
+ this.from = from;
+ this.to = to;
+ this.priority = mapPriority(priority);
+ }
+
+ /**
+ * Converts the task to a user-friendly string representation.
+ *
+ * @return A string representation of the task.
+ */
+ @Override
+ public String toString() {
+ return "[E]" + super.toString() + " (from: " + from + " to: " + to + ")";
+ }
+}
diff --git a/src/main/java/Jarvis/IncorrectJarvisCommandException.java b/src/main/java/Jarvis/IncorrectJarvisCommandException.java
new file mode 100644
index 0000000000..983815f722
--- /dev/null
+++ b/src/main/java/Jarvis/IncorrectJarvisCommandException.java
@@ -0,0 +1,7 @@
+package Jarvis;
+
+public class IncorrectJarvisCommandException extends JarvisException {
+ public IncorrectJarvisCommandException(String errorMsg) {
+ super(errorMsg);
+ }
+}
diff --git a/src/main/java/Jarvis/InvalidTaskNumberException.java b/src/main/java/Jarvis/InvalidTaskNumberException.java
new file mode 100644
index 0000000000..de294146a4
--- /dev/null
+++ b/src/main/java/Jarvis/InvalidTaskNumberException.java
@@ -0,0 +1,7 @@
+package Jarvis;
+
+public class InvalidTaskNumberException extends JarvisException {
+ public InvalidTaskNumberException(String errorMsg) {
+ super(errorMsg);
+ }
+}
diff --git a/src/main/java/Jarvis/Jarvis.java b/src/main/java/Jarvis/Jarvis.java
new file mode 100644
index 0000000000..53e0ad7f0b
--- /dev/null
+++ b/src/main/java/Jarvis/Jarvis.java
@@ -0,0 +1,47 @@
+package Jarvis;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Scanner;
+
+/**
+ * Main application class for the Jarvis chatbot.
+ *
+ * This class is responsible initialising the task list, loading and saving tasks in storage and
+ * running the main event loop of the application. It delegates command parsing to the Parser class and storage
+ * operations to the Storage class.
+ *
+ */
+public class Jarvis {
+ private static TaskList tasks;
+ private static Storage storage;
+ private static Parser parser;
+
+ /**
+ * Constructs a new Jarvis chatbot.
+ *
+ * @param filePath Path of the file to store the task data in.
+ */
+ public Jarvis(String filePath) {
+
+ assert !filePath.isEmpty() : "filePath cannot be empty";
+
+ storage = new Storage(filePath);
+ tasks = new TaskList(storage.load());
+ parser = new Parser();
+ }
+
+ public String getResponse(String input) {
+ try {
+ return parser.parseCommand(storage, tasks, input);
+ } catch (IncorrectJarvisCommandException e) {
+ return Ui.getListOfCommands(parser.validCommands, e);
+ } catch (InvalidTaskNumberException e) {
+ return Ui.respond(e.getMessage() + "\n" +
+ " There are currently " + tasks.countTask() + " tasks in the list.");
+ } catch (WrongJarvisCommandFormatException e) {
+ return Ui.respond("");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/Jarvis/JarvisException.java b/src/main/java/Jarvis/JarvisException.java
new file mode 100644
index 0000000000..5c9eb1c565
--- /dev/null
+++ b/src/main/java/Jarvis/JarvisException.java
@@ -0,0 +1,7 @@
+package Jarvis;
+
+public class JarvisException extends Exception {
+ public JarvisException(String errorMsg) {
+ super(errorMsg);
+ }
+}
diff --git a/src/main/java/Jarvis/Parser.java b/src/main/java/Jarvis/Parser.java
new file mode 100644
index 0000000000..8513aecfe5
--- /dev/null
+++ b/src/main/java/Jarvis/Parser.java
@@ -0,0 +1,245 @@
+package Jarvis;
+
+import java.util.ArrayList;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Responsible for parsing user input, executing commands and handling any exceptions
+ *
+ * This class takes care of interpreting user commands and then calling the appropriate methods to perform
+ * the actions described by those commands. It also handles any exceptions and calls the appropriate methods
+ * to print the details of the exception for the user to read.
+ *
+ */
+public class Parser {
+ protected ArrayList validCommands; // list of valid commands
+
+ /**
+ * Constructs a new Parser object.
+ */
+ public Parser() {
+ validCommands = new ArrayList<>();
+ addValidCommands();
+ }
+
+ /**
+ * Adds the list of valid commands into the validCommands ArrayList.
+ */
+ private void addValidCommands() {
+ validCommands.add("list");
+ validCommands.add("bye");
+ validCommands.add("mark");
+ validCommands.add("uncheck");
+ validCommands.add("todo");
+ validCommands.add("deadline");
+ validCommands.add("event");
+ validCommands.add("find");
+ }
+
+ /**
+ * Parses the priority string provided by the user to a string understandable by the program
+ * @param priority contains priority of the task, either low, medium or high
+ * @return L, M or H representing low, medium or high respectively
+ */
+ private String parsePriority(String priority) {
+ if (priority.equals("low")) {
+ return "L";
+ } else if (priority.equals("medium")) {
+ return "M";
+ } else {
+ return "H";
+ }
+ }
+
+ /**
+ * checks if the user inputted a valid command in the validCommand ArrayList.
+ *
+ * @param inputCommand commands that the user inputs.
+ * @return the valid command that the user inputted, an empty string otherwise.
+ */
+ // checks if command is valid and throws Jarvis.IncorrectJarvisCommandException
+ private String isValidCommand(String inputCommand) {
+
+ assert !inputCommand.isEmpty() : "inputCommand parameter in isValidCommand() is an empty string";
+
+ // check if command is one of the valid keywords
+ boolean isValidCommand = false;
+ String validInputCommand = "";
+
+ for (String validCommand: validCommands) {
+ if (inputCommand.contains(validCommand)) {
+ isValidCommand = true;
+ validInputCommand = validCommand;
+ break;
+ }
+ }
+
+ if (!isValidCommand) {
+ try {
+ throw new IncorrectJarvisCommandException(
+ "Apologies Sir, the command " + inputCommand + " is not a valid command.");
+ } catch (IncorrectJarvisCommandException e) {
+
+ }
+ }
+ return validInputCommand;
+ }
+
+ /**
+ * checks if the command the user inputted has a wrong format, if the format of the command is wrong,
+ * print the details to the screen for the user to know how to correct it. This method assumes that
+ * isValidCommand has been run before this and the user has used a valid command. It just checks if
+ * the formatting of the command is correct.
+ *
+ * @param inputCommand the command the user inputted
+ * @param validInputCommand the valid command which the user inputted
+ */
+ // identifies which command has wrong formatting and prints feedback to user
+ public String isWrongFormat(String inputCommand, String validInputCommand) {
+
+ assert !inputCommand.isEmpty() : "inputCommand parameter in isWrongFormat() is an empty string";
+ assert !validInputCommand.isEmpty() : "validInputCommand parameter in isWrongFormat() is an empty string";
+
+ // since command is valid, check if formatting of the command is correct
+ boolean markMatch = inputCommand.matches("mark \\d+");
+ boolean uncheckMatch = inputCommand.matches("unmark \\d+");
+ boolean todoMatch = inputCommand.matches("todo .+ (low|medium|high)");
+ boolean deadlineMatch = inputCommand.matches("deadline .+ /.+ (low|medium|high)");
+ boolean eventMatch = inputCommand.matches("event .+ /.+ /.+ (low|medium|high)");
+
+ if (validInputCommand.equals("mark") && !markMatch) { // if mark command but wrong format
+ try {
+ throw new WrongJarvisCommandFormatException(
+ "Apologies Sir, the format of the mark command you provided is incorrect.");
+ } catch (WrongJarvisCommandFormatException e) {
+ return Ui.getWrongFormatMessage("mark", e);
+ }
+ } else if (validInputCommand.equals("uncheck") && !uncheckMatch) { // if uncheck command but wrong format
+ try {
+ throw new WrongJarvisCommandFormatException(
+ "Apologies Sir, the format of the uncheck command you provided is incorrect.");
+ } catch (WrongJarvisCommandFormatException e) {
+ return Ui.getWrongFormatMessage("uncheck", e);
+ }
+ } else if (validInputCommand.equals("todo") && !todoMatch) { // if todo command but wrong format
+ try {
+ throw new WrongJarvisCommandFormatException(
+ "Apologies Sir, the format of the todo command you provided is incorrect.");
+ } catch (WrongJarvisCommandFormatException e) {
+ return Ui.getWrongFormatMessage("todo", e);
+ }
+ } else if (validInputCommand.equals("deadline") && !deadlineMatch) { // if deadline command but wrong format
+ try {
+ throw new WrongJarvisCommandFormatException(
+ "Apologies Sir, the format of the deadline command you provided is incorrect.");
+ } catch (WrongJarvisCommandFormatException e) {
+ return Ui.getWrongFormatMessage("deadline", e);
+ }
+ } else { // if event command but wrong format
+ try {
+ throw new WrongJarvisCommandFormatException(
+ "Apologies Sir, the format of the event command you provided is incorrect.");
+ } catch (WrongJarvisCommandFormatException e) {
+ return Ui.getWrongFormatMessage("event", e);
+ }
+ }
+ }
+
+ /**
+ * Parses and executes command specified in the given user input.
+ *
+ * @param storage The storage object responsible for task storage operations.
+ * @param tasks The task list that holds all tasks.
+ * @param userInput The string input from the user.
+ * @throws IncorrectJarvisCommandException If the command is incorrect.
+ * @throws InvalidTaskNumberException If the task number is out of the range of the number of tasks.
+ * @throws WrongJarvisCommandFormatException If the command is correct but its formatted incorrectly.
+ */
+ public String parseCommand(Storage storage, TaskList tasks, String userInput) throws
+ IncorrectJarvisCommandException, InvalidTaskNumberException, WrongJarvisCommandFormatException {
+
+ Pattern todoPattern = Pattern.compile("(todo) (.+) (low|medium|high)");
+ Pattern deadlinePattern = Pattern.compile("(deadline) (.+) /by (.+) (low|medium|high)");
+ Pattern eventPattern = Pattern.compile("(event) (.+) /from (.+) /to (.+) (low|medium|high)");
+ Pattern deletePattern = Pattern.compile("(delete) (\\d+)");
+ Pattern findPattern = Pattern.compile("(find) (.+)");
+
+ Matcher todoMatcher = todoPattern.matcher(userInput);
+ Matcher deadlineMatcher = deadlinePattern.matcher(userInput);
+ Matcher eventMatcher = eventPattern.matcher(userInput);
+ Matcher deleteMatcher = deletePattern.matcher(userInput);
+ Matcher findMatcher = findPattern.matcher(userInput);
+
+ String nameOfValidCommand = isValidCommand(userInput);
+
+ if (userInput.matches("list")) { // if "list" is entered
+ return Ui.getTaskList(tasks);
+
+ } else if (userInput.matches("bye")) { // if "bye" is entered
+ storage.deleteContents();
+ storage.save(tasks); // save task list to data file after bye is entered
+ return Ui.getByeMessage();
+
+ } else if (userInput.matches("mark \\d+")
+ || userInput.matches("uncheck \\d+")) { // if "mark" or "uncheck" is entered
+
+ int taskNum = Integer.parseInt(userInput.substring(userInput.length() - 1));
+ if (!tasks.isValidTaskNumber(taskNum)) {
+ return null;
+ }
+
+ Task currentTask = tasks.getTask(taskNum - 1);
+ if (userInput.matches("uncheck \\d+")) { // if "uncheck" is entered
+ currentTask.setDone(false);
+ return Ui.getUncheckMessage(currentTask);
+
+ } else { // if "mark" is entered
+ currentTask.setDone(true);
+ return Ui.getMarkMessage(currentTask);
+ }
+
+ } else if (todoMatcher.matches()
+ || deadlineMatcher.matches()
+ || eventMatcher.matches()) { // if task command is entered
+ Task newTask;
+ if (todoMatcher.matches()) { // if "todo" is entered
+ String taskDescription = todoMatcher.group(2);
+ String priority = todoMatcher.group(3);
+ newTask = new ToDo(taskDescription, parsePriority(priority));
+
+ } else if (deadlineMatcher.matches()) { // if "deadline" is entered
+ String taskDescription = deadlineMatcher.group(2);
+ String by = deadlineMatcher.group(3);
+ String priority = deadlineMatcher.group(4);
+ newTask = new Deadline(taskDescription, by, parsePriority(priority));
+
+ } else { // if "event" is entered
+ String taskDescription = eventMatcher.group(2);
+ String from = eventMatcher.group(3);
+ String to = eventMatcher.group(4);
+ String priority = eventMatcher.group(5);
+ newTask = new Event(taskDescription, from, to, parsePriority(priority));
+
+ }
+ tasks.appendTask(newTask);
+ return Ui.getTaskMessage(newTask, tasks);
+
+ } else if (deleteMatcher.matches()) { // if delete is entered
+ int taskNum = Integer.parseInt(deleteMatcher.group(2));
+ if (tasks.isValidTaskNumber(taskNum)) {
+ tasks.deleteTask(taskNum - 1);
+ Task deletedTask = tasks.getTask(taskNum);
+ return Ui.getDeleteMessage(deletedTask ,tasks);
+ }
+
+ } else if (findMatcher.matches()) { // if find is entered
+ String keyword = findMatcher.group(2);
+ return Ui.getFoundTaskMessage(tasks.findTask(keyword));
+
+ } else { // if none of the above commands go through
+ return isWrongFormat(userInput, nameOfValidCommand);
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/Jarvis/Storage.java b/src/main/java/Jarvis/Storage.java
new file mode 100644
index 0000000000..4d70cac851
--- /dev/null
+++ b/src/main/java/Jarvis/Storage.java
@@ -0,0 +1,63 @@
+package Jarvis;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+
+public class Storage {
+ private String filePath;
+
+ public Storage(String filePath) {
+ this.filePath = filePath;
+ }
+
+ // load data from dataFile
+ public String load() {
+ File file = new File(this.filePath);
+ String fullData = "";
+ try {
+ if (file.exists()) {
+ BufferedReader reader = new BufferedReader(new FileReader(file));
+ String line;
+
+ while ((line = reader.readLine()) != null) { // append each line and a \n to the fullData variable
+ if (fullData.equals("")) {
+ fullData = line;
+ } else {
+ fullData = fullData + "\n" + line;
+ }
+ }
+ reader.close();
+ } else {
+ boolean fileCreated = file.createNewFile();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return fullData;
+ }
+
+ // delete contents in dataFile
+ public void deleteContents() {
+ try (FileWriter writer = new FileWriter(this.filePath)) {
+ // Write an empty string to the file
+ writer.write("");
+ } catch (IOException e) {
+ System.err.println("An error occurred: " + e.getMessage());
+ }
+ }
+
+ // write tasks in taskList to dateFile
+ public void save(TaskList tasks) {
+ for (int i = 0; i < tasks.countTask(); i++) { // writing the string of each task to the data file
+ Task currentTask = tasks.getTask(i);
+ try (FileWriter dataFileWriter = new FileWriter(this.filePath, true)) {
+ dataFileWriter.write(currentTask.toString() + "\n");
+ } catch (IOException e) {
+ System.err.println("An error occurred: " + e.getMessage());
+ }
+ }
+ }
+}
diff --git a/src/main/java/Jarvis/Task.java b/src/main/java/Jarvis/Task.java
new file mode 100644
index 0000000000..49f91431c4
--- /dev/null
+++ b/src/main/java/Jarvis/Task.java
@@ -0,0 +1,87 @@
+package Jarvis;
+
+/**
+ * Abstract class representing a task.
+ *
+ * This class serves as the parent for different types of tasks to inherit from and encapsulates
+ * the basic information and operations related to a task.
+ *
+ */
+public class Task {
+ protected String description;
+ protected boolean isDone;
+ protected priorityLevels priority;
+
+ protected enum priorityLevels {
+ LOW,
+ MEDIUM,
+ HIGH
+ }
+
+ /**
+ * Constructs a new Task with the given description
+ *
+ * @param description The description of the task
+ */
+ public Task(String description) {
+ this.description = description;
+ this.isDone = false;
+ }
+
+ /**
+ * Maps the priority given to its respective enum value
+ * @param priority String representing the priority, either L, M or H
+ * @return the corresponding enum value
+ */
+ public priorityLevels mapPriority(String priority) {
+ if (priority.equals("L")) {
+ return priorityLevels.LOW;
+ } else if (priority.equals("M")) {
+ return priorityLevels.MEDIUM;
+ } else {
+ return priorityLevels.HIGH;
+ }
+ }
+
+ public void setPriority(String priority) {
+ this.priority = mapPriority(priority);
+ }
+
+ /**
+ * Sets if the task is done or not
+ *
+ * @param done true if task is done, false otherwise
+ */
+ public void setDone(boolean done) {
+ this.isDone = done;
+ }
+
+ /**
+ * Abstract method for string to date conversion
+ */
+ public void stringToDate() {}
+
+ /**
+ * Converts the task to a user-friendly string representation
+ *
+ * @return A string representation of the task
+ */
+ public String toString() { // generates the string of marking and task
+ String marking = "";
+ if (this.isDone) {
+ marking = "[X]";
+ } else {
+ marking = "[ ]";
+ }
+
+ String priority = "";
+ if (this.priority == priorityLevels.LOW) {
+ priority = "[L]";
+ } else if (this.priority == priorityLevels.MEDIUM) {
+ priority = "[M]";
+ } else {
+ priority = "[H]";
+ }
+ return marking + priority + " " + this.description;
+ }
+}
diff --git a/src/main/java/Jarvis/TaskList.java b/src/main/java/Jarvis/TaskList.java
new file mode 100644
index 0000000000..4e4864bb3d
--- /dev/null
+++ b/src/main/java/Jarvis/TaskList.java
@@ -0,0 +1,121 @@
+package Jarvis;
+
+import java.util.ArrayList;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Class representing a list of tasks.
+ *
+ * This class encapsulates the list of tasks and provides methods for adding, removing, and retrieving tasks.
+ *
+ */
+public class TaskList {
+ private ArrayList taskList;
+
+ /**
+ * Constructs a new TaskList object from saved data.
+ *
+ * @param tasks The string that holds the saved data.
+ */
+ public TaskList(String tasks) { // tasks is a string
+
+ if (tasks.isEmpty()) {
+ taskList = new ArrayList<>();
+ } else {
+ String[] stringTasks = tasks.split("\n");
+ Pattern todoPattern = Pattern.compile(
+ "\\[T\\]\\[(X|\\s)\\]\\[(L|M|H)\\] (.+)"); // [T][-][L/M/H] xxxx
+ Pattern deadlinePattern = Pattern.compile(
+ "\\[D\\]\\[(X|\\s)\\]\\[(L|M|H)\\] (.+) \\(by: (.+)\\)"); // [D][-][L/M/H] xxxx (by: xxxxxx)
+ Pattern eventPattern = Pattern.compile(
+ "\\[E\\]\\[(X|\\s)\\]\\[(L|M|H)\\] (.+) \\(from: (.+) to: (.+)\\)"); // [E][-][L/M/H] xxxx (from: xxxxxx to: xxxxxx)
+ // [E][ ][H] project meeting (from: Monday 2pm to: 4pm)
+
+ taskList = new ArrayList<>();
+ for (int i = 0; i < stringTasks.length; i++) {
+ Matcher todoMatcher = todoPattern.matcher(stringTasks[i]);
+ Matcher deadlineMatcher = deadlinePattern.matcher(stringTasks[i]);
+ Matcher eventMatcher = eventPattern.matcher(stringTasks[i]);
+
+ Task newTask;
+ if (todoMatcher.matches()) {
+ String description = todoMatcher.group(3);
+ String priority = todoMatcher.group(2);
+ newTask = new ToDo(description, priority);
+ if (todoMatcher.group(1).equals("X")) {
+ newTask.setDone(true);
+ }
+ } else if (deadlineMatcher.matches()) {
+ String description = deadlineMatcher.group(3);
+ String by = deadlineMatcher.group(4);
+ String priority = deadlineMatcher.group(2);
+ newTask = new Deadline(description, by, priority);
+ if (deadlineMatcher.group(1).equals("X")) {
+ newTask.setDone(true);
+ }
+ } else {
+ eventMatcher.matches();
+ String description = eventMatcher.group(3);
+ String from = eventMatcher.group(4);
+ String to = eventMatcher.group(5);
+ String priority = eventMatcher.group(2);
+ newTask = new Event(description, from, to, priority);
+ if (eventMatcher.group(1).equals("X")) {
+ newTask.setDone(true);
+ }
+ }
+
+ taskList.add(newTask); // add the task to the task list
+ }
+ }
+ }
+
+ public void appendTask(Task task) {
+ this.taskList.add(task);
+ }
+
+ public void addTask(Task task, int index) {
+ this.taskList.add(index, task);
+ }
+
+ public void deleteTask(int index) {
+ this.taskList.remove(index);
+ }
+
+ public Task getTask(int index) {
+ return this.taskList.get(index);
+ }
+
+ public int countTask() {
+ return taskList.size();
+ }
+
+ public boolean isEmpty() {
+ return taskList.isEmpty();
+ }
+
+ // function checks if task number is valid and throws Jarvis.InvalidTaskNumberException
+ public boolean isValidTaskNumber(int taskNum) {
+ final String line = "____________________________________________________________";
+ boolean isValid = true;
+ if (taskNum < 0 || taskNum > this.taskList.size()) { // check if task number is of valid range
+ try {
+ throw new InvalidTaskNumberException("Apologies Sir, the task number you provided is out of range.");
+ } catch (InvalidTaskNumberException e) {
+ isValid = false;
+ }
+ }
+ return isValid;
+ }
+
+ public ArrayList findTask(String keyword) {
+ ArrayList foundTasks = new ArrayList<>();
+ for (Task task: taskList) {
+ if (task.description.contains(keyword)) {
+ foundTasks.add(task);
+ }
+ }
+ return foundTasks;
+ }
+}
diff --git a/src/main/java/Jarvis/ToDo.java b/src/main/java/Jarvis/ToDo.java
new file mode 100644
index 0000000000..8ba8fc80ae
--- /dev/null
+++ b/src/main/java/Jarvis/ToDo.java
@@ -0,0 +1,30 @@
+package Jarvis;
+
+/**
+ * Represents a ToDo task that extends the Task class.
+ *
+ * The ToDo class is a subclass of the Task class
+ *
+ */
+public class ToDo extends Task {
+
+ /**
+ * Constructs a new ToDo object.
+ *
+ * @param description description of the deadline task.
+ */
+ public ToDo(String description, String priority) {
+ super(description);
+ this.priority = mapPriority(priority);
+ }
+
+ /**
+ * Converts the task to a user-friendly string representation
+ *
+ * @return A string representation of the task
+ */
+ @Override
+ public String toString() {
+ return "[T]" + super.toString();
+ }
+}
diff --git a/src/main/java/Jarvis/Ui.java b/src/main/java/Jarvis/Ui.java
new file mode 100644
index 0000000000..0f696b0125
--- /dev/null
+++ b/src/main/java/Jarvis/Ui.java
@@ -0,0 +1,112 @@
+package Jarvis;
+
+import java.util.ArrayList;
+
+public class Ui {
+ private static final String name = "Jarvis";
+ private static final String greeting = "Good day Sir! I'm ";
+ private static final String question = "How can I help you today Sir?";
+ private static final String signOff = "Good bye Sir!";
+ private static final String listInforming = "Here are the tasks in your list Sir:";
+ private static final String markInforming = "Roger that Sir, I've marked this task as done:";
+ private static final String uncheckInforming = "Noted Sir, I've marked this task as NOT done yet:";
+ private static final String taskInforming = "As you please Sir, I've added the task:";
+ private static final String deleteInforming = "Alright Sir, I've removed this task:";
+ private static final String findInforming = "Here are the matching tasks in your list Sir:";
+ private static final String listEmptyInforming = "There are currently no tasks in the list Sir.";
+
+ public static String printGreeting() {
+ return greeting + name + "!" + "\n" + question;
+ }
+
+ public static String getByeMessage() {
+ return signOff;
+ }
+
+ public static String getMarkMessage(Task currentTask) {
+ return markInforming + "\n" +
+ " " + currentTask.toString();
+ }
+
+ public static String getUncheckMessage(Task currentTask) {
+ return uncheckInforming + "\n" +
+ " " + currentTask.toString();
+ }
+
+ public static String getListOfCommands(ArrayList validCommands, Exception e) {
+ String returnString = e.getMessage() + "\n";
+ for (int i = 0; i < validCommands.size(); i++) { // listing out the current task
+ int numbering = i + 1;
+ returnString = returnString + " " + numbering + ". " + validCommands.get(i) + "\n";
+ }
+ return returnString;
+ }
+
+ public static String getWrongFormatMessage(String command, Exception e) {
+
+ assert command.matches("(?i:mark|uncheck|todo|deadline|event)"): "Command is not valid";
+
+ if (command.equals("mark")) {
+ return e.getMessage() + "\n" +
+ " The following is the correct format:" + "\n"
+ + " mark ";
+ } else if (command.equals("uncheck")) {
+ return e.getMessage() + "\n" +
+ " The following is the correct format:" + "\n"
+ + " uncheck ";
+ } else if (command.equals("todo")) {
+ return e.getMessage() + "\n" +
+ " The following is the correct format:" + "\n"
+ + " todo ";
+ } else if (command.equals("deadline")) {
+ return e.getMessage() + "\n" +
+ " The following is the correct format:" + "\n"
+ + " deadline /by ";
+ } else {
+ return e.getMessage() + "\n" +
+ " The following is the correct format:" + "\n"
+ + " event /from /to ";
+ }
+ }
+
+ public static String getTaskMessage(Task task,TaskList tasks) {
+ return taskInforming + "\n" +
+ " " + task.toString() + "\n" +
+ "You have now " + tasks.countTask() + " tasks in the list Sir.";
+ }
+
+ public static String getDeleteMessage(Task task, TaskList tasks) {
+ return deleteInforming + "\n" +
+ " " + task.toString() + "\n" +
+ "You have now " + tasks.countTask() + " tasks in the list Sir.";
+ }
+
+ public static String getTaskList(TaskList tasks) {
+ String returnString;
+ if (tasks.isEmpty()) {
+ returnString = listEmptyInforming + "\n";
+ } else {
+ returnString = listInforming + "\n";
+ for (int i = 0; i < tasks.countTask(); i++) { // listing out the current task
+ int count = i + 1;
+ Task currentTask = tasks.getTask(i);
+ returnString = returnString + count + "." + currentTask.toString() + "\n";
+ }
+ }
+ return returnString;
+ }
+
+ public static String getFoundTaskMessage(ArrayList foundTasks) {
+ String returnString = findInforming + "\n";
+ for (int i = 0; i < foundTasks.size(); i++) { // listing out the current task
+ int count = i + 1;
+ Task currentTask = foundTasks.get(i);
+ returnString = returnString + count + "." + currentTask.toString() + "\n";
+ }
+ return returnString;
+ }
+
+ public static String respond(String message) {
+ return message;
+ }
+}
diff --git a/src/main/java/Jarvis/WrongJarvisCommandFormatException.java b/src/main/java/Jarvis/WrongJarvisCommandFormatException.java
new file mode 100644
index 0000000000..0674d24944
--- /dev/null
+++ b/src/main/java/Jarvis/WrongJarvisCommandFormatException.java
@@ -0,0 +1,7 @@
+package Jarvis;
+
+public class WrongJarvisCommandFormatException extends JarvisException {
+ public WrongJarvisCommandFormatException(String errorMsg) {
+ super(errorMsg);
+ }
+}
diff --git a/src/main/java/Launcher.java b/src/main/java/Launcher.java
new file mode 100644
index 0000000000..43d64c26fb
--- /dev/null
+++ b/src/main/java/Launcher.java
@@ -0,0 +1,10 @@
+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/Main.java b/src/main/java/Main.java
new file mode 100644
index 0000000000..6859714542
--- /dev/null
+++ b/src/main/java/Main.java
@@ -0,0 +1,27 @@
+import java.io.IOException;
+
+import Jarvis.Jarvis;
+import javafx.application.Application;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Scene;
+import javafx.scene.layout.AnchorPane;
+import javafx.stage.Stage;
+
+
+public class Main extends Application {
+ private Jarvis jarvis = new Jarvis("jarvis.txt");
+
+ @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().setJarvis(jarvis);
+ stage.show();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/src/main/java/MainWindow.java b/src/main/java/MainWindow.java
new file mode 100644
index 0000000000..acf5f1592f
--- /dev/null
+++ b/src/main/java/MainWindow.java
@@ -0,0 +1,50 @@
+import Jarvis.Jarvis;
+import javafx.fxml.FXML;
+import javafx.scene.control.Button;
+import javafx.scene.control.ScrollPane;
+import javafx.scene.control.TextField;
+import javafx.scene.image.Image;
+import javafx.scene.layout.AnchorPane;
+import javafx.scene.layout.VBox;
+/**
+ * Controller for MainWindow. Provides the layout for the other controls.
+ */
+public class MainWindow extends AnchorPane {
+ @FXML
+ private ScrollPane scrollPane;
+ @FXML
+ private VBox dialogContainer;
+ @FXML
+ private TextField userInput;
+ @FXML
+ private Button sendButton;
+
+ private Jarvis jarvis;
+
+ private Image userImage = new Image(this.getClass().getResourceAsStream("/images/tony_stark.jpg"));
+ private Image dukeImage = new Image(this.getClass().getResourceAsStream("/images/jarvis.jpeg"));
+
+ @FXML
+ public void initialize() {
+ scrollPane.vvalueProperty().bind(dialogContainer.heightProperty());
+ }
+
+ public void setJarvis(Jarvis jarvis) {
+ this.jarvis = jarvis;
+ }
+
+ /**
+ * 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 = this.jarvis.getResponse(input);
+ dialogContainer.getChildren().addAll(
+ DialogBox.getUserDialog(input, userImage),
+ DialogBox.getDukeDialog(response, dukeImage)
+ );
+ userInput.clear();
+ }
+}
diff --git a/src/main/resources/images/jarvis.jpeg b/src/main/resources/images/jarvis.jpeg
new file mode 100644
index 0000000000..3f247c0164
Binary files /dev/null and b/src/main/resources/images/jarvis.jpeg differ
diff --git a/src/main/resources/images/tony_stark.jpg b/src/main/resources/images/tony_stark.jpg
new file mode 100644
index 0000000000..41c1d55c43
Binary files /dev/null and b/src/main/resources/images/tony_stark.jpg differ
diff --git a/src/main/resources/view/DialogBox.fxml b/src/main/resources/view/DialogBox.fxml
new file mode 100644
index 0000000000..d3e6e9db86
--- /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..0d636bce2e
--- /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/Jarvis/DeadlineTest.java b/src/test/java/Jarvis/DeadlineTest.java
new file mode 100644
index 0000000000..e20d4f315c
--- /dev/null
+++ b/src/test/java/Jarvis/DeadlineTest.java
@@ -0,0 +1,12 @@
+package Jarvis;
+
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class DeadlineTest {
+ @Test
+ public void deadline_correctInitialisation_toStringCorrect() {
+ Deadline deadline = new Deadline("make helmet", "2023-09-02", "low");
+ assertEquals("[D][ ][L] make helmet (by: Sep 2 2023)", deadline.toString());
+ }
+}
diff --git a/src/test/java/Jarvis/ToDoTest.java b/src/test/java/Jarvis/ToDoTest.java
new file mode 100644
index 0000000000..d5825d757b
--- /dev/null
+++ b/src/test/java/Jarvis/ToDoTest.java
@@ -0,0 +1,12 @@
+package Jarvis;
+
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class ToDoTest {
+ @Test
+ public void todo_correctInitialisation_toStringCorrect() {
+ ToDo todo = new ToDo("Mark XXVI is ready for you sir", "low");
+ assertEquals("[T][ ][L] Mark XXVI is ready for you sir", todo.toString());
+ }
+}
diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT
index 657e74f6e7..c780a93b93 100644
--- a/text-ui-test/EXPECTED.TXT
+++ b/text-ui-test/EXPECTED.TXT
@@ -1,7 +1,60 @@
-Hello from
- ____ _
-| _ \ _ _| | _____
-| | | | | | | |/ / _ \
-| |_| | |_| | < __/
-|____/ \__,_|_|\_\___|
-
+____________________________________________________________
+Good day Sir! I'm Jarvis!
+How can I help you today Sir?
+____________________________________________________________
+____________________________________________________________
+As you please Sir, I've added the task:
+[T][ ] borrow book
+You have now 1 tasks in the list Sir.
+____________________________________________________________
+____________________________________________________________
+As you please Sir, I've added the task:
+[D][ ] return book (by: Sunday)
+You have now 2 tasks in the list Sir.
+____________________________________________________________
+____________________________________________________________
+As you please Sir, I've added the task:
+[E][ ] project meeting (from: Mon 2pm to: 4pm)
+You have now 3 tasks in the list Sir.
+____________________________________________________________
+____________________________________________________________
+As you please Sir, I've added the task:
+[D][ ] finish assignment (by: Saturday)
+You have now 4 tasks in the list Sir.
+____________________________________________________________
+____________________________________________________________
+As you please Sir, I've added the task:
+[T][ ] start on vlog
+You have now 5 tasks in the list Sir.
+____________________________________________________________
+____________________________________________________________
+Here are the task in your list Sir:
+1.[T][ ] borrow book
+2.[D][ ] return book (by: Sunday)
+3.[E][ ] project meeting (from: Mon 2pm to: 4pm)
+4.[D][ ] finish assignment (by: Saturday)
+5.[T][ ] start on vlog
+____________________________________________________________
+____________________________________________________________
+Roger that Sir, I've marked this task as done:
+[T][X] borrow book
+____________________________________________________________
+____________________________________________________________
+Roger that Sir, I've marked this task as done:
+[E][X] project meeting (from: Mon 2pm to: 4pm)
+____________________________________________________________
+____________________________________________________________
+Noted Sir, I've marked this task as NOT done yet:
+[E][ ] project meeting (from: Mon 2pm to: 4pm)
+____________________________________________________________
+____________________________________________________________
+Here are the task in your list Sir:
+1.[T][X] borrow book
+2.[D][ ] return book (by: Sunday)
+3.[E][ ] project meeting (from: Mon 2pm to: 4pm)
+4.[D][ ] finish assignment (by: Saturday)
+5.[T][ ] start on vlog
+____________________________________________________________
+____________________________________________________________
+Good bye Sir!
+____________________________________________________________
diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt
index e69de29bb2..90f9ad616e 100644
--- a/text-ui-test/input.txt
+++ b/text-ui-test/input.txt
@@ -0,0 +1,11 @@
+todo borrow book
+deadline return book /by Sunday
+event project meeting /from Mon 2pm /to 4pm
+deadline finish assignment /by Saturday
+todo start on vlog
+list
+mark 1
+mark 3
+unmark 3
+list
+bye
\ No newline at end of file
diff --git a/text-ui-test/runtest.sh b/text-ui-test/runtest.sh
old mode 100644
new mode 100755
index c9ec870033..fb7eed2a2e
--- a/text-ui-test/runtest.sh
+++ b/text-ui-test/runtest.sh
@@ -20,7 +20,7 @@ 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
+java -classpath ../bin Jarvis < input.txt > ACTUAL.TXT
# convert to UNIX format
cp EXPECTED.TXT EXPECTED-UNIX.TXT