diff --git a/docs/README.md b/docs/README.md
index 47b9f984f..58563f389 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,30 +1,169 @@
-# Duke User Guide
+# Volkov User Guide
-// Update the title above to match the actual product name
+Volkov is a command-line task manager that allows you to add 3 different types of tasks,
+set their relevant dates, and track your progress on the tasks with simple commands.
+Your tasks are saved automatically, allowing you to revisit whenever you need to.
-// Product screenshot goes here
+## Adding a Todo Task: `todo`
+Adds a todo task for Volkov to remember.
-// Product intro goes here
+**Format:**
+`todo DESCRIPTION`
+**Examples:**
+```
+todo buy groceries
+```
+**Expected Outcome:**
+```
+ ____________________________________________________________
+ Roger. Task has been added to the list:
+ [T][ ] buy groceries
+ You now have 1 tasks in the list.
+ ____________________________________________________________
+
+```
+
+## Adding a Deadline Task: `deadline`
+Adds a deadline task for Volkov to remember.
+
+**Format:**
+`deadline DESCRIPTION /by DATE`
+**Examples:**
+```
+deadline do homework /by 2025-03-14
+```
+**Expected Outcome:**
+```
+ ____________________________________________________________
+ Roger. Task has been added to the list:
+ [D][ ] do homework (by: Mar 14 2025)
+ You now have 2 tasks in the list.
+ ____________________________________________________________
+```
+
+## Adding an Event Task: `event`
+Adds an event task for Volkov to remember.
+
+**Format:**
+`event DESCRIPTION /from STARTDATE /to ENDDATE`
+**Examples:**
+```
+event open house /from 2025-02-16 /to 2025-02-18
+```
+**Expected Outcome:**
+```
+ ____________________________________________________________
+ Roger. Task has been added to the list:
+ [E][ ] open house (from: Feb 16 2025 to: Feb 18 2025)
+ You now have 3 tasks in the list.
+ ____________________________________________________________
+```
+
+## Mark a task as done: `mark`
+Marks a task as completed in Volkov's task list.
+
+**Format:**
+`mark TASKNO`
+**Examples:**
+```
+mark 1
+```
+**Expected Outcome:**
+```
+ ____________________________________________________________
+ Roger. This task has been marked as done:
+ [T][X] buy groceries
+ ____________________________________________________________
+```
-## Adding deadlines
+## Mark a task as notdone: `unmark`
+Marks a task as uncompleted in Volkov's task list.
-// Describe the action and its outcome.
+**Format:**
+`unmark TASKNO`
+**Examples:**
+```
+unmark 1
+```
+**Expected Outcome:**
+```
+ ____________________________________________________________
+ Roger. This task has been marked as not done yet:
+ [T][ ] buy groceries
+ ____________________________________________________________
+```
-// Give examples of usage
+## Delete a task: `delete`
+Removes a task from Volkov's task list.
-Example: `keyword (optional arguments)`
+**Format:**
+`delete TASKNO`
+**Examples:**
+```
+delete 2
+```
+**Expected Outcome:**
+```
+ ____________________________________________________________
+ Roger. Task has been removed:
+ [D][ ] do homework (by: Mar 14 2025)
+ You now have 2 tasks in the list.
+ ____________________________________________________________
+```
-// A description of the expected outcome goes here
+## List all tasks: `list`
+Lists all tasks in Volkov's task list.
+**Format:**
+`list`
+**Expected Outcome:**
```
-expected output
+ ____________________________________________________________
+ These are all the tasks in your list:
+ 1.[T][ ] buy groceries
+ 2.[E][ ] open house (from: Feb 16 2025 to: Feb 18 2025)
+ ____________________________________________________________
```
-## Feature ABC
+## Find a task: `find`
+Finds the tasks in Volkov's task list that match the query
-// Feature details
+**Format:**
+`find SEARCHTERM`
+**Examples:**
+```
+find groceries
+```
+**Expected Outcome:**
+```
+ ____________________________________________________________
+ These are the tasks that match you search query:
+ 1.[T][ ] buy groceries
+ ____________________________________________________________
+```
+
+## Exit Program: `bye`
+Saves the task list on the hard disk and closes the program
+**Format:**
+`bye`
+**Expected Outcome:**
+```
+ ____________________________________________________________
+ See you again soon! Service terminated.
+ ____________________________________________________________
+```
-## Feature XYZ
+### Command Summary
-// Feature details
\ No newline at end of file
+| Action | Format and Examples |
+|--------------|--------------------------------------------------------------------------------------------------------------|
+| **Todo** | `todo DESCRIPTION`
e.g., `todo buy groceries` |
+| **Deadline** | `deadline DESCRIPTION /by DATE`
e.g., `deadline do homework /by 2025-03-14` |
+| **Event** | `event DESCRIPTION /from STARTDATE /to ENDDATE`
e.g., `event open house /from 2025-02-16 /to 2025-02-18` |
+| **Mark** | `mark TASKNO`
e.g., `mark 1` |
+| **Unmark** | `unmark TASKNO`
e.g., `unmark 1` |
+| **Delete** | `delete TASKNO`
e.g., `delete 1` |
+| **List** | `list` |
+| **Find** | `find SEARCHTERM`
e.g., `find groceries` |
+| **Bye** | `bye` |
diff --git a/src/main/java/ByeCommand.java b/src/main/java/ByeCommand.java
new file mode 100644
index 000000000..1498397ce
--- /dev/null
+++ b/src/main/java/ByeCommand.java
@@ -0,0 +1,25 @@
+/**
+ * Represents the command to exit the program
+ */
+public class ByeCommand extends Commands {
+
+ /**
+ * Executes the exit command by displaying a farewell message to the user.
+ *
+ * @param tasks The {@code TaskList} containing all tasks.
+ * @param ui The {@code Ui} for user interaction.
+ * @param storage The {@code Storage} for file operations.
+ */
+ public void execute(TaskList tasks, Ui ui, Storage storage) {
+ new Ui().formatResponse(" See you again soon! Service terminated.");
+ }
+
+ /**
+ * Returns boolean true to indicate the exiting of the program
+ *
+ * @return {@code true}
+ */
+ public boolean isExit() {
+ return true;
+ }
+}
diff --git a/src/main/java/Commands.java b/src/main/java/Commands.java
new file mode 100644
index 000000000..eae21798c
--- /dev/null
+++ b/src/main/java/Commands.java
@@ -0,0 +1,29 @@
+/**
+ * Represents an abstract command to be executed in the main body.
+ * Subclasses of {@code Commands} implements methods with specific behavior
+ * relevant to their respective commands
+ */
+public abstract class Commands {
+ /**
+ * Executes the command with the task list, user interface, and storage.
+ *
+ * Exact implementations depends on the relevant command. + * + * @param tasks The {@code TaskList} containing all tasks. + * @param ui The {@code Ui} object handling user interactions. + * @param storage The {@code Storage} object for file operations. + */ + public void execute(TaskList tasks, Ui ui, Storage storage) {} + + /** + * Determines whether this command is an exit command. + *
+ * Defaults to {@code false}, and only exit commands + * would override this method to return {@code true}. + * + * @return {@code true} if the command should terminate the program, otherwise {@code false}. + */ + public boolean isExit() { + return false; + } +} diff --git a/src/main/java/Deadline.java b/src/main/java/Deadline.java new file mode 100644 index 000000000..6e62d7ae5 --- /dev/null +++ b/src/main/java/Deadline.java @@ -0,0 +1,49 @@ +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; + +/** + * Represents a deadline task with a description, completion status, and deadline date. + *
+ * The {@code Deadline} class provides methods to mark the task as done, + * unmark it, retrieve its status, and formatting for displaying or saving. + */ +public class Deadline extends Task { + protected String deadline; + protected LocalDate deadlineLocalDate; + + public Deadline(String description, String deadline) { + super(description); + this.deadline = deadline; + try { + this.deadlineLocalDate = LocalDate.parse(deadline); + } catch (DateTimeParseException e) { + this.deadlineLocalDate = null; + } + + } + + /** + * {@inheritDoc} + * This method is inherited from {@link Task}. + * @return A string representation of the task, its description, and deadline date + */ + public String txtSave() { + return "[D]" + super.txtSave() + "|" + deadline; + } + + /** + * {@inheritDoc} + * This method is inherited from {@link Task}. + * @return A formatted string representing the task. + */ + @Override + public String toString() { + if (deadlineLocalDate != null) { + String deadlineFormatted = deadlineLocalDate.format(DateTimeFormatter.ofPattern("MMM d yyyy")); + return "[D]" + super.toString() + " (by: " + deadlineFormatted + ")"; + } else { + return "[D]" + super.toString() + " (by: " + deadline + ")"; + } + } +} diff --git a/src/main/java/DeadlineCommand.java b/src/main/java/DeadlineCommand.java new file mode 100644 index 000000000..035b449ae --- /dev/null +++ b/src/main/java/DeadlineCommand.java @@ -0,0 +1,30 @@ +/** + * Represents the command to add a deadline task + *
+ * The {@code DeadlineCommand} creates a new {@code Deadline} task + * with a specified description and a deadline date, + * and adds it to the {@code TaskList}. + */ +public class DeadlineCommand extends Commands { + private final String taskDesc; + private final String deadline; + + DeadlineCommand(String taskDesc, String deadline) { + this.taskDesc = taskDesc; + this.deadline = deadline; + } + + /** + * Executes the deadline command by creating a {@code Deadline} object, + * adds it to the taskList and displays a confirmation message. + * + * @param tasks The {@code TaskList} containing all tasks. + * @param ui The {@code Ui} for user interaction. + * @param storage The {@code Storage} for file operations. + */ + public void execute(TaskList tasks, Ui ui, Storage storage) { + Deadline t = new Deadline(taskDesc, deadline); + tasks.addTask(t); + ui.formatTaskMsg(t, tasks); + } +} diff --git a/src/main/java/DeleteCommand.java b/src/main/java/DeleteCommand.java new file mode 100644 index 000000000..51509ebcb --- /dev/null +++ b/src/main/java/DeleteCommand.java @@ -0,0 +1,36 @@ +/** + * Represents the command to delete a task based on its taskNo from the tasklist + */ +public class DeleteCommand extends Commands { + private final int taskNo; + + DeleteCommand(int taskNo) { + this.taskNo = taskNo; + } + + /** + * Executes the delete command by removing the specified task from the task list. + * Displays a confirmation message when successfully deleted. + * If the task number is invalid, an error message is displayed instead. + * + * @param tasks The {@code TaskList} containing all tasks. + * @param ui The {@code Ui} for user interaction. + * @param storage The {@code Storage} for file operations. + */ + public void execute(TaskList tasks, Ui ui, Storage storage) { + try { + String reply = " Roger. Task has been removed:\n" + + " " + tasks.getTask(taskNo).toString() + "\n" + + " You now have " + (tasks.size()-1) + " tasks in the list."; + ui.formatResponse(reply); + tasks.removeTask(taskNo); + } catch (NullPointerException | IndexOutOfBoundsException e) { + String reply = " Task number not found, reenter with correct task number:"; + new MissingCommand(reply).execute(tasks, ui, storage); + } catch (NumberFormatException e) { + String reply = " No task number detected, reenter with correct task number:"; + new MissingCommand(reply).execute(tasks, ui, storage); + } + + } +} diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java deleted file mode 100644 index 5d313334c..000000000 --- a/src/main/java/Duke.java +++ /dev/null @@ -1,10 +0,0 @@ -public class Duke { - public static void main(String[] args) { - String logo = " ____ _ \n" - + "| _ \\ _ _| | _____ \n" - + "| | | | | | | |/ / _ \\\n" - + "| |_| | |_| | < __/\n" - + "|____/ \\__,_|_|\\_\\___|\n"; - System.out.println("Hello from\n" + logo); - } -} diff --git a/src/main/java/Event.java b/src/main/java/Event.java new file mode 100644 index 000000000..dbecc1b1f --- /dev/null +++ b/src/main/java/Event.java @@ -0,0 +1,54 @@ +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; + +/** + * Represents an event task with a description, completion status, and start and end dates. + *
+ * The {@code Event} class provides methods to mark the task as done, + * unmark it, retrieve its status, and formatting for displaying or saving. + */ +public class Event extends Task { + protected String startDate; + protected String endDate; + protected LocalDate startLocalDate; + protected LocalDate endLocalDate; + + public Event(String description, String startDate, String endDate) { + super(description); + this.startDate = startDate; + this.endDate = endDate; + try { + this.startLocalDate = LocalDate.parse(startDate); + this.endLocalDate = LocalDate.parse(endDate); + } catch (DateTimeParseException e) { + this.startLocalDate = null; + this.endLocalDate = null; + } + } + + /** + * {@inheritDoc} + * This method is inherited from {@link Task}. + * @return A string representation of the task, its description, and start and end dates. + */ + public String txtSave() { + return "[E]" + super.txtSave() + "|" + startDate + "|" + endDate; + } + + /** + * {@inheritDoc} + * This method is inherited from {@link Task}. + * @return A formatted string representing the task. + */ + @Override + public String toString() { + if (startLocalDate != null && endLocalDate != null) { + String startDateFormatted = startLocalDate.format(DateTimeFormatter.ofPattern("MMM d yyyy")); + String endDateFormatted = endLocalDate.format(DateTimeFormatter.ofPattern("MMM d yyyy")); + return "[E]" + super.toString() + " (from: " + startDateFormatted + " to: " + endDateFormatted + ")"; + } else { + return "[E]" + super.toString() + " (from: " + startDate + " to: " + endDate + ")"; + } + } +} diff --git a/src/main/java/EventCommand.java b/src/main/java/EventCommand.java new file mode 100644 index 000000000..6d4071ea8 --- /dev/null +++ b/src/main/java/EventCommand.java @@ -0,0 +1,32 @@ +/** + * Represents the command to add an event task. + *
+ * The {@code EventCommand} creates a new {@code Event} task with + * a specified description and a start and end date, + * and adds it to the {@code TaskList}. + */ +public class EventCommand extends Commands { + private final String taskDesc; + private final String startDate; + private final String endDate; + + EventCommand(String taskDesc, String startDate, String endDate) { + this.taskDesc = taskDesc; + this.startDate = startDate; + this.endDate = endDate; + } + + /** + * Executes the command by creating a new {@code Event} object, + * adds it to the task list, and displays a confirmation message. + * + * @param tasks The {@code TaskList} containing all tasks. + * @param ui The {@code Ui} for user interaction. + * @param storage The {@code Storage} for file operations. + */ + public void execute(TaskList tasks, Ui ui, Storage storage) { + Event t = new Event(taskDesc, startDate, endDate); + tasks.addTask(t); + ui.formatTaskMsg(t, tasks); + } +} diff --git a/src/main/java/FindCommand.java b/src/main/java/FindCommand.java new file mode 100644 index 000000000..a4657e8e7 --- /dev/null +++ b/src/main/java/FindCommand.java @@ -0,0 +1,33 @@ +/** + * Represents the command to search for tasks + *
+ * The {@code FindCommand} finds the task the user wishes to search for in the {@code TaskList}. + */ +public class FindCommand extends Commands { + private final String searchTerm; + + FindCommand(String searchTerm) { + this.searchTerm = searchTerm; + } + + /** + * Executes the command by iterating through and printing every task in the {@code TaskList} + * that matches the searchTerm. + * + * @param tasks The {@code TaskList} containing all tasks. + * @param ui The {@code Ui} for user interaction. + * @param storage The {@code Storage} for file operations. + */ + public void execute(TaskList tasks, Ui ui, Storage storage) { + StringBuilder sb = new StringBuilder(); + sb.append(" These are the tasks that match you search query:"); + int count = 1; + for (int i = 0; i < tasks.size(); i++) { + if (tasks.getTask(i).find(searchTerm)) { + sb.append("\n ").append(count).append(".").append(tasks.getTask(i)); + count++; + } + } + ui.formatResponse(sb.toString()); + } +} diff --git a/src/main/java/ListCommand.java b/src/main/java/ListCommand.java new file mode 100644 index 000000000..b5abb0a3a --- /dev/null +++ b/src/main/java/ListCommand.java @@ -0,0 +1,23 @@ +/** + * Represents the command to list all tasks in the task list. + *
+ * The {@code ListCommand} retrieves and displays all tasks currently stored in the {@code TaskList}. + */ +public class ListCommand extends Commands { + + /** + * Executes the command by iterating through and printing every task in the {@code TaskList}. + * + * @param tasks The {@code TaskList} containing all tasks. + * @param ui The {@code Ui} for user interaction. + * @param storage The {@code Storage} for file operations. + */ + public void execute(TaskList tasks, Ui ui, Storage storage) { + StringBuilder sb = new StringBuilder(); + sb.append(" These are all the tasks in your list:"); + for (int i = 0; i < tasks.size(); i++) { + sb.append("\n ").append(i + 1).append(".").append(tasks.getTask(i)); + } + ui.formatResponse(sb.toString()); + } +} diff --git a/src/main/java/MarkCommand.java b/src/main/java/MarkCommand.java new file mode 100644 index 000000000..2f32d0e11 --- /dev/null +++ b/src/main/java/MarkCommand.java @@ -0,0 +1,39 @@ +/** + * Represents the command to mark a task as done. + *
+ * The {@code MarkCommand} updates the specified task number's + * task's status as done and displays a confirmation message to the user. + */ +public class MarkCommand extends Commands { + private final int taskNo; + + MarkCommand(int taskNo) { + this.taskNo = taskNo; + } + + /** + * Executes the command by marking the specified task as done. + * Displays a confirmation message when successful, or an error + * message if the task number is invalid. + * + * @param tasks The {@code TaskList} containing all tasks. + * @param ui The {@code Ui} for user interaction. + * @param storage The {@code Storage} for file operations. + */ + public void execute(TaskList tasks, Ui ui, Storage storage) { + try { + tasks.getTask(taskNo).markAsDone(); + String reply = " Roger. This task has been marked as done:\n" + + " " + tasks.getTask(taskNo).toString(); + ui.formatResponse(reply); + } catch (NullPointerException | IndexOutOfBoundsException e) { + String reply = " Task number not found, reenter with correct task number:"; + new MissingCommand(reply).execute(tasks, ui, storage); + } catch (NumberFormatException e) { + String reply = " No task number detected, reenter with correct task number:"; + new MissingCommand(reply).execute(tasks, ui, storage); + } + } + + +} diff --git a/src/main/java/MissingCommand.java b/src/main/java/MissingCommand.java new file mode 100644 index 000000000..787481674 --- /dev/null +++ b/src/main/java/MissingCommand.java @@ -0,0 +1,25 @@ +/** + * Represents a command for handling unrecognized or incomplete commands. + *
+ * The {@code MissingCommand} is used when a user enters an invalid command + * or lacks relevant parameters. It then displays an error message to + * guide the user in correcting this. + */ +public class MissingCommand extends Commands{ + private final String reply; + + MissingCommand(String reply){ + this.reply = reply; + } + + /** + * Executes the command by displaying the provided error message to the user. + * + * @param tasks The {@code TaskList} containing all tasks. + * @param ui The {@code Ui} for user interaction. + * @param storage The {@code Storage} for file operations. + */ + public void execute(TaskList tasks, Ui ui, Storage storage) { + new Ui().formatResponse(" " + reply); + } +} diff --git a/src/main/java/MissingDescription.java b/src/main/java/MissingDescription.java new file mode 100644 index 000000000..7a2076be2 --- /dev/null +++ b/src/main/java/MissingDescription.java @@ -0,0 +1,5 @@ +public class MissingDescription extends RuntimeException { + public MissingDescription(String message) { + super(message); + } +} diff --git a/src/main/java/MissingKeyword.java b/src/main/java/MissingKeyword.java new file mode 100644 index 000000000..9ba9d6caa --- /dev/null +++ b/src/main/java/MissingKeyword.java @@ -0,0 +1,5 @@ +public class MissingKeyword extends RuntimeException { + public MissingKeyword(String message) { + super(message); + } +} diff --git a/src/main/java/Parser.java b/src/main/java/Parser.java new file mode 100644 index 000000000..21f90d611 --- /dev/null +++ b/src/main/java/Parser.java @@ -0,0 +1,131 @@ +/** + * The {@code Parser} class deals with making sense of the user command, + * then maps it to the respective commands. + */ +public class Parser { + private static final int FIND_CMD_LENGTH = 5; + private static final int MARK_CMD_LENGTH = 5; + private static final int UNMARK_CMD_LENGTH = 7; + private static final int DELETE_CMD_LENGTH = 7; + + private static final int TODO_CMD_LENGTH = 5; + private static final int DEADLINE_CMD_LENGTH = 8; + private static final int EVENT_CMD_LENGTH = 5; + private static final int DEADLINE_BY_LENGTH = 4; + private static final int EVENT_FROM_LENGTH = 6; + private static final int EVENT_TO_LENGTH = 4; + + private static final String BYE_COMMAND = "bye"; + private static final String LIST_COMMAND = "list"; + private static final String FIND_COMMAND = "find"; + private static final String MARK_COMMAND = "mark"; + private static final String UNMARK_COMMAND = "unmark"; + private static final String DELETE_COMMAND = "delete"; + private static final String TODO_COMMAND = "todo"; + private static final String DEADLINE_COMMAND = "deadline"; + private static final String EVENT_COMMAND = "event"; + private static final String BY_KEYWORD = "/by"; + private static final String FROM_KEYWORD = "/from"; + private static final String TO_KEYWORD = "/to"; + + /** + * Parses the given user input and returns the corresponding command. + *
+ * This method identifies the relevant command by checking the input string + * against predetermined command keywords and extracts the respective necessary + * parameters, using which to return an appropriate {@code Commands} object. + * + * @param input The user input to parse. + * @return A {@code Commands} object representing the parsed command. + */ + public static Commands parse(String input) { + if (input.startsWith(BYE_COMMAND)) { + return new ByeCommand(); + } else if (input.startsWith(LIST_COMMAND)) { + return new ListCommand(); + + } else if (input.startsWith(FIND_COMMAND)) { + String searchTerm = input.substring(FIND_CMD_LENGTH); + return new FindCommand(searchTerm); + + } else if (input.startsWith(MARK_COMMAND)) { + int taskNo = Integer.parseInt(input.substring(MARK_CMD_LENGTH)) - 1; + return new MarkCommand(taskNo); + + } else if (input.startsWith(UNMARK_COMMAND)) { + int taskNo = Integer.parseInt(input.substring(UNMARK_CMD_LENGTH)) - 1; + return new UnmarkCommand(taskNo); + + } else if (input.startsWith(DELETE_COMMAND)) { + int taskNo = Integer.parseInt(input.substring(DELETE_CMD_LENGTH)) - 1; + return new DeleteCommand(taskNo); + + } else if (input.startsWith(TODO_COMMAND)) { + try { + String taskDesc = input.substring(TODO_CMD_LENGTH); + if (taskDesc.trim().isEmpty()) { + throw new MissingDescription("MISSING DESCRIPTION, reenter with description"); + } + return new TodoCommand(taskDesc); + } catch (MissingDescription e) { + return new MissingCommand(" " + e.getMessage()); + } catch (IndexOutOfBoundsException e) { + return new MissingCommand(" " + "MISSING DESCRIPTION, reenter with description"); + } + + } else if (input.startsWith(DEADLINE_COMMAND)) { + try { + int indexOfDeadline = input.indexOf(BY_KEYWORD); + if (indexOfDeadline == -1) { + throw new MissingKeyword("MISSING '/by' keyword"); + } + String taskDesc = input.substring(DEADLINE_CMD_LENGTH, indexOfDeadline - 1).trim(); + if (taskDesc.trim().isEmpty()) { + throw new MissingDescription("MISSING DESCRIPTION, reenter with description"); + } + String deadline = input.substring(indexOfDeadline - 1 + DEADLINE_BY_LENGTH).trim(); + if (deadline.trim().isEmpty()) { + throw new MissingDescription("MISSING DEADLINE, reenter with deadline"); + } + return new DeadlineCommand(taskDesc, deadline); + } catch (MissingDescription | MissingKeyword e) { + return new MissingCommand(" " + e.getMessage()); + } catch (IndexOutOfBoundsException e) { + return new MissingCommand(" " + "MISSING DESCRIPTION, reenter with description"); + } + + } else if (input.startsWith(EVENT_COMMAND)) { + try { + int indexOfStartDate = input.indexOf(FROM_KEYWORD); + if (indexOfStartDate == -1) { + throw new MissingKeyword("MISSING '/from' keyword"); + } + int indexOfEndDate = input.indexOf(TO_KEYWORD); + if (indexOfEndDate == -1) { + throw new MissingKeyword("MISSING '/to' keyword"); + } + String taskDesc = input.substring(EVENT_CMD_LENGTH, indexOfStartDate - 1).trim(); + if (taskDesc.trim().isEmpty()) { + throw new MissingDescription("MISSING DESCRIPTION, reenter with description"); + } + + String startDate = input.substring(indexOfStartDate - 1 + EVENT_FROM_LENGTH, indexOfEndDate - 1).trim(); + if (startDate.trim().isEmpty()) { + throw new MissingDescription("MISSING DESCRIPTION, reenter with description"); + } + String endDate = input.substring(indexOfEndDate - 1 + EVENT_TO_LENGTH).trim(); + if (endDate.trim().isEmpty()) { + throw new MissingDescription("MISSING DESCRIPTION, reenter with description"); + } + return new EventCommand(taskDesc, startDate, endDate); + } catch (MissingDescription | MissingKeyword e) { + return new MissingCommand(" " + e.getMessage()); + } catch (IndexOutOfBoundsException e) { + return new MissingCommand(" " + "MISSING DESCRIPTION, reenter with description"); + } + + } else { + return new MissingCommand("Unknown command received, reenter command:"); + } + } +} diff --git a/src/main/java/Storage.java b/src/main/java/Storage.java new file mode 100644 index 000000000..cb28404c6 --- /dev/null +++ b/src/main/java/Storage.java @@ -0,0 +1,113 @@ +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Scanner; + +/** + * The {@code Storage} class deals with the loading + * and saving of tasks in a local txt file. + */ +public class Storage { + private final String filePath; + Ui ui; + + Storage(String filePath, Ui ui) { + this.filePath = filePath; + this.ui = ui; + } + + /** + * Saves the current list of tasks to a txt file. + *
+ * If the file or directory does not exist, it will be created first. + * Tasks are saved in a format to ease the load process + * + * @param taskList The list of tasks to be saved. + */ + public void saveFile(TaskList taskList) { + try { + File f = new File(filePath); + + File parentDir = f.getParentFile(); + if (!parentDir.exists()) { + if (parentDir.mkdirs()) { + ui.formatResponse(" Folder missing, new folder created"); + } + } + + if (f.createNewFile()) { + ui.formatResponse(" No file present, new file created"); + } + + FileWriter fw = new FileWriter(filePath); + for (Task task : taskList.getList()) { + fw.write(task.txtSave() + "\n"); + } + fw.close(); + + } catch (IOException e) { + System.out.println("Something went wrong: " + e.getMessage()); + } + } + + /** + * Attempts to load tasks from the txt file into a new {@code TaskList}. + *
+ * If the file exists, it greets the user as a returning user, + * reads, parses and loads the tasks from the txt file to a new {@code TaskList} + * which then gets returned. Otherwise, it greets the user as a new user + * and returns an empty {@code TaskList}. + * + * @return A {@code TaskList} containing the loaded tasks. + */ + public TaskList loadFile() { + TaskList taskList = new TaskList(); + + File f = new File(filePath); + if (f.exists()) { + ui.returnUserGreet(); + } else { + ui.newUserGreet(); + } + + try { + Scanner s = new Scanner(f); + while (s.hasNextLine()) { + String taskString = s.nextLine(); + + if (taskString.startsWith("[T]")) { + Task t = new Todo(taskString.substring(7).trim()); + if (taskString.contains("[X]")) { + t.markAsDone(); + } + taskList.addTask(t); + } else if (taskString.startsWith("[D]")) { + String[] descAndDate = taskString.substring(7).split("\\|"); + String desc = descAndDate[0].trim(); + String date = descAndDate[1].trim(); + Task t = new Deadline(desc, date); + if (taskString.contains("[X]")) { + t.markAsDone(); + } + taskList.addTask(t); + } else if (taskString.startsWith("[E]")) { + String[] descAndDate = taskString.substring(7).split("\\|"); + String desc = descAndDate[0].trim(); + String start = descAndDate[1].trim(); + String end = descAndDate[2].trim(); + Task t = new Event(desc, start, end); + if (taskString.contains("[X]")) { + t.markAsDone(); + } + taskList.addTask(t); + } + } + } catch (FileNotFoundException e) { + + } + return taskList; + } +} + + diff --git a/src/main/java/Task.java b/src/main/java/Task.java new file mode 100644 index 000000000..d640d7e85 --- /dev/null +++ b/src/main/java/Task.java @@ -0,0 +1,61 @@ +/** + * Represents a generic task with a description and completion status. + *
+ * The {@code Task} class provides methods to mark the task as done, + * unmark it, retrieve its status, and formatting for displaying or saving. + */ +public class Task { + protected String description; + protected boolean isDone; + + public Task(String description) { + this.description = description; + this.isDone = false; + } + + public void markAsDone() { + isDone = true; + } + + public void unmarkAsDone() { + isDone = false; + } + + /** + * Returns the string formatting that represents the completion status of the task. + * + * @return A string "[X] " if the task is done, "[ ] " if not. + */ + public String getMark() { + if (isDone) { + return "[X] "; + } else { + return "[ ] "; + } + } + + public boolean find(String searchTerm) { + return this.description.contains(searchTerm); + } + + /** + * Returns the string formatting used for saving of the task's information + * + * @return A string representation of the task and its information + */ + public String txtSave() { + return getMark() + description; + } + + /** + * Returns a string representation of the task, + * including its type, completion status and description. + * + * @return A formatted string representing the task. + */ + @Override + public String toString() { + String mark = getMark(); + return mark + description; + } +} diff --git a/src/main/java/TaskList.java b/src/main/java/TaskList.java new file mode 100644 index 000000000..f4191d2c5 --- /dev/null +++ b/src/main/java/TaskList.java @@ -0,0 +1,31 @@ +/** + * Contains the list of tasks + *
+ * The {@code TaskList} class provides methods to add, remove, retrieve,
+ * and access the number of tasks stored in an {@code ArrayList}.
+ */
+import java.util.ArrayList;
+
+public class TaskList {
+ private static final ArrayList
+ * The {@code Todo} class provides methods to mark the task as done,
+ * unmark it, retrieve its status, and formatting for displaying or saving.
+ */
+public class Todo extends Task {
+
+ public Todo(String description) {
+ super(description);
+ }
+
+ /**
+ * {@inheritDoc}
+ * This method is inherited from {@link Task}.
+ * @return A string representation of the task and its description
+ */
+ public String txtSave() {
+ return "[T]" + super.txtSave();
+ }
+
+ /**
+ * {@inheritDoc}
+ * This method is inherited from {@link Task}.
+ * @return A formatted string representing the task.
+ */
+ @Override
+ public String toString() {
+ return "[T]" + super.toString();
+ }
+}
diff --git a/src/main/java/TodoCommand.java b/src/main/java/TodoCommand.java
new file mode 100644
index 000000000..109bf35b9
--- /dev/null
+++ b/src/main/java/TodoCommand.java
@@ -0,0 +1,27 @@
+/**
+ * Represents a command to add a new to-do task.
+ *
+ * The {@code TodoCommand} creates a new to-do task with a specified description
+ * and adds it to the task list.
+ * @param t The task that was added.
+ * @param tasks The task list that the task is added to.
+ */
+ public void formatTaskMsg(Task t, TaskList tasks) {
+ String msg = " Roger. Task has been added to the list:\n"
+ + " " + t.toString() + "\n"
+ + " You now have " + tasks.size() + " tasks in the list.";
+ System.out.println(formatResponseString(msg));
+ }
+
+ public String readCommand() {
+ return sc.nextLine();
+ }
+
+ /**
+ * Displays the new user greeting message.
+ */
+ public void newUserGreet() {
+ System.out.println(newUserOpening);
+ }
+
+ /**
+ * Displays the returning user greeting message.
+ */
+ public void returnUserGreet() {
+ System.out.println(returnUserOpening);
+ }
+}
diff --git a/src/main/java/UnmarkCommand.java b/src/main/java/UnmarkCommand.java
new file mode 100644
index 000000000..cfd3c6d55
--- /dev/null
+++ b/src/main/java/UnmarkCommand.java
@@ -0,0 +1,37 @@
+/**
+ * Represents the command to mark a task as not done.
+ *
+ * The {@code UnmarkCommand} updates the specified task number's
+ * task's status as not done and displays a confirmation message to the user.
+ */
+public class UnmarkCommand extends Commands {
+ private final int taskNo;
+
+ UnmarkCommand(int taskNo) {
+ this.taskNo = taskNo;
+ }
+
+ /**
+ * Executes the command by marking the specified task as not done.
+ * Displays a confirmation message when successful, or an error
+ * message if the task number is invalid.
+ *
+ * @param tasks The {@code TaskList} containing all tasks.
+ * @param ui The {@code Ui} for user interaction.
+ * @param storage The {@code Storage} for file operations.
+ */
+ public void execute(TaskList tasks, Ui ui, Storage storage) {
+ try {
+ tasks.getTask(taskNo).unmarkAsDone();
+ String reply = " Roger. This task has been marked as not done yet:\n"
+ + " " + tasks.getTask(taskNo).toString();
+ ui.formatResponse(reply);
+ } catch (NullPointerException | IndexOutOfBoundsException e) {
+ String reply = " Task number not found, reenter with correct task number:";
+ new MissingCommand(reply).execute(tasks, ui, storage);
+ } catch (NumberFormatException e) {
+ String reply = " No task number detected, reenter with correct task number:";
+ new MissingCommand(reply).execute(tasks, ui, storage);
+ }
+ }
+}
diff --git a/src/main/java/Volkov.java b/src/main/java/Volkov.java
new file mode 100644
index 000000000..39821a409
--- /dev/null
+++ b/src/main/java/Volkov.java
@@ -0,0 +1,33 @@
+/**
+ * The main code for the Volkov application that handles the overall workflow.
+ */
+public class Volkov {
+ private final Storage storage;
+ private final TaskList tasks;
+ private final Ui ui;
+
+ public Volkov(String filePath) {
+ ui = new Ui();
+ storage = new Storage("./data/volkov.txt", ui);
+ this.tasks = storage.loadFile();
+ }
+
+ /**
+ * Main application loop for processing user commands. This keeps
+ * running until the user exits the program.
+ */
+ public void run() {
+ boolean isExit = false;
+ while (!isExit) {
+ String fullCommand = ui.readCommand();
+ Commands c = Parser.parse(fullCommand);
+ c.execute(tasks, ui, storage);
+ isExit = c.isExit();
+ }
+ storage.saveFile(tasks);
+ }
+
+ public static void main(String[] args) {
+ new Volkov("data/tasks.txt").run();
+ }
+}