diff --git a/.gitignore b/.gitignore index 2873e189e..09df8809c 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ bin/ /text-ui-test/ACTUAL.TXT text-ui-test/EXPECTED-UNIX.TXT +*.class diff --git a/docs/README.md b/docs/README.md index 8077118eb..08eea6b9b 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,29 +1,108 @@ -# User Guide +# DOM User Guide +Dom is a desktop program to store a list of tasks for the user via a Command Line Interface(CLI) + +## Quick Start +1. Ensure that you have JAVA '11' or above installed in your computer. +2. Download the latest 'ip.jar' from [here](https://github.com/bljhty/ip/releases) +3. Copy downloaded 'ip.jar' file to the desired home folder of your choice +4. open a command terminal and 'cd' to the folder mentioned in ster 3 +5. type in the following to run the application + +``` +java -jar ip.jar +``` + +6. if successful, the following greeting should appear: +``` +Hello from + ____ + | _ \ ___ _ __ ___ + | | | |/ _ \| '_ ` _ \ + | |_| | (_) | | | | | | + |____/ \___/|_| |_| |_| + +____________________________________________________________ +Hello! I'm Dom +What can I do for you? +``` +7. Type in desired command to start using the program. ## Features +### Adding a todo task: `todo` +Adds a task of type todo to the list. +
Todo contains a description. -### Feature-ABC +Format: `todo DESCRIPTION` +
Example: `todo read book` -Description of the feature. +### Adding an event: `event` +Adds a task of type event to the list. +
Event contains a description, from and to. -### Feature-XYZ +Format: `event DESCRIPTION /from FROM /to TO` +
Example: `event CS2113 tutorial /from 4 April 2023 4pm /to 6pm` -Description of the feature. -## Usage +### Adding a deadline: `deadline` +Adds a task deadline to the list. +
Deadline contains description and by. -### `Keyword` - Describe action +Format: `deadline DESCRIPTION /by BY` +
Example: `deadline CS2113 iP /by 3 March 11.59pm` -Describe the action and its outcome. +### Viewing full list of tasks: `list` +Shows a list of all tasks recorded in the list. -Example of usage: +Format: `list` -`keyword (optional arguments)` +### Mark task as done: `mark` +Marks task as done based on the task number indicated. -Expected outcome: +Format: `mark TASK_NUMBER` +- TASK_NUMBER must be an integer that is within the list size -Description of the outcome. +Example: `mark 2` -``` -expected output -``` +### Mark task as not done: `unmark` +Marks task as not done based on the task number indicated. + +Format: `unmark TASK_NUMBER` +- TASK_NUMBER must be an integer that is within the list size + +Example: `unmark 3` + +### Delete task: `delete` +Deletes the task based on the task number indicated. + +Format: `delete TASK_NUMBER` +- TASK_NUMBER must be an integer that is within the list size + +Example: `delete 1` + +### Find task(s): `find` +Searches the tasks in the list, returning the tasks containing the keyword in their description. + +Format: `find KEYWORD` +
Example: `find CS2113` + +### Exiting DOM: `bye` +Exits and closes the program. + +Format: `bye` + +## FAQ +**Q**: Can I edit the file manually from the save.txt file? +
**A**: It is not advised to do so, as formatting of the save.txt file incorrectly will cause the program to crash. + +## Command Summary +| **Command** | **Format, Examples** | +|-------------|------------------------------------------------------------------------------------------------------| +| todo | `todo DESCRIPTION`
e.g. `todo read book` | +| event | `event DESCRIPTION /from FROM /to TO`
e.g. `event CS2113 tutorial /from 4 April 2023 4pm /to 6pm` | +| deadline | `deadline DESCRIPTION /by BY`
e.g. `deadline CS2113 iP /by 3 March 11.59pm` | +| list | `list` | +| mark | `mark TASK_NUMBER`
e.g. `mark 2` | +| unmark | `unmark TASK_NUMBER`
e.g. `unmark 3` | +| delete | `delete TASK_NUMBER`
e.g. `delete 1` | +| find | `find KEYWORD`
e.g. `find CS2113` | +| bye | `bye` | diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 5d313334c..125c584ce 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -1,10 +1,284 @@ +import java.io.IOException; +import java.util.ArrayList; + +import storage.Storage; + +import tasks.Deadline; +import tasks.Event; +import tasks.Task; +import tasks.Todo; +import tasks.DukeException; +import tasks.TaskList; +import ui.Ui; +import tasks.Parser; + + +/** + * Represents the main logic for the Duke application. + * Manages the initialization, execution, and storage of tasks. + */ public class Duke { + private static final String lineDivider = "____________________________________________________________"; + private Storage storage; + private TaskList tasks; + private Ui ui; + + /* + * Initializes a Duke object with the given file path. + * + * @param filePath The file path to the file containing the tasks. + * @throws DukeException If an error occurs while loading the tasks from the file. + * + */ + public Duke(String filePath) { + ui = new Ui(); + storage = new Storage(filePath); + try { + tasks = new TaskList(storage.load()); + } catch (DukeException e) { + ui.showErrorMsg(e.getMessage()); + tasks = new TaskList(); + } + } + + /* + * Runs the Duke application. + * + * @throws DukeException If an error occurs while running the application. + */ + public void run() { + Ui.greet(); + boolean isExit = false; + while (!isExit) { + try { + String fullCommand = ui.readCommand(); + String action = Parser.parse(fullCommand); + String[] parts = action.split("-", 2); + switch(parts[0]) { + case "exit": + isExit = true; + ui.showLineDivider(); + break; + case "list": + tasks.listTasks(); + ui.showLineDivider(); + break; + case "markAsDone": + handleMarkAsDone(parts[1]); + break; + case "markAsNotDone": + handleMarkAsNotDone(parts[1]); + break; + case "delete": + handleDelete(parts[1]); + break; + case "addTodo": + handleAddTodo(parts[1]); + break; + case "addDeadline": + handleAddDeadline(parts[1]); + break; + case "addEvent": + handleAddEvent(parts[1]); + break; + case "find": + handleFind(parts[1]); + break; + default: + throw new DukeException("Unknown action"); + } + } catch (DukeException e) { + ui.showError(e.getMessage()); + } + } + ui.showGoodbye(); + } + + /* + * Handles the markAsDone command. + * also checks if the task number is valid. + * + * @param part The part of the command after the markAsDone keyword. + * @throws DukeException If an error occurs while marking the task as done. + */ + private void handleMarkAsDone(String part) { + try { + int doneIndex = Integer.parseInt(part) - 1; + tasks.markTaskAsDone(doneIndex); + ui.showUnmarkedTask(tasks.getTaskIndex(doneIndex)); + storage.save(tasks.getTasks()); + } catch (NumberFormatException e) { + ui.showErrorMsg("Error: ☹ OOPS!!! Task number must be an integer.\n"); + ui.showErrorMsg("Please enter command in the format: mark "); + } catch (IOException e) { + System.out.println("An error occurred while saving tasks to the file."); + } catch (IndexOutOfBoundsException e) { + ui.showErrorMsg("Error: ☹ OOPS!!! Invalid task number."); + } + ui.showLineDivider(); + } + + /* + * Handles the markAsNotDone command. + * also checks if the task number is valid. + * + * @param part The part of the command after the markAsNotDone keyword. + * @throws DukeException If an error occurs while marking the task as not done. + * @throws IOException If an error occurs while saving the tasks to the file. + * @throws IndexOutOfBoundsException If the task number is invalid. + */ + private void handleMarkAsNotDone(String part) { + try { + int undoneIndex = Integer.parseInt(part) - 1; + tasks.getTaskIndex(undoneIndex).markAsDone(false); + ui.showUnmarkedTask(tasks.getTaskIndex(undoneIndex)); + storage.save(tasks.getTasks()); + } catch (NumberFormatException e) { + ui.showErrorMsg("Error: ☹ OOPS!!! Task number must be an integer.\n"); + ui.showErrorMsg("Please enter command in the format: unmark "); + } catch (IndexOutOfBoundsException e) { + ui.showErrorMsg("Error: ☹ OOPS!!! Invalid task number."); + } catch (IOException e) { + System.out.println("An error occurred while saving tasks to the file."); + } + ui.showLineDivider(); + } + + /* + * Handles the delete command. + * also checks if the task number is valid. + * + * @param part The part of the command after the delete keyword. + * @throws DukeException If an error occurs while deleting the task. + * @throws IOException If an error occurs while saving the tasks to the file. + * @throws IndexOutOfBoundsException If the task number is invalid. + * @throws NumberFormatException If the task number is not an integer.q + */ + private void handleDelete(String part) { + try { + int deleteIndex = Integer.parseInt(part) - 1; + if (deleteIndex < 0 || deleteIndex >= tasks.getTaskCount()) { + System.out.println("☹ OOPS!!! Invalid task number."); + } + Task taskToDelete = tasks.getTaskIndex(deleteIndex); + tasks.deleteTask(deleteIndex); + storage.save(tasks.getTasks()); + ui.showDeletedTask(taskToDelete, tasks.getTaskCount()); + } catch (IOException e) { + System.out.println("An error occurred while saving tasks to the file."); + } catch (NumberFormatException e) { + ui.showErrorMsg("Error: ☹ OOPS!!! Task number must be an integer.\n"); + ui.showErrorMsg("Please enter command in the format: delete "); + } catch (IndexOutOfBoundsException e) { + ui.showErrorMsg("Error: ☹ OOPS!!! Invalid task number."); + } + ui.showLineDivider(); + } + + /* + * Handles the addTodo command. + * checks if the description is empty. + * + * @param part The part of the command after the addTodo keyword. + * @throws DukeException If an error occurs while adding the todo. + * @throws IOException If an error occurs while saving the tasks to the file. + */ + private void handleAddTodo(String part) { + if (part.trim().isEmpty()) { + ui.showErrorMsg("☹ OOPS!!! The description of a todo cannot be empty.\n" + lineDivider); + return; + } + Task newTodo = new Todo(part); + tasks.addTask(newTodo); + ui.showAddedTask(newTodo, tasks.getTaskCount()); + try { + storage.save(tasks.getTasks()); + } catch (IOException e) { + System.out.println("An error occurred while saving tasks to the file."); + } + ui.showLineDivider(); + } + + /* + * Handles the addDeadline command. + * checks if description or deadline is empty. + * + * @param part The part of the command after the addDeadline keyword. + * @throws DukeException If an error occurs while adding the deadline. + * @throws IOException If an error occurs while saving the tasks to the file. + */ + private void handleAddDeadline(String part) { + String[] deadlineParts = part.split("/by", 2); + if (deadlineParts.length < 2 || deadlineParts[1].trim().isEmpty() || deadlineParts[0].trim().isEmpty()) { + ui.showErrorMsg("☹ OOPS!!! The description of a deadline cannot be empty. \n" + lineDivider); + return; + } + Task newDeadline = new Deadline(deadlineParts[0].trim(), deadlineParts[1].trim()); + tasks.addTask(newDeadline); + ui.showAddedTask(newDeadline, tasks.getTaskCount()); + try { + storage.save(tasks.getTasks()); + } catch (IOException e) { + System.out.println("An error occurred while saving tasks to the file."); + } + ui.showLineDivider(); + } + + /* + * Handles the addEvent command. + * checks if description, start time or end time is empty. + * + * @param part The part of the command after the addEvent keyword. + * @throws DukeException If an error occurs while adding the event. + */ + private void handleAddEvent(String part) { + String[] eventParts = part.split("/from", 2); + if (eventParts.length < 2 || eventParts[1].trim().isEmpty() || eventParts[0].trim().isEmpty()) { + ui.showErrorMsg("☹ OOPS!!! The description of an event cannot be empty.\n" + lineDivider); + return; + } + + String[] fromToParts = eventParts[1].split("/to", 2); + if (fromToParts.length < 2 || fromToParts[0].trim().isEmpty() || fromToParts[1].trim().isEmpty()) { + ui.showErrorMsg("☹ OOPS!!! The start or end time for an event cannot be empty.\n" + lineDivider); + return; + } + + Task newEvent = new Event(eventParts[0].trim(), fromToParts[0].trim(), fromToParts[1].trim()); + tasks.addTask(newEvent); + ui.showAddedTask(newEvent, tasks.getTaskCount()); + try { + storage.save(tasks.getTasks()); + } catch (IOException e) { + System.out.println("An error occurred while saving tasks to the file."); + } + ui.showLineDivider(); + } + + /* + * Handles the find command. + * checks if the keyword is empty. + * + * @param part The part of the command after the find keyword. + * @throws DukeException If an error occurs while finding the tasks. + */ + private void handleFind(String part) { + String keyword = part.trim(); + if (keyword.isEmpty()) { + ui.showErrorMsg("☹ OOPS!!! The keyword for find cannot be empty.\n" + lineDivider); + return; + } + + ArrayList foundTasks = tasks.findTasks(keyword); + if (foundTasks.isEmpty()) { + ui.showErrorMsg("☹ OOPS!!! No matching tasks found with the given keyword.\n" + lineDivider); + return; + } + ui.showFoundTasks(foundTasks); + ui.showLineDivider(); + } + public static void main(String[] args) { - String logo = " ____ _ \n" - + "| _ \\ _ _| | _____ \n" - + "| | | | | | | |/ / _ \\\n" - + "| |_| | |_| | < __/\n" - + "|____/ \\__,_|_|\\_\\___|\n"; - System.out.println("Hello from\n" + logo); + new Duke("data/tasks.txt").run(); } } diff --git a/src/main/java/storage/Storage.java b/src/main/java/storage/Storage.java new file mode 100644 index 000000000..930aeea4a --- /dev/null +++ b/src/main/java/storage/Storage.java @@ -0,0 +1,97 @@ +package storage; + +import tasks.Task; +import tasks.Todo; +import tasks.Deadline; +import tasks.Event; +import tasks.DukeException; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Scanner; + +/** + * Represents the storage of tasks. + */ +public class Storage { + + private String filePath; + + /** + * Initializes a new Storage instance with the given file path. + * + * @param filePath The path of the file to store task data. + */ + public Storage(String filePath) { + this.filePath = filePath; + } + + /** + * Loads the tasks from the file. + * + * @return The list of tasks. + * @throws DukeException If the file is not found or the data format is incorrect. + */ + public ArrayList load() throws DukeException { + File file = new File(filePath); + ArrayList tasks = new ArrayList<>(); + + try { + Scanner scanner = new Scanner(file); + while (scanner.hasNext()) { + String data = scanner.nextLine(); + String[] parts = data.split(" \\| "); + + switch (parts[0]) { + case "T": + Todo todo = new Todo(parts[2]); + if (parts[1].equals("1")) { + todo.markAsDone(true); + } + tasks.add(todo); + break; + case "D": + Deadline deadline = new Deadline(parts[2], parts[3]); + if (parts[1].equals("1")) { + deadline.markAsDone(true); + } + tasks.add(deadline); + break; + case "E": + if (parts.length < 5) { + throw new DukeException("Stored Event task format is incorrect"); + } + Event event = new Event(parts[2], parts[3], parts[4]); + if (parts[1].equals("1")) { + event.markAsDone(true); + } + tasks.add(event); + break; + default: + System.out.println("Invalid task type"); + } + } + scanner.close(); + } catch (IOException e) { + System.out.println("Error loading file"); + } + + return tasks; + } + + /** + * Saves the tasks to the file. + * + * @param tasks The list of tasks to be saved. + * @throws IOException If there is an error writing to the file. + */ + public void save(ArrayList tasks) throws IOException { + FileWriter fw = new FileWriter(filePath); + for (Task task : tasks) { + fw.write(task.toData() + "\n"); + } + fw.close(); + } +} diff --git a/src/main/java/tasks/Deadline.java b/src/main/java/tasks/Deadline.java new file mode 100644 index 000000000..6d9d7afe8 --- /dev/null +++ b/src/main/java/tasks/Deadline.java @@ -0,0 +1,70 @@ +package tasks; + +/** + * Represents a 'Deadline' task in the Duke application. + * + * @see Task + */ +public class Deadline extends Task{ + protected String by; + + /* + * Initializes a new Deadline with the given description and deadline. + * + * @param description The description of the deadline. + * @param by The deadline of the deadline. + */ + public Deadline(String description, String by) { + super(description); + if (description == null || description.trim().isEmpty()) { + throw new IllegalArgumentException("description cannot be empty"); + } + this.by = by; + } + + /* + * Returns the deadline of the deadline. + * + * @return The deadline of the deadline. + */ + public String getBy() { + return this.by; + } + + /* + * Converts the Deadline task object to a data representation for saving to a file. + * + * @return The data representation of the Deadline task. + */ + public static Task dataToTask(String taskData) { + int firstSplitIndex = taskData.indexOf("|"); + int secondSplitIndex = taskData.indexOf("|", firstSplitIndex + 1); + boolean isDone = taskData.substring(0, firstSplitIndex - 1).equals("1"); + String desc = taskData.substring(firstSplitIndex + 2, secondSplitIndex - 1); + String by = taskData.substring(secondSplitIndex + 2); + Deadline newDeadline = new Deadline(desc, by); + newDeadline.isDone = isDone; + return newDeadline; + } + + /* + * Converts the Deadline task object to a data representation for saving to a file. + * + * @return The data representation of the Deadline task. + */ + @Override + public String toData() { + String done = String.valueOf(this.isDone ? 1 : 0); + return "D | " + done + " | " + this.description + " | " + this.by; + } + + /* + * Returns the string representation of the Deadline task. + * + * @return The string representation of the Deadline task. + */ + @Override + public String toString() { + return "[D]" + super.toString() + " (by: " + this.by + ")"; + } +} diff --git a/src/main/java/tasks/DukeException.java b/src/main/java/tasks/DukeException.java new file mode 100644 index 000000000..8f1089040 --- /dev/null +++ b/src/main/java/tasks/DukeException.java @@ -0,0 +1,22 @@ +package tasks; + +/** + * Represents an exception that is thrown when an error occurs in the Duke application. + */ +public class DukeException extends Exception { + public DukeException() { + super(); + } + + public DukeException(String message) { + super(message); + } + + public DukeException(String message, Throwable cause) { + super(message, cause); + } + + public DukeException(Throwable cause) { + super(cause); + } +} diff --git a/src/main/java/tasks/Event.java b/src/main/java/tasks/Event.java new file mode 100644 index 000000000..f31c315b8 --- /dev/null +++ b/src/main/java/tasks/Event.java @@ -0,0 +1,80 @@ +package tasks; + +/** + * Represents an 'Event' task in the Duke application. + */ +public class Event extends Task { + protected String from; + protected String to; + + /* + * Initializes a new Event with the given description, start time, and end time. + * + * @param description The description of the event. + * @param from The start time of the event. + * @param to The end time of the event. + */ + public Event(String description, String from, String to) { + super(description); + this.from = from; + this.to = to; + } + + /* + * Returns the start time of the event. + * + * @return The start time of the event. + */ + public String getFrom() { + return this.from; + } + + /* + * Returns the end time of the event. + * + * @return The end time of the event. + */ + public String getTo() { + return this.to; + } + + /* + * Converts the Event task object to a data representation for saving to a file. + * + * @return The data representation of the Event task. + */ + @Override + public String toData() { + String done = String.valueOf(this.isDone ? 1 : 0); + return "E | " + done + " | " + this.description + " | " + this.from + " | " + this.to; + } + + /* + * Returns the string representation of the Event task. + * + * @return The string representation of the Event task. + */ + @Override + public String toString() { + return "[E]" + super.toString() + "(from: " + this.from + " to: " + this.to + ")"; + } + + /* + * Returns a new Event task based on the given task data. + * + * @param taskData The task data to be converted. + * @return The new Event task. + */ + public static Task dataToTask(String taskData) { + int firstSplitIndex = taskData.indexOf("|"); + int secondSplitIndex = taskData.indexOf("|", firstSplitIndex + 1); + int thirdSplitIndex = taskData.indexOf("|", secondSplitIndex + 1); + boolean isDone = taskData.substring(0, firstSplitIndex - 1).equals("1"); + String desc = taskData.substring(firstSplitIndex + 2, secondSplitIndex - 1); + String from = taskData.substring(secondSplitIndex + 2, thirdSplitIndex - 1); + String to = taskData.substring(thirdSplitIndex + 2); + Event newEvent = new Event(desc, from, to); + newEvent.isDone = isDone; + return newEvent; + } +} diff --git a/src/main/java/tasks/Parser.java b/src/main/java/tasks/Parser.java new file mode 100644 index 000000000..ac6ea7905 --- /dev/null +++ b/src/main/java/tasks/Parser.java @@ -0,0 +1,51 @@ +package tasks; + +/** + * Represents a parser that parses user commands. + */ + +public class Parser { + /** + * Parses the user command into actionable instructions. + * + * @param fullCommand The entire command entered by the user. + * @return A string representation of the action to be taken. + * @throws DukeException If the command is invalid or incomplete. + */ + + public static String parse(String fullCommand) throws DukeException { + String trimmedCommand = fullCommand.trim(); // Remove leading and trailing whitespaces + String commandLowerCase = trimmedCommand.toLowerCase(); // Convert command to lowercase for case-insensitive matching + + if (commandLowerCase.equals("bye")) { + return "exit"; + } else if (commandLowerCase.equals("list")) { + return "list"; + } else if (commandLowerCase.startsWith("mark ")) { + return "markAsDone-" + trimmedCommand.substring(5); + } else if (commandLowerCase.startsWith("unmark ")) { + return "markAsNotDone-" + trimmedCommand.substring(7); + } else if (commandLowerCase.startsWith("delete")) { + if (commandLowerCase.length() <= 6 || commandLowerCase.substring(6).trim().isEmpty()) { + throw new DukeException("☹ OOPS!!! Please provide a task number to delete."); + } + return "delete-" + trimmedCommand.substring(7); + } else if (commandLowerCase.startsWith("todo")) { + if (commandLowerCase.length() <= 4 || commandLowerCase.substring(4).trim().isEmpty()) { + throw new DukeException("☹ OOPS!!! The description of a todo cannot be empty."); + } + return "addTodo-" + trimmedCommand.substring(5); + } else if (commandLowerCase.startsWith("deadline ")) { + // Additional parsing for deadline command + return "addDeadline-" + trimmedCommand.substring(9); + } else if (commandLowerCase.startsWith("event ")) { + // Additional parsing for event command + return "addEvent-" + trimmedCommand.substring(6); + } else if (commandLowerCase.startsWith("find ")) { + // Additional parsing for find command + return "find-" + trimmedCommand.substring(5); + } else { + throw new DukeException("Unknown command"); + } + } +} diff --git a/src/main/java/tasks/Task.java b/src/main/java/tasks/Task.java new file mode 100644 index 000000000..913662d31 --- /dev/null +++ b/src/main/java/tasks/Task.java @@ -0,0 +1,64 @@ +package tasks; + +/** + * Represents a task in the Duke application. + */ +public abstract class Task { + protected String description; + protected boolean isDone; + + + /* + * Initializes a new Task with the given description. + * + * @param description The description of the task. + * @param isDone The status of the task. + */ + public Task(String description) { + this.description = description; + this.isDone = false; + } + + /* + * Returns the status icon of the task. + * + * @return The status icon of the task. + */ + public String getStatusIcon() { + return (isDone ? "[X]" : "[ ]"); + } + + /* + * Marks the task as done. + * + * @param isDone The status of the task. + */ + public void markAsDone(boolean isDone) { + this.isDone = isDone; + } + + /* + * Returns the string representation of the task. + * + * @return The string representation of the task. + */ + public String toString() { + return getStatusIcon() + " " + this.description; + } + + /* + * Returns the description of the task. + * + * @return The description of the task. + */ + public String getDescription() { + return this.description; + } + + /* + * Converts the task object to a data representation for saving to a file. + * + * @return The data representation of the task. + */ + public abstract String toData(); +} diff --git a/src/main/java/tasks/TaskList.java b/src/main/java/tasks/TaskList.java new file mode 100644 index 000000000..dfbcda51f --- /dev/null +++ b/src/main/java/tasks/TaskList.java @@ -0,0 +1,122 @@ +package tasks; + +import java.util.ArrayList; + +/** + * Represents a list of tasks. + */ +public class TaskList { + private ArrayList tasks; + + /** + * Initializes a new TaskList instance with an empty list of tasks. + */ + public TaskList() { + this.tasks = new ArrayList<>(); + } + + /** + * Initializes a new TaskList instance with the specified list of tasks. + * + * @param tasks The list of tasks. + */ + public TaskList(ArrayList tasks) { + this.tasks = tasks; + } + + /** + * Adds a task to the list. + * + * @param task The task to be added. + */ + public void addTask(Task task) { + this.tasks.add(task); + } + + /** + * Removes a task from the list based on its index. + * + * @param index The index of the task to be removed. + */ + public void deleteTask(int index) { + this.tasks.remove(index); + } + + /** + * Returns the task at the specified index. + * + * @param index The index of the task to be returned. + * @return The task at the specified index. + */ + public Task getTaskIndex(int index) { + return this.tasks.get(index); + } + + /** + * Returns the number of tasks in the list. + * + * @return The number of tasks in the list. + */ + public int getTaskCount() { + return this.tasks.size(); + } + + /** + * Marks a specified task as done. + * + * @param index The index of the task to be marked as done. + */ + public void markTaskAsDone(int index) { + Task task = tasks.get(index); + task.markAsDone(true); + } + + /** + * Marks a specified task as undone. + * + * @param index The index of the task to be marked as undone. + */ + public void markTaskAsUndone(int index) { + Task task = tasks.get(index); + task.markAsDone(false); + } + + /** + * Prints the list of tasks. + */ + public void listTasks() { + if (tasks.size() == 0) { + System.out.println("ERROR: ☹ OOPS!!! No tasks is available."); + return; + } + 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)); + } + } + + /** + * Returns the list of tasks. + * + * @return The list of tasks. + */ + public ArrayList getTasks() { + return this.tasks; + } + + /** + * Finds tasks that contain the specified keyword. + * + * @param keyword The keyword to be searched for. + * @return The list of tasks that contain the specified keyword. + */ + public ArrayList findTasks(String keyword) { + ArrayList foundTasks = new ArrayList<>(); + for (Task task : tasks) { + if (task.getDescription().contains(keyword)) { + foundTasks.add(task); + } + } + return foundTasks; + } +} diff --git a/src/main/java/tasks/Todo.java b/src/main/java/tasks/Todo.java new file mode 100644 index 000000000..1ec2f0b58 --- /dev/null +++ b/src/main/java/tasks/Todo.java @@ -0,0 +1,46 @@ +package tasks; + +/** + * Represents a 'Todo' task in the Duke application. + */ +public class Todo extends Task { + public Todo (String description) { + super(description); + } + + /** + * Returns a new Todo task based on the given task data. + * + * @param taskData The task data to be converted. + * @return The new Todo task. + */ + public static Task dataToTask(String taskData) { + int firstSplitIndex = taskData.indexOf("|"); + boolean isDone = taskData.substring(0, firstSplitIndex - 1).equals("1"); + String desc = taskData.substring(firstSplitIndex + 2); + Todo newTodo = new Todo(desc); + newTodo.isDone = isDone; + return newTodo; + } + + /** + * Converts the Todo task object to a data representation for saving to a file. + * + * @return The data representation of the Todo task. + */ + @Override + public String toData() { + String done = String.valueOf(this.isDone ? 1 : 0); + return "T | " + done + " | " + this.description; + } + + /** + * Returns the string representation of the Todo task. + * + * @return The string representation of the Todo task. + */ + @Override + public String toString() { + return "[T]" + super.toString(); + } +} diff --git a/src/main/java/ui/Ui.java b/src/main/java/ui/Ui.java new file mode 100644 index 000000000..1d55b3c79 --- /dev/null +++ b/src/main/java/ui/Ui.java @@ -0,0 +1,205 @@ +package ui; + +import java.util.Scanner; +import java.util.ArrayList; +import tasks.Task; + +/** + * Represents the user interface of the Duke application. + */ + +public class Ui { + private Scanner scanner; + + private static final String lineDivider = "____________________________________________________________"; + + /** + * Initializes the Ui with a new Scanner instance. + */ + public Ui() { + this.scanner = new Scanner(System.in); + } + + /** + * Displays the greeting message to the user when the application starts. + */ + public static void greet() { + String logo = " ____ \n" + + " | _ \\ ___ _ __ ___ \n" + + " | | | |/ _ \\| '_ ` _ \\ \n" + + " | |_| | (_) | | | | | |\n" + + " |____/ \\___/|_| |_| |_|\n"; + System.out.println("Hello from\n" + logo); + String greetings = lineDivider + + "\nHello! I'm Dom\n" + + "What can I do for you?\n" + + lineDivider; + System.out.println(greetings); + } + + /** + * Displays a goodbye message to the user. + */ + public void goodbye() { + String goodbye = lineDivider + + "\nBye. Hope to see you again soon!\n" + + lineDivider; + System.out.println(goodbye); + } + + /** + * Displays an error message when there is a loading error. + */ + public static void showLoadingError() { + System.out.println("Error loading file"); + } + + /** + * Displays the specified error message to the user. + * + * @param errorMsg The error message to display. + */ + public void showErrorMsg(String errorMsg) { + System.out.println(errorMsg); + } + + /** + * Reads a command from the user. + * + * @return The command entered by the user. + */ + public String readCommand() { + return scanner.nextLine(); + } + + /** + * Displays a line divider to the user. + */ + public void showLineDivider() { + System.out.println(lineDivider); + } + + /** + * Lists all tasks currently in the task list. + * + * @param tasks The list of tasks to be displayed. + */ + public void listTasks(ArrayList tasks) { + if (tasks.size() == 0) { + System.out.println("You have no tasks in your list"); + return; + } + 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)); + } + } + + /** + * Displays a message to the user when a task is marked as done. + * + * @param task The task that was marked as done. + */ + public void showTaskMarked(Task task) { + System.out.println("OK, I've marked this task as done:\n" + " " + + task.getStatusIcon() + " " + task.getDescription()); + } + + + /** + * Displays a message to the user when a task is marked as undone. + * + * @param task The task that was marked as undone. + */ + public void showUnmarkedTask(Task task) { + System.out.println(" Nice! I've marked this task as not done:\n" + " " + task.getStatusIcon() + + " " + task.getDescription()); + } + + /** + * Displays a message to the user when a task is deleted. + * + * @param task The task that was deleted. + * @param taskCount The total number of tasks in the list after deletion. + */ + public void showInvalidTaskNumberError() { + System.out.println("Error: Invalid task number."); + } + + /* + * Displays a message to the user when a task is deleted. + * + * @param task The task that was deleted. + * @param taskCount The total number of tasks in the list after deletion. + * + */ + public void showEmptyTaskNumberError() { + System.out.println("Error: ☹ OOPS!!! Task number cannot be empty\n"); + System.out.println("Please enter command in the format: unmark "); + } + + /* + * Displays a message to the user when a task is deleted. + * + * @param task The task that was deleted. + * @param taskCount The total number of tasks in the list after deletion. + * + */ + public void showInvalidTaskNumberFormatError() { + System.out.println("Error: ☹ OOPS!!! Task number must be an integer.\n"); + System.out.println("Please enter command in the format: unmark "); + } + + /** + * Displays a message to the user when a task is added. + * @param task The task that was added. + * @param taskCount The total number of tasks in the list after addition. + */ + public void showAddedTask(Task task, int taskCount) { + 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."); + } + + /** + * Displays a message to the user when a task is deleted. + * @param task The task that was deleted. + * @param taskCount The total number of tasks in the list after deletion. + */ + public void showDeletedTask(Task task, int taskCount) { + System.out.println("Noted. I've removed this task:"); + System.out.println(" " + task); + System.out.println("Now you have " + taskCount + " tasks in the list."); + } + + /** + * Displays a message to the user when a task is found. + * + * @param task The task that was found. + */ + public void showError(String errorMessage) { + System.out.println(errorMessage); + } + + /** + * Displays a message to the user when a task is found. + * + * @param task The task that was found. + */ + public void showGoodbye() { + System.out.println("Bye. Hope to see you again soon!"); + } + + /** + * Displays a message to the user when a task is found. + * + * @param task The task that was found. + */ + public void showFoundTasks(ArrayList foundTasks) { + System.out.println(lineDivider); + System.out.println("Here are the matching tasks in your list:"); + for (int i = 0; i < foundTasks.size(); i++) { + System.out.println((i + 1) + "." + foundTasks.get(i)); + } + } +} diff --git a/text-ui-test/runtest.sh b/text-ui-test/runtest.sh index c9ec87003..15e6ff3ae 100644 --- a/text-ui-test/runtest.sh +++ b/text-ui-test/runtest.sh @@ -13,7 +13,7 @@ then fi # compile the code into the bin folder, terminates if error occurred -if ! javac -cp ../src/main/java -Xlint:none -d ../bin ../src/main/java/*.java +if ! javac -cp /Users/bryanlee/Desktop/NUS/NUS Y3S1/CS2113/ip/src/main/java -Xlint:none -d ../bin /Users/bryanlee/Desktop/NUS/NUS Y3S1/CS2113/ip/src/main/java/Duke.java then echo "********** BUILD FAILURE **********" exit 1 diff --git a/text-ui-test/tempCodeRunnerFile.sh b/text-ui-test/tempCodeRunnerFile.sh new file mode 100644 index 000000000..e73c4013e --- /dev/null +++ b/text-ui-test/tempCodeRunnerFile.sh @@ -0,0 +1 @@ +/Users/bryanlee/Desktop/NUS/NUS Y3S1/CS2113/ip/src/main/java/Duke.java \ No newline at end of file