Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Izdiyad] iP #184

Open
wants to merge 30 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
0ae4476
Level 0: Greet
izdiyadfrhn Aug 19, 2021
f1e5ced
Level-1
izdiyadfrhn Aug 24, 2021
3414427
Add, List
izdiyadfrhn Aug 24, 2021
73e3f7f
Mark as Done
izdiyadfrhn Aug 24, 2021
3422f00
Follow the Coding Standard
izdiyadfrhn Aug 26, 2021
875b0d9
ToDos, Events, Deadlines
izdiyadfrhn Sep 1, 2021
8ee920b
A-TextUiTesting
izdiyadfrhn Sep 1, 2021
d053ba6
Improve code quality
izdiyadfrhn Sep 1, 2021
8ffc16c
Handle Errors
izdiyadfrhn Sep 5, 2021
87960ac
Merge branch 'branch-Level-5'
izdiyadfrhn Sep 6, 2021
01d5ff9
Organize into packages
izdiyadfrhn Sep 6, 2021
b95926a
Merge branch 'branch-A-Packages'
izdiyadfrhn Sep 6, 2021
eb20187
Add support for deleting tasks from the list.
izdiyadfrhn Sep 12, 2021
d2b41c7
Save the tasks in the hard disk automatically whenever the task list …
izdiyadfrhn Sep 12, 2021
a808481
Merge branch 'branch-Level-7'
izdiyadfrhn Sep 12, 2021
cd1002c
Attempt to solve merge conflict from branches level 6 and 7
izdiyadfrhn Sep 12, 2021
67b4f6d
Use More OOP
izdiyadfrhn Sep 17, 2021
5d454d8
Give users a way to find a task by searching for a keyword.
izdiyadfrhn Sep 18, 2021
236ed11
Add JavaDoc comments to the code.
izdiyadfrhn Sep 18, 2021
6f88ba0
Merge pull request #1 from izdiyadfrhn/branch-Level-9
izdiyadfrhn Sep 18, 2021
873adc0
Merge branch 'master' of https://github.com/izdiyadfrhn/ip
izdiyadfrhn Sep 18, 2021
f36e778
Merge branch 'master' into branch-A-JavaDoc
izdiyadfrhn Sep 18, 2021
b03f960
Merge branch 'branch-A-JavaDoc'
izdiyadfrhn Sep 18, 2021
cac1c93
Changed task manager's name
izdiyadfrhn Sep 19, 2021
2a1bdf2
Reviewed code after comments from TA
izdiyadfrhn Sep 19, 2021
5fb40cf
renamed some files
izdiyadfrhn Sep 19, 2021
8a45ea6
Set theme jekyll-theme-slate
izdiyadfrhn Sep 19, 2021
1c71c8a
Add a User Guide to the project
izdiyadfrhn Sep 19, 2021
acf3a4d
Merge branch 'master' of https://github.com/izdiyadfrhn/ip
izdiyadfrhn Sep 19, 2021
7a0c133
Minor UI and UG fixes
izdiyadfrhn Sep 20, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions duke_data/duke.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
T | 0 | buy McD
T | 1 | iP submission
T | 0 | match
10 changes: 0 additions & 10 deletions src/main/java/Duke.java

This file was deleted.

3 changes: 3 additions & 0 deletions src/main/java/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Manifest-Version: 1.0
Main-Class: duke.Duke

57 changes: 57 additions & 0 deletions src/main/java/duke/Duke.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package duke;

import duke.command.Command;
import duke.exceptions.DukeException;

public class Duke {

private Ui ui;
private Storage storage;
private Parser parser;
private TaskList tasks;

/**
* Duke constructor which initialises the User Interface(UI), Storage, Parser and TaskList.
* @param filePath file path of duke_data, which stores data of Duke
*/
public Duke(String filePath) {
ui = new Ui();
storage = new Storage(filePath, ui);

try {
tasks = new TaskList(storage.readFile());
} catch (DukeException e) {
ui.printWithLines(e.toString());
tasks = new TaskList();
}

parser = new Parser(ui, storage, tasks);
}

/** Reads user command and executes it until the exit command is entered by the user. */
public void run() {
ui.printHelloMessage();

String input;

input = ui.getUserCommand();

while (true) {
try {
Command userCommand = parser.selectCommand(input);
userCommand.execute();
storage.saveFile(tasks);
} catch (DukeException e) {
ui.printWithLines(e.getMessage());
}

// Gets the next command entered

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code is well structured and easy to understand what the code is doing here. Hence, this comment seems redundant. You may want to consider removing it :)

input = ui.getUserCommand();
}
}

public static void main(String[] args) {
new Duke("duke_data/store.txt").run();
}

}
171 changes: 171 additions & 0 deletions src/main/java/duke/Parser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
package duke;

import duke.command.*;
import duke.tasks.Task;
import duke.tasks.ToDo;
import duke.tasks.Deadline;
import duke.tasks.Event;
import duke.exceptions.DukeException;

public class Parser {

protected Ui ui;
protected Storage storage;
protected TaskList tasks;
Task newTask;

public static final String TODO = "todo";
public static final String TODO_EXCEPTION = "Description of todo cannot be empty.";
public static final String DEADLINE = "deadline";
public static final String DEADLINE_EXCEPTION = "Deadline task requires a /by property.";
public static final String DEADLINE_EMPTY_DESCRIPTION = " /by property cannot be empty.";
public static final String EVENT = "event";
public static final String EVENT_EXCEPTION = "Event task requires an /at property.";
public static final String EVENT_EMPTY_DESCRIPTION = " /at property cannot be empty.";
public static final String INVALID_TASK_TYPE = "Invalid task type. Try a todo, deadline or event.";
public static final String LIST = "list";
public static final String DONE = "done";
public static final String FIND = "find";
public static final String DELETE = "delete";
public static final String BYE = "bye";
public static final String INVALID_INPUT = "Your input is invalid.";


/**
* Parser constructor with its associated UI, Storage and TaskList
* @param ui UI to be used by Parser
* @param storage Storage to be used by Parser
* @param tasks TaskList to be used by Parser
*/
public Parser(Ui ui, Storage storage, TaskList tasks) {
this.ui = ui;
this.storage = storage;
this.tasks = tasks;
}

/**
* Parses user's raw input and initiates a new task to be added according to its type.
* @param input string of user's raw input
*/
public void trimInput(String input) {

if (input.startsWith(DEADLINE)) {
String description =
input.substring(0, input.indexOf("/by")).replaceFirst(DEADLINE, "").trim();
String by = input.substring(input.indexOf("/by") + "/by".length()).trim();
newTask = new Deadline(description, by);

} else if (input.startsWith(EVENT)) {
String description =
input.substring(0, input.indexOf("/at")).replaceFirst(EVENT, "").trim();
String at = input.substring(input.indexOf("/at") + "/at".length()).trim();
newTask = new Event(description, at);

} else if (input.startsWith(TODO)) {
String description = input.replaceFirst(TODO, "").trim();
newTask = new ToDo(description);
}
}

/**
* Dissects user's raw input and returns an appropriate AddCommand object.
* @param input string of user's raw input
* @return an appropriate AddCommand object
* @throws DukeException
*/
public Command addTask(String input) throws DukeException {

if (input.startsWith(TODO)) {
if (input.substring(4).isEmpty()) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 seems to be a Magic Number

throw new DukeException(TODO_EXCEPTION);
}
trimInput(input);

} else if (input.startsWith(DEADLINE)) {
if (!input.contains("/by")) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can also consider making "/by" a constant just like DEADLINE

throw new DukeException(DEADLINE_EXCEPTION);
} else if (input.substring(input.indexOf("/by") + "/by".length()).isEmpty()) {
throw new DukeException(DEADLINE_EMPTY_DESCRIPTION);
}
trimInput(input);

} else if (input.startsWith(EVENT)) {
if (!input.contains("/at")) {
throw new DukeException(EVENT_EXCEPTION);
} else if (input.substring(input.indexOf("/at") + "/at".length()).isEmpty()) {
throw new DukeException(EVENT_EMPTY_DESCRIPTION);
}
trimInput(input);

} else {
throw new DukeException(INVALID_TASK_TYPE);
}

return new AddCommand(ui, tasks, newTask);
}

/**
* Dissects user's raw input and returns an appropriate SetDoneCommand
* @param input string of user's raw input
* @return a SetDoneCommand object
*/
public Command markTaskDone(String input) {
int taskNumber = Integer.parseInt(input.replace("done ", "")) - 1;

return new SetDoneCommand(ui, tasks, taskNumber);
}

/**
* Dissects user's raw input and returns an appropriate DeleteCommand
* @param input string of user's raw input
* @return a DeleteCommand object
*/
public Command deleteTask(String input) {
int taskNumber = Integer.parseInt(input.replaceFirst("delete", "").trim()) - 1;

return new DeleteCommand(ui, tasks, taskNumber);
}

/**
* Dissects user's raw input beforehand and then calls appropriate command methods.
* @param input string of user's raw input
* @return a Command object
* @throws DukeException
*/
public Command findTask(String input) {
String keyword = input.replaceFirst("find", "").trim();

return new FindCommand(ui, tasks, keyword);
}

public Command selectCommand(String input) throws DukeException {

String trimmedInput = input.trim().split(" ")[0];
Command commandFromInput = null;

switch (trimmedInput){
case TODO: case DEADLINE: case EVENT:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When you have multiple cases and you want the control to cascade down, you can still follow the same recommended code style to have each case in a new line.

commandFromInput = addTask(input);
break;
case LIST:
commandFromInput = new ListCommand(ui, tasks);
break;
case DONE:
commandFromInput = markTaskDone(input);
break;
case FIND:
commandFromInput = findTask(input);
break;
case DELETE:
commandFromInput = deleteTask(input);
break;
case BYE:
ui.printByeMessage();
System.exit(0);
break;
default:
throw new DukeException(INVALID_INPUT);
}
return commandFromInput;
}
}
118 changes: 118 additions & 0 deletions src/main/java/duke/Storage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package duke;

import duke.tasks.Task;
import duke.tasks.ToDo;
import duke.tasks.Deadline;
import duke.tasks.Event;
import duke.exceptions.DukeException;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.FileNotFoundException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

import java.util.Scanner;

public class Storage {

protected Ui ui;
private final Path FILE_PATH;

/**
* Storage constructor with its associated UI and file path.
* @param filePath file path to be used by Storage
* @param ui UI to be used by Storage
*/
public Storage(String filePath, Ui ui) {
this.ui = ui;
FILE_PATH = Paths.get("duke_data/duke.txt");
}

/**
* Reads storage file, decodes encoded TaskList written in that file.
* @return a TaskList object
* @throws DukeException
*/
public TaskList readFile() throws DukeException {
TaskList readTasks = new TaskList(100);

try {
File f = new File(FILE_PATH.toString());
Scanner s = new Scanner(f);

while (s.hasNext()) {
Task t;
String line = s.nextLine();
String[] inputArray = line.split(" [ | ] ");

switch (inputArray[0]) {
case "T":
t = new ToDo(inputArray[2]);
break;
case "D":
t = new Deadline(inputArray[2], inputArray[3]);
break;
case "E":
t = new Event(inputArray[2], inputArray[3]);
break;
default:
throw new DukeException("Error while parsing");
}

if (inputArray[1].equals("1")) {
t.setDone();
}

readTasks.add(t);
}

return readTasks;

} catch (FileNotFoundException e) {
try {
createFile();
} catch (IOException o) {
throw new DukeException("Error while reading file.");
}
}
return null;
}

/**
* Encodes and saves a TaskList. Overwrites previously saved file
* @param tasks a list of tasks to be encoded and saved
* @throws DukeException
*/
public void saveFile(TaskList tasks) throws DukeException {
try {
FileWriter fw = new FileWriter(FILE_PATH.toString());
String newText = "";

for (int i = 0; i < tasks.size(); i++) {
Task t = tasks.get(i);
newText = newText.concat(t.encode());
newText = newText.concat("\n");
}

fw.write(newText);
fw.close();

} catch (IOException e) {
throw new DukeException("IO exception");
}
}

/**
* Creates a storage file if there is none existing. Also creates a directory if path is non-existing.
* @throws IOException
*/
public void createFile() throws IOException {
// from https://stackoverflow.com/questions/2833853/create-whole-path-automatically-when-writing-to-a-new-file
Files.createDirectories(FILE_PATH.getParent());
Files.createFile(FILE_PATH);
ui.printWithLines("New storage file created.");
}
}
Loading