diff --git a/docs/CapyScreenshot.png b/docs/CapyScreenshot.png new file mode 100644 index 000000000..fe1d9b525 Binary files /dev/null and b/docs/CapyScreenshot.png differ diff --git a/docs/README.md b/docs/README.md index 47b9f984f..999f64fe6 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,30 +1,317 @@ -# Duke User Guide +# Capy Task Manager User Guide -// Update the title above to match the actual product name +![Product Screenshot](CapyScreenshot.png) -// Product screenshot goes here +## Description +Capy is a simple yet powerful task management application designed to help users organize and track their tasks +effectively. With its intuitive command-line interface, users can add, delete, search, and manage tasks like +ToDos, Deadlines, and Events. It also supports persistent storage, so your tasks are always saved. -// Product intro goes here +--- -## Adding deadlines +## Quick Start -// Describe the action and its outcome. +1. **Install Java**: Ensure you have Java 17 or above installed on your computer. +2. **Download the Application**: Download the latest `.jar` file from [here](https://github.com/Hansel-K/ip/releases/tag/A-Release). +3. **Set Up**: Copy the `.jar` file to the folder you want to use as the home folder for Capy Task Manager. +4. **Run the Application**: + - Open a command terminal. + - Navigate (`cd`) into the folder where you placed the `.jar` file. + - Run the application with: + ```bash + java -jar capy.jar + ``` +5. **Using the Application**: The application is now running, and you can start entering commands! -// Give examples of usage +--- -Example: `keyword (optional arguments)` +## Key Features +- **ToDo**: Add a task without any time or due date. +- **Deadline**: Add a task with a specific due date or time. +- **Event**: Add a task with a defined start and end date/time. +- **View All Tasks**: Display the list of all tasks saved. +- **Mark Task as Done**: Mark a task as completed. +- **Unmark Task as Not Done**: Unmark a task as incomplete. +- **Delete Task**: Remove a task from the list of tasks saved. +- **Find Tasks**: Find tasks by keywords and display a list of tasks with matching keywords. +- **Exit Program**: Terminate the program. -// A description of the expected outcome goes here +## Command Format +### Notes: +- Words in **UPPER_CASE** are placeholders for user-supplied parameters. + Example: `todo TASK_A` means the `todo` command and `TASK_A` is the task description you provide, such as + `todo CS2113 Quiz`. + +--- + +## Managing Tasks + +### 1. Adding a ToDo Task: `todo` +Adds a task without any time or due date. + +Format: +```bash +todo TASK +``` + +Example Input: +```bash +todo CS2113 Quiz +``` + +Expected Output: +```bash +Got it. I've added this task: + [T][ ] Read a book +Now you have 1 tasks in the list. +``` +--- + +### 2. Adding a Deadline Task : `deadline` +Add tasks with a specific due date or time. + +Format: +```bash +deadline TASK /by DUE_DATE +``` + +Example Input: +```bash +deadline CS2113 Quiz /by Friday 1159 +``` + +Expected Output: +```bash +Got it. I've added this task: + [D][ ] CS2113 Quiz (by: Friday 1159) +Now you have 2 tasks in the list. +``` + +--- + +### 3. Adding an Event Task: `event` +Add tasks with a defined start and end date/time. + +Format: +```bash +event TASK /from START /to END +``` + +Example Input: +```bash +event CS2113 Tutorial /from Wed 12pm /to 1pm +``` + +Expected Output: +```bash +Got it. I've added this task: + [E][ ] CS2113 Tutorial (from: Wed 12pm to: 1pm) +Now you have 3 tasks in the list. ``` -expected output +--- + +### 4. View All Tasks: `list` +View all tasks in the list along with their types and statuses. + +Format: +```bash +list +``` + +Example Input: +```bash +list +``` + +Expected Output: +```bash +Here are the tasks in your list: +1. [T][ ] CS2113 Quiz +2. [D][ ] CS2113 Quiz (by: Friday 1159) +3. [E][ ] CS2113 Tutorial (from: Wed 12pm to: 1pm) +``` + +--- + +### 5. Marking a Task as Done: `mark` +Marks the task at the specified index within the task list as completed. + +Format: +```bash +mark TASK_INDEX +``` + +Example Input: +```bash +mark 1 ``` -## Feature ABC +Expected Output: +```bash +Nice! I've marked this task as done: + [T][X] CS2113 Quiz +``` + +--- + +### 6. Unmarking a Task as Not Done: `unmark` +Unmarks a task at the specified index within the task list as incomplete. + +Format: +```bash +unmark TASK_INDEX +``` + +Example Input: +```bash +unmark 1 +``` + +Expected Output: +```bash +Ok, I've marked this task as not done yet: + [T][ ] CS2113 Quiz +``` + +--- + +### 7. Deleting a Task: `delete` +Deletes a task at the specified index within the task list. + +Format: +```bash +delete TASK_INDEX +``` + +Example Input: +```bash +delete 1 +``` + +Expected Output: +```bash +Noted. I've removed this task: + [T][ ] CS2113 Quiz +Now you have 2 tasks in the list. +``` + +--- + +### 8. Finding Tasks: `find` +Find tasks by keywords and display a list of tasks with matching keywords. + +Format: +```bash +find KEYWORDS +``` + +Example Input: +```bash +find CS2113 Quiz +``` + +Expected Output: +```bash +Here are the matching tasks in your list: +1. [D][ ] CS2113 Quiz (by: Friday 1159) +``` + +--- + +### 9. Exiting the Program: `bye` +Terminate the program. + +Format: +```bash +bye +``` + +Example Input: +```bash +bye +``` + +Expected Output: +```bash +Bye. Hope to see you again soon! +``` + +--- + +## Error Handling + +### Invalid Commands +If the program does not recognise a command, it will return the following error message: + +```bash +Oops! Command not Recognised! Please try a valid command! +``` + +### Missing Arguments +If a command is entered without some required arguments (e.g. `todo` without a task description), +the system will notify you your command is missing some details: + +```bash +Oops! Seems like the command is missing some details! +``` + +#### Notes: +There are also different error messages for specific cases when the missing argument can be identified. + +- Missing Description: +```bash +Oops! Seems like the command is missing a description! +``` + +- Missing Task Number: +```bash +Oops! Seems like the command is missing a task number! +``` + +### Out-of-Range Task Indexes +If you try to mark, unmark, or delete a task with an index that does not exist within the list of tasks +(e.g. trying to delete task 5 when there are only 3 tasks), the program will inform you of your mistake: + +```bash +Oops! Invalid task number! +``` + +### Invalid Data Format +For the mark, unmark, and delete commands, if you input a string or decimal in place of an integer for the task index +(e.g. trying to enter `mark XYZ` or `mark 3.14`), the program will inform you of your mistake: + +```bash +Oops! Please enter a valid integer as a task number! +``` + +--- + +## Saving Data +Have no fear! Capy Task Manager automatically saves your tasks to a files after any modification! + +--- + +## Editing the Data File +Task data is saved in a capy.txt file. This file can be dited directly, but do ensure that the format remains +valid to avoid errors when loading the data! -// Feature details +### CAUTION: +If you make changes that corrupt the file format, *all your tasks may be discarded* by Capy Task Manager! +Capy Task Manager would then start fresh with an empty list instead, so be careful when making edits to the data file! +--- -## Feature XYZ +## Command Summary -// Feature details \ No newline at end of file +| Action | Command Format | +|---------------------------|-----------------------------------------------------| +| Add a Todo Task | `todo ` | +| Add a Deadline Task | `deadline /by ` | +| Add an Event Task | `event /from /to ` | +| List All Saved Tasks | `list` | +| Mark a Task as Done | `mark ` | +| Unmark a Task as Not Done | `unmark ` | +| Delete a Task | `delete ` | +| Find Tasks | `find ` | +| Exit the Program | `bye` | 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/META-INF/MANIFEST.MF b/src/main/java/META-INF/MANIFEST.MF new file mode 100644 index 000000000..951b3a04e --- /dev/null +++ b/src/main/java/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: capy.Capy + diff --git a/src/main/java/capy/Capy.java b/src/main/java/capy/Capy.java new file mode 100644 index 000000000..f26334fc8 --- /dev/null +++ b/src/main/java/capy/Capy.java @@ -0,0 +1,66 @@ +package capy; + +import capy.command.Command; +import capy.parser.Parser; +import capy.storage.Storage; +import capy.tasklist.TaskList; +import capy.ui.Ui; + +/** + * The main class of the application that initializes the components, + * processes user input, and executes commands to manage a task list + */ +public class Capy { + private Ui ui; + private Storage storage; + private TaskList tasks; + + /** + * Constructs the Capy application by initializing the UI, storage, and task list + * + * @param filePath the file path where tasks will be loaded from and saved to + */ + public Capy(String filePath) { + ui = new Ui(); + storage = new Storage(filePath); + try { + tasks = new TaskList(storage.loadTasks()); + } catch (CapyException e) { + ui.showLoadingError(); + tasks = new TaskList(); // Initialize with an empty task list if loading fails + } + } + + /** + * Runs the main application loop, handling user commands until the exit command is issued + * The loop reads user input, parses it into commands, executes the commands, and + * displays appropriate output or error messages + */ + public void run() { + ui.showWelcome(); + boolean isExit = false; + + while (!isExit) { + try { + String fullCommand = ui.readCommand(); // Read user input + ui.showLine(); // Print a separator line + Command command = Parser.parse(fullCommand); // Parse input into a Command + command.execute(tasks, ui, storage); // Execute the parsed command + isExit = command.isExit(); // Check if this is the exit command + } catch (CapyException e) { + ui.showError(e.getMessage()); // Display error messages for invalid commands + } finally { + ui.showLine(); // Always print a separator line at the end + } + } + } + + /** + * The main entry point of the application + * + * @param args Command-line arguments + */ + public static void main(String[] args) { + new Capy("./data/capy.txt").run(); // Run the application with the specified data file path + } +} diff --git a/src/main/java/capy/Capy.java.rej b/src/main/java/capy/Capy.java.rej new file mode 100644 index 000000000..bd77aef66 --- /dev/null +++ b/src/main/java/capy/Capy.java.rej @@ -0,0 +1,131 @@ +diff a/src/main/java/capy/Capy.java b/src/main/java/capy/Capy.java (rejected hunks) +@@ -1,6 +1,7 @@ + package capy; + + import javax.swing.*; ++import java.util.ArrayList; + import java.util.Scanner; // Import Scanner class to enable reading of inputs + + public class Capy { +@@ -13,8 +14,7 @@ + Scanner inputObj = new Scanner(System.in); // Create Scanner object + String userInput; // Declare string to hold user input + +- Task[] tasks = new Task[100]; // Declare array of Task objects +- int taskCounter = 0; // Declare int to track how many strings have been added ++ ArrayList tasks = new ArrayList<>(); // Use ArrayList instead of Array to store objects + + // Read input until "bye" is received + while (true) { +@@ -32,9 +32,9 @@ + // Print stored tasks as a list + System.out.println("____________________________________________________________"); + System.out.println("Here are the tasks in your list:"); +- for (int i = 0; i < taskCounter; i++) { ++ for (int i = 0; i < tasks.size(); i++) { + // Iterate through task array to print stored tasks +- System.out.println(i + 1 + ". " + tasks[i]); ++ System.out.println(i + 1 + ". " + tasks.get(i)); + } + System.out.println("____________________________________________________________"); + } else if (userInput.startsWith("mark")) { +@@ -47,11 +47,11 @@ + throw new CapyException("Oops! Seems like the command has included extrat details!"); + } + int taskNumber = Integer.parseInt(parts[1]) - 1; +- if (taskNumber >= 0 && taskNumber < taskCounter) { +- tasks[taskNumber].markDone(); ++ if (taskNumber >= 0 && taskNumber < tasks.size()) { ++ tasks.get(taskNumber).markDone(); + System.out.println("____________________________________________________________"); + System.out.println("Nice! I've marked this task as done: "); +- System.out.println(tasks[taskNumber]); ++ System.out.println(tasks.get(taskNumber)); + System.out.println("____________________________________________________________"); + } else { + System.out.println("Oops! Invalid task number!"); +@@ -71,11 +71,11 @@ + throw new CapyException("Oops! Seems like the command has included extra details!"); + } + int taskNumber = Integer.parseInt(parts[1]) - 1; +- if (taskNumber >= 0 && taskNumber < taskCounter) { +- tasks[taskNumber].unmarkDone(); ++ if (taskNumber >= 0 && taskNumber < tasks.size()) { ++ tasks.get(taskNumber).unmarkDone(); + System.out.println("____________________________________________________________"); + System.out.println("Ok, I've marked this task as not done yet: "); +- System.out.println(tasks[taskNumber]); ++ System.out.println(tasks.get(taskNumber)); + System.out.println("____________________________________________________________"); + } + } catch (NumberFormatException e) { +@@ -93,18 +93,12 @@ + throw new CapyException("Oops! Seems like the command has included extra details!"); + } + int taskNumber = Integer.parseInt(parts[1]) - 1; +- if (taskNumber >= 0 && taskNumber < taskCounter) { +- Task removedTask = tasks[taskNumber]; +- // Adjust numbers of remaining tasks to compensate for removed task +- for (int i = taskNumber; i < taskCounter - 1; i++) { +- tasks[i] = tasks[i + 1]; +- } +- tasks[taskCounter - 1] = null; +- taskCounter--; ++ if (taskNumber >= 0 && taskNumber < tasks.size()) { ++ Task removedTask = tasks.remove(taskNumber); // ArrayList helps shift taskNumber + System.out.println("____________________________________________________________"); + System.out.println("Noted. I've removed this task:"); + System.out.println(" " + removedTask); +- System.out.println("Now you have " + taskCounter + " tasks in the list."); ++ System.out.println("Now you have " + tasks.size() + " tasks in the list."); + System.out.println("____________________________________________________________"); + } else { + System.out.println("Oops! Invalid task number!"); +@@ -120,12 +114,11 @@ + if (description.trim().isEmpty()) { + throw new CapyException("Oops! Seems like the command is missing a description!"); + } +- tasks[taskCounter] = new ToDo(description); +- taskCounter++; ++ tasks.add(new ToDo(description)); + System.out.println("____________________________________________________________"); + System.out.println("Got it. I've added this task:"); +- System.out.println(" " + tasks[taskCounter - 1]); +- System.out.println("Now you have " + taskCounter + " tasks in the list."); ++ System.out.println(" " + tasks.get(tasks.size() - 1)); ++ System.out.println("Now you have " + tasks.size() + " tasks in the list."); + System.out.println("____________________________________________________________"); + } else if (userInput.startsWith("deadline")) { + // Input command "deadline description /by dueDate" +@@ -138,12 +131,11 @@ + if (description.isEmpty()) { + throw new CapyException("Oops! Seems like the command is missing a description!"); + } +- tasks[taskCounter] = new Deadline(description, dueDate); +- taskCounter++; ++ tasks.add(new Deadline(description, dueDate)); + System.out.println("____________________________________________________________"); + System.out.println("Got it. I've added this task:"); +- System.out.println(" " + tasks[taskCounter - 1]); +- System.out.println("Now you have " + taskCounter + " tasks in the list."); ++ System.out.println(" " + tasks.get(tasks.size() - 1)); ++ System.out.println("Now you have " + tasks.size() + " tasks in the list."); + System.out.println("____________________________________________________________"); + } else if (userInput.startsWith("event")) { + // Input command "event description /from start /to end" +@@ -159,12 +151,11 @@ + } else if (start.isEmpty()) { + throw new CapyException("Oops! Seems like the command is missing a start date/time!"); + } +- tasks[taskCounter] = new Event(description, start, end); +- taskCounter++; ++ tasks.add(new Event(description, start, end)); + System.out.println("____________________________________________________________"); + System.out.println("Got it. I've added this task:"); +- System.out.println(" " + tasks[taskCounter - 1]); +- System.out.println("Now you have " + taskCounter + " tasks in the list."); ++ System.out.println(" " + tasks.get(tasks.size() - 1)); ++ System.out.println("Now you have " + tasks.size() + " tasks in the list."); + System.out.println("____________________________________________________________"); + } else { + throw new CapyException("Oops! Command not recognised! Please try a valid command!"); diff --git a/src/main/java/capy/CapyException.java b/src/main/java/capy/CapyException.java new file mode 100644 index 000000000..b565ec686 --- /dev/null +++ b/src/main/java/capy/CapyException.java @@ -0,0 +1,7 @@ +package capy; + +public class CapyException extends Exception { + public CapyException(String message) { + super(message); + } +} diff --git a/src/main/java/capy/command/AddCommand.java b/src/main/java/capy/command/AddCommand.java new file mode 100644 index 000000000..0ee39b9be --- /dev/null +++ b/src/main/java/capy/command/AddCommand.java @@ -0,0 +1,49 @@ +package capy.command; + +import capy.task.Task; +import capy.tasklist.TaskList; +import capy.ui.Ui; +import capy.storage.Storage; +import capy.CapyException; + +/** + * Represents a command to add a new task to the task list + */ +public class AddCommand extends Command { + private final String fullCommand; + + /** + * Constructs an AddCommand with the given user input + * + * @param fullCommand the user's input, which includes the task type and details + */ + public AddCommand(String fullCommand) { + this.fullCommand = fullCommand; + } + + /** + * Executes the add command by identifying the task type (ToDo, Deadline, or Event) + * and adding the corresponding task to the task list. Displays a message and saves + * the updated task list + * + * @param tasks the task list to which the task will be added + * @param ui the user interface to display messages + * @param storage the storage system for saving tasks + * @throws CapyException if the task type is invalid + */ + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) throws CapyException { + if (fullCommand.startsWith("todo")) { + tasks.addTodoTask(fullCommand); + } else if (fullCommand.startsWith("deadline")) { + tasks.addDeadlineTask(fullCommand); + } else if (fullCommand.startsWith("event")) { + tasks.addEventTask(fullCommand); + } else { + throw new CapyException("Invalid task type for AddCommand."); + } + Task addedTask = tasks.getTasks().get(tasks.size() - 1); + ui.showAddTaskMessage(addedTask, tasks.size()); + storage.saveTasks(tasks.getTasks()); + } +} \ No newline at end of file diff --git a/src/main/java/capy/command/Command.java b/src/main/java/capy/command/Command.java new file mode 100644 index 000000000..e662c5ed6 --- /dev/null +++ b/src/main/java/capy/command/Command.java @@ -0,0 +1,17 @@ +package capy.command; + +import capy.CapyException; +import capy.storage.Storage; +import capy.tasklist.TaskList; +import capy.ui.Ui; + +/** + * Executes certain functionalities based on user commands + */ +public abstract class Command { + public abstract void execute(TaskList tasks, Ui ui, Storage storage) throws CapyException; + + public boolean isExit() { + return false; // Default behavior: command does not exit the application + } +} \ No newline at end of file diff --git a/src/main/java/capy/command/DeleteCommand.java b/src/main/java/capy/command/DeleteCommand.java new file mode 100644 index 000000000..fb473d350 --- /dev/null +++ b/src/main/java/capy/command/DeleteCommand.java @@ -0,0 +1,39 @@ +package capy.command; + +import capy.task.Task; +import capy.tasklist.TaskList; +import capy.ui.Ui; +import capy.storage.Storage; +import capy.CapyException; + +/** + * Represents a command to delete a task from the task list + */ +public class DeleteCommand extends Command { + private final String fullCommand; + + /** + * Constructs a DeleteCommand with the given user input + * + * @param fullCommand the user's input, which includes the "delete" command and the task number + */ + public DeleteCommand(String fullCommand) { + this.fullCommand = fullCommand; + } + + /** + * Executes the delete command by removing the specified task from the task list, + * displaying a message, and saving the updated task list + * + * @param tasks the task list from which the task will be deleted + * @param ui the user interface to display messages + * @param storage the storage system for saving tasks + * @throws CapyException if the task number is invalid or out of bounds + */ + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) throws CapyException { + Task removedTask = tasks.deleteTask(fullCommand); + ui.showDeleteTaskMessage(removedTask, tasks.size()); + storage.saveTasks(tasks.getTasks()); + } +} diff --git a/src/main/java/capy/command/ExitCommand.java b/src/main/java/capy/command/ExitCommand.java new file mode 100644 index 000000000..7ffe5d91d --- /dev/null +++ b/src/main/java/capy/command/ExitCommand.java @@ -0,0 +1,33 @@ +package capy.command; + +import capy.tasklist.TaskList; +import capy.ui.Ui; +import capy.storage.Storage; + +/** + * Represents a command to exit the application + */ +public class ExitCommand extends Command { + + /** + * Executes the exit command by displaying a goodbye message + * + * @param tasks the current task list (not used in this command) + * @param ui the user interface to display the goodbye message + * @param storage the storage system (not used in this command) + */ + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) { + System.out.println("Bye. Hope to see you again soon!"); + } + + /** + * Indicates whether the command is an exit command + * + * @return true, so that the command terminates the application + */ + @Override + public boolean isExit() { + return true; // Indicates the program should terminate + } +} \ No newline at end of file diff --git a/src/main/java/capy/command/FindCommand.java b/src/main/java/capy/command/FindCommand.java new file mode 100644 index 000000000..5cab54020 --- /dev/null +++ b/src/main/java/capy/command/FindCommand.java @@ -0,0 +1,49 @@ +package capy.command; + +import capy.task.Task; +import capy.tasklist.TaskList; +import capy.ui.Ui; +import capy.storage.Storage; +import capy.CapyException; + +/** + * Finds and lists tasks that contain a specified keyword. + */ +public class FindCommand extends Command { + + private final String keyword; + + public FindCommand(String fullCommand) throws CapyException { + String[] parts = fullCommand.split(" ", 2); + if (parts.length < 2 || parts[1].trim().isEmpty()) { + throw new CapyException(Ui.MISSING_DESCRIPTION); // Ensure keyword is provided + } + this.keyword = parts[1].trim(); + } + + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) throws CapyException { + System.out.println("Here are the matching tasks in your list:"); + + int matchCounter = 1; // Counter for matching tasks + boolean found = false; + + for (int i = 0; i < tasks.size(); i++) { + Task task = tasks.getTask(i); // Use TaskList's getTask method + if (task.getDescription().contains(keyword)) { + System.out.println(matchCounter + ". " + task); // Number matches correctly + matchCounter++; + found = true; + } + } + + if (!found) { + System.out.println("No tasks found matching the keyword: " + keyword); + } + } + + @Override + public boolean isExit() { + return false; // This command does not terminate the application + } +} diff --git a/src/main/java/capy/command/ListCommand.java b/src/main/java/capy/command/ListCommand.java new file mode 100644 index 000000000..f3f2cb603 --- /dev/null +++ b/src/main/java/capy/command/ListCommand.java @@ -0,0 +1,22 @@ +package capy.command; + +import capy.tasklist.TaskList; +import capy.ui.Ui; +import capy.storage.Storage; + +/** + * Represents a command to list all tasks in the task list + */ +public class ListCommand extends Command { + /** + * Executes the list command by displaying all tasks in the task list + * + * @param tasks the task list containing all the tasks + * @param ui the user interface to display the tasks + * @param storage the storage system (not used in this command) + */ + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) { + tasks.listTasks(ui); + } +} diff --git a/src/main/java/capy/command/MarkCommand.java b/src/main/java/capy/command/MarkCommand.java new file mode 100644 index 000000000..9b27e5f80 --- /dev/null +++ b/src/main/java/capy/command/MarkCommand.java @@ -0,0 +1,40 @@ +package capy.command; + +import capy.task.Task; +import capy.tasklist.TaskList; +import capy.ui.Ui; +import capy.storage.Storage; +import capy.CapyException; + +/** + * Represents a command to mark a task as completed in the task list + */ +public class MarkCommand extends Command { + private final String fullCommand; + + /** + * Constructs a MarkCommand with the given user input + * + * @param fullCommand the user's input, which includes the "mark" command and the task number + */ + public MarkCommand(String fullCommand) { + this.fullCommand = fullCommand; + } + + /** + * Executes the mark command by marking the specified task as completed, + * displaying a message, and saving the updated task list + * + * @param tasks the task list containing the task to be marked + * @param ui the user interface to display messages + * @param storage the storage system for saving tasks + * @throws CapyException if the task number is invalid or out of bounds + */ + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) throws CapyException { + tasks.markTask(fullCommand); + Task markedTask = tasks.getTask(Integer.parseInt(fullCommand.split(" ")[1]) - 1); + ui.showMarkTaskMessage(markedTask); + storage.saveTasks(tasks.getTasks()); + } +} diff --git a/src/main/java/capy/command/UnmarkCommand.java b/src/main/java/capy/command/UnmarkCommand.java new file mode 100644 index 000000000..cd2bb1ef2 --- /dev/null +++ b/src/main/java/capy/command/UnmarkCommand.java @@ -0,0 +1,40 @@ +package capy.command; + +import capy.task.Task; +import capy.tasklist.TaskList; +import capy.ui.Ui; +import capy.storage.Storage; +import capy.CapyException; + +/** + * Represents a command to mark a task as not completed in the task list + */ +public class UnmarkCommand extends Command { + private final String fullCommand; + + /** + * Constructs an UnmarkCommand with the given user input + * + * @param fullCommand the user's input, which includes the "unmark" command and the task number + */ + public UnmarkCommand(String fullCommand) { + this.fullCommand = fullCommand; + } + + /** + * Executes the unmark command by marking the specified task as not completed, + * displaying a message, and saving the updated task list + * + * @param tasks the task list containing the task to be unmarked + * @param ui the user interface to display messages + * @param storage the storage system for saving tasks + * @throws CapyException if the task number is invalid or out of bounds + */ + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) throws CapyException { + tasks.unmarkTask(fullCommand); + Task unmarkedTask = tasks.getTask(Integer.parseInt(fullCommand.split(" ")[1]) - 1); + ui.showUnmarkTaskMessage(unmarkedTask); + storage.saveTasks(tasks.getTasks()); + } +} diff --git a/src/main/java/capy/parser/Parser.java b/src/main/java/capy/parser/Parser.java new file mode 100644 index 000000000..f840ccf9a --- /dev/null +++ b/src/main/java/capy/parser/Parser.java @@ -0,0 +1,38 @@ +package capy.parser; + +import capy.CapyException; +import capy.command.*; +import capy.ui.Ui; + +/** + * Parses user input and identifies the command entered + */ +public class Parser { + + /** + * Parses the user input string and returns the appropriate command object + * + * @param fullCommand The full input string entered by the user. + * @return A Command object corresponding to the user input. + * @throws CapyException If the command is unrecognized or invalid. + */ + public static Command parse(String fullCommand) throws CapyException { + if (fullCommand.equals("bye")) { + return new ExitCommand(); + } else if (fullCommand.equals("list")) { + return new ListCommand(); + } else if (fullCommand.startsWith("mark")) { + return new MarkCommand(fullCommand); + } else if (fullCommand.startsWith("unmark")) { + return new UnmarkCommand(fullCommand); + } else if (fullCommand.startsWith("todo") || fullCommand.startsWith("deadline") || fullCommand.startsWith("event")) { + return new AddCommand(fullCommand); + } else if (fullCommand.startsWith("delete")) { + return new DeleteCommand(fullCommand); + } else if (fullCommand.startsWith("find")) { + return new FindCommand(fullCommand); + } else { + throw new CapyException(Ui.INVALID_COMMAND); + } + } +} diff --git a/src/main/java/capy/storage/Storage.java b/src/main/java/capy/storage/Storage.java new file mode 100644 index 000000000..4463a36e5 --- /dev/null +++ b/src/main/java/capy/storage/Storage.java @@ -0,0 +1,99 @@ +package capy.storage; + +import capy.*; +import capy.task.Deadline; +import capy.task.Event; +import capy.task.Task; +import capy.task.ToDo; +import capy.ui.Ui; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; + +/** + * Manages the loading and saving of tasks to a persistent file storage + * Handles the creation of directories and files as needed, and ensures tasks are + * properly saved and loaded in a specified format + */ +public class Storage { + private String filePath; + + /** + * Constructs a Storage object for managing the given file path + * + * @param filePath the path to the file where tasks will be saved and loaded from + */ + public Storage(String filePath) { + this.filePath = filePath; + } + + /** + * Loads tasks from the file specified in the file path + * + * @return an ArrayList of tasks loaded from the file + * @throws CapyException if the file contains corrupted data or cannot be accessed + */ + public ArrayList loadTasks() throws CapyException { + ArrayList tasks = new ArrayList<>(); + try { + // Ensure the directory exists + Files.createDirectories(Paths.get("./data")); + File file = new File(filePath); + + // Read tasks if the file exists + if (file.exists()) { + BufferedReader reader = new BufferedReader(new FileReader(file)); + String line; + while ((line = reader.readLine()) != null) { + String[] parts = line.split("\\|"); + + // Check for corrupted file format + if (parts.length < 3) { + throw new CapyException(Ui.CORRUPTED_FILE); + } + + String type = parts[0].trim(); + boolean isDone = parts[1].trim().equals("1"); + + // Parse tasks based on their type + switch (type) { + case "T": + tasks.add(new ToDo(parts[2].trim(), isDone)); + break; + case "D": + tasks.add(new Deadline(parts[2].trim(), parts[3].trim(), isDone)); + break; + case "E": + tasks.add(new Event(parts[2].trim(), parts[3].trim(), parts[4].trim(), isDone)); + break; + default: + throw new CapyException(Ui.CORRUPTED_FILE); + } + } + reader.close(); + } + } catch (IOException e) { + throw new CapyException("Error loading tasks: " + e.getMessage()); + } + return tasks; + } + + /** + * Saves the tasks to the file specified in the file path + * + * @param tasks the list of tasks to be saved to the file + * @throws CapyException if an I/O error occurs while saving the tasks + */ + public void saveTasks(ArrayList tasks) throws CapyException { + try (BufferedWriter writer = new BufferedWriter(new FileWriter(filePath))) { + for (Task task : tasks) { + writer.write(task.toFileFormat()); + writer.newLine(); + } + } catch (IOException e) { + throw new CapyException("Error saving tasks: " + e.getMessage()); + } + } +} diff --git a/src/main/java/capy/task/Deadline.java b/src/main/java/capy/task/Deadline.java new file mode 100644 index 000000000..89ee87324 --- /dev/null +++ b/src/main/java/capy/task/Deadline.java @@ -0,0 +1,41 @@ +package capy.task; + +/** + * Represents a Deadline task with a description and a due date + */ +public class Deadline extends Task { + protected String dueDate; + + /** + * Constructs a Deadline task with the given description, due date, and completion status + * + * @param description the description of the deadline + * @param dueDate the due date of the task + * @param isDone whether the task is completed + */ + public Deadline(String description, String dueDate, boolean isDone) { + super(description); + this.dueDate = dueDate; + this.isDone = isDone; + } + + /** + * Returns the string representation of the Deadline task + * + * @return a string in the format "[D][status] description (by: dueDate)" + */ + @Override + public String toString() { + return "[D]" + super.toString() + " (by: " + dueDate + ")"; + } + + /** + * Converts the Deadline task to a format suitable for saving to a file + * + * @return a formatted string representing the Deadline task's data + */ + @Override + public String toFileFormat() { // Method to save to file + return "D | " + (isDone ? "1" : "0") + " | " + description + " | " + dueDate; + } +} diff --git a/src/main/java/capy/task/Event.java b/src/main/java/capy/task/Event.java new file mode 100644 index 000000000..ba84138ff --- /dev/null +++ b/src/main/java/capy/task/Event.java @@ -0,0 +1,45 @@ +package capy.task; + +/** + * Represents an Event task with a description, start time, and end time + */ +public class Event extends Task { + protected String start; + protected String end; + + /** + * Constructs an Event task with the given description, start time, end time, and completion status + * + * @param description the description of the event + * @param start the start time of the event + * @param end the end time of the event + * @param isDone whether the event is completed + */ + public Event(String description, String start, String end, boolean isDone) { + super(description); + this.start = start; + this.end = end; + this.isDone = isDone; + } + + /** + * Returns the string representation of the Event task + * + * @return a string in the format "[E][status] description (from: start to: end)" + */ + @Override + public String toString() { + return "[E]" + super.toString() + " (from: " + start + " " + + "to: " + end + ")"; + } + + /** + * Converts the Event task to a format suitable for saving to a file + * + * @return a formatted string representing the Event task's data + */ + @Override + public String toFileFormat() { // Method to save to file + return "E | " + (isDone ? "1" : "0") + " | " + description + " | " + start + " | " + end; + } +} diff --git a/src/main/java/capy/task/Task.java b/src/main/java/capy/task/Task.java new file mode 100644 index 000000000..527b15fad --- /dev/null +++ b/src/main/java/capy/task/Task.java @@ -0,0 +1,65 @@ +package capy.task; + +/** + * Represents a task with a description and its completion status + * This is an abstract class to be extended by specific types of tasks + */ +public abstract class Task { + protected String description; + protected boolean isDone; + + /** + * Constructs a new Task with the given description + * + * @param description the description of the task + */ + public Task(String description) { + this.description = description; + this.isDone = false; // Set default as not done + } + + /** + * Returns the status icon of the task + * + * @return "X" if the task is done, else return a space character " " + */ + public String getStatusIcon() { + return (isDone ? "X" : " "); // Mark done task with X + } + + /** + * Marks the task as done + */ + public void markDone() { + this.isDone = true; + } + + /** + * Marks the task as not done + */ + public void unmarkDone() { + this.isDone = false; + } + + /** + * Returns the string representation of the task + * + * @return a string in the format "[status] description" + */ + + public String getDescription() { + return this.description; // Return the description of the task + } + + @Override + public String toString() { + return "[" + getStatusIcon() + "] " + description; + } + + /** + * Converts the task to a format suitable for saving to a file + * + * @return a formatted string representing the task's data + */ + public abstract String toFileFormat(); // Abstract method for saving tasks +} diff --git a/src/main/java/capy/task/ToDo.java b/src/main/java/capy/task/ToDo.java new file mode 100644 index 000000000..da91c6998 --- /dev/null +++ b/src/main/java/capy/task/ToDo.java @@ -0,0 +1,48 @@ +package capy.task; + +/** + * Represents a To-Do task with a description + */ +public class ToDo extends Task { + /** + * Constructs a ToDo task with the given description + * + * @param description the description of the task + */ + public ToDo(String description) { + super(description); + } + + /** + * Constructs a ToDo task with the given description and completion status + * + * @param description The description of the task. + * @param isDone Whether the task is completed. + */ + public ToDo(String description, boolean isDone) { // Constructor to load from file + super(description); + this.isDone = isDone; + } + + /** + * Returns the string representation of the ToDo task. + * + * @return a string in the format "[T][status] description" + */ + @Override + public String toString() { + return "[T]" + super.toString(); + } + + /** + * Converts the ToDo task to a format suitable for saving to a file + * + * @return a formatted string representing the ToDo task's data + */ + @Override + public String toFileFormat() { // Method to save to file + return "T | " + (isDone ? "1" : "0") + " | " + description; + } +} + + diff --git a/src/main/java/capy/tasklist/TaskList.java b/src/main/java/capy/tasklist/TaskList.java new file mode 100644 index 000000000..5f10f6905 --- /dev/null +++ b/src/main/java/capy/tasklist/TaskList.java @@ -0,0 +1,216 @@ +package capy.tasklist; + +import capy.*; +import capy.task.Deadline; +import capy.task.Event; +import capy.task.Task; +import capy.task.ToDo; +import capy.ui.Ui; + +import java.util.ArrayList; + +/** + * Manages the list of tasks and provides operations to modify and retrieve tasks + */ +public class TaskList { + + private ArrayList tasks; + + /** + * Constructor, constructs an empty task list + */ + public TaskList() { + tasks = new ArrayList<>(); + } + + /** + * Constructs a task list with the given tasks + * + * @param tasks the initial list of tasks + */ + public TaskList(ArrayList tasks) { + this.tasks = tasks; + } + + /** + * Returns the number of tasks in the list + * + * @return the size of the task list + */ + public int size() { + return tasks.size(); + } + + /** + * Retrieves the entire list of tasks + * + * @return an ArrayList of Task objects + */ + public ArrayList getTasks() { + return tasks; + } + + /** + * Retrieves a task at a specific index. + * + * @param index the index of the task to retrieve (0-based) + * @return the Task object at the specified index. + * @throws CapyException if the index is out of bounds + */ + public Task getTask(int index) throws CapyException { + if (index < 0 || index >= tasks.size()) { + throw new CapyException(Ui.INVALID_TASK_NUMBER); + } + return tasks.get(index); + } + + /** + * Displays all tasks in the task list using the UI + * + * @param ui the user interface for displaying tasks + */ + public void listTasks(Ui ui) { + 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)); + } + } + + /** + * Adds a ToDo task to the task list + * + * @param userInput the user input containing the task description + * @throws CapyException if the task description is missing + */ + public void addTodoTask(String userInput) throws CapyException { + String description = userInput.substring(4).trim(); + if (description.isEmpty()) { + throw new CapyException(Ui.MISSING_DESCRIPTION); + } + tasks.add(new ToDo(description)); + } + + /** + * Adds a Deadline task to the task list + * + * @param userInput the user input containing the task details + * @throws CapyException if the task details are incomplete or missing + */ + public void addDeadlineTask(String userInput) throws CapyException { + String[] parts = userInput.split("/by"); + if (parts.length < 2) { + throw new CapyException(Ui.MISSING_DETAILS); + } + + String description = parts[0].substring(8).trim(); + String dueDate = parts[1].trim(); + + if (description.isEmpty()) { + throw new CapyException(Ui.MISSING_DESCRIPTION); + } + tasks.add(new Deadline(description, dueDate, false)); + } + + /** + * Adds an Event task to the task list + * + * @param userInput the user input containing the task details + * @throws CapyException if the task details are incomplete or missing + */ + public void addEventTask(String userInput) throws CapyException { + String[] parts = userInput.split("/from|/to"); + if (parts.length < 3) { + throw new CapyException(Ui.MISSING_DETAILS); + } + String description = parts[0].substring(5).trim(); + String start = parts[1].trim(); + String end = parts[2].trim(); + + if (description.isEmpty()) { + throw new CapyException(Ui.MISSING_DESCRIPTION); + } + + Event newEvent = new Event(description, start, end, false); + tasks.add(newEvent); + } + + /** + * Marks a task as done based on the task number provided in the user input + * + * @param userInput the user input containing the "mark" command and the task number + * @throws CapyException if the task number is missing, in the wrong format, + * not a valid integer, or out of bounds. + */ + public void markTask(String userInput) throws CapyException { + String[] parts = userInput.split(" "); + if (parts.length < 2) { + throw new CapyException(Ui.MISSING_TASK_NUMBER); + } else if (parts.length > 2) { + throw new CapyException(Ui.WRONG_FORMAT); + } + + try { + int taskNumber = Integer.parseInt(parts[1]) - 1; + if (taskNumber < 0 || taskNumber >= tasks.size()) { + throw new CapyException(Ui.INVALID_TASK_NUMBER); + } + tasks.get(taskNumber).markDone(); + } catch (NumberFormatException e) { + throw new CapyException(Ui.NOT_AN_INTEGER); + } + } + + /** + * Marks a task as not done based on the task number provided in the user input + * + * @param userInput the user input containing the "unmark" command and the task number + * @throws CapyException if the task number is missing, in the wrong format, + * not a valid integer, or out of bounds + */ + public void unmarkTask(String userInput) throws CapyException { + String[] parts = userInput.split(" "); + if (parts.length < 2) { + throw new CapyException(Ui.MISSING_TASK_NUMBER); + } else if (parts.length > 2) { + throw new CapyException(Ui.WRONG_FORMAT); + } + + try { + int taskNumber = Integer.parseInt(parts[1]) - 1; + if (taskNumber < 0 || taskNumber >= tasks.size()) { + throw new CapyException(Ui.INVALID_TASK_NUMBER); + } + tasks.get(taskNumber).unmarkDone(); + } catch (NumberFormatException e) { + throw new CapyException(Ui.NOT_AN_INTEGER); + } + } + + /** + * Deletes a task from the task list based on the task number provided in the user input + * + * @param userInput the user input containing the "delete" command and the task number + * @return the deleted Task object. + * @throws CapyException if the task number is missing, in the wrong format, + * not a valid integer, or out of bounds + */ + public Task deleteTask(String userInput) throws CapyException { + String[] parts = userInput.split(" "); + if (parts.length < 2) { + throw new CapyException(Ui.MISSING_TASK_NUMBER); + } else if (parts.length > 2) { + throw new CapyException(Ui.WRONG_FORMAT); + } + + try { + int taskNumber = Integer.parseInt(parts[1]) - 1; + if (taskNumber < 0 || taskNumber >= tasks.size()) { + throw new CapyException(Ui.INVALID_TASK_NUMBER); + } + return tasks.remove(taskNumber); + } catch (NumberFormatException e) { + throw new CapyException(Ui.NOT_AN_INTEGER); + } + } + +} diff --git a/src/main/java/capy/ui/Ui.java b/src/main/java/capy/ui/Ui.java new file mode 100644 index 000000000..a68df7b2a --- /dev/null +++ b/src/main/java/capy/ui/Ui.java @@ -0,0 +1,120 @@ +package capy.ui; + +import capy.task.Task; + +import java.util.Scanner; + +/** + * Handles user interaction, including input and output + * Provides methods for displaying messages, reading user commands, + * and showing feedback or errors to the user + */ +public class Ui { + private static final String LINE_SPACING = "____________________________________________________________"; + + // Constants for common error messages + public static final String INVALID_COMMAND = "Oops! Command not Recognised! Please try a valid command!"; + public static final String INVALID_TASK_NUMBER = "Oops! Invalid task number!"; + public static final String MISSING_TASK_NUMBER = "Oops! Seems like the command is missing a task number!"; + public static final String NOT_AN_INTEGER = "Oops! Please enter a valid integer as a task number!"; + public static final String MISSING_DESCRIPTION = "Oops! Seems like the command is missing a description!"; + public static final String MISSING_DETAILS = "Oops! Seems like the command is missing some details!"; + public static final String WRONG_FORMAT = "Oops! Seems like the command is in the wrong format!"; + public static final String CORRUPTED_FILE = "Oh no! Data file is corrupted!"; + + private Scanner scanner; + + /** + * Constructs a new Ui object that handles user interaction + * Initializes the scanner for reading user input + */ + public Ui() { + scanner = new Scanner(System.in); + } + + /** + * Displays a welcome message to greet the user and start the application + */ + public void showWelcome() { + showLine(); + System.out.println("Hello! I'm Capy"); + System.out.println("What can I do for you?"); + showLine(); + } + + /** + * Reads and returns a command entered by the user + * + * @return The user command as a trimmed string + */ + public String readCommand() { + return scanner.nextLine().trim(); + } + + /** + * Displays a generic error message + * + * @param errorMessage the error message to be displayed + */ + public void showError(String errorMessage) { + System.out.println(errorMessage); + } + + /** + * Displays an error message when tasks fail to load + */ + public void showLoadingError() { + System.out.println("Error loading tasks."); + } + + /** + * Displays a line separator for formatting the output + */ + public void showLine() { + System.out.println(LINE_SPACING); + } + + /** + * Displays a message when a task is successfully added + * + * @param task the task that was added + * @param size the total number of tasks in the task list after adding the task + */ + public void showAddTaskMessage(Task task, int size) { + 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."); + } + + /** + * Displays a message when a task is successfully deleted + * + * @param task the task that was deleted + * @param size the total number of tasks in the task list after deleting the task + */ + public void showDeleteTaskMessage(Task task, int size) { + System.out.println("Noted. I've removed this task:"); + System.out.println(" " + task); + System.out.println("Now you have " + size + " tasks in the list."); + } + + /** + * Displays a message when a task is successfully marked as done + * + * @param task the task that was marked as done + */ + public void showMarkTaskMessage(Task task) { + System.out.println("Nice! I've marked this task as done:"); + System.out.println(" " + task); + } + + /** + * Displays a message when a task is successfully marked as not done + * + * @param task the task that was marked as not done + */ + public void showUnmarkTaskMessage(Task task) { + System.out.println("Ok, I've marked this task as not done yet:"); + System.out.println(" " + task); + } +} \ No newline at end of file