From 72bd9de6489a1d6764056881098f7896e3d2d5d5 Mon Sep 17 00:00:00 2001 From: Mohideen Imran Khan Date: Sun, 29 Sep 2019 18:51:58 +0800 Subject: [PATCH] Refactor TimeFrame class (#42) * Reimplement TimeFrameClass * Refactor ViewScheduleCommand * Allow for instantaneous tasks * Implement printing of schedule --- .../logic/commands/ViewScheduleCommand.java | 35 +++--- src/main/java/duchess/model/TimeFrame.java | 107 ++++++++++++------ .../java/duchess/model/task/Deadline.java | 18 +-- src/main/java/duchess/model/task/Event.java | 29 +---- src/main/java/duchess/model/task/Task.java | 20 ++-- .../java/duchess/model/task/TaskList.java | 2 - src/main/java/duchess/model/task/Todo.java | 15 +-- src/main/java/duchess/ui/Ui.java | 55 ++------- src/test/java/ViewScheduleCommandTest.java | 24 ---- 9 files changed, 112 insertions(+), 193 deletions(-) diff --git a/src/main/java/duchess/logic/commands/ViewScheduleCommand.java b/src/main/java/duchess/logic/commands/ViewScheduleCommand.java index 9db9071c11..f7d7d25a1e 100644 --- a/src/main/java/duchess/logic/commands/ViewScheduleCommand.java +++ b/src/main/java/duchess/logic/commands/ViewScheduleCommand.java @@ -9,16 +9,13 @@ import java.text.ParseException; import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Collections; import java.util.Date; import java.util.List; +import java.util.stream.Collectors; public class ViewScheduleCommand extends Command { private List words; - private List schedules; - private Date start; - private Date end; + private TimeFrame timeFrame; /** * Constructor for class. @@ -28,9 +25,10 @@ public class ViewScheduleCommand extends Command { */ public ViewScheduleCommand(List words) throws DukeException { this.words = words; - this.schedules = new ArrayList<>(); - this.start = returnDate(" 0000"); - this.end = returnDate(" 2359"); + + Date start = processDate(" 0000"); + Date end = processDate(" 2359"); + this.timeFrame = new TimeFrame(start, end); } /** @@ -39,7 +37,7 @@ public ViewScheduleCommand(List words) throws DukeException { * @return Date * @throws DukeException Exception thrown for invalid or missing date time */ - private Date returnDate(String time) throws DukeException { + private Date processDate(String time) throws DukeException { try { // todo fix bug which allows input 'schedule 12/12/2019' without /for String dateString = words.get(words.indexOf("/for") + 1) + time; @@ -55,17 +53,16 @@ private Date returnDate(String time) throws DukeException { @Override public void execute(TaskList taskList, Ui ui, Storage storage) throws DukeException { - for (Task t : taskList.getTasks()) { - TimeFrame tempSchedule = t.getTimeFrame(start, end); - if (tempSchedule != null) { - (this.schedules).add(tempSchedule); - } - } - if (schedules.size() <= 0) { + List tasksForToday = + taskList.getTasks().stream() + .filter(task -> task.getTimeFrame().fallsWithin(this.timeFrame)) + .collect(Collectors.toList()); + + if (tasksForToday.size() <= 0) { throw new DukeException("There are no tasks in the schedule."); - } else { - Collections.sort(schedules); - ui.showScheduleResult(schedules, words.get(words.indexOf("/for") + 1)); } + + tasksForToday.sort((a, b) -> a.getTimeFrame().compareTo(b.getTimeFrame())); + ui.showScheduleResult(tasksForToday, words.get(words.indexOf("/for") + 1)); } } diff --git a/src/main/java/duchess/model/TimeFrame.java b/src/main/java/duchess/model/TimeFrame.java index 6ce7510783..c2aeadff82 100644 --- a/src/main/java/duchess/model/TimeFrame.java +++ b/src/main/java/duchess/model/TimeFrame.java @@ -1,63 +1,98 @@ package duchess.model; -import java.text.SimpleDateFormat; +import duchess.logic.commands.exceptions.DukeException; + import java.util.Date; public class TimeFrame implements Comparable { - private Date time; - private String task; - private boolean isOngoing; + /** + * Start and end points of the timeframe. + */ + private Date start; + private Date end; /** - * Constructor for schedule. - * - * @param time time to be shown in timetable - * @param toString task details + * Marks timeframe as indefinite, i.e. things that + * don't have a definite start or end time. */ - public TimeFrame(Date time, String toString) { - this.time = time; - this.task = toString; - this.isOngoing = false; - } + private boolean isIndefinite; /** - * Constructor for schedule. Tracks ongoing events. + * Marks time frame as instantaneous. + */ + private boolean isInstantaneous; + + /** + * Creates a TimeFrame that represents an interval in time. * - * @param time time to be shown in timetable - * @param toString task details - * @param ongoing event ongoing + * @param start Starting time + * @param end Ending time */ - public TimeFrame(Date time, String toString, boolean ongoing) { - this.time = time; - this.task = toString; - this.isOngoing = ongoing; + public TimeFrame(Date start, Date end) { + this.start = start; + this.end = end; + this.isIndefinite = false; + this.isInstantaneous = false; + } + + private TimeFrame(Date time) { + this.start = time; + this.end = time; + this.isIndefinite = false; + this.isInstantaneous = true; } - public Date getStart() { - return time; + private TimeFrame() { + this.isIndefinite = true; + this.isInstantaneous = false; + } + + public static TimeFrame ofInstantaneousTask(Date time) { + return new TimeFrame(time); + } + + public static TimeFrame ofTimelessTask() { + return new TimeFrame(); } /** - * Changes Date to String. + * Returns true if this TimeFrame lies within the other TimeFrame. * - * @return string containing time of task + * @param that the other TimeFrame */ - public String getStartString() { - SimpleDateFormat formatter = new SimpleDateFormat("HHmm"); - formatter.setLenient(false); - return formatter.format(time); - } + public boolean fallsWithin(TimeFrame that) { + if (this.isIndefinite || that.isIndefinite) { + return false; + } - public String getTask() { - return task; + return !(this.end.before(that.start) || that.end.before(this.start)); } - public boolean getOngoing() { - return isOngoing; + /** + * Returns true if this TimeFrame clashes with the other TimeFrame. + * + * @param that the other TimeFrame + */ + public boolean clashesWith(TimeFrame that) { + if (this.isInstantaneous || that.isInstantaneous) { + return false; + } + + return this.fallsWithin(that); } @Override - public int compareTo(TimeFrame timeFrame) { - return time.compareTo(timeFrame.time); + public int compareTo(TimeFrame that) { + if (this.isIndefinite && that.isIndefinite) { + return 0; + } else if (this.isIndefinite) { + return -1; + } else if (that.isIndefinite) { + return 1; + } else if (!this.start.equals(that.start)) { + return this.start.compareTo(that.start); + } else { + return this.end.compareTo(that.end); + } } } diff --git a/src/main/java/duchess/model/task/Deadline.java b/src/main/java/duchess/model/task/Deadline.java index 3e2f00b3a4..2ece26d116 100644 --- a/src/main/java/duchess/model/task/Deadline.java +++ b/src/main/java/duchess/model/task/Deadline.java @@ -58,27 +58,13 @@ public List getReminders() { return list; } - @Override - public List getClashables() { - return new ArrayList<>(); - } - - @Override - public boolean clashesWith(Task task) { - return false; - } - @Override public String toString() { return String.format("[D]%s %s (by: %s)", super.toString(), this.description, formatter.format(this.deadline)); } @Override - public TimeFrame getTimeFrame(Date startDate, Date endDate) { - if (deadline.compareTo(startDate) >= 0 && deadline.compareTo(endDate) <= 0) { - return new TimeFrame(deadline, String.format("[D]%s %s", super.toString(), this.description)); - } - return null; + public TimeFrame getTimeFrame() { + return TimeFrame.ofInstantaneousTask(this.deadline); } - } diff --git a/src/main/java/duchess/model/task/Event.java b/src/main/java/duchess/model/task/Event.java index 354db83040..b9b5e4ec22 100644 --- a/src/main/java/duchess/model/task/Event.java +++ b/src/main/java/duchess/model/task/Event.java @@ -47,18 +47,6 @@ public Event(List input) throws DukeException { } } - /** - * Checks if the task being added clashes with this instance of event. - * - * @param task the task to be added - * @return true if the event clashes, false otherwise - */ - @Override - public boolean clashesWith(Task task) { - return (startClashes((Event) task) || endClashes((Event) task) - || entireEventClashes((Event) task) || isStartOrEndEqual((Event) task)); - } - private boolean startClashes(Event event) { return event.start.after(this.start) && event.start.before(this.end); } @@ -98,13 +86,6 @@ public List getReminders() { return new ArrayList<>(); } - @Override - public List getClashables() { - List list = new ArrayList<>(); - list.add(this); - return list; - } - @Override public String toString() { return String.format("[E]%s %s (at: %s to %s)", super.toString(), this.description, @@ -112,13 +93,7 @@ public String toString() { } @Override - public TimeFrame getTimeFrame(Date startDate, Date endDate) { - if (start.compareTo(startDate) < 0 && end.compareTo(startDate) >= 0) { // starts before ends after that date - return new TimeFrame(start, String.format("[E]%s %s (at: %s to %s)", super.toString(), this.description, - formatter.format(this.start), formatter.format(this.end)),true); - } else if (start.compareTo(startDate) >= 0 && start.compareTo(endDate) <= 0) { // starts during date - return new TimeFrame(start, String.format("[E]%s %s", super.toString(), this.description)); - } - return null; + public TimeFrame getTimeFrame() { + return new TimeFrame(this.start, this.end); } } diff --git a/src/main/java/duchess/model/task/Task.java b/src/main/java/duchess/model/task/Task.java index 8eccf83620..c737a4cb5a 100644 --- a/src/main/java/duchess/model/task/Task.java +++ b/src/main/java/duchess/model/task/Task.java @@ -4,10 +4,9 @@ import duchess.model.TimeFrame; import java.io.Serializable; -import java.util.Date; import java.util.List; -public abstract class Task implements Serializable { +public abstract class Task implements Serializable, Comparable { private boolean isDone; public Task() { @@ -18,20 +17,25 @@ public void markAsDone() { this.isDone = true; } - public abstract boolean containsKeyword(String keyword); - @Override public String toString() { return "[" + (this.isDone ? "✓" : "✘") + "]"; } - public abstract TimeFrame getTimeFrame(Date startDate, Date endDate); + @Override + public int compareTo(Task that) { + return this.getTimeFrame().compareTo(that.getTimeFrame()); + } + + public final boolean clashesWith(Task that) { + return this.getTimeFrame().clashesWith(that.getTimeFrame()); + } + + public abstract TimeFrame getTimeFrame(); public abstract void snooze() throws DukeException; public abstract List getReminders(); - public abstract List getClashables(); - - public abstract boolean clashesWith(Task task); + public abstract boolean containsKeyword(String keyword); } diff --git a/src/main/java/duchess/model/task/TaskList.java b/src/main/java/duchess/model/task/TaskList.java index 4c2603fe98..d79327fa14 100644 --- a/src/main/java/duchess/model/task/TaskList.java +++ b/src/main/java/duchess/model/task/TaskList.java @@ -40,8 +40,6 @@ public void delete(int i) { */ public boolean isClashing(Task newTask) { return this.getTasks().stream() - .map(Task::getClashables) - .flatMap(Collection::stream) .anyMatch(task -> task.clashesWith(newTask)); } } diff --git a/src/main/java/duchess/model/task/Todo.java b/src/main/java/duchess/model/task/Todo.java index 4e80e12e19..1b3be26b4e 100644 --- a/src/main/java/duchess/model/task/Todo.java +++ b/src/main/java/duchess/model/task/Todo.java @@ -4,7 +4,6 @@ import duchess.model.TimeFrame; import java.util.ArrayList; -import java.util.Date; import java.util.List; public class Todo extends Task { @@ -34,8 +33,8 @@ public String toString() { } @Override - public TimeFrame getTimeFrame(Date startDate, Date endDate) { - return null; + public TimeFrame getTimeFrame() { + return TimeFrame.ofTimelessTask(); } @Override @@ -47,14 +46,4 @@ public void snooze() throws DukeException { public List getReminders() { return new ArrayList<>(); } - - @Override - public List getClashables() { - return new ArrayList<>(); - } - - @Override - public boolean clashesWith(Task task) { - return false; - } } diff --git a/src/main/java/duchess/ui/Ui.java b/src/main/java/duchess/ui/Ui.java index 5fe0dec6ab..9a83fe1f06 100644 --- a/src/main/java/duchess/ui/Ui.java +++ b/src/main/java/duchess/ui/Ui.java @@ -1,6 +1,5 @@ package duchess.ui; -import duchess.model.TimeFrame; import duchess.model.task.Task; import java.util.List; @@ -91,61 +90,21 @@ public void showSearchResult(List tasks) { showTasks(tasks); } - private String taskPadding(String task) { // total 58 spaces - if (task.length() > 30) { - task = task.substring(0, 30); - task += "..."; - } - int taskLength = task.length(); - StringBuilder taskBuilder = new StringBuilder(); - for (int i = 0; i < Math.ceil((39 - taskLength) / (double) 2); i++) { - taskBuilder.append(" "); - } - taskBuilder.append(task); - for (int i = 0; i < Math.floorDiv(39 - taskLength, 2); i++) { - taskBuilder.append(" "); - } - taskBuilder.append("|"); - return taskBuilder.toString(); - } - /** * Displays schedule of a single day to user. * Informs user if there are ongoing events. * - * @param schedules Schedule list with start and task details - * @param date Date of choice - */ - public void showScheduleResult(List schedules, String date) { - boolean hasOngoing = false; - printIndented("Here is your schedule:"); - printScheduleRow("|\t\t\t" + date + "\t\t\t|"); // 4 tab + 2 spaces including date - printScheduleRow("|\tTime\t|\t\t Task \t\t|"); - for (TimeFrame s : schedules) { - if (!s.getOngoing()) { - printScheduleRow("|\t" + s.getStartString() + "\t|" + taskPadding(s.getTask())); - } else { - hasOngoing = true; - } - } - printIndented("-----------------------------------------------------"); - if (hasOngoing) { - printIndented("Here are your ongoing tasks:"); - } + * @param tasks List of tasks to show + * @param date Date of choice + */ + public void showScheduleResult(List tasks, String date) { + printIndented("Here is your schedule for " + date + ":"); int counter = 1; - for (TimeFrame s : schedules) { - if (s.getOngoing()) { - printIndented(counter + ". " + s.getTask()); - counter++; - } + for (Task t : tasks) { + printIndented(counter++ + ". " + t.toString()); } } - private void printScheduleRow(String string) { - printIndented("-----------------------------------------------------"); - printIndented(string); - } - /** * Shows the task that was just snoozed. * diff --git a/src/test/java/ViewScheduleCommandTest.java b/src/test/java/ViewScheduleCommandTest.java index 6cb4d296f0..a846bb262d 100644 --- a/src/test/java/ViewScheduleCommandTest.java +++ b/src/test/java/ViewScheduleCommandTest.java @@ -1,37 +1,13 @@ import duchess.logic.commands.ViewScheduleCommand; import duchess.logic.commands.exceptions.DukeException; -import duchess.model.TimeFrame; import org.junit.jupiter.api.Test; -import java.text.ParseException; -import java.text.SimpleDateFormat; import java.util.Arrays; -import java.util.Date; import java.util.List; -import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; public class ViewScheduleCommandTest { - - @Test - void getStartString_returnsCorrectly() throws ParseException { - String dateString = "12/12/2019 1400"; - SimpleDateFormat formatter = new SimpleDateFormat("dd/MM/yyyy HHmm"); - Date time = formatter.parse(dateString); - String toString = "[D][✘] test"; - assertEquals("1400", new TimeFrame(time, toString).getStartString()); - } - - @Test - void getTask_returnsCorrectly() throws ParseException { - String dateString = "12/12/2019 1400"; - SimpleDateFormat formatter = new SimpleDateFormat("dd/MM/yyyy HHmm"); - Date time = formatter.parse(dateString); - String toString = "[D][✘] test"; - assertEquals("[D][✘] test", new TimeFrame(time, toString).getTask()); - } - @Test void viewScheduleCommand_emptySchedule_dukeExceptionThrown() { List invalidSchedule = Arrays.asList("schedule", "/for", "16/9/");