diff --git a/.gitignore b/.gitignore index f69985ef1..ef409edb7 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ bin/ /text-ui-test/ACTUAL.txt text-ui-test/EXPECTED-UNIX.TXT +src/main/java/META-INF/MANIFEST.MF diff --git a/DukeTaskData.txt b/DukeTaskData.txt new file mode 100644 index 000000000..e69de29bb diff --git a/docs/README.md b/docs/README.md index 8077118eb..921db1ddb 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2,28 +2,191 @@ ## Features -### Feature-ABC +### 1. Add and Delete Tasks -Description of the feature. +There are three different types of tasks that can be tracked - +todo, deadline and event. -### Feature-XYZ +### 2. List Tasks -Description of the feature. +A list of all the tasks will show. Details such as +which category of task, completion of task and description +of tasks will be shown. + +### 3. Complete Tasks + +Once you are done with a task, you can mark +the task as completed. + +### 4. Find Tasks + +Search for tasks with a certain keyword. This +will allow you to see specific tasks that +contains the keyword of your choice. ## Usage -### `Keyword` - Describe action +### `todo` - Adds todo task -Describe the action and its outcome. +Begin your input with a 'todo' followed by +the description of the todo task. Example of usage: -`keyword (optional arguments)` +`todo borrow book` + +Expected outcome: + +A new todo task with the corresponding description +will be added. + +``` +-------------------- +Got it. I've added this task: +[T] [ ] borrow book +Now you have 1 tasks in the list. +-------------------- +``` +### `deadline` - Adds deadline task + +Begin your input with a 'deadline' followed by +the description of the deadline task. Remember to include '/by' +followed by the deadline description at the end. + +Example of usage: + +`deadline return book /by tonight` Expected outcome: -Description of the outcome. +A new deadline task with the corresponding description +and deadline will be added. ``` -expected output +-------------------- +Got it. I've added this task: +[D] [ ] return book (by: tonight) +Now you have 1 tasks in the list. +-------------------- ``` +### `event` - Adds event task + +Begin your input with a 'event' followed by +the description of the event task. Remember to include '/at' +followed by the event timing description at the end. + +Example of usage: + +`event library party /at tonight 10pm` + +Expected outcome: + +A new event task with the corresponding description +and event timing will be added. + +``` +-------------------- +Got it. I've added this task: +[E] [ ] library party (at: tonight 10pm) +Now you have 1 tasks in the list. +-------------------- +``` +### `delete` - Deletes a task + +Removes a specific task from the list. + +Example of usage: + +`delete 1` + +Expected outcome: + +A specified task will be removed. + +``` +-------------------- +Noted. I've removed this task: +1.[ ] library party +Now you have 0 tasks in the list. +-------------------- +``` +### `list` - Lists all tasks + +A list of all tasks will be shown. + +Example of usage: + +`list` + +Expected outcome: + +A list of all tasks will be shown according to +the sequence that they were added. + +``` +-------------------- +Here are the tasks in your list: +1.[T] [ ] eat meat +2.[D] [ ] drink water (by: tonight) +3.[E] [ ] workout (at: midnight) +-------------------- +``` +### `done` - Mark a task as completed + +A specified task will be marked as completed. + +Example of usage: + +`done 1` + +Expected outcome: + +A specified task will be marked as completed. This +task will still remain on the list. + +``` +-------------------- +Nice! I've marked this task as done: +1.[X] eat meat +-------------------- +``` +### `find` - Find tasks containing keyword + +All tasks in the list containing the specific +keyword will be shown. + +Example of usage: + +`find water` + +Expected outcome: + +All tasks in the list containing the specific +keyword will be shown. + +``` +-------------------- +Here are the matching tasks in your list: +2.[D] [ ] drink water (by: tonight) +-------------------- +``` +### `bye` - Terminates program safely + +Duke will be terminated safely. + +Example of usage: + +`bye` + +Expected outcome: + +A goodbye message will be shown. + +``` +-------------------- +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/duke/Duke.java b/src/main/java/duke/Duke.java new file mode 100644 index 000000000..6cc47533f --- /dev/null +++ b/src/main/java/duke/Duke.java @@ -0,0 +1,34 @@ +package duke; + +import duke.processes.Parser; +import duke.processes.Storage; +import duke.processes.TaskList; +import duke.processes.Ui; + +import java.io.IOException; +import java.util.Scanner; + +public class Duke { + + public static void main(String[] args) throws IOException { + TaskList tasks = new TaskList(); + Storage.loadTasks(tasks); + Ui.printWelcomeMessage(); + activeChat(tasks); + Storage.saveTasks(tasks); + Ui.printGoodbyeMessage(); + } + + private static void activeChat(TaskList tasks) { + boolean isBye = false; + String input; + Scanner in = new Scanner(System.in); + while(!isBye){ + //store input into String + input = in.nextLine(); + //process input + isBye = Parser.processInput(tasks, input); + } + } + +} diff --git a/src/main/java/duke/exceptions/IllegalTaskException.java b/src/main/java/duke/exceptions/IllegalTaskException.java new file mode 100644 index 000000000..e7d3f1cbb --- /dev/null +++ b/src/main/java/duke/exceptions/IllegalTaskException.java @@ -0,0 +1,4 @@ +package duke.exceptions; + +public class IllegalTaskException extends Exception{ +} diff --git a/src/main/java/duke/processes/Parser.java b/src/main/java/duke/processes/Parser.java new file mode 100644 index 000000000..fbb9ef3bb --- /dev/null +++ b/src/main/java/duke/processes/Parser.java @@ -0,0 +1,146 @@ +package duke.processes; + +import duke.exceptions.IllegalTaskException; +import duke.tasks.Deadline; +import duke.tasks.Event; +import duke.tasks.Task; +import duke.tasks.ToDo; + +public class Parser { + /** + * Returns a boolean to break the while loop. + * This method also processes what user inputs. + * @param tasks is an instance of the public class TaskList, + * where the list of tasks and counter for number of tasks + * can be found and is updated based on user inputs. + * @param input is the entire chunk of message that user inputs. + * @return boolean whether input starts with 'Bye' + */ + public static boolean processInput(TaskList tasks, String input) { + boolean isBye = false; + if (input.equals("bye")){ //check if bye + isBye = true; + } else if (input.startsWith("list")) { //check if list + processList(tasks); + } else if (input.startsWith("done") ) { //check if done + processDone(tasks, input); + } else if (input.startsWith("delete") ) { //check if delete + processDelete(tasks, input); + } else if (input.startsWith("find") ) { + processFind(tasks, input); + } else { + try { + tasks.counter = processTasks(tasks, input); //process tasks + } catch ( IllegalTaskException e ) { + System.out.println("Please include /by for deadline and /at for event"); + } + } + return isBye; + } + + private static void processFind(TaskList tasks, String input) { + int findCount = 0; + String keyword = input.substring(5); + Ui.printFindMessageStart(); + for(int i = 0; i < tasks.counter; i += 1) { + Task currTask = tasks.list.get(i); + if( currTask.getDescription().contains(keyword) ){ + Ui.printListOfTaskSubMessage(currTask,i); + findCount += 1; + } + } + if(findCount == 0) { + Ui.printFindNothingMessage(); + } + Ui.printFindMessageEnd(); + } + + private static void processDelete(TaskList tasks, String input) { + int deletePos = input.indexOf("delete"); + if (tasks.list.size() != 0) { + if (input.length() < deletePos + 7) { + Ui.printDeleteButNotSpecificMessage(); //when input contains delete but no number + } else { + processValidDelete(tasks, input, deletePos); //when input contains delete and specified number + } + } else { + Ui.printDeleteButEmptyMessage(); //when input contains delete but list is empty + } + } + + private static void processValidDelete(TaskList tasks, String input, int deletePos) { + String itemNumDone = input.substring(deletePos + 7, deletePos + 8); + int itemNum = Integer.parseInt(itemNumDone); + Ui.printDeleteMessage(tasks.list, itemNum); + tasks.list.remove(itemNum - 1); + tasks.counter -= 1; + } + + private static void processList(TaskList tasks) { + if (tasks.list.size() != 0){ + Ui.printListMessage(tasks); + } else { + Ui.printListButEmptyMessage(); + } + } + + private static void processDone(TaskList tasks, String input) { + int donePos = input.indexOf("done"); + if (tasks.list.size() != 0) { + if (input.length() < donePos + 5) { + Ui.printDoneButNotSpecificMessage(); //when input contains done but no number + } else { + processValidDone(tasks, input, donePos); //when input contains done and specified number + } + } else { + Ui.printDoneButEmptyMessage(); //when input contains done but list is empty + } + } + + private static void processValidDone(TaskList tasks, String input, int donePos) { + String itemNumDone = input.substring(donePos + 5, donePos + 6); + int itemNum = Integer.parseInt(itemNumDone); + tasks.list.get(itemNum - 1).setDone(); + Ui.printDoneMessage(tasks.list, itemNum); + } + + private static int processTasks(TaskList tasks, String input) throws IllegalTaskException{ + if ( ( input.startsWith("deadline") || input.startsWith("event") ) && !input.contains("/")){ + throw new IllegalTaskException(); + } + if (input.startsWith("todo")) { + String description = input.substring(5); + ToDo newTask = new ToDo(description); + tasks.list.add(tasks.counter, newTask); + tasks.counter += 1; + Ui.printAddedTaskMessage(newTask, tasks.counter); + } else if (input.startsWith("deadline")) { + int donePos = input.indexOf("/"); //finds pos of '/' + String description = input.substring(9,donePos); + if (!input.substring(donePos + 1, donePos + 3).equals("by")) { + throw new IllegalTaskException(); + } + String date = input.substring(donePos + 4); + Deadline newTask = new Deadline(description,date); + tasks.list.add(tasks.counter, newTask); + tasks.counter += 1; + Ui.printAddedTaskMessage(newTask, tasks.counter); + } else if (input.startsWith("event")) { + int donePos = input.indexOf("/"); //finds pos of '/' + String description = input.substring(6,donePos); + if (!input.substring(donePos + 1, donePos + 3).equals("at")) { + throw new IllegalTaskException(); + } + String date = input.substring(donePos + 4); + Event newTask = new Event(description,date); + tasks.list.add(tasks.counter, newTask); + tasks.counter += 1; + Ui.printAddedTaskMessage(newTask, tasks.counter); + } else { + System.out.println("Please specify tasks: todo, deadline or event"); + System.out.println("Example - type in the following: todo read book"); + } + return tasks.counter; + } + +} diff --git a/src/main/java/duke/processes/Storage.java b/src/main/java/duke/processes/Storage.java new file mode 100644 index 000000000..3120d168f --- /dev/null +++ b/src/main/java/duke/processes/Storage.java @@ -0,0 +1,99 @@ +package duke.processes; + +import duke.tasks.Deadline; +import duke.tasks.Event; +import duke.tasks.ToDo; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Scanner; + +public class Storage { + + /** + * Loads pre-existing tasks found in DukeTaskData.txt. + * If there is no DukeTaskData.txt, a new file will be created. + * There is no output if tasks are loaded successfully. + * @param tasks is an instance of the public class TaskList, + * where the list of tasks and counter for number of tasks + * can be found and is updated based on user inputs. + */ + public static void loadTasks(TaskList tasks) { + + File DukeTaskData = new File("DukeTaskData.txt"); + Scanner scan; + String[] execute; + + System.out.println("Currently Loading List of Tasks"); + + try { + scan = new Scanner(DukeTaskData); + if (DukeTaskData.exists()) { + while (scan.hasNext()) { + execute = scan.nextLine().split("/"); + processLoading(tasks, execute); + } + } else { + FileWriter f = new FileWriter(DukeTaskData.getAbsoluteFile()); + f.close(); + } + } catch (IOException e) { + System.out.println("There is an issue with the data file."); + System.out.println("A new file will be created."); + System.out.println("No action is required. Cheers!"); + } finally { + System.out.println("--------------------"); + } + + } + + private static void processLoading(TaskList tasks, String[] execute) { + switch (execute[0]) { + case "T": + ToDo taskTodo = new ToDo(execute[2]); + tasks.list.add(taskTodo); + tasks.counter += 1; + break; + case "D": + Deadline taskDeadline = new Deadline(execute[2], execute[3]); + tasks.list.add(taskDeadline); + tasks.counter += 1; + break; + case "E": + Event taskEvent = new Event(execute[2], execute[3]); + tasks.list.add(taskEvent); + tasks.counter += 1; + break; + default: + System.out.println("Error With File"); + } + if (execute[1].equalsIgnoreCase("X")) { + tasks.list.get(tasks.list.size() - 1).setDone(); + } + } + + /** + * Saves existing tasks into DukeTaskData.txt. + * There is no output if tasks are saved successfully. + * @param tasks is an instance of the public class TaskList, + * where the list of tasks and counter for number of tasks + * can be found and is updated based on user inputs. + */ + public static void saveTasks(TaskList tasks) throws IOException { + + try { + FileWriter fw = new FileWriter("DukeTaskData.txt"); + for (int i = 0; i <= tasks.counter - 1; i += 1) { + fw.write(tasks.list.get(i).getLetter() + "/" + + tasks.list.get(i).getStatusIcon() + "/" + + tasks.list.get(i).getDescription() + "/" + + tasks.list.get(i).getDateOnly() + System.lineSeparator()); + } + fw.close(); + } catch (IOException e) { + System.out.println("There is an error" + e.getMessage()); + } + } + +} diff --git a/src/main/java/duke/processes/TaskList.java b/src/main/java/duke/processes/TaskList.java new file mode 100644 index 000000000..c8a58a6a3 --- /dev/null +++ b/src/main/java/duke/processes/TaskList.java @@ -0,0 +1,18 @@ +package duke.processes; + +import duke.tasks.Task; + +import java.util.ArrayList; + +/** + * When an instance of this class is created, other classes + * and methods can access the ArrayList and the counter. + * This ensures that the entire code updates and draws information + * from a single ArrayList and a single integer counter. + */ +public class TaskList { + + public ArrayList list = new ArrayList<>(); + public int counter = 0; + +} diff --git a/src/main/java/duke/processes/Ui.java b/src/main/java/duke/processes/Ui.java new file mode 100644 index 000000000..967bcc0d0 --- /dev/null +++ b/src/main/java/duke/processes/Ui.java @@ -0,0 +1,114 @@ +package duke.processes; + +import duke.tasks.Task; + +import java.util.ArrayList; + +public class Ui { + + public static void printWelcomeMessage() { + String logo = " ____ _ \n" + + "| _ \\ _ _| | _____ \n" + + "| | | | | | | |/ / _ \\\n" + + "| |_| | |_| | < __/\n" + + "|____/ \\__,_|_|\\_\\___|\n"; + System.out.println("Hello from\n" + logo); + + System.out.println("--------------------"); + System.out.println("Hello! I'm Duke"); + System.out.println("What can I do for you?"); + System.out.println(" "); + System.out.println("--------------------"); + } + + public static void printGoodbyeMessage() { + System.out.println("--------------------"); + System.out.println("Bye. Hope to see you again soon!"); + System.out.println(" "); + System.out.println("--------------------"); + } + + public static void printDoneMessage(ArrayList list, int itemNum) { + System.out.println("--------------------"); + System.out.println("Nice! I've marked this task as done:"); + System.out.println( itemNum + ".[" + list.get(itemNum - 1).getStatusIcon() + "] " + list.get(itemNum - 1).getDescription() ); + System.out.println("--------------------"); + } + + public static void printListMessage(TaskList tasks) { + System.out.println("--------------------"); + System.out.println("Here are the tasks in your list:"); + for(int i = 0; i < tasks.counter; i += 1){ + printListOfTaskSubMessage(tasks.list.get(i), i); + } + System.out.println("--------------------"); + } + + public static void printListButEmptyMessage() { + System.out.println("--------------------"); + System.out.println("List is empty. Time to get productive!"); + System.out.println("--------------------"); + } + + public static void printDoneButNotSpecificMessage() { + System.out.println("Please specify which task is done."); + } + + public static void printDoneButEmptyMessage() { + System.out.println("--------------------"); + System.out.println("Unable to tick off list."); + System.out.println("List is empty. Time to get productive!"); + System.out.println("--------------------"); + } + + public static void printAddedTaskMessage(Task task, int i) { + System.out.println("--------------------"); + System.out.println("Got it. I've added this task: "); + System.out.println("[" + task.getLetter() + "] " + + "[" + task.getStatusIcon() + "] " + + task.getDescription() + + task.getDate() ); + System.out.println("Now you have " + i + " tasks in the list."); + System.out.println("--------------------"); + } + + public static void printListOfTaskSubMessage(Task task, int i) { + System.out.println(i + 1 + + ".[" + task.getLetter() + "] " + + "[" + task.getStatusIcon() + "] " + + task.getDescription() + + task.getDate() ); + } + + public static void printDeleteMessage(ArrayList list, int itemNum) { + System.out.println("--------------------"); + System.out.println("Noted. I've removed this task:"); + System.out.println( itemNum + ".[" + list.get(itemNum - 1).getStatusIcon() + "] " + list.get(itemNum - 1).getDescription() ); + System.out.println("Now you have " + ( list.size() - 1 )+ " tasks in the list."); + System.out.println("--------------------"); + } + + public static void printDeleteButNotSpecificMessage() { + System.out.println("Please specify which task is to be deleted."); + } + + public static void printDeleteButEmptyMessage() { + System.out.println("--------------------"); + System.out.println("Unable to delete."); + System.out.println("List is empty. Time to get productive!"); + System.out.println("--------------------"); + } + + public static void printFindMessageStart() { + System.out.println("--------------------"); + System.out.println("Here are the matching tasks in your list:"); + } + + public static void printFindMessageEnd() { + System.out.println("--------------------"); + } + + public static void printFindNothingMessage() { + System.out.println("(there are 0 matches)"); + } +} diff --git a/src/main/java/duke/tasks/Deadline.java b/src/main/java/duke/tasks/Deadline.java new file mode 100644 index 000000000..f4106c68b --- /dev/null +++ b/src/main/java/duke/tasks/Deadline.java @@ -0,0 +1,27 @@ +package duke.tasks; + +public class Deadline extends Task{ + + protected final static char LETTER = 'D'; + protected String date; + + public Deadline(String description, String date) { + super(description); + this.date = date; + } + + public String toString() { + return description; + } + + public char getLetter() { + return LETTER; + } + + public String getDate() { + return "(by: " + date + ")"; + } + + public String getDateOnly() { return date; } + +} diff --git a/src/main/java/duke/tasks/Event.java b/src/main/java/duke/tasks/Event.java new file mode 100644 index 000000000..f4def539d --- /dev/null +++ b/src/main/java/duke/tasks/Event.java @@ -0,0 +1,26 @@ +package duke.tasks; + +public class Event extends Task{ + + protected final static char LETTER = 'E'; + protected String date; + + public Event(String description, String date) { + super(description); + this.date = date; + } + + public String toString() { + return description; + } + + public char getLetter() { + return LETTER; + } + + public String getDate() { + return "(at: " + date + ")"; + } + + public String getDateOnly() { return date; } +} diff --git a/src/main/java/duke/tasks/Task.java b/src/main/java/duke/tasks/Task.java new file mode 100644 index 000000000..83d280f4a --- /dev/null +++ b/src/main/java/duke/tasks/Task.java @@ -0,0 +1,41 @@ +package duke.tasks; + +public class Task { + protected String description; + protected boolean isDone; + + public Task(){ + this.description = ""; + isDone = false; + } + public Task(String description) { + this.description = description; + this.isDone = false; + } + + public String getStatusIcon() { + return (isDone ? "X" : " "); // mark done task with X + } + + public String getDescription(){ + return this.description; + } + + public void setDone(){ + this.isDone = true; + } + + public void setDescription(String description) { + this.description = description; + } + + public char getLetter() { + return ' '; + } + + public String getDate() { + return " "; + } + + public String getDateOnly() { return " "; } +} diff --git a/src/main/java/duke/tasks/ToDo.java b/src/main/java/duke/tasks/ToDo.java new file mode 100644 index 000000000..a37406b77 --- /dev/null +++ b/src/main/java/duke/tasks/ToDo.java @@ -0,0 +1,23 @@ +package duke.tasks; + +public class ToDo extends Task{ + + protected final static char LETTER = 'T'; + + public ToDo(String description) { + super(description); + } + + public String toString() { + return description; + } + + public char getLetter() { + return LETTER; + } + + public String getDate() { + return ""; + } + +}