diff --git a/data.text b/data.text new file mode 100644 index 000000000..b01de328c --- /dev/null +++ b/data.text @@ -0,0 +1,4 @@ +todo go to class /isdone true +todo say hi /isdone false +deadline go for meeting /by 01-01-2000 00:00 /isdone true +event Senior Seminar RC4 /from 10-10-2024 14:00 /to 10-10-2024 17:00 /isdone true diff --git a/docs/README.md b/docs/README.md index 47b9f984f..ccdd20339 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,30 +1,197 @@ -# Duke User Guide +# Juan User Guide -// Update the title above to match the actual product name + ._-'-_ . + . ' /_-_-_\ ` . + .' |-_-_-_-| `. + ( `.-_-_-.' ) + !`. .'! + ! ` . . ' ! + ! ! ! ! ! ! ! ! ! + / / \ \ + _-| \___ ___/ /-_ + (_ )__\_)\(_/__( _) + ))))\X\ (((( + \/ \/ -// Product screenshot goes here +Juan is a simple command-line task manager that helps users keep track of tasks such as to-dos, deadlines, and events. It allows users to manage their tasks, mark them as complete, and store them for future use. Juan is written in Java and supports basic task operations with a fun user interface. -// Product intro goes here +## Run -## Adding deadlines +To Run Juan, open Command Prompt and navigate to the directory `ip.jar` is located in and run the following command: +``` +java -jar ip.jar +``` + +## Command Overview + +| Command | Description | Format | Example | +|---------------|--------------------------------------------|----------------------------------------------------|---------------------------------------------------------------------| +| Add ToDo | Adds a to-do task | `todo ` | `todo Buy groceries` | +| Add Deadline | Adds a deadline task | `deadline /by ` | `deadline Submit assignment /by 15-10-2024 23:59` | +| Add Event | Adds an event with start and end | `event /from /to ` | `event Project meeting /from 15-10-2024 10:00 /to 15-10-2024 12:00` | +| Mark Task | Marks a task as done | `mark ` | `mark 2` | +| Unmark Task | Unmarks a task | `unmark ` | `unmark 2` | +| Delete Task | Deletes a task | `delete ` | `delete 3` | +| List Tasks | Lists all tasks | `list` | `list` | +| Find Task | Finds tasks matching a keyword | `find ` | `find groceries` | +| Exit | Saves the tasks into a text file and exits | `bye` | `bye` | + + +## Commands + +### 1. Add ToDo Task + +**Format**: +``` +todo +``` + +Adds a new to-do task to the task list. + +Example: +``` +todo Buy groceries + +Muy Bien, work hard compadre! +I've Added the Task: +[T][ ] Buy groceries +``` + +### 2. Add Deadline Task + +**Format**: +``` +deadline /by +``` + +Adds a task with a specific deadline. + +Example: +``` +deadline Submit assignment /by 15-10-2024 23:59 + +Muy Bien, work hard compadre! +I've Added the Task: +[D][ ] Submit assignment (by: 15-10-2024 23:59) +``` + +### 3. Add Event Task + +**Format**: +``` +event /from /to +``` + +Adds a task with a specific start and end time. + +Example: +``` +event Project meeting /from 15-10-2024 10:00 /to 15-10-2024 12:00 + +Muy Bien, work hard compadre! +I've Added the Task: +[E][ ] Project meeting (from: 15-10-2024 10:00 to: 15-10-2024 12:00) +``` + +### 4. Mark Task as Done + +**Format**: +``` +mark +``` -// Describe the action and its outcome. +Marks the specified task as completed. -// Give examples of usage +Example: +``` +mark 1 + +Fantastica!!!! I marked it: +[T][X] Buy groceries +``` -Example: `keyword (optional arguments)` +### 5. Unmark Task + +**Format**: +``` +unmark +``` -// A description of the expected outcome goes here +Unmarks the specified task, indicating it is not completed. +Example: ``` -expected output +unmark 1 + +Ay Caramba, I unmarked it: +[T][ ] Buy groceries ``` -## Feature ABC +### 6. Delete Task -// Feature details +**Format**: +``` +delete +``` +Deletes the specified task from the task list. -## Feature XYZ +Example: +``` +delete 1 + +Ay Caramba, Task deleted: [T][ ] Buy groceries +``` + +### 7. List All Tasks + +**Format**: +``` +list +``` + +Lists all current tasks in the task list, including their status (marked as done or not done) and task numbers. + +Example: +``` +list + +Si compinche, your 2 tasks: +1.[D][X] Submit assignment (by: 15-10-2024 23:59) +2.[E][ ] Project meeting (from: 15-10-2024 10:00 to: 15-10-2024 12:00) +``` + +### 8. Find Task + +**Format**: +``` +find +``` + +Finds all tasks that contain the specified keyword in their description. + +Example: +``` +find meeting + +Si compinche, your tasks with the phrase : +2.[E][ ] Project meeting (from: 15-10-2024 10:00 to: 15-10-2024 12:00) +5.[T][X] Do Tutorial +``` + +### 9. Exit + +**Format**: +``` +bye +``` +Saves changes done to .txt file and exits program. + +Example: + +``` +bye -// Feature details \ No newline at end of file +Data File Written +Adios amigo, la familia will miss you +``` \ No newline at end of file diff --git a/src/main/java/CommandHandling.java b/src/main/java/CommandHandling.java new file mode 100644 index 000000000..4e6c63f2b --- /dev/null +++ b/src/main/java/CommandHandling.java @@ -0,0 +1,140 @@ +import customexceptions.DeadlineConstructorException; +import customexceptions.EventConstructorException; +import customexceptions.ToDoConstructorException; +import taskpackage.Deadline; +import taskpackage.Event; +import taskpackage.TaskList; +import taskpackage.ToDo; + +/** + * Handles various user commands related to tasks such as adding, deleting, finding, and marking tasks. + */ +public class CommandHandling { + public static final String BYE_COMMAND = "bye"; + public static final String LIST_COMMAND = "list"; + public static final String DELETE_COMMAND = "delete "; + public static final String FIND_COMMAND = "find "; + public static final String MARK_COMMAND = "mark "; + public static final String UNMARK_COMMAND = "unmark "; + public static final String TODO_COMMAND = "todo "; + public static final String DEADLINE_COMMAND = "deadline "; + public static final String EVENT_COMMAND = "event "; + + /** + * Deletes a task from the task list based on the user input. + * + * @param tasks The task list from which to delete the task. + * @param line The user input line. + * @param ui The user interface to display messages. + */ + public static void deleteCommand(TaskList tasks, String line, UI ui) { + try { + int taskIndex = Integer.parseInt(line.replace(DELETE_COMMAND, "")) - 1; // Parse the task index + tasks.deleteTask(taskIndex); // Delete the task + } catch (NumberFormatException | IndexOutOfBoundsException e) { + ui.porFavorMessage("DELETE EXCEPTION: INVALID TASK INDEX"); + } catch (NullPointerException e) { + ui.porFavorMessage("DELETE EXCEPTION: NULL TASK INDEX"); + } + } + + /** + * Finds tasks in the task list that match the search string. + * + * @param tasks The task list to search in. + * @param line The user input line containing the search string. + * @param ui The user interface to display messages. + */ + public static void findCommand(TaskList tasks, String line, UI ui) { + String findString = line.replace(FIND_COMMAND, ""); + if (findString.isEmpty()) { + ui.porFavorMessage("FIND EXCEPTION: INVALID TASK INDEX"); + return; + } + tasks.findTasksList(findString); + } + + /** + * Marks a task as done in the task list based on user input. + * + * @param tasks The task list containing the task to be marked. + * @param line The user input line specifying the task index. + * @param ui The user interface to display messages. + */ + public static void markCommand(TaskList tasks, String line, UI ui) { + try { + int taskIndex = Integer.parseInt(line.replace(MARK_COMMAND, "")) - 1; // Parse the task index + tasks.mark(taskIndex); // Mark the task as done + } catch (NumberFormatException | IndexOutOfBoundsException e) { + ui.porFavorMessage("MARK EXCEPTION: INVALID TASK INDEX"); + } catch (NullPointerException e) { + ui.porFavorMessage("MARK EXCEPTION: NULL TASK INDEX"); + } + } + + /** + * Unmarks a task as not done in the task list based on user input. + * + * @param tasks The task list containing the task to be unmarked. + * @param line The user input line specifying the task index. + * @param ui The user interface to display messages. + */ + public static void unmarkCommand(TaskList tasks, String line, UI ui) { + try { + int taskIndex = Integer.parseInt(line.replace(UNMARK_COMMAND, "")) - 1; // Parse the task index + tasks.unmark(taskIndex); // Unmark the task + } catch (NumberFormatException | IndexOutOfBoundsException e) { + ui.porFavorMessage("UNMARK EXCEPTION: INVALID TASK INDEX"); + } catch (NullPointerException e) { + ui.porFavorMessage("UNMARK EXCEPTION: NULL TASK INDEX"); + } + } + + /** + * Adds a new to-do task to the task list based on user input. + * + * @param tasks The task list to add the to-do task to. + * @param line The user input line containing the to-do details. + * @param ui The user interface to display messages. + */ + public static void addTodoCommand(TaskList tasks, String line, UI ui) { + try { + new ToDo(line.replace(TODO_COMMAND, ""), tasks, true); // Create a new ToDo object + } catch (ToDoConstructorException e) { + ui.porFavorMessage(e.getMessage()); + tasks.deleteLatestTask(); + } + } + + /** + * Adds a new deadline task to the task list based on user input. + * + * @param tasks The task list to add the deadline task to. + * @param line The user input line containing the deadline details. + * @param ui The user interface to display messages. + */ + public static void addDeadlineCommand(TaskList tasks, String line, UI ui) { + try { + new Deadline(line.replace(DEADLINE_COMMAND, ""), tasks, true); // Create a new Deadline object + } catch (DeadlineConstructorException e) { + ui.porFavorMessage(e.getMessage()); + tasks.deleteLatestTask(); + } + } + + /** + * Adds a new event task to the task list based on user input. + * + * @param tasks The task list to add the event task to. + * @param line The user input line containing the event details. + * @param ui The user interface to display messages. + */ + public static void addEventCommand(TaskList tasks, String line, UI ui) { + try { + new Event(line.replace(EVENT_COMMAND, ""), tasks, true); // Create a new Event object + } catch (EventConstructorException e) { + ui.porFavorMessage(e.getMessage()); + tasks.deleteLatestTask(); + } + } +} 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/Juan.java b/src/main/java/Juan.java new file mode 100644 index 000000000..3a7e444e3 --- /dev/null +++ b/src/main/java/Juan.java @@ -0,0 +1,57 @@ +import taskpackage.TaskList; + +/** + * Main class for the Juan task manager. + * This class handles the initialization of the system, including user interface, storage, and task list management. + */ +public class Juan { + + private final static String dataFilePath = "data.text"; + private final UI ui; + private final Storage storage; + private TaskList tasks; + private final Parser parser; + + /** + * Constructor for Juan. + * Initializes the user interface, storage, and task list. + * + * @param dataFilePath Path to the file for storing task data. + */ + public Juan(String dataFilePath) { + ui = new UI(); + storage = new Storage(dataFilePath, ui); + try { + tasks = storage.readData(); + } catch (Exception e) { + ui.porFavorMessage(e.getMessage()); + } + parser = new Parser(ui, tasks); + } + + /** + * Main running function of Juan. + * Displays welcome message, handles user input, and writes task data to the file upon termination. + */ + public void run() { + ui.helloMessage(); // Display welcome message + + boolean continueChatting = true; + while (continueChatting) { + continueChatting = parser.chatFeature(ui.readUserInput()); + } + + storage.writeDate(parser.getTasks()); // Write task data to file + ui.lineMessage(); + ui.byeMessage(); // Display goodbye message + } + + /** + * Main method to start the Juan application. + * + * @param args Command-line arguments (not used). + */ + public static void main(String[] args) { + new Juan(dataFilePath).run(); + } +} diff --git a/src/main/java/META-INF/MANIFEST.MF b/src/main/java/META-INF/MANIFEST.MF new file mode 100644 index 000000000..d02c17cc4 --- /dev/null +++ b/src/main/java/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: Juan + diff --git a/src/main/java/Parser.java b/src/main/java/Parser.java new file mode 100644 index 000000000..ea782a731 --- /dev/null +++ b/src/main/java/Parser.java @@ -0,0 +1,65 @@ +import taskpackage.TaskList; + +/** + * The parser class handles user input and interprets commands for the task manager. + */ +public class Parser { + + private final UI ui; + private final TaskList tasks; + + /** + * Constructor for Parser. + * + * @param ui The user interface to interact with the user. + * @param tasks The task list to manage tasks. + */ + public Parser(UI ui, TaskList tasks) { + this.ui = ui; + this.tasks = tasks; + } + + /** + * Returns the current task list. + * + * @return The task list. + */ + public TaskList getTasks() { + return tasks; + } + + /** + * Handles user input and executes the corresponding commands based on the input. + * + * @param line The user input. + * @return True if the chat should continue, false if the "bye" command was given. + */ + public boolean chatFeature(String line) { + ui.lineMessage(); // Print separator line + + if (line.equals(CommandHandling.BYE_COMMAND)) { + return false; // End the conversation + } else if (line.equals(CommandHandling.LIST_COMMAND)) { + tasks.printTasksList(); + } else if (line.startsWith(CommandHandling.DELETE_COMMAND)) { + CommandHandling.deleteCommand(tasks, line, ui); + } else if (line.startsWith(CommandHandling.FIND_COMMAND)) { + CommandHandling.findCommand(tasks, line, ui); + } else if (line.startsWith(CommandHandling.MARK_COMMAND)) { + CommandHandling.markCommand(tasks, line, ui); + } else if (line.startsWith(CommandHandling.UNMARK_COMMAND)) { + CommandHandling.unmarkCommand(tasks, line, ui); + } else if (line.startsWith(CommandHandling.TODO_COMMAND)) { + CommandHandling.addTodoCommand(tasks, line, ui); + } else if (line.startsWith(CommandHandling.DEADLINE_COMMAND)) { + CommandHandling.addDeadlineCommand(tasks, line, ui); + } else if (line.startsWith(CommandHandling.EVENT_COMMAND)) { + CommandHandling.addEventCommand(tasks, line, ui); + } else { + ui.porFavorMessage("UNRECOGNIZED REQUEST"); // Inform user about an unrecognized command + } + + ui.lineMessage(); // Print separator line + return true; // Continue conversation + } +} diff --git a/src/main/java/Storage.java b/src/main/java/Storage.java new file mode 100644 index 000000000..a75ac28bb --- /dev/null +++ b/src/main/java/Storage.java @@ -0,0 +1,125 @@ +import customexceptions.DeadlineConstructorException; +import customexceptions.EventConstructorException; +import customexceptions.ToDoConstructorException; +import taskpackage.*; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Scanner; + +/** + * The Storage class handles reading and writing task data from and to a file. + */ +public class Storage { + + private final String dataFilePath; + private final UI ui; + + /** + * Constructor for Storage. + * + * @param dataFilePath Path to the file for storing task data. + * @param ui The user interface to display messages. + */ + public Storage(String dataFilePath, UI ui) { + this.dataFilePath = dataFilePath; + this.ui = ui; + } + + /** + * Reads task data from the file and returns a TaskList. + * + * @return A TaskList containing the tasks from the file. + */ + public TaskList readData() { + + TaskList tempTaskList = new TaskList(); + try { + File dataFile = new File(dataFilePath); + Scanner scanner = new Scanner(dataFile); + while (scanner.hasNextLine()) { + String inputLine = scanner.nextLine(); + try { + handleDataLine(inputLine, tempTaskList); + + } catch (Exception e) { + ui.printMessage("ERROR READING LINE: " + inputLine); + ui.printMessage(e.getMessage()); + } + } + ui.printMessage("Data File Read"); + } catch (FileNotFoundException e) { + ui.printMessage("Data File does not exist"); + } + + return tempTaskList; + } + + /** + * Handles each line of the data file and adds the corresponding task to the TaskList. + * + * @param inputLine The line from the data file. + * @param taskList The TaskList to which the task will be added. + */ + private void handleDataLine(String inputLine, TaskList taskList) { + String[] lineSegments = inputLine.split(" /isdone "); + String line = lineSegments[0]; + boolean isDone; + if (lineSegments[1].trim().equals("true")) { + isDone = true; + } else if (lineSegments[1].trim().equals("false")) { + isDone = false; + } else { + ui.printMessage("ERROR READING ISDONE VALUE: " + inputLine); + return; + } + if (line.startsWith(CommandHandling.TODO_COMMAND)) { + try { + new ToDo(line.replace(CommandHandling.TODO_COMMAND, ""), taskList, false); // Create a new ToDo object + } catch (ToDoConstructorException e) { + ui.printMessage("CORRUPTED: " + line); + taskList.deleteLatestTask(); + } + } else if (line.startsWith(CommandHandling.DEADLINE_COMMAND)) { + try { + new Deadline(line.replace(CommandHandling.DEADLINE_COMMAND, ""), taskList, false); // Create a new Deadline object + } catch (DeadlineConstructorException e) { + ui.printMessage("CORRUPTED: " + line); + taskList.deleteLatestTask(); + } + } else if (line.startsWith(CommandHandling.EVENT_COMMAND)) { + try { + new Event(line.replace(CommandHandling.EVENT_COMMAND, ""), taskList, false); // Create a new Event object + } catch (EventConstructorException e) { + ui.printMessage("CORRUPTED: " + line); + taskList.deleteLatestTask(); + } + } else { + ui.printMessage("CORRUPTED: " + line); + } + + if (isDone) { + taskList.markLatestTask(); + } + } + + /** + * Writes the task data to the file. + * + * @param taskList The TaskList to write to the file. + */ + public void writeDate(TaskList taskList) { + try { + FileWriter writer = new FileWriter(dataFilePath); + for (int i = 0; i < taskList.size(); i++) { + writer.write(taskList.dataFileEntry(i)); + } + writer.close(); + ui.printMessage("Data File Written"); + } catch (IOException e) { + ui.porFavorMessage(e.getMessage()); + } + } +} diff --git a/src/main/java/UI.java b/src/main/java/UI.java new file mode 100644 index 000000000..ca87e8b80 --- /dev/null +++ b/src/main/java/UI.java @@ -0,0 +1,89 @@ +import java.util.Scanner; // Import Scanner for user input + +/** + * The UI class handles interaction with the user by reading input and displaying output messages. + */ +public class UI { + + // Constant for common error message to improve code readability and reusability + private final static String PORFAVOR = "Por Favor?\n"; + private final static String SEPARATOR = "____________________________________________________________\n"; + private final static String BYEMESSAGE = "Adios amigo, la familia will miss you\n"; + private final static String GREETINGMESSAGE = + " ._-'-_ .\n" + + " . ' /_-_-_\\ ` .\n" + + " .' |-_-_-_-| `.\n" + + " ( `.-_-_-.' )\n" + + " !`. .'!\n" + + " ! ` . . ' !\n" + + " ! ! ! ! ! ! ! ! !\n" + + " / / \\ \\\n" + + " _-| \\___ ___/ /-_\n" + + " (_ )__\\_)\\(_/__( _)\n" + + " ))))\\X\\ ((((\n" + + " \\/ \\/ \n" + + "Hola Amigo, I am Juan Cervantes Salamanca from Michoacan \n" + + "Welcome to la familia \n" + + "How can we help you? \n"; + + private Scanner scanner; + + /** + * Constructor for UI. + * Initializes the scanner for reading user input. + */ + public UI() { + scanner = new Scanner(System.in); // Initialize the scanner for reading user input + } + + /** + * Reads the user's input from the console. + * + * @return The user's input as a String. + */ + public String readUserInput() { + return scanner.nextLine(); // Read user input + } + + /** + * Prints a message to the console. + * + * @param message The message to be printed. + */ + public void printMessage(String message) { + System.out.println(message); + } + + /** + * Prints a separator line to the console for clean output formatting. + */ + public void lineMessage() { + System.out.print(SEPARATOR); + } + + /** + * Prints a message with a "Por Favor" prefix to the console. + * + * @param message The message to be printed. + */ + public void porFavorMessage(String message) { + System.out.println(PORFAVOR + message); + } + + /** + * Displays a welcome message when the program starts. + */ + public void helloMessage() { + System.out.print(SEPARATOR); + System.out.print(GREETINGMESSAGE); // Print the welcome message + System.out.print(SEPARATOR); + } + + /** + * Displays a goodbye message when the program ends. + */ + public void byeMessage() { + System.out.print(BYEMESSAGE); // Print the goodbye message + System.out.print(SEPARATOR); + } +} diff --git a/src/main/java/customexceptions/DeadlineConstructorException.java b/src/main/java/customexceptions/DeadlineConstructorException.java new file mode 100644 index 000000000..444b2badb --- /dev/null +++ b/src/main/java/customexceptions/DeadlineConstructorException.java @@ -0,0 +1,45 @@ +package customexceptions; // Package for custom exceptions + +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; + +/** + * Custom exception class for handling errors in Deadline task construction. + */ +public class DeadlineConstructorException extends Exception { + + /** + * Constructor that takes an error message as input and generates a custom exception for Deadline tasks. + * + * @param message The error message for the exception. + */ + public DeadlineConstructorException(String message) { + super("DEADLINE CONSTRUCTOR EXCEPTION: " + errorMessage(message)); // Call superclass constructor + } + + /** + * Private helper method to interpret and identify specific errors in the deadline command. + * + * @param message The input message that caused the error. + * @return The interpreted error message. + */ + private static String errorMessage(String message) { + if (!(message.contains(" /by "))) { + return "MISSING BY COMMAND"; // Error if "/by" is missing + } + + String[] taskStringBreakdown = message.replace("deadline ", "").split(" /by "); + if (taskStringBreakdown[0].isEmpty()) { + return "MISSING TASK STATEMENT"; // Error if task description is missing + } + + DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("dd-MM-yyyy HHmm"); + try { + dateTimeFormatter.parse(taskStringBreakdown[1]); + } catch (DateTimeParseException e) { + return "INVALID DATETIME EXCEPTION"; // Error if the date/time format is invalid + } + + return "UNKNOWN ERROR"; // Fallback error message + } +} diff --git a/src/main/java/customexceptions/EventConstructorException.java b/src/main/java/customexceptions/EventConstructorException.java new file mode 100644 index 000000000..4c4399633 --- /dev/null +++ b/src/main/java/customexceptions/EventConstructorException.java @@ -0,0 +1,51 @@ +package customexceptions; // Package for custom exceptions + +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; + +/** + * Custom exception class for handling errors in Event task construction. + */ +public class EventConstructorException extends Exception { + + /** + * Constructor that takes an error message as input and generates a custom exception for Event tasks. + * + * @param message The error message for the exception. + */ + public EventConstructorException(String message) { + super("EVENT CONSTRUCTOR EXCEPTION: " + errorMessage(message)); // Call superclass constructor + } + + /** + * Private helper method to interpret and identify specific errors in the event command. + * + * @param message The input message that caused the error. + * @return The interpreted error message. + */ + private static String errorMessage(String message) { + if (!(message.contains(" /from ") || message.contains(" /to "))) { + return "MISSING FROM/TO COMMANDS"; // Error if "/from" or "/to" is missing + } + + String[] taskStringBreakdown = message.replace("event ", "").split(" /from "); + if (taskStringBreakdown[0].isEmpty()) { + return "MISSING TASK STATEMENT"; // Error if task description is missing + } + + String[] fromToStringBreakdown = taskStringBreakdown[1].split(" /to "); + if (fromToStringBreakdown.length != 2) { + return "MISSING FROM/TO DATES"; // Error if either "from" or "to" date is missing + } + + DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("dd-MM-yyyy HHmm"); + try { + dateTimeFormatter.parse(fromToStringBreakdown[0]); + dateTimeFormatter.parse(fromToStringBreakdown[1]); + } catch (DateTimeParseException e) { + return "INVALID DATETIME EXCEPTION"; // Error if the date/time format is invalid + } + + return "UNKNOWN ERROR"; // Fallback error message + } +} diff --git a/src/main/java/customexceptions/ToDoConstructorException.java b/src/main/java/customexceptions/ToDoConstructorException.java new file mode 100644 index 000000000..2399e6e90 --- /dev/null +++ b/src/main/java/customexceptions/ToDoConstructorException.java @@ -0,0 +1,31 @@ +package customexceptions; // Package for custom exceptions + +/** + * Custom exception class for handling errors in ToDo task construction. + */ +public class ToDoConstructorException extends Exception { + + /** + * Constructor that takes an error message as input and generates a custom exception for ToDo tasks. + * + * @param message The error message for the exception. + */ + public ToDoConstructorException(String message) { + super("TODO CONSTRUCTOR EXCEPTION: " + errorMessage(message)); // Call superclass constructor + } + + /** + * Private helper method to interpret and identify specific errors in the ToDo command. + * + * @param message The input message that caused the error. + * @return The interpreted error message. + */ + private static String errorMessage(String message) { + String taskString = message.replace("todo ", ""); + if (taskString.isEmpty()) { + return "MISSING TASK STATEMENT"; // Error if task description is missing + } + + return "UNKNOWN ERROR"; // Fallback error message + } +} diff --git a/src/main/java/taskpackage/Deadline.java b/src/main/java/taskpackage/Deadline.java new file mode 100644 index 000000000..92b519b7c --- /dev/null +++ b/src/main/java/taskpackage/Deadline.java @@ -0,0 +1,61 @@ +package taskpackage; // Package for Task-related classes + +import customexceptions.DeadlineConstructorException; // Import custom exception for Deadline tasks + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; + +/** + * Represents a task with a specific deadline, extending the Task class. + */ +public class Deadline extends Task { + + private final LocalDateTime deadlineDateTime; // Stores the deadline date/time + private final static String DATE_TIME_FORMAT = "dd-MM-yyyy HH:mm"; // Format for date and time + private static final String DEADLINE_COMMAND = "deadline "; + + /** + * Constructor for creating a Deadline task. + * + * @param inputString The input string containing the task description and deadline. + * @param tasks The task list to which the task is added. + * @param constructorMessage A flag to indicate whether to display a constructor message. + * @throws DeadlineConstructorException if the task description or deadline is invalid. + */ + public Deadline(String inputString, TaskList tasks, boolean constructorMessage) throws DeadlineConstructorException { + super(inputString.split(" /by ")[0], tasks); + this.inputString = inputString; + + this.storageString = DEADLINE_COMMAND + inputString; + + // Check if the "/by" keyword is present and if the task description is valid + if (!(inputString.contains(" /by ")) || this.taskString.isEmpty()) { + throw new DeadlineConstructorException(inputString); + } + + // Parse the deadline date/time + try { + DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(DATE_TIME_FORMAT); + this.deadlineDateTime = LocalDateTime.parse(inputString.replace("deadline ", "").split(" /by ")[1], dateTimeFormatter); + } catch (DateTimeParseException e) { + throw new DeadlineConstructorException(inputString); + } + + // Display a confirmation message if the constructorMessage flag is true + if (constructorMessage) { + constructorMessage(); + } + } + + /** + * Returns the task's string with a checkbox and deadline details. + * + * @return The formatted task string with deadline information. + */ + @Override + public String checkboxString() { + DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(DATE_TIME_FORMAT); + return "[D]" + super.checkboxString() + " (by: " + deadlineDateTime.format(dateTimeFormatter) + ")"; + } +} diff --git a/src/main/java/taskpackage/Event.java b/src/main/java/taskpackage/Event.java new file mode 100644 index 000000000..d37bae826 --- /dev/null +++ b/src/main/java/taskpackage/Event.java @@ -0,0 +1,67 @@ +package taskpackage; // Package for Task-related classes + +import customexceptions.EventConstructorException; // Import custom exception for Event tasks + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; + +/** + * Represents a task with a specific start and end time, extending the Task class. + */ +public class Event extends Task { + + private final LocalDateTime fromDateTime; // Stores the event start time + private final LocalDateTime toDateTime; // Stores the event end time + private final static String DATE_TIME_FORMAT = "dd-MM-yyyy HH:mm"; // Format for date and time + private static final String EVENT_COMMAND = "event "; + + /** + * Constructor for creating an Event task. + * + * @param inputString The input string containing the task description, start time, and end time. + * @param tasks The task list to which the task is added. + * @param constructorMessage A flag to indicate whether to display a constructor message. + * @throws EventConstructorException if the task description, start time, or end time is invalid. + */ + public Event(String inputString, TaskList tasks, boolean constructorMessage) throws EventConstructorException { + super(inputString.split(" /from ")[0], tasks); + this.inputString = inputString; + this.storageString = EVENT_COMMAND + inputString; + + // Check if both "/from" and "/to" keywords are present and the task description is valid + if (!(inputString.contains(" /from ") & inputString.contains(" /to ")) || this.taskString.isEmpty()) { + throw new EventConstructorException(inputString); + } + + // Parse the start and end times + String[] fromToStrings = inputString.replace("event ", "").split(" /from ")[1].split(" /to "); + if (fromToStrings.length != 2) { + throw new EventConstructorException(inputString); + } + + try { + DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(DATE_TIME_FORMAT); + this.fromDateTime = LocalDateTime.parse(fromToStrings[0], dateTimeFormatter); + this.toDateTime = LocalDateTime.parse(fromToStrings[1], dateTimeFormatter); + } catch (DateTimeParseException e) { + throw new EventConstructorException(inputString); + } + + // Display a confirmation message if the constructorMessage flag is true + if (constructorMessage) { + constructorMessage(); + } + } + + /** + * Returns the task's string with a checkbox, start time, and end time. + * + * @return The formatted task string with event details. + */ + @Override + public String checkboxString() { + DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(DATE_TIME_FORMAT); + return "[E]" + super.checkboxString() + " (from: " + fromDateTime.format(dateTimeFormatter) + " to: " + toDateTime.format(dateTimeFormatter) + ")"; + } +} diff --git a/src/main/java/taskpackage/Task.java b/src/main/java/taskpackage/Task.java new file mode 100644 index 000000000..89274fc19 --- /dev/null +++ b/src/main/java/taskpackage/Task.java @@ -0,0 +1,48 @@ +package taskpackage; // Package for Task-related classes + +/** + * Represents a general task that can be extended to specific types of tasks such as ToDo, Deadline, and Event. + */ +public class Task { + + public String inputString; // Input string for the task + public String taskString; // Description of the task + public boolean isDone = false; // Boolean to track if the task is marked as done + + public String storageString; + /** + * Constructor for creating a general task. + * + * @param inputString The description of the task. + * @param tasks The task list to which the task is added. + */ + public Task(String inputString, TaskList tasks) { + this.taskString = inputString; // Set the task description + tasks.addTask(this); // Add the task to the task list + } + + /** + * Displays a message confirming the task was successfully created. + */ + public void constructorMessage() { + System.out.println("Muy Bien, work hard compadre!"); // Success message in Spanish + System.out.println("I've Added the Task:"); + System.out.println(checkboxString()); // Display the task in checkbox format + } + + /** + * Returns the task's string with a checkbox indicating whether it's done. + * + * @return The formatted task string. + */ + public String checkboxString() { + String returnString = "["; // Start of the checkbox string + if (this.isDone) { + returnString += "X"; // Mark the checkbox as done if the task is completed + } else { + returnString += " "; // Leave the checkbox empty if the task is not completed + } + returnString += "] " + this.taskString; // Append the task description + return returnString; // Return the formatted string + } +} diff --git a/src/main/java/taskpackage/TaskList.java b/src/main/java/taskpackage/TaskList.java new file mode 100644 index 000000000..dcea2f625 --- /dev/null +++ b/src/main/java/taskpackage/TaskList.java @@ -0,0 +1,131 @@ +package taskpackage; + +import java.util.ArrayList; + +/** + * Represents a list of tasks, allowing for operations such as adding, deleting, marking, and unmarking tasks. + */ +public class TaskList { + + private ArrayList tasks; // List to store tasks + + /** + * Constructor for initializing an empty TaskList. + */ + public TaskList() { + this.tasks = new ArrayList<>(); + } + + /** + * Adds a task to the task list. + * + * @param task The task to be added. + */ + public void addTask(Task task) { + this.tasks.add(task); + } + + /** + * Deletes a task from the task list based on the task index. + * + * @param taskIndex The index of the task to be deleted. + */ + public void deleteTask(int taskIndex) { + String taskString = this.tasks.get(taskIndex).checkboxString(); + this.tasks.remove(taskIndex); + System.out.println("Ay Caramba, Task deleted: " + taskString); + } + + /** + * Deletes the latest task in the task list in case of errors. + */ + public void deleteLatestTask() { + this.tasks.remove(tasks.size() - 1); // Remove the latest task from the list + } + + /** + * Marks the latest task in the task list as done. + */ + public void markLatestTask() { + this.tasks.get(tasks.size() - 1).isDone = true; + } + + /** + * Returns the size of the task list. + * + * @return The number of tasks in the list. + */ + public int size() { + return this.tasks.size(); + } + + /** + * Returns the string representation of a task for storing in a data file. + * + * @param index The index of the task. + * @return The formatted string for storing the task data. + */ + public String dataFileEntry(int index) { + return (this.tasks.get(index).storageString + " /isdone " + this.tasks.get(index).isDone + "\n"); + } + + /** + * Marks a task as done by its index. + * + * @param taskIndex The index of the task to be marked as done. + */ + public void mark(int taskIndex) { + tasks.get(taskIndex).isDone = true; + System.out.println("Fantastica!!!! I marked it:"); + System.out.println(tasks.get(taskIndex).checkboxString()); + } + + /** + * Unmarks a task as not done by its index. + * + * @param taskIndex The index of the task to be unmarked. + */ + public void unmark(int taskIndex) { + tasks.get(taskIndex).isDone = false; + System.out.println("Ay Caramba, I unmarked it:"); + System.out.println(tasks.get(taskIndex).checkboxString()); + } + + /** + * Prints the list of tasks to the console. + */ + public void printTasksList() { + if (tasks.isEmpty()) { + System.out.println("Por Favor? Nothing Here"); + } else { + System.out.println("Si compinche, your " + tasks.size() + " tasks:"); + for (int i = 0; i < tasks.size(); i++) { + System.out.println((i + 1) + "." + tasks.get(i).checkboxString()); + } + } + } + + /** + * Prints the tasks that contain the specified search string. + * + * @param findString The string to search for in the tasks. + */ + public void findTasksList(String findString) { + if (tasks.isEmpty()) { + System.out.println("Por Favor? Nothing Here"); + } else { + System.out.println("Si compinche, your tasks with the phrase <" + findString + ">:"); + int foundCount = 0; + for (int i = 0; i < tasks.size(); i++) { + if (tasks.get(i).checkboxString().contains(findString)) { + foundCount++; + System.out.println((i + 1) + "." + tasks.get(i).checkboxString()); + } + } + + if (foundCount == 0) { + System.out.println("Nothing Here"); + } + } + } +} diff --git a/src/main/java/taskpackage/ToDo.java b/src/main/java/taskpackage/ToDo.java new file mode 100644 index 000000000..d4a9f8fe1 --- /dev/null +++ b/src/main/java/taskpackage/ToDo.java @@ -0,0 +1,47 @@ +package taskpackage; // Package for Task-related classes + +import customexceptions.ToDoConstructorException; // Import custom exception for ToDo tasks + +/** + * Represents a simple to-do task, extending the Task class. + */ +public class ToDo extends Task { + + + private static final String TODO_COMMAND = "todo "; + + /** + * Constructor for creating a ToDo task. + * + * @param inputString The description of the to-do task. + * @param tasks The task list to which the task is added. + * @param constructorMessage A flag to indicate whether to display a constructor message. + * @throws ToDoConstructorException if the task description is empty. + */ + public ToDo(String inputString, TaskList tasks, boolean constructorMessage) throws ToDoConstructorException { + super(inputString, tasks); + this.inputString = inputString; + + this.storageString = TODO_COMMAND + inputString; + + // Check if the task description is empty and throw an exception if true + if (this.taskString.isEmpty()) { + throw new ToDoConstructorException(inputString); + } + + // Display a confirmation message if the constructorMessage flag is true + if (constructorMessage) { + constructorMessage(); + } + } + + /** + * Returns the task's string with a checkbox, prefixed with "[T]" to indicate it is a ToDo. + * + * @return The formatted task string. + */ + @Override + public String checkboxString() { + return "[T]" + super.checkboxString(); + } +} diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 657e74f6e..8b19cf0f6 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,7 +1,65 @@ -Hello from - ____ _ -| _ \ _ _| | _____ -| | | | | | | |/ / _ \ -| |_| | |_| | < __/ -|____/ \__,_|_|\_\___| - +Data File Read +____________________________________________________________ + ._-'-_ . + . ' /_-_-_\ ` . + .' |-_-_-_-| `. + ( `.-_-_-.' ) + !`. .'! + ! ` . . ' ! + ! ! ! ! ! ! ! ! ! + / / \ \ + _-| \___ ___/ /-_ + (_ )__\_)\(_/__( _) + ))))\X\ (((( + \/ \/ +Hola Amigo, I am Juan Cervantes Salamanca from Michoacan +Welcome to la familia +How can we help you? +____________________________________________________________ +____________________________________________________________ +Si compinche, your 4 tasks: +1.[T][X] go to class +2.[T][ ] say hi +3.[D][X] go for meeting (by: 01-01-2000 00:00) +4.[E][X] Senior Seminar RC4 (from: 10-10-2024 14:00 to: 10-10-2024 17:00) +____________________________________________________________ +____________________________________________________________ +Si compinche, your tasks with the phrase : +4.[E][X] Senior Seminar RC4 (from: 10-10-2024 14:00 to: 10-10-2024 17:00) +____________________________________________________________ +____________________________________________________________ +Ay Caramba, I unmarked it: +[E][ ] Senior Seminar RC4 (from: 10-10-2024 14:00 to: 10-10-2024 17:00) +____________________________________________________________ +____________________________________________________________ +Fantastica!!!! I marked it: +[E][X] Senior Seminar RC4 (from: 10-10-2024 14:00 to: 10-10-2024 17:00) +____________________________________________________________ +____________________________________________________________ +Muy Bien, work hard compadre! +I've Added the Task: +[T][ ] graduate +____________________________________________________________ +____________________________________________________________ +Si compinche, your 5 tasks: +1.[T][X] go to class +2.[T][ ] say hi +3.[D][X] go for meeting (by: 01-01-2000 00:00) +4.[E][X] Senior Seminar RC4 (from: 10-10-2024 14:00 to: 10-10-2024 17:00) +5.[T][ ] graduate +____________________________________________________________ +____________________________________________________________ +Ay Caramba, Task deleted: [T][ ] graduate +____________________________________________________________ +____________________________________________________________ +Si compinche, your 4 tasks: +1.[T][X] go to class +2.[T][ ] say hi +3.[D][X] go for meeting (by: 01-01-2000 00:00) +4.[E][X] Senior Seminar RC4 (from: 10-10-2024 14:00 to: 10-10-2024 17:00) +____________________________________________________________ +____________________________________________________________ +Data File Written +____________________________________________________________ +Adios amigo, la familia will miss you +____________________________________________________________ \ No newline at end of file diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index e69de29bb..826a2abc0 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -0,0 +1,9 @@ +list +find Senior +unmark 4 +mark 4 +todo graduate +list +delete 5 +list +bye \ No newline at end of file diff --git a/text-ui-test/runtest.bat b/text-ui-test/runtest.bat index 087374464..e5e20765c 100644 --- a/text-ui-test/runtest.bat +++ b/text-ui-test/runtest.bat @@ -15,7 +15,7 @@ IF ERRORLEVEL 1 ( REM no error here, errorlevel == 0 REM run the program, feed commands from input.txt file and redirect the output to the ACTUAL.TXT -java -classpath ..\bin Duke < input.txt > ACTUAL.TXT +java -classpath ..\bin Juan < input.txt > ACTUAL.TXT REM compare the output to the expected output FC ACTUAL.TXT EXPECTED.TXT diff --git a/text-ui-test/runtest.sh b/text-ui-test/runtest.sh index c9ec87003..b81ccd5e8 100644 --- a/text-ui-test/runtest.sh +++ b/text-ui-test/runtest.sh @@ -20,7 +20,7 @@ then fi # run the program, feed commands from input.txt file and redirect the output to the ACTUAL.TXT -java -classpath ../bin Duke < input.txt > ACTUAL.TXT +java -classpath ../bin Juan < input.txt > ACTUAL.TXT # convert to UNIX format cp EXPECTED.TXT EXPECTED-UNIX.TXT