diff --git a/.gitignore b/.gitignore index 2873e189e..c20381871 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,8 @@ bin/ /text-ui-test/ACTUAL.TXT text-ui-test/EXPECTED-UNIX.TXT + +/src/main/java/META-INF + +/data +/data/memory.txt \ No newline at end of file diff --git a/docs/README.md b/docs/README.md index 8077118eb..4c5e76d14 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,29 +1,209 @@ -# User Guide +# User Guide for Luke ## Features -### Feature-ABC +### Task List -Description of the feature. +Luke keeps a task list of all the todo, deadlines and events. -### Feature-XYZ +### Mark as Completed/Incomplete -Description of the feature. +You can mark tasks as completed or incompleted. + +### Store Memory + +When closing Luke, all current tasks are stored in a txt file. +The next time Luke is opened, all tasks in the txt file is loaded back into Luke. ## Usage -### `Keyword` - Describe action -Describe the action and its outcome. +### `todo` - Add todo + +Add new task (todo) to your list. + +Format of usage: + +`todo DESCRIPTION` + +Example of usage: + +`todo read book` + +Expected outcome: + +New todo task added, so there will be a confirmation message. + +``` + Got it. I've added this task: + [T][ ] read book + Now you have 1 tasks in the list. +``` + +### `deadline` - Add deadline + +Add new task (deadline) to your list. + +Format of usage: + +`deadline DESCRIPTION /by DATE` + +Example of usage: + +`deadline return book /by next saturday` + +Expected outcome: + +New deadline task added, so there will be a confirmation message. + +``` + Got it. I've added this task: + [D][ ] return book (do by: next saturday) + Now you have 2 tasks in the list. +``` + +### `event` - Add event + +Add new task (event) to your list. + +Format of usage: + +`event DESCRIPTION /from STARTDATE /to ENDDATE` + +Example of usage: + +`event reading club session /from friday 4 /to 6pm` + +Expected outcome: + +New event task added, so there will be a confirmation message. + +``` + Got it. I've added this task: + [E][ ] reading club session (from: friday 4 to: 6pm) + Now you have 1 tasks in the list. +``` + +### `list` - Lists all tasks + +Lists all tasks in the task list. Example of usage: -`keyword (optional arguments)` +`list` + +Expected outcome: + +This will display all your tasks, including their type (todo, deadline, event), their task description and their status (completed or not). + +``` + Here are the tasks in your list: + 1. [T][ ] read book + 2. [D][ ] return book (do by: next saturday) + 3. [E][ ] reading club session (from: friday 4 to: 6pm) +``` + +### `mark` - Mark a task as completed + +Mark a task as completed using the "mark" command followed by the task number. + +Format of usage: + +`mark INDEX` + +Example of usage: + +`mark 1` + +Expected outcome: + +This will mark the task with index 1 as completed. + +``` + Woohoo! You have accomplished: + [T][X] read book +``` + +### `unmark` - Mark a task as incomplete + +Mark a task as incomplete using the "unmark" command followed by the task number. + +Format of usage: + +`unmark INDEX` + +Example of usage: + +`unmark 1` + +Expected outcome: + +This will mark the task with index 1 as incomplete. + +``` + HA! You still have to complete: + [T][ ] read book +``` + + +### `find` - Find user's input in tasks descriptions + +Show the corresponding tasks. + +Format of usage: + +`find KEYWORD` + +Example of usage: + +`find book` + +Expected outcome: + +List of tasks that have descriptions matching the user's input. + +``` + Here are the matching tasks in your list: + 1. [T][ ] read book + 2. [D][ ] return book (do by: next saturday) +``` + +### `delete` - Remove a task from the task list + +To delete a task from your list, use the "delete" command followed by the task number. + +Format of usage: + +`delete INDEX` + +Example of usage: + +`delete 2` + +Expected outcome: + +This will remove the task with index 2 from your list. + + +``` + Noted. I've removed this task: + [D][ ] return book (do by: next saturday) + Now you have 2 tasks in the list. +``` + +### `bye` - Close Luke + +Close Luke and store memory to a txt file. + +Example of usage: + +`bye` Expected outcome: -Description of the outcome. +Message whether memory is stored safely, and Luke is closed. ``` -expected output + Memory Stored Safely! + Bye. Hope to see you again soon! ``` 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/Luke.java b/src/main/java/Luke.java new file mode 100644 index 000000000..323b9e8d1 --- /dev/null +++ b/src/main/java/Luke.java @@ -0,0 +1,71 @@ + +import luke.actions.*; +import luke.user.LukeException; +import luke.user.Ui; +import luke.tasks.*; +import luke.files.*; + +/** + * The Luke Class represents the main application class for Luke, a task management application. + * It initializes the user interface, task storage and task list. + * It runs the application loop, managing the user interface, task storage, and task list. + */ +public class Luke { + public static final String folderPath = "data"; + + private Storage storage; + private TaskList tasks; + private Ui ui; + + /** + * Constructs a Luke object. + */ + public Luke() { + ui = new Ui(); + storage = new Storage(folderPath); + try { + tasks = new TaskList(storage.load()); + } catch (LukeException e) { + ui.showLoadingError(); + tasks = new TaskList(); + } catch (NullPointerException e) { + //ui.showNoMemoryFileError(); + tasks = new TaskList(); + } + } + + /** + * Runs the Luke application. + * It displays a welcome message, reads user commands, and executes corresponding actions. + * The application continues running until the user exits. + */ + public void run() { + ui.showWelcome(); + boolean isExit = false; + while (!isExit) { + try { + String fullCommand = ui.readCommand(); + ui.showLine(); // show the divider line ("_______") + Command c = Parser.parse(fullCommand); + //c has theActionWord and parameters + c.execute(tasks, ui, storage); + //tasks has ArrayList mainTaskList, ui has String echo, storage has ArrayList tasks + isExit = c.isExit(); //for bye command + } catch (LukeException e) { //from Parser.parse + ui.showError("\t☹ An error occurred." + e.getMessage()); + } finally { + ui.showLine(); + } + } + } + + /** + * The main method that starts the Luke application. + * + * @param args The command-line arguments (not used in this application). + */ + public static void main(String[] args) { + new Luke().run(); + } +} + diff --git a/src/main/java/luke/actions/ActionType.java b/src/main/java/luke/actions/ActionType.java new file mode 100644 index 000000000..d763a25e7 --- /dev/null +++ b/src/main/java/luke/actions/ActionType.java @@ -0,0 +1,9 @@ +package luke.actions; + +/** + * The ActionType enum represents the various types of actions that can be performed in the LukeTime application. + * Each action corresponds to a specific user command. + */ +public enum ActionType { + LIST, FIND, MARK, UNMARK, TODO, DEADLINE, EVENT, DELETE, BYE +} diff --git a/src/main/java/luke/actions/AddCommand.java b/src/main/java/luke/actions/AddCommand.java new file mode 100644 index 000000000..cbf900383 --- /dev/null +++ b/src/main/java/luke/actions/AddCommand.java @@ -0,0 +1,67 @@ +package luke.actions; + +import luke.files.Storage; +import luke.tasks.*; +import luke.user.LukeException; +import luke.user.Ui; + +import static luke.actions.ActionType.*; + +/** + * The AddCommand Class represents a command for adding a task to the Luke application. + * It extends the Command class and includes specific behavior for adding different types of tasks. + */ +public class AddCommand extends Command { + + /** + * The latest task created as a result of the add command. + */ + private Task latestTask; + + /** + * Constructs an AddCommand with the specified action type and parameters. + * + * @param theAction The action type (TODO, DEADLINE, EVENT). + * @param parameters The parameters provided with the command (task description or task details). + * @throws LukeException If there are missing or invalid arguments for creating the task. + */ + public AddCommand(ActionType theAction, String parameters) throws LukeException { + + super(theAction, parameters); + if (theAction == TODO) { + latestTask = new Todo(parameters); + } + if (theAction == DEADLINE) { + try { + latestTask = new Deadline(parameters); + } catch (LukeException e) { + System.out.println("\t☹ OOPS!!! You have missing/invalid arguments for deadline. No changes have been made."); + throw new LukeException(); + } + } + if (theAction == EVENT) { + try { + latestTask = new Event(parameters); + } catch (LukeException e) { + System.out.println("\t☹ OOPS!!! You have missing/invalid arguments for event. No changes have been made."); + throw new LukeException(); + } + } + + } + + /** + * Executes the AddCommand to add the latest task to the task list. + * + * @param tasks The task list to which the task will be added. + * @param ui The user interface for displaying messages. + * @param storage The storage for saving task changes (not used in this case). + */ + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) { + tasks.addTask(latestTask); + + System.out.println("\tGot it. I've added this task:" + "\n" + tasks.get(tasks.size() - 1)); + System.out.println("\tNow you have " + tasks.size() + " tasks in the list."); + } +} diff --git a/src/main/java/luke/actions/Command.java b/src/main/java/luke/actions/Command.java new file mode 100644 index 000000000..541ebdfe5 --- /dev/null +++ b/src/main/java/luke/actions/Command.java @@ -0,0 +1,66 @@ +package luke.actions; + +import luke.files.Storage; +import luke.tasks.*; +import luke.user.Ui; + +/** + * The Command class represents an abstract command in the Luke application. + * It serves as the base class for more specific command types. + */ +public abstract class Command { + + /** + * The type of action associated with the command. + */ + protected ActionType theAction; + + /** + * Additional parameters provided with the command (e.g., task description or task number). + */ + protected String parameters; + + /** + * Flag indicating whether the command triggers an exit from the application. + */ + private boolean isExit; + + /** + * Constructs a Command with the specified action type and parameters. + * + * @param theAction The type of action associated with the command. + * @param parameters Additional parameters provided with the command. + */ + public Command(ActionType theAction, String parameters) { + this.theAction = theAction; + this.parameters = parameters; + } + + /** + * Executes the command with the given task list, user interface, and storage. + * + * @param tasks The task list used for command execution. + * @param ui The user interface for displaying messages. + * @param storage The storage for saving task changes (not used in all commands). + */ + public void execute(TaskList tasks, Ui ui, Storage storage) { + } + + /** + * Sets the exit flag to indicate whether the command triggers an exit from the application. + * + * @param isExit True if the command triggers an exit, false otherwise. + */ + public void setIsExit(boolean isExit) { + this.isExit = isExit; + } + + /** + * Checks if the command triggers an exit from the application. + * + * @return True if the command triggers an exit, false otherwise. + */ + public boolean isExit() { + return isExit; + } +} diff --git a/src/main/java/luke/actions/DeleteCommand.java b/src/main/java/luke/actions/DeleteCommand.java new file mode 100644 index 000000000..62e1a8d75 --- /dev/null +++ b/src/main/java/luke/actions/DeleteCommand.java @@ -0,0 +1,58 @@ +package luke.actions; + +import luke.files.Storage; +import luke.tasks.TaskList; +import luke.user.Ui; + +/** + * The DeleteCommand Class represents a command for deleting a task in the Luke application. + * It extends the Command class and includes specific behavior for deleting a task. + */ +public class DeleteCommand extends Command { + + /** + * Constructs a DeleteCommand with the specified action type and parameters. + * + * @param theAction The action type (DELETE). + * @param parameters The parameters provided with the command (task number to delete). + */ + public DeleteCommand(ActionType theAction, String parameters) { + super(theAction, parameters); + } + + /** + * Prints a guide to provide user instructions for giving a DeleteCommand. + * + * @param tasks The task list to check if it is empty for a more specific user guide. + */ + public void printIntegerGuide(TaskList tasks) { + if (tasks.isEmpty()) { + System.out.println("\tNo tasks in task list. Please add a task before using " + theAction + "."); + } else { + System.out.println("\tPlease input an integer from 1 to " + tasks.size() + "."); + } + } + + /** + * Executes the DeleteCommand to remove a task from the task list when given valid arguments. + * + * @param tasks The task list from which the task will be deleted. + * @param ui The user interface for displaying messages. + * @param storage The storage for saving task changes (not used in this case). + */ + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) { + try { + int taskNumber = Integer.parseInt(parameters) - 1; + System.out.println("\tNoted. I've removed this task:\n" + tasks.get(taskNumber)); + tasks.removeTask(taskNumber); + System.out.println("\tNow you have " + tasks.size() + " tasks in the list."); + return; + } catch (IndexOutOfBoundsException e) { + System.out.println("\t☹ OOPS!!! Your argument for " + theAction + " exceeds your task list."); + } catch (NumberFormatException e) { + System.out.println("\t☹ OOPS!!! Your argument for " + theAction + " need to be an integer."); + } + printIntegerGuide(tasks); + } +} diff --git a/src/main/java/luke/actions/DoNothingCommand.java b/src/main/java/luke/actions/DoNothingCommand.java new file mode 100644 index 000000000..b162de63c --- /dev/null +++ b/src/main/java/luke/actions/DoNothingCommand.java @@ -0,0 +1,12 @@ +package luke.actions; + +/** + * The DoNothingCommand class represents a null command. + * It extends the Command class and is used when there are errors or invalid input in the user's command. + */ +public class DoNothingCommand extends Command { + public DoNothingCommand(ActionType theAction, String parameters) { + super(theAction, parameters); + } +} + diff --git a/src/main/java/luke/actions/ExitCommand.java b/src/main/java/luke/actions/ExitCommand.java new file mode 100644 index 000000000..7f20e4c2f --- /dev/null +++ b/src/main/java/luke/actions/ExitCommand.java @@ -0,0 +1,37 @@ +package luke.actions; + +import luke.files.Storage; +import luke.tasks.TaskList; +import luke.user.Ui; + +/** + * The ExitCommand class represents a command for exiting the Luke application. + * It extends the Command class and includes specific behavior for exiting the application. + */ +public class ExitCommand extends Command { + + /** + * Constructs an ExitCommand with the specified action type and parameters. + * + * @param theAction The action type (BYE). + * @param parameters The parameters provided with the command (not used in this case). + */ + public ExitCommand(ActionType theAction, String parameters) { + super(theAction, parameters); + } + + /** + * Executes the ExitCommand to store the task list and close the application. + * + * @param tasks The task list to be stored. + * @param ui The user interface for displaying messages. + * @param storage The storage for saving task changes. + * @throws IllegalArgumentException If there is an issue with storing the task list. + */ + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) throws IllegalArgumentException{ + storage.store(tasks); // Store the task list + setIsExit(true); + ui.closeUi(); // Close the user interface + } +} diff --git a/src/main/java/luke/actions/FindCommand.java b/src/main/java/luke/actions/FindCommand.java new file mode 100644 index 000000000..1ffad5b30 --- /dev/null +++ b/src/main/java/luke/actions/FindCommand.java @@ -0,0 +1,41 @@ +package luke.actions; + +import luke.files.Storage; +import luke.tasks.TaskList; +import luke.user.Ui; + +/** + * The FindCommand Class represents a command for finding tasks in the Luke application based on a keyword. + * It extends the Command class and includes specific behavior for finding tasks. + */ +public class FindCommand extends Command { + + /** + * Constructs a FindCommand with the specified action type and keyword parameters. + * + * @param theAction The action type (FIND). + * @param parameters The keyword parameters used for searching tasks. + */ + public FindCommand(ActionType theAction, String parameters) { + super(theAction, parameters); + } + + /** + * Executes the FindCommand to search for and display tasks that match the provided keyword. + * + * @param tasks The task list to search within. + * @param ui The user interface for displaying messages. + * @param storage The storage for saving task changes (not used in this case). + */ + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) { + System.out.println("\tHere are the matching tasks in your list:"); + int j = 1; + for (int i = 0; i < tasks.size(); i += 1) { + if (tasks.get(i).getDescription().contains(parameters)) { + System.out.println("\t" + j + "." + tasks.get(i)); + j += 1; + } + } + } +} diff --git a/src/main/java/luke/actions/ListCommand.java b/src/main/java/luke/actions/ListCommand.java new file mode 100644 index 000000000..c5aaa6b10 --- /dev/null +++ b/src/main/java/luke/actions/ListCommand.java @@ -0,0 +1,30 @@ +package luke.actions; + +import luke.files.Storage; +import luke.tasks.TaskList; +import luke.user.Ui; + +/** + * The ListCommand Class represents a command for listing tasks in the Luke application. + * It extends the Command class and includes specific behavior for listing tasks. + */ +public class ListCommand extends Command { + public ListCommand(ActionType theAction, String parameters) { + super(theAction, parameters); + } + + /** + * Executes the ListCommand to display the list of tasks. + * + * @param tasks The task list to be displayed. + * @param ui The user interface for displaying messages. + * @param storage The storage for saving task changes (not used in this case). + */ + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) { + System.out.println("\tHere are the tasks in your list:"); + for (int i = 0; i < tasks.size(); i += 1) { + System.out.println("\t" + (i + 1) + "." + tasks.get(i)); + } + } +} diff --git a/src/main/java/luke/actions/MarkCommand.java b/src/main/java/luke/actions/MarkCommand.java new file mode 100644 index 000000000..859b187d5 --- /dev/null +++ b/src/main/java/luke/actions/MarkCommand.java @@ -0,0 +1,68 @@ +package luke.actions; + +import luke.files.Storage; +import luke.tasks.TaskList; +import luke.user.Ui; + +import static luke.actions.ActionType.MARK; +import static luke.actions.ActionType.UNMARK; + +/** + * The MarkCommand Class represents a command for marking or unmarking tasks as done in the Luke application. + * It extends the Command class and includes specific behavior for marking and unmarking tasks. + */ +public class MarkCommand extends Command { + private boolean isDone; + + /** + * Constructs a MarkCommand with the specified action type and parameters. + * + * @param theAction The action type (MARK or UNMARK). + * @param parameters The parameters provided with the command. + */ + public MarkCommand(ActionType theAction, String parameters) { + super(theAction, parameters); + + if (theAction == MARK) { + isDone = true; + } + if (theAction == UNMARK) { + isDone = false; + } + } + + /** + * Prints a guide to provide user instructions for giving a DeleteCommand. + * + * @param tasks The task list to check if it is empty for a more specific user guide. + */ + public void printIntegerGuide(TaskList tasks) { + if (tasks.isEmpty()) { + System.out.println("\tNo tasks in task list. Please add a task before using " + theAction + "."); + } else { + System.out.println("\tPlease input an integer from 1 to " + tasks.size() + "."); + } + } + + /** + * Executes the MarkCommand to mark or unmark a task as done in the task list when given valid arguments. + * + * @param tasks The task list where the task should be marked or unmarked. + * @param ui The user interface for displaying messages. + * @param storage The storage for saving task changes. + */ + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) { + try { + int taskNumber = Integer.parseInt(parameters) - 1; + tasks.get(taskNumber).setDone(isDone); + System.out.println(tasks.get(taskNumber)); + return; + } catch (IndexOutOfBoundsException e) { + System.out.println("\t☹ OOPS!!! Your argument for " + theAction + " exceeds your task list."); + } catch (NumberFormatException e) { + System.out.println("\t☹ OOPS!!! Your argument for " + theAction + " need to be an integer."); + } + printIntegerGuide(tasks); + } +} diff --git a/src/main/java/luke/actions/Parser.java b/src/main/java/luke/actions/Parser.java new file mode 100644 index 000000000..6ec65ed3a --- /dev/null +++ b/src/main/java/luke/actions/Parser.java @@ -0,0 +1,93 @@ +package luke.actions; + +import luke.user.LukeException; + +/** + * The Parser Class handles the parsing of user input into valid commands. + */ +public class Parser{ + + /** + * Parses the user's full command and returns an appropriate Command object. + * + * @param fullCommand The full user input command. + * @return A Command object corresponding to the parsed command. + * @throws LukeException If an error specific to the LukeTime application occurs during parsing. + */ + public static Command parse(String fullCommand) throws LukeException { + ActionType theAction; + String parameters; + + String[] words = fullCommand.split(" "); + //to get parameters for actions "mark", "unmark" & "delete" + Command c; + try { + ActionType action = ActionType.valueOf(words[0].toUpperCase()); + theAction = action; + + try { + switch (action) { + case LIST: + parameters = ""; + c = new ListCommand(theAction, parameters); + break; + + case FIND: + parameters = fullCommand.substring(5); + c = new FindCommand(theAction, parameters); + break; + + case MARK: case UNMARK: + parameters = words[1]; + c = new MarkCommand(theAction, parameters); + break; + + case TODO: + parameters = fullCommand.substring(5); + c = new AddCommand(theAction, parameters); + break; + + case DEADLINE: + parameters = fullCommand.substring(9); + try { + c = new AddCommand(theAction, parameters); + } catch (LukeException e) { + c = new DoNothingCommand(ActionType.LIST, parameters); + } + break; + + case EVENT: + parameters = fullCommand.substring(6); + try { + c = new AddCommand(theAction, parameters); + } catch (LukeException e) { + c = new DoNothingCommand(ActionType.LIST, parameters); + } + break; + + case DELETE: + parameters = words[1]; + c = new DeleteCommand(theAction, parameters); + break; + + case BYE: + parameters = ""; + c = new ExitCommand(theAction, parameters); + break; + + default: + c = new DoNothingCommand(theAction, ""); + assert false: "This line should never be reached"; + break; + } + } catch (IndexOutOfBoundsException e) { //empty for MARK, UNMARK, TO DO description, DEADLINE description, EVENT description + System.out.println("\t☹ OOPS!!! You have missing arguments for " + words[0] + ". No changes have been made."); + c = new DoNothingCommand(ActionType.LIST, ""); + } + } catch (IllegalArgumentException e) { + System.out.println("\t☹ OOPS!!! I'm sorry, but I don't know what that means :-("); + c = new DoNothingCommand(ActionType.LIST, ""); + } + return c; + } +} diff --git a/src/main/java/luke/files/Memory.java b/src/main/java/luke/files/Memory.java new file mode 100644 index 000000000..021bf4533 --- /dev/null +++ b/src/main/java/luke/files/Memory.java @@ -0,0 +1,96 @@ +package luke.files; + +import luke.user.LukeException; +import luke.tasks.*; + +import java.io.File; +import java.io.FileNotFoundException; +import java.util.Scanner; +import java.io.FileWriter; + +import java.io.IOException; +import java.util.ArrayList; + +/** + * The Memory Class provides methods for reading from and storing tasks to a file. + */ +public class Memory { + + /** + * Reads tasks from a specified file and returns them as an ArrayList of Task objects. + * + * @param filePath The path to the file to read tasks from. + * @return An ArrayList of Task objects read from the file. + * @throws FileNotFoundException If the specified file is not found. + */ + public static ArrayList readMemory(String filePath) throws FileNotFoundException { + System.out.println("\n\tRetrieving memory..."); + + ArrayList tasks = new ArrayList<>(); + + File f = new File(filePath); // create a File for the given file path + Scanner s = new Scanner(f); // create a Scanner using the File as the source + while (s.hasNext()) { + String currentLine = s.nextLine(); + System.out.println("\t" + currentLine); + + String taskDetails = currentLine.substring(0,5); + String taskDescription = currentLine.substring(7); + char[] characters = taskDetails.toCharArray(); + try { + Task newTask; + switch (characters[1]) { + case 'T': + newTask = new Todo(taskDescription); + break; + case 'D': + newTask = new Deadline(taskDescription); + break; + case 'E': + newTask = new Event(taskDescription); + break; + default: //memory.txt is corrupted/in the wrong format + newTask = new Todo("error"); + break; + } + switch (characters[4]) { + case 'X': + newTask.setDone(true); + break; + case ' ': + newTask.setDone(false); + break; + } + tasks.add(newTask); + } catch (LukeException e) { + System.out.println("\t☹ An error occurred." + e.getMessage()); + } + } + System.out.println("\tMemory retrieval successful!\n"); + return tasks; + } + + /** + * Stores a list of Task objects to a specified file. + * + * @param filePath The path to the file to store tasks in. + * @param taskList The ArrayList of Task objects to be stored. + */ + public static void storeMemory(String filePath, ArrayList taskList){ + try { + FileWriter fw = new FileWriter(filePath); //overwrite file + + for (Task currentTask : taskList) { + fw.write(currentTask.memoryString() + "\n"); + } + + fw.close(); + + System.out.println("\tMemory Stored Safely!"); + + } catch (IOException e) { + System.out.println(("\t☹ An error occurred." + e.getMessage())); + } + + } +} \ No newline at end of file diff --git a/src/main/java/luke/files/Storage.java b/src/main/java/luke/files/Storage.java new file mode 100644 index 000000000..95b1c052e --- /dev/null +++ b/src/main/java/luke/files/Storage.java @@ -0,0 +1,90 @@ +package luke.files; + +import luke.tasks.Task; +import luke.tasks.TaskList; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.ArrayList; + +/** + * The Storage Class handles the reading from and writing task data to a file. + */ +public class Storage { + protected String filePath; + protected String folderPath; + private ArrayList tasks; + + /** + * Constructs a Storage object with the specified file path. If the file exists, + * it retrieves tasks from the file. If the file does not exist, it creates a new file. + * + * @param folderPath The path to the folder where the memory file is located. + */ + public Storage(String folderPath) { + this.folderPath = folderPath; + filePath = folderPath + "/memory.txt"; + + System.out.println(); + createDirectory(); + createFile(); + } + + /** + * Creates a directory at the specified folder path if it does not already exist. + */ + public void createDirectory () { + File d = new File(folderPath); + if (d.mkdir()) { + System.out.println("\tDirectory has been successfully created!"); + } else { + System.out.println("\tDirectory cannot be created. Directory may already exist."); + } + //System.out.println("\tFull path: " + d.getAbsolutePath()); + } + + /** + * Creates a memory file at the specified file path if it does not already exist. + */ + public void createFile () { + File f = new File(filePath); + try { + if (f.createNewFile()) { + System.out.println("\tNew memory file has been successfully created!"); + } else { + System.out.println("\tNew memory file cannot be created. Memory file may already exist."); + } + } catch (IOException e) { + System.out.println("\t☹ An error occurred." + e.getMessage()); + } + System.out.println("\tFull path: " + f.getAbsolutePath()); + } + + /** + * Loads and returns the ArrayList of Task objects retrieved from the file. + * + * @return An ArrayList of Task objects loaded from the file. + */ + public ArrayList load() { + try { + tasks = Memory.readMemory(filePath); + return tasks; + } catch (FileNotFoundException e) { + System.out.println("\t☹ An error occurred." + e.getMessage()); + } + return null; + } + + /** + * Stores the main task list to a specified file path. + * + * @param tasksToStore The TaskList object containing the main task list. + */ + public void store(TaskList tasksToStore) { + //store in memory.txt + Memory.storeMemory(filePath, tasksToStore.getMainTaskList()); + + System.out.println("\tBye. Hope to see you again soon!"); + } +} diff --git a/src/main/java/luke/tasks/Deadline.java b/src/main/java/luke/tasks/Deadline.java new file mode 100644 index 000000000..a30ea9ba9 --- /dev/null +++ b/src/main/java/luke/tasks/Deadline.java @@ -0,0 +1,90 @@ +package luke.tasks; +import luke.user.LukeException; + +/** + * The Deadline Class represents a task of type "Deadline" in the Luke application. + * It extends the Task class and includes specific behavior for deadline tasks. + */ +public class Deadline extends Task { + protected String date; + protected String deadlineGuide = "\tdeadline /by "; + + /** + * Constructs a Deadline task with the specified task description and deadline date. + * + * @param taskDescription The description of the deadline task including the deadline date. + * @throws LukeException If there are syntax or formatting errors in the task description. + */ + public Deadline(String taskDescription) throws LukeException { + super(taskDescription); + + int byIndex = taskDescription.indexOf("/by "); + if (byIndex == 0) { + System.out.println("\tThere is a missing task description. Please follow this format:"); + printGuide(); + throw new LukeException(); + } else if (byIndex < 0) { + System.out.println("\tThere is a syntax problem. Please follow this format:"); + printGuide(); + throw new LukeException(); + } + + description = taskDescription.substring(0, byIndex); + + setDate(taskDescription.substring(byIndex + 4)); + } + + /** + * Gets the deadline date of the deadline task. + * + * @return The deadline date of the deadline task. + */ + public String getDate() { + return date; + } + + + + /** + * Parses and sets the deadline date of the deadline task from the provided date string. + * + * @param dateString The date string containing the deadline date information. + * @throws LukeException If there are syntax or formatting errors in the date string. + */ + public void setDate(String dateString) throws LukeException { + if (dateString.isEmpty()) { + System.out.println("\tThere is a missing date. Please follow this format:"); + printGuide(); + throw new LukeException(); + } + date = dateString; + } + + /** + * Displays a guide on how to format a deadline task when printing an error message. + */ + @Override + public void printGuide() { + System.out.println(deadlineGuide); + } + + /** + * Returns a string representation of the deadline task, including its completion status and deadline date. + * + * @return A string representation of the deadline task. + */ + @Override + public String toString() { + return "\t[D]" + getIsDone() + " " + getDescription() + "(do by: " + getDate() + ")"; + } + + /** + * Returns a string representation of the deadline task for memory storage. + * + * @return A string representation of the deadline task for memory storage. + */ + @Override + public String memoryString() { + return "[D]" + getIsDone() + " " + getDescription() + "/by " + getDate(); + } +} diff --git a/src/main/java/luke/tasks/Event.java b/src/main/java/luke/tasks/Event.java new file mode 100644 index 000000000..89d2fb60d --- /dev/null +++ b/src/main/java/luke/tasks/Event.java @@ -0,0 +1,101 @@ +package luke.tasks; +import luke.user.LukeException; + +/** + * The Event class represents a task of type "Event" in the Luke application. + * It extends the Task class and includes specific behavior for event tasks. + */ +public class Event extends Task { + protected String startDate; + protected String endDate; + protected String eventGuide = "\tevent /from /to "; + + /** + * Constructs an Event task with the specified task description, start date, and end date. + * + * @param taskDescription The description of the event task including start and end dates. + * @throws LukeException If there are syntax or formatting errors in the task description. + */ + public Event(String taskDescription) throws LukeException { + super(taskDescription); + + int fromIndex = taskDescription.indexOf("/from "); + int toIndex = taskDescription.indexOf("/to "); + if (fromIndex == 0) { + System.out.println("\tThere is a missing task description. Please follow this format:"); + printGuide(); + throw new LukeException(); + } else if (fromIndex < 0 || toIndex <= fromIndex) { + System.out.println("\tThere is a syntax problem. Please follow this format:"); + printGuide(); + throw new LukeException(); + } + + description = taskDescription.substring(0, fromIndex); + setDates(taskDescription.substring(fromIndex + 6, toIndex), taskDescription.substring(toIndex + 4)); + } + + /** + * Gets the start date of the event. + * + * @return The start date of the event. + */ + public String getStartDate() { + return startDate; + } + + /** + * Gets the end date of the event. + * + * @return The end date of the event. + */ + public String getEndDate() { + return endDate; + } + + /** + * Parses and sets the start and end dates of the event from the provided date string. + * + * @param fromDateString The start date string. + * @param toDateString The end date string. + * @throws LukeException If there are syntax or formatting errors in the date string. + */ + public void setDates(String fromDateString, String toDateString) throws LukeException { + if (fromDateString.isEmpty() || toDateString.isEmpty()) { + System.out.println("\tThere is a missing date. Please follow this format:"); + printGuide(); + throw new LukeException(); + } + + startDate = fromDateString; + endDate = toDateString; + } + + /** + * Displays a guide on how to format an event task when printing an error message. + */ + @Override + public void printGuide() { + System.out.println(eventGuide); + } + + /** + * Returns a string representation of the event task for printing. + * + * @return A string representation of the event task. + */ + @Override + public String toString() { + return "\t[E]" + getIsDone() + " " + getDescription() + "(from: " + getStartDate() + "to: " + getEndDate() + ")"; + } + + /** + * Returns a string representation of the event task for memory storage. + * + * @return A string representation of the event task for memory storage. + */ + @Override + public String memoryString() { + return "[E]" + getIsDone() + " " + getDescription() + "/from " + getStartDate() + "/to " + getEndDate(); + } +} diff --git a/src/main/java/luke/tasks/Task.java b/src/main/java/luke/tasks/Task.java new file mode 100644 index 000000000..038de47a2 --- /dev/null +++ b/src/main/java/luke/tasks/Task.java @@ -0,0 +1,81 @@ +package luke.tasks; + +/** + * The Task Class represents an abstract class in the Luke application. + * It serves as the base class for different types of tasks and provides common task attributes and methods. + */ +public abstract class Task { + protected String description; + protected boolean isDone; + + /** + * Constructs a Task object with the specified description and sets its initial completion status to false. + * + * @param description The description of the task. + */ + public Task(String description) { + this.description = description; + isDone = false; + } + + /** + * Gets the description of the task. + * + * @return The description of the task. + */ + public String getDescription() { + return description; + } + + /** + * Checks if the task is marked as done. + * + * @return true if the task is done, false otherwise. + */ + public boolean isDone() { + return isDone; + } + + /** + * Gets a string representation of the task's completion status. + * + * @return "[X]" if the task is done, "[ ]" if it's not done. + */ + public String getIsDone() { + if (isDone()) { + return "[X]"; + } else { + return "[ ]"; + } + } + + /** + * Sets the completion status of the task and displays a corresponding message. + * + * @param done true if the task is done, false otherwise. + */ + public void setDone(boolean done) { + if (done) { + System.out.println("\tWoohoo! You have accomplished:"); + } else { + System.out.println("\tHA! You still have to complete:"); + } + isDone = done; + } + + /** + * Provides a default implementation for printing a guide specific to each task type. + */ + public void printGuide() { + //Default Implementation: do nothing + } + + /** + * Returns a string representation of the task for memory storage. + * + * @return A string representation of the task for memory storage. + */ + public String memoryString() { + return "[T]" + getIsDone() + " task" + getDescription(); + } +} \ No newline at end of file diff --git a/src/main/java/luke/tasks/TaskList.java b/src/main/java/luke/tasks/TaskList.java new file mode 100644 index 000000000..abaf1ad68 --- /dev/null +++ b/src/main/java/luke/tasks/TaskList.java @@ -0,0 +1,97 @@ +package luke.tasks; + +import java.util.ArrayList; +import luke.user.LukeException; + +/** + * The TaskList Class contains the task list and provides methods to manage and retrieve tasks from the task list. + */ +public class TaskList{ + + private ArrayList mainTaskList; + public int numberOfTasks; + + /** + * Constructs a TaskList object from an existing ArrayList of tasks. + * + * @param tasklist An ArrayList of Task objects to initialize the task list with. + * @throws LukeException If an error specific to the LukeTime application occurs during initialization. + */ + + public TaskList(ArrayList tasklist) throws LukeException, NullPointerException { + mainTaskList = new ArrayList(); + numberOfTasks = 0; + for (Task item: tasklist) { + addTask(item); + } + } + + /** + * Constructs an empty TaskList object. + */ + public TaskList() { + mainTaskList = new ArrayList(); + numberOfTasks = 0; + } + + /** + * Adds a task to the task list. + * + * @param taskName The Task object to be added. + */ + public void addTask(Task taskName) { + mainTaskList.add(taskName); + numberOfTasks += 1; + } + + /** + * Removes a task from the task list by its index. + * + * @param taskNumber The index of the task to be removed. + */ + public void removeTask(int taskNumber) { + mainTaskList.remove(taskNumber); + numberOfTasks -= 1; + } + + /** + * Returns the number of tasks in the task list. + * + * @return The number of tasks in the task list. + */ + public int size() { + return numberOfTasks; + } + + /** + * Checks if the task list is empty. + * + * @return true if the task list is empty, false otherwise. + */ + public boolean isEmpty() { + if (numberOfTasks <= 0) { + return true; + } + return false; + } + + /** + * Retrieves a task from the task list by its index. + * + * @param taskNumber The index of the task to be retrieved. + * @return The Task object at the specified index. + * @throws IndexOutOfBoundsException If the specified index is out of bounds. + */ + public Task get(int taskNumber) throws IndexOutOfBoundsException { + return mainTaskList.get(taskNumber); + } + + /** + * Returns the main ArrayList of Task objects containing the task list. + * + * @return An ArrayList of Task objects representing the task list. + */ + public ArrayList getMainTaskList() { + return mainTaskList; + } +} diff --git a/src/main/java/luke/tasks/Todo.java b/src/main/java/luke/tasks/Todo.java new file mode 100644 index 000000000..7a9c89c7b --- /dev/null +++ b/src/main/java/luke/tasks/Todo.java @@ -0,0 +1,52 @@ +package luke.tasks; + +/** + * The Todo class represents a task of type "Todo" in the Luke application. + * It extends the Task class and includes specific behavior for todo tasks. + */ +public class Todo extends Task { + protected String todoGuide = "\ttodo "; + + /** + * Constructs a Todo task with the specified description. + * + * @param description The description of the todo task. + */ + public Todo(String description) { + super(description); //ensures superclass is properly initialised + + if (description.isEmpty()) { + System.out.println("\tThere is a missing task description. Please follow this format:"); + printGuide(); + throw new IndexOutOfBoundsException(); + } + } + + /** + * Displays a guide on how to format a todo task when printing an error message. + */ + @Override + public void printGuide() { + System.out.println(todoGuide); + } + + /** + * Returns a string representation of the todo task for printing. + * + * @return A string representation of the todo task. + */ + @Override + public String toString() { + return "\t[T]" + getIsDone() + " " + getDescription(); + } + + /** + * Returns a string representation of the todo task for storing in memory. + * + * @return A string representation of the todo task for memory storage. + */ + @Override + public String memoryString() { + return "[T]" + getIsDone() + " " + getDescription(); + } +} diff --git a/src/main/java/luke/user/LukeException.java b/src/main/java/luke/user/LukeException.java new file mode 100644 index 000000000..862eff4a3 --- /dev/null +++ b/src/main/java/luke/user/LukeException.java @@ -0,0 +1,12 @@ +package luke.user; + +/** + * The LukeException Class represents custom exceptions specific to the Luke application. + * It extends the Exception class. + */ +public class LukeException extends Exception { + @Override + public String getMessage() { + return "LukeException has occurred."; + } +} \ No newline at end of file diff --git a/src/main/java/luke/user/Ui.java b/src/main/java/luke/user/Ui.java new file mode 100644 index 000000000..841e0b27b --- /dev/null +++ b/src/main/java/luke/user/Ui.java @@ -0,0 +1,94 @@ +package luke.user; + +import java.util.Scanner; + +/** + * The Ui Class is responsible for user interaction, including reading user input and displaying messages. + */ +public class Ui { + public String echo; + public Scanner userInput; + + private String logo = "\t _ _ \n" + + "\t| | _ _| | _____ \n" + + "\t| | | | | | |/ / _ \\\n" + + "\t| |___| |_| | < __/\n" + + "\t|_____|\\__,_|_|\\_\\___|\n"; + + private String userGuide = "\n\tQuick guide to using Luke...\n" + + "\t\tlist\n" + + "\t\ttodo \n" + + "\t\tdeadline /by \n" + + "\t\tevent /from /to \n" + + "\t\tmark \n" + + "\t\tunmark \n" + + "\t\tdelete \n" + + "\t\tfind \n" + + "\t\tbye\n"; + + /** + * Constructs a Ui object and initializes the user input scanner. + */ + public Ui () { + userInput = new Scanner(System.in); + } + + /** + * Closes the user input scanner. + */ + public void closeUi() { + userInput.close(); + } + + /** + * Displays a welcome message with a logo when the application starts. + */ + public void showWelcome() { + System.out.println("\t" + "Hello! I'm\n" + logo); + System.out.println("\tWhat can I do for you?"); + + System.out.print(userGuide); + showLine(); + } + + /** + * Displays a divider line to separate different sections of output. + */ + public void showLine() { + // show the divider line ("_______") + System.out.println("\t____________________________________________________________"); + } + + /** + * Displays an error message for loading issues. + */ + public void showLoadingError() { + System.out.println("\tError in loading memory."); + } + + /** + * Displays an error message for loading issues. + */ + public void showNoMemoryFileError() { + System.out.println("\tNo memory is loaded."); + } + + /** + * Displays an error message to the user. + * + * @param error The error message to be displayed. + */ + public void showError(String error) { + System.out.println(error); + } + + /** + * Reads a command from the user and returns it as a string. + * + * @return The user's input command as a string. + */ + public String readCommand() { + echo = userInput.nextLine(); + return echo; + } +} diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 657e74f6e..4c260316b 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,7 +1,27 @@ -Hello from - ____ _ -| _ \ _ _| | _____ -| | | | | | | |/ / _ \ -| |_| | |_| | < __/ -|____/ \__,_|_|\_\___| +Hello! I'm + _ _ +| | _ _| | _____ +| | | | | | |/ / _ \ +| |___| |_| | < __/ +|_____|\__,_|_|\_\___| +What can I do for you? +Got it. I've added this task: +[T][ ] Liow Enqi Janelle Answer CS2113 tutorial questions +Now you have 1 tasks in the list. +Got it. I've added this task: +[D][ ] weekly CS2113 quiz (do by: Monday 9pm) +Now you have 2 tasks in the list. +Here are the tasks in your list: +1.[T][ ] Liow Enqi Janelle Answer CS2113 tutorial questions +2.[D][ ] weekly CS2113 quiz (do by: Monday 9pm) +Got it. I've added this task: +[E][ ] CS2113 lecture (from: Friday 4 to: 6pm) +Now you have 3 tasks in the list. +Woohoo! You have accomplished: +[T][X] Liow Enqi Janelle Answer CS2113 tutorial questions +Here are the tasks in your list: +1.[T][X] Liow Enqi Janelle Answer CS2113 tutorial questions +2.[D][ ] weekly CS2113 quiz (do by: Monday 9pm) +3.[E][ ] CS2113 lecture (from: Friday 4 to: 6pm) +Bye. Hope to see you again soon! diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index e69de29bb..07daf1c7e 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -0,0 +1,7 @@ +todo Liow Enqi Janelle Answer CS2113 tutorial questions +deadline weekly CS2113 quiz /by Monday 9pm +list +event CS2113 lecture /from Friday 4 /to 6pm +mark 1 +list +bye \ No newline at end of file diff --git a/text-ui-test/runtest.sh b/text-ui-test/runtest.sh index c9ec87003..3844ed30b 100644 --- a/text-ui-test/runtest.sh +++ b/text-ui-test/runtest.sh @@ -1,5 +1,6 @@ #!/usr/bin/env bash + # create bin directory if it doesn't exist if [ ! -d "../bin" ] then @@ -13,7 +14,7 @@ then fi # compile the code into the bin folder, terminates if error occurred -if ! javac -cp ../src/main/java -Xlint:none -d ../bin ../src/main/java/*.java +if ! javac -cp /Users/janelle/cs2113repo/ip/src/main/java -Xlint:none -d ../bin /Users/janelle/cs2113repo/ip/src/main/java/*.java then echo "********** BUILD FAILURE **********" exit 1 @@ -23,11 +24,11 @@ fi java -classpath ../bin Duke < input.txt > ACTUAL.TXT # convert to UNIX format -cp EXPECTED.TXT EXPECTED-UNIX.TXT -dos2unix ACTUAL.TXT EXPECTED-UNIX.TXT +#cp EXPECTED.TXT EXPECTED-UNIX.TXT +#dos2unix ACTUAL.TXT EXPECTED-UNIX.TXT # compare the output to the expected output -diff ACTUAL.TXT EXPECTED-UNIX.TXT +diff ACTUAL.TXT EXPECTED.TXT if [ $? -eq 0 ] then echo "Test result: PASSED"