diff --git a/src/main/java/AddCommand.java b/src/main/java/AddCommand.java new file mode 100644 index 000000000..42519afe5 --- /dev/null +++ b/src/main/java/AddCommand.java @@ -0,0 +1,28 @@ +package main.java; + +/** + * Represents the command to add a task to the task list. + */ +public class AddCommand extends Command { + private final Task task; + + public AddCommand(Task task) { + this.task = task; + } + + /** + * Perform the add command by adding the task to the task list, + * saving the task list and notifying the user. + * + * @param tasks The task list. + * @param ui The UI component which the user will see. + * @param storage The storage component to save the updated task list. + * @throws KenChatException If there is an error when saving the task list. + */ + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) throws KenChatException { + tasks.addTask(task); + storage.save(tasks.getTasks()); + ui.showTaskAdded(task, tasks.getSize()); + } +} diff --git a/src/main/java/Command.java b/src/main/java/Command.java new file mode 100644 index 000000000..2059dd536 --- /dev/null +++ b/src/main/java/Command.java @@ -0,0 +1,28 @@ +package main.java; + +/** + * Represents an abstract command that can be performed with the task management chat bot. + * Specific commands like add or delete will extend this class. + */ +public abstract class Command { + + /** + * Performs the command using the given task list, UI and storage components. + * + * @param tasks The task list that the command will work on. + * @param ui The UI component of the chat bot. + * @param storage The storage component of the chat bot. + * @throws KenChatException If there is an error when performing the command. + */ + public abstract void execute(TaskList tasks, Ui ui, Storage storage) throws KenChatException; + + /** + * Notifying the chat bat on its running status after performing the command. + * All commands will return true, except only when ExitCommand is called. + * + * @return true If the chat bot should continue running. + */ + public boolean isRunning() { + return true; + } +} diff --git a/src/main/java/DeleteCommand.java b/src/main/java/DeleteCommand.java new file mode 100644 index 000000000..378f4fe89 --- /dev/null +++ b/src/main/java/DeleteCommand.java @@ -0,0 +1,35 @@ +package main.java; + +/** + * Represents a command to delete a task from the task list. + */ +public class DeleteCommand extends Command { + private final int index; + + public DeleteCommand(int index) { + this.index = index - 1; + } + + /** + * Performs the delete command, removing the specific task from the task list, using the task index. + * This is followed by saving to the storage and notifying the user through a display from the UI. + * + * @param tasks The task list that the command will work on. + * @param ui The UI component of the chat bot. + * @param storage The storage component of the chat bot. + * @throws KenChatException If the task list is empty, or if the task number is invalid. + */ + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) throws KenChatException { + if (tasks.getSize() == 0) { + throw new KenChatException(KenChatException.getTaskNotExistMessage()); + } + if (index < 0 || index >= tasks.getSize()) { + throw new KenChatException(KenChatException.getTaskNumberDoesNotExistMessage()); + } + Task removedTask = tasks.getTask(index); + tasks.deleteTask(index); + storage.save(tasks.getTasks()); + ui.showTaskRemoved(removedTask, tasks.getSize()); + } +} diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java deleted file mode 100644 index 5d313334c..000000000 --- a/src/main/java/Duke.java +++ /dev/null @@ -1,10 +0,0 @@ -public class Duke { - public static void main(String[] args) { - String logo = " ____ _ \n" - + "| _ \\ _ _| | _____ \n" - + "| | | | | | | |/ / _ \\\n" - + "| |_| | |_| | < __/\n" - + "|____/ \\__,_|_|\\_\\___|\n"; - System.out.println("Hello from\n" + logo); - } -} diff --git a/src/main/java/ExitCommand.java b/src/main/java/ExitCommand.java new file mode 100644 index 000000000..268102230 --- /dev/null +++ b/src/main/java/ExitCommand.java @@ -0,0 +1,27 @@ +package main.java; + +/** + * Represents a command to exit the chat bot. + */ +public class ExitCommand extends Command { + + /** + * Performs the exit command. No other action is required. + * + * @param tasks The task list that the command will work on. + * @param ui The UI component of the chat bot. + * @param storage The storage component of the chat bot. + */ + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) {} + + /** + * Notify the chat bot to terminate. + * + * @return false so that the chat bot will stop running. + */ + @Override + public boolean isRunning() { + return false; + } +} diff --git a/src/main/java/FindCommand.java b/src/main/java/FindCommand.java new file mode 100644 index 000000000..f77f49dcf --- /dev/null +++ b/src/main/java/FindCommand.java @@ -0,0 +1,43 @@ +package main.java; + +import java.util.Map; + +/** + * Represents a command to find tasks in the task list according to keyword(s). + */ +public class FindCommand extends Command { + private final String keyword; + + public FindCommand(String keyword) { + this.keyword = keyword; + } + + /** + * Performs the find command to search for tasks containing the specific keyword(s). + * + * + * @param tasks The task list that the command will work on. + * @param ui The UI component of the chat bot. + * @param storage The storage component of the chat bot. + * @throws KenChatException If an error occurs while performing the command. + */ + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) throws KenChatException { + // Use the findTasksByKeywordWithIndex method that returns a Map of + Map matchingTasks = tasks.findTasksByKeywordWithIndex(keyword); + if (matchingTasks.isEmpty()) { + ui.showError("No matching tasks found for: " + keyword); + } else { + ui.printLine(); + ui.showMessage("Here are the matching tasks in your list:"); + // Display the matching tasks with the correct indices + for (Map.Entry entry : matchingTasks.entrySet()) { + int index = entry.getKey(); // The original index of the task in the task list + Task task = entry.getValue(); // The task itself + ui.showMessage(index + ". " + task); // Output task with its original index + } + ui.printLine(); + System.out.println(); + } + } +} diff --git a/src/main/java/HelpCommand.java b/src/main/java/HelpCommand.java new file mode 100644 index 000000000..b63708968 --- /dev/null +++ b/src/main/java/HelpCommand.java @@ -0,0 +1,19 @@ +package main.java; + +/** + * Represents a command to display help information to the user. + */ +public class HelpCommand extends Command { + + /** + * Performs the help command by displaying all the valid commands the user can enter. + * + * @param tasks The task list that the command will work on. + * @param ui The UI component of the chat bot. + * @param storage The storage component of the chat bot. + */ + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) { + ui.showHelp(); + } +} diff --git a/src/main/java/KenChat.java b/src/main/java/KenChat.java new file mode 100644 index 000000000..90a802621 --- /dev/null +++ b/src/main/java/KenChat.java @@ -0,0 +1,55 @@ +package main.java; + +/** + * The main class for KenChat application, managing the UI, task storage and command execution. + */ +public class KenChat { + private final Storage storage; + private TaskList tasks; + private final Ui ui; + + /** + * Initialises the KenChat application with the specific file path for storage. + * + * @param filePath The file path where task list is stored. + */ + public KenChat(String filePath) { + ui = new Ui(); + storage = new Storage(filePath); + try { + tasks = new TaskList(storage.load()); + } catch (KenChatException e) { + tasks = new TaskList(); + ui.showLoadingError(); + } + } + + /** + * Runs the KenChat application, processing user commands until exit is prompted. + */ + public void run() { + ui.showStart(); + boolean isRunning = true; + while (isRunning) { + try { + String fullCommand = ui.readCommand(); + Command command = Parser.parse(fullCommand); + command.execute(tasks, ui, storage); + isRunning = command.isRunning(); + } catch (KenChatException e) { + ui.showError(e.getMessage()); + } + } + ui.showEnd(); + } + + /** + * The main entry point of the KenChat application. + * + * @param args Command line arguments (not used). + * @throws KenChatException If there is an error during initialization. + */ + public static void main(String[] args) throws KenChatException { + new KenChat("data/KenChat.txt").run(); + } +} \ No newline at end of file diff --git a/src/main/java/KenChatException.java b/src/main/java/KenChatException.java new file mode 100644 index 000000000..0b134e4b2 --- /dev/null +++ b/src/main/java/KenChatException.java @@ -0,0 +1,69 @@ +package main.java; + +/** + * Exception class for the KenChat application + */ +public class KenChatException extends Exception { + + public KenChatException(String message) { + super(message); + } + + /** + * Returns an error message because of an empty description for a specific command + * @param command The command that has the missing description. + * @return The error message. + */ + public static String getEmptyDescriptionMessage(String command) { + return "Description is missing! Enter a description for the command. Use: " + command + " "; + } + + public static String getUnknownCommandMessage() { + return "Wrong Command! Use for full list of commands. "; + } + + public static String getInvalidDeadlineFormatMessage() { + return "Wrong deadline format. Use: deadline /by Note: can be date and/or time."; + } + + public static String getInvalidEventFormatMessage() { + return "Wrong event format. Use: event /from /to Note: and can be date and/or time."; + } + + public static String getTaskNotExistMessage() { + return "No task exists."; + } + + public static String getInvalidTaskNumberMessage() { + return "Please enter a valid task number. Use: mark/unmark/delete "; + } + + public static String getTaskNumberDoesNotExistMessage() { + return "That task number does not exist. Use to see full task list."; + } + + /** + * Returns an error message because of a missing task number for Mark/Unmark/Delete command. + * + * @param action The command that is supposed to carry out the action of Mark/Unmark/Delete. + * @return The error message. + */ + public static String getEmptyTaskNumberMessage(String action) { + return "Task number is missing!! Please specify the task number to " + action + "."; + } + + public static String emptyCommand() { + return "Empty command!! Use for full list of commands."; + } + + public static String multipleSpaces() { + return "Too many spaces between words! Ensure each word is separated by only a space."; + } + public static String directoryCreationFailure() { + return "Failed to create directory."; + } + + public static String getKeyWordMissing() { + return "Keyword is missing. Use: find "; + } +} diff --git a/src/main/java/ListCommand.java b/src/main/java/ListCommand.java new file mode 100644 index 000000000..221dfef0c --- /dev/null +++ b/src/main/java/ListCommand.java @@ -0,0 +1,24 @@ +package main.java; + +/** + * Represents a command to list all the tasks in the task list. + */ +public class ListCommand extends Command { + + /** + * Performs the list command, displaying all the tasks in the task list via the UI. + * If the task list is empty, an exception is thrown. + * + * @param tasks The task list that the command will work on. + * @param ui The UI component of the chat bot. + * @param storage The storage component of the chat bot. + * @throws KenChatException If the task list is empty, which will display that there is not task in the tasklist. + */ + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) throws KenChatException { + if (tasks.getSize() == 0) { + throw new KenChatException(KenChatException.getTaskNotExistMessage()); + } + ui.showTasksList(tasks.getTasks()); + } +} diff --git a/src/main/java/MarkCommand.java b/src/main/java/MarkCommand.java new file mode 100644 index 000000000..3576c632a --- /dev/null +++ b/src/main/java/MarkCommand.java @@ -0,0 +1,35 @@ +package main.java; + +/** + * Performs a command to mark a task as completed in the task list. + */ +public class MarkCommand extends Command { + private final int index; + + public MarkCommand(int index) { + this.index = index - 1; + } + + /** + * Performs the mark command, marking the specific task as done. + * Saves the updated task list to the storage and notifying the user through display via the UI + * + * @param tasks The task list that the command will work on. + * @param ui The UI component of the chat bot. + * @param storage The storage component of the chat bot. + * @throws KenChatException If the task list is empty or the index is invalid. + */ + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) throws KenChatException { + if (tasks.getSize() == 0) { + throw new KenChatException(KenChatException.getTaskNotExistMessage()); + } + if (index < 0 || index >= tasks.getSize()) { + throw new KenChatException(KenChatException.getTaskNumberDoesNotExistMessage()); + } + Task task = tasks.getTask(index); + task.markAsDone(); + storage.save(tasks.getTasks()); + ui.showTaskMarked(task); + } +} diff --git a/src/main/java/Parser.java b/src/main/java/Parser.java new file mode 100644 index 000000000..b129c7772 --- /dev/null +++ b/src/main/java/Parser.java @@ -0,0 +1,97 @@ +package main.java; + +/** + * Parses user input and creates the matching command objects. + */ +public class Parser { + /** + * Parses the user input and returns the corresponding Command. + * + * @param userInput The input command string from the user. + * @return The Command object corresponding to the user input. + * @throws KenChatException If the input is invalid or commands are wrong. + */ + public static Command parse(String userInput) throws KenChatException { + userInput = userInput.trim(); + + if (userInput.isEmpty()) { // Check for empty command + throw new KenChatException(KenChatException.emptyCommand()); + } + + if (userInput.contains(" ")) { // Check for multiple spaces + throw new KenChatException(KenChatException.multipleSpaces()); + } + + String[] command = userInput.split(" ", 2); + String action = command[0].toLowerCase(); + String arguments = command.length > 1 ? command[1] : ""; + + switch (action) { + case "bye": + return new ExitCommand(); + case "list": + return new ListCommand(); + case "mark": + if (arguments.isEmpty()) { + throw new KenChatException(KenChatException.getEmptyTaskNumberMessage("mark")); + } + try { + return new MarkCommand(Integer.parseInt(arguments)); + } catch (NumberFormatException e) { + throw new KenChatException(KenChatException.getInvalidTaskNumberMessage()); + } + case "unmark": + if (arguments.isEmpty()) { + throw new KenChatException(KenChatException.getEmptyTaskNumberMessage("mark")); + } + try { + return new UnmarkCommand(Integer.parseInt(arguments)); + } catch (NumberFormatException e) { + throw new KenChatException(KenChatException.getInvalidTaskNumberMessage()); + } + case "delete": + if (arguments.isEmpty()) { + throw new KenChatException(KenChatException.getEmptyTaskNumberMessage("mark")); + } + try { + return new DeleteCommand(Integer.parseInt(arguments)); + } catch (NumberFormatException e) { + throw new KenChatException(KenChatException.getInvalidTaskNumberMessage()); + } + case "todo": + if (arguments.isEmpty()) { + throw new KenChatException(KenChatException.getEmptyDescriptionMessage(action)); + } + return new AddCommand(new Task.ToDo(arguments)); + case "deadline": + if (arguments.isEmpty()) { + throw new KenChatException(KenChatException.getEmptyDescriptionMessage(action)); + } + String[] deadlineParts = arguments.split(" /by ", 2); + if (deadlineParts.length == 2) { + return new AddCommand(new Task.Deadline(deadlineParts[0], deadlineParts[1])); + } else { + throw new KenChatException(KenChatException.getInvalidDeadlineFormatMessage()); + } + case "event": + if (arguments.isEmpty()) { + throw new KenChatException(KenChatException.getEmptyDescriptionMessage(action)); + } + String[] eventParts = arguments.split(" /from | /to ", 3); + if (eventParts.length == 3) { + return new AddCommand(new Task.Event(eventParts[0], eventParts[1], eventParts[2])); + } else { + throw new KenChatException(KenChatException.getInvalidEventFormatMessage()); + } + case "help": + return new HelpCommand(); + case "find": + if (arguments.isEmpty()) { + throw new KenChatException(KenChatException.getKeyWordMissing()); + } + return new FindCommand(arguments); + default: + throw new KenChatException(KenChatException.getUnknownCommandMessage()); + } + } +} diff --git a/src/main/java/Storage.java b/src/main/java/Storage.java new file mode 100644 index 000000000..366491cfd --- /dev/null +++ b/src/main/java/Storage.java @@ -0,0 +1,103 @@ +package main.java; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.BufferedReader; +import java.io.FileReader; +import java.util.ArrayList; + +/** + * Handles load and save to a file + */ +public class Storage { + private final String filePath; + + public Storage(String filePath) { + this.filePath = filePath; + } + + /** + * Loads tasks from the file into an ArrayList + * + * @return An ArrayList of tasks loaded from the file + * @throws KenChatException If there is an error when loading tasks. + */ + public ArrayList load() throws KenChatException { + ArrayList doList = new ArrayList<>(); + File file = new File(filePath); + + if (!file.exists()) { + System.out.println("Data file no found. Beginning with a new empty task list."); + return doList; + } + + try (BufferedReader reader = new BufferedReader(new FileReader(file))) { + String line; + while ((line = reader.readLine()) != null) { + Task task = getTask(line); + doList.add(task); + } + } catch (IOException e) { + throw new KenChatException("Error loading data from file: " + e.getMessage()); + } + return doList; + } + + /** + * Parses a line from the file and returns a matching Task object + * + * @param line The line of text from the file which represents a task. + * @return The matching task object. + * @throws KenChatException If the data format is wrong/corrupted. + */ + private static Task getTask(String line) throws KenChatException { + String[] parts = line.split(" \\| "); + Task task; + switch (parts[0]) { + case "T": + task = new Task.ToDo(parts[2]); + break; + case "D": + task = new Task.Deadline(parts[2], parts[3]); + break; + case "E": + task = new Task.Event(parts[2], parts[3], parts[4]); + break; + default: + throw new KenChatException("Corrupted data file."); + } + if (parts[1].equals("1")) { + task.markAsDone(); + } + return task; + } + + /** + * Saves the current list of tasks to the file. + * + * @param doList The task list to save. + * @throws KenChatException If there is an error when saving tasks. + */ + public void save(ArrayList doList) throws KenChatException { + File file = new File(filePath); + + // Ensure the directory exists + File directory = new File(file.getParent()); + if (!directory.exists()) { + if (directory.mkdirs()) { + System.out.println("Directory created successfully."); + } else { + throw new KenChatException(KenChatException.directoryCreationFailure()); + } + } + + try (FileWriter writer = new FileWriter(file)) { + for (Task task : doList) { + writer.write(task.formatForStorage() + System.lineSeparator()); + } + } catch (IOException e) { + throw new KenChatException("Error saving data to file: " + e.getMessage()); + } + } +} diff --git a/src/main/java/Task.java b/src/main/java/Task.java new file mode 100644 index 000000000..95a22e56b --- /dev/null +++ b/src/main/java/Task.java @@ -0,0 +1,119 @@ +package main.java; + +/** + * Represents a task with a description/name and its completion status. + */ +public abstract class Task { + protected String description; + protected boolean isDone; + + public abstract String formatForStorage(); + + public Task(String description) { + this.description = description; + this.isDone = false; + } + + /** + * Returns the task's completion status icon. + * + * @return "X" if task is completed. + */ + public String getStatusIcon() { + return (isDone ? "X" : " "); // mark done task with X + } + + /** + * Returns the description/name of the task. + * + * @return The task description. + */ + public String getName() { + return description; + } + + public void markAsDone(){ + this.isDone = true; + } + + public void markAsUndone(){ + this.isDone = false; + } + + /** + * Returns a formatted string of the task. + * + * @return The string of the task. + */ + public String toString() {return "["+ getStatusIcon() +"] "+getName();} + + // ToDo class + public static class ToDo extends Task { + /** + * Create a ToDo task with the specific description. + * + * @param description The description of the ToDo task. + */ + public ToDo(String description) { + super(description); + } + + public String toString() { + return "[T]" + super.toString(); + } + + public String formatForStorage() { + return "T | " + (isDone ? "1" : "0") + " | " + description; + } + } + + // Deadline class + public static class Deadline extends Task { + public String dueDate; + + /** + * Creates a Deadline task with the specific description and due date. + * + * @param description The description of the Deadline task. + * @param dueDate The due date of the Deadline task. + */ + public Deadline(String description, String dueDate) { + super(description); + this.dueDate = dueDate; + } + + public String toString() { + return "[D]" + super.toString() + " (by: " + dueDate + ")"; + } + public String formatForStorage() { + return "D | " + (isDone ? "1" : "0") + " | " + description + " | " + dueDate; + } + } + + // Event class + public static class Event extends Task { + public String startDate; + public String endDate; + + /** + * Creates an Event task with the specific description, start date, and end date. + * @param description The description of the Event task. + * @param startDate The start date of the Event task. + * @param endDate The end date of the Event task. + */ + public Event(String description, String startDate, String endDate) { + super(description); + this.startDate = startDate; + this.endDate = endDate; + } + + public String toString() { + return "[E]" + super.toString() + " (from: " + startDate + " to: " + endDate + ")"; + } + + public String formatForStorage() { + return "E | " + (isDone ? "1" : "0") + " | " + description + " | " + startDate + " | " + endDate; + } + } + +} \ No newline at end of file diff --git a/src/main/java/TaskList.java b/src/main/java/TaskList.java new file mode 100644 index 000000000..a5291cae9 --- /dev/null +++ b/src/main/java/TaskList.java @@ -0,0 +1,74 @@ +package main.java; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.HashMap; + +/** + * Represents a list of tasks. + */ +public class TaskList { + private final List tasks; + + public TaskList() { + tasks = new ArrayList<>(); + } + + public TaskList(List tasks) { + this.tasks = tasks; + } + + public void addTask(Task task) { + tasks.add(task); + } + + /** + * Deletes a task from the task list at the specific index. + * + * @param index The index of the task to be deleted. + */ + public void deleteTask(int index) { + tasks.remove(index); + } + + /** + * Retrieves the task at the specific index in the task list. + * + * @param index The index of the task to retrieve. + * @return The task at the specific index. + */ + public Task getTask(int index) { + return tasks.get(index); + } + + /** + * Returns the number of tasks in the task list. + * + * @return The size of the task list. + */ + public int getSize() { + return tasks.size(); + } + + public ArrayList getTasks() { + return (ArrayList) tasks; + } + + /** + * Finds and returns a map of tasks that match the specific keyword and their corresponding indices. + * + * @param keyword The keyword to search for in the tasks. + * @return A map where the keys are the 1-based indices of the matching tasks and the values are the matching tasks. + */ + public Map findTasksByKeywordWithIndex(String keyword) { + Map matchingTasks = new HashMap<>(); // Store index-task pairs + for (int i = 0; i < tasks.size(); i++) { + Task task = tasks.get(i); + if (task.getName().contains(keyword)) { + matchingTasks.put(i + 1, task); // Store the 1-based index + } + } + return matchingTasks; + } +} diff --git a/src/main/java/Ui.java b/src/main/java/Ui.java new file mode 100644 index 000000000..b85780aa3 --- /dev/null +++ b/src/main/java/Ui.java @@ -0,0 +1,164 @@ +package main.java; + +import java.util.ArrayList; +import java.util.Scanner; + +/** + * Handles the user interface and interactions with the user. + */ +public class Ui { + private final Scanner sc; + + public Ui() { + this.sc = new Scanner(System.in); + } + + /** + * Displays the start message to the user. + */ + public void showStart() { + printLine(); + System.out.println("Hello! I'm KenChat"); + System.out.println("What can I do for you?"); + System.out.println("Use to show full list of commands."); + printLine(); + System.out.println(); + } + + /** + * Displays the end message to the user. + */ + public void showEnd() { + printLine(); + System.out.println("Bye. Hope to see you again soon!"); + printLine(); + System.out.println(); + } + + public void printLine() { + System.out.println("____________________________________"); + } + + /** + * Reads and returns the command entered by the user. + * + * @return The user input. + */ + public String readCommand() { + return sc.nextLine(); + } + + /** + * Displays the given error message to the user. + * + * @param message The error message to display. + */ + public void showError(String message) { + printLine(); + System.out.println(message); + printLine(); + System.out.println(); + } + + /** + * Displays an error message for failure to load data. + */ + public void showLoadingError() { + showError("Error loading data from file."); + } + + /** + * Displays a message indicating that a task has been added to the list. + * + * @param task The task that was added. + * @param size The current number of tasks in the list. + */ + public void showTaskAdded(Task task, int size) { + printLine(); + System.out.println("Got it. I've added this task:"); + System.out.println(" " + task); + System.out.println("Now you have " + size + " tasks in the list."); + printLine(); + System.out.println(); + } + + /** + * Displays a message indicating that a task has been removed from the list. + * + * @param task The task that was removed. + * @param size The current number of tasks in the list. + */ + public void showTaskRemoved(Task task, int size) { + printLine(); + System.out.println("Noted. I've removed this task:"); + System.out.println(" " + task); + System.out.println("Now you have " + size + " tasks in the list."); + printLine(); + System.out.println(); + } + + /** + * Displays a message indicating that a task has been marked as completed. + * + * @param task The task that was marked as done. + */ + public void showTaskMarked(Task task) { + printLine(); + System.out.println("Nice! I've marked this task as done:"); + System.out.println(" " + task); + printLine(); + System.out.println(); + } + + /** + * Displays a message indicating that a task has been marked as not completed. + * + * @param task The task that was unmarked. + */ + public void showTaskUnmarked(Task task) { + printLine(); + System.out.println("OK, I've marked this task as not done yet:"); + System.out.println(" " + task); + printLine(); + System.out.println(); + } + + /** + * Displays the list of tasks. + * + * @param tasks The list of tasks to display. + */ + public void showTasksList(ArrayList tasks) { + printLine(); + 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)); + } + printLine(); + System.out.println(); + } + + /** + * Displays the list of valid commands for the user. + */ + public void showHelp() { + printLine(); + System.out.println("Valid commands:"); + System.out.println("1. todo - Adds a todo task."); + System.out.println("2. deadline /by - Adds a deadline task. can be date and/or time."); + System.out.println("3. event /from /to - Adds an event task. can be date and/or time."); + System.out.println("4. list - Displays all tasks in the list."); + System.out.println("5. mark - Marks the specified task as done."); + System.out.println("6. unmark - Marks the specified task as not done."); + System.out.println("7. delete - Deletes the specified task from the list."); + System.out.println("8. find - Find task(s) in the list by searching with keyword(s)."); + System.out.println("9. bye - Exits the program."); + System.out.println("10. help - Shows this help message."); + printLine(); + System.out.println(); + } + + public void showMessage(String message) { + System.out.println(message); + } +} diff --git a/src/main/java/UnmarkCommand.java b/src/main/java/UnmarkCommand.java new file mode 100644 index 000000000..08c164766 --- /dev/null +++ b/src/main/java/UnmarkCommand.java @@ -0,0 +1,34 @@ +package main.java; + +/** + * Represents a command to unmark a task as not completed. + */ +public class UnmarkCommand extends Command { + private final int index; + + public UnmarkCommand(int index) { + this.index = index - 1; + } + + /** + * Performs the unmark command indicating that the task is not yet completed.\ + * + * @param tasks The task list that the command will work on. + * @param ui The UI component of the chat bot. + * @param storage The storage component of the chat bot. + * @throws KenChatException If the task list is empty or the index is invalid. + */ + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) throws KenChatException { + if (tasks.getSize() == 0) { + throw new KenChatException(KenChatException.getTaskNotExistMessage()); + } + if (index < 0 || index >= tasks.getSize()) { + throw new KenChatException(KenChatException.getTaskNumberDoesNotExistMessage()); + } + Task task = tasks.getTask(index); + task.markAsUndone(); + storage.save(tasks.getTasks()); + ui.showTaskUnmarked(task); + } +}