diff --git a/.gitignore b/.gitignore index f69985ef1..76264e19c 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ bin/ /text-ui-test/ACTUAL.txt text-ui-test/EXPECTED-UNIX.TXT +data/ diff --git a/README.md b/README.md index 8715d4d91..0dc173b2e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Duke project template +# duke.command.Duke project template This is a project template for a greenfield Java project. It's named after the Java mascot _Duke_. Given below are instructions on how to use it. @@ -13,7 +13,7 @@ Prerequisites: JDK 11, update Intellij to the most recent version. 1. If there are any further prompts, accept the defaults. 1. Configure the project to use **JDK 11** (not other versions) as explained in [here](https://www.jetbrains.com/help/idea/sdk.html#set-up-jdk).
In the same dialog, set the **Project language level** field to the `SDK default` option. -3. After that, locate the `src/main/java/Duke.java` file, right-click it, and choose `Run Duke.main()` (if the code editor is showing compile errors, try restarting the IDE). If the setup is correct, you should see something like the below as the output: +3. After that, locate the `src/main/java/duke.command.Duke.java` file, right-click it, and choose `Run duke.command.Duke.main()` (if the code editor is showing compile errors, try restarting the IDE). If the setup is correct, you should see something like the below as the output: ``` Hello from ____ _ diff --git a/docs/README.md b/docs/README.md index 8077118eb..837626489 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2,28 +2,136 @@ ## Features -### Feature-ABC +### Listing +List out all tasks in the tasklist. -Description of the feature. +### Add/Delete tasks +Add different type of tasks into the tasklist. +Delete a task anytime. -### Feature-XYZ +### Find +Find a task using keywords. -Description of the feature. +### Mark done +Mark a task as done. + +### Auto-saving +Tasks in the tasklist are saved automatically when program exits, ready to be loaded next time. ## Usage -### `Keyword` - Describe action +### `list` +List out all tasks in the tasklist. + +Example of usage: + +`list` + +Expected outcome: + +``` +1.[T][X] do wct +2.[E][ ] finish inquiry(at: 6pm) +``` + +### `done` +Mark a task as done. + +Example of usage: + +`done 2` + +Expected outcome: + +``` +Nice! I've marked this task as done: + [X] finish inquiry +``` + +### `todo` +Add a "todo" type task. + +Example of usage: + +`todo do cs2113 ip` + +Expected outcome: + +``` +Got it. I've added this task: + [T][ ] do cs2113 ip +Now you have 3 tasks in the list +``` + +### `deadline` +Add a "deadline" type task. + +Example of usage: + +`deadline cs2113 ip /by tonight` -Describe the action and its outcome. +Expected outcome: + +``` +Got it. I've added this task: + [D][ ] cs2113 ip(by: tonight) +Now you have 5 tasks in the list +``` -Example of usage: +### `event` +Add a "event" type task. -`keyword (optional arguments)` +Example of usage: + +`event go to party /at tonight` Expected outcome: -Description of the outcome. +``` +Got it. I've added this task: + [E][ ] go to party(at: tonight) +Now you have 6 tasks in the list +``` + +### `find` +Find a task in the tasklist that contains the keyword. + +Example of usage: +`find cs2113` + +Expected outcome: + +``` +Here are the matching tasks in your list: +1.[T][ ] do cs2113 ip +2.[D][ ] cs2113 ip(by: tonight) ``` -expected output + +### `delete` +Delete a task in the task list using the index. + +Example of usage: + +`delete 1` + +Expected outcome: + +``` +Noted. I've removed this task: + [T][X] do wct +Now you have 4 tasks in the list +``` + +### `bye` +Terminates the program, saves all changes. + +Example of usage: + +`bye` + +Expected outcome: + ``` +Bye. Hope to see you again soon! +``` \ No newline at end of file diff --git a/docs/_config.yml b/docs/_config.yml new file mode 100644 index 000000000..c4192631f --- /dev/null +++ b/docs/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-cayman \ No newline at end of file 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/META-INF/MANIFEST.MF b/src/main/java/META-INF/MANIFEST.MF new file mode 100644 index 000000000..d749a8d57 --- /dev/null +++ b/src/main/java/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: duke.command.Duke + diff --git a/src/main/java/duke/command/Duke.java b/src/main/java/duke/command/Duke.java new file mode 100644 index 000000000..e38170bb8 --- /dev/null +++ b/src/main/java/duke/command/Duke.java @@ -0,0 +1,46 @@ +package duke.command; +import java.util.Scanner; + +public class Duke { + private static Parser parser = new Parser(); + private static Ui ui = new Ui(); + private static TaskList taskList = new TaskList(); + private static Storage s = new Storage(); + + public static void running() { + s.loadFromFile(taskList); + String line; + Scanner in = new Scanner(System.in); + while (true) { + line = in.nextLine(); + + if (line.equals("list")) { + taskList.listOut(); + } else if (line.startsWith("find")) { + taskList.find(line); + } else if (line.startsWith("done")) { + taskList.markDone(line); + } else if (line.startsWith("delete")) { + int index = parser.getIndexForDelete(line); + taskList.deleteTask(index); + } else if (line.startsWith("todo")) { + taskList.addTodo(line); + } else if (line.startsWith("deadline")) { + taskList.addDeadline(line); + } else if (line.startsWith("event")) { + taskList.addEvent(line); + } else if (line.equals("bye")) { + s.saveToFile("data/data.txt", taskList); + return; + } else { + System.out.println("☹ OOPS!!! I'm sorry, but I don't know what that means :-("); + } + } + } + + public static void main(String[] args) { + ui.sayHi(); + running(); + ui.sayBye(); + } +} diff --git a/src/main/java/duke/command/Parser.java b/src/main/java/duke/command/Parser.java new file mode 100644 index 000000000..79487c463 --- /dev/null +++ b/src/main/java/duke/command/Parser.java @@ -0,0 +1,45 @@ +package duke.command; + +public class Parser { + public static Integer getIndexForDelete(String line) { + return Integer.parseInt(line.substring(7)) - 1; + } + + public static Integer getIndexForDone(String line) { + return Integer.parseInt(line.substring(5)) - 1; + } + + /** + * Returns the index of the word "/by" in the array of strings generated from the input line. + * @param line The whole line of input from the command line. + * @return Index position of the the word "/by" + */ + public static Integer getIndexForDeadline(String line) { + String[] words = line.split(" "); + int index = 0; + for (int i = 0; i < words.length; i++) { + if (words[i].equals("/by")) { + index = i; + break; + } + } + return index; + } + + /** + * Returns the index of the word "/at" in the array of strings generated from the input line. + * @param line The whole line of input from the command line. + * @return Index position of the the word "/at" + */ + public static Integer getIndexForEvent(String line) { + String[] words = line.split(" "); + int index = 0; + for (int i = 0; i < words.length; i++) { + if (words[i].equals("/at")) { + index = i; + break; + } + } + return index; + } +} diff --git a/src/main/java/duke/command/Storage.java b/src/main/java/duke/command/Storage.java new file mode 100644 index 000000000..8a893f039 --- /dev/null +++ b/src/main/java/duke/command/Storage.java @@ -0,0 +1,108 @@ +package duke.command; + +import duke.task.Deadline; +import duke.task.Event; +import duke.task.Task; +import duke.task.ToDo; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Scanner; + +/** + * Represents a storage space for a TaskList object. + */ +public class Storage { + protected File directory; + protected File file; + + public Storage() { + + directory = new File("data"); + if (!directory.exists()) { + directory.mkdirs(); + } + //create txt file if file does not exist + file = new File("data/data.txt"); + if(!file.exists()) { + try { + file.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + /** + * Checks the text file set earlier for storage, + * loads the tasks into the input TaskList object. + * + * @param taskList The list of tasks initialized without any content yet. + * @throws IOException If an error occurs from reading or writing to the text file. + */ + public void loadFromFile(TaskList taskList) { + int taskNumber = 0; + try { + File f = new File("data/data.txt"); + if (!f.createNewFile()) { + Scanner s = new Scanner(f); + while (s.hasNext()) { + String task = s.nextLine(); + String[] words = task.split(" \\| "); + switch (words[0]) { + case "T": + taskList.tasks[taskNumber] = new ToDo(Integer.parseInt(words[1]), words[2]); + taskNumber++; + break; + case "D": + taskList.tasks[taskNumber] = new Deadline(Integer.parseInt(words[1]), words[2], words[3]); + taskNumber++; + break; + case "E": + taskList.tasks[taskNumber] = new Event(Integer.parseInt(words[1]), words[2], words[3]); + taskNumber++; + break; + } + } + } + taskList.taskNumber = taskNumber; + return; + } catch (IOException e) { + System.out.println("An error occurred."); + e.printStackTrace(); + return; + } + } + + /** + * Saves the current tasks in a TaskList object + * into a text file when the program terminates. + * + * @param filePath Path of the text file used to store the list of tasks. + * @param taskList The list of tasks. + * @throws IOException If an error occurs from reading or writing to the text file. + */ + public static void saveToFile(String filePath, TaskList taskList) { + try { + FileWriter fw = new FileWriter(filePath); + for (int i = 0; i < taskList.taskNumber; i++) { + switch (taskList.tasks[i].getType()) { + case "todo": + fw.write("T" + " | " + taskList.tasks[i].getStatusNumber() + " | " + taskList.tasks[i].getDescription() + System.lineSeparator()); + break; + case "deadline": + fw.write("D" + " | " + taskList.tasks[i].getStatusNumber() + " | " + taskList.tasks[i].getDescription() + " | " + taskList.tasks[i].getTime() + System.lineSeparator()); + break; + case "event": + fw.write("E" + " | " + taskList.tasks[i].getStatusNumber() + " | " + taskList.tasks[i].getDescription() + " | " + taskList.tasks[i].getTime() + System.lineSeparator()); + break; + } + } + fw.close(); + } catch (IOException e) { + System.out.println("Something went wrong: " + e.getMessage()); + } + } + +} diff --git a/src/main/java/duke/command/TaskList.java b/src/main/java/duke/command/TaskList.java new file mode 100644 index 000000000..246b45796 --- /dev/null +++ b/src/main/java/duke/command/TaskList.java @@ -0,0 +1,160 @@ +package duke.command; + +import duke.task.Deadline; +import duke.task.Event; +import duke.task.Task; +import duke.task.ToDo; + +/** + * Represents a list of all tasks added. + */ +public class TaskList { + protected Task[] tasks; + protected int taskNumber; + + public TaskList() { + tasks = new Task[100]; + taskNumber = 0; + } + + + /** + * Prints out all the tasks in a TaskList object. + */ + + public void listOut() { + for (int i = 0; i < taskNumber; i++) { + System.out.println((i + 1) + "." + tasks[i]); + } + } + + + /** + * Marks a particular task done, from index of the task + * inputted from the command lind. + * + * @param line The whole line of input from the command line. + * @throws StringIndexOutOfBoundsException If there is no index contained in the line. + */ + + public void find(String line) { + Ui ui = new Ui(); + try { + int j = 1; + String keyword = line.substring(5); + ui.find(); + for (int i = 0; i < taskNumber; i++) { + if (tasks[i].getDescription().contains(keyword)) { + System.out.println(j + "." + tasks[i]); + j++; + } + } + } catch (StringIndexOutOfBoundsException e) { + ui.findFailed(); + } + } + + + public void markDone(String line) { + Ui ui = new Ui(); + Parser parser = new Parser(); + try { + int index = parser.getIndexForDone(line); + tasks[index].setDone(); + ui.markDone(index, tasks); + } catch (StringIndexOutOfBoundsException e) { + ui.markDoneFailed(); + } + } + + /** + * Delete a particular task from a TaskList object. + * @param index Index of the task to be deleted + * @throws StringIndexOutOfBoundsException If there is no index contained in the line. + */ + public void deleteTask(int index) { + Ui ui = new Ui(); + try { + ui.deleteTask(index, tasks, taskNumber); + for (int i = index; i < taskNumber - 1; i++) { + tasks[i] = tasks[i + 1]; + } + taskNumber--; + } catch (StringIndexOutOfBoundsException e) { + ui.deleteTaskFailed(); + } + } + + /** + * Add a "todo" type of task into a TaskList object. + * @param line The whole line of input from the command line. + * @throws StringIndexOutOfBoundsException If there is no argument after the "todo" command + */ + public void addTodo(String line) { + Ui ui = new Ui(); + try { + tasks[taskNumber] = new ToDo(line.substring(5)); + taskNumber++; + ui.addTask(tasks, taskNumber); + } catch (StringIndexOutOfBoundsException e) { + ui.addTodoFailed(); + } + } + + /** + * Add a "deadline" type of task into a TaskList object. + * @param line The whole line of input from the command line. + * @throws StringIndexOutOfBoundsException If there is no argument after the "deadline" command + */ + public void addDeadline(String line) { + Ui ui = new Ui(); + Parser parser = new Parser(); + String[] words = line.split(" "); + int index = parser.getIndexForDeadline(line); + if (index == 0) { + ui.addDeadlineFailed(); + } + String deadlineDescription = ""; + String by = ""; + for (int i = 1; i < index; i++) { + deadlineDescription = deadlineDescription + words[i] + " "; + } + deadlineDescription = deadlineDescription.substring(0, deadlineDescription.length() - 1); + for (int i = index + 1; i < words.length; i++) { + by = by + words[i] + " "; + } + by = by.substring(0, by.length() - 1); + tasks[taskNumber] = new Deadline(deadlineDescription, by); + taskNumber++; + ui.addTask(tasks, taskNumber); + } + + /** + * Add a "event" type of task into a TaskList object. + * @param line The whole line of input from the command line. + * @throws StringIndexOutOfBoundsException If there is no argument after the "event" command + */ + + public void addEvent(String line) { + Ui ui = new Ui(); + Parser parser = new Parser(); + String[] words = line.split(" "); + int index = parser.getIndexForEvent(line); + if (index == 0) { + ui.addEventFailed(); + } + String eventDescription = ""; + String at = ""; + for (int i = 1; i < index; i++) { + eventDescription = eventDescription + words[i] + " "; + } + eventDescription = eventDescription.substring(0, eventDescription.length() - 1); + for (int i = index + 1; i < words.length; i++) { + at = at + words[i] + " "; + } + at = at.substring(0, at.length() - 1); + tasks[taskNumber] = new Event(eventDescription, at); + taskNumber++; + ui.addTask(tasks, taskNumber); + } +} diff --git a/src/main/java/duke/command/Ui.java b/src/main/java/duke/command/Ui.java new file mode 100644 index 000000000..51ad57dbb --- /dev/null +++ b/src/main/java/duke/command/Ui.java @@ -0,0 +1,59 @@ +package duke.command; + +import duke.task.Task; + +public class Ui { + public static void sayHi() { + System.out.println("Hello I'm Duke"); + System.out.println("What can I do for you?"); + } + + public static void find() { + System.out.println("Here are the matching tasks in your list:"); + } + + public static void findFailed() { + System.out.println("OOPS!!! The description of a find cannot be empty."); + } + + public static void markDone(int index, Task[] tasks) { + System.out.println("Nice! I've marked this task as done:"); + System.out.println(" " + tasks[index].getStatusIcon() + " " + tasks[index].getDescription()); + } + + public static void markDoneFailed() { + System.out.println("OOPS!!! The description of a mark-done cannot be empty."); + } + + public static void deleteTask(int index, Task[] tasks, int taskNumber) { + System.out.println("Noted. I've removed this task:"); + System.out.println(" " + tasks[index]); + System.out.println("Now you have " + (taskNumber - 1) + " tasks in the list"); + } + + public static void deleteTaskFailed() { + System.out.println("OOPS!!! The description of a deletion cannot be empty."); + } + + public static void addTask(Task[] tasks, int taskNumber) { + System.out.println("Got it. I've added this task:"); + System.out.println(" " + tasks[taskNumber - 1]); + System.out.println("Now you have " + taskNumber + " tasks in the list"); + } + + public static void addTodoFailed() { + System.out.println("OOPS!!! The description of a todo cannot be empty."); + } + + public static void addDeadlineFailed() { + System.out.println("OOPS!!! The description of a deadline cannot be empty."); + } + + public static void addEventFailed() { + System.out.println("OOPS!!! The description of an event cannot be empty."); + } + + public static void sayBye() { + System.out.println("Bye. Hope to see you again soon!"); + } +} diff --git a/src/main/java/duke/task/Deadline.java b/src/main/java/duke/task/Deadline.java new file mode 100644 index 000000000..713d9b31b --- /dev/null +++ b/src/main/java/duke/task/Deadline.java @@ -0,0 +1,35 @@ +package duke.task; + +/** + * Represents an deadline that has a description, a status inherited from Task class, + * and an extra at attribute indicating the deadline of the task. + */ +public class Deadline extends Task { + + protected String by; + + public Deadline(String description, String by) { + super(description); + this.by = by; + } + + public Deadline(int status, String description, String by) { + super(status, description); + this.by = by; + } + + @Override + public String getType() { + return "deadline"; + } + + @Override + public String getTime() { + return this.by; + } + + @Override + public String toString() { + return "[D]" + super.toString() + "(by: " + by + ")"; + } +} diff --git a/src/main/java/duke/task/Event.java b/src/main/java/duke/task/Event.java new file mode 100644 index 000000000..2e603484c --- /dev/null +++ b/src/main/java/duke/task/Event.java @@ -0,0 +1,38 @@ +package duke.task; + +/** + * Represents an event that has a description, a status inherited from Task class, + * and an extra at attribute indicating the time of the task. + */ +public class Event extends Task { + protected String at; + + public Event(String description, String at) { + super(description); + this.at = at; + } + + public Event(int status, String description, String at) { + super(status, description); + this.at = at; + } + + @Override + public String getType() { + return "event"; + } + + @Override + public String getStatusNumber() { + return super.getStatusNumber(); + } + + public String getTime() { + return this.at; + } + + @Override + public String toString() { + return "[E]" + super.toString() + "(at: " + at + ")"; + } +} diff --git a/src/main/java/duke/task/Task.java b/src/main/java/duke/task/Task.java new file mode 100644 index 000000000..47d7388e1 --- /dev/null +++ b/src/main/java/duke/task/Task.java @@ -0,0 +1,53 @@ +package duke.task; + +/** + * Represent a general task that might be a "todo", a "deadline", or a "event". + * The task has a description attribute, and a status attribute indicating whether it is done. + */ +public class Task { + protected String description; + protected boolean isDone; + + public Task(String description) { + this.description = description; + this.isDone = false; + } + + public Task(int status, String description) { + this.description = description; + if (status == 1) { + this.isDone = true; + } else { + this.isDone = false; + } + } + + public void setDone() { + this.isDone = true; + } + + public String getDescription() { + return this.description; + } + + public String getStatusIcon() { + return (isDone ? "[X]" : "[ ]"); // mark done task with X + } + + public String getStatusNumber() { + return (isDone ? "1" : "0"); // mark done task with 1 + } + + public String getTime() { + return ""; + }; + + public String getType() { + return ""; + } + + @Override + public String toString() { + return this.getStatusIcon() + " " + this.getDescription(); + } +} diff --git a/src/main/java/duke/task/ToDo.java b/src/main/java/duke/task/ToDo.java new file mode 100644 index 000000000..d66c113d3 --- /dev/null +++ b/src/main/java/duke/task/ToDo.java @@ -0,0 +1,17 @@ +package duke.task; +/** + * Represents an todo that has a description, a status inherited from Task class. + */ +public class ToDo extends Task{ + public ToDo(String description) { + super(description); + } + public ToDo(int status, String description) { super(status, description);} + public String getType() { + return "todo"; + } + @Override + public String toString() { + return "[T]" + super.toString(); + } +} diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 657e74f6e..5c6addc53 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,7 +1,17 @@ -Hello from - ____ _ -| _ \ _ _| | _____ -| | | | | | | |/ / _ \ -| |_| | |_| | < __/ -|____/ \__,_|_|\_\___| - +Hello I'm duke.command.Duke +What can I do for you? +Got it. I've added this task: + [T][ ] do wct +Now you have 1 tasks in the list +Got it. I've added this task: + [D][ ] inquiry (by: now ) +Now you have 2 tasks in the list +Got it. I've added this task: + [E][ ] wct (at: tomorrow 2pm ) +Now you have 3 tasks in the list +Nice! I've marked this task as done: + [X] do wct +1.[T][X] do wct +2.[D][ ] inquiry (by: now ) +3.[E][ ] wct (at: tomorrow 2pm ) +Bye. Hope to see you again soon! diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index e69de29bb..d1a747a5c 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -0,0 +1,6 @@ +todo do wct +deadline inquiry /by now +event wct /at tomorrow 2pm +done 1 +list +bye