From a40616ada7f08d994c1e82a730348ff03f2d97fb Mon Sep 17 00:00:00 2001 From: T1duS Date: Fri, 18 Oct 2024 18:18:37 +0800 Subject: [PATCH] code quality and exception changes --- data/tasks.txt | 2 + src/main/java/opus/Main.java | 6 +- src/main/java/opus/Opus.java | 157 ++---------------- src/main/java/opus/OpusException.java | 17 -- src/main/java/opus/Parser.java | 152 ++++++++++++++--- src/main/java/opus/Storage.java | 5 + src/main/java/opus/TaskList.java | 2 + src/main/java/opus/commands/AddCommand.java | 60 +++++++ src/main/java/opus/commands/ByeCommand.java | 29 ++++ src/main/java/opus/commands/Command.java | 13 ++ .../java/opus/commands/CommandResult.java | 28 ++++ .../java/opus/commands/DeleteCommand.java | 53 ++++++ src/main/java/opus/commands/HelpCommand.java | 34 ++++ src/main/java/opus/commands/ListCommand.java | 32 ++++ src/main/java/opus/commands/MarkCommand.java | 60 +++++++ .../OpusEmptyDescriptionException.java | 2 +- .../java/opus/exceptions/OpusException.java | 16 ++ .../OpusInvalidArgumentException.java | 16 ++ .../OpusInvalidDateFormatException.java | 16 ++ .../OpusMissingArgumentException.java | 16 ++ .../exceptions/OpusTaskNotFoundException.java | 16 ++ .../OpusUnknownCommandException.java | 2 +- src/main/java/opus/{ => tasks}/Deadline.java | 4 +- src/main/java/opus/{ => tasks}/Event.java | 3 +- src/main/java/opus/{ => tasks}/Task.java | 2 +- src/main/java/opus/{ => tasks}/ToDo.java | 2 +- src/test/java/DeadlineTest.java | 5 +- src/test/java/TaskTest.java | 4 +- src/test/java/ToDoTest.java | 4 +- 29 files changed, 562 insertions(+), 196 deletions(-) delete mode 100644 src/main/java/opus/OpusException.java create mode 100644 src/main/java/opus/commands/AddCommand.java create mode 100644 src/main/java/opus/commands/ByeCommand.java create mode 100644 src/main/java/opus/commands/Command.java create mode 100644 src/main/java/opus/commands/CommandResult.java create mode 100644 src/main/java/opus/commands/DeleteCommand.java create mode 100644 src/main/java/opus/commands/HelpCommand.java create mode 100644 src/main/java/opus/commands/ListCommand.java create mode 100644 src/main/java/opus/commands/MarkCommand.java rename src/main/java/opus/{ => exceptions}/OpusEmptyDescriptionException.java (90%) create mode 100644 src/main/java/opus/exceptions/OpusException.java create mode 100644 src/main/java/opus/exceptions/OpusInvalidArgumentException.java create mode 100644 src/main/java/opus/exceptions/OpusInvalidDateFormatException.java create mode 100644 src/main/java/opus/exceptions/OpusMissingArgumentException.java create mode 100644 src/main/java/opus/exceptions/OpusTaskNotFoundException.java rename src/main/java/opus/{ => exceptions}/OpusUnknownCommandException.java (94%) rename src/main/java/opus/{ => tasks}/Deadline.java (99%) rename src/main/java/opus/{ => tasks}/Event.java (99%) rename src/main/java/opus/{ => tasks}/Task.java (99%) rename src/main/java/opus/{ => tasks}/ToDo.java (98%) diff --git a/data/tasks.txt b/data/tasks.txt index 6e463b44db..b046a1907e 100644 --- a/data/tasks.txt +++ b/data/tasks.txt @@ -1,2 +1,4 @@ D|0|return book|Sunday E|1|project meeting|Mon 2pm|4pm +T|1|submit report +T|0|return book diff --git a/src/main/java/opus/Main.java b/src/main/java/opus/Main.java index d9d8d6f900..8792738256 100644 --- a/src/main/java/opus/Main.java +++ b/src/main/java/opus/Main.java @@ -10,6 +10,7 @@ import javafx.scene.layout.Region; import javafx.stage.Stage; import javafx.scene.image.Image; +import opus.commands.CommandResult; @@ -97,14 +98,15 @@ public void start(Stage stage) { private void handleUserInput() { String userText = userInput.getText(); - String opusText = opus.getResponse(userInput.getText()); + CommandResult commandResult = opus.getResponse(userText); + String opusText = commandResult.getFeedbackToUser(); dialogContainer.getChildren().addAll( DialogBox.getUserDialog(userText, userImage), DialogBox.getOpusDialog(opusText, opusImage) ); userInput.clear(); - if (opus.isExit()) { + if (commandResult.isExit()) { Platform.exit(); } } diff --git a/src/main/java/opus/Opus.java b/src/main/java/opus/Opus.java index e5013af2b9..8467110de7 100644 --- a/src/main/java/opus/Opus.java +++ b/src/main/java/opus/Opus.java @@ -1,21 +1,21 @@ package opus; +import opus.exceptions.OpusException; +import opus.commands.Command; +import opus.commands.CommandResult; + /** - * The Opus class is the main entry point of the Duke task manager application. - * It initializes the necessary components (UI, storage, task list) and runs - * the main loop to interact with the user, handle commands, and manage tasks. + * Represents the main class of the program. */ public class Opus { private Storage storage; private TaskList taskList; - private boolean isExit = false; /** - * Initializes the Opus application with the specified file path for storage. - * Loads tasks from storage and sets up UI and task list. + * Creates a new instance of Opus. * - * @param filePath Path to the file where tasks are stored. + * @param filePath The file path of the storage file. */ public Opus(String filePath) { storage = new Storage(filePath); @@ -23,144 +23,19 @@ public Opus(String filePath) { } /** - * Handles the bye command by saving tasks to storage and setting the exit flag. + * Gets the response to the user input. * - * @return The response message indicating the application will exit. - * @throws OpusException If an error occurs while saving tasks. + * @param input The user input. + * @return The response to the user. */ - private String handleByeCommand() throws OpusException { - storage.save(taskList.getTasks()); - isExit = true; // Set the exit flag - return "Bye. Hope to see you again soon!"; - } - - /** - * Returns whether the application should exit. - * - * @return True if the application should exit, false otherwise. - */ - public boolean isExit() { - return isExit; - } - - /** - * Handles the list command by displaying all tasks in the task list. - * - * @return The response message containing all tasks in the list. - */ - private String handleListCommand() { - String response = ""; - for (Task task : taskList.getTasks()) { - response += task.toString() + "\n"; - } - return response; - } - /** - * Handles the mark command by marking a task as done. - * - * @param words The command split into words. - * @return The response message indicating the task has been marked as done. - * @throws OpusException If the task number is not specified or is invalid. - */ - private String handleMarkCommand(String[] words) throws OpusException { - if (words.length < 2) { - throw new OpusException("Please specify the task number to mark."); - } - int i = Integer.parseInt(words[1]) - 1; - assert i >= 0 && i < taskList.getSize() : "Index out of bounds: " + i; - taskList.getTask(i).markAsDone(); - return "Nice! I've marked this task as done:\n" + taskList.getTask(i).toString(); - } - /** - * Handles the delete command by removing a task from the list. - * - * @param words The command split into words. - * @return The response message indicating the task has been deleted. - * @throws OpusException If the task number is not specified or is invalid. - */ - private String handleDeleteCommand(String[] words) throws OpusException { - String response = ""; - if (words.length < 2) { - throw new OpusException("Please specify the task number to delete."); - } - int i = Integer.parseInt(words[1]) - 1; - assert i >= 0 && i < taskList.getSize() : "Index out of bounds: " + i; - response = "Noted. I've removed this task:\n" + taskList.getTask(i).toString() + "\n"; - taskList.removeTask(i); - response += "Now you have " + taskList.getSize() + " tasks in the list."; - return response; - } - /** - * Handles the help command by displaying a list of available commands. - * - * @return The response message containing the list of commands. - */ - private String handleHelpCommand() { - String response = "Here are the commands you can use:\n"; - response += "1. list - List all tasks\n"; - response += "2. mark - Mark a task as done\n"; - response += "3. delete - Delete a task\n"; - response += "4. deadline /by - Add a deadline by the end date\n"; - response += "5. event /from /to "; - response += " - Add an event with start and end dates\n"; - response += "6. todotask <> - Add a Todo\n"; - response += "7. bye - Exit the application"; - return response; - } - - - /** - * Main method to launch the Opus application. - * @param args Command-line arguments, not used. - */ - public static void main(String[] args) { - new Opus("data/tasks.txt"); - } - - public String getResponse(String input) { - String[] words = Parser.parse(input); - String response = ""; + public CommandResult getResponse(String input) { try { - assert words.length > 0 : "Command cannot be empty"; - if (words[0].equals("bye")) { - response = handleByeCommand(); - } else if (words[0].equals("list")) { - response = handleListCommand(); - } else if (words[0].equals("mark")) { - response = handleMarkCommand(words); - } else if (words[0].equals("delete")) { - response = handleDeleteCommand(words); - } else if (words[0].equals("help")) { - response = handleHelpCommand(); - } else { - if (words[0].equals("todo")) { - assert words.length > 1 : "The description of a todo cannot be empty"; - if (words.length <= 1) { - throw new OpusEmptyDescriptionException("The description of a todo cannot be empty."); - } - Task todo = new ToDo(words[1]); - taskList.addTask(todo); - } else if (words[0].equals("deadline")) { - assert words.length > 1 : "Deadline details are missing"; - String[] parts = Parser.parseDeadlineDetails(words[1]); - Task deadline = new Deadline(parts[0], parts[1]); - taskList.addTask(deadline); - } else if (words[0].equals("event")) { - assert words.length > 1 : "Event details are missing"; - String[] parts = Parser.parseEventDetails(words[1]); - Task event = new Event(parts[0], parts[1], parts[2]); - taskList.addTask(event); - } else { - throw new OpusUnknownCommandException("I'm sorry, but I don't know what that means."); - } - response = "Got it. I've added this task:\n"; - response += taskList.getTask(taskList.getSize() - 1).toString() + "\n"; - response += "Now you have " + taskList.getSize() + " tasks in the list."; - return response; - } + Command command = Parser.parse(input); + String response = command.execute(taskList, storage); + boolean isExit = command.isExit(); + return new CommandResult(response, isExit); } catch (OpusException e) { - return e.getMessage(); + return new CommandResult(e.getMessage(), false); } - return response; } } diff --git a/src/main/java/opus/OpusException.java b/src/main/java/opus/OpusException.java deleted file mode 100644 index fd0b01332c..0000000000 --- a/src/main/java/opus/OpusException.java +++ /dev/null @@ -1,17 +0,0 @@ -package opus; - -/** - * Represents a custom exception specific to the Opus application. - * This serves as the base exception class for all other Opus-related exceptions. - */ -public class OpusException extends Exception { - - /** - * Constructs an OpusException with a specific error message. - * - * @param message The error message that describes the exception. - */ - public OpusException(String message) { - super(message); - } -} diff --git a/src/main/java/opus/Parser.java b/src/main/java/opus/Parser.java index 087195b5e4..a0e0cbb49a 100644 --- a/src/main/java/opus/Parser.java +++ b/src/main/java/opus/Parser.java @@ -1,41 +1,133 @@ package opus; +import opus.commands.AddCommand; +import opus.commands.Command; +import opus.commands.ByeCommand; +import opus.commands.ListCommand; +import opus.commands.MarkCommand; +import opus.commands.DeleteCommand; +import opus.commands.HelpCommand; +import opus.exceptions.OpusException; +import opus.exceptions.OpusEmptyDescriptionException; +import opus.exceptions.OpusMissingArgumentException; +import opus.exceptions.OpusInvalidDateFormatException; +import opus.exceptions.OpusUnknownCommandException; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; + /** - * The Parser class is responsible for parsing user commands and extracting relevant - * information such as the action, details, and specific components for tasks like deadlines - * and events. + * The Parser class is responsible for parsing user commands and returning the corresponding Command objects. + * It handles validation and throws specific exceptions for invalid inputs. */ public class Parser { /** - * Parses the full command into an action and details. + * Parses the full command entered by the user and returns the corresponding Command object. * * @param fullCommand The complete command entered by the user. - * @return A string array containing the action and the rest of the command. + * @return The Command object corresponding to the command. + * @throws OpusException If the command is invalid or cannot be parsed. */ - public static String[] parse(String fullCommand) { - return fullCommand.split(" ", 2); + public static Command parse(String fullCommand) throws OpusException { + String[] words = fullCommand.trim().split(" ", 2); + String action = words[0]; + String details = words.length > 1 ? words[1].trim() : ""; + + switch (action) { + case "bye": + return new ByeCommand(); + + case "list": + return new ListCommand(); + + case "mark": + return new MarkCommand(details); + + case "delete": + return new DeleteCommand(details); + + case "todo": + if (details.isEmpty()) { + throw new OpusEmptyDescriptionException("The description of a todo cannot be empty."); + } + return new AddCommand(action, details); + + case "deadline": + if (details.isEmpty()) { + throw new OpusMissingArgumentException("The description and date of a deadline cannot be empty."); + } + String[] deadlineParts = details.split(" /by ", 2); + if (deadlineParts.length < 2 || deadlineParts[0].trim().isEmpty() || deadlineParts[1].trim().isEmpty()) { + throw new OpusMissingArgumentException("Please provide details for the deadline."); + } + if (!isValidDate(deadlineParts[1].trim())) { + throw new OpusInvalidDateFormatException("Invalid date format for deadline. Please use yyyy-MM-dd."); + } + return new AddCommand(action, details); + + case "event": + if (details.isEmpty()) { + throw new OpusMissingArgumentException("The description and dates of an event cannot be empty."); + } + String[] eventParts = details.split(" /from ", 2); + if (eventParts.length < 2 || eventParts[0].trim().isEmpty()) { + throw new OpusMissingArgumentException("Please provide the description and /from date for the event."); + } + String[] timeParts = eventParts[1].split(" /to ", 2); + if (timeParts.length < 2 || timeParts[0].trim().isEmpty() || timeParts[1].trim().isEmpty()) { + throw new OpusMissingArgumentException("Please provide both the dates for the event."); + } + if (!isValidDate(timeParts[0].trim()) || !isValidDate(timeParts[1].trim())) { + throw new OpusInvalidDateFormatException("Invalid date format. Please use yyyy-MM-dd."); + } + return new AddCommand(action, details); + + case "help": + return new HelpCommand(); + + default: + throw new OpusUnknownCommandException("I'm sorry, but I don't know what that means."); + } } /** - * Extracts the action (first word) from the command. + * Parses a string containing a task index and returns the index as an integer. * - * @param fullCommand The complete command entered by the user. - * @return The action part of the command. + * @param details The string containing the task index. + * @return The task index as an integer. + * @throws OpusException If the index is missing or invalid. */ - public static String parseAction(String fullCommand) { - return fullCommand.split(" ")[0]; + private static int parseIndex(String details) throws OpusException { + if (details.trim().isEmpty()) { + throw new OpusMissingArgumentException("Please provide a task number."); + } + try { + int index = Integer.parseInt(details.trim()) - 1; + if (index < 0) { + throw new OpusException("Task number must be a positive integer."); + } + return index; + } catch (NumberFormatException e) { + throw new OpusException("Please provide a valid task number."); + } } /** - * Extracts the details (everything after the first word) from the command. + * Validates if the provided date string matches the expected format yyyy-MM-dd. * - * @param fullCommand The complete command entered by the user. - * @return The details part of the command, or an empty string if none exists. + * @param date The date string to validate. + * @return True if the date is valid, false otherwise. */ - public static String parseDetails(String fullCommand) { - String[] parts = fullCommand.split(" ", 2); - return parts.length > 1 ? parts[1] : ""; + private static boolean isValidDate(String date) { + try { + DateTimeFormatter inputFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + LocalDate.parse(date, inputFormat); + return true; + } catch (DateTimeParseException e) { + return false; + } } /** @@ -43,9 +135,14 @@ public static String parseDetails(String fullCommand) { * * @param details The details part of the command for a deadline task. * @return A string array containing the task description and the deadline. + * @throws OpusException If the details are invalid. */ - public static String[] parseDeadlineDetails(String details) { - return details.split(" /by "); + public static String[] parseDeadlineDetails(String details) throws OpusException { + String[] deadlineParts = details.split(" /by ", 2); + if (deadlineParts.length < 2 || deadlineParts[0].trim().isEmpty() || deadlineParts[1].trim().isEmpty()) { + throw new OpusMissingArgumentException("Please provide both details for the deadline."); + } + return deadlineParts; } /** @@ -53,10 +150,17 @@ public static String[] parseDeadlineDetails(String details) { * * @param details The details part of the command for an event task. * @return A string array containing the task description, start time, and end time. + * @throws OpusException If the details are invalid. */ - public static String[] parseEventDetails(String details) { - String[] parts = details.split(" /from "); - String[] timeParts = parts[1].split(" /to "); - return new String[] { parts[0], timeParts[0], timeParts[1] }; + public static String[] parseEventDetails(String details) throws OpusException { + String[] eventParts = details.split(" /from ", 2); + if (eventParts.length < 2 || eventParts[0].trim().isEmpty()) { + throw new OpusMissingArgumentException("Please provide the description and /from date for the event."); + } + String[] timeParts = eventParts[1].split(" /to ", 2); + if (timeParts.length < 2 || timeParts[0].trim().isEmpty() || timeParts[1].trim().isEmpty()) { + throw new OpusMissingArgumentException("Please provide both the /from date and /to date for the event."); + } + return new String[] { eventParts[0].trim(), timeParts[0].trim(), timeParts[1].trim() }; } } diff --git a/src/main/java/opus/Storage.java b/src/main/java/opus/Storage.java index 3125788474..20efeb986d 100644 --- a/src/main/java/opus/Storage.java +++ b/src/main/java/opus/Storage.java @@ -6,6 +6,11 @@ import java.util.ArrayList; import java.util.Scanner; +import opus.tasks.Deadline; +import opus.tasks.Event; +import opus.tasks.Task; +import opus.tasks.ToDo; + /** * The Storage class handles the loading and saving of tasks from/to a file. * It reads tasks from a file to initialize the task list and writes tasks to diff --git a/src/main/java/opus/TaskList.java b/src/main/java/opus/TaskList.java index 9fec760591..185bbf39b7 100644 --- a/src/main/java/opus/TaskList.java +++ b/src/main/java/opus/TaskList.java @@ -2,6 +2,8 @@ import java.util.ArrayList; +import opus.tasks.Task; + /** * Manages a list of tasks in the application. Supports adding, retrieving, * removing, and listing tasks. diff --git a/src/main/java/opus/commands/AddCommand.java b/src/main/java/opus/commands/AddCommand.java new file mode 100644 index 0000000000..54be5ccaad --- /dev/null +++ b/src/main/java/opus/commands/AddCommand.java @@ -0,0 +1,60 @@ +package opus.commands; + +import opus.Storage; +import opus.TaskList; +import opus.exceptions.OpusException; +import opus.Parser; +import opus.tasks.Deadline; +import opus.tasks.Event; +import opus.tasks.Task; +import opus.tasks.ToDo; + +/** + * Represents a command to add a task to the task list. + */ +public class AddCommand implements Command { + private final String action; + private final String details; + + /** + * Constructs an AddCommand object. + * + * @param action The type of task to add. + * @param details The details of the task to add. + */ + public AddCommand(String action, String details) { + this.action = action; + this.details = details; + } + + @Override + public String execute(TaskList taskList, Storage storage) throws OpusException { + Task task; + switch (action) { + case "todo": + if (details.isEmpty()) { + throw new OpusException("The description of a todo cannot be empty."); + } + task = new ToDo(details); + break; + case "deadline": + String[] deadlineParts = Parser.parseDeadlineDetails(details); + task = new Deadline(deadlineParts[0], deadlineParts[1]); + break; + case "event": + String[] eventParts = Parser.parseEventDetails(details); + task = new Event(eventParts[0], eventParts[1], eventParts[2]); + break; + default: + throw new OpusException("Invalid task type."); + } + taskList.addTask(task); + return "Got it. I've added this task:\n" + task.toString() + + "\n" + "Now you have " + taskList.getSize() + " tasks in the list."; + } + + @Override + public boolean isExit() { + return false; + } +} diff --git a/src/main/java/opus/commands/ByeCommand.java b/src/main/java/opus/commands/ByeCommand.java new file mode 100644 index 0000000000..f691fb4827 --- /dev/null +++ b/src/main/java/opus/commands/ByeCommand.java @@ -0,0 +1,29 @@ +package opus.commands; + +import opus.exceptions.OpusException; +import opus.Storage; +import opus.TaskList; + +/** + * Represents a command to exit the program. + */ +public class ByeCommand implements Command { + /** + * Executes the command to exit the program. + * + * @param taskList The task list. + * @param storage The storage. + * @return The response to the user. + * @throws OpusException If an error occurs during execution. + */ + @Override + public String execute(TaskList taskList, Storage storage) throws OpusException { + storage.save(taskList.getTasks()); + return "Bye. Hope to see you again soon!"; + } + + @Override + public boolean isExit() { + return true; + } +} diff --git a/src/main/java/opus/commands/Command.java b/src/main/java/opus/commands/Command.java new file mode 100644 index 0000000000..34d754bd2d --- /dev/null +++ b/src/main/java/opus/commands/Command.java @@ -0,0 +1,13 @@ +package opus.commands; + +import opus.exceptions.OpusException; +import opus.Storage; +import opus.TaskList; + +/** + * Represents a command that can be executed by the user. + */ +public interface Command { + String execute(TaskList taskList, Storage storage) throws OpusException; + boolean isExit(); +} diff --git a/src/main/java/opus/commands/CommandResult.java b/src/main/java/opus/commands/CommandResult.java new file mode 100644 index 0000000000..fd9cbc2415 --- /dev/null +++ b/src/main/java/opus/commands/CommandResult.java @@ -0,0 +1,28 @@ +package opus.commands; + +/** + * Represents the result of a command execution. + */ +public class CommandResult { + private final String feedbackToUser; + private final boolean isExit; + + /** + * Creates a new command result. + * + * @param feedbackToUser The feedback to the user. + * @param isExit Whether the program should exit. + */ + public CommandResult(String feedbackToUser, boolean isExit) { + this.feedbackToUser = feedbackToUser; + this.isExit = isExit; + } + + public String getFeedbackToUser() { + return feedbackToUser; + } + + public boolean isExit() { + return isExit; + } +} diff --git a/src/main/java/opus/commands/DeleteCommand.java b/src/main/java/opus/commands/DeleteCommand.java new file mode 100644 index 0000000000..1a57334d3c --- /dev/null +++ b/src/main/java/opus/commands/DeleteCommand.java @@ -0,0 +1,53 @@ +package opus.commands; + +import opus.exceptions.OpusException; +import opus.exceptions.OpusMissingArgumentException; +import opus.exceptions.OpusInvalidArgumentException; +import opus.exceptions.OpusTaskNotFoundException; +import opus.Storage; +import opus.TaskList; +import opus.tasks.Task; + +/** + * Represents a command to delete a task from the task list. + */ +public class DeleteCommand implements Command { + private final int taskIndex; + + /** + * Creates a new DeleteCommand object. + * + * @param details The details of the command. + * @throws OpusException If the task number is invalid. + */ + public DeleteCommand(String details) throws OpusException { + if (details.trim().isEmpty()) { + throw new OpusMissingArgumentException("Please provide the task number to delete."); + } + try { + this.taskIndex = Integer.parseInt(details.trim()) - 1; // Convert to zero-based index + if (taskIndex < 0) { + throw new OpusInvalidArgumentException("Task number must be a positive integer."); + } + } catch (NumberFormatException e) { + throw new OpusInvalidArgumentException("Please provide a valid task number."); + } + } + + @Override + public String execute(TaskList taskList, Storage storage) throws OpusException { + if (taskIndex < 0 || taskIndex >= taskList.getSize()) { + throw new OpusTaskNotFoundException("Task number out of range."); + } + Task task = taskList.getTask(taskIndex); + taskList.removeTask(taskIndex); + storage.save(taskList.getTasks()); + return "Noted. I've removed this task:\n" + task.toString() + + "\nNow you have " + taskList.getSize() + " tasks in the list."; + } + + @Override + public boolean isExit() { + return false; + } +} diff --git a/src/main/java/opus/commands/HelpCommand.java b/src/main/java/opus/commands/HelpCommand.java new file mode 100644 index 0000000000..b84ba67325 --- /dev/null +++ b/src/main/java/opus/commands/HelpCommand.java @@ -0,0 +1,34 @@ +package opus.commands; + +import opus.Storage; +import opus.TaskList; + +/** + * Represents a command to show the help message. + */ +public class HelpCommand implements Command { + /** + * Executes the command. + * + * @param taskList The task list. + * @param storage The storage. + * @return The response to the user. + */ + @Override + public String execute(TaskList taskList, Storage storage) { + return "Here are the commands you can use:\n" + + "1. list - List all tasks\n" + + "2. mark - Mark a task as done\n" + + "3. delete - Delete a task\n" + + "4. deadline /by - Add a deadline\n" + + "5. event /from /to - Add an event\n" + + "6. todo - Add a Todo task\n" + + "7. bye - Exit the application\n" + + "8. help - Show this help message"; + } + + @Override + public boolean isExit() { + return false; + } +} diff --git a/src/main/java/opus/commands/ListCommand.java b/src/main/java/opus/commands/ListCommand.java new file mode 100644 index 0000000000..9a63ffd324 --- /dev/null +++ b/src/main/java/opus/commands/ListCommand.java @@ -0,0 +1,32 @@ +package opus.commands; + +import opus.Storage; +import opus.TaskList; +import opus.tasks.Task; + +/** + * Represents a command to list all tasks in the task list. + */ +public class ListCommand implements Command { + /** + * Executes the command to list all tasks in the task list. + * + * @param taskList The task list. + * @param storage The storage. + * @return The response to the user. + */ + @Override + public String execute(TaskList taskList, Storage storage) { + StringBuilder response = new StringBuilder(); + for (int i = 0; i < taskList.getSize(); i++) { + Task task = taskList.getTask(i); + response.append(i + 1).append(". ").append(task.toString()).append("\n"); + } + return response.toString(); + } + + @Override + public boolean isExit() { + return false; + } +} diff --git a/src/main/java/opus/commands/MarkCommand.java b/src/main/java/opus/commands/MarkCommand.java new file mode 100644 index 0000000000..606309f9f7 --- /dev/null +++ b/src/main/java/opus/commands/MarkCommand.java @@ -0,0 +1,60 @@ +package opus.commands; + +import opus.exceptions.OpusException; +import opus.exceptions.OpusMissingArgumentException; +import opus.exceptions.OpusInvalidArgumentException; +import opus.exceptions.OpusTaskNotFoundException; +import opus.Storage; +import opus.TaskList; +import opus.tasks.Task; + +/** + * Represents a command to mark a task as done. + */ +public class MarkCommand implements Command { + private final int taskIndex; + + /** + * Creates a new MarkCommand object. + * + * @param details The details of the command. + * @throws OpusException If the task number is invalid. + */ + public MarkCommand(String details) throws OpusException { + if (details.trim().isEmpty()) { + throw new OpusMissingArgumentException("Please provide the task number to mark."); + } + try { + this.taskIndex = Integer.parseInt(details.trim()) - 1; // Convert to zero-based index + if (taskIndex < 0) { + throw new OpusInvalidArgumentException("Task number must be a positive integer."); + } + } catch (NumberFormatException e) { + throw new OpusInvalidArgumentException("Please provide a valid task number."); + } + } + + /** + * Marks the task at the specified index as done. + * + * @param taskList The task list containing the task to mark as done. + * @param storage The storage object to save the task list to. + * @return A message indicating that the task has been marked as done. + * @throws OpusException If the task number is out of range. + */ + @Override + public String execute(TaskList taskList, Storage storage) throws OpusException { + if (taskIndex < 0 || taskIndex >= taskList.getSize()) { + throw new OpusTaskNotFoundException("Task number out of range."); + } + Task task = taskList.getTask(taskIndex); + task.markAsDone(); + storage.save(taskList.getTasks()); + return "Nice! I've marked this task as done:\n" + task.toString(); + } + + @Override + public boolean isExit() { + return false; + } +} diff --git a/src/main/java/opus/OpusEmptyDescriptionException.java b/src/main/java/opus/exceptions/OpusEmptyDescriptionException.java similarity index 90% rename from src/main/java/opus/OpusEmptyDescriptionException.java rename to src/main/java/opus/exceptions/OpusEmptyDescriptionException.java index 032f7203a8..58ab75c0a6 100644 --- a/src/main/java/opus/OpusEmptyDescriptionException.java +++ b/src/main/java/opus/exceptions/OpusEmptyDescriptionException.java @@ -1,4 +1,4 @@ -package opus; +package opus.exceptions; /** * Represents an exception thrown when the description of a task is empty. diff --git a/src/main/java/opus/exceptions/OpusException.java b/src/main/java/opus/exceptions/OpusException.java new file mode 100644 index 0000000000..9bfa7bb5dc --- /dev/null +++ b/src/main/java/opus/exceptions/OpusException.java @@ -0,0 +1,16 @@ +package opus.exceptions; + +/** + * Base exception class for all Opus-related exceptions. + */ +public class OpusException extends Exception { + + /** + * Constructs a new OpusException with the specified detail message. + * + * @param message The detail message. + */ + public OpusException(String message) { + super(message); + } +} diff --git a/src/main/java/opus/exceptions/OpusInvalidArgumentException.java b/src/main/java/opus/exceptions/OpusInvalidArgumentException.java new file mode 100644 index 0000000000..31641e3970 --- /dev/null +++ b/src/main/java/opus/exceptions/OpusInvalidArgumentException.java @@ -0,0 +1,16 @@ +package opus.exceptions; + +/** + * Thrown when an argument provided by the user is invalid. + */ +public class OpusInvalidArgumentException extends OpusException { + + /** + * Constructs a new OpusInvalidArgumentException with the specified detail message. + * + * @param message The detail message. + */ + public OpusInvalidArgumentException(String message) { + super(message); + } +} diff --git a/src/main/java/opus/exceptions/OpusInvalidDateFormatException.java b/src/main/java/opus/exceptions/OpusInvalidDateFormatException.java new file mode 100644 index 0000000000..544be85737 --- /dev/null +++ b/src/main/java/opus/exceptions/OpusInvalidDateFormatException.java @@ -0,0 +1,16 @@ +package opus.exceptions; + +/** + * Thrown when a date format is invalid or cannot be parsed. + */ +public class OpusInvalidDateFormatException extends OpusException { + + /** + * Constructs a new OpusInvalidDateFormatException with the specified detail message. + * + * @param message The detail message. + */ + public OpusInvalidDateFormatException(String message) { + super(message); + } +} diff --git a/src/main/java/opus/exceptions/OpusMissingArgumentException.java b/src/main/java/opus/exceptions/OpusMissingArgumentException.java new file mode 100644 index 0000000000..1ec4771a53 --- /dev/null +++ b/src/main/java/opus/exceptions/OpusMissingArgumentException.java @@ -0,0 +1,16 @@ +package opus.exceptions; + +/** + * Thrown when a required argument is missing in the user's command. + */ +public class OpusMissingArgumentException extends OpusException { + + /** + * Constructs a new OpusMissingArgumentException with the specified detail message. + * + * @param message The detail message. + */ + public OpusMissingArgumentException(String message) { + super(message); + } +} diff --git a/src/main/java/opus/exceptions/OpusTaskNotFoundException.java b/src/main/java/opus/exceptions/OpusTaskNotFoundException.java new file mode 100644 index 0000000000..beaf86baa5 --- /dev/null +++ b/src/main/java/opus/exceptions/OpusTaskNotFoundException.java @@ -0,0 +1,16 @@ +package opus.exceptions; + +/** + * Thrown when a task is not found in the task list, typically due to an invalid index. + */ +public class OpusTaskNotFoundException extends OpusException { + + /** + * Constructs a new OpusTaskNotFoundException with the specified detail message. + * + * @param message The detail message. + */ + public OpusTaskNotFoundException(String message) { + super(message); + } +} diff --git a/src/main/java/opus/OpusUnknownCommandException.java b/src/main/java/opus/exceptions/OpusUnknownCommandException.java similarity index 94% rename from src/main/java/opus/OpusUnknownCommandException.java rename to src/main/java/opus/exceptions/OpusUnknownCommandException.java index fdd6be983d..ccf5dd1908 100644 --- a/src/main/java/opus/OpusUnknownCommandException.java +++ b/src/main/java/opus/exceptions/OpusUnknownCommandException.java @@ -1,4 +1,4 @@ -package opus; +package opus.exceptions; /** * Represents an exception thrown when an unknown or unsupported command is entered by the user. diff --git a/src/main/java/opus/Deadline.java b/src/main/java/opus/tasks/Deadline.java similarity index 99% rename from src/main/java/opus/Deadline.java rename to src/main/java/opus/tasks/Deadline.java index 51e227f89d..0f90b03563 100644 --- a/src/main/java/opus/Deadline.java +++ b/src/main/java/opus/tasks/Deadline.java @@ -1,9 +1,11 @@ -package opus; +package opus.tasks; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; + + /** * Represents a task with a deadline. **/ diff --git a/src/main/java/opus/Event.java b/src/main/java/opus/tasks/Event.java similarity index 99% rename from src/main/java/opus/Event.java rename to src/main/java/opus/tasks/Event.java index b9c26a12d4..ebb167f328 100644 --- a/src/main/java/opus/Event.java +++ b/src/main/java/opus/tasks/Event.java @@ -1,9 +1,10 @@ -package opus; +package opus.tasks; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; + /** * Represents an event task with a start date and an end date. * The event dates are parsed from a string using {@code yyyy-MM-dd} format. diff --git a/src/main/java/opus/Task.java b/src/main/java/opus/tasks/Task.java similarity index 99% rename from src/main/java/opus/Task.java rename to src/main/java/opus/tasks/Task.java index 9140c8de86..833f8aab38 100644 --- a/src/main/java/opus/Task.java +++ b/src/main/java/opus/tasks/Task.java @@ -1,4 +1,4 @@ -package opus; +package opus.tasks; /** * Represents a generic task in the application. Each task has a description and diff --git a/src/main/java/opus/ToDo.java b/src/main/java/opus/tasks/ToDo.java similarity index 98% rename from src/main/java/opus/ToDo.java rename to src/main/java/opus/tasks/ToDo.java index 545170603e..ef93cd9106 100644 --- a/src/main/java/opus/ToDo.java +++ b/src/main/java/opus/tasks/ToDo.java @@ -1,4 +1,4 @@ -package opus; +package opus.tasks; /** * Represents a ToDo task, which is a basic task without any date or time associated with it. diff --git a/src/test/java/DeadlineTest.java b/src/test/java/DeadlineTest.java index 7bb1751add..4fbea1e56d 100644 --- a/src/test/java/DeadlineTest.java +++ b/src/test/java/DeadlineTest.java @@ -1,6 +1,7 @@ -import opus.Deadline; // Regular import - import org.junit.jupiter.api.Test; // Special import + +import opus.tasks.Deadline; + import static org.junit.jupiter.api.Assertions.assertEquals; // Static import public class DeadlineTest { diff --git a/src/test/java/TaskTest.java b/src/test/java/TaskTest.java index 443a40b82b..9e42cd485b 100644 --- a/src/test/java/TaskTest.java +++ b/src/test/java/TaskTest.java @@ -1,5 +1,5 @@ -import opus.ToDo; -import opus.Task; +import opus.tasks.Task; +import opus.tasks.ToDo; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; diff --git a/src/test/java/ToDoTest.java b/src/test/java/ToDoTest.java index f9d93df19f..305f815b35 100644 --- a/src/test/java/ToDoTest.java +++ b/src/test/java/ToDoTest.java @@ -1,8 +1,8 @@ -import opus.ToDo; - import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; +import opus.tasks.ToDo; + public class ToDoTest {