diff --git a/data/duke.txt b/data/duke.txt new file mode 100644 index 000000000..5da82cfb6 --- /dev/null +++ b/data/duke.txt @@ -0,0 +1,2 @@ +T | 0 | sleep +E | 0 | go to school | monday | tuesday diff --git a/docs/README.md b/docs/README.md index 47b9f984f..b5ea989aa 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,30 +1,113 @@ -# Duke User Guide -// Update the title above to match the actual product name +# Poirot User Guide -// Product screenshot goes here +Poirot is a powerful command-line task management application designed to help you organize your to-dos, deadlines, and events efficiently. -// Product intro goes here +## Adding a To-Do + +Add tasks without any specific deadline or time, allowing you to track general tasks. + +Example: `todo Buy groceries` + +This command adds a new to-do task to your list. + +``` +____________________________________________________________ +Got it. I've added this task: + [T][ ] Buy groceries +Now you have 1 tasks in the list. +____________________________________________________________ +``` ## Adding deadlines -// Describe the action and its outcome. +Add tasks with specific due dates and times to keep track of important deadlines. + +Example: `deadline Submit report /by 2023-12-31 2359` + +This command adds a new deadline task to your list. + +``` +____________________________________________________________ +Got it. I've added this task: + [D][ ] Submit report (by: Dec 31 2023, 11:59 PM) +Now you have 1 tasks in the list. +____________________________________________________________ +``` + +## Adding events + +Create events with start and end times to manage your schedule effectively. + +Example: `event Team meeting /from 2023-11-15 1400 /to 2023-11-15 1500` + +This command adds a new event to your task list. + +``` +____________________________________________________________ +Got it. I've added this task: + [E][ ] Team meeting (from: Nov 15 2023, 02:00 PM to: Nov 15 2023, 03:00 PM) +Now you have 2 tasks in the list. +____________________________________________________________ +``` + +## Listing tasks -// Give examples of usage +View all your current tasks in one place. -Example: `keyword (optional arguments)` +Example: `list` -// A description of the expected outcome goes here +This command displays all tasks currently in your list. ``` -expected output +____________________________________________________________ +1.[D][ ] Submit report (by: Dec 31 2023, 11:59 PM) +2.[E][ ] Team meeting (from: Nov 15 2023, 02:00 PM to: Nov 15 2023, 03:00 PM) +____________________________________________________________ ``` -## Feature ABC +## Marking tasks as done -// Feature details +Keep track of your progress by marking completed tasks. +Example: `mark 1` -## Feature XYZ +This command marks the first task in your list as done. + +``` +____________________________________________________________ +Nice! I've marked this task as done: + +[X] Submit report +____________________________________________________________ +``` + +## Finding tasks + +Quickly locate tasks related to specific keywords. + +Example: `find report` + +This command searches for tasks containing the word "report". + +``` +Here are the matching tasks in your list: +1. [D][X] Submit report (by: Dec 31 2023, 11:59 PM) +``` + +## Deleting tasks + +Remove tasks that are no longer needed. + +Example: `delete 2` + +This command deletes the second task in your list. + +``` +____________________________________________________________ +Noted. I've removed this task: + [E][ ] Team meeting (from: Nov 15 2023, 02:00 PM to: Nov 15 2023, 03:00 PM) +Now you have 1 tasks in the list. +____________________________________________________________ +``` -// Feature details \ No newline at end of file diff --git a/src/main/java/AddDeadlineCommand.class b/src/main/java/AddDeadlineCommand.class new file mode 100644 index 000000000..692e86ed1 Binary files /dev/null and b/src/main/java/AddDeadlineCommand.class differ diff --git a/src/main/java/AddDeadlineCommand.java b/src/main/java/AddDeadlineCommand.java new file mode 100644 index 000000000..cb24f3588 --- /dev/null +++ b/src/main/java/AddDeadlineCommand.java @@ -0,0 +1,43 @@ +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; + +/** + * Command to add a deadline task. + */ +public class AddDeadlineCommand extends Command { + private String description; + private String by; + private static final DateTimeFormatter INPUT_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HHmm"); + + /** + * Constructs an AddDeadlineCommand with the specified input. + * + * @param input The user input containing the task description and deadline. + */ + public AddDeadlineCommand(String input) { + String[] parts = input.split("/by"); + this.description = parts[0].trim().substring(9).trim(); + this.by = parts[1].trim(); + } + + /** + * Executes the command to add the deadline task to the task list. + * + * @param tasks The task list to which the task will be added. + * @param ui The user interface for displaying messages to the user. + * @param storage The storage for saving tasks to a file. + */ + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) { + try { + LocalDateTime.parse(by, INPUT_FORMAT); // Validate the date format + Task newTask = new Deadline(description, by); + tasks.add(newTask); + ui.showAddedTask(newTask, tasks.getTaskCount()); + storage.saveTasksToFile(tasks.getTasks(), tasks.getTaskCount()); + } catch (DateTimeParseException e) { + ui.showError("Invalid date format. Please use yyyy-MM-dd HHmm format."); + } + } +} diff --git a/src/main/java/AddEventCommand.class b/src/main/java/AddEventCommand.class new file mode 100644 index 000000000..6de7d0b8e Binary files /dev/null and b/src/main/java/AddEventCommand.class differ diff --git a/src/main/java/AddEventCommand.java b/src/main/java/AddEventCommand.java new file mode 100644 index 000000000..55cc60f41 --- /dev/null +++ b/src/main/java/AddEventCommand.java @@ -0,0 +1,39 @@ +/** + * Represents a command to add a new event to the task list. + * This command creates a new Event task with a description, start time, and end time. + */ +public class AddEventCommand extends Command { + private String description; + private String from; + private String to; + + /** + * Constructs a new AddEventCommand with the specified event details. + * + * @param description The description of the event. + * @param from The start time/date of the event. + * @param to The end time/date of the event. + */ + public AddEventCommand(String description, String from, String to) { + this.description = description; + this.from = from; + this.to = to; + } + + /** + * Executes the command to add a new event to the task list. + * This method creates a new Event, adds it to the task list, updates the UI, + * and saves the updated task list to storage. + * + * @param tasks The TaskList to which the new event will be added. + * @param ui The Ui object used to display output to the user. + * @param storage The Storage object used to save the updated task list. + */ + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) { + Task newTask = new Event(description, from, to); + tasks.add(newTask); + ui.showAddedTask(newTask, tasks.getTaskCount()); + storage.saveTasksToFile(tasks.getTasks(), tasks.getTaskCount()); + } +} diff --git a/src/main/java/AddTodoCommand.class b/src/main/java/AddTodoCommand.class new file mode 100644 index 000000000..215b4bf56 Binary files /dev/null and b/src/main/java/AddTodoCommand.class differ diff --git a/src/main/java/AddTodoCommand.java b/src/main/java/AddTodoCommand.java new file mode 100644 index 000000000..bf922e5db --- /dev/null +++ b/src/main/java/AddTodoCommand.java @@ -0,0 +1,30 @@ +/** + * Command to add a todo task. + */ +public class AddTodoCommand extends Command { + private String description; + + /** + * Constructs an AddTodoCommand with the specified description. + * + * @param description The description of the todo task. + */ + public AddTodoCommand(String description) { + this.description = description; + } + + /** + * Executes the command to add the todo task to the task list. + * + * @param tasks The task list to which the task will be added. + * @param ui The user interface for displaying messages to the user. + * @param storage The storage for saving tasks to a file. + */ + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) { + Task newTask = new Todo(description); + tasks.add(newTask); + ui.showAddedTask(newTask, tasks.getTaskCount()); + storage.saveTasksToFile(tasks.getTasks(), tasks.getTaskCount()); + } +} diff --git a/src/main/java/Command.class b/src/main/java/Command.class new file mode 100644 index 000000000..3b58bf141 Binary files /dev/null and b/src/main/java/Command.class differ diff --git a/src/main/java/Command.java b/src/main/java/Command.java new file mode 100644 index 000000000..7f959fbf4 --- /dev/null +++ b/src/main/java/Command.java @@ -0,0 +1,27 @@ +/** + * Represents an abstract command in the task management system. + * This class serves as a base for all concrete command implementations. + */ +public abstract class Command { + /** + * Executes the command. + * This method should be implemented by all concrete command classes to define + * their specific behavior. + * + * @param tasks The TaskList on which the command operates. + * @param ui The Ui object used to interact with the user. + * @param storage The Storage object used to save and load tasks. + * @throws PoirotException If an error occurs during command execution. + */ + public abstract void execute(TaskList tasks, Ui ui, Storage storage) throws PoirotException; + + /** + * Checks if this command should exit the program. + * By default, commands do not exit the program. + * + * @return true if the program should exit after this command, false otherwise. + */ + public boolean isExit() { + return false; + } +} diff --git a/src/main/java/Deadline.class b/src/main/java/Deadline.class new file mode 100644 index 000000000..3169ee89f Binary files /dev/null and b/src/main/java/Deadline.class differ diff --git a/src/main/java/Deadline.java b/src/main/java/Deadline.java new file mode 100644 index 000000000..f73f6a1c7 --- /dev/null +++ b/src/main/java/Deadline.java @@ -0,0 +1,43 @@ +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +/** + * Represents a deadline task in the task management system. + * A deadline task is a task that needs to be completed by a specific date and time. + */ +public class Deadline extends Task { + private LocalDateTime by; + private static final DateTimeFormatter INPUT_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HHmm"); + private static final DateTimeFormatter OUTPUT_FORMAT = DateTimeFormatter.ofPattern("MMM d yyyy, hh:mm a"); + + /** + * Constructs a new Deadline task with the given description and deadline. + * + * @param description The description of the deadline task. + * @param by The deadline date and time in the format "yyyy-MM-dd HHmm". + */ + public Deadline(String description, String by) { + super(description); + this.by = LocalDateTime.parse(by, INPUT_FORMAT); + } + + /** + * Returns a string representation of the deadline task. + * + * @return A formatted string representing the deadline task, including its status, description, and deadline. + */ + @Override + public String toString() { + return "[D][" + getStatusIcon() + "] " + description + " (by: " + by.format(OUTPUT_FORMAT) + ")"; + } + + /** + * Returns a string representation of the deadline task for file storage. + * + * @return A formatted string representing the deadline task for saving to a file. + */ + @Override + public String toFileString() { + return "D | " + (isDone ? "1" : "0") + " | " + description + " | " + by.format(INPUT_FORMAT); + } +} diff --git a/src/main/java/DeleteCommand.class b/src/main/java/DeleteCommand.class new file mode 100644 index 000000000..c040f7cf9 Binary files /dev/null and b/src/main/java/DeleteCommand.class differ diff --git a/src/main/java/DeleteCommand.java b/src/main/java/DeleteCommand.java new file mode 100644 index 000000000..17c960324 --- /dev/null +++ b/src/main/java/DeleteCommand.java @@ -0,0 +1,34 @@ +/** + * Command to delete a task from the task list. + */ +public class DeleteCommand extends Command { + private int index; + + /** + * Constructs a DeleteCommand with the specified index. + * + * @param index The index of the task to delete. + */ + public DeleteCommand(int index) { + this.index = index; + } + + /** + * Executes the command to delete the specified task from the task list. + * + * @param tasks The task list from which the task will be deleted. + * @param ui The user interface for displaying messages to the user. + * @param storage The storage for saving tasks to a file. + * @throws PoirotException If the index is out of bounds. + */ + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) throws PoirotException { + if (index < 1 || index > tasks.getTaskCount()) { + throw new PoirotException("Task number is out of bounds!"); + } + Task taskToDelete = tasks.getTask(index - 1); + tasks.delete(index - 1); + ui.showDeletedTask(taskToDelete, tasks.getTaskCount()); + storage.saveTasksToFile(tasks.getTasks(), tasks.getTaskCount()); + } +} diff --git a/src/main/java/Duke.class b/src/main/java/Duke.class new file mode 100644 index 000000000..4fa6a3709 Binary files /dev/null and b/src/main/java/Duke.class differ 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/Event.class b/src/main/java/Event.class new file mode 100644 index 000000000..44b00bde7 Binary files /dev/null and b/src/main/java/Event.class differ diff --git a/src/main/java/Event.java b/src/main/java/Event.java new file mode 100644 index 000000000..d5c4cdda8 --- /dev/null +++ b/src/main/java/Event.java @@ -0,0 +1,44 @@ +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +public class Event extends Task { + private LocalDateTime from; + private LocalDateTime to; + private static final DateTimeFormatter inputFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HHmm"); + private static final DateTimeFormatter outputFormatter = DateTimeFormatter.ofPattern("MMM dd yyyy, HH:mm"); + + /** + * Constructs an Event with the specified description, from, and to time. + * + * @param description The description of the event. + * @param from The starting time of the event in 'yyyy-MM-dd HHmm' format. + * @param to The ending time of the event in 'yyyy-MM-dd HHmm' format. + */ + public Event(String description, String from, String to) { + super(description); + this.from = LocalDateTime.parse(from, inputFormatter); + this.to = LocalDateTime.parse(to, inputFormatter); + } + + /** + * Returns a string representation of the event task. + * + * @return A formatted string of the event task. + */ + @Override + public String toString() { + return "[E][" + getStatusIcon() + "] " + description + " (from: " + + from.format(outputFormatter) + " to: " + to.format(outputFormatter) + ")"; + } + + /** + * Returns a string representation for saving the event task to a file. + * + * @return A formatted string of the event task for file storage. + */ + @Override + public String toFileString() { + return "E | " + (isDone ? "1" : "0") + " | " + description + " | " + + from.format(inputFormatter) + " | " + to.format(inputFormatter); + } +} diff --git a/src/main/java/ExitCommand.class b/src/main/java/ExitCommand.class new file mode 100644 index 000000000..9a437394f Binary files /dev/null and b/src/main/java/ExitCommand.class differ diff --git a/src/main/java/ExitCommand.java b/src/main/java/ExitCommand.java new file mode 100644 index 000000000..611ff78f4 --- /dev/null +++ b/src/main/java/ExitCommand.java @@ -0,0 +1,26 @@ +/** + * Command to exit the application. + */ +public class ExitCommand extends Command { + /** + * Executes the command to exit the application. + * + * @param tasks The task list (not used). + * @param ui The user interface for displaying messages to the user. + * @param storage The storage for saving tasks to a file (not used). + */ + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) { + ui.showExit(); + } + + /** + * Indicates whether this command is an exit command. + * + * @return true, since this command is an exit command. + */ + @Override + public boolean isExit() { + return true; + } +} diff --git a/src/main/java/FindCommand.class b/src/main/java/FindCommand.class new file mode 100644 index 000000000..764aeea33 Binary files /dev/null and b/src/main/java/FindCommand.class differ diff --git a/src/main/java/FindCommand.java b/src/main/java/FindCommand.java new file mode 100644 index 000000000..65ae8ce72 --- /dev/null +++ b/src/main/java/FindCommand.java @@ -0,0 +1,34 @@ +import java.util.ArrayList; + +/** + * Command to find tasks matching a specified keyword. + */ +public class FindCommand extends Command { + private String keyword; + + /** + * Constructs a FindCommand with the specified keyword. + * + * @param keyword The keyword to search for in the task descriptions. + */ + public FindCommand(String keyword) { + this.keyword = keyword.trim(); + } + + /** + * Executes the command to find tasks in the task list that match the keyword. + * + * @param tasks The task list to search for matching tasks. + * @param ui The user interface for displaying messages to the user. + * @param storage The storage for saving tasks to a file (not used). + */ + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) { + ArrayList foundTasks = tasks.findTasks(keyword); + if (foundTasks.isEmpty()) { + ui.showMessage("No tasks found with the keyword: " + keyword); + } else { + ui.showFoundTasks(foundTasks); + } + } +} diff --git a/src/main/java/ListCommand.class b/src/main/java/ListCommand.class new file mode 100644 index 000000000..f54b80436 Binary files /dev/null and b/src/main/java/ListCommand.class differ diff --git a/src/main/java/ListCommand.java b/src/main/java/ListCommand.java new file mode 100644 index 000000000..e60852602 --- /dev/null +++ b/src/main/java/ListCommand.java @@ -0,0 +1,16 @@ +/** + * Command to list all tasks in the task list. + */ +public class ListCommand extends Command { + /** + * Executes the command to display the list of tasks. + * + * @param tasks The task list to display. + * @param ui The user interface for displaying messages to the user. + * @param storage The storage for saving tasks to a file (not used). + */ + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) { + ui.showTaskList(tasks.getTasks(), tasks.getTaskCount()); + } +} diff --git a/src/main/java/META-INF/MANIFEST.MF b/src/main/java/META-INF/MANIFEST.MF new file mode 100644 index 000000000..bf4a2bebf --- /dev/null +++ b/src/main/java/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: Poirot + diff --git a/src/main/java/MarkCommand.class b/src/main/java/MarkCommand.class new file mode 100644 index 000000000..54dcb1e1b Binary files /dev/null and b/src/main/java/MarkCommand.class differ diff --git a/src/main/java/MarkCommand.java b/src/main/java/MarkCommand.java new file mode 100644 index 000000000..0dde56510 --- /dev/null +++ b/src/main/java/MarkCommand.java @@ -0,0 +1,33 @@ +/** + * Command to mark a task as done. + */ +public class MarkCommand extends Command { + private int index; + + /** + * Constructs a MarkCommand with the specified index. + * + * @param index The index of the task to mark as done. + */ + public MarkCommand(int index) { + this.index = index; + } + + /** + * Executes the command to mark the specified task as done. + * + * @param tasks The task list containing the task to mark. + * @param ui The user interface for displaying messages to the user. + * @param storage The storage for saving tasks to a file. + * @throws PoirotException If the index is out of bounds. + */ + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) throws PoirotException { + if (index < 1 || index > tasks.getTaskCount()) { + throw new PoirotException("Task number is out of bounds!"); + } + tasks.markTask(index - 1, true); + ui.showMarkTask(tasks.getTask(index - 1)); + storage.saveTasksToFile(tasks.getTasks(), tasks.getTaskCount()); + } +} diff --git a/src/main/java/Parser.class b/src/main/java/Parser.class new file mode 100644 index 000000000..e80c83dbc Binary files /dev/null and b/src/main/java/Parser.class differ diff --git a/src/main/java/Parser.java b/src/main/java/Parser.java new file mode 100644 index 000000000..6d08d2916 --- /dev/null +++ b/src/main/java/Parser.java @@ -0,0 +1,92 @@ +/** + * The Parser class is responsible for parsing user input and creating appropriate Command objects. + */ +public class Parser { + /** + * Parses the input string and returns the corresponding Command object. + * + * @param input The user input string to be parsed. + * @return A Command object based on the parsed input. + * @throws PoirotException If the input is invalid or cannot be parsed. + */ + public static Command parse(String input) throws PoirotException { + String[] list_input = input.split(" "); + switch (list_input[0]) { + case "list": + return new ListCommand(); + case "mark": + return new MarkCommand(Integer.parseInt(list_input[1])); + case "unmark": + return new UnmarkCommand(Integer.parseInt(list_input[1])); + case "todo": + return new AddTodoCommand(input.substring(5).trim()); + case "deadline": + return parseDeadlineCommand(input); + case "event": + return parseEventCommand(input); + case "delete": + return new DeleteCommand(Integer.parseInt(list_input[1])); + case "find": + return new FindCommand(input); + case "bye": + return new ExitCommand(); + default: + throw new PoirotException("Unknown command!"); + } + } + /** + * Parses the deadline command input and creates an AddDeadlineCommand object. + * + * @param input The deadline command input string. + * @return An AddDeadlineCommand object. + * @throws PoirotException If the deadline format is invalid. + */ + private static Command parseDeadlineCommand(String input) throws PoirotException { + try { + String[] parts = input.split("/by"); + if (parts.length < 2) { + throw new PoirotException("Invalid deadline format! Use: deadline /by "); + } + String description = parts[0].trim(); + String by = parts[1].trim(); + return new AddDeadlineCommand(description + " /by " + by); + } catch (ArrayIndexOutOfBoundsException e) { + throw new PoirotException("Invalid format for deadline command."); + } + } + /** + * Parses the event command input and creates an AddEventCommand object. + * + * @param input The event command input string. + * @return An AddEventCommand object. + * @throws PoirotException If the event format is invalid. + */ + private static Command parseEventCommand(String input) throws PoirotException { + try { + if (!input.contains("/from") || !input.contains("/to")) { + throw new PoirotException("Invalid event format! Use: event /from /to "); + } + + String[] fromParts = input.split("/from"); + if (fromParts.length < 2) { + throw new PoirotException("Invalid event format! Use: event /from /to "); + } + + String description = fromParts[0].trim().substring(6).trim(); + + String[] toParts = fromParts[1].split("/to"); + if (toParts.length < 2) { + throw new PoirotException("Invalid event format! Use: event /from /to "); + } + + String from = toParts[0].trim(); + String to = toParts[1].trim(); + + return new AddEventCommand(description, from, to); + } catch (StringIndexOutOfBoundsException e) { + throw new PoirotException("Error extracting event description. Please check your input format."); + } + } + + +} diff --git a/src/main/java/Poirot.class b/src/main/java/Poirot.class new file mode 100644 index 000000000..da34a1d60 Binary files /dev/null and b/src/main/java/Poirot.class differ diff --git a/src/main/java/Poirot.java b/src/main/java/Poirot.java new file mode 100644 index 000000000..02fd881e9 --- /dev/null +++ b/src/main/java/Poirot.java @@ -0,0 +1,58 @@ +/** + * The main class for the Poirot task management application. + * This class initializes the application, loads saved tasks, and runs the main command loop. + */ +public class Poirot { + + private Storage storage; + private TaskList tasks; + private Ui ui; + + /** + * Constructs a new Poirot application instance. + * Initializes the UI, storage, and task list, and loads any previously saved tasks. + * + * @param filePath The file path for storing and loading tasks. + */ + public Poirot(String filePath) { + ui = new Ui(); + storage = new Storage(filePath); + tasks = new TaskList(); + + Task[] loadedTasks = storage.loadTasksFromFile(); + for (Task task : loadedTasks) { + if (task != null) { + tasks.add(task); + } + } + } + + /** + * Runs the main command loop of the application. + * Continuously reads user input, executes commands, and displays results until an exit command is given. + */ + public void run() { + ui.showWelcome(); + boolean isExit = false; + while (!isExit) { + try { + String fullCommand = ui.readCommand(); + Command command = Parser.parse(fullCommand); + command.execute(tasks, ui, storage); + isExit = command.isExit(); + } catch (PoirotException e) { + ui.showError(e.getMessage()); + } + } + } + + /** + * The main entry point for the Poirot application. + * Creates a new Poirot instance and starts running it. + * + * @param args Command line arguments (not used). + */ + public static void main(String[] args) { + new Poirot("./data/duke.txt").run(); + } +} diff --git a/src/main/java/PoirotException.class b/src/main/java/PoirotException.class new file mode 100644 index 000000000..92234d430 Binary files /dev/null and b/src/main/java/PoirotException.class differ diff --git a/src/main/java/PoirotException.java b/src/main/java/PoirotException.java new file mode 100644 index 000000000..6817ab4a6 --- /dev/null +++ b/src/main/java/PoirotException.java @@ -0,0 +1,14 @@ +/** + * Represents a custom exception for the Poirot task management application. + * This exception is used to handle application-specific errors and provide meaningful error messages. + */ +public class PoirotException extends Exception { + /** + * Constructs a new PoirotException with the specified error message. + * + * @param message The detailed error message explaining the reason for the exception. + */ + public PoirotException(String message) { + super(message); + } +} diff --git a/src/main/java/Storage.class b/src/main/java/Storage.class new file mode 100644 index 000000000..15438ee4e Binary files /dev/null and b/src/main/java/Storage.class differ diff --git a/src/main/java/Storage.java b/src/main/java/Storage.java new file mode 100644 index 000000000..f4a177e7f --- /dev/null +++ b/src/main/java/Storage.java @@ -0,0 +1,90 @@ +import java.io.*; +import java.util.Scanner; + +/** + * Handles loading and saving tasks to a file. + */ +public class Storage { + private String filePath; + + /** + * Constructs a Storage object with the specified file path. + * + * @param filePath The path of the file to load/save tasks. + */ + public Storage(String filePath) { + this.filePath = filePath; + } + + /** + * Loads tasks from the file and returns them as an array. + * + * @return An array of loaded tasks. + */ + public Task[] loadTasksFromFile() { + Task[] tasks = new Task[100]; + int lastIndex = 0; + + try { + File file = new File(filePath); + if (!file.exists()) { + return tasks; + } + + Scanner scanner = new Scanner(file); + while (scanner.hasNextLine()) { + String line = scanner.nextLine(); + String[] parts = line.split(" \\| "); + String taskType = parts[0]; + boolean isDone = parts[1].equals("1"); + String description = parts[2]; + + switch (taskType) { + case "T": + tasks[lastIndex] = new Todo(description); + break; + case "D": + String by = parts[3]; + tasks[lastIndex] = new Deadline(description, by); + break; + case "E": + String from = parts[3]; + String to = parts[4]; + tasks[lastIndex] = new Event(description, from, to); + break; + } + + tasks[lastIndex].setDone(isDone); + lastIndex++; + } + scanner.close(); + } catch (IOException e) { + System.out.println("Error loading tasks from file: " + e.getMessage()); + } + + return tasks; + } + + /** + * Saves the specified tasks to the file. + * + * @param tasks The tasks to save. + * @param lastIndex The number of tasks to save. + */ + public void saveTasksToFile(Task[] tasks, int lastIndex) { + try { + File dir = new File("./data"); + if (!dir.exists()) { + dir.mkdirs(); + } + + FileWriter writer = new FileWriter(filePath); + for (int i = 0; i < lastIndex; i++) { + writer.write(tasks[i].toFileString() + "\n"); + } + writer.close(); + } catch (IOException e) { + System.out.println("Error saving tasks to file: " + e.getMessage()); + } + } +} diff --git a/src/main/java/Task.class b/src/main/java/Task.class new file mode 100644 index 000000000..a60f77cd6 Binary files /dev/null and b/src/main/java/Task.class differ diff --git a/src/main/java/Task.java b/src/main/java/Task.java new file mode 100644 index 000000000..8f9059125 --- /dev/null +++ b/src/main/java/Task.java @@ -0,0 +1,48 @@ +/** + * Represents a task with a description and completion status. + */ +abstract class Task { + protected String description; + protected boolean isDone; + + /** + * Constructs a Task with the specified description. + * + * @param description The description of the task. + */ + public Task(String description) { + this.description = description; + this.isDone = false; + } + + public String getDescription() { + return description; + } + + /** + * Returns the status icon of the task. + * + * @return A string representing the task status icon. + */ + public String getStatusIcon() { + return (isDone ? "X" : " "); + } + + /** + * Marks the task as done or not done. + * + * @param isDone true to mark the task as done, false otherwise. + */ + public void setDone(boolean isDone) { + this.isDone = isDone; + } + + public abstract String toString(); + + /** + * Returns a string representation for saving the task to a file. + * + * @return A formatted string of the task for file storage. + */ + public abstract String toFileString(); +} diff --git a/src/main/java/TaskList.class b/src/main/java/TaskList.class new file mode 100644 index 000000000..2776cf037 Binary files /dev/null and b/src/main/java/TaskList.class differ diff --git a/src/main/java/TaskList.java b/src/main/java/TaskList.java new file mode 100644 index 000000000..aa9abad5b --- /dev/null +++ b/src/main/java/TaskList.java @@ -0,0 +1,81 @@ +import java.util.ArrayList; + +/** + * Represents a list of tasks. + */ + +public class TaskList { + private Task[] tasks = new Task[100]; + private int lastIndex = 0; + + /** + * Adds a task to the task list. + * + * @param task The task to add. + */ + public void add(Task task) { + tasks[lastIndex] = task; + lastIndex++; + } + + public ArrayList findTasks(String keyword) { + ArrayList foundTasks = new ArrayList<>(); + for (int i = 0; i < lastIndex; i++) { + if (tasks[i].getDescription().toLowerCase().contains(keyword.toLowerCase())) { + foundTasks.add(tasks[i]); + } + } + return foundTasks; + } + + /** + * Deletes a task from the task list at the specified index. + * + * @param index The index of the task to delete. + */ + public void delete(int index) { + for (int i = index; i < lastIndex - 1; i++) { + tasks[i] = tasks[i + 1]; + } + tasks[lastIndex - 1] = null; + lastIndex--; + } + + /** + * Returns the list of tasks. + * + * @return The list of tasks. + */ + public Task[] getTasks() { + return tasks; + } + + /** + * Returns the number of tasks in the task list. + * + * @return The count of tasks. + */ + public int getTaskCount() { + return lastIndex; + } + + /** + * Returns the task at the specified index. + * + * @param index The index of the task to retrieve. + * @return The task at the specified index. + */ + public Task getTask(int index) { + return tasks[index]; + } + + /** + * Marks the specified task as done. + * + * @param index The index of the task to mark. + * @param isDone true to mark as done, false otherwise. + */ + public void markTask(int index, boolean isDone) { + tasks[index].setDone(isDone); + } +} diff --git a/src/main/java/Todo.class b/src/main/java/Todo.class new file mode 100644 index 000000000..4f529c709 Binary files /dev/null and b/src/main/java/Todo.class differ diff --git a/src/main/java/Todo.java b/src/main/java/Todo.java new file mode 100644 index 000000000..a260ded37 --- /dev/null +++ b/src/main/java/Todo.java @@ -0,0 +1,33 @@ +/** + * Represents a simple todo task without any specific date or time. + */ +public class Todo extends Task { + /** + * Constructs a Todo with the specified description. + * + * @param description The description of the todo task. + */ + public Todo(String description) { + super(description); + } + + /** + * Returns the string representation of the todo task. + * + * @return The formatted string of the todo task. + */ + @Override + public String toString() { + return "[T][" + getStatusIcon() + "] " + description; + } + + /** + * Returns a string representation for saving the todo task to a file. + * + * @return A formatted string of the todo task for file storage. + */ + @Override + public String toFileString() { + return "T | " + (isDone ? "1" : "0") + " | " + description; + } +} diff --git a/src/main/java/Ui.class b/src/main/java/Ui.class new file mode 100644 index 000000000..e8bfcbfbd Binary files /dev/null and b/src/main/java/Ui.class differ diff --git a/src/main/java/Ui.java b/src/main/java/Ui.java new file mode 100644 index 000000000..712af4ae1 --- /dev/null +++ b/src/main/java/Ui.java @@ -0,0 +1,132 @@ +import java.util.Scanner; +import java.util.ArrayList; + +/** + * Handles the user interface interactions for the task management application. + */ +public class Ui { + private static final String LINE = "____________________________________________________________\n"; + + public void showLine() { + System.out.println(LINE); + } + + public void showWelcome() { + showLine(); + System.out.println("Hello! I'm POIROT\nWhat can I do for you?"); + showLine(); + } + + /** + * Displays a goodbye message when exiting the application. + */ + public void showExit() { + showLine(); + System.out.println("Bye. Hope to see you again soon!"); + showLine(); + } + + public String readCommand() { + Scanner scanner = new Scanner(System.in); + return scanner.nextLine(); + } + + /** + * Displays an error message to the user. + * + * @param message The error message to display. + */ + public void showError(String message) { + showLine(); + System.out.println(message); + showLine(); + } + + /** + * Displays a message indicating that a task has been added. + * + * @param task The task that was added. + * @param taskCount The current count of tasks in the list. + */ + public void showAddedTask(Task task, int taskCount) { + showLine(); + System.out.println("Got it. I've added this task:"); + System.out.println(" " + task); + System.out.println("Now you have " + taskCount + " tasks in the list."); + showLine(); + } + + /** + * Displays a message indicating that a task has been deleted. + * + * @param task The task that was deleted. + * @param taskCount The current count of tasks in the list. + */ + public void showDeletedTask(Task task, int taskCount) { + showLine(); + System.out.println("Noted. I've removed this task:"); + System.out.println(" " + task); + System.out.println("Now you have " + taskCount + " tasks in the list."); + showLine(); + } + + /** + * Displays the list of tasks to the user. + * + * @param tasks The list of tasks to display. + * @param lastIndex The count of tasks in the list. + */ + public void showTaskList(Task[] tasks, int lastIndex) { + showLine(); + if (lastIndex == 0) { + System.out.println("No actions available"); + } else { + for (int i = 0; i < lastIndex; i++) { + System.out.println((i + 1) + "." + tasks[i]); + } + } + showLine(); + } + + /** + * Displays a message indicating that a task has been marked as done. + * + * @param task The task that was marked as done. + */ + public void showMarkTask(Task task) { + showLine(); + System.out.println("Nice! I've marked this task as done:\n"); + System.out.print("[" + task.getStatusIcon() + "] "); + System.out.println(task.getDescription()); + showLine(); + } + + /** + * Displays a message indicating that a task has been unmarked as done. + * + * @param task The task that was unmarked as done. + */ + public void showUnmarkTask(Task task) { + showLine(); + System.out.println("OK, I've marked this task as not done yet:\n"); + System.out.print("[" + task.getStatusIcon() + "] "); + System.out.println(task.getDescription()); + showLine(); + } + + /** + * Displays the tasks found that match the keyword. + * + * @param tasks The list of found tasks to display. + */ + public void showFoundTasks(ArrayList tasks) { + System.out.println("Here are the matching tasks in your list:"); + for (int i = 0; i < tasks.size(); i++) { + System.out.println((i + 1) + ". " + tasks.get(i).toString()); + } + } + + public void showMessage(String message) { + System.out.println(message); + } +} diff --git a/src/main/java/UnmarkCommand.class b/src/main/java/UnmarkCommand.class new file mode 100644 index 000000000..5a5a0dfb0 Binary files /dev/null and b/src/main/java/UnmarkCommand.class differ diff --git a/src/main/java/UnmarkCommand.java b/src/main/java/UnmarkCommand.java new file mode 100644 index 000000000..4f1383111 --- /dev/null +++ b/src/main/java/UnmarkCommand.java @@ -0,0 +1,37 @@ +/** + * Represents a command to unmark a task as not completed in the Poirot task management system. + * This command changes the status of a task from completed to not completed. + */ +public class UnmarkCommand extends Command { + private int index; + + /** + * Constructs a new UnmarkCommand with the specified task index. + * + * @param index The index of the task to be unmarked (1-based indexing). + */ + public UnmarkCommand(int index) { + this.index = index; + } + + /** + * Executes the unmark command. + * This method unmarks the specified task as not completed, updates the UI, + * and saves the changes to storage. + * + * @param tasks The TaskList containing all tasks. + * @param ui The UI object used to display messages to the user. + * @param storage The Storage object used to save tasks. + * @throws PoirotException If the task index is out of bounds. + */ + + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) throws PoirotException { + if (index < 1 || index > tasks.getTaskCount()) { + throw new PoirotException("Task number is out of bounds!"); + } + tasks.markTask(index - 1, false); + ui.showUnmarkTask(tasks.getTask(index - 1)); + storage.saveTasksToFile(tasks.getTasks(), tasks.getTaskCount()); + } +} diff --git a/src/main/java/data/duke.txt b/src/main/java/data/duke.txt new file mode 100644 index 000000000..02a909e61 --- /dev/null +++ b/src/main/java/data/duke.txt @@ -0,0 +1,11 @@ +T | 0 | sleep +T | 0 | work +T | 0 | go to work +D | 0 | t | 2025-04-01 1800 +E | 0 | party | 2025-04-01 1800 | 2025-04-01 2100 +D | 0 | work | 2025-04-01 1800 +T | 0 | mini project +D | 0 | uation | 2024-10-31 2359 +T | 0 | go to work +D | 0 | peer evaluation | 2024-10-31 2359 +E | 0 | new party | 2024-11-21 1700 | 2024-11-23 1800 diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 657e74f6e..b4f2ce97e 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,7 +1,42 @@ -Hello from - ____ _ -| _ \ _ _| | _____ -| | | | | | | |/ / _ \ -| |_| | |_| | < __/ -|____/ \__,_|_|\_\___| +____________________________________________________________ +Hello! I'm POIROT + +What can I do for you? +____________________________________________________________ + +todo borrow book +____________________________________________________________ + +Got it. I've added this task: + [T][ ] borrow book +Now you have 1 tasks in the list. +____________________________________________________________ + +list +____________________________________________________________ + +1.[T][ ] borrow book +____________________________________________________________ + +deadline return book /by Sunday +____________________________________________________________ + +Got it. I've added this task: + [D][ ] return book (by: Sunday) +Now you have 2 tasks in the list. +____________________________________________________________ + +event project meeting /from Mon 2pm /to 4pm +____________________________________________________________ + +Got it. I've added this task: + [E][ ] project meeting (from: Mon 2pm to: 4pm) +Now you have 3 tasks in the list. +____________________________________________________________ + +bye +____________________________________________________________ + +Bye. Hope to see you again soon! +____________________________________________________________ \ No newline at end of file diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index e69de29bb..30b2c284e 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -0,0 +1,5 @@ +todo borrow book +list +deadline return book /by Sunday +event project meeting /from Mon 2pm /to 4pm +bye \ No newline at end of file diff --git a/text-ui-test/runtest.bat b/text-ui-test/runtest.bat index 087374464..57b286e0b 100644 --- a/text-ui-test/runtest.bat +++ b/text-ui-test/runtest.bat @@ -7,7 +7,7 @@ REM delete output from previous run if exist ACTUAL.TXT del ACTUAL.TXT REM compile the code into the bin folder -javac -cp ..\src\main\java -Xlint:none -d ..\bin ..\src\main\java\*.java +javac -cp ..\src\main\java -Xlint:none -d ..\bin ..\src\main\java\Poirot.java IF ERRORLEVEL 1 ( echo ********** BUILD FAILURE ********** exit /b 1 @@ -15,7 +15,7 @@ IF ERRORLEVEL 1 ( REM no error here, errorlevel == 0 REM run the program, feed commands from input.txt file and redirect the output to the ACTUAL.TXT -java -classpath ..\bin Duke < input.txt > ACTUAL.TXT +java -classpath ..\bin Poirot < input.txt > ACTUAL.TXT REM compare the output to the expected output -FC ACTUAL.TXT EXPECTED.TXT +FC ACTUAL.TXT EXPECTED.TXT \ No newline at end of file