diff --git a/.classpath b/.classpath new file mode 100644 index 000000000..b6cbb3c39 --- /dev/null +++ b/.classpath @@ -0,0 +1,6 @@ + + + + + + diff --git a/.project b/.project new file mode 100644 index 000000000..5bfb5bc84 --- /dev/null +++ b/.project @@ -0,0 +1,17 @@ + + + ip + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/data/save.txt b/data/save.txt new file mode 100644 index 000000000..fd7182a7d --- /dev/null +++ b/data/save.txt @@ -0,0 +1,2 @@ +D|1|write book|1999/03/12 1200 +T|0|read book diff --git a/docs/README.md b/docs/README.md index 8077118eb..c6b83c7f6 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,29 +1,211 @@ # User Guide -## Features -### Feature-ABC + |___ | | | |_____| / / -- \ \ + / / | | / / | | | | + / / | | / / | | | | + / /___ | | /_/__ | | -- | | + |_____| | | |_____| \ \____/ / -Description of the feature. +Zizo is a desktop app for managing tasks, optimized for use via a Command Line Interface (CLI). +If you can type fast, Zizo can get your task management done faster than traditional GUI apps. +## Features -### Feature-XYZ +### Add Todo -Description of the feature. +Adds a **Todo** task -## Usage +### Add Event + +Adds an **Event** task + +### Add Deadline + +Adds a **Deadline** task + +### List + +Show all the tasks in a ordered list +### Done + +Mark a specific task as completed +### Delete + +Remove a specific task from the tasks list +### Show Date + +Show all tasks with the given deadline +### Find + +Search for a tasks given a keyword +### Terminate -### `Keyword` - Describe action +Ends the program -Describe the action and its outcome. +## Usage + +### `todo` - Adds a todo task to the tasks list Example of usage: -`keyword (optional arguments)` +`todo Read a book` + + +###Expected output +``` +____________________________________________________________ + +Got it. I've added this task: +[T][ ]Read a book +Now you have 1 tasks in the list. +____________________________________________________________ +``` +### `deadline` - Adds a deadline task to the tasks list + + +Example of usage: + +`deadline Read a book /by 2021/09/25 1800` + + +###Expected output +``` +____________________________________________________________ + +Got it. I've added this task: +[D][ ]Read a book (by: 25 Sep 2021, 6 00 PM) +Now you have 1 tasks in the list. +____________________________________________________________ +``` +### `event` - Adds a event task to the tasks list + + +Example of usage: + +`event Read a book /at 2021/09/25 1800` + +###Expected output +``` +____________________________________________________________ + +Got it. I've added this task: +[E][ ]Read a book (by: 25 Sep 2021, 6 00 PM) +Now you have 1 tasks in the list. +____________________________________________________________ +``` +### `list` - Shows a list of tasks + + +Example of usage: + +`list` + + +###Expected output +``` +____________________________________________________________ + +Here are the task in your list: +1.[D][ ]Read a book (by: 25 Sep 2021, 6 00 PM) +____________________________________________________________ +``` +### `done` - Mark a task as done + +Selects which task base on its index in the list to mark as done + +Format: done (Task index) +* Index must be a positive integer + +Example of usage: + +`done 1` Expected outcome: +``` + Here are the task in your list: +1. [D][X]Read a book (by: 25 Sep 2021, 6 00 PM) +``` + +###Expected output +``` +____________________________________________________________ + +Nice! task is done +____________________________________________________________ +``` + +### `delete` - Delete task + +Selects which task to delete based on its index in the tasks list + +Format: delete (Task Index) +* Index must be a positive integer + + +Example of usage: + +`delete 1` + + +###Expected output +``` +____________________________________________________________ +Noted. I've removed this task: +[D][X]Read a book (by: 25 Sep 2021, 6 00 PM) +Now you have 0 tasks in the list. +____________________________________________________________ +``` + +### `show date` Show tasks based on date + +Show tasks with the specific deadline + +Example of usage: + +`show date (date in yyyy/MM/dd format)` + +`show date 2012/12/12` + + +###Expected output +``` +____________________________________________________________ +Here are the all the task in your list to be done by:13 Mar 1999 +[E][X] collect book (at: 13 Mar 1999, 01 00 PM) +____________________________________________________________ +``` + +### `find` - Search for a task + +Show tasks with the specific deadline + +Example of usage: + +`find (keyword of task)` + +`find book` + +###Expected output +``` +____________________________________________________________ +Here are the all the task in your list with keyword: book +1.[D][ ] write book (by: 12 Mar 1999, 12 00 PM) +2.[T][ ] read book + +____________________________________________________________ +____________________________________________ +``` +### `Bye` - Ends the program + +End the program + +Example of usage: -Description of the outcome. +`bye` +###Expected output ``` -expected output +____________________________________________________________ +chat again next time! +____________________________________________________________ ``` 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..6e864153e --- /dev/null +++ b/src/main/java/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: duke.Duke + diff --git a/src/main/java/duke/Duke.java b/src/main/java/duke/Duke.java new file mode 100644 index 000000000..217c7bea9 --- /dev/null +++ b/src/main/java/duke/Duke.java @@ -0,0 +1,63 @@ +package duke; + +import duke.command.Command; +import duke.exception.DukeException; +import duke.parser.Parser; +import duke.storage.Storage; +import duke.tasklist.TaskList; +import duke.ui.Ui; +import java.io.FileNotFoundException; +import java.util.Locale; +import java.util.Scanner; + + +public class Duke { + private static final String fileDir = "./data"; + private static final String fileName = "./data/save.txt"; + private final Storage storage; + private TaskList task; + private final Ui ui; + + + /** + * Initialise all classes and load save file if it exists otherwise create a new save file + * + * @param fileName The name of the save file + * @param fileDir The directory of the save file + */ + public Duke(String fileName, String fileDir) { + storage = new Storage(fileName, fileDir); + ui = new Ui(); + try { + task = new TaskList(storage.items); + storage.load(task); + } catch (FileNotFoundException e) { + ui.showSaveFileError(); + storage.create(); + task = new TaskList(storage.items); + } + } + private void run() { + Scanner in = new Scanner(System.in); + boolean isExit = false; + while (!isExit) { + try { + String fullCommand = ui.readCommand(in).toLowerCase(Locale.ROOT); + Command c = Parser.parse(fullCommand); + boolean hasError = Parser.verifyCommand(task, c, ui); + if (hasError) { + throw new DukeException(); + } + c.execute(task, ui, storage); + isExit = c.isExit(); + } catch (DukeException e) { + ui.showError(); + } + } + storage.save(); + ui.printEndMessage(); + } + public static void main(String[] args) { + new Duke(fileName, fileDir).run(); + } +} diff --git a/src/main/java/duke/command/AddCommand.java b/src/main/java/duke/command/AddCommand.java new file mode 100644 index 000000000..efc3aaa5e --- /dev/null +++ b/src/main/java/duke/command/AddCommand.java @@ -0,0 +1,4 @@ +package duke.command; + +public class AddCommand { +} diff --git a/src/main/java/duke/command/Command.java b/src/main/java/duke/command/Command.java new file mode 100644 index 000000000..065fcaeeb --- /dev/null +++ b/src/main/java/duke/command/Command.java @@ -0,0 +1,145 @@ +package duke.command; +import duke.storage.Storage; +import duke.tasklist.TaskList; +import duke.tasklist.task.Task; +import duke.ui.Ui; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; + +public class Command { + private static final int CMD_NOT_FOUND = 0; + private static final int CMD_TODO = 1; + private static final int CMD_EVENT = 2; + private static final int CMD_DEADLINE = 3; + private static final int CMD_LIST = 4; + private static final int CMD_DONE = 5; + private static final int CMD_DELETE = 6; + private static final int CMD_SHOW_DATE = 7; + private static final int CMD_FIND = 8; + private static final int CMD_TERMINATE = 0; + private static final String TODO = "todo"; + private static final String EVENT = "event"; + private static final String DEADLINE = "deadline"; + private static final int INDEX_NUM_DONE = 5; + private static final int INDEX_NUM_DELETE = 7; + private static final String BY = "/by"; + private static final String AT = "/at"; + private static final String SHOW_DATE = "show date"; + private static final String FIND = "find"; + + protected int command; + protected boolean isExit = false; + protected String userInput; + + public Command() { + this.command = CMD_NOT_FOUND; + } + public void setCommand(int command) { + this.command = command; + } + public void setUserInput(String userInput) { + this.userInput = userInput; + + } + public String getUserInput() { + return this.userInput; + } + public int getCommand() { + return this.command; + } + /** + * Executes the command based on the command type + * + * @param tasks An object that contains the list of tasks + * @param storage An object to allow saving and loading of the list of tasks + * @param ui An object to interacts with the user + */ + public void execute(TaskList tasks, Ui ui, Storage storage) { + int taskCount = tasks.getTaskCount(); + String[] userInputs; + switch (command) { + case CMD_TODO: + tasks.addTodo(storage.items, userInput.replace(TODO, "").trim()); + tasks.loadTaskCount(storage.items); + ui.addTaskMessage(tasks, storage.items.get(taskCount)); + break; + case CMD_EVENT: + userInputs = userInput.split(AT); + tasks.addEvent(storage.items, userInputs[0].trim().replace(EVENT, "").trim(), userInputs[1].trim()); + tasks.loadTaskCount(storage.items); + ui.addTaskMessage(tasks, storage.items.get(taskCount)); + break; + case CMD_DEADLINE: + userInputs = userInput.split(BY); + tasks.addDeadline(storage.items, userInputs[0].trim().replace(DEADLINE, "").trim(), userInputs[1].trim()); + tasks.loadTaskCount(storage.items); + ui.addTaskMessage(tasks, storage.items.get(taskCount)); + break; + case CMD_LIST: + int j = 1; + System.out.println(Ui.border); + System.out.println("Here are the task in your list:"); + for (Task item : storage.items) { + if (item != null) { + System.out.print(j + "."); + System.out.println(item); + j++; + } + } + break; + case CMD_SHOW_DATE: + int index = 1; + LocalDate deadline = LocalDate.parse(userInput.replace(SHOW_DATE, "").trim(), DateTimeFormatter.ofPattern("yyyy/MM/dd")); + System.out.println(Ui.border); + System.out.println("Here are the all the task in your list to be done by:" + deadline.format(DateTimeFormatter.ofPattern("dd MMM yyyy"))); + for (Task item : storage.items) { + if (!(item.getDate().contains("empty")) && item.getDeadline().isEqual(deadline)) { + System.out.print(index + "."); + System.out.println(item); + index++; + } + } + break; + case CMD_FIND: + index = 1; + String key = userInput.replace(FIND, "").trim(); + System.out.println(Ui.border); + System.out.println("Here are the all the task in your list with keyword: " + key); + for (Task item : storage.items) { + if (item.getDescription().contains(key)) { + System.out.print(index + "."); + System.out.println(item); + index++; + } + } + break; + case CMD_DONE: + int dividerPosition = userInput.indexOf(" ") + 1; + int endPosition = userInput.length(); + if (endPosition > INDEX_NUM_DONE) { + String num = userInput.substring(dividerPosition, endPosition); + int taskNum = Integer.parseInt(num) - 1; + storage.items.get(taskNum).markDone(); + ui.showLine(); + System.out.println("Nice! task is done " + '\n'); + ui.showLine(); + } + break; + case CMD_DELETE: + dividerPosition = userInput.indexOf(" ") + 1; + endPosition = userInput.length(); + if (endPosition > INDEX_NUM_DELETE) { + String num = userInput.substring(dividerPosition, endPosition); + int taskNum = Integer.parseInt(num) - 1; + tasks.removeItem(ui, storage.items, taskNum); + } + break; + case CMD_TERMINATE: + isExit = true; + break; + } + } + public boolean isExit() { + return this.isExit; + } +} diff --git a/src/main/java/duke/command/DeleteCommand.java b/src/main/java/duke/command/DeleteCommand.java new file mode 100644 index 000000000..38b5d9e01 --- /dev/null +++ b/src/main/java/duke/command/DeleteCommand.java @@ -0,0 +1,4 @@ +package duke.command; + +public class DeleteCommand { +} diff --git a/src/main/java/duke/command/FindCommand.java b/src/main/java/duke/command/FindCommand.java new file mode 100644 index 000000000..e45b2d3a6 --- /dev/null +++ b/src/main/java/duke/command/FindCommand.java @@ -0,0 +1,4 @@ +package duke.command; + +public class FindCommand { +} diff --git a/src/main/java/duke/command/ListCommand.java b/src/main/java/duke/command/ListCommand.java new file mode 100644 index 000000000..74e12fc53 --- /dev/null +++ b/src/main/java/duke/command/ListCommand.java @@ -0,0 +1,4 @@ +package duke.command; + +public class ListCommand { +} diff --git a/src/main/java/duke/command/ShowDateCommand.java b/src/main/java/duke/command/ShowDateCommand.java new file mode 100644 index 000000000..acc9bd6ec --- /dev/null +++ b/src/main/java/duke/command/ShowDateCommand.java @@ -0,0 +1,4 @@ +package duke.command; + +public class ShowDateCommand { +} diff --git a/src/main/java/duke/exception/CommandDoesNotExistException.java b/src/main/java/duke/exception/CommandDoesNotExistException.java new file mode 100644 index 000000000..c3c6f33a0 --- /dev/null +++ b/src/main/java/duke/exception/CommandDoesNotExistException.java @@ -0,0 +1,4 @@ +package duke.exception; + +public class CommandDoesNotExistException extends Exception { +} diff --git a/src/main/java/duke/exception/CommandInvalidException.java b/src/main/java/duke/exception/CommandInvalidException.java new file mode 100644 index 000000000..e6cb9ee3e --- /dev/null +++ b/src/main/java/duke/exception/CommandInvalidException.java @@ -0,0 +1,4 @@ +package duke.exception; + +public class CommandInvalidException extends Exception { +} diff --git a/src/main/java/duke/exception/DateDoesNotExistException.java b/src/main/java/duke/exception/DateDoesNotExistException.java new file mode 100644 index 000000000..28918e340 --- /dev/null +++ b/src/main/java/duke/exception/DateDoesNotExistException.java @@ -0,0 +1,4 @@ +package duke.exception; + +public class DateDoesNotExistException extends Exception { +} diff --git a/src/main/java/duke/exception/DukeException.java b/src/main/java/duke/exception/DukeException.java new file mode 100644 index 000000000..6464da39f --- /dev/null +++ b/src/main/java/duke/exception/DukeException.java @@ -0,0 +1,4 @@ +package duke.exception; + +public class DukeException extends Exception { +} diff --git a/src/main/java/duke/exception/EmptyDescriptionException.java b/src/main/java/duke/exception/EmptyDescriptionException.java new file mode 100644 index 000000000..5f6a65836 --- /dev/null +++ b/src/main/java/duke/exception/EmptyDescriptionException.java @@ -0,0 +1,4 @@ +package duke.exception; + +public class EmptyDescriptionException extends Exception{ +} diff --git a/src/main/java/duke/exception/FindIsEmptyException.java b/src/main/java/duke/exception/FindIsEmptyException.java new file mode 100644 index 000000000..04859def0 --- /dev/null +++ b/src/main/java/duke/exception/FindIsEmptyException.java @@ -0,0 +1,4 @@ +package duke.exception; + +public class FindIsEmptyException extends Exception { +} diff --git a/src/main/java/duke/exception/ShowDateIsEmptyException.java b/src/main/java/duke/exception/ShowDateIsEmptyException.java new file mode 100644 index 000000000..18d532507 --- /dev/null +++ b/src/main/java/duke/exception/ShowDateIsEmptyException.java @@ -0,0 +1,4 @@ +package duke.exception; + +public class ShowDateIsEmptyException extends Exception { +} diff --git a/src/main/java/duke/exception/WrongDateFormatException.java b/src/main/java/duke/exception/WrongDateFormatException.java new file mode 100644 index 000000000..13458314a --- /dev/null +++ b/src/main/java/duke/exception/WrongDateFormatException.java @@ -0,0 +1,4 @@ +package duke.exception; + +public class WrongDateFormatException extends Exception { +} diff --git a/src/main/java/duke/parser/Parser.java b/src/main/java/duke/parser/Parser.java new file mode 100644 index 000000000..773bc5f88 --- /dev/null +++ b/src/main/java/duke/parser/Parser.java @@ -0,0 +1,201 @@ +package duke.parser; + +import duke.command.Command; +import duke.exception.*; +import duke.tasklist.TaskList; +import duke.ui.Ui; + +public class Parser { + private static final int LENGTH_OF_DONE = 4; + private static final int LENGTH_OF_TODO = 4; + private static final int LENGTH_OF_FIND = 4; + private static final int LEN_OF_YEAR = 4; + private static final int LENGTH_OF_EVENT = 5; + private static final int LENGTH_OF_DELETE = 6; + private static final int LENGTH_OF_DEADLINE = 8; + private static final int LENGTH_OF_SHOW_DATE = 9; + private static final int TOTAL_NUM_MONTH = 12; + private static final int TOTAL_NUM_DAY = 31; + private static final int MIN_SPLIT_SIZE = 2; + private static final int CMD_NOT_FOUND = -1; + private static final int CMD_TODO = 1; + private static final int CMD_EVENT = 2; + private static final int CMD_DEADLINE = 3; + private static final int CMD_LIST = 4; + private static final int CMD_DONE = 5; + private static final int CMD_DELETE = 6; + private static final int CMD_SHOW_DATE = 7; + private static final int CMD_FIND = 8; + private static final int CMD_TERMINATE = 0; + private static final String LIST = "list"; + private static final String SHOW_DATE = "show date"; + private static final String TODO = "todo"; + private static final String EVENT = "event"; + private static final String DEADLINE = "deadline"; + private static final String DONE = "done"; + private static final String FIND = "find"; + private static final String BYE = "bye"; + private static final String DELETE = "delete"; + private static final String BY = "/by"; + private static final String AT = "/at"; + + private static boolean isInvalid(TaskList task, String line, String key) { + if (!line.split(key)[1].trim().isEmpty()) { + if (Integer.parseInt(line.split(key)[1].trim()) > task.getTaskCount()) { + return true; + } + } + return (line.length() <= LENGTH_OF_EVENT); + } + + /** + * Extracts the command parameter from the user input + * + * @param fullCommand Input provided by user + * @return Command object of the given command + */ + public static Command parse(String fullCommand) { + Command c; + c = new Command(); + c.setUserInput(fullCommand); + if (fullCommand.matches(LIST)) { + c.setCommand(CMD_LIST); + } else if (fullCommand.length() > LENGTH_OF_DONE && fullCommand.substring(0, LENGTH_OF_DONE).contains(DONE)) { + c.setCommand(CMD_DONE); + } else if (fullCommand.length() >= LENGTH_OF_TODO && fullCommand.substring(0, LENGTH_OF_TODO).contains(TODO)) { + c.setCommand(CMD_TODO); + } else if (fullCommand.length() >= LENGTH_OF_EVENT && fullCommand.substring(0, LENGTH_OF_EVENT).contains(EVENT)) { + c.setCommand(CMD_EVENT); + } else if (fullCommand.length() >= LENGTH_OF_DEADLINE && fullCommand.substring(0, LENGTH_OF_DEADLINE).contains(DEADLINE)) { + c.setCommand(CMD_DEADLINE); + } else if (fullCommand.length() >= LENGTH_OF_DELETE && fullCommand.substring(0, LENGTH_OF_DELETE).contains(DELETE)) { + c.setCommand(CMD_DELETE); + } else if (fullCommand.length() >= LENGTH_OF_SHOW_DATE && fullCommand.substring(0, LENGTH_OF_SHOW_DATE).contains(SHOW_DATE)) { + c.setCommand(CMD_SHOW_DATE); + } else if (fullCommand.length() >= LENGTH_OF_FIND && fullCommand.substring(0, LENGTH_OF_FIND).contains(FIND)) { + c.setCommand(CMD_FIND); + } else if (fullCommand.matches(BYE)) { + c.setCommand(CMD_TERMINATE); + } else { + c.setCommand(CMD_NOT_FOUND); + } + return c; + } + + /** + * Verify the if command is valid + * + * @param task List of task so far + * @param c Command object to access and execute the command + * @param ui Ui object to interact with the user + * @return boolean value true if command is valid + */ + public static boolean verifyCommand(TaskList task, Command c, Ui ui) { + String userInput = c.getUserInput(); + switch (c.getCommand()) { + case CMD_TODO: + try { + if (userInput.length() <= LENGTH_OF_TODO + 1) { + throw new EmptyDescriptionException(); + } + } catch (EmptyDescriptionException e) { + ui.printEmptyDescriptionError(TODO); + return true; + } + return false; + case CMD_EVENT: + try { + String[] keys = userInput.replace(EVENT, "").split(AT); + if (keys.length < MIN_SPLIT_SIZE || keys[0].isBlank() || keys[1].isBlank()) { + throw new EmptyDescriptionException(); + } + } catch (EmptyDescriptionException e) { + ui.printEmptyDescriptionError(EVENT); + return true; + } + return false; + case CMD_DEADLINE: + try { + String[] keys = userInput.replace(DEADLINE, "").split(BY); + if (keys.length < MIN_SPLIT_SIZE || keys[0].isBlank() || keys[1].isBlank()) { + throw new EmptyDescriptionException(); + } + } catch (EmptyDescriptionException e) { + ui.printEmptyDescriptionError(DEADLINE); + return true; + } + return false; + case CMD_SHOW_DATE: + String key = userInput.replace(SHOW_DATE, ""); + try { + if (key.isBlank()) { + throw new ShowDateIsEmptyException(); + } + } catch (ShowDateIsEmptyException e) { + ui.printEmptyDateError(); + return true; + } + try { + if (Integer.parseInt(key.split("/")[1].trim()) > TOTAL_NUM_MONTH) { + throw new WrongDateFormatException(); + } + if (Integer.parseInt(key.split("/")[2].trim()) > TOTAL_NUM_DAY) { + throw new WrongDateFormatException(); + } + } catch (WrongDateFormatException e) { + ui.printWrongDateFormatError(); + return true; + } + try { + if (key.split("/")[0].trim().length() < LEN_OF_YEAR) { + throw new WrongDateFormatException(); + } + } catch (WrongDateFormatException e) { + ui.printWrongDateFormatError(); + return true; + } + return false; + case CMD_FIND: + try { + key = userInput.replace(FIND, ""); + if (key.isBlank()) { + throw new FindIsEmptyException(); + } + } catch (FindIsEmptyException e) { + ui.printFindFieldEmpty(); + return true; + } + return false; + case CMD_DONE: + try { + if (isInvalid(task, userInput, DONE)) { + throw new CommandInvalidException(); + } + } catch (CommandInvalidException e) { + ui.printCommandIsInvalid(); + return true; + } + return false; + case CMD_DELETE: + try { + if (isInvalid(task, userInput, DELETE)) { + throw new CommandInvalidException(); + } + } catch (CommandInvalidException e) { + ui.printCommandIsInvalid(); + return true; + } + return false; + case CMD_LIST: + case CMD_TERMINATE: + return false; + default: + try { + throw new CommandDoesNotExistException(); + } catch (CommandDoesNotExistException e) { + ui.printCommandDoesNotExist(); + return true; + } + } + } +} \ No newline at end of file diff --git a/src/main/java/duke/storage/Storage.java b/src/main/java/duke/storage/Storage.java new file mode 100644 index 000000000..71c45d63b --- /dev/null +++ b/src/main/java/duke/storage/Storage.java @@ -0,0 +1,111 @@ +package duke.storage; + +import duke.tasklist.TaskList; +import duke.tasklist.task.Task; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Scanner; + + +public class Storage { + protected String fileName; + protected String fileDir; + public final ArrayList items = new ArrayList<>(); + + + /** + * @param fileDir the directory of the save file + * @param fileName the name of the save file + */ + public Storage(String fileName, String fileDir) { + this.fileDir = fileDir; + this.fileName = fileName; + } + + /** + * Check and create save folder and save file if it does not exist + * + */ + public void create() { + File saveFile = new File(fileName); + File saveDir = new File(fileDir); + if (saveDir.mkdirs()) { + System.out.println("Successfully created save dir"); + } else { + System.out.println("Save folder already exists."); + } + try { + if (saveFile.createNewFile()) { + System.out.println("Successfully created save file"); + } else { + System.out.println("Save file already exists"); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * Save the current list of tasks to save file + * + */ + public void save() { + Task[] saveLists = new Task[items.size()]; + items.toArray(saveLists); + try { + FileWriter fw = new FileWriter(fileName); + for (Task saveList : saveLists) { + fw.write(saveList.toString().charAt(1) + "|"); + fw.write(saveList.getStatus() + "|" + saveList.getDescription()); + if (!saveList.getDate().equals("empty")) { + fw.write("|" + saveList.getDate()); + } + fw.write("\n"); + } + System.out.println("Automatically saved to " + fileName); + fw.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * Loads the tasks list from save file + * + */ + public void load(TaskList task) throws FileNotFoundException { + File saveFile = new File(fileName); + Scanner s = new Scanner(saveFile); + while (s.hasNext()) { + String[] chars = s.nextLine().split("\\|"); + String type = chars[0].trim(); + String status = chars[1].trim(); + String description = chars[2].trim(); + switch (type) { + case "E": + String date = chars[3].trim(); + task.addEvent(items, description, date); + if (status.equals("1")) { + items.get(task.getTaskCount()).markDone(); + } + break; + case "D": + date = chars[3].trim(); + task.addDeadline(items, description, date); + if (status.equals("1")) { + items.get(task.getTaskCount()).markDone(); + } + break; + case "T": + task.addTodo(items, description); + break; + } + } + task.loadTaskCount(items); + System.out.println("Save file successfully loaded"); + } + +} diff --git a/src/main/java/duke/tasklist/TaskList.java b/src/main/java/duke/tasklist/TaskList.java new file mode 100644 index 000000000..1558c8b92 --- /dev/null +++ b/src/main/java/duke/tasklist/TaskList.java @@ -0,0 +1,72 @@ +package duke.tasklist; +import duke.tasklist.task.Task; +import duke.tasklist.task.Deadlines; +import duke.tasklist.task.Todo; +import duke.tasklist.task.Events; +import duke.ui.Ui; +import java.util.ArrayList; + +public class TaskList { + + protected int taskCount; + + /** + * @param items The list of tasks stored in an ArrayList + */ + public TaskList(ArrayList items) { + loadTaskCount(items); + } + + public int getTaskCount() { + return taskCount; + } + + public void loadTaskCount(ArrayList items) { + this.taskCount = items.size(); + } + + /** + * Create a new Event object and add it to the tasks list + * + * @param items list of items stored in an ArrayList + * @param description task description input by user + * @param at the deadline for the event input by the user + */ + public void addEvent(ArrayList items, String description, String at) { + Events newEvent = new Events(description, at); + items.add(taskCount, newEvent); + } + + /** + * Create a new Deadline object and add it to the tasks list + * + * @param items list of items stored in an ArrayList + * @param description task description input by user + * @param by the date for the deadline input by the user + */ + public void addDeadline(ArrayList items, String description, String by) { + Deadlines newDeadline = new Deadlines(description, by); + items.add(taskCount, newDeadline); + } + + /** + * Create a new Todo object and add it to the tasks list + * + * @param items list of items stored in an ArrayList + * @param description task description input by user + * + */ + public void addTodo(ArrayList items, String description) { + Todo newTodo = new Todo(description); + items.add(taskCount, newTodo); + } + + public void removeItem(Ui ui, ArrayList items, int taskNum) { + taskCount--; + ui.removeTaskMessage(items.get(taskNum), taskCount); + items.remove(taskNum); + } + +} + + diff --git a/src/main/java/duke/tasklist/task/Deadlines.java b/src/main/java/duke/tasklist/task/Deadlines.java new file mode 100644 index 000000000..883055da4 --- /dev/null +++ b/src/main/java/duke/tasklist/task/Deadlines.java @@ -0,0 +1,52 @@ +package duke.tasklist.task; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; + +public class Deadlines extends Task { + protected String by; + protected LocalDate date; + protected LocalTime time; + + /** + * @param description The description of the task given by the user + * @param by the deadline given by the user for the task + */ + public Deadlines(String description, String by) { + super(description); + this.by = by; + this.date = LocalDate.parse(by.split(" ")[0], DateTimeFormatter.ofPattern("yyyy/MM/dd")); + this.time = LocalTime.parse(readTime(by)); + + } + /** + * Reformat the time parameter input to one that is readable by the library + * + * @param str is the string input by the user after '/by' + * @return a string that is reformatted to the right format for LocalTime.parse() + */ + private String readTime(String str) { + String hour = str.split(" ")[1].substring(0, 2); + String colon = ":"; + String minutes = str.split(" ")[1].substring(2, 4); + return hour.concat(colon).concat(minutes); + } + @Override + public String getDate() { + return this.by; + } + private String formatDate(LocalDate date) { + return date.format(DateTimeFormatter.ofPattern("dd MMM yyyy")); + } + private String formatTime(LocalTime time) { + return time.format(DateTimeFormatter.ofPattern("hh mm a")); + } + @Override + public LocalDate getDeadline() { + return this.date; + } + public String toString() { + return "[D]" + super.toString() + " (by: " + formatDate(this.date) + ", " + formatTime(this.time) + ")"; + } +} + diff --git a/src/main/java/duke/tasklist/task/Events.java b/src/main/java/duke/tasklist/task/Events.java new file mode 100644 index 000000000..843c55485 --- /dev/null +++ b/src/main/java/duke/tasklist/task/Events.java @@ -0,0 +1,52 @@ +package duke.tasklist.task; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; + +public class Events extends Task { + protected String at; + protected LocalDate date; + protected LocalTime time; + + /** + * @param description The description of the task given by the user + * @param at the deadline given by the user for the task + */ + public Events(String description, String at) { + super(description); + this.at = at; + this.date = LocalDate.parse(at.split(" ")[0], DateTimeFormatter.ofPattern("yyyy/MM/dd")); + this.time = LocalTime.parse(readTime(at)); + } + /** + * Reformat the time parameter input to one that is readable by the library + * + * @param str is the string input by the user after '/at' + * @return a string that is reformatted to the right format for LocalTime.parse() + */ + private String readTime(String str) { + String hour = str.split(" ")[1].substring(0, 2); + String colon = ":"; + String minutes = str.split(" ")[1].substring(2, 4); + return hour.concat(colon).concat(minutes); + } + @Override + public String getDate() { + return this.at; + } + private String formatDate(LocalDate date) { + return date.format(DateTimeFormatter.ofPattern("dd MMM yyyy")); + } + + private String formatTime(LocalTime time) { + return time.format(DateTimeFormatter.ofPattern("hh mm a")); + } + @Override + public LocalDate getDeadline() { + return this.date; + } + public String toString() { + return "[E]" + super.toString() + " (at: " + formatDate(this.date) + ", " + formatTime(this.time) + ")"; + } +} diff --git a/src/main/java/duke/tasklist/task/Task.java b/src/main/java/duke/tasklist/task/Task.java new file mode 100644 index 000000000..177ddb176 --- /dev/null +++ b/src/main/java/duke/tasklist/task/Task.java @@ -0,0 +1,42 @@ +package duke.tasklist.task; + +import java.time.LocalDate; + +public class Task { + protected String description; + protected boolean isDone; + + /** + * @param description The description of the task given by the user + */ + public Task(String description) { + this.description = description; + this.isDone = false; + } + + public String getStatusIcon() { + + return (isDone ? "X" : " "); + } + public String getDescription() { + + return this.description; + } + public String getStatus() { + return (isDone ? "1" : "0"); + } + public String getDate() { + return "empty"; + } + + public LocalDate getDeadline() { + return null; + } + public void markDone() { + + this.isDone = true; + } + public String toString() { + return "[" + getStatusIcon() + "] " + getDescription(); + } +} diff --git a/src/main/java/duke/tasklist/task/Todo.java b/src/main/java/duke/tasklist/task/Todo.java new file mode 100644 index 000000000..ee004a042 --- /dev/null +++ b/src/main/java/duke/tasklist/task/Todo.java @@ -0,0 +1,15 @@ +package duke.tasklist.task; + +public class Todo extends Task { + + /** + * @param description The description of the task given by the user + */ + public Todo(String description) { + super(description); + } + + public String toString() { + return "[T]" + super.toString(); + } +} diff --git a/src/main/java/duke/ui/Ui.java b/src/main/java/duke/ui/Ui.java new file mode 100644 index 000000000..b957b706b --- /dev/null +++ b/src/main/java/duke/ui/Ui.java @@ -0,0 +1,94 @@ +package duke.ui; + +import duke.tasklist.TaskList; +import duke.tasklist.task.Task; +import java.util.Scanner; + +public class Ui { + private static final String logo = + " _____ ___ _____ ______\n" + + "|___ | | | |_____| / / -- \\ \\ \n" + + " / / | | / / | | | | \n" + + " / / | | / / | | | |\n" + + " / /___ | | /_/__ | | -- | |\n" + + "|_____| | | |_____| \\ \\____/ /\n"; + public static final String border = "____________________________________________________________\n"; + + /** + * Check and create save folder and save file if it does not exist + * + */ + public void showLine() { + System.out.println(border); + } + public void showError() { + System.out.println("Error occurred! Please try again."); + } + public void showSaveFileError() { + System.out.println("Could not find existing save file!"); + } + public void printStartMessage() { + System.out.println(logo); + System.out.println(border + "Hi bro, my name is Zizo"); + System.out.println("What do you want?\n" + border); + System.out.println("Type bye to exit\n" + border); + } + public void printEndMessage() { + System.out.println(border); + System.out.println("chat again next time!\n" + border); + } + public void addTaskMessage(TaskList tasks, Task task) { + int taskCount = tasks.getTaskCount(); + System.out.println(border); + System.out.println("Got it. I've added this task:"); + System.out.println(task); + System.out.println("Now you have " + taskCount + " tasks in the list."); + System.out.println(border); + } + public void removeTaskMessage(Task task, int taskCount) { + System.out.println(border); + System.out.println("Noted. I've removed this task:"); + System.out.println(task); + System.out.println("Now you have " + taskCount + " tasks in the list."); + System.out.println(border); + } + public void printEmptyDescriptionError(String command) { + System.out.println(border); + System.out.println("\uD83D\uDE00 " + "OOPS!!! The description of a " + command + " cannot be empty."); + System.out.println(border); + } + public void printCommandDoesNotExist() { + System.out.println(border); + System.out.println("\uD83D\uDE00 " + "OOPS!!! I'm sorry, but I don't know what that means :-("); + System.out.println(border); + } + public void printCommandIsInvalid() { + System.out.println(border); + System.out.println("\uD83D\uDE00 " + "OOPS!!! I'm sorry, the command is invalid."); + System.out.println(border); + } + public void printEmptyDateError() { + System.out.println(border); + System.out.println("\uD83D\uDE00 " + "OOPS!!! I'm sorry, please input the date you would like to search for " + + "in yyyy/mm/dd format" ); + System.out.println(border); + } + public void printFindFieldEmpty() { + System.out.println(border); + System.out.println("\uD83D\uDE00 " + "OOPS!!! I'm sorry, please input a keyword for the task " + + "you would like to search for"); + System.out.println(border); + } + public void printWrongDateFormatError() { + System.out.println(border); + System.out.println("\uD83D\uDE00 " + "OOPS!!! I'm sorry, please input the correct date format: " + + "yyyy/dd/mm"); + System.out.println(border); + } + public String readCommand(Scanner in) { + return in.nextLine(); + } + public Ui() { + printStartMessage(); + } +} diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 657e74f6e..c53b9a459 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,7 +1,72 @@ -Hello from - ____ _ -| _ \ _ _| | _____ -| | | | | | | |/ / _ \ -| |_| | |_| | < __/ -|____/ \__,_|_|\_\___| + _____ ___ _____ ______ +|___ | | | |_____| / / -- \ \ + / / | | / / | | | | + / / | | / / | | | | + / /___ | | /_/__ | | -- | | +|_____| | | |_____| \ \____/ / +____________________________________________________________ +Hi bro, my name is Echo +What do you want? +____________________________________________________________ + +Type bye to exit +____________________________________________________________ + +____________________________________________________________ + +Got it. I've added this task: +[E][ ]CCA (at: Tues & Thurs) +Now you have 1 tasks in the list. +____________________________________________________________ + +____________________________________________________________ + +Got it. I've added this task: +[D][ ]CS2113T (by: Thursday 2359) +Now you have 2 tasks in the list. +____________________________________________________________ + +____________________________________________________________ + +Got it. I've added this task: +[T][ ]CFG1002 +Now you have 3 tasks in the list. +____________________________________________________________ + +____________________________________________________________ + +Here is your list +1.[E][ ]CCA (at: Tues & Thurs) +2.[D][ ]CS2113T (by: Thursday 2359) +3.[T][ ]CFG1002 +____________________________________________________________ + +____________________________________________________________ +Nice! task is done +____________________________________________________________ + +____________________________________________________________ + +Here is your list +1.[E][X]CCA (at: Tues & Thurs) +2.[D][ ]CS2113T (by: Thursday 2359) +3.[T][ ]CFG1002 +____________________________________________________________ + +____________________________________________________________ +Nice! task is done +____________________________________________________________ + +____________________________________________________________ + +Here is your list +1.[E][X]CCA (at: Tues & Thurs) +2.[D][X]CS2113T (by: Thursday 2359) +3.[T][ ]CFG1002 +____________________________________________________________ + +____________________________________________________________ + +chat again next time! +____________________________________________________________ \ No newline at end of file diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index e69de29bb..edcf35c89 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -0,0 +1,9 @@ +event CCA /at Tues & Thurs +deadline CS2113T /by Thursday 2359 +todo CFG1002 +list +done 1 +list +done 2 +list +bye \ No newline at end of file diff --git a/text-ui-test/runtest.bat b/text-ui-test/runtest.bat index 087374464..e798e0298 100644 --- a/text-ui-test/runtest.bat +++ b/text-ui-test/runtest.bat @@ -4,10 +4,10 @@ REM create bin directory if it doesn't exist if not exist ..\bin mkdir ..\bin REM delete output from previous run -if exist ACTUAL.TXT del ACTUAL.TXT +del ACTUAL.TXT REM compile the code into the bin folder -javac -cp ..\src\main\java -Xlint:none -d ..\bin ..\src\main\java\*.java +javac -cp ..\src\main\java\ -Xlint:none -d ..\bin ..\src\main\java\duke\*.java IF ERRORLEVEL 1 ( echo ********** BUILD FAILURE ********** exit /b 1 @@ -18,4 +18,4 @@ REM run the program, feed commands from input.txt file and redirect the output t java -classpath ..\bin Duke < input.txt > ACTUAL.TXT REM compare the output to the expected output -FC ACTUAL.TXT EXPECTED.TXT +FC ACTUAL.TXT EXPECTED.TXT \ No newline at end of file diff --git a/text-ui-test/runtest.sh b/text-ui-test/runtest.sh deleted file mode 100644 index c9ec87003..000000000 --- a/text-ui-test/runtest.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env bash - -# create bin directory if it doesn't exist -if [ ! -d "../bin" ] -then - mkdir ../bin -fi - -# delete output from previous run -if [ -e "./ACTUAL.TXT" ] -then - rm ACTUAL.TXT -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 -then - echo "********** BUILD FAILURE **********" - exit 1 -fi - -# run the program, feed commands from input.txt file and redirect the output to the ACTUAL.TXT -java -classpath ../bin Duke < input.txt > ACTUAL.TXT - -# convert to UNIX format -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 -if [ $? -eq 0 ] -then - echo "Test result: PASSED" - exit 0 -else - echo "Test result: FAILED" - exit 1 -fi \ No newline at end of file