diff --git a/build.gradle b/build.gradle index 5867c00ad2..f01fafc30d 100644 --- a/build.gradle +++ b/build.gradle @@ -44,15 +44,11 @@ test { } application { - mainClass.set("seedu.duke.Duke") + mainClassName = "homie.Launcher" } shadowJar { - mainClassName = 'seedu.duke.Main' - archiveBaseName = "duke" - archiveClassifier = null - archiveFileName = 'duke.jar' - dependsOn("distZip", "distTar") + archiveFileName = 'homie.jar' } run{ diff --git a/src/main/java/homie/Deadline.java b/src/main/java/homie/Deadline.java index 367147a5ed..b979c50ce5 100644 --- a/src/main/java/homie/Deadline.java +++ b/src/main/java/homie/Deadline.java @@ -1,31 +1,31 @@ package homie; +import java.io.Serializable; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; /** - * Deadline class that extends the Task class. - * Can specify the Local Date Time to keep track of - * when to complete deadline by. + * The Deadline class that extends the Task class. Handles all deadline related tasks. + * Can specify the Local Date Time to keep track of when to complete deadline by. */ -public class Deadline extends Task { +public class Deadline extends Task implements Serializable { - protected LocalDateTime by; + protected LocalDateTime dueDateTime; /** - * Constructor for Deadline class - * @param description For the deadline task - * @param by This is the Local Date Time to complete the deadline by + * Constructor for Deadline class to create a new deadline instance. + * + * @param description The description of deadline task. + * @param by This is the Local Date Time to complete the deadline by. */ public Deadline(String description, String by) { super(description); - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd MM yyyy HHmm"); - this.by = LocalDateTime.parse(by, formatter); + this.dueDateTime = LocalDateTime.parse(by, DateTimeFormatter.ofPattern("dd MM yyyy HHmm")); } @Override public String toString() { return "[D][" + this.getStatusIcon() + "] " + super.toString() + " (by: " - + by.format(DateTimeFormatter.ofPattern("MM-dd-yyyy HH:mm")) + ")"; + + this.dueDateTime.format(DateTimeFormatter.ofPattern("MM-dd-yyyy HH:mm")) + ")"; } } diff --git a/src/main/java/homie/DeadlineException.java b/src/main/java/homie/DeadlineException.java new file mode 100644 index 0000000000..30b6a066ec --- /dev/null +++ b/src/main/java/homie/DeadlineException.java @@ -0,0 +1,17 @@ +package homie; + +/** + * Deadline exception class. Handles all exceptions related to Deadline class. + * Thrown when no description for deadline task is given, or format of due date is wrong. + */ +public class DeadlineException extends Exception { + /** + * Constructor for DeadlineException class. + * + * @param message The error message. + */ + public DeadlineException(String message) { + super("Bruh... " + message + "\nPlease follow the format:\ndeadline " + + "{DEADLINE_DESCRIPTION} /by {dd MM yyyy HHmm}"); + } +} diff --git a/src/main/java/homie/DeleteException.java b/src/main/java/homie/DeleteException.java new file mode 100644 index 0000000000..8b9aa649a7 --- /dev/null +++ b/src/main/java/homie/DeleteException.java @@ -0,0 +1,16 @@ +package homie; + +/** + * Delete exception class. Handles all exceptions related to Delete command. + * Thrown when no index is given, or index is out of range. + */ +public class DeleteException extends Exception { + /** + * Constructor for DeleteException class. + * + * @param message The error message. + */ + public DeleteException(String message) { + super("Bruh... " + message + "\nPlease follow the format: \ndelete {INDEX}"); + } +} diff --git a/src/main/java/homie/Event.java b/src/main/java/homie/Event.java index 3a8380b483..ecd90d6910 100644 --- a/src/main/java/homie/Event.java +++ b/src/main/java/homie/Event.java @@ -1,28 +1,35 @@ package homie; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + /** - * Event class that extends Task class. - * Can specify event starts from when and when the events end. + * Event class that extends Task class. Handles all event related tasks. + * Can specify event starting date time and ending date time. */ public class Event extends Task { - protected String from; - protected String to; + protected LocalDateTime startDateTime; + protected LocalDateTime endDateTime; /** * Constructor for Event class - * @param description For the Event Task. - * @param from This is to keep track when the event starts - * @param to This is to keep track when the event ends + * + * @param description The description of the event task. + * @param start The start date time of event task in String, of 'dd MM yyyy HHmm' format + * @param end The end date time of event task in String, of 'dd MM yyyy HHmm' format */ - public Event(String description, String from, String to) { + public Event(String description, String start, String end) { super(description); - this.from = from; - this.to = to; + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd MM yyyy HHmm"); + this.startDateTime = LocalDateTime.parse(start, formatter); + this.endDateTime = LocalDateTime.parse(end, formatter); } @Override public String toString() { - return "[E][" + this.getStatusIcon() + "] " + super.toString() + " (from: " + from + " to: " + to + ")"; + return "[E][" + this.getStatusIcon() + "] " + super.toString() + " (from: " + + this.startDateTime.format(DateTimeFormatter.ofPattern("MM-dd-yyyy HH:mm")) + " to: " + + this.endDateTime.format(DateTimeFormatter.ofPattern("MM-dd-yyyy HH:mm")) + ")"; } } diff --git a/src/main/java/homie/EventException.java b/src/main/java/homie/EventException.java new file mode 100644 index 0000000000..c4dee36dd3 --- /dev/null +++ b/src/main/java/homie/EventException.java @@ -0,0 +1,17 @@ +package homie; + +/** + * Event exception class. Handles all exceptions related to Event Class. + * Thrown when no description of event is given, or start date or end date is of incorrect format. + */ +public class EventException extends Exception { + /** + * Constructor for EventException class. + * + * @param message The error message. + */ + public EventException(String message) { + super("Bruh... " + message + "\nPlease follow the format:\n event {EVENT_DESCRIPTION} " + + "/from {dd MM yyyy HHmm} /to {dd MM yyyy HHmm}"); + } +} diff --git a/src/main/java/homie/FindException.java b/src/main/java/homie/FindException.java new file mode 100644 index 0000000000..58d4a5f1fc --- /dev/null +++ b/src/main/java/homie/FindException.java @@ -0,0 +1,16 @@ +package homie; + +/** + * Find exception class. Handles all exceptions related to Find command. + * Thrown when no keyword is given, or keyword is more than one word. + */ +public class FindException extends Exception { + /** + * Constructor for MarkException class. + * + * @param message The error message. + */ + public FindException(String message) { + super("Bruh... " + message + "\nPlease follow the format:\nfind {KEYWORD}"); + } +} diff --git a/src/main/java/homie/Homie.java b/src/main/java/homie/Homie.java index 370e8ab301..f2eb20f416 100644 --- a/src/main/java/homie/Homie.java +++ b/src/main/java/homie/Homie.java @@ -1,11 +1,9 @@ package homie; -import javax.imageio.IIOException; import java.io.IOException; -import java.util.Scanner; /** - * A chatbot programme named Homie that helps you keep track + * A chatbot program named Homie that helps you keep track * of to-do tasks, deadlines and events. Date and time can be specified for deadlines and events. * Other functions include adding tasks, finding tasks, marking or un-marking tasks as done, * deleting tasks and listing tasks. @@ -16,7 +14,7 @@ public class Homie { private Ui ui; /** - * Constructor for Homie class + * Constructor for Homie class. Initialise storage and task list to be used later. */ public Homie() { this.ui = new Ui(); @@ -26,19 +24,20 @@ public Homie() { System.out.println(e.getMessage()); } this.tasks = new TaskList(storage.loadTasksFromFile()); - ui.showLoadingError(); } /** * Returns a String response based on user input to the GUI. */ - public String getResponse(String input) throws HomieException { + public String getResponse(String input) { Parser parser = new Parser(this.tasks, this.ui, this.storage); try { - String outputResponse = parser.parse(input); - return outputResponse; - } catch (HomieException | TodoException e) { + return parser.parse(input); + } catch (HomieException | TodoException | EventException | DeadlineException | UnmarkException | MarkException + | DeleteException | FindException e) { return e.getMessage(); + } catch (Exception ex) { + return "Invalid Command!!"; } } } diff --git a/src/main/java/homie/HomieException.java b/src/main/java/homie/HomieException.java index 96c039493f..873ba794cf 100644 --- a/src/main/java/homie/HomieException.java +++ b/src/main/java/homie/HomieException.java @@ -1,10 +1,11 @@ package homie; /** - * Homie Exception class + * Homie Exception class. Handles all exceptions related to Homie class. + * Thrown when invalid commands are given by user. */ public class HomieException extends Exception { public HomieException(String message) { - super(message); + super("Bruh... Invalid command!\nPlease try again."); } } diff --git a/src/main/java/homie/Launcher.java b/src/main/java/homie/Launcher.java index 2cca90235a..deb11bcfa1 100644 --- a/src/main/java/homie/Launcher.java +++ b/src/main/java/homie/Launcher.java @@ -3,7 +3,7 @@ import javafx.application.Application; /** - * A launcher class to workaround classpath issues. + * A launcher class to workaround classpath issues. Entrypoint for Homie. */ public class Launcher { public static void main(String[] args) { diff --git a/src/main/java/homie/MainWindow.java b/src/main/java/homie/MainWindow.java index 529375b1f5..f2a462f620 100644 --- a/src/main/java/homie/MainWindow.java +++ b/src/main/java/homie/MainWindow.java @@ -1,12 +1,12 @@ package homie; import javafx.fxml.FXML; -import javafx.scene.control.Button; import javafx.scene.control.ScrollPane; import javafx.scene.control.TextField; import javafx.scene.image.Image; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.VBox; + /** * Controller for MainWindow. Provides the layout for the other controls. */ @@ -17,13 +17,11 @@ public class MainWindow extends AnchorPane { private VBox dialogContainer; @FXML private TextField userInput; - @FXML - private Button sendButton; private Homie homie; - private Image userImage = new Image(this.getClass().getResourceAsStream("/images/gojo.png")); - private Image homieImage = new Image(this.getClass().getResourceAsStream("/images/homie.png")); + private final Image userImage = new Image(this.getClass().getResourceAsStream("/images/user.png")); + private final Image homieImage = new Image(this.getClass().getResourceAsStream("/images/homie.png")); /** * Initializes the chatbot GUI and also instantly shows a welcome message. @@ -32,9 +30,8 @@ public class MainWindow extends AnchorPane { public void initialize() { scrollPane.vvalueProperty().bind(dialogContainer.heightProperty()); Ui ui = new Ui(); - String welcomeMessage = ui.showWelcomeMessage(); + String welcomeMessage = ui.getWelcomeMessage(); dialogContainer.getChildren().add(DialogBox.getHomieDialog(welcomeMessage, homieImage)); - } public void setHomie(Homie h) { @@ -46,7 +43,7 @@ public void setHomie(Homie h) { * the dialog container. Clears the user input after processing. */ @FXML - private void handleUserInput() throws HomieException { + private void handleUserInput() { String input = userInput.getText(); String response = homie.getResponse(input); dialogContainer.getChildren().addAll( diff --git a/src/main/java/homie/MarkException.java b/src/main/java/homie/MarkException.java new file mode 100644 index 0000000000..9a9cf2bb95 --- /dev/null +++ b/src/main/java/homie/MarkException.java @@ -0,0 +1,16 @@ +package homie; + +/** + * Mark exception class. Handles all exceptions related to mark command. + * Thrown when no index is given as a parameter, or index out of range. + */ +public class MarkException extends Exception { + /** + * Constructor for MarkException class. + * + * @param message The error message. + */ + public MarkException(String message) { + super("Bruh... " + message + "\nPlease follow the format:\nmark {INDEX}"); + } +} diff --git a/src/main/java/homie/Parser.java b/src/main/java/homie/Parser.java index 1795cdd276..e299dbc003 100644 --- a/src/main/java/homie/Parser.java +++ b/src/main/java/homie/Parser.java @@ -1,7 +1,10 @@ package homie; +import java.text.ParseException; +import java.text.SimpleDateFormat; + /** - * Parser class to parse the user command + * Parser class to parse the user command. Process all the user commands and performs the relevant tasks. */ public class Parser { private boolean isExit = false; @@ -23,15 +26,33 @@ public Parser(TaskList taskList, Ui ui, Storage storage) { this.ui = ui; this.storage = storage; } + //@@author ZhiWei1010-reused + //Reused from https://www.baeldung.com/java-string-valid-date + // with minor modifications + /** + * Checks if input string is of 'dd MM yyyy HHmm' format + * + * @param inDate The string to be checked. + * @return The boolean value of whether the String is of specified date format "dd MM yyy HHmm". + */ + public static boolean isValidDate(String inDate) { + SimpleDateFormat dateFormat = new SimpleDateFormat("dd MM yyyy HHmm"); + dateFormat.setLenient(false); + try { + dateFormat.parse(inDate.trim()); + } catch (ParseException pe) { + return false; + } + return true; + } /** - * Parses user input calling the relevant methods to - * process the command. + * Parses user input calling the relevant methods to process the command. * * @param fullCommand The full user input command */ - - public String parse(String fullCommand) throws HomieException, TodoException { - String[] inputStringSplits = fullCommand.split(" "); + public String parse(String fullCommand) throws HomieException, TodoException, EventException, DeadlineException, + MarkException, UnmarkException, DeleteException, FindException { + String[] inputStringSplits = fullCommand.trim().split(" "); String primaryCommand = inputStringSplits[0].toLowerCase(); String outputResponse; switch (primaryCommand) { @@ -58,38 +79,34 @@ public String parse(String fullCommand) throws HomieException, TodoException { break; case "f": case "find": - outputResponse = this.processFindCommand(inputStringSplits); + outputResponse = this.processFindCommand(fullCommand, inputStringSplits); break; case "t": case "todo": - outputResponse = this.processTodoCommand(inputStringSplits); + outputResponse = this.processTodoCommand(fullCommand, inputStringSplits); break; case "dead": case "deadline": - outputResponse = this.processDeadlineCommand(fullCommand); + outputResponse = this.processDeadlineCommand(fullCommand, inputStringSplits); break; case "e": case "event": - outputResponse = this.processEventCommand(fullCommand); + outputResponse = this.processEventCommand(fullCommand, inputStringSplits); break; default: - outputResponse = this.processInvalidCommand(); + outputResponse = this.processInvalidCommand(fullCommand); break; } return outputResponse; } - public boolean isExit() { - return this.isExit; - } - /** * Process bye command and set isExit to true. * - * @return Returns a string goodbye message. + * @return Returns a goodbye message in String. */ private String processByeCommand() { // Get response message - outputResponse = this.ui.showGoodbyeMessage(); + outputResponse = this.ui.getGoodbyeMessage(); this.isExit = true; return outputResponse; } @@ -101,50 +118,111 @@ private String processByeCommand() { */ private String processListCommand() { // Get response message - outputResponse = this.ui.showListMessage(this.taskList); + String allTasks = this.taskList.getTasks(); + outputResponse = this.ui.showListMessage(allTasks); return outputResponse; } /** - * Process mark command to mark selected task as done. + * Process the to-do command to add new to-do task into the task list. * Updates the storage file as well. * - * @param inputStringSplits The String array containing the input command split by whitespace. - * @return String message that shows selected task marked as done. + * @param fullCommand The full command of the user input. + * @return String message to show that task has been added. */ - private String processMarkCommand(String[] inputStringSplits) { - // Mark task as done (execute command) - Task currTask; - int taskIndex = Integer.parseInt(inputStringSplits[1]); - this.taskList.markTask(taskIndex); + private String processTodoCommand(String fullCommand, String[] commandSplits) throws TodoException { + // Get Description for new to-do tasks + if (commandSplits.length < 2) { + throw new TodoException("No description given!"); + } + int startIndexForTodoDescription = 5; + String todoDescription = fullCommand.trim().substring(startIndexForTodoDescription); + // Create to-do task + Todo currTodo = new Todo(todoDescription); + // Add to-do task to task list + this.taskList.addTask(currTodo); // Update storage this.storage.updateStorageFile(this.taskList); // Get response message - currTask = this.taskList.getTask(taskIndex); - outputResponse = this.ui.showMarkMessage(currTask); + outputResponse = this.ui.getToDoMessage(currTodo, this.taskList.getSize()); return outputResponse; } - /** - * Process the unmark command to set selected task as not done. + * Process deadline command to add new deadline task into the task list. * Updates the storage file as well. * - * @param inputStringSplits The String array containing the input command split by whitespace. - * @return String message that shows selected task marked as not done. + * @param fullCommand The full user input command. + * @return String message showing that selected deadline task has been added into the task list. */ - private String processUnmarkCommand(String[] inputStringSplits) { - // Mark task as not done - Task currTask; - int taskIndex = Integer.parseInt(inputStringSplits[1]); - this.taskList.unMarkTask(taskIndex); - // Update Storage + private String processDeadlineCommand(String fullCommand, String[] commandSplits) throws DeadlineException { + int startIndexForDeadlineDescription = 9; + int startIndexForDeadlineDueDate = 3; + if (commandSplits.length < 2) { + throw new DeadlineException("No description given!"); + } + // Create new deadline task + String[] deadlineCommandStringSplits = (fullCommand.trim().substring(startIndexForDeadlineDescription)) + .split("/"); + String deadlineDescription = deadlineCommandStringSplits[0].trim(); + if (deadlineDescription.isEmpty()) { + throw new DeadlineException("No description given!"); + } + String deadlineDueDateInString = deadlineCommandStringSplits[1].substring(startIndexForDeadlineDueDate); + if (!isValidDate(deadlineDueDateInString)) { + throw new DeadlineException("Invalid date format!"); + } + Deadline currDeadline = new Deadline(deadlineDescription, deadlineDueDateInString); + // Add new deadline task to task list + this.taskList.addTask(currDeadline); + // Update storage this.storage.updateStorageFile(this.taskList); // Get response message - currTask = this.taskList.getTask(taskIndex); - outputResponse = this.ui.showUnmarkMessage(currTask); + outputResponse = this.ui.getDeadlineMessage(currDeadline, this.taskList.getSize()); + return outputResponse; + } + /** + * Process event command to add new event task into the task list. + * Updates the storage file as well. + * + * @param fullCommand The full user input command. + * @return String message showing that selected event task has been added into the task list. + */ + private String processEventCommand(String fullCommand, String[] commandSplits) throws EventException { + if (commandSplits.length < 2) { + throw new EventException("No description given!"); + } + int startIndexForEventDescription = 6; + int startIndexForStartDateTime = 5; + int startIndexForEndDateTime = 3; + int endIndexForStartDateTime = 20; + int endIndexForEndDateTime = 18; + // Create new event task + String[] eventCommandStringSplits = (fullCommand.trim() + .substring(startIndexForEventDescription)).split("/"); + String eventDescription = eventCommandStringSplits[0].trim(); + if (eventDescription.isEmpty()) { + throw new EventException("No description given!"); + } + String eventStartDateTimeInString = eventCommandStringSplits[1].substring(startIndexForStartDateTime, + endIndexForStartDateTime); + if (!isValidDate(eventStartDateTimeInString)) { + throw new EventException("/from date is of incorrect format!"); + } + String eventEndDateTimeInString = eventCommandStringSplits[2].substring(startIndexForEndDateTime, + endIndexForEndDateTime); + if (!isValidDate(eventEndDateTimeInString)) { + throw new EventException("/to date is of incorrect format!"); + } + Event currEvent = new Event(eventDescription, eventStartDateTimeInString, + eventEndDateTimeInString); + // Add event task to task list + this.taskList.addTask(currEvent); + // Update storage + this.storage.updateStorageFile(this.taskList); + // Get response message + outputResponse = this.ui.getEventMessage(currEvent, this.taskList.getSize()); return outputResponse; } - /** * Process delete command to delete selected task from task list. * Update the storage file for future loading of tasks. @@ -152,108 +230,95 @@ private String processUnmarkCommand(String[] inputStringSplits) { * @param inputStringSplits The String array containing the input command split by whitespace. * @return String message that shows selected task is deleted. */ - private String processDeleteCommand(String[] inputStringSplits) { + private String processDeleteCommand(String[] inputStringSplits) throws DeleteException { // Delete Task from task list Task currTask; + if (inputStringSplits.length < 2) { + throw new DeleteException("No index given!"); + } int taskIndex = Integer.parseInt(inputStringSplits[1]); + if (taskIndex < 1 || taskIndex > this.taskList.getSize()) { + throw new DeleteException("Invalid index."); + } currTask = this.taskList.getTask(taskIndex); this.taskList.deleteTask(taskIndex); // Update Storage this.storage.updateStorageFile(this.taskList); // Get response message - outputResponse = this.ui.showDeleteMessage(currTask, taskList); + outputResponse = this.ui.getDeleteMessage(currTask, taskList.getSize()); return outputResponse; } - /** - * Process find command to find the selected task from task list. + * Process mark command to mark selected task as done. + * Updates the storage file as well. * * @param inputStringSplits The String array containing the input command split by whitespace. - * @return A String of tasks that has the matching keyword. + * @return String message that shows selected task marked as done. */ - private String processFindCommand(String[] inputStringSplits) { - String keywordToFind = inputStringSplits[1]; + private String processMarkCommand(String[] inputStringSplits) throws MarkException { + // Mark task as done (execute command) + if (inputStringSplits.length < 2) { + throw new MarkException("No index given!"); + } + Task currTask; + int taskIndex = Integer.parseInt(inputStringSplits[1]); + if (taskIndex < 1 || taskIndex > this.taskList.getSize()) { + throw new MarkException("Invalid index."); + } + this.taskList.markTask(taskIndex); + // Update storage + this.storage.updateStorageFile(this.taskList); // Get response message - outputResponse = this.ui.showFindMessage(this.taskList, keywordToFind); + currTask = this.taskList.getTask(taskIndex); + outputResponse = this.ui.getMarkMessage(currTask); return outputResponse; } /** - * Process the to-do command to add new to-do task into the task list. + * Process the unmark command to set selected task as not done. * Updates the storage file as well. * * @param inputStringSplits The String array containing the input command split by whitespace. - * @return String message to show that task has been added. + * @return String message that shows selected task marked as not done. */ - private String processTodoCommand(String[] inputStringSplits) throws TodoException { - // Get Description for new to-do tasks - StringBuilder todoDescription = new StringBuilder(); - for (int i = 1; i < inputStringSplits.length; i++) { - todoDescription.append(inputStringSplits[i]); + private String processUnmarkCommand(String[] inputStringSplits) throws UnmarkException { + // Mark task as not done + if (inputStringSplits.length < 2) { + throw new UnmarkException("No index given!"); } - try { - // Create to-do task - Todo currTodo = new Todo(todoDescription.toString()); - // Add to-do task to task list - this.taskList.addTask(currTodo); - // Update storage - this.storage.updateStorageFile(this.taskList); - // Get response message - outputResponse = this.ui.showToDoMessage(currTodo, this.taskList); - } catch (TodoException e) { - throw e; + Task currTask; + int taskIndex = Integer.parseInt(inputStringSplits[1]); + this.taskList.unMarkTask(taskIndex); + if (taskIndex < 1 || taskIndex > this.taskList.getSize()) { + throw new UnmarkException("Invalid index."); } - return outputResponse; - } - - /** - * Process deadline command to add new deadline task into the task list. - * Updates the storage file as well. - * - * @param fullCommand The full user input command. - * @return String message showing that selected deadline task has been added into the task list. - */ - private String processDeadlineCommand(String fullCommand) { - int lengthOfDeadline = 9; - int lengthOfBy = 3; - // Create new deadline task - String[] deadlineCommandStringSplits = (fullCommand.substring(lengthOfDeadline)).split("/"); - String deadlineDescription = deadlineCommandStringSplits[0]; - String deadlineByInString = deadlineCommandStringSplits[1].substring(lengthOfBy); - Deadline currDeadline = new Deadline(deadlineDescription, deadlineByInString); - // Add new deadline task to task list - this.taskList.addTask(currDeadline); - // Update storage + // Update Storage this.storage.updateStorageFile(this.taskList); // Get response message - outputResponse = this.ui.showDeadlineMessage(currDeadline, this.taskList); + currTask = this.taskList.getTask(taskIndex); + outputResponse = this.ui.getUnmarkMessage(currTask); return outputResponse; } - /** - * Process event command to add new event task into the task list. - * Updates the storage file as well. + * Process find command to find the selected task from task list. * - * @param fullCommand The full user input command. - * @return String message showing that selected event task has been added into the task list. + * @param inputStringSplits The String array containing the input command split by whitespace. + * @return A String of tasks that has the matching keyword. */ - private String processEventCommand(String fullCommand) { - int lengthOfEvent = 6; - int lengthOfFrom = 5; - int lengthOfTo = 3; - // Create new event task - String[] eventCommandStringSplits = (fullCommand.substring(lengthOfEvent)).split("/"); - String eventDescription = eventCommandStringSplits[0]; - String eventStartTimeInString = eventCommandStringSplits[1].substring(lengthOfFrom); - String eventEndTimeInString = eventCommandStringSplits[2].substring(lengthOfTo); - Event currEvent = new Event(eventDescription, eventStartTimeInString, - eventEndTimeInString); - // Add event task to task list - this.taskList.addTask(currEvent); - // Update storage - this.storage.updateStorageFile(this.taskList); + private String processFindCommand(String fullCommand, String[] inputStringSplits) throws FindException { + if (inputStringSplits.length < 2) { + throw new FindException("No keyword given!"); + } + if (inputStringSplits.length != 3) { + throw new FindException("Keyword can only be 1 word!"); + } + String keywordToFind = inputStringSplits[1].trim(); + if (keywordToFind.isEmpty()) { + throw new FindException("No keyword given!"); + } // Get response message - outputResponse = this.ui.showEventMessage(currEvent, this.taskList); + String matchingTasks = this.taskList.findTask(keywordToFind); + outputResponse = this.ui.showFindMessage(matchingTasks); return outputResponse; } @@ -262,10 +327,8 @@ private String processEventCommand(String fullCommand) { * * @return String message showing that the command is invalid. */ - private String processInvalidCommand() throws HomieException { + private String processInvalidCommand(String fullCommand) throws HomieException { // Get response message - throw new HomieException(this.ui.showWrongCommand()); + throw new HomieException(fullCommand); } - - } diff --git a/src/main/java/homie/Storage.java b/src/main/java/homie/Storage.java index 588ed1bb2a..9cb7477bb2 100644 --- a/src/main/java/homie/Storage.java +++ b/src/main/java/homie/Storage.java @@ -12,9 +12,9 @@ import java.util.ArrayList; /** - * Storage class to deal with storing of tasks and + * Storage class to handle storing of tasks and * loading of tasks when the homie chatbot starts, ends or have - * any updates in the task list + * any updates in the task list. */ public class Storage { private static final String CURRENT_DIRECTORY_STRING = System.getProperty("user.dir"); @@ -23,7 +23,7 @@ public class Storage { private File myStorageFile; /** - * Constructor for Storage class + * Constructor for Storage class. */ public Storage() throws IOException { File storageDirectory = new File(STORAGE_DIRECTORY_PATH.toString()); @@ -57,7 +57,7 @@ public void createStorageDirectory(Path directoryPath) throws IOException { } } /** - * Method to create the storage text file if file is not yet created. + * Method to create the storage text file if file does not exist. */ public void createStorageFile(Path filePath) { File f = new File(filePath.toString()); @@ -74,6 +74,7 @@ public void createStorageFile(Path filePath) { /** * Method to load tasks from the text file into the tasks object. + * * @return Returns an ArrayList of type Tasks loaded with tasks from the text file to the caller. */ public ArrayList loadTasksFromFile() { @@ -82,7 +83,7 @@ public ArrayList loadTasksFromFile() { if (readTasks.available() == 0) { readTasks.close(); System.out.println("No tasks in file."); - return new ArrayList(); + return new ArrayList<>(); } ObjectInputStream readStream = new ObjectInputStream(readTasks); @@ -98,12 +99,16 @@ public ArrayList loadTasksFromFile() { } System.out.println("Error in loading task!"); - return new ArrayList(); + return new ArrayList<>(); } + //@@author ZhiWei1010-reused + //Reused from https://stackoverflow.com/questions/24475286/saving-class-objects-to-a-text-file-in-java + // with minor modifications /** * Method to update storage text file whenever there are changes to the task list. - * @param taskList This is the tasklist to update. + * + * @param taskList The tasklist that stores all tasks. */ public void updateStorageFile(TaskList taskList) { try { @@ -118,6 +123,4 @@ public void updateStorageFile(TaskList taskList) { System.out.println("Error updating storage file: " + e); } } - - } diff --git a/src/main/java/homie/Task.java b/src/main/java/homie/Task.java index d6e06cbfe6..cf845788a3 100644 --- a/src/main/java/homie/Task.java +++ b/src/main/java/homie/Task.java @@ -10,7 +10,8 @@ public class Task implements Serializable { protected boolean isDone; /** - * Constructor for Task class + * Constructor for Task class. + * * @param description This is the String description of the tasks. */ public Task(String description) { @@ -18,13 +19,25 @@ public Task(String description) { this.isDone = false; } + /** + * Gets status icon of task. + * + * @return Returns X if task is done. Else returns a whitespace. + */ public String getStatusIcon() { return (isDone ? "X" : " "); // mark done task with X } + + /** + * Sets a task as done. + */ public void setDone() { this.isDone = true; } + /** + * Sets a task as not done. + */ public void setNotDone() { this.isDone = false; } diff --git a/src/main/java/homie/TaskList.java b/src/main/java/homie/TaskList.java index e35e187916..b7e2677ae8 100644 --- a/src/main/java/homie/TaskList.java +++ b/src/main/java/homie/TaskList.java @@ -89,7 +89,7 @@ public void markTask(int taskIndex) { } /** - * Unmarks a task in the task list as done. + * Marks a task in the task list as not done. * * @param taskIndex Index of the task to be marked in the task list. */ @@ -100,14 +100,14 @@ public void unMarkTask(int taskIndex) { /** * Finds all tasks with matching keyword. * - * @param text The keyword to be matched in the task list + * @param keyword The keyword to be matched in the task list * @return The matching tasks in a string */ - public String findTask(String text) { + public String findTask(String keyword) { String matchingTasks = ""; int index = 1; for (Task task : this.myList) { - if (task.toString().contains(text)) { + if (task.toString().contains(keyword)) { matchingTasks = matchingTasks + "\t" + index + ". " + task + "\n"; index++; } diff --git a/src/main/java/homie/Todo.java b/src/main/java/homie/Todo.java index 33b8664d01..ed8a65885e 100644 --- a/src/main/java/homie/Todo.java +++ b/src/main/java/homie/Todo.java @@ -1,24 +1,17 @@ package homie; /** - * To-do class that extends the Task class. - * Throws a To-do exception when description is empty + * To-do class that extends the Task class. Handles all to-do related tasks. */ public class Todo extends Task { - private String DIVIDER = "________________________________________"; /** * Constructor for to-do object + * * @param description The String description of the to-do task - * @throws TodoException The exception when to-do description is empty */ - public Todo(String description) throws TodoException { + public Todo(String description) { super(description); - if (description.isEmpty()) { - throw new TodoException("\n" + DIVIDER + "\nOpps!!! The description of a todo cannot be empty.\n" - + DIVIDER); - } - } @Override diff --git a/src/main/java/homie/TodoException.java b/src/main/java/homie/TodoException.java index 818cdb062d..5d8f872720 100644 --- a/src/main/java/homie/TodoException.java +++ b/src/main/java/homie/TodoException.java @@ -1,10 +1,11 @@ package homie; /** - * To do Exception class. Thrown when there is an error creating to do task. + * TodoException class. Handles all exceptions related to to-do tasks. + * Thrown when no description is given when creating to-do task. */ public class TodoException extends Exception { public TodoException(String message) { - super(message); + super("Bruh... " + message + "\nPlease follow this format:\ntodo {TODO_DESCRIPTION}"); } } diff --git a/src/main/java/homie/Ui.java b/src/main/java/homie/Ui.java index d666041ef9..6f5b45b975 100644 --- a/src/main/java/homie/Ui.java +++ b/src/main/java/homie/Ui.java @@ -1,39 +1,38 @@ package homie; /** - * Ui Class to handle any output to user + * Ui Class to handle outputs to user. */ public class Ui { - public static final String DIVIDER = "________________________________________"; public Ui() { } /** - * Shows welcome message when user first open application. + * Gets welcome message when user first open application. * - * @return String message for welcome. + * @return Welcome message in String. */ - public String showWelcomeMessage() { - return DIVIDER + "\nSup I'm Homie!!\n" + "What can I do for you?\n" + DIVIDER; + public String getWelcomeMessage() { + return "Sup I'm Homie!!\n" + "What can I do for you?"; } /** - * Shows goodbye message when user exits the application. + * Gets goodbye message when user exits the application. * - * @return String message for goodbye. + * @return Goodbye message in String. */ - public String showGoodbyeMessage() { - return DIVIDER + "\nBye Homie. Hope to see you again soon!\n" + DIVIDER; + public String getGoodbyeMessage() { + return "Bye Homie. Hope to see you again soon!"; } /** - * Shows loading error message when there is an error loading tasks from storage. + * Gets loading error message when there is an error loading tasks from storage. * - * @return String message for error loading tasks. + * @return Loading error message in String. */ - public String showLoadingError() { - return DIVIDER + "\nHomie, theres an error loading your tasks!\n" + DIVIDER; + public String getLoadingErrorMessage() { + return "Homie, theres an error loading your tasks!"; } /** @@ -41,104 +40,84 @@ public String showLoadingError() { * * @return String message to show all tasks in the list. */ - public String showListMessage(TaskList tasks) { - return DIVIDER + "\nHere are the tasks in your list:\n" + tasks.getTasks() + DIVIDER; + public String showListMessage(String allTasks) { + return "Here are the tasks in your list:\n" + allTasks; } /** - * Shows delete message after deleting a task + * Gets delete message after deleting a task. * - * @param task The task object to be deleted - * @param tasks The task lists that stores all the task objects - * @return String message to acknowledge the task has been deleted + * @param task The task object to be deleted. + * @param taskListSize The remaining number of tasks in the task list. + * @return Delete task message in String. */ - public String showDeleteMessage(Task task, TaskList tasks) { - return DIVIDER + "\nNoted. I've removed this task:\n" + "\t" + task.toString() - + "\nNow you have " + tasks.getSize() + " tasks in the list.\n" + DIVIDER; + public String getDeleteMessage(Task task, int taskListSize) { + return "Noted. I've removed this task:\n" + "\t" + task.toString() + + "\nNow you have " + taskListSize + " tasks in the list."; } /** - * Shows message after adding a to-do task + * Gets message after adding a to-do task. * - * @param task The task object to be added - * @param tasks The task lists that stores all the task objects - * @return String message to acknowledge that the to-do task has been added + * @param task The new to-do task to be added. + * @param taskListSize The remaining number of tasks in the task list. + * @return String message to acknowledge that the to-do task has been added. */ - public String showToDoMessage(Task task, TaskList tasks) { - return DIVIDER + "\nGot it. I've added this task:\n" + "\t" + task.toString() - + "\nNow you have " + tasks.getSize() + " tasks in the list.\n" + DIVIDER; + public String getToDoMessage(Task task, int taskListSize) { + return "Got it. I've added this task:\n" + "\t" + task.toString() + + "\nNow you have " + taskListSize + " tasks in the list."; } /** - * Shows message after adding a deadline task + * Gets message after adding a deadline task. * - * @param task The deadline task to be added - * @param tasks The task lists that stores all the task objects - * @return String message to acknowledge that the deadline task has been added + * @param task The deadline task to be added. + * @param taskListSize The remaining number of tasks in the task list. + * @return String message to acknowledge that the deadline task has been added. */ - public String showDeadlineMessage(Task task, TaskList tasks) { - return DIVIDER + "\nGot it. I've added this task:\n" + "\t" + task.toString() - + "\nNow you have " + tasks.getSize() + " tasks in the list.\n" + DIVIDER; + public String getDeadlineMessage(Task task, int taskListSize) { + return "Got it. I've added this task:\n" + "\t" + task.toString() + + "\nNow you have " + taskListSize + " tasks in the list.\n"; } /** - * Shows message after added an event task. + * Gets message after adding an event task. * * @param task The event task to be added. - * @param tasks The task lists that stores all the tasks object. + * @param taskListSize The remaining number of tasks in the task list. * @return String message to acknowledge that the event task has been added. */ - public String showEventMessage(Task task, TaskList tasks) { - return DIVIDER + "\nGot it. I've added this task:\n" + "\t" + task.toString() - + "\nNow you have " + tasks.getSize() + " tasks in the list.\n" + DIVIDER; + public String getEventMessage(Task task, int taskListSize) { + return "Got it. I've added this task:\n" + "\t" + task.toString() + + "\nNow you have " + taskListSize + " tasks in the list."; } /** - * Shows message after marking a task. + * Gets message after marking a task as done. * - * @param task The task to be marked. + * @param task The task to be marked as done. * @return String message to acknowledge task has been marked. */ - public String showMarkMessage(Task task) { - return DIVIDER + "\nNice! I've marked this task as done:\n" + "\t" + task.toString() + "\n" + DIVIDER; + public String getMarkMessage(Task task) { + return "Nice! I've marked this task as done:\n" + "\t" + task.toString(); } /** - * Shows message when un marking a task. + * Gets message after marking a task as not done. * - * @param task The task to be unmarked. - * @return String message to acknowledge task has been unmarked. + * @param task The task to be marked as no done. + * @return String message to acknowledge task has been marked as not done. */ - public String showUnmarkMessage(Task task) { - return DIVIDER + "\nOk, I've marked this task as not done yet:\n" + "\t" + task.toString() + "\n" + DIVIDER; + public String getUnmarkMessage(Task task) { + return "Ok, I've marked this task as not done yet:\n" + "\t" + task.toString(); } /** - * Shows message after user entered a wrong command. - * - * @return String message that user has entered a wrong command. - */ - public String showWrongCommand() { - return DIVIDER + "\nWrong Command!\n" + DIVIDER; - } - - /** - * Shows tasks with the matching keyword. + * Gets all tasks that contains the matching keyword. * * @return String message of all tasks with matching keyword. */ - public String showFindMessage(TaskList tasks, String keyword) { - String matchingTasks = tasks.findTask(keyword); - return DIVIDER + "\nHere are the matching tasks in your list:\n" + matchingTasks + DIVIDER; - } - - /** - * Show empty to-do description task message - * - * @return String message of to-do description cannot be empty - */ - public String showEmptyTodoDescriptionMessage() { - return DIVIDER + "\nPlease la bro, Todo description cannot be empty...\nWhat you want to add liddat?\n" - + DIVIDER; + public String showFindMessage(String matchingTasks) { + return "Here are the matching tasks in your list:\n" + matchingTasks; } } diff --git a/src/main/java/homie/UnmarkException.java b/src/main/java/homie/UnmarkException.java new file mode 100644 index 0000000000..ae095d72ba --- /dev/null +++ b/src/main/java/homie/UnmarkException.java @@ -0,0 +1,16 @@ +package homie; + +/** + * UnmarkException class. Handles all exceptions related to unmark command. + * Thrown when no index is given as a parameter, or index is out of range. + */ +public class UnmarkException extends Exception { + /** + * Constructor for UnmarkException class. + * + * @param message The error message. + */ + public UnmarkException(String message) { + super("Bruh... " + message + "\nPlease follow the format:\nunmark {INDEX}"); + } +} diff --git a/src/main/resources/images/gojo.png b/src/main/resources/images/gojo.png deleted file mode 100644 index 2c0c745e34..0000000000 Binary files a/src/main/resources/images/gojo.png and /dev/null differ diff --git a/src/main/resources/images/homie.png b/src/main/resources/images/homie.png index bbbfbf5a1d..9bf82279c1 100644 Binary files a/src/main/resources/images/homie.png and b/src/main/resources/images/homie.png differ diff --git a/src/main/resources/images/user.png b/src/main/resources/images/user.png new file mode 100644 index 0000000000..beff2d197e Binary files /dev/null and b/src/main/resources/images/user.png differ diff --git a/src/test/java/homie/ParserTest.java b/src/test/java/homie/ParserTest.java new file mode 100644 index 0000000000..155b9decb8 --- /dev/null +++ b/src/test/java/homie/ParserTest.java @@ -0,0 +1,98 @@ +package homie; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +import org.junit.jupiter.api.Test; + +public class ParserTest { + @Test + void parseTodoCommand_validCommand_success() throws Exception { + Ui ui = new Ui(); + Storage storage = new Storage(); + TaskList taskList = new TaskList(); + Parser parser = new Parser(taskList, ui, storage); + String outputMessage = parser.parse("todo read book"); + assertEquals(ui.getToDoMessage(new Todo("read book"), 1), outputMessage); + assertEquals(1, taskList.getSize()); + outputMessage = parser.parse("todo read manga"); + assertEquals(ui.getToDoMessage(new Todo("read manga"), 2), outputMessage); + assertEquals(2, taskList.getSize()); + } + + @Test + void parseTodoCommand_emptyDescription_failure() throws Exception { + Ui ui = new Ui(); + Storage storage = new Storage(); + TaskList taskList = new TaskList(); + Parser parser = new Parser(taskList, ui, storage); + // no description + try { + parser.parse("todo"); + fail(); + } catch (TodoException e) { + assertEquals("Bruh... No description given!\nPlease follow this format:" + + "\ntodo {TODO_DESCRIPTION}", e.getMessage()); + assertEquals(0, taskList.getSize()); + } + // one empty white space + try { + parser.parse("todo "); + fail(); + } catch (TodoException e) { + assertEquals("Bruh... No description given!\nPlease follow this format:" + + "\ntodo {TODO_DESCRIPTION}", e.getMessage()); + assertEquals(0, taskList.getSize()); + } + // two empty white space + try { + parser.parse("todo "); + fail(); + } catch (TodoException e) { + assertEquals("Bruh... No description given!\nPlease follow this format:" + + "\ntodo {TODO_DESCRIPTION}", e.getMessage()); + assertEquals(0, taskList.getSize()); + } + } + @Test + void parseDeadlineCommand_validDeadlineCommand_success() throws Exception { + Ui ui = new Ui(); + Storage storage = new Storage(); + TaskList taskList = new TaskList(); + Parser parser = new Parser(taskList, ui, storage); + String outputMessage = parser.parse("deadline cs2103 quiz 3 /by 23 03 2024 2359"); + assertEquals(ui.getDeadlineMessage( + new Deadline("cs2103 quiz 3", "23 03 2024 2359"), 1), outputMessage); + assertEquals(1, taskList.getSize()); + outputMessage = parser.parse("deadline cs2107 assignment /by 28 02 2024 2359"); + assertEquals(ui.getDeadlineMessage( + new Deadline("cs2107 assignment", "28 02 2024 2359"), 2), outputMessage); + assertEquals(2, taskList.getSize()); + } + + @Test + void parseDeadlineCommand_emptyDescriptionAndInvalidDateCommand_failure() throws Exception { + Ui ui = new Ui(); + Storage storage = new Storage(); + TaskList taskList = new TaskList(); + Parser parser = new Parser(taskList, ui, storage); + // no description + try { + parser.parse("deadline"); + fail(); + } catch (DeadlineException e) { + assertEquals("Bruh... No description given!\nPlease follow the format:\ndeadline " + + "{DEADLINE_DESCRIPTION} /by {dd MM yyyy HHmm}", e.getMessage()); + assertEquals(taskList.getSize(), 0); + } + // incorrect date format + try { + parser.parse("deadline cs2103 quiz /by 23022024 3pm"); + fail(); + } catch (DeadlineException e) { + assertEquals(e.getMessage(), "Bruh... Invalid date format!\nPlease follow the format:\ndeadline " + + "{DEADLINE_DESCRIPTION} /by {dd MM yyyy HHmm}"); + assertEquals(taskList.getSize(), 0); + } + } +} diff --git a/src/test/java/homie/TaskListTest.java b/src/test/java/homie/TaskListTest.java index f0f03d1973..f984b777fe 100644 --- a/src/test/java/homie/TaskListTest.java +++ b/src/test/java/homie/TaskListTest.java @@ -7,39 +7,54 @@ public class TaskListTest { @Test - public void add_task_test() { + public void findTask_noMatchingTask_success() { TaskList tasks = new TaskList(); - try { - Task todoTask = new Todo("todo 1"); - tasks.addTask(todoTask); - assertEquals(todoTask, tasks.getTask(1)); - } catch (TodoException e) { - System.out.println(e.getMessage()); - } - Task eventTask = new Task("event go movie /from 6pm /to 830pm"); - Task deadlineTask = new Task("deadline quiz /by 3pm"); + Task todoTask = new Todo("todo read book"); + tasks.addTask(todoTask); + assertEquals(1, tasks.getSize()); + assertEquals(todoTask, tasks.getTask(1)); + Task eventTask = new Task("event movie date /from 23 02 2024 1400 /to 23 02 2024 1630"); tasks.addTask(eventTask); + assertEquals(2, tasks.getSize()); + assertEquals(eventTask, tasks.getTask(2)); + Task deadlineTask = new Task("deadline quiz /by 23 02 2024 2359"); tasks.addTask(deadlineTask); + assertEquals(3, tasks.getSize()); + assertEquals(deadlineTask, tasks.getTask(3)); + assertEquals("", tasks.findTask("fly")); + } + @Test + public void findTask_oneMatchingTask_success() { + TaskList tasks = new TaskList(); + Task todoTask = new Todo("todo read book"); + tasks.addTask(todoTask); + assertEquals(1, tasks.getSize()); + assertEquals(todoTask, tasks.getTask(1)); + Task eventTask = new Task("event movie date /from 23 02 2024 1400 /to 23 02 2024 1630"); + tasks.addTask(eventTask); + assertEquals(2, tasks.getSize()); assertEquals(eventTask, tasks.getTask(2)); + Task deadlineTask = new Task("deadline quiz /by 23 02 2024 2359"); + tasks.addTask(deadlineTask); + assertEquals(3, tasks.getSize()); assertEquals(deadlineTask, tasks.getTask(3)); + String expectedText = "\t1. " + todoTask + "\n"; + assertEquals(expectedText, tasks.findTask("read")); } - @Test - public void delete_task_test() { + public void getTask_oneTaskInTaskList_success() { TaskList tasks = new TaskList(); - try { - Task todoTask = new Todo("todo 1"); - tasks.addTask(todoTask); - } catch (TodoException e) { - System.out.println(e.getMessage()); - } - Task eventTask = new Task("event go movie /from 6pm /to 830pm"); - Task deadlineTask = new Task("deadline quiz /by 3pm"); + Task todoTask = new Todo("todo read book"); + tasks.addTask(todoTask); + assertEquals(1, tasks.getSize()); + assertEquals(todoTask, tasks.getTask(1)); + Task eventTask = new Task("event movie date /from 23 02 2024 1400 /to 23 02 2024 1630"); tasks.addTask(eventTask); + assertEquals(2, tasks.getSize()); + assertEquals(eventTask, tasks.getTask(2)); + Task deadlineTask = new Task("deadline quiz /by 23 02 2024 2359"); tasks.addTask(deadlineTask); - tasks.deleteTask(1); - assertEquals(tasks.getSize(), 2); - assertEquals(eventTask, tasks.getTask(1)); - assertEquals(deadlineTask, tasks.getTask(2)); + assertEquals(3, tasks.getSize()); + assertEquals(deadlineTask, tasks.getTask(3)); } } diff --git a/src/test/java/homie/TodoTest.java b/src/test/java/homie/TodoTest.java deleted file mode 100644 index f743f59a5c..0000000000 --- a/src/test/java/homie/TodoTest.java +++ /dev/null @@ -1,22 +0,0 @@ -package homie; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.fail; - -import org.junit.jupiter.api.Test; - - - -public class TodoTest { - @Test - public void no_command_exceptionThrown() { - String line = "____________________________________________________________"; - try { - Todo todo = new Todo(""); - assertEquals(todo, new Todo("")); - fail(); // the test should not reach this line - } catch (Exception e) { - assertEquals("\n" + line + "\nOpps!!! The description of a todo cannot be empty.\n" + line, e.getMessage()); - } - } -}