-
Notifications
You must be signed in to change notification settings - Fork 438
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
[Foo Jing Yi] iP #449
base: master
Are you sure you want to change the base?
[Foo Jing Yi] iP #449
Changes from 58 commits
3b19ba1
77511f4
6c96bd9
2777c68
09e2eb0
52872bf
e8d0b49
8cd1a6a
8e532e7
2f575a1
3d27ce2
bfad948
a12d247
33bf0df
c7910a9
bfb4cbd
d68bc56
6139e1e
86f01bb
ebd9ad3
0a72af0
3381f40
839111a
de39330
758a8e4
2a5ce27
04e0bbe
086487c
f677798
b285f63
729fbcf
9cc342e
4435cac
e724755
1e93d96
a1711a0
eb41a03
692d003
400181a
4641470
0e2d60c
02060a6
c217a62
0862515
7abd6e1
5865350
9c00f72
3ebac0e
7377d8f
674b42c
5cd15c7
f641c4d
517e09c
d084ede
78ed732
1a83d10
8b61904
c095ea5
3d4e0dd
d7fad1c
1cfde58
f34acd0
5116894
cef2839
568289c
2b9c430
01d7af9
43c1fa3
2f9cd8a
9182cb9
c8e9f16
b414ce2
9f1450c
2a5b62b
ee160bf
10a3091
d6fec0d
740f1eb
21f3d42
7298c01
9745094
b819617
f280b79
b0f45d7
314b8a6
a0e1081
5ce9a68
523f0c3
6c97df7
a6a88dd
487312c
528ac62
ae1e474
33db2f0
cca9d95
019a2e6
b535320
4037a86
70f88c2
b85a7da
b47957c
86334ec
b545ebc
c8beb1f
215ce02
907230e
ee28f71
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,3 +15,6 @@ bin/ | |
|
||
/text-ui-test/ACTUAL.txt | ||
text-ui-test/EXPECTED-UNIX.TXT | ||
duke.txt | ||
tasks.txt | ||
MANIFEST.MF |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
package duke; | ||
|
||
import duke.command.Command; | ||
import duke.command.Parser; | ||
import duke.dukeexception.DukeException; | ||
|
||
/** | ||
* The Duke program is an interactive bot that offers commands to help the | ||
* user keep track of a mutable list of tasks that can be of 3 types: | ||
* <code>Todo</code>, <code>Deadline</code> or <code>Event</code>. | ||
* These tasks can also be marked as done and will be saved in the hard disk. | ||
* | ||
* @author Foo Jing Yi | ||
*/ | ||
public class Duke { | ||
/** Storage object used by Duke to load from and write to hard disk */ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps you can move the description to the class file itself? To reduce clutter |
||
private Storage storage; | ||
/** TaskList object that contains the list of tasks */ | ||
private TaskList tasks; | ||
/** Ui object that deals with interactions with the user */ | ||
private Ui ui; | ||
|
||
/** | ||
* Public class constructor that takes in the location of a file as a string | ||
* indicating the relative file path. | ||
* The list of tasks will be loaded from and saved to this file. | ||
* | ||
* @param pathFile Relative file path. | ||
*/ | ||
public Duke(String pathFile) { | ||
this.ui = new Ui(); | ||
this.storage = new Storage(pathFile); | ||
try { | ||
this.tasks = new TaskList(storage.load()); | ||
} catch (DukeException e) { | ||
// todo | ||
this.tasks = new TaskList(); | ||
} | ||
} | ||
|
||
/** | ||
* Runs the bot. | ||
*/ | ||
public void run() { | ||
ui.showWelcomeMsg(); | ||
boolean isExit = false; | ||
while (!isExit) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe instead of declaring isExit, how about while(True)... and break/return when condition is reached? |
||
try { | ||
String fullCommand = this.ui.readCommand(); | ||
this.ui.showLine(); | ||
Command command = Parser.parse(fullCommand); | ||
command.execute(tasks, storage); | ||
isExit = command.isExit(); | ||
} catch (DukeException e) { | ||
ui.showError(e.getMessage()); | ||
} finally { | ||
this.ui.showLine(); | ||
} | ||
} | ||
} | ||
|
||
public static void main(String[] args) { | ||
new Duke("data/tasks.txt").run(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,201 @@ | ||
package duke; | ||
|
||
import duke.dukeexception.DukeException; | ||
import duke.task.Deadline; | ||
import duke.task.Event; | ||
import duke.task.Task; | ||
import duke.task.Todo; | ||
|
||
import java.io.File; | ||
import java.io.FileNotFoundException; | ||
import java.io.FileWriter; | ||
import java.io.IOException; | ||
|
||
import java.time.LocalDateTime; | ||
import java.time.format.DateTimeFormatter; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.Scanner; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like how the import statements are separated based on their association |
||
|
||
/** | ||
* Represents the hard disk storage used by the bot. | ||
* While this class does not store the data within the class objects, | ||
* each <code>Storage</code> object keeps a link to an actual file where | ||
* data is stored, and handles loading and writing to this file. | ||
*/ | ||
public class Storage { | ||
/** File where data is stored */ | ||
private final File taskFile; | ||
|
||
/** | ||
* Public constructor. | ||
* | ||
* @param filePath Relative file path. | ||
*/ | ||
public Storage(String filePath) { | ||
this.taskFile = new File(filePath); | ||
} | ||
|
||
/** | ||
* Loads list of tasks from <code>taskFile</code>. | ||
* | ||
* @return List of tasks to be passed to a <code>TaskList</code> object | ||
* @throws DukeException If file cannot be created, read or parsed. | ||
*/ | ||
public List<Task> load() throws DukeException { | ||
if (!this.taskFile.exists()) { | ||
File dir = this.taskFile.getParentFile(); | ||
if (dir != null && !dir.exists()) { | ||
dir.mkdirs(); | ||
} | ||
|
||
try { | ||
this.taskFile.createNewFile(); | ||
} catch (IOException e) { | ||
e.printStackTrace(); // todo | ||
} | ||
} | ||
|
||
Scanner sc = null; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it be better to just declare sc without the null initialisation? |
||
try { | ||
sc = new Scanner(this.taskFile); | ||
} catch (FileNotFoundException e) { | ||
e.printStackTrace(); // todo | ||
} | ||
|
||
if (sc != null) { | ||
List<Task> list = new ArrayList<>(); | ||
while (sc.hasNext()) { | ||
String storedTask = sc.nextLine(); | ||
Task task = parseFromStorage(storedTask); | ||
list.add(task); | ||
} | ||
return list; | ||
} else { | ||
return new ArrayList<Task>(); //todo | ||
} | ||
} | ||
|
||
/** | ||
* Updates file by added a new task. | ||
* | ||
* @param task Task to be added to file. | ||
* @throws DukeException If task cannot be parsed or file cannot be written to. | ||
*/ | ||
public void update(Task task) throws DukeException { | ||
try { | ||
FileWriter fileWriter = new FileWriter(this.taskFile, true); | ||
fileWriter.write("\n" + parseToStorage(task)); | ||
fileWriter.close(); | ||
} catch (IOException e) { | ||
e.printStackTrace(); | ||
} | ||
} | ||
|
||
/** | ||
* Updates file with list when tasks are marked or deleted. | ||
* | ||
* @param list Updated list given by <code>TaskList</code> object. | ||
* @throws DukeException If tasks cannot be parsed or file cannot be written to. | ||
*/ | ||
public void update(List<Task> list) throws DukeException { | ||
try { | ||
FileWriter fileWriter = new FileWriter(this.taskFile); | ||
String fileContents = parseToStorage(list.get(0)); | ||
|
||
for (int i = 1; i < list.size(); i++) { | ||
fileContents += "\n" + parseToStorage(list.get(i)); | ||
} | ||
|
||
fileWriter.write(fileContents); | ||
fileWriter.close(); | ||
} catch (IOException e) { | ||
e.printStackTrace(); // todo | ||
} | ||
} | ||
|
||
/** | ||
* Parses <code>Task</code> objects into strings to be written to the | ||
* file on the hard disk. | ||
* | ||
* @param task <code>Task</code> object to be parsed | ||
* @return String representation of the task. Note that this string is | ||
* different from the string returned by <code>task.toString()</code>. | ||
* e.g. "[T][✓] Homework" from <code>task.toString()</code> will be | ||
* represented as "T | 1 | Homework". | ||
* @throws DukeException If the type of the task cannot be recognised. | ||
*/ | ||
protected String parseToStorage(Task task) throws DukeException { | ||
String taskType = ""; | ||
String status = task.isDone() ? "1" : "0"; // 1 for done and 0 for not done | ||
String taskDescription = ""; | ||
|
||
if (task instanceof Todo) { | ||
taskType = "T"; | ||
taskDescription = task.getTaskName(); | ||
} else if (task instanceof Deadline) { | ||
taskType = "D"; // + date | ||
taskDescription = task.getTaskName() + | ||
" | " + | ||
((Deadline) task).getDateTime().format( | ||
DateTimeFormatter.ofPattern("d MMM yyyy, h.m a")); | ||
} else if (task instanceof Event) { | ||
taskType = "E"; | ||
taskDescription = task.getTaskName() + | ||
" | " + | ||
((Event) task).getDateTime().format( | ||
DateTimeFormatter.ofPattern("d MMM yyyy, h.m a")); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps you can abstract out the common code |
||
} else { | ||
throw new DukeException("Cannot recognise type"); | ||
} | ||
|
||
return taskType + | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe it's more readable to put it in a single line instead |
||
" | " + | ||
status + | ||
" | " + | ||
taskDescription; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps place the operator as part of the next line for ease of reading (easily shows that the line is part of the previous)? |
||
} | ||
|
||
/** | ||
* Parses string representation of task stored in the file on the hard disk | ||
* to create a <code>Task</code> object. | ||
* | ||
* @param storedTask String representation of the task | ||
* @return Task represented by the string input. | ||
* @throws DukeException If the string is in the wrong format. | ||
*/ | ||
protected Task parseFromStorage(String storedTask) throws DukeException { | ||
String[] taskElements = storedTask.split(" \\| ", 4); | ||
|
||
try { | ||
Task task = null; | ||
if (storedTask.charAt(0) == 'T') { | ||
task = new Todo(taskElements[2]); | ||
} else if (storedTask.charAt(0) == 'D') { | ||
String taskName = taskElements[2]; | ||
LocalDateTime dateTime = LocalDateTime.parse(taskElements[3], | ||
DateTimeFormatter.ofPattern("d MMM yyyy, h.m a")); | ||
task = new Deadline(taskName, dateTime); | ||
} else if (storedTask.charAt(0) == 'E') { | ||
String taskName = taskElements[2]; | ||
LocalDateTime dateTime = LocalDateTime.parse(taskElements[3], | ||
DateTimeFormatter.ofPattern("d MMM yyyy, h.m a")); | ||
task = new Event(taskName, dateTime); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps this (and other similar chunks of code) should be broken up into distinct logic units with comments to describe what they do? |
||
} | ||
|
||
if (taskElements[1].equals("1")) { | ||
task.markDone(); | ||
} | ||
|
||
if (task != null) { | ||
return task; | ||
} else { | ||
throw new DukeException("Cannot read tasks from file."); | ||
} | ||
} catch (Exception e) { | ||
System.out.println(e); | ||
throw new DukeException("Cannot read tasks from file."); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like how you summarised the functionality of Duke concisely 👍