diff --git a/README.md b/README.md index 8715d4d91..cc6375c6b 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,114 @@ -# Duke project template - -This is a project template for a greenfield Java project. It's named after the Java mascot _Duke_. Given below are instructions on how to use it. - -## Setting up in Intellij - -Prerequisites: JDK 11, update Intellij to the most recent version. - -1. Open Intellij (if you are not in the welcome screen, click `File` > `Close Project` to close the existing project first) -1. Open the project into Intellij as follows: - 1. Click `Open`. - 1. Select the project directory, and click `OK`. - 1. If there are any further prompts, accept the defaults. -1. Configure the project to use **JDK 11** (not other versions) as explained in [here](https://www.jetbrains.com/help/idea/sdk.html#set-up-jdk).
- In the same dialog, set the **Project language level** field to the `SDK default` option. -3. After that, locate the `src/main/java/Duke.java` file, right-click it, and choose `Run Duke.main()` (if the code editor is showing compile errors, try restarting the IDE). If the setup is correct, you should see something like the below as the output: - ``` - Hello from - ____ _ - | _ \ _ _| | _____ - | | | | | | | |/ / _ \ - | |_| | |_| | < __/ - |____/ \__,_|_|\_\___| - ``` + +# User Guide for Ken + +``` +K K EEEEE N N +K K E NN N +KKK EEEE N N N +K K E N NN +K K EEEEE N N +``` +## Features +Ken is a CLI chatbot that helps you manage your tasks efficiently. Easily add and delete tasks, deadline and events, save them, and use Ken to manage them easily. + +## Usage + +### `todo, deadline, event` - Add a Task + +To add a task, use one of the following commands: + +- `todo [task description]`: Adds a todo task. +- `deadline [task description] /by [deadline]`: Adds a deadline task, with when it is due by. +- `event [task description] /from [start date] /to [end date]`: Adds an event task, with the duration of the event. + +#### Example of usage : + +`todo activity` + +`deadline project /by Friday` + +`event lunch /from 2pm /to 4pm` + + +#### Expected outcome: + +``` +Got it. I've added this task: + [T][ ] activity +Now you have 1 tasks in the list. +``` +``` +Got it. I've added this task: +[D][ ] project by: Friday +Now you have 4 tasks in the list. +``` +``` +Got it. I've added this task: +[E][ ] lunch from: 2pm to: 4pm +Now you have 5 tasks in the list. +``` + + +### `list` - Viewing all tasks + +To view a list of all your tasks and their corresponding information, use this command + +`list` + +#### Expected outcome: + + +``` +Here are the tasks in your list: + 1.[T][ ] studying for test + 2.[D][ ] project by: Friday + 3.[E][ ] lunch from: 2pm to: 4pm +``` + + +### `delete` - Removing tasks + +To delete a task, use the delete command followed by the task number: + +`delete 2` + +#### Expected outcome: + + +``` +Got it. I've removed this task: + [D][ ] project by: Friday +Now you have 2 tasks in the list. +``` +### `find` - Searching for tasks by keywords + +To find a particular task and their corresponding information from the list by using any part of description, use this command + +`find lunch` + +#### Expected outcome: + + +``` +Here are the matching tasks in your list: +2. [E][ ] lunch ( from: 2pm to: 4pm ) +``` + + +### `mark` - Searching for tasks by keywords + +To mark a task as done, use the mark command followed by the task number: + +#### Example of usage : + +`mark 2` + +#### Expected outcome: + +``` +Nice! I've marked this task as done: + [X] lunch from: 2pm to: 4pm +``` + + + diff --git a/data/ken.txt b/data/ken.txt new file mode 100644 index 000000000..4d6d5c8ef --- /dev/null +++ b/data/ken.txt @@ -0,0 +1 @@ +T | 0 | it works!!!!!! diff --git a/docs/README.md b/docs/README.md index 8077118eb..12a567bd2 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,29 +1,139 @@ -# User Guide +# User Guide for Ken +``` +K K EEEEE N N +K K E NN N +KKK EEEE N N N +K K E N NN +K K EEEEE N N +``` ## Features +Ken is a CLI chatbot that helps you manage your tasks efficiently. Easily add and delete tasks, deadline and events, save them, and use Ken to manage them easily. -### Feature-ABC +- Add tasks of different types: Todo, Deadline, Event +- List all your tasks +- Mark tasks as done +- Unmark tasks +- Delete tasks +- Find tasks -Description of the feature. +## Usage -### Feature-XYZ +### `todo, deadline, event` - Add a Task -Description of the feature. +To add a task, use one of the following commands: -## Usage +- `todo [task description]`: Adds a todo task. +- `deadline [task description] /by [deadline]`: Adds a deadline task, with when it is due by. +- `event [task description] /from [start date] /to [end date]`: Adds an event task, with the duration of the event. + +#### Example of usage : + +`todo activity` + +`deadline project /by Friday` + +`event lunch /from 2pm /to 4pm` + + +#### Expected outcome: + +``` +Got it. I've added this task: + [T][ ] activity +Now you have 1 tasks in the list. +``` +``` +Got it. I've added this task: +[D][ ] project by: Friday +Now you have 4 tasks in the list. +``` +``` +Got it. I've added this task: +[E][ ] lunch from: 2pm to: 4pm +Now you have 5 tasks in the list. +``` -### `Keyword` - Describe action -Describe the action and its outcome. +### `list` - Viewing all tasks -Example of usage: +To view a list of all your tasks and their corresponding information, use this command + +`list` -`keyword (optional arguments)` +#### Expected outcome: -Expected outcome: -Description of the outcome. +``` +Here are the tasks in your list: + 1.[T][ ] studying for test + 2.[D][ ] project by: Friday + 3.[E][ ] lunch from: 2pm to: 4pm +``` + + +### `delete` - Removing tasks + +To delete a task, use the delete command followed by the task number: + +`delete 2` + +#### Expected outcome: + ``` -expected output +Got it. I've removed this task: + [D][ ] project by: Friday +Now you have 2 tasks in the list. ``` +### `find` - Searching for tasks by keywords + +To find a particular task and their corresponding information from the list by using any part of description, use this command + +`find lunch` + +#### Expected outcome: + + +``` +Here are the matching tasks in your list: +2. [E][ ] lunch ( from: 2pm to: 4pm ) +``` + + +### `mark` - Marking tasks as completed + +To mark a task as done, use the mark command followed by the task number: + +#### Example of usage : + +`mark 2` + +#### Expected outcome: + +``` +Nice! I've marked this task as done: + [X] lunch from: 2pm to: 4pm +``` + + +### `unmark` - Unmarking tasks as uncompleted + +To unmark a previously marked task, use the unmark command followed by the task number: + +#### Example of usage : + +`unmark 2` + +#### Expected outcome: + +``` +Nice! I've unmarked this task as done: + [ ] lunch from: 2pm to: 4pm +``` + + +### `bye` - Terminating Ken + +When you've finished organizing your tasks, simply enter +`bye` diff --git a/ken.txt b/ken.txt new file mode 100644 index 000000000..798985379 --- /dev/null +++ b/ken.txt @@ -0,0 +1,2 @@ +D | 1 | ip | by: friday +T | 0 | studying for test | null diff --git a/pullreq1.txt b/pullreq1.txt new file mode 100644 index 000000000..18d1d885f --- /dev/null +++ b/pullreq1.txt @@ -0,0 +1 @@ +Pull request 1 diff --git a/src/main/java/AddCommand.java b/src/main/java/AddCommand.java new file mode 100644 index 000000000..9a0be0daa --- /dev/null +++ b/src/main/java/AddCommand.java @@ -0,0 +1,86 @@ +public class AddCommand { + /** + * Handles the addition of a task to the task list. + * + * @param taskType The type of the task (e.g., "T" for Todo). + * @param taskDescription The description of the task. + * @param taskList The TaskList to which the task will be added. + * @throws EmptyDescriptionException If the task description is empty. + * @throws MissingInfoException If required information is missing for a task type. + */ + public static void AddTask(String taskType, String taskDescription, TaskList taskList) throws EmptyDescriptionException, MissingInfoException { + if (taskDescription.isEmpty()) { + throw new EmptyDescriptionException("Hey!! Description cannot be empty for a " + taskType + " task."); + } + taskType = taskType.substring(0, 1).toUpperCase(); + String[] dateInfo = CommandParser.extractDateInfo(taskDescription); + if (isMissingInfo(taskType, dateInfo)) { + throw new MissingInfoException(taskType); + } + taskDescription = dateInfo[0]; + addTaskToList(taskType, taskDescription, dateInfo[1], taskList); + displayTaskAddedMessage(taskType, taskDescription, dateInfo[1], taskList); + } + + /** + * Checks if required information is missing for a task type. + * + * @param taskType The type of the task. + * @param dateInfo An array containing task description and date information. + * @return True if required information is missing, otherwise false. + */ + private static boolean isMissingInfo(String taskType, String[] dateInfo) { + if (taskType.equals("D") && dateInfo[1] == null) { + return true; + } else return taskType.equals("E") && (dateInfo[1] == null || dateInfo[1].isEmpty()); + } + + /** + * Adds a task to the task list. + * + * @param taskType The type of the task. + * @param taskDescription The description of the task. + * @param dateInfo The date information for the task. + * @param taskList The TaskList to which the task will be added. + */ + private static void addTaskToList(String taskType, String taskDescription, String dateInfo, TaskList taskList) { + taskList.getTaskTypes().add(taskType); + taskList.getTaskDates().add(dateInfo); + taskList.getTaskDescriptions().add(taskDescription.trim()); + taskList.getTaskDoneStatus().add(false); + } + + /** + * Displays a message confirming the addition of a task. + * + * @param taskType The type of the task. + * @param taskDescription The description of the task. + * @param dateInfo The date information for the task. + * @param taskList The TaskList to which the task was added. + */ + private static void displayTaskAddedMessage(String taskType, String taskDescription, String dateInfo, TaskList taskList) { + System.out.println(" Got it. I've added this task:"); + String dateInfoText = dateInfo != null ? " " + dateInfo : ""; + System.out.println(" [" + taskType + "][ ] " + taskDescription + dateInfoText); + System.out.println(" Now you have " + taskList.getTaskDescriptions().size() + " tasks in the list."); + } + + /** + * Handles adding a task and saves tasks after adding it. + * + * @param taskType The type of the task (e.g., "T" for Todo). + * @param taskDescription The description of the task. + * @param taskList The TaskList to which the task will be added. + * @throws KenException If there's an issue with the task or task list handling. + */ + public static void handleAddTask(String taskType, String taskDescription, TaskList taskList) throws KenException { + AddTask(taskType, taskDescription, taskList); + Storage.saveTasks(taskList); // Save tasks after deleting + } + + public static class MissingInfoException extends KenException { + public MissingInfoException(String taskType) { + super("Hey!! Don't leave the description or date empty for a " + taskType + " task."); + } + } +} diff --git a/src/main/java/CommandParser.java b/src/main/java/CommandParser.java new file mode 100644 index 000000000..aeb7069d4 --- /dev/null +++ b/src/main/java/CommandParser.java @@ -0,0 +1,150 @@ +/** + * This class is responsible for parsing and processing user commands for the Ken chatbot. + * It defines constants for various supported commands and provides methods to handle + * user input and extract task information from it. + */ +public class CommandParser { + + /** + * Command keyword for terminating the chatbot. + */ + public static final String COMMAND_BYE = "bye"; + + /** + * Command keyword for listing tasks. + */ + public static final String COMMAND_LIST = "list"; + + /** + * Command keyword for adding a to-do task. + */ + public static final String COMMAND_TODO = "todo"; + + /** + * Command keyword for adding a deadline task. + */ + public static final String COMMAND_DEADLINE = "deadline"; + + /** + * Command keyword for adding an event task. + */ + public static final String COMMAND_EVENT = "event"; + + /** + * Command keyword for deleting a task. + */ + public static final String COMMAND_DELETE = "delete"; + + /** + * Command keyword for marking a task as done. + */ + public static final String COMMAND_MARK = "mark"; + + /** + * Command keyword for marking a task as done. + */ + public static final String COMMAND_UNMARK = "unmark"; + + /** + * Command keyword for finding tasks by keyword. + */ + public static final String COMMAND_FIND = "find"; + + + /** + * Processes the user's command, extracts task type and description, and performs + * the corresponding action (e.g., adding, deleting, marking) on the task list. + * + * @param userInput The user's input command. + * @param taskList The task list to which the command should be applied. + * @throws KenException If there's an issue with the user's command or task handling. + */ + public static void processUserCommand(String userInput, TaskList taskList) throws KenException { + String[] parts = userInput.split(" ", 2); + + if (parts.length != 2) { + handleInvalidCommand(parts[0]); + } else { + String taskType = parts[0].trim(); + String taskDescription = parts[1].trim(); + + if (taskType.equalsIgnoreCase(COMMAND_TODO) || taskType.equalsIgnoreCase(COMMAND_DEADLINE) || taskType.equalsIgnoreCase(COMMAND_EVENT)) { + AddCommand.handleAddTask(taskType, taskDescription, taskList); + } else if (taskType.equalsIgnoreCase(COMMAND_DELETE)) { + DeleteCommand.handleDeleteTask(taskDescription, taskList); + } else if (taskType.equalsIgnoreCase(COMMAND_MARK)) { + MarkCommand.handleMarkTask(taskDescription, taskList); + } else if (taskType.equalsIgnoreCase(COMMAND_UNMARK)) { + MarkCommand.handleUnmarkTask(taskDescription, taskList); + } else { + throw new InvalidCommandException(); + } + } + } + + private static void handleInvalidCommand(String command) throws InvalidCommandException, EmptyDescriptionException, WrongFormatException { + if (command.equalsIgnoreCase(COMMAND_DELETE)) { + throw new WrongFormatException(); + } else if (command.equalsIgnoreCase(COMMAND_TODO) || command.equalsIgnoreCase(COMMAND_DEADLINE) || command.equalsIgnoreCase(COMMAND_EVENT)) { + throw new EmptyDescriptionException("Hey!! Description cannot be empty for a " + command + " task."); + } else { + throw new InvalidCommandException(); + } + } + + /** + * Extracts date information from a task description, if present, and returns + * an array containing the task description and date information. + * + * @param description The task description that may contain date information. + * @return An array containing the task description and date information. + * @throws AddCommand.MissingInfoException If there's an issue with the date information extraction. + */ + public static String[] extractDateInfo(String description) throws AddCommand.MissingInfoException { + if (description.contains("/from") && description.contains("/to")) { + return extractEventDateInfo(description); + } else if (description.contains("/by")) { + return extractDeadlineDateInfo(description); + } else { + return new String[]{description, null}; + } + } + + /** + * Extracts date information for an event task from the given description. + * + * @param description The task description that contains date information for an event task. + * @return An array containing the task description and date information. + * @throws AddCommand.MissingInfoException If there's an issue with the date information extraction. + */ + private static String[] extractEventDateInfo(String description) throws AddCommand.MissingInfoException { + String[] parts = description.split(" /from | /to "); + if (parts.length > 2) { + String startDate = parts[1]; + String endDate = parts[2]; + if (endDate.isEmpty()) { + throw new AddCommand.MissingInfoException("/to date cannot be empty"); + } + String dateInfo = " from: " + startDate + " to: " + endDate; + return new String[]{parts[0], dateInfo}; + } else { + throw new AddCommand.MissingInfoException("Invalid date format for event task"); + } + } + + /** + * Extracts date information for a deadline task from the given description. + * + * @param description The task description that contains date information for a deadline task. + * @return An array containing the task description and date information. + */ + private static String[] extractDeadlineDateInfo(String description) { + String[] parts = description.split(" /by "); + if (parts.length > 1) { + String deadlineDate = parts[1]; + String dateInfo = " by: " + deadlineDate; + return new String[]{parts[0], dateInfo}; + } + return new String[]{description, null}; + } +} diff --git a/src/main/java/DeleteCommand.java b/src/main/java/DeleteCommand.java new file mode 100644 index 000000000..e5c1d7d2b --- /dev/null +++ b/src/main/java/DeleteCommand.java @@ -0,0 +1,38 @@ + + +public class DeleteCommand { + /** + * Handles the deletion of a task from the task list. + * + * @param taskDescription The description of the task to be deleted. + * @param taskList The TaskList from which the task will be deleted. + * @throws KenException If there is an error while handling the task deletion. + */ + public static void DeleteTask(String taskDescription, TaskList taskList) throws KenException { + try { + int taskIndex = Integer.parseInt(taskDescription.trim()) - 1; + if (taskIndex >= 0 && taskIndex < taskList.getTaskDescriptions().size()) { + String deletedTaskDescription = taskList.getTaskDescriptions().get(taskIndex); + printDeletedTaskInfo(taskList, taskIndex, deletedTaskDescription); + Storage.deleteTask(taskIndex, taskList); + System.out.println("Now you have " + taskList.getTaskDescriptions().size() + " tasks in the list."); + } else { + throw new TaskNotFoundException(); + } + } catch (NumberFormatException e) { + throw new WrongFormatException(); + } + } + + public static void handleDeleteTask(String taskDescription, TaskList taskList) throws KenException { + DeleteTask(taskDescription, taskList); + Storage.saveTasks(taskList); // Save tasks after deleting + } + + private static void printDeletedTaskInfo(TaskList taskList, int taskIndex, String deletedTaskDescription) { + System.out.println("Got it. I've removed this task:"); + System.out.println(" [" + taskList.getTaskTypes().get(taskIndex) + + "][ ] " + deletedTaskDescription + + (taskList.getTaskDates().get(taskIndex) != null ? " " + taskList.getTaskDates().get(taskIndex) : "")); + } +} 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/EmptyDescriptionException.java b/src/main/java/EmptyDescriptionException.java new file mode 100644 index 000000000..cd8abef1f --- /dev/null +++ b/src/main/java/EmptyDescriptionException.java @@ -0,0 +1,5 @@ +public class EmptyDescriptionException extends KenException { + public EmptyDescriptionException(String message) { + super(message); + } +} diff --git a/src/main/java/FindCommand.java b/src/main/java/FindCommand.java new file mode 100644 index 000000000..fb14927de --- /dev/null +++ b/src/main/java/FindCommand.java @@ -0,0 +1,42 @@ +import java.util.ArrayList; + +public class FindCommand { + /** + * Finds tasks containing a specified keyword and returns them in a list. + * + * @param keyword The keyword to search for in task descriptions. + * @return A list of matching tasks. + */ + public static ArrayList findTasksByKeyword(String keyword) { + ArrayList matchingTasks = new ArrayList<>(); + for (int i = 0; i < TaskList.taskDescriptions.size(); i++) { + String description = TaskList.taskDescriptions.get(i); + if (description.contains(keyword)) { + String matchingTask = "[" + TaskList.taskTypes.get(i) + "]" + TaskList.taskToString(i); + matchingTasks.add((i + 1) + ". " + matchingTask); // Include the task number + } + } + return matchingTasks; + } + + /** + * Handles the "find" command, searching for tasks with a specified keyword. + * + * @param userInput The user input string containing the "find" command and keyword. + */ + public static void handleFindCommand(String userInput) { + String keyword = userInput.substring(CommandParser.COMMAND_FIND.length()).trim(); + ArrayList matchingTasks = findTasksByKeyword(keyword); + + Ui.printLine(); + if (matchingTasks.isEmpty()) { + System.out.println("No matching tasks found."); + } else { + System.out.println("Here are the matching tasks in your list:"); + for (String matchingTask : matchingTasks) { + System.out.println(matchingTask); + } + } + Ui.printLine(); + } +} diff --git a/src/main/java/Ken.java b/src/main/java/Ken.java new file mode 100644 index 000000000..25d607f1e --- /dev/null +++ b/src/main/java/Ken.java @@ -0,0 +1,69 @@ +import java.util.Scanner; + +/** + * Ken initializes its components, including the user interface, storage for task data, and the task list. + * It loads tasks from a file upon startup and + * provides a run loop to process user commands until the "bye" command is entered. + */ +public class Ken { + private Storage storage; + private TaskList taskList; + private Ui ui; + + /** + * Constructs a Ken object with the specified file path. + * + * @param filePath The path to the file where task data is stored. + */ + public Ken(String filePath) { + ui = new Ui(); + storage = new Storage(filePath); + taskList = new TaskList(); + + // Load tasks from the file when the chatbot starts up + storage.loadTasks(taskList); + } + + /** + * The main method to start the Ken chatbot. + * + * @param args The command-line arguments (not used). + * @throws KenException If there is an exception during chatbot initialization or execution. + */ + public static void main(String[] args) throws KenException { + Ken ken = new Ken("ken.txt"); + ken.run(); + } + + public void run() throws KenException { + Ui.printLine(); + Ui.printWelcomeMessage(); + Ui.printLine(); + + Scanner scanner = new Scanner(System.in); + + while (true) { + String userInput = scanner.nextLine(); + Ui.printLine(); + + if (userInput.toLowerCase().startsWith(CommandParser.COMMAND_FIND)) { + FindCommand.handleFindCommand(userInput); + } else if (userInput.equalsIgnoreCase(CommandParser.COMMAND_BYE)) { + Ui.printGoodbyeMessage(); + break; + } else { + try { + CommandParser.processUserCommand(userInput, taskList); + } catch (KenException e) { + if (userInput.equalsIgnoreCase(CommandParser.COMMAND_LIST)) { + ListCommand.listTasks(taskList); + } else { + System.out.println("Error: " + e.getMessage()); + } + } + } + Ui.printLine(); + } + scanner.close(); + } +} diff --git a/src/main/java/KenException.java b/src/main/java/KenException.java new file mode 100644 index 000000000..04d235fa8 --- /dev/null +++ b/src/main/java/KenException.java @@ -0,0 +1,23 @@ +public class KenException extends Exception { + public KenException(String message) { + super(message); + } +} + +class InvalidCommandException extends KenException { + public InvalidCommandException() { + super("Hmm, what's that? Please use 'todo,' 'deadline,' 'event,' 'delete [number],' or 'mark [number]."); + } +} + +class TaskNotFoundException extends KenException { + public TaskNotFoundException() { + super("I can't find this task. Please provide a valid task number."); + } +} + +class WrongFormatException extends KenException { + public WrongFormatException() { + super("Wrong format!! Please provide the number of the task."); + } +} diff --git a/src/main/java/ListCommand.java b/src/main/java/ListCommand.java new file mode 100644 index 000000000..6e12dec2d --- /dev/null +++ b/src/main/java/ListCommand.java @@ -0,0 +1,17 @@ +public class ListCommand { + /** + * Lists all the tasks in the task list. + * + * @param taskList The TaskList to be listed. + */ + public static void listTasks(TaskList taskList) { + System.out.println(" Here are the tasks in your list:"); + int numTasks = taskList.getTaskDescriptions().size(); // Get the number of tasks + + for (int i = 0; i < numTasks; i++) { + char doneSymbol = taskList.getTaskDoneStatus().get(i) ? 'X' : ' '; + String dateInfo = taskList.getTaskDates().get(i) != null ? taskList.getTaskDates().get(i) : ""; + System.out.println(" " + (i + 1) + ".[" + taskList.getTaskTypes().get(i) + "][" + doneSymbol + "] " + taskList.getTaskDescriptions().get(i) + dateInfo); + } + } +} diff --git a/src/main/java/META-INF/MANIFEST.MF b/src/main/java/META-INF/MANIFEST.MF new file mode 100644 index 000000000..104a0b4f9 --- /dev/null +++ b/src/main/java/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: Ken + diff --git a/src/main/java/MarkCommand.java b/src/main/java/MarkCommand.java new file mode 100644 index 000000000..eb94246d4 --- /dev/null +++ b/src/main/java/MarkCommand.java @@ -0,0 +1,77 @@ +public class MarkCommand { + /** + * Handles the marking or unmarking of a task as completed in the task list. + * + * @param taskDescription The description of the task to be marked or unmarked. + * @param taskList The TaskList in which the task will be marked or unmarked. + * @param markAsDone Set to true to mark the task as done, false to unmark it. + * @throws KenException If there is an error while handling the task completion or unmarking. + */ + private static void markOrUnmarkTask(String taskDescription, TaskList taskList, boolean markAsDone) throws KenException { + int taskIndex = getTaskIndex(taskDescription, taskList); + boolean currentStatus = taskList.getTaskDoneStatus().get(taskIndex); + + if (shouldMarkOrUnmark(currentStatus, markAsDone)) { + taskList.getTaskDoneStatus().set(taskIndex, markAsDone); + displayMarkedOrUnmarkedMessage(markAsDone, taskList, taskIndex); + } else { + System.out.println("This task is already " + (markAsDone ? "marked as done." : "unmarked.")); + } + } + + public static int getTaskIndex(String taskDescription, TaskList taskList) throws KenException { + try { + int taskIndex = Integer.parseInt(taskDescription.trim()) - 1; + if (taskIndex >= 0 && taskIndex < taskList.getTaskDescriptions().size()) { + return taskIndex; + } else { + throw new TaskNotFoundException(); + } + } catch (NumberFormatException e) { + throw new WrongFormatException(); + } + } + + private static boolean shouldMarkOrUnmark(boolean currentStatus, boolean markAsDone) { + return (markAsDone && !currentStatus) || (!markAsDone && currentStatus); + } + + private static void displayMarkedOrUnmarkedMessage(boolean markAsDone, TaskList taskList, int taskIndex) { + String status = markAsDone ? "marked as done" : "unmarked"; + String statusSymbol = markAsDone ? "X" : " "; + System.out.println("Nice! I've " + status + " this task:"); + System.out.println(" [" + statusSymbol + "] " + taskList.getTaskDescriptions().get(taskIndex) + (taskList.getTaskDates().get(taskIndex) != null ? taskList.getTaskDates().get(taskIndex) : "")); + } + + /** + * Handles the marking of a task as completed in the task list. + * + * @param taskDescription The description of the task to be marked as completed. + * @param taskList The TaskList in which the task will be marked. + * @throws KenException If there is an error while handling the task completion. + */ + public static void MarkTask(String taskDescription, TaskList taskList) throws KenException { + markOrUnmarkTask(taskDescription, taskList, true); + } + + /** + * Handles the unmarking of a task as incomplete in the task list. + * + * @param taskDescription The description of the task to be unmarked. + * @param taskList The TaskList in which the task will be unmarked. + * @throws KenException If there is an error while handling the task unmarking, such as an invalid task number. + */ + public static void UnmarkTask(String taskDescription, TaskList taskList) throws KenException { + markOrUnmarkTask(taskDescription, taskList, false); + } + + public static void handleMarkTask(String taskDescription, TaskList taskList) throws KenException { + MarkTask(taskDescription, taskList); + Storage.saveTasks(taskList); // Save tasks after marking + } + + public static void handleUnmarkTask(String taskDescription, TaskList taskList) throws KenException { + UnmarkTask(taskDescription, taskList); + Storage.saveTasks(taskList); // Save tasks after unmarking + } +} diff --git a/src/main/java/Storage.java b/src/main/java/Storage.java new file mode 100644 index 000000000..4dfd93639 --- /dev/null +++ b/src/main/java/Storage.java @@ -0,0 +1,86 @@ +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; + +/** + * The Storage class handles loading and saving tasks to a data file. + */ +public class Storage { + private static String DATA_FILE_PATH = "ken.txt"; + + /** + * Constructs a Storage object with a specified file path. + * + * @param filePath The path to the data file. + */ + public Storage(String filePath) { + DATA_FILE_PATH = filePath; + } + + /** + * Loads tasks from the data file and adds them to the given TaskList. + * + * @param taskList The TaskList to which tasks will be loaded. + */ + public void loadTasks(TaskList taskList) { + File file = new File(DATA_FILE_PATH); + + if (file.exists()) { + try (BufferedReader reader = new BufferedReader(new FileReader(file))) { + String line; + while ((line = reader.readLine()) != null) { + taskList.parseAndAddTask(line); + } + } catch (IOException e) { + System.out.println("Error loading tasks from the data file."); + } + } + } + + /** + * Saves tasks from the given TaskList to the data file. + * + * @param taskList The TaskList containing tasks to be saved. + */ + public static void saveTasks(TaskList taskList) { + File file = new File(DATA_FILE_PATH); + + try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) { + for (int i = 0; i < taskList.getTaskDescriptions().size(); i++) { + String taskType = taskList.getTaskTypes().get(i); + String doneStatus = taskList.getTaskDoneStatus().get(i) ? "1" : "0"; + String description = taskList.getTaskDescriptions().get(i); + String date = taskList.getTaskDates().get(i); + + // Use the same separator character "|" for consistency + writer.write(taskType + " | " + doneStatus + " | " + description + " | " + date); + writer.newLine(); + } + } catch (IOException e) { + System.out.println("Error saving tasks to the data file."); + } + } + + /** + * Deletes a task at the specified index in the TaskList and saves the updated list to the data file. + * + * @param taskIndex The index of the task to be deleted. + * @param taskList The TaskList from which the task will be deleted. + */ + public static void deleteTask(int taskIndex, TaskList taskList) { + if (taskIndex >= 0 && taskIndex < TaskList.taskDescriptions.size()) { + TaskList.taskDescriptions.remove(taskIndex); + TaskList.taskDoneStatus.remove(taskIndex); + TaskList.taskTypes.remove(taskIndex); + TaskList.taskDates.remove(taskIndex); + + // Call saveTasks to save the tasks after deleting + saveTasks(taskList); + } else { + System.out.println("Task not found. Nothing was deleted."); + } + } +} diff --git a/src/main/java/TaskList.java b/src/main/java/TaskList.java new file mode 100644 index 000000000..16b071f47 --- /dev/null +++ b/src/main/java/TaskList.java @@ -0,0 +1,122 @@ +import java.util.ArrayList; +import java.util.List; + +/** + * The TaskList class manages the task-related data and provides methods to manipulate tasks. + */ +public class TaskList { + /** + * A list to store task descriptions. + */ + public static List taskDescriptions = new ArrayList<>(); + + /** + * A list to store task completion status (true for completed, false for incomplete). + */ + public static List taskDoneStatus = new ArrayList<>(); + + /** + * A list to store task types (e.g., "T" for Todo, "D" for Deadline, "E" for Event). + */ + public static List taskTypes = new ArrayList<>(); + + /** + * A list to store task dates (optional for some task types). + */ + public static List taskDates = new ArrayList<>(); + + /** + * Parses a task description from a string and adds it to the task lists. + * + * @param line The string containing task information. + */ + public void parseAndAddTask(String line) { + String[] parts = line.split(" \\| "); + if (parts.length >= 3) { + taskTypes.add(parts[0]); + taskDoneStatus.add(parts[1].equals("1")); + taskDescriptions.add(parts[2]); + + if (parts[0].equals("T")) { + taskDates.add(null); // Set the date to null for Todo tasks + } else if (parts.length >= 4) { + taskDates.add(parts[3]); // Set the date for Deadline and Event tasks + } else { + taskDates.add(null); // Handle the case where the date is missing for Deadline and Event tasks + } + } + } + + /** + * Retrieves the list of task descriptions. + * + * @return The list of task descriptions. + */ + public List getTaskDescriptions() { + return taskDescriptions; + } + + /** + * Retrieves the list of task completion statuses. + * + * @return The list of task completion statuses. + */ + public List getTaskDoneStatus() { + return taskDoneStatus; + } + + /** + * Retrieves the list of task types. + * + * @return The list of task types. + */ + public List getTaskTypes() { + return taskTypes; + } + + /** + * Retrieves the list of task dates. + * + * @return The list of task dates. + */ + public List getTaskDates() { + return taskDates; + } + + /** + * Converts a task at the specified index into a formatted string. + * + * @param index The index of the task to be converted. + * @return The formatted string representing the task. + */ + public static String taskToString(int index) { + String taskType = taskTypes.get(index); + String doneStatus = taskDoneStatus.get(index) ? "[X]" : "[ ]"; + String description = taskDescriptions.get(index); + String date = taskDates.get(index); + + switch (taskType) { + case "T": + return doneStatus + " " + description; + case "D": + if (date != null) { + return doneStatus + " " + description + " (" + date + ")"; + } else { + return doneStatus + " " + description; + } + case "E": + if (date != null) { + String[] dateParts = date.split(" to "); + if (dateParts.length == 2) { + return doneStatus + " " + description + " (" + dateParts[0] + " to " + dateParts[1] + ")"; + } else if (dateParts.length == 1) { + return doneStatus + " " + description + " (" + dateParts[0] + " )"; + } + } else { + return doneStatus + " " + description; + } + default: + return "Unknown task type: " + taskType; + } + } +} diff --git a/src/main/java/Ui.java b/src/main/java/Ui.java new file mode 100644 index 000000000..a7df0bbb5 --- /dev/null +++ b/src/main/java/Ui.java @@ -0,0 +1,40 @@ +/** + * This class represents the user interface (UI) for the Ken chatbot. + * It provides methods for displaying welcome and goodbye messages, as well + * as a horizontal line separator for a cleaner UI presentation. + */ +public class Ui { + /** + * Displays the welcome message when the chatbot starts. + */ + public static void printWelcomeMessage() { + printKEN(); + System.out.println(" Hello! I'm Ken"); + System.out.println(" What would you like to do?"); + } + + /** + * Displays the goodbye message when the chatbot is exiting. + */ + public static void printGoodbyeMessage() { + System.out.println("Bye for now!"); + } + + /** + * Prints a horizontal line separator to separate different sections of the output. + */ + public static void printLine() { + System.out.println(" ____________________________________________________________"); + } + + /** + * Prints the ASCII art representation of the chatbot's name "KEN." + */ + private static void printKEN() { + System.out.println(" K K EEEEE N N"); + System.out.println(" K K E NN N"); + System.out.println(" KKK EEEE N N N"); + System.out.println(" K K E N NN"); + System.out.println(" K K EEEEE N N"); + } +}