diff --git a/.gitignore b/.gitignore index f69985ef1..7d958bad6 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,6 @@ bin/ /text-ui-test/ACTUAL.txt text-ui-test/EXPECTED-UNIX.TXT +src/main/java/IllegalShapeException.java +src/main/java/Main.java +src/main/java/temp.java diff --git a/README.md b/README.md index 8715d4d91..f76aff5e4 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,23 @@ -# Duke project template +# Smart XiaoAi TongXue -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. +This is a task management Java project. It's named after the Smart XiaoAi TongXue. Given below are instructions on how to use it. -## Setting up in Intellij +## list +list all tasks in the task list +## todo {task description} +add Todo task +## deadline {task description} /by {time} +add Deadline +## Event {task description} /at {time} +add Event +## mark {index} +mark task +## unmark {index} +unmark task +## delete {index} +delete task from list +## find {text} +find matching tasks containing the {text} +## bye +exit the program -Prerequisites: JDK 11, update Intellij to the most recent version. - -1. Open Intellij (if you are not in the welcome screen, click `File` > `Close Project` to close the existing project first) -1. Open the project into Intellij as follows: - 1. Click `Open`. - 1. Select the project directory, and click `OK`. - 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: - ``` - Hello from - ____ _ - | _ \ _ _| | _____ - | | | | | | | |/ / _ \ - | |_| | |_| | < __/ - |____/ \__,_|_|\_\___| - ``` diff --git a/docs/README.md b/docs/README.md index 8077118eb..fea907c7d 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,29 +1,42 @@ # User Guide +Smart XiaoAI is a task management app use via a Command Line Interface(CLI). -## Features +---- +The file path: task-file -### Feature-ABC +---- -Description of the feature. -### Feature-XYZ +## Features -Description of the feature. +### Feature- list all task -## Usage +'list' +list all tasks -### `Keyword` - Describe action +### Feature- Add a todo task +'todo {description}' -Describe the action and its outcome. +### Feature- Add a deadline task +'deadline {description} /by {time}' -Example of usage: +### Feature- Add a event task +'event {description} /at {time}' -`keyword (optional arguments)` +### Feature- Mark a task as done +'mark {index}' -Expected outcome: +### Feature- Unmark a task +'unmark {index}' -Description of the outcome. +### Feature- Unmark a task +'unmark {index}' -``` -expected output -``` +### Feature- remove a task +'delete {index}' + +### Feature- Find matching tasks +'find {text}' + +### Feature- Exit program +'bye' 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..fea33e730 --- /dev/null +++ b/src/main/java/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: controller.Duke + diff --git a/src/main/java/command/AddCommand.java b/src/main/java/command/AddCommand.java new file mode 100644 index 000000000..190e250d7 --- /dev/null +++ b/src/main/java/command/AddCommand.java @@ -0,0 +1,90 @@ +package command; + +import controller.Storage; +import controller.TaskList; +import controller.UI; +import exception.DukeException; +import exception.InvalidCommandException; +import exception.NoDescriptionException; +import exception.NoTimeException; +import task.Task; +import task.Todo; + +public class AddCommand extends Command { + String input; + String action; + + public AddCommand(String action, String input) { + super(); + this.input = input; + this.action =action; + } + + /** + * Add specific task into task list according to the input + * Show task count + * Save the task list data into file + * @param tasks task list + * @param ui user interface + * @param storage storage. + */ + + public void execute(TaskList tasks, UI ui, Storage storage) throws DukeException { + String description = ""; + String time = ""; + String[] info = new String[2]; + switch (action){ + case "todo": + description = input.replace("todo", "").trim(); + if(description == null || input.isEmpty()){ + throw new NoDescriptionException(); + } + tasks.addNewTodo(description); + break; + case "event": + info = getInfo("event"); + description = info[0]; + time = info[1]; + tasks.addNewEvent(description,time); + break; + case "deadline": + info = getInfo("deadline"); + description = info[0]; + time = info[1]; + tasks.addNewDeadline(description,time); + break; + default: + throw new InvalidCommandException(); + } + ui.showAdd(); + ui.showTask(tasks.getNewAdd()); + ui.showTaskCount(tasks.getCount()); + storage.save(tasks.getTaskList()); + } + + private String[] getInfo(String action) throws DukeException { + int descriptionIdx = input.indexOf(" "); + if(descriptionIdx == -1){ + throw new NoDescriptionException(); + } + int timeIdx = 0; + if(action.equals("event")) { + timeIdx = input.indexOf("/at"); + }else if(action.equals("deadline")){ + timeIdx = input.indexOf("/by"); + } + if(timeIdx == -1){ + throw new NoTimeException(); + } + String description = input.substring(descriptionIdx+1, timeIdx).trim(); + String time = input.substring(timeIdx+4).trim(); + String[] res = new String[2]; + res[0] = description; + res[1] = time; + return res; + } + public boolean isExit(){ + return false; + } + +} diff --git a/src/main/java/command/Command.java b/src/main/java/command/Command.java new file mode 100644 index 000000000..cf0fe233f --- /dev/null +++ b/src/main/java/command/Command.java @@ -0,0 +1,12 @@ +package command; + +import controller.Storage; +import controller.TaskList; +import controller.UI; +import exception.DukeException; +import exception.NoDescriptionException; + +public abstract class Command { + public abstract void execute(TaskList tasks, UI ui, Storage storage) throws NoDescriptionException, DukeException; + public abstract boolean isExit(); +} diff --git a/src/main/java/command/DeleteCommand.java b/src/main/java/command/DeleteCommand.java new file mode 100644 index 000000000..3dce98b93 --- /dev/null +++ b/src/main/java/command/DeleteCommand.java @@ -0,0 +1,42 @@ +package command; + +import controller.Helper; +import controller.Storage; +import controller.TaskList; +import controller.UI; +import exception.DukeException; + +public class DeleteCommand extends Command{ + private static final String INDENT = " "; + String command; + public DeleteCommand(String command) { + this.command=command; + } + + /** + * Remove task from task list according to the input + * Show task count + * Save the task list data into file + * @param tasks task list + * @param ui user interface + * @param storage storage. + */ + + public void execute(TaskList tasks, UI ui, Storage storage) throws DukeException { + String idxInString = command.replace("delete","").trim(); + Helper.checkIndex(idxInString,tasks); + int idx = Integer.parseInt(idxInString)-1; + ui.showDelete(); + ui.showTask(tasks.removeTaskByIdx(idx)); + ui.showList(); + for(int i = 0; i targetList = new ArrayList<>(); + for(Task task : tasks.getTaskList()){ + if(task.toString().contains(text)){ + targetList.add(task); + } + } + + if(targetList.size() ==0){ + ui.showNotFound(); + return; + } + ui.showFound(); + int i = 1; + for(Task task : targetList){ + System.out.println(INDENT + (i++)+ "." + task.toString()); + } + } + + public boolean isExit(){ + return false; + } + +} diff --git a/src/main/java/command/ListCommand.java b/src/main/java/command/ListCommand.java new file mode 100644 index 000000000..1ad30b4e4 --- /dev/null +++ b/src/main/java/command/ListCommand.java @@ -0,0 +1,30 @@ +package command; + + +import controller.Storage; +import controller.TaskList; +import controller.UI; + +public class ListCommand extends Command { + private static final String INDENT = " "; + + /** + * Print out all task in the task list. + * @param taskList task list. + * @param ui user interface. + * @param storage storage. + */ + public void execute(TaskList taskList, UI ui, Storage storage){ + if(taskList.getCount() == 0){ + ui.showEmptyList(); + return; + } + ui.showList(); + for(int i = 0; i= tasks.getCount() || idx < 0){ + throw new InvalidIndexException(); + } + } +} diff --git a/src/main/java/controller/Parser.java b/src/main/java/controller/Parser.java new file mode 100644 index 000000000..6080b0c54 --- /dev/null +++ b/src/main/java/controller/Parser.java @@ -0,0 +1,60 @@ +package controller; + +import command.*; +import exception.DukeException; +import exception.InvalidCommandException; +//import command.MarkCommand; + + +public class Parser { + private static final String INDENT = " "; + private static final String LINE="-------------------------------------------"; + public static final String SPACE = " "; + private static final int MAX_TASK_COUNT = 100; + private static final String DIR = "data/task-file"; + private static final String FILE_SEPARATOR = " | "; + + /** + * Scan the input. + * Specify the action. + * Return the corresponding Command. + * + * @param fullCommand command taken from user. + * @throws DukeException if the input is invalid. + * @return specific command. + */ + public static Command parse(String fullCommand) throws DukeException { + String action = getAction(fullCommand); + switch(action){ + case "list": + return new ListCommand(); + case "mark": + return new UpdateCommand("mark",fullCommand); + //updateTask(Integer.parseInt(input.split(SPACE)[1])-1,true); //refractor get index + case "unmark": + return new UpdateCommand("unmark",fullCommand); + //updateTask(Integer.parseInt(input.split(SPACE)[1])-1,false); + case "todo": + + case "deadline": + + case "event": + return new AddCommand(action, fullCommand); + case "delete": + return new DeleteCommand(fullCommand); + //deleteTask(Integer.parseInt(input.split(SPACE)[1])-1); + case "bye": + return new ExitCommand(); + case "find": + return new FindCommand(fullCommand); + default: + throw new InvalidCommandException(); + } + } + + private static String getAction(String input){ + String action = input.split(SPACE)[0].toLowerCase(); + return action; + } + +} diff --git a/src/main/java/controller/Storage.java b/src/main/java/controller/Storage.java new file mode 100644 index 000000000..cb45461bf --- /dev/null +++ b/src/main/java/controller/Storage.java @@ -0,0 +1,70 @@ +package controller; + +import task.Task; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Scanner; +import java.util.StringTokenizer; + + +public class Storage { + private static final String FILE_SEPARATOR = " | "; + private String filePath; + + public Storage(String path) { + filePath = path; + } + + + public void save(ArrayList al) { + StringBuilder sb = new StringBuilder(); + List newList = new ArrayList(); + for (int i = 0; i < al.size(); i++) { + Task task = al.get(i); + if (task == null) break; + String taskType = task.getTypeIcon(); + String taskStatus = task.getStatusIcon(); + String taskDetails = task.getDescription(); + sb.append(taskType); + sb.append(FILE_SEPARATOR); + sb.append(taskStatus); + sb.append(FILE_SEPARATOR); + sb.append(taskDetails); + if (taskType.equals("D") || taskType.equals("E")) { + sb.append(FILE_SEPARATOR); + sb.append(task.getTime()); + } + sb.append(System.lineSeparator()); + } + try { + writeToFile(sb.toString()); + } catch (IOException e) { + System.out.println("Something went wrong:" + e.getMessage()); + } + } + + private void writeToFile(String data) throws IOException { + FileWriter fw = new FileWriter(filePath); + fw.write(data); + fw.close(); + } + + public String load() throws IOException { + StringBuilder sb = new StringBuilder(); + File f = new File(filePath); + f.createNewFile(); // if the file doesn't exist, it will create one + Scanner s = new Scanner(f); // create a Scanner using the File as the source + while (s.hasNext()) { + sb.append(s.nextLine()); + sb.append(System.lineSeparator()); + } + return sb.toString(); + } +} + + + diff --git a/src/main/java/controller/TaskList.java b/src/main/java/controller/TaskList.java new file mode 100644 index 000000000..40143e2f0 --- /dev/null +++ b/src/main/java/controller/TaskList.java @@ -0,0 +1,109 @@ +package controller; + +import task.Deadline; +import task.Event; +import task.Task; +import task.Todo; + +import java.util.ArrayList; +import java.util.StringTokenizer; + +public class TaskList { + private static final String FILE_SEPARATOR = " | "; + private static final String INDENT = " "; + public static final String SPACE = " "; + private static ArrayList taskList = new ArrayList<>(); + + public TaskList(String data) { + if(data == null || data.isEmpty()) return; + String[] taskStrList = data.split(System.lineSeparator()); + for (int i = 0; i < taskStrList.length; i++) { + String newLine = taskStrList[i]; + Task newTask; + StringTokenizer st = new StringTokenizer(newLine, FILE_SEPARATOR); + String taskType = st.nextToken(); + String taskStatus = st.nextToken(); + String description = st.nextToken(); + + if (taskType.equals("D")) { + String time = st.nextToken(); + newTask = new Deadline(description, time); + } else if (taskType.equals("E")) { + String time = st.nextToken(); + newTask = new Event(description, time); + } else { + newTask = new Todo(description); + } + + if (taskStatus.equals("X")) newTask.markAsDone(); + taskList.add(newTask); + } + } + + public TaskList() { + taskList = new ArrayList<>(); + } + /** + * Return total task count in the task list + * @return total task count + */ + public int getCount () { + return taskList.size(); + } + + /** + * Return task of corresponding index. + * @param i the index of the target task. + * @return task of corresponding index. + */ + public Task getTaskByIdx ( int i){ + return taskList.get(i); + } + + /** + * Remove corresponding task by index + * @param i the index of target task. + * @return total task count + */ + public String removeTaskByIdx ( int i){ + String task = taskList.get(i).toString(); + taskList.remove(i); + return task; + } + + /** + * Return all tasks in the list + * @return arraylist of tasks + */ + public ArrayList getTaskList () { + return taskList; + } + + public String markTaskByIdx(int i){ + taskList.get(i).markAsDone(); + return taskList.get(i).toString(); + } + + public String unmarkTaskByIdx(int i){ + taskList.get(i).markAsUndone(); + return taskList.get(i).toString(); + } + + public void addNewTodo(String description) { + taskList.add(new Todo(description)); + } + + public void addNewEvent(String description, String time) { + taskList.add(new Event(description,time)); + } + + public void addNewDeadline(String description, String time) { + taskList.add(new Deadline(description, time)); + } + + public String getNewAdd() { + return taskList.get(taskList.size()-1).toString(); + } +} + + diff --git a/src/main/java/controller/UI.java b/src/main/java/controller/UI.java new file mode 100644 index 000000000..d96517013 --- /dev/null +++ b/src/main/java/controller/UI.java @@ -0,0 +1,95 @@ +package controller; + +import java.util.Scanner; + +public class UI { + private static final String INDENT = " "; + private static final String LINE="-------------------------------------------"; + public static final String SPACE = " "; + private static final int MAX_TASK_COUNT = 100; + private static final String DIR = "data/task-file"; + private static final String FILE_SEPARATOR = " | "; + private Scanner sc = new Scanner(System.in); + public UI(){ + + } + public void showWelcome(){ + greet(); + } + + private void greet(){ + printLine(); + System.out.println(INDENT + "Hi, I am XiaoAi TongXue ;D"); + System.out.println(INDENT + "What can I do for you?"); + printLine(); + } + + private void printLine(){ + System.out.println(INDENT + LINE); + } + + public String readCommand(){ + String input = getInput(); + return input; + } + + private String getInput(){ + String input = sc.nextLine().trim(); + return input; + } + + public void showLine(){ + printLine(); + } + + public void showError(String s){ + System.out.println(INDENT + s); + } + + public void showLoadingError(){ + System.out.println(INDENT + "oops! cannot find file!"); + } + + public void showList() { + System.out.println(INDENT+"Here are the task(s) in your list:"); + } + + public void showDelete() { + System.out.println(INDENT+"Noted. I've removed this task:"); + } + + public void showTaskCount(int taskCount){ + System.out.println(INDENT + "Now you have " + taskCount + " task(s) in the list."); + } + + public void showAdd(){ + System.out.println(INDENT+"Got it. I've added this task:"); + // System.out.println(INDENT+t); + } + + public void showErrorTask() { + System.out.println(INDENT + "OOPS! the task doesn't have description or time :("); + } + + public void showTask(String t) { + System.out.println(INDENT + t); + } + + public void showBye() { + System.out.println(INDENT + "Bye. Hope to see you again soon!"); + printLine(); + } + + public void showFound() { + System.out.println(INDENT + "Here are the matching tasks in your list:"); + } + + public void showNotFound() { + System.out.println(INDENT + "Opps, no matching tasks found in your list :("); + } + + public void showEmptyList() { + System.out.println(INDENT + "There are nothing in the task list now"); + } +} + diff --git a/src/main/java/exception/DukeException.java b/src/main/java/exception/DukeException.java new file mode 100644 index 000000000..671fd58e4 --- /dev/null +++ b/src/main/java/exception/DukeException.java @@ -0,0 +1,9 @@ +package exception; + +public class DukeException extends Throwable{ + private static final String ERROR_MESSAGE = ""; + + public String getErrorMessage() { + return ERROR_MESSAGE; + } +} diff --git a/src/main/java/exception/InvalidCommandException.java b/src/main/java/exception/InvalidCommandException.java new file mode 100644 index 000000000..8adf98e56 --- /dev/null +++ b/src/main/java/exception/InvalidCommandException.java @@ -0,0 +1,9 @@ +package exception; + +public class InvalidCommandException extends DukeException{ + private static final String ERROR_MESSAGE = "Oops! I don't understand this command"; + + public String getErrorMessage() { + return ERROR_MESSAGE; + } +} diff --git a/src/main/java/exception/InvalidIndexException.java b/src/main/java/exception/InvalidIndexException.java new file mode 100644 index 000000000..db39f952e --- /dev/null +++ b/src/main/java/exception/InvalidIndexException.java @@ -0,0 +1,9 @@ +package exception; + +public class InvalidIndexException extends DukeException{ + private static final String ERROR_MESSAGE = "Oops! This is invalid index!"; + + public String getErrorMessage() { + return ERROR_MESSAGE; + } +} diff --git a/src/main/java/exception/NoDescriptionException.java b/src/main/java/exception/NoDescriptionException.java new file mode 100644 index 000000000..4bf100852 --- /dev/null +++ b/src/main/java/exception/NoDescriptionException.java @@ -0,0 +1,9 @@ +package exception; + +public class NoDescriptionException extends DukeException{ + private static final String ERROR_MESSAGE = "Oops! No Description Found!"; + + public String getErrorMessage() { + return ERROR_MESSAGE; + } +} diff --git a/src/main/java/exception/NoTaskIndexFoundException.java b/src/main/java/exception/NoTaskIndexFoundException.java new file mode 100644 index 000000000..ce51db61b --- /dev/null +++ b/src/main/java/exception/NoTaskIndexFoundException.java @@ -0,0 +1,10 @@ +package exception; + +public class NoTaskIndexFoundException extends DukeException{ + private static final String ERROR_MESSAGE = "Oops! No Task Index Found!"; + + public String getErrorMessage() { + return ERROR_MESSAGE; + } +} + diff --git a/src/main/java/exception/NoTimeException.java b/src/main/java/exception/NoTimeException.java new file mode 100644 index 000000000..0cc259546 --- /dev/null +++ b/src/main/java/exception/NoTimeException.java @@ -0,0 +1,9 @@ +package exception; + +public class NoTimeException extends DukeException{ + private static final String ERROR_MESSAGE = "Oops! No time information found!"; + + public String getErrorMessage() { + return ERROR_MESSAGE; + } +} diff --git a/src/main/java/task/Deadline.java b/src/main/java/task/Deadline.java new file mode 100644 index 000000000..69983a58a --- /dev/null +++ b/src/main/java/task/Deadline.java @@ -0,0 +1,30 @@ +package task; + +public class Deadline extends Task { + private String by; + + public Deadline(String description, String by){ + super(description); + this.by=by; + } + + public String getBy(){ + return by; + } + + public void setBy(String by){ + this.by = by; + } + + @Override + public String toString(){ + return "[D]" + super.toString() + "(by: " + by + ")"; + } + + public String getTypeIcon(){return "D";} + + public String getTime(){ + return by; + } + +} diff --git a/src/main/java/task/Event.java b/src/main/java/task/Event.java new file mode 100644 index 000000000..1694ad2bb --- /dev/null +++ b/src/main/java/task/Event.java @@ -0,0 +1,21 @@ +package task; + +public class Event extends Task{ + private String at; + + public Event(String description, String at) { + super(description); + this.at = at; + } + + public String toString(){ + return "[E]" + super.toString() + "(at: " + at + ")"; + } + public String getTypeIcon(){return "E";} + public String getTime(){ + return at; + } + + + +} diff --git a/src/main/java/task/Task.java b/src/main/java/task/Task.java new file mode 100644 index 000000000..d10bfadbd --- /dev/null +++ b/src/main/java/task/Task.java @@ -0,0 +1,37 @@ +package task; + +public class Task { + protected String description; + protected boolean isDone; + + public Task(String description){ + this.description = description; + this.isDone = false; + } + + public String getStatusIcon(){ + return (isDone ? "X" : "0"); + } + + public String getDescription() { + return description; + } + + public void markAsDone(){ + this.isDone=true; + } + + public void markAsUndone(){ + this.isDone=false; + } + + public String toString(){ + return "[" + getStatusIcon() + "] " + description; + } + + public String getTypeIcon(){return null;} + + public String getTime(){return null;} + + +} diff --git a/src/main/java/task/Todo.java b/src/main/java/task/Todo.java new file mode 100644 index 000000000..c3f982cd3 --- /dev/null +++ b/src/main/java/task/Todo.java @@ -0,0 +1,15 @@ +package task; + +public class Todo extends Task{ + + public Todo(String description){ + super(description); + } + + public String toString(){ + return "[T]"+super.toString(); + } + + public String getTypeIcon(){return "T";} + +} diff --git a/text-ui-test/runtest.bat b/text-ui-test/runtest.bat index 087374464..c8e451714 100644 --- a/text-ui-test/runtest.bat +++ b/text-ui-test/runtest.bat @@ -15,7 +15,7 @@ IF ERRORLEVEL 1 ( REM no error here, errorlevel == 0 REM 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 +java -classpath ..\bin controller.TaskManager < input.txt > ACTUAL.TXT REM compare the output to the expected output FC ACTUAL.TXT EXPECTED.TXT