diff --git a/Collate-TUI.jar b/Collate-TUI.jar
new file mode 100644
index 000000000000..50bdfe6902f8
Binary files /dev/null and b/Collate-TUI.jar differ
diff --git a/build.gradle b/build.gradle
index 32fccab6bc4a..2414c91a4931 100644
--- a/build.gradle
+++ b/build.gradle
@@ -102,7 +102,7 @@ allprojects {
}
shadowJar {
- archiveName = "taskmaster.jar"
+ archiveName = "happyjimtaskmaster.jar"
manifest {
attributes "Main-Class": "seedu.address.MainApp"
diff --git a/collate.bat b/collate.bat
new file mode 100644
index 000000000000..fe9495364100
--- /dev/null
+++ b/collate.bat
@@ -0,0 +1,5 @@
+java -jar Collate-TUI.jar collate from src/main to collated/main include java, fxml, css
+
+java -jar Collate-TUI.jar collate from src/test to collated/test include java
+
+java -jar Collate-TUI.jar collate from docs to collated/docs include md, html
\ No newline at end of file
diff --git a/collated/docs/A0135782Y.md b/collated/docs/A0135782Y.md
new file mode 100644
index 000000000000..dc3e3397bf8b
--- /dev/null
+++ b/collated/docs/A0135782Y.md
@@ -0,0 +1,101 @@
+# A0135782Y
+###### \DeveloperGuide.md
+``` md
+
+```
+###### \DeveloperGuide.md
+``` md
+
+```
+###### \DeveloperGuide.md
+``` md
+The _Sequence Diagram_ below show how recurring tasks are handled when they are first added by the user into Happy Jim Task Master.
+
+
+
+> Note task is a Task reference from the Model and thus any changes made in the RecurringTaskManager will mutate the values of the task.
+
+The _Sequence Diagram_ below show how recurring tasks have dates appended to them every startup of Happy Jim Task Master
+
+
+
+> Note that repeatingTasks is a reference to the UniqueTaskList from the TaskMaster. Any changes made to repeatingTasks in RecurringTaskManager will affect TaskMaster's version of UniqueTaskList.
+```
+###### \DeveloperGuide.md
+``` md
+
+```
+###### \DeveloperGuide.md
+``` md
+ _Unit tests_ targeting the lowest level methods/classes. Below are some snippets,
+
+ _Task.java_
+
+
+ _RecurringTaskManager.java_
+
+
+ _Integration tests_ that are checking the integration of multiple code units
+ (those code units are assumed to be working). Below are some snippets,
+
+ _XmlTaskListStorage.java_
+
+```
+###### \DeveloperGuide.md
+``` md
+ _LogicManagerTest.java_
+
+```
+###### \DeveloperGuide.md
+``` md
+## Appendix E : Product Survey
+Product Name | Strengths | Weaknesses
+---|---|---
+**Remember the Milk**|
Allows for recurring tasks
Allows floating tasks
Allows for location
Allows for estimate
Allows priority
|
Requires an accounr
Not really command line input friendly
Requires internet connection
+**Google Calendar**|
Generally suitable for target audience's requirements
Has a database to store tasks that can be synced
|
Not command line input friendly
Requires internet connection
+**Any.do**|
Can sync across platforms
Provide convenience service for scheduling
|
Not command line input friendly
Requires an account
Requires internet connection`
+**Calendar Iphone App**|
Separates tasks and calendar into one app
Able to add task and tag them
Able to add recurring task
Able to add in tasks to calendar in one line using auto detect
Able to view completed tasks
|
Not really command line input friendly, use touch input heavily
+
+
+```
+###### \UserGuide.md
+``` md
+#### Adding a floating task: `add`
+Adds a task to the todo list
+Format:`add TASK_NAME [t/TAG]...`
+
+Examples:
+* `add Homework`
+* `add Homework t/CS1231`
+
+
+
+#### Adding a task with deadline: `add`
+Format: `add TASK_NAME by DATE TIME [RECURRING_TYPE] [t/TAG]...`
+
+> `RECURRING_TYPE` consists of daily, weekly, monthly and yearly case insensitive.
+> Tasks can have only 1 `RECURRING_TYPE`.
+> If multiple `RECURRING_TYPE` are used, only the first instance will be accepted.
+
+Examples:
+* `add Homework by 24 sep 8pm t/CS1231`
+* `add Homework by 24 sep 6pm daily t/CS1231`
+
+
+
+#### Adding a task with start time and end time: `add`
+Format: `add TASK_NAME from DATE TIME to DATE TIME [RECURRING_TYPE] [t/TAG]...`
+
+> `RECURRING_TYPE` consists of daily, weekly, monthly and yearly case insensitive.
+> Tasks can have only 1 `RECURRING_TYPE`.
+> If multiple `RECURRING_TYPE` are used, only the first instance will be accepted.
+
+Examples:
+* `add Homework from 24 sep 8pm to 25 sep 9pm tag/CS1231`
+* `add Homework from today 8.03pm to today 8.15pm t/CS1231`
+
+
+* `add Homework from 26 oct 10am to 26 oct 11am daily`
+
+
+```
diff --git a/collated/docs/A0135784W.md b/collated/docs/A0135784W.md
new file mode 100644
index 000000000000..01533ca717f1
--- /dev/null
+++ b/collated/docs/A0135784W.md
@@ -0,0 +1,56 @@
+# A0135784W
+###### \UserGuide.md
+``` md
+## HappyJimTaskMaster's GUI
+
+1. Command box. This is where the commands are entered. Simply type in the command and press enter to execute it.
+2. Result display. This is where the results of commands are shown.
+3. Agenda. This where the agenda of this week is shown.
+4. Tasklist panel. This is where the tasks are displayed.
+5. Navigation bar panel. This is where the navigation categories are displayed.
+```
+###### \UserGuide.md
+``` md
+## Features
+### Command Format
+> * Each command consists of a command word (such as add or delete), followed by other options such as DATE,TIME or [t/TAG]
+> * Words in `UPPER_CASE` are the description of what kind data to input.
+> * Items in `SQUARE_BRACKETS` `[items]` are optional.
+> * Items with `...` after them can have multiple instances.
+> * The order of parameters is fixed.
+
+### Date and Time Format
+HappyJimTaskMaster uses Natty date parser to parse date and time options.
+
+Some examples of acceptable format include:
+* 21 nov 2005
+* 24 sep 8pm
+* jan 1st
+* next thursday
+* 3 days from now
+
+For a full list of acceptable formats, please refer to http://natty.joestelmach.com/doc.jsp
+```
+###### \UserGuide.md
+``` md
+## Command Summary
+
+Command | Format
+-------- | :--------
+Add | `add TASK_NAME [t/TAG]...`
+Add | `add TASK_NAME by DATE TIME [RECURRING_TYPE] [t/TAG]...`
+Add | `add TASK_NAME from DATE TIME to DATE TIME [RECURRING_TYPE] [t/TAG]...`
+Edit | `edit TASK_ID [from EDIT_START_DATE EDIT_START_TIME to EDIT_END_DATE EDIT_END_TIME] [by EDIT_END_DATE EDIT_END_TIME] [t/EDIT_TAG]...`
+Delete | `delete TASK_ID`
+Complete | `done TASK_ID`
+Block | `block TASK_NAME from [START_DATE] START_TIME to [START_DATE] START_TIME [t/TAG]...`
+Redo | `r`
+Undo | `u`
+Find | `find [KEY_WORD] [from DATE_TIME to DATE_TIME | by DATE_TIME] [t/TAG]...`
+View | `view DATE [TIME]`
+Clear | `clear`
+Change directory | `cd FILE_PATH`
+Exit | `exit`
+
+-----
+```
diff --git a/collated/docs/A0147967J.md b/collated/docs/A0147967J.md
new file mode 100644
index 000000000000..9d2c9dc8fd6f
--- /dev/null
+++ b/collated/docs/A0147967J.md
@@ -0,0 +1,167 @@
+# A0147967J
+###### \DeveloperGuide.md
+``` md
+The _Sequence Diagram_ below shows how Happy Jim Task Master handles undo request from user.
+
+
+
+> Note that the context is a class that stores previous task master in the previous model before the target command executes.
+
+The _Class Diagram_ below shows the structure of how Happy Jim Task Master implements undo and redo operations.
+
+
+
+> Note that LogicManager maintains an URManager. UR manager contains two ArrayDeque, one for undo and the other for redo,
+> to store the command and its context, specifically, the model before the command executes.
+> To undo/redo a command, it is just to restore the previous model (specifically, the data, which is TaskMaster).
+> As a result, as the task master grows, the consumption of memory to store the context grows.
+> To maintain a good performance regarding to memory consumption, we restrict maximum undo/redo number to 3.
+> (Noted that it is possible to reach unlimited undo/redo by simply wiping off the limit number.)
+
+```
+###### \DeveloperGuide.md
+``` md
+
+1. **GUI Tests** - These are _System Tests_ that test the entire App by simulating user actions on the GUI.
+ These are in the `guitests` package.
+
+ Currently, _Systems Tests_ have covered the basic functionalities of Happy Jim Task Master v0.4.
+ Following form shows the some of the essential commands and corresponding testcases.
+
+ 1. _AddCommandTest_
+
+
+ | Case# | Event | Basis Path | Output |
+ | :---: | --- | --- | --- |
+ | 1 | add floating task to existing task list `add eat with Hoon Meier` | 1 -> 2 | `New floating task added: eat with Hoon Meier Tags: ` |
+ | 2 | add floating task to existing task list `add play with Ida Mueller` | 1 -> 2 | `New floating task added: play with Ida Mueller Tags: ` |
+ | 3 | add duplicate floating task to existing task master `add eat with Hoon Meier` | 1 | `This task already exists in the task list` |
+ | 4 | clear existing task list `clear` | 1 -> 2 | `Task list has been cleared!` |
+ | 5 | add to empty task list `add take trash t/notUrgent` | 1 -> 2 | `New floating task added: take trash Tags: [notUrgent]` |
+ | 6 | invalid add command `adds Johnny` | 1 | `Unknown command` |
+
+ 2. _ClearCommandTest_
+
+
+ | Case# | Event | Basis Path | Output |
+ | :---: | --- | :---: | --- |
+ | 1 | clear existing non-empty task list `clear` | 1 -> 2 | `Task list has been cleared!` |
+ | 2 | verify other commands can work after task list cleared `add eat with Hoon Meier` | 1 -> 2 | `New floating task added: eat with Hoon Meier Tags: ` |
+ | 3 | add duplicate floating task `delete 1` | 1 -> 2| `Deleted Task: eat with Hoon Meier Tags: ` |
+ | 4 | verify clear command works when the list is empty `clear` | 1 -> 2 | `Task list has been cleared!` |
+
+ 3. _CommandBoxTest_
+
+
+ | Case# | Event | Basis Path | Output |
+ | :---: | --- | :---: | --- |
+ | 1 | command succeeds text cleared `add read book t/textBook t/weekly` | 1 -> 2 | `This task already exists in the task list` |
+ | 2 | command fails text stays `invalid command` | 1 | `Unknown Command` |
+
+ 4. _DeleteCommandTest_
+
+
+ | Case# | Event | Basis Path | Output |
+ | :---: | --- | :---: | --- |
+ | 1 | delete the first in the list `delete 1` | 1 -> 2 | `Deleted Task: take trash Tags: [notUrgent]` |
+ | 2 | delete the last in the list `delete 6` | 1 -> 2 | `Deleted Task: visit George Best Tags: ` |
+ | 3 | delete from the middle of the list `delete 2` | 1 -> 2 | `Deleted Task: do homework Tags: ` |
+ | 4 | delete with invalid index `delete 51` | 1 | `The task index provided is invalid` |
+
+ 5. _FindCommandTest_
+
+
+ | Case# | Event | Basis Path | Output |
+ | :---: | --- | :---: | --- |
+ | 1 | find in non-empty list with no results `find Mark` | 1 -> 2 | `0 tasks listed!` |
+ | 2 | find in non-empty list with multiple results `find read` | 1 -> 2 | `2 tasks listed!` |
+ | 3 | delete one result `delete 1` | 1 -> 2 | `Deleted Task: read book Tags: [textBook][weekly]` |
+ | 4 | find in non-empty list with one result `find read` | 1 -> 2 | `1 tasks listed!` |
+ | 5 | find in empty list `find Jean` | 1 -> 2 | `0 tasks listed!` |
+ | 6 | invalid find command `findgeorge` | 1 | `Unknown command` |
+
+```
+###### \DeveloperGuide.md
+``` md
+ Hybrids of unit and integration tests. These test are checking multiple code units as well as
+ how the are connected together. Below are some snippets,
+ e.g. `seedu.taskmaster.logic.LogicManagerTest`
+ In the `LogicManagerTest`, Happy Jim Task Master tests the logic it uses.
+ Typically, Happy Jim Task Master focuses on some boundary tests.
+ e.g. To `find` a task, for instance, `Test Task 1 by 20 oct 11am `,
+ try execute
+ *`find by 20 oct 11am` --> exact boundary, task found;
+ *`find by 20 oct 10.59am` --> smaller boundary, lists nothing;
+ *`find by 20 oct 11.01am` --> lax boundary, task found.
+ > Note that this is a test not merely for `logic`, but also `parser` and `model`.
+```
+###### \UserGuide.md
+``` md
+
+#### Archive completed tasks : `done`
+Format: done TASK_ID
+
+Examples:
+* `done 5`
+
+
+
+
+ >Completed tasks can be viewed from navigation bar on the side.
+
+#### Block out timeslot : `block`
+Format: block from [START_DATE] START_TIME to [START_DATE] START_TIME [t/TAG]
+
+Examples:
+* `block from tomorrow 3pm to tomorrow 5pm t/meeting`
+
+ >
+ >
+
+#### Undo tasks : `undo`
+Format: u
+
+> Maximum 3 undo
+
+Examples:
+* `u`
+
+
+
+
+#### Redo tasks : `redo`
+Format: r
+
+> Maximum 3 redo
+
+Examples:
+* `r`
+
+
+
+
+#### View agenda of a day: `view`
+Format: view DATE [TIME]
+
+Examples:
+* `view next monday`
+
+
+
+
+
+```
+###### \UserGuide.md
+``` md
+
+#### Change directory: `cd`
+Format: cd FILE_PATH
+
+Examples:
+* `cd data\newlist.xml`
+
+
+
+
+
+```
diff --git a/collated/docs/A0147995H.md b/collated/docs/A0147995H.md
new file mode 100644
index 000000000000..7ab7ee23164a
--- /dev/null
+++ b/collated/docs/A0147995H.md
@@ -0,0 +1,72 @@
+# A0147995H
+###### \UserGuide.md
+``` md
+
+#### Edit tasks : `edit`
+Format: `edit TASK_ID [NEW_TASK_NAME] [from DATE_TIME to DATE_TIME | by DATE_TIME [daily | weekly | monthly | yearly] ] [tag/EDIT_TAG]...`
+
+> Every field in edit is optional. After you specify the task that you are going to edit,
+> you are able to change its name, date time and tag.
+> For editing date time of a task, you have the following restrictions:
+> 1. You cannot change a non-floating task to a floating task.
+> 2. You cannot directly change recurring type of a task (need to specify time first).
+
+Examples:
+* `edit 1 cs2103 webcast`
+
+
+* `edit 1 t/study`
+
+* `edit 1 from today 4pm to today 5pm`
+
+* `edit 2 by today 7pm`
+
+* `edit 1 from today 4pm to today 5pm daily`
+
+
+#### Delete tasks : `delete`
+Format: delete TASK_ID
+
+Examples:
+* `delete 2`
+
+```
+###### \UserGuide.md
+``` md
+
+#### Find tasks : `find`
+Format: `find [KEY_WORD] [from DATE_TIME to DATE_TIME | by DATE_TIME] [t/TAG]...`
+
+> For find command, all parameters optional.
+> You are able to search by key words of a particular task,
+> or search by a particular time period, search by deadline,
+> or search by particular tags.
+> (You can have more than one tags to search)
+
+Examples:
+ * `find cs2103`
+
+
+
+
+ * `find from today 5am to today 6am`
+
+
+
+ * `find by today 10am`
+
+
+
+ * `find cs2103 tag/lolo`
+
+
+
+#### Undo tasks : `clear`
+Format: clear
+
+> clears all the tasks
+
+Examples:
+* `clear`
+
+```
diff --git a/collated/main/A0135782Y.md b/collated/main/A0135782Y.md
new file mode 100644
index 000000000000..86860cc90f2e
--- /dev/null
+++ b/collated/main/A0135782Y.md
@@ -0,0 +1,1063 @@
+# A0135782Y
+###### \java\seedu\address\logic\RecurringTaskManager.java
+``` java
+/**
+ * Handles the behaviour of recurring tasks
+ * Dictates when should the recurring tasks be shown
+ * This class is using a singleton pattern.
+ * Use RecurringTaskManager.getInstance() to get the instance of the class
+ */
+public class RecurringTaskManager {
+ private static final int APPEND_INCREMENT = 1;
+ private static final double NUM_MONTHS_IN_YEAR = 12.0;
+ private static final double NUM_WEEKS_IN_MONTH = 4.0;
+ private static final double NUM_DAYS_IN_WEEK = 7.0;
+ private static final int NUMBER_OF_DAYS_IN_A_WEEK = 7;
+ private static RecurringTaskManager instance;
+ private static final Logger logger = LogsCenter.getLogger(MainApp.class);
+
+ private UniqueTaskList repeatingTasks;
+ private RecurringTaskManager() {}
+
+ public void setTaskList(UniqueTaskList referenceList) {
+ assert referenceList != null : "Reference Task list cannot be null";
+ logger.fine("Initializing with RecurringTaskManager to manage: " + referenceList.toString());
+ repeatingTasks = referenceList;
+ }
+
+ public void updateAnyRecurringTasks() {
+ assert repeatingTasks != null : "Repeating Task list reference cannot be null";
+ logger.info("=============================[ RecurringTaskManager Updating ]===========================");
+ for(ReadOnlyTask task : repeatingTasks){
+ if (task.getRecurringType().equals(RecurringType.NONE)) {
+ continue;
+ }
+ updateRecurringTask(task);
+ }
+ }
+
+ /**
+ * Corrects the recurring task that are overdued to reflect the present and future recurring dates
+ *
+ * @param task Added task that might have dates that are overdued at the start
+ */
+ public void correctAddingOverdueTasks(Task task) {
+ assert task != null : "task that needs correcting cannot be null!";
+ correctAddingOverdueTasks(task, LocalDate.now());
+ }
+
+ /**
+ * Helps to correct date till a certain date.
+ * Helps with testing of the correcting of date.
+ *
+ * @param task Task to be correct, cannot be null
+ * @param currentDate LocalDate that we are correcting towards, cannot be null
+ */
+ public void correctAddingOverdueTasks(Task task, LocalDate currentDate) {
+ assert !CollectionUtil.isAnyNull(task,currentDate);
+ if (task.getRecurringType().equals(RecurringType.NONE)) {
+ return;
+ }
+ LocalDate localDateCurrently = currentDate;
+ LocalDate startDateInLocalDate = null;
+ if (!task.getComponentForNonRecurringType().hasOnlyEndDate()) {
+ startDateInLocalDate = task.getComponentForNonRecurringType().getStartDate().getDate()
+ .toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
+ }
+ LocalDate endDateInLocalDate = task.getComponentForNonRecurringType().getEndDate().getDate()
+ .toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
+ switch(task.getRecurringType()) {
+ case DAILY:
+ attemptCorrectDailyRecurringTask(task, localDateCurrently, startDateInLocalDate, endDateInLocalDate);
+ break;
+ case WEEKLY:
+ attemptCorrectWeeklyRecurringTask(task, localDateCurrently, startDateInLocalDate, endDateInLocalDate);
+ break;
+ case MONTHLY:
+ attemptCorrectMonthlyRecurringTask(task, localDateCurrently, startDateInLocalDate, endDateInLocalDate);
+ break;
+ case YEARLY:
+ attempCorrectYearlyRecurringTask(task, localDateCurrently, startDateInLocalDate, endDateInLocalDate);
+ break;
+ default:
+ assert false : "Recurring Type must always be specified";
+ break;
+ }
+ }
+
+ /**
+ * Corrects recurring tasks that are overdued to the next possible recurring slot.
+ * Does not correct the tasks if it is not overdued.
+ *
+ * @param task Task that we are interested in correcting
+ * @param localDateCurrently Local date should not be null and should be the date that we are interested in.
+ * @param startDateInLocalDate Converted form of start date.
+ * @param endDateInLocalDate Converted form of end date.
+ */
+ private void attempCorrectYearlyRecurringTask(Task task, LocalDate localDateCurrently,
+ LocalDate startDateInLocalDate, LocalDate endDateInLocalDate) {
+ final int elapsedYear;
+ if (startDateInLocalDate != null) {
+ elapsedYear = (int) Math.ceil(ChronoUnit.MONTHS.between(startDateInLocalDate, localDateCurrently) / NUM_MONTHS_IN_YEAR);
+ } else {
+ elapsedYear = (int) Math.ceil(ChronoUnit.MONTHS.between(endDateInLocalDate, localDateCurrently) / NUM_MONTHS_IN_YEAR);
+ }
+ if (elapsedYear > 0) {
+ correctYearlyRecurringTask(task, elapsedYear);
+ } else {
+ final int elapsedDay = (int) ChronoUnit.DAYS.between(startDateInLocalDate, localDateCurrently);
+ if (elapsedDay > 0) {
+ correctYearlyRecurringTask(task, 1);
+ }
+ }
+ }
+
+ /**
+ * Corrects recurring tasks that are overdued to the next possible recurring slot.
+ * Does not correct the tasks if it is not overdued.
+ *
+ * @param task Task that we are interested in correcting
+ * @param localDateCurrently Local date should not be null and should be the date that we are interested in.
+ * @param startDateInLocalDate Converted form of start date.
+ * @param endDateInLocalDate Converted form of end date.
+ */
+ private void attemptCorrectMonthlyRecurringTask(Task task, LocalDate localDateCurrently,
+ LocalDate startDateInLocalDate, LocalDate endDateInLocalDate) {
+ final int elapsedMonth;
+ if (startDateInLocalDate != null) {
+ elapsedMonth = (int) Math.ceil(ChronoUnit.WEEKS.between(startDateInLocalDate, localDateCurrently) / NUM_WEEKS_IN_MONTH);
+ } else {
+ elapsedMonth = (int) Math.ceil(ChronoUnit.WEEKS.between(endDateInLocalDate, localDateCurrently) / NUM_WEEKS_IN_MONTH);
+ }
+ if(elapsedMonth > 0) {
+ correctMonthlyRecurringTask(task, elapsedMonth);
+ } else {
+ final int elapsedDay = (int) ChronoUnit.DAYS.between(startDateInLocalDate, localDateCurrently);
+ if (elapsedDay > 0) {
+ correctMonthlyRecurringTask(task, 1);
+ }
+ }
+ }
+
+ /**
+ * Corrects recurring tasks that are overdued to the next possible recurring slot.
+ * Does not correct the tasks if it is not overdued.
+ *
+ * @param task Task that we are interested in correcting
+ * @param localDateCurrently Local date should not be null and should be the date that we are interested in.
+ * @param startDateInLocalDate Converted form of start date.
+ * @param endDateInLocalDate Converted form of end date.
+ */
+ private void attemptCorrectWeeklyRecurringTask(Task task, LocalDate localDateCurrently,
+ LocalDate startDateInLocalDate, LocalDate endDateInLocalDate) {
+ final int elapsedWeek;
+ if (startDateInLocalDate != null) {
+ elapsedWeek = (int) (ChronoUnit.DAYS.between(startDateInLocalDate, localDateCurrently) / NUM_DAYS_IN_WEEK);
+ } else {
+ elapsedWeek = (int) (ChronoUnit.DAYS.between(endDateInLocalDate, localDateCurrently) / NUM_DAYS_IN_WEEK);
+ }
+ if(elapsedWeek > 0) {
+ correctWeeklyRecurringTask(task, elapsedWeek);
+ } else {
+ final int elapsedDay = (int) ChronoUnit.DAYS.between(startDateInLocalDate, localDateCurrently);
+ if (elapsedDay > 0) {
+ correctWeeklyRecurringTask(task, 1);
+ }
+ }
+ }
+
+ /**
+ * Corrects recurring tasks that are overdued to the next possible recurring slot.
+ * Does not correct the tasks if it is not overdued.
+ *
+ * @param task Task that we are interested in correcting
+ * @param localDateCurrently Local date should not be null and should be the date that we are interested in.
+ * @param startDateInLocalDate Converted form of start date.
+ * @param endDateInLocalDate Converted form of end date.
+ */
+ private void attemptCorrectDailyRecurringTask(Task task, LocalDate localDateCurrently,
+ LocalDate startDateInLocalDate, LocalDate endDateInLocalDate) {
+ final int elapsedDay;
+ if (startDateInLocalDate != null) {
+ elapsedDay = (int) ChronoUnit.DAYS.between(startDateInLocalDate, localDateCurrently);
+ } else {
+ elapsedDay = (int) ChronoUnit.DAYS.between(endDateInLocalDate, localDateCurrently);
+ }
+ if(elapsedDay > 0) {
+ correctDailyRecurringTask(task, elapsedDay);
+ }
+ }
+
+ /**
+ * Corrects the overdued yearly tasks.
+ *
+ * @param task Task that we are correcting.
+ * @param elapsedYear How many years to correct to.
+ */
+ private void correctYearlyRecurringTask(ReadOnlyTask task, int elapsedYear) {
+ Calendar calendar = Calendar.getInstance();
+ TaskDate correctedStartDate = new TaskDate();
+ TaskDate correctedEndDate = new TaskDate();
+ TaskDate startDate = task.getComponentForNonRecurringType().getStartDate();
+ TaskDate endDate = task.getComponentForNonRecurringType().getEndDate();
+
+ if(!task.getComponentForNonRecurringType().hasOnlyEndDate()) {
+ calendar.setTime(startDate.getDate());
+ calendar.add(Calendar.YEAR, elapsedYear);
+ correctedStartDate.setDateInLong(calendar.getTime().getTime());
+ }else {
+ correctedStartDate.setDateInLong((new TaskDate()).getDateInLong());
+ }
+
+ calendar.setTime(endDate.getDate());
+ calendar.add(Calendar.YEAR, elapsedYear);
+ correctedEndDate.setDateInLong(calendar.getTime().getTime());
+
+ task.getComponentForNonRecurringType().setStartDate(correctedStartDate);
+ task.getComponentForNonRecurringType().setEndDate(correctedEndDate);
+ }
+
+ /**
+ * Corrects the overdued monthly tasks.
+ *
+ * @param task Task that we are correcting.
+ * @param elapsedMonth How many months to correct to.
+ */
+ private void correctMonthlyRecurringTask(ReadOnlyTask task, int elapsedMonth) {
+ Calendar calendar = Calendar.getInstance();
+ TaskDate correctedStartDate = new TaskDate();
+ TaskDate correctedEndDate = new TaskDate();
+ TaskDate startDate = task.getComponentForNonRecurringType().getStartDate();
+ TaskDate endDate = task.getComponentForNonRecurringType().getEndDate();
+
+ if(!task.getComponentForNonRecurringType().hasOnlyEndDate()) {
+ calendar.setTime(startDate.getDate());
+ calendar.add(Calendar.MONTH, elapsedMonth);
+ correctedStartDate.setDateInLong(calendar.getTime().getTime());
+ }else {
+ correctedStartDate.setDateInLong((new TaskDate()).getDateInLong());
+ }
+
+ calendar.setTime(endDate.getDate());
+ calendar.add(Calendar.MONTH, elapsedMonth);
+ correctedEndDate.setDateInLong(calendar.getTime().getTime());
+
+ task.getComponentForNonRecurringType().setStartDate(correctedStartDate);
+ task.getComponentForNonRecurringType().setEndDate(correctedEndDate);
+ }
+
+ /**
+ * Corrects the overdued weekly tasks.
+ *
+ * @param task Task that we are correcting.
+ * @param elapsedWeek How many weeks to correct to.
+ */
+ private void correctWeeklyRecurringTask(ReadOnlyTask task, int elapsedWeek) {
+ Calendar calendar = Calendar.getInstance();
+ TaskDate correctedStartDate = new TaskDate();
+ TaskDate correctedEndDate = new TaskDate();
+ TaskDate startDate = task.getComponentForNonRecurringType().getStartDate();
+ TaskDate endDate = task.getComponentForNonRecurringType().getEndDate();
+
+ if(!task.getComponentForNonRecurringType().hasOnlyEndDate()) {
+ calendar.setTime(startDate.getDate());
+ calendar.add(Calendar.DAY_OF_MONTH, elapsedWeek * NUMBER_OF_DAYS_IN_A_WEEK);
+ correctedStartDate.setDateInLong(calendar.getTime().getTime());
+ }else {
+ correctedStartDate.setDateInLong((new TaskDate()).getDateInLong());
+ }
+
+ calendar.setTime(endDate.getDate());
+ calendar.add(Calendar.DAY_OF_MONTH, elapsedWeek * NUMBER_OF_DAYS_IN_A_WEEK);
+ correctedEndDate.setDateInLong(calendar.getTime().getTime());
+
+ task.getComponentForNonRecurringType().setStartDate(correctedStartDate);
+ task.getComponentForNonRecurringType().setEndDate(correctedEndDate);
+ }
+
+ /**
+ * Corrects the overdued daily tasks.
+ *
+ * @param task Task that we are correcting.
+ * @param elapsedDays How many days to correct to.
+ */
+ private void correctDailyRecurringTask(Task task, int elapsedDay) {
+ Calendar calendar = Calendar.getInstance();
+ TaskDate correctedStartDate = new TaskDate();
+ TaskDate correctedEndDate = new TaskDate();
+ TaskDate startDate = task.getComponentForNonRecurringType().getStartDate();
+ TaskDate endDate = task.getComponentForNonRecurringType().getEndDate();
+
+ if(!task.getComponentForNonRecurringType().hasOnlyEndDate()) {
+ calendar.setTime(startDate.getDate());
+ calendar.add(Calendar.DAY_OF_MONTH, elapsedDay);
+ correctedStartDate.setDateInLong(calendar.getTime().getTime());
+ }else {
+ correctedStartDate.setDateInLong((new TaskDate()).getDateInLong());
+ }
+
+ calendar.setTime(endDate.getDate());
+ calendar.add(Calendar.DAY_OF_MONTH, elapsedDay);
+ correctedEndDate.setDateInLong(calendar.getTime().getTime());
+
+ task.getComponentForNonRecurringType().setStartDate(correctedStartDate);
+ task.getComponentForNonRecurringType().setEndDate(correctedEndDate);
+ }
+
+ /**
+ * @param Updates recurring tasks to append a new date when their recurring period has elapsed
+ * @return True if the recurring task has been updated
+ * False if the recurring tasks has not been updated;
+ */
+ private void updateRecurringTask(ReadOnlyTask task) {
+ Calendar startDate = new GregorianCalendar();
+ Calendar endDate = new GregorianCalendar();
+
+ List dateComponents = task.getTaskDateComponent();
+ TaskComponent lastAddedComponent = dateComponents.get(dateComponents.size()-1);
+ startDate.setTime(lastAddedComponent.getStartDate().getDate());
+ endDate.setTime(lastAddedComponent.getEndDate().getDate());
+
+ if(!lastAddedComponent.getStartDate().isValid()) {
+ startDate = null;
+ }
+ appendRecurringTasks(task, startDate, endDate);
+ }
+
+ /**
+ * Appends new task when the it is time to add the recurring task.
+ * Recurring tasks that pass their recurring dates will be appended to show the next task date.
+ * The LocalDate is assumed to be the current system date
+ *
+ * @param task Task that we are interested in
+ * @param startDate Start date of the task in Calendar form to account for time zone.
+ * @param endDate Start date of the task in Calendar form to account for time zone.
+ */
+ private void appendRecurringTasks(ReadOnlyTask task, Calendar startDate, Calendar endDate) {
+ appendRecurringTasks(task,startDate,endDate,LocalDate.now());
+ }
+
+
+ /**
+ * Appends new task when the it is time to add the recurring task.
+ * Recurring tasks that pass their recurring dates will be appended to show the next task date.
+ *
+ * @param task Task that we are interested in
+ * @param startDate Start date of the task in Calendar form to account for time zone.
+ * @param endDate Start date of the task in Calendar form to account for time zone.
+ * @param currentDate Current date to measure the amount of tasks that have not been done.
+ */
+ public void appendRecurringTasks(ReadOnlyTask task, Calendar startDate, Calendar endDate, LocalDate currentDate) {
+ assert !CollectionUtil.isAnyNull(task, startDate, endDate, currentDate);
+ LocalDate localDateCurrently = currentDate;
+ LocalDate startDateInLocalDate = null;
+ if (startDate != null) {
+ startDateInLocalDate = startDate.getTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
+ }
+ LocalDate endDateInLocalDate = endDate.getTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
+ switch (task.getRecurringType()) {
+ case DAILY:
+ attemptAppendDailyRecurringTasks(task, startDate, endDate,
+ localDateCurrently, startDateInLocalDate, endDateInLocalDate);
+ break;
+ case WEEKLY:
+ attempAppendWeeklyRecurringTasks(task, startDate, endDate,
+ localDateCurrently, startDateInLocalDate, endDateInLocalDate);
+ break;
+ case MONTHLY:
+ attemptAppendMonthlyRecurringTasks(task, startDate, endDate,
+ localDateCurrently, startDateInLocalDate, endDateInLocalDate);
+ break;
+ case YEARLY:
+ attemptAppendYearlyRecurringTasks(task, startDate, endDate,
+ localDateCurrently, startDateInLocalDate, endDateInLocalDate);
+ break;
+ default:
+ assert true : "Failed to set recurring type";
+ }
+ }
+
+ /**
+ * Appends yearly recurring tasks if the task has crossed over to a new year
+ *
+ * @param task Task that we are interest in
+ * @param startDate Start date of the task
+ * @param endDate End date of the ask
+ * @param localDateCurrently Current date that we are at now
+ * @param startDateInLocalDate Converted start date as a LocalDate
+ * @param endDateInLocalDate Converted end date as a LocalDate
+ */
+ private void attemptAppendYearlyRecurringTasks(ReadOnlyTask task, Calendar startDate, Calendar endDate,
+ LocalDate localDateCurrently, LocalDate startDateInLocalDate, LocalDate endDateInLocalDate) {
+ final int elapsedYear;
+ if (startDateInLocalDate != null) {
+ elapsedYear = (int) Math.ceil(ChronoUnit.MONTHS.between(startDateInLocalDate, localDateCurrently) / NUM_MONTHS_IN_YEAR);
+ } else {
+ elapsedYear = (int) Math.ceil(ChronoUnit.MONTHS.between(endDateInLocalDate, localDateCurrently) / NUM_MONTHS_IN_YEAR);
+ }
+ for (int i = 0; i < elapsedYear; i++) {
+ appendYearlyRecurringTask(task, startDate, endDate, elapsedYear);
+ }
+ }
+
+ /**
+ * Appends monthly recurring tasks if the task has crossed over to a new month
+ *
+ * @param task Task that we are interest in
+ * @param startDate Start date of the task
+ * @param endDate End date of the ask
+ * @param localDateCurrently Current date that we are at now
+ * @param startDateInLocalDate Converted start date as a LocalDate
+ * @param endDateInLocalDate Converted end date as a LocalDate
+ */
+ private void attemptAppendMonthlyRecurringTasks(ReadOnlyTask task, Calendar startDate, Calendar endDate,
+ LocalDate localDateCurrently, LocalDate startDateInLocalDate, LocalDate endDateInLocalDate) {
+ final int elapsedMonth;
+ if (startDateInLocalDate != null) {
+ elapsedMonth = (int) Math.ceil(ChronoUnit.WEEKS.between(startDateInLocalDate, localDateCurrently) / NUM_WEEKS_IN_MONTH);
+ } else {
+ elapsedMonth = (int) Math.ceil(ChronoUnit.WEEKS.between(endDateInLocalDate, localDateCurrently) / NUM_WEEKS_IN_MONTH);
+ }
+ for (int i = 0; i < elapsedMonth; i++) {
+ appendMonthlyRecurringTask(task, startDate, endDate, elapsedMonth);
+ }
+ }
+
+ /**
+ * Appends weekly recurring tasks if the task has crossed over to a new week
+ *
+ * @param task Task that we are interest in
+ * @param startDate Start date of the task
+ * @param endDate End date of the ask
+ * @param localDateCurrently Current date that we are at now
+ * @param startDateInLocalDate Converted start date as a LocalDate
+ * @param endDateInLocalDate Converted end date as a LocalDate
+ */
+ private void attempAppendWeeklyRecurringTasks(ReadOnlyTask task, Calendar startDate, Calendar endDate,
+ LocalDate localDateCurrently, LocalDate startDateInLocalDate, LocalDate endDateInLocalDate) {
+ final int elapsedWeek;
+ if ( startDateInLocalDate != null) {
+ elapsedWeek = (int) Math.ceil((ChronoUnit.DAYS.between(startDateInLocalDate, localDateCurrently) / NUM_DAYS_IN_WEEK));
+ } else {
+ elapsedWeek = (int) Math.ceil((ChronoUnit.DAYS.between(endDateInLocalDate, localDateCurrently) / NUM_DAYS_IN_WEEK));
+ }
+ for (int i = 0; i < elapsedWeek; i++) {
+ appendWeeklyRecurringTask(task, startDate, endDate, elapsedWeek);
+ }
+ }
+
+ /**
+ * Appends daily recurring tasks if the task has crossed over to a new day
+ *
+ * @param task Task that we are interest in
+ * @param startDate Start date of the task
+ * @param endDate End date of the ask
+ * @param localDateCurrently Current date that we are at now
+ * @param startDateInLocalDate Converted start date as a LocalDate
+ * @param endDateInLocalDate Converted end date as a LocalDate
+ */
+ private void attemptAppendDailyRecurringTasks(ReadOnlyTask task, Calendar startDate, Calendar endDate,
+ LocalDate localDateCurrently, LocalDate startDateInLocalDate, LocalDate endDateInLocalDate) {
+ final int elapsedDay;
+ if (startDateInLocalDate != null) {
+ elapsedDay = (int) ChronoUnit.DAYS.between(startDateInLocalDate, localDateCurrently);
+ } else {
+ elapsedDay = (int) ChronoUnit.DAYS.between(endDateInLocalDate, localDateCurrently);
+ }
+ for (int i = 0; i < elapsedDay; i++) {
+ appendDailyRecurringTask(task, startDate, endDate, elapsedDay);
+ }
+ }
+
+ /**
+ * Updates Yearly recurring tasks to the their latest date slot.
+ *
+ * @param task Recurring task to be considered.
+ * @param startDate The start date of this task if any. Null values represents that start date is not present.
+ * @param endDate The end date of the is task.
+ * @param elapsedYear The years that have elapsed.
+ */
+ private void appendYearlyRecurringTask(ReadOnlyTask task, Calendar startDate,
+ Calendar endDate, int elapsedYear) {
+ // Append a new date to the current task
+ Calendar calendar = Calendar.getInstance();
+ TaskDate editedStartDate = new TaskDate();
+ TaskDate editedEndDate = new TaskDate();
+ if(startDate != null) {
+ calendar.setTime(startDate.getTime());
+ calendar.add(Calendar.YEAR, APPEND_INCREMENT);
+ editedStartDate.setDateInLong(calendar.getTime().getTime());
+ startDate.setTime(editedStartDate.getDate());
+ }else {
+ editedStartDate.setDateInLong((new TaskDate()).getDateInLong());
+ }
+
+ calendar.setTime(endDate.getTime());
+ calendar.add(Calendar.YEAR, APPEND_INCREMENT);
+ editedEndDate.setDateInLong(calendar.getTime().getTime());
+ endDate.setTime(editedEndDate.getDate());
+
+ TaskComponent newAppendedDate = new TaskComponent((Task) task, editedStartDate, editedEndDate);
+ task.appendRecurringDate(newAppendedDate);
+ repeatingTasks.appendTaskComponent(newAppendedDate);
+ }
+
+ /**
+ * Updates Monthly recurring tasks to the their latest date slot.
+ *
+ * @param task Recurring task to be considered.
+ * @param startDate The start date of this task if any. Null values represents that start date is not present.
+ * @param endDate The end date of the is task.
+ * @param elapsedYear The months that have elapsed.
+ */
+ private void appendMonthlyRecurringTask(ReadOnlyTask task, Calendar startDate,
+ Calendar endDate, int elapsedMonth) {
+ // Append a new date to the current task
+ // Append a new date to the current task
+ Calendar calendar = Calendar.getInstance();
+ TaskDate editedStartDate = new TaskDate();
+ TaskDate editedEndDate = new TaskDate();
+ if(startDate != null) {
+ calendar.setTime(startDate.getTime());
+ calendar.add(Calendar.MONTH, APPEND_INCREMENT);
+ editedStartDate.setDateInLong(calendar.getTime().getTime());
+ startDate.setTime(editedStartDate.getDate());
+ }else {
+ editedStartDate.setDateInLong((new TaskDate()).getDateInLong());
+ }
+
+ calendar.setTime(endDate.getTime());
+ calendar.add(Calendar.MONTH, APPEND_INCREMENT);
+ editedEndDate.setDateInLong(calendar.getTime().getTime());
+ endDate.setTime(editedEndDate.getDate());
+
+ TaskComponent newAppendedDate = new TaskComponent((Task) task, editedStartDate, editedEndDate);
+ task.appendRecurringDate(newAppendedDate);
+ repeatingTasks.appendTaskComponent(newAppendedDate);
+ }
+
+ /**
+ * Updates Weekly recurring tasks to the their latest date slot.
+ *
+ * @param task Recurring task to be considered.
+ * @param startDate The start date of this task if any. Null values represents that start date is not present.
+ * @param endDate The end date of the is task.
+ * @param elapsedYear The weeks that have elapsed.
+ */
+ private void appendWeeklyRecurringTask(ReadOnlyTask task, Calendar startDate,
+ Calendar endDate, int elapsedWeek) {
+ // Append a new date to the current task
+ Calendar calendar = Calendar.getInstance();
+ TaskDate editedStartDate = new TaskDate();
+ TaskDate editedEndDate = new TaskDate();
+ if(startDate != null) {
+ calendar.setTime(startDate.getTime());
+ calendar.add(Calendar.DAY_OF_MONTH, NUMBER_OF_DAYS_IN_A_WEEK);
+ editedStartDate.setDateInLong(calendar.getTime().getTime());
+ startDate.setTime(editedStartDate.getDate());
+ }else {
+ editedStartDate.setDateInLong((new TaskDate()).getDateInLong());
+ }
+
+ calendar.setTime(endDate.getTime());
+ calendar.add(Calendar.DAY_OF_MONTH, NUMBER_OF_DAYS_IN_A_WEEK);
+ editedEndDate.setDateInLong(calendar.getTime().getTime());
+ endDate.setTime(editedEndDate.getDate());
+
+ TaskComponent newAppendedDate = new TaskComponent((Task) task, editedStartDate, editedEndDate);
+ task.appendRecurringDate(newAppendedDate);
+ repeatingTasks.appendTaskComponent(newAppendedDate);
+ }
+
+ /**
+ * Updates Daily recurring tasks to the their latest date slot.
+ *
+ * @param task Recurring task to be considered.
+ * @param startDate The start date of this task if any. Null values represents that start date is not present.
+ * @param endDate The end date of the is task.
+ * @param elapsedYear The days that have elapsed.
+ */
+ private void appendDailyRecurringTask(ReadOnlyTask task, Calendar startDate,
+ Calendar endDate, int elapsedDay) {
+ // Append a new date to the current task
+ Calendar calendar = Calendar.getInstance();
+ TaskDate editedStartDate = new TaskDate();
+ TaskDate editedEndDate = new TaskDate();
+ if(startDate != null) {
+ calendar.setTime(startDate.getTime());
+ calendar.add(Calendar.DAY_OF_MONTH, APPEND_INCREMENT);
+ editedStartDate.setDateInLong(calendar.getTime().getTime());
+ startDate.setTime(editedStartDate.getDate());
+ }else {
+ editedStartDate.setDateInLong((new TaskDate()).getDateInLong());
+ }
+
+ calendar.setTime(endDate.getTime());
+ calendar.add(Calendar.DAY_OF_MONTH, APPEND_INCREMENT);
+ editedEndDate.setDateInLong(calendar.getTime().getTime());
+ endDate.setTime(editedEndDate.getDate());
+
+ TaskComponent newAppendedDate = new TaskComponent((Task) task, editedStartDate, editedEndDate);
+ task.appendRecurringDate(newAppendedDate);
+ repeatingTasks.appendTaskComponent(newAppendedDate);
+ }
+
+
+ public static RecurringTaskManager getInstance() {
+ if (instance == null) {
+ instance = new RecurringTaskManager();
+ }
+ return instance;
+ }
+}
+```
+###### \java\seedu\address\model\ModelManager.java
+``` java
+ /**
+ * Initializes a ModelManager with the given TaskList
+ * TaskList and its variables should not be null
+ */
+ public ModelManager(TaskMaster src, UserPrefs userPrefs) {
+ super();
+ assert src != null;
+ assert userPrefs != null;
+ logger.fine("Initializing with address book: " + src + " and user prefs " + userPrefs);
+
+ taskMaster = new TaskMaster(src);
+ tasks = taskMaster.getTasks();
+ filteredTaskComponents = new FilteredList<>(taskMaster.getTaskComponentList());
+ RecurringTaskManager.getInstance().setTaskList(taskMaster.getUniqueTaskList());
+ RecurringTaskManager.getInstance().updateAnyRecurringTasks();
+
+ }
+```
+###### \java\seedu\address\model\ModelManager.java
+``` java
+ public ModelManager(ReadOnlyTaskMaster initialData, UserPrefs userPrefs) {
+ taskMaster = new TaskMaster(initialData);
+ tasks = taskMaster.getTasks();
+
+ filteredTaskComponents = new FilteredList<>(taskMaster.getTaskComponentList());
+ RecurringTaskManager.getInstance().setTaskList(taskMaster.getUniqueTaskList());
+ RecurringTaskManager.getInstance().updateAnyRecurringTasks();
+
+ }
+```
+###### \java\seedu\address\model\ModelManager.java
+``` java
+ @Override
+ public synchronized void addTask(Task task) throws UniqueTaskList.DuplicateTaskException, TimeslotOverlapException {
+ taskMaster.addTask(task);
+ RecurringTaskManager.getInstance().correctAddingOverdueTasks(task);
+ updateFilteredListToShowAll();
+ indicateTaskListChanged();
+ }
+
+```
+###### \java\seedu\address\model\ModelManager.java
+``` java
+ private class ArchiveQualifier implements Qualifier {
+ private boolean isArchived;
+
+ ArchiveQualifier(boolean isItArchive) {
+ this.isArchived= isItArchive;
+ }
+
+ @Override
+ public boolean run(TaskComponent task) {
+ return task.isArchived() == isArchived;
+ }
+
+ @Override
+ public String toString() {
+ return "type=" + isArchived;
+ }
+ }
+```
+###### \java\seedu\address\model\task\RecurringType.java
+``` java
+public enum RecurringType {
+ NONE,
+ DAILY,
+ WEEKLY,
+ MONTHLY,
+ YEARLY,
+ IGNORED //Merely for parsing
+}
+```
+###### \java\seedu\address\model\task\Task.java
+``` java
+ @Override
+ public List getTaskDateComponent() {
+ return recurringDates;
+ }
+
+ @Override
+ public TaskType getTaskType() {
+ return taskType;
+ }
+ @Override
+ public RecurringType getRecurringType() {
+ return recurringType;
+ }
+
+ public void setTaskType(TaskType type) {
+ this.taskType = type;
+ }
+
+ public void setRecurringType(RecurringType type) {
+ if (taskType == TaskType.FLOATING) {
+ assert (!type.equals(RecurringType.NONE)) : "Floating Task cannot be a recurring task";
+ }
+ this.recurringType = type;
+ }
+```
+###### \java\seedu\address\model\task\Task.java
+``` java
+ @Override
+ public void completeTaskWhenAllComponentArchived() {
+ for (TaskComponent c : recurringDates) {
+ if (c.isArchived() == false || c.getTaskReference().getRecurringType() != RecurringType.NONE) {
+ return;
+ }
+ }
+ taskType = TaskType.COMPLETED;
+ }
+```
+###### \java\seedu\address\model\task\Task.java
+``` java
+ @Override
+ public TaskComponent getComponentForNonRecurringType() {
+ assert recurringDates.size() == 1 : "This method should only be used for non recurring tasks";
+ return recurringDates.get(0);
+ }
+
+ @Override
+ public TaskComponent getLastAppendedComponent() {
+ return recurringDates.get(recurringDates.size()-1);
+ }
+
+ @Override
+ public void appendRecurringDate(TaskComponent componentToBeAdded) {
+ assert !recurringType.equals(RecurringType.NONE) : "You cannot append new dates to non recurring tasks";
+ recurringDates.add(componentToBeAdded);
+ recurringDates.get(recurringDates.size()-1).setTaskReferrence(this);
+ }
+```
+###### \java\seedu\address\model\task\TaskComponent.java
+``` java
+/**
+* This class served as the occurrence portion in an abstraction occurrence pattern.
+* The abstraction is the Task and the occurrence is the TaskDateComponent.
+*
+*/
+public class TaskComponent {
+
+ private Task taskReference;
+ private TaskDate startDate, endDate;
+ private boolean isArchived;
+
+ public TaskComponent(Task taskReference, TaskDate startDate, TaskDate endDate) {
+ assert !CollectionUtil.isAnyNull(startDate, endDate);
+ this.startDate = new TaskDate(startDate);
+ this.endDate = new TaskDate(endDate);
+ this.taskReference = taskReference;
+ }
+
+ public TaskComponent(TaskComponent taskDateComponent) {
+ assert taskDateComponent != null : "Cannot pass in null values";
+ this.taskReference = taskDateComponent.taskReference;
+ this.startDate = taskDateComponent.startDate;
+ this.endDate = taskDateComponent.endDate;
+ this.isArchived = taskDateComponent.isArchived;
+ }
+
+ public void setStartDate(TaskDate startDate) {
+ this.startDate = startDate;
+ }
+
+ public void setEndDate(TaskDate endDate) {
+ this.endDate = endDate;
+ }
+
+ public void setTaskReferrence(Task task) {
+ this.taskReference = task;
+ }
+
+ public TaskDate getStartDate() {
+ return startDate;
+ }
+
+ public TaskDate getEndDate() {
+ return endDate;
+ }
+
+ /**
+ * Checks if TaskDateComponent is in a valid time slot
+ *
+ * @return True if it is in a valid time slot
+ */
+ public boolean isValidTimeSlot(){
+ if(startDate!=null && endDate!=null){
+ return (endDate.getDate()).after(startDate.getDate());
+ }else{
+ return true;
+ }
+ }
+
+ /**
+ * Checks if the TaskDate has only end date
+ *
+ * @return True if it has only an end date.
+ * False if it also contains a start date.
+ */
+ public boolean hasOnlyEndDate() {
+ if (startDate.getDateInLong() != TaskDate.DATE_NOT_PRESENT){
+ return false;
+ }
+ return true;
+ }
+
+
+ public ReadOnlyTask getTaskReference() {
+ return taskReference;
+ }
+
+ /**
+ * Archive this task component
+ */
+ public void archive() {
+ isArchived = true;
+ }
+
+```
+###### \java\seedu\address\model\task\TaskDate.java
+``` java
+/**
+ * Helper class for storing date for the Task
+ */
+public class TaskDate {
+ public static final int DATE_NOT_PRESENT = -1;
+ private long date;
+
+ /**
+ * Date is not present by default if nothing is specified
+ * Convenience and defensive
+ */
+ public TaskDate() {
+ this.date = DATE_NOT_PRESENT;
+ }
+
+ public TaskDate(Date date) {
+ this.date = date.getTime();
+ }
+
+ public TaskDate(long date) {
+ this.date = date;
+ }
+
+ public TaskDate(TaskDate copy) {
+ this.date = copy.date;
+ }
+
+ //For sake of testing, not implemented in main app
+ public TaskDate(String inputDate) {
+ this.date = new com.joestelmach.natty.Parser().parse(inputDate).get(0).getDates().get(0).getTime();
+ }
+
+ public void setDateInLong(long date) {
+ this.date = date;
+ }
+ /**
+ * Formats the date in (EEE, MMM d hh.mma) format which will give MON, Oct 20 10.00PM
+ * If there is no date present return empty string
+ * @return Empty string if there is no date present
+ * Formatted date if there is date
+ */
+ public String getFormattedDate() {
+ if (date == DATE_NOT_PRESENT) {
+ return "";
+ }
+ SimpleDateFormat formatter = new SimpleDateFormat("EEE, MMM d hh.mma", Locale.ENGLISH);
+ return formatter.format(new Date(date));
+ }
+
+ //For sake of testing
+ public String getInputDate() {
+ if (date == DATE_NOT_PRESENT) {
+ return "";
+ }
+ SimpleDateFormat formatter = new SimpleDateFormat("dd MMM hha", Locale.ENGLISH);
+ return formatter.format(new Date(date));
+ }
+
+ public long getDateInLong() {
+ return date;
+ }
+
+ /**
+ * Parses the date in Long and provides it in the Date class format
+ * @return
+ */
+ public Date getDate() {
+ return new Date(date);
+ }
+
+ @Override
+ public boolean equals(Object other){
+ return other == this ||
+ (other instanceof TaskDate // instanceof handles nulls
+ && this.getDate().equals(((TaskDate) other).getDate()));
+ }
+
+ public boolean isValid() {
+ return date != DATE_NOT_PRESENT;
+ }
+
+ @Override
+ public String toString() {
+ return getFormattedDate();
+ }
+
+}
+```
+###### \java\seedu\address\model\task\TaskType.java
+``` java
+public enum TaskType {
+ FLOATING,
+ NON_FLOATING,
+ COMPLETED
+}
+```
+###### \java\seedu\address\model\task\UniqueTaskList.java
+``` java
+ /**
+ * Adds a task to the list.
+ *
+ * @throws DuplicateTaskException if the task to add is a duplicate of an existing task in the list.
+ * @throws TimeslotOverlapException
+ */
+ public void add(Task toAdd) throws DuplicateTaskException, TimeslotOverlapException {
+ assert toAdd != null;
+ if (contains(toAdd)) {
+ if (!toAdd.getRecurringType().equals(RecurringType.NONE)) {
+ // append this "task" as date component to the task
+ appendDuplicateRecurringDatesToTask(toAdd);
+ return;
+ }
+ throw new DuplicateTaskException();
+ }
+ if(overlaps(toAdd)){
+ throw new TimeslotOverlapException();
+ }
+ internalList.add(toAdd);
+ internalComponentList.addAll(toAdd.getTaskDateComponent());
+ }
+
+ private void appendDuplicateRecurringDatesToTask(Task toAdd) {
+ int idx = internalList.indexOf(toAdd);
+ Task toBeAppendedOn = internalList.get(idx);
+ internalComponentList.add(toAdd.getComponentForNonRecurringType());
+ toBeAppendedOn.appendRecurringDate(toAdd.getComponentForNonRecurringType());
+ }
+```
+###### \java\seedu\address\storage\XmlAdaptedTaskComponent.java
+``` java
+ @XmlElement
+ private long startDate;
+ @XmlElement
+ private long endDate;
+ @XmlElement
+ private String recurringType;
+ @XmlElement
+ private boolean isArchived;
+```
+###### \java\seedu\address\storage\XmlAdaptedTaskComponent.java
+``` java
+ /**
+ * Converts a given Task into this class for JAXB use.
+ *
+ * @param source future changes to this will not affect the created XmlAdaptedTask
+ */
+ public XmlAdaptedTaskComponent(TaskComponent source) {
+ name = source.getTaskReference().getName().fullName;
+ tagged = new ArrayList<>();
+ for (Tag tag : source.getTaskReference().getTags()) {
+ tagged.add(new XmlAdaptedTag(tag));
+ }
+
+ if (source.getTaskReference().getTaskType() == TaskType.NON_FLOATING) {
+ startDate = source.getStartDate().getDateInLong();
+ endDate = source.getEndDate().getDateInLong();
+ } else if (source.getTaskReference().getTaskType() == TaskType.FLOATING) {
+ startDate = TaskDate.DATE_NOT_PRESENT;
+ endDate = TaskDate.DATE_NOT_PRESENT;
+ } else if(source.getTaskReference().getTaskType() == TaskType.COMPLETED){
+ startDate = source.getStartDate().getDateInLong();
+ endDate = source.getEndDate().getDateInLong();
+ }
+ if (source.getTaskReference().getRecurringType() != RecurringType.NONE && source.isArchived()) {
+ TaskDate startCopy = new TaskDate(source.getStartDate());
+ TaskDate endCopy = new TaskDate(source.getEndDate());
+ startDate = startCopy.getDateInLong();
+ endDate = endCopy.getDateInLong();
+ }
+ recurringType = source.getTaskReference().getRecurringType().name();
+ isArchived = source.isArchived();
+ }
+
+ /**
+ * Converts this jaxb-friendly adapted task object into the model's Task object.
+ *
+ * @throws IllegalValueException if there were any data constraints violated in the adapted task
+ */
+ public Task toModelType() throws IllegalValueException {
+ final List taskTags = new ArrayList<>();
+ for (XmlAdaptedTag tag : tagged) {
+ taskTags.add(tag.toModelType());
+ }
+ final Name name = new Name(this.name);
+ final UniqueTagList tags = new UniqueTagList(taskTags);
+
+ if (endDate != TaskDate.DATE_NOT_PRESENT) {
+ return toModelTypeNonFloating(name, tags);
+ }
+ return toModelTypeFloating(name, tags);
+ }
+
+ private Task toModelTypeFloating(final Name name, final UniqueTagList tags) {
+ Task task = new Task(name, tags);
+
+ if(isArchived){
+ task.setTaskType(TaskType.COMPLETED);
+ for(TaskComponent t: task.getTaskDateComponent()){
+ t.archive();
+ }
+ }
+
+ return task;
+ }
+
+ private Task toModelTypeNonFloating(final Name name, final UniqueTagList tags) {
+ final TaskDate taskStartDate = new TaskDate(startDate);
+ final TaskDate taskEndDate = new TaskDate(endDate);
+ RecurringType toBeAdded = RecurringType.NONE;
+ if (recurringType != null ) {
+ toBeAdded = RecurringType.valueOf(recurringType);
+ }
+
+ Task task = new Task(name, tags, taskStartDate, taskEndDate, toBeAdded);
+ if(isArchived){
+ task.setTaskType(TaskType.COMPLETED);
+ for(TaskComponent t: task.getTaskDateComponent()){
+ t.archive();
+ }
+ }
+
+ return task;
+ }
+```
+###### \resources\view\TaskListCard.fxml
+``` fxml
+
+```
diff --git a/collated/main/A0135784W.md b/collated/main/A0135784W.md
new file mode 100644
index 000000000000..fff6d1776c63
--- /dev/null
+++ b/collated/main/A0135784W.md
@@ -0,0 +1,18 @@
+# A0135784W
+###### \java\seedu\address\ui\HelpWindow.java
+``` java
+ private void configure() throws IOException{
+ Scene scene = new Scene(mainPane);
+ //Null passed as the parent stage to make it non-modal.
+ dialogStage = createDialogStage(TITLE, null, scene);
+ dialogStage.setMaximized(true); //TODO: set a more appropriate initial size
+ setIcon(dialogStage, ICON);
+
+ ClassLoader classloader = getClass().getClassLoader();
+ File file = new File(classloader.getResource("help.html").getFile());
+ WebView browser = new WebView();
+ browser.getEngine().load(file.toURI().toURL().toString());
+ FxViewUtil.applyAnchorBoundaryParameters(browser, 0.0, 0.0, 0.0, 0.0);
+ mainPane.getChildren().add(browser);
+ }
+```
diff --git a/collated/main/A0147967J.md b/collated/main/A0147967J.md
new file mode 100644
index 000000000000..9f44a9537f72
--- /dev/null
+++ b/collated/main/A0147967J.md
@@ -0,0 +1,1264 @@
+# A0147967J
+###### \java\seedu\address\commons\events\model\FilePathChangeEvent.java
+``` java
+/** Indicates the file path of the task master should change.*/
+public class FilePathChangeEvent extends BaseEvent {
+
+ public final String newFilePath;
+
+ public FilePathChangeEvent(String newFilePath){
+ this.newFilePath = newFilePath;
+ }
+
+ @Override
+ public String toString() {
+ return "File path changes to :" + newFilePath;
+ }
+}
+```
+###### \java\seedu\address\commons\events\ui\AgendaTimeRangeChangedEvent.java
+``` java
+/**
+ * Indicates a change in the displayed time range of the agenda.
+ */
+public class AgendaTimeRangeChangedEvent extends BaseEvent {
+
+
+ private final TaskDate inputDate;
+ private final List taskComponentList;
+
+ public AgendaTimeRangeChangedEvent(TaskDate inputDate, List list){
+ this.inputDate = inputDate;
+ this.taskComponentList = list;
+ }
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName();
+ }
+
+ public TaskDate getInputDate() {
+ return inputDate;
+ }
+
+ public List getData() {
+ return taskComponentList;
+ }
+}
+```
+###### \java\seedu\address\commons\events\ui\FailedCommandAttemptedEvent.java
+``` java
+/**
+ * Indicates an attempt to execute a failed command
+ */
+public class FailedCommandAttemptedEvent extends BaseEvent {
+
+
+ public FailedCommandAttemptedEvent(Command command) {
+ }
+
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName();
+ }
+
+}
+```
+###### \java\seedu\address\commons\events\ui\NavigationSelectionChangedEvent.java
+``` java
+/**
+ * Represents a selection change in the Navigation Bar Panel
+ */
+public class NavigationSelectionChangedEvent extends BaseEvent {
+
+
+ private final String newSelection;
+
+ public NavigationSelectionChangedEvent(String newSelection){
+ this.newSelection = newSelection;
+ }
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName();
+ }
+
+ public String getNewSelection() {
+ return newSelection;
+ }
+}
+```
+###### \java\seedu\address\logic\commands\BlockCommand.java
+``` java
+/**
+ * Blocks a certain time slot in the task list.
+ */
+public class BlockCommand extends Command {
+
+ public static final String COMMAND_WORD = "block";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Blocks a certain time slot in the schedule. "
+ + "Parameters: TASK_NAME [t/TAG]...\n"
+ + "Example: " + COMMAND_WORD
+ + " from 2 oct 2am to 2 oct 3am t/highPriority";
+
+ public static final String MESSAGE_SUCCESS = "Timeslot blocked: %1$s";
+ public static final String MESSAGE_TIMESLOT_OCCUPIED = "This timeslot is already blocked or overlapped with existing tasks.";
+ public static final String MESSAGE_ILLEGAL_TIME_SLOT = "End time must be later than Start time.";
+ public static final String DUMMY_NAME = "BLOCKED SLOT";
+
+
+
+ private final Task toBlock;
+
+ /**
+ * Convenience constructor using raw values.
+ *
+ * @throws IllegalValueException if any of the raw values are invalid
+ */
+ public BlockCommand(Set tags, TaskDate startDate, TaskDate endDate)
+ throws IllegalValueException {
+ final Set tagSet = new HashSet<>();
+ for (String tagName : tags) {
+ tagSet.add(new Tag(tagName));
+ }
+ this.toBlock = new Task(
+ new Name(DUMMY_NAME),
+ new UniqueTagList(tagSet),
+ new TaskDate(startDate),
+ new TaskDate(endDate),
+ RecurringType.NONE
+ );
+ if(!this.toBlock.getComponentForNonRecurringType().isValidTimeSlot()){
+ indicateAttemptToExecuteIncorrectCommand();
+ throw new IllegalValueException(MESSAGE_ILLEGAL_TIME_SLOT);
+ }
+ }
+
+ @Override
+ public CommandResult execute() {
+ assert model != null;
+ try {
+ model.addTask(toBlock);
+ return new CommandResult(String.format(MESSAGE_SUCCESS, toBlock));
+ } catch (TimeslotOverlapException e) {
+ indicateAttemptToExecuteFailedCommand();
+ urManager.popFromUndoQueue();
+ return new CommandResult(MESSAGE_TIMESLOT_OCCUPIED);
+ } catch (DuplicateTaskException e) {
+ assert false: "not applicable for block command";
+ return new CommandResult(MESSAGE_TIMESLOT_OCCUPIED);
+ }
+
+ }
+
+}
+```
+###### \java\seedu\address\logic\commands\ChangeDirectoryCommand.java
+``` java
+/**
+ * Changes the file path of the file and exports all existing data to the specified file.
+ */
+public class ChangeDirectoryCommand extends Command{
+
+ public static final String COMMAND_WORD = "cd";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Changes the directory for the tasklist."
+ + "Parameters: FILE_PATH\n"
+ + "Example: " + COMMAND_WORD
+ + " D:\t.xml";
+
+ public static final String MESSAGE_SUCCESS = "Alert: This operation is irreversible.\nFile path successfully changed to : %1$s";
+ public static final String MESSAGE_IO_ERROR = "Error when saving/reading file...";
+ public static final String MESSAGE_CONVENSION_ERROR = "Wrong file type/Invalid file path detected.";
+
+ private final String filePath;
+
+ public ChangeDirectoryCommand(String filePath){
+ this.filePath = filePath;
+ }
+
+ @Override
+ public CommandResult execute() {
+ try{
+ if(!filePath.endsWith(".xml"))
+ throw new DataConversionException(null);
+ XmlTaskListStorage newFile = new XmlTaskListStorage(filePath);
+ newFile.saveTaskList(model.getTaskMaster(), filePath);
+ model.changeDirectory(filePath);
+ Config config = ConfigUtil.readConfig(Config.DEFAULT_CONFIG_FILE).get();
+ config.setTaskListFilePath(filePath);
+ ConfigUtil.saveConfig(config, Config.DEFAULT_CONFIG_FILE);
+ urManager.resetQueue();
+ return new CommandResult(String.format(MESSAGE_SUCCESS, filePath));
+ }catch (DataConversionException dce){
+ indicateAttemptToExecuteIncorrectCommand();
+ return new CommandResult(MESSAGE_CONVENSION_ERROR);
+ }catch (IOException ioe){
+ indicateAttemptToExecuteFailedCommand();
+ return new CommandResult(MESSAGE_IO_ERROR);
+ }
+ }
+
+}
+```
+###### \java\seedu\address\logic\commands\Command.java
+``` java
+ /**
+ * Assigns an undo/redo manager to the command to manage undo/redo operation.
+ */
+ public void assignManager(URManager urManager) {
+ this.urManager = urManager;
+ }
+
+ /**
+ * Raises an event to indicate an attempt to execute a failed command
+ */
+ protected void indicateAttemptToExecuteFailedCommand() {
+ EventsCenter.getInstance().post(new FailedCommandAttemptedEvent(this));
+ }
+}
+```
+###### \java\seedu\address\logic\commands\CompleteCommand.java
+``` java
+/**
+ * Marks a task as done identified using it's last displayed index from the task list.
+ */
+public class CompleteCommand extends Command {
+
+ public static final String COMMAND_WORD = "done";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Archives the task identified by the index number used in the last task listing. The Task will be deleted after exiting the app.\n"
+ + "Parameters: INDEX (must be a positive integer)\n"
+ + "Example: " + COMMAND_WORD + " 1";
+
+ public static final String MESSAGE_COMPLETE_TASK_SUCCESS = "Completed Task: %1$s";
+
+ public final int targetIndex;
+
+ public CompleteCommand(int targetIndex) {
+ this.targetIndex = targetIndex;
+ }
+
+
+ @Override
+ public CommandResult execute() {
+
+ UnmodifiableObservableList lastShownList = model.getFilteredTaskComponentList();
+
+ if (lastShownList.size() < targetIndex) {
+ indicateAttemptToExecuteIncorrectCommand();
+ urManager.popFromUndoQueue();
+ return new CommandResult(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX);
+ }
+
+ TaskComponent taskToDelete = lastShownList.get(targetIndex - 1);
+
+ try {
+ model.archiveTask(taskToDelete);
+ } catch (TaskNotFoundException pnfe) {
+ assert false : "The target task cannot be missing";
+ }
+
+ return new CommandResult(String.format(MESSAGE_COMPLETE_TASK_SUCCESS, taskToDelete.getTaskReference()));
+ }
+}
+```
+###### \java\seedu\address\logic\commands\RedoCommand.java
+``` java
+/**
+ * Redos the previous redoable operation.
+ */
+public class RedoCommand extends Command{
+
+ public static final String COMMAND_WORD = "r";
+
+ public static final String MESSAGE_FAIL = "No command to redo.";
+
+ public RedoCommand() {}
+
+ @Override
+ public CommandResult execute() {
+
+ try {
+ Context contextToRedo = urManager.getContextToRedo();
+ urManager.addToUndoQueueUsedByRedo(model, contextToRedo.getCommand());
+ return contextToRedo.getCommand().execute();
+ } catch (NoAvailableCommandException nace){
+ indicateAttemptToExecuteFailedCommand();
+ return new CommandResult(MESSAGE_FAIL);
+ }
+ }
+}
+```
+###### \java\seedu\address\logic\commands\UndoCommand.java
+``` java
+/**
+ * Undos the previous undoable operation.
+ */
+public class UndoCommand extends Command{
+
+ public static final String COMMAND_WORD = "u";
+ public static final String MESSAGE_SUCCESS = "Undo successfully.";
+ public static final String MESSAGE_FAIL = "No command to undo.";
+
+ public UndoCommand() {}
+
+ @Override
+ public CommandResult execute() {
+
+ try {
+ Context contextToUndo = urManager.getContextToUndo();
+ model.resetData(contextToUndo.getData());
+ return new CommandResult(MESSAGE_SUCCESS);
+ } catch (NoAvailableCommandException nace){
+ indicateAttemptToExecuteFailedCommand();
+ return new CommandResult(MESSAGE_FAIL);
+ }
+ }
+
+}
+```
+###### \java\seedu\address\logic\commands\ViewCommand.java
+``` java
+/**
+ * Views the agenda for the week specified by (contains) input date.
+ */
+public class ViewCommand extends Command {
+
+ public final TaskDate inputDate;
+
+ private SimpleDateFormat formatter = new SimpleDateFormat("yyyy MMM dd, EEE");
+
+ public static final String COMMAND_WORD = "view";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": View the agenda of the week specified by input date.\n"
+ + "Parameters: DATE_TIME \n"
+ + "Example: " + COMMAND_WORD + " next WednesDay";
+
+ public static final String MESSAGE_UPDATE_AGENDA_SUCCESS = "Agenda Updated to Week specified by: %1$s";
+
+ public ViewCommand(TaskDate inputDate) {
+ this.inputDate = inputDate;
+ }
+
+ @Override
+ public CommandResult execute() {
+
+ EventsCenter.getInstance().post(new AgendaTimeRangeChangedEvent(inputDate, model.getTaskMaster().getTaskComponentList()));
+ return new CommandResult(String.format(MESSAGE_UPDATE_AGENDA_SUCCESS, formatter.format(inputDate.getDate())));
+
+ }
+
+}
+```
+###### \java\seedu\address\logic\parser\Parser.java
+``` java
+ private Command prepareBlock(String args) {
+ Matcher matcher = BLOCK_DATA_ARGS_FORMAT.matcher(args.trim());
+ if (!matcher.matches()) {
+ return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, BlockCommand.MESSAGE_USAGE));
+ }
+ try {
+
+ String startInput = matcher.group("startTime");
+ String endInput = matcher.group("endTime");
+
+ return new BlockCommand(
+ getTagsFromArgs(matcher.group("tagArguments")),
+ new TaskDate(getDateFromString(startInput).getTime()),
+ new TaskDate(getDateFromString(endInput).getTime())
+ );
+ } catch (IllegalValueException ive) {
+ return new IncorrectCommand(ive.getMessage());
+ }
+ }
+```
+###### \java\seedu\address\logic\parser\Parser.java
+``` java
+ /**
+ * Parses arguments in the context of the complete task command.
+ *
+ * @param args full command args string
+ * @return the prepared command
+ */
+ private Command prepareComplete(String args) {
+
+ Optional index = parseIndex(args);
+ if(!index.isPresent()){
+ return new IncorrectCommand(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, CompleteCommand.MESSAGE_USAGE));
+ }
+
+ return new CompleteCommand(index.get());
+ }
+```
+###### \java\seedu\address\logic\parser\Parser.java
+``` java
+ /**
+ * Returns the view command with input date parsed
+ * @param arguments passed by user
+ * @return prepared view command
+ */
+ private Command prepareView(String arguments) {
+ // TODO Auto-generated method stub
+ Date date;
+ try{
+ date = getDateFromString(arguments);
+ }catch(IllegalArgumentException e){
+ return new IncorrectCommand(e.getMessage());
+ }
+ return new ViewCommand(new TaskDate(date));
+ }
+```
+###### \java\seedu\address\logic\URManager.java
+``` java
+/**
+ * Stores and provides context for undo/redo operations.
+ */
+public class URManager {
+
+ private ArrayDeque undoQueue;
+ private ArrayDeque redoQueue;
+
+ /** Arbitrarily chosen number to ensure overall performance. */
+ private final int MAX_TIMES = 3;
+
+ public URManager(){
+ undoQueue = new ArrayDeque();
+ redoQueue = new ArrayDeque();
+ }
+
+ /**
+ * Adds the command to undo queue for LogicManager.
+ */
+ public void addToUndoQueue(Model model, Command command){
+ if(!isUndoable(command)){
+ //Stop here to wait for result.
+ }else{
+ if(!isIgnored(command)){
+ if(undoQueue.size() == MAX_TIMES) undoQueue.removeFirst();
+ undoQueue.addLast(new Context(model, command));
+ redoQueue.clear();
+ }
+ }
+ }
+
+ /**
+ * Pops the failed or incorrect (but not detected during parsing) command from undo queue
+ */
+ public void popFromUndoQueue(){
+ undoQueue.removeLast();
+ }
+
+ /**
+ * Change Directory command succeeds, clear all undo and redo queue.
+ */
+ public void resetQueue(){
+ undoQueue.clear();
+ redoQueue.clear();
+ }
+
+ /**
+ * Adds the command to undo queue for redo command.
+ */
+ public void addToUndoQueueUsedByRedo(Model model, Command command){
+ if(!isUndoable(command)){
+ undoQueue.clear();
+ redoQueue.clear();
+ }else{
+ if(!isIgnored(command)){
+ if(undoQueue.size() == MAX_TIMES) undoQueue.removeFirst();
+ undoQueue.addLast(new Context(model, command));
+ }
+ }
+ }
+
+ public Context getContextToUndo() throws NoAvailableCommandException{
+ try{
+ Context contextToUndo = undoQueue.removeLast();
+ redoQueue.addLast(contextToUndo);
+ return contextToUndo;
+ } catch (Exception e){
+ throw new NoAvailableCommandException();
+ }
+ }
+
+ public Context getContextToRedo() throws NoAvailableCommandException{
+ try{
+ Context contextToRedo = redoQueue.removeLast();
+ undoQueue.addLast(contextToRedo);
+ return contextToRedo;
+ } catch (Exception e){
+ throw new NoAvailableCommandException();
+ }
+ }
+
+ /**
+ * Returns true if the command does not need to be added in undo/redo queue.
+ * Exclusion for view command is just tentative and needs further consideration.
+ */
+ public Boolean isIgnored(Command command){
+ return command instanceof RedoCommand ||
+ command instanceof UndoCommand ||
+ command instanceof ViewCommand ||
+ command instanceof IncorrectCommand;
+ }
+
+ /**
+ * Returns true if the command is undoable.
+ * Currently, only ChangeDirectoryCommand is undoable.
+ */
+ public Boolean isUndoable(Command command){
+ return !(command instanceof ChangeDirectoryCommand);
+ }
+ //=================================================================
+ public class NoAvailableCommandException extends Exception{}
+
+ /**
+ * Inner class for backup previous data and commands.
+ */
+ public class Context{
+
+ private ReadOnlyTaskMaster taskList;
+ private Command command;
+ Context(Model model, Command command){
+ this.command = command;
+ this.taskList = new TaskMaster(model.getTaskMaster());
+ }
+ public Command getCommand(){
+ return command;
+ }
+ public ReadOnlyTaskMaster getData(){
+ return taskList;
+ }
+ }
+}
+```
+###### \java\seedu\address\model\ModelManager.java
+``` java
+ @Override
+ public synchronized void archiveTask(TaskComponent target) throws TaskNotFoundException {
+ taskMaster.archiveTask(target);
+ indicateTaskListChanged();
+ updateFilteredListToShowAll();
+ }
+
+ @Override
+ public void changeDirectory(String filePath) {
+ raise(new FilePathChangeEvent(filePath));
+ }
+```
+###### \java\seedu\address\model\ModelManager.java
+``` java
+
+ //=========== Filtered Task List Accessors ===============================================================
+
+ @Override
+ public List getTaskList() {
+ return new ArrayList(tasks);
+ }
+
+ @Override
+ public UnmodifiableObservableList getFilteredTaskComponentList() {
+ return new UnmodifiableObservableList<>(filteredTaskComponents);
+ }
+
+ @Override
+ public void updateFilteredListToShowAll() {
+ filteredTaskComponents.setPredicate(new PredicateExpression(new ArchiveQualifier(true))::unsatisfies);
+ }
+
+ @Override
+ public void updateFilteredTaskList(Set keywords, Set tags, Date startDate, Date endDate, Date deadline) {
+ updateFilteredTaskList(new PredicateExpression(new FindQualifier(keywords, tags, startDate, endDate, deadline)));
+ }
+
+ private void updateFilteredTaskList(Expression expression) {
+ filteredTaskComponents.setPredicate(expression::satisfies);
+ }
+
+ //========== Inner classes/interfaces used for filtering ==================================================
+
+ interface Expression {
+ boolean satisfies(TaskComponent t);
+ String toString();
+ }
+
+ private class PredicateExpression implements Expression {
+
+ private final Qualifier qualifier;
+
+ PredicateExpression(Qualifier qualifier) {
+ this.qualifier = qualifier;
+ }
+
+ @Override
+ public boolean satisfies(TaskComponent task) {
+ return qualifier.run(task);
+ }
+
+
+ public boolean unsatisfies(TaskComponent task) {
+ return !qualifier.run(task);
+ }
+
+ @Override
+ public String toString() {
+ return qualifier.toString();
+ }
+ }
+
+ interface Qualifier {
+ boolean run(TaskComponent task);
+ String toString();
+ }
+
+```
+###### \java\seedu\address\model\ModelManager.java
+``` java
+ private class TypeQualifier implements Qualifier {
+ private TaskType typeKeyWords;
+
+ TypeQualifier(TaskType typeKeyWords) {
+ this.typeKeyWords = typeKeyWords;
+ }
+
+ @Override
+ public boolean run(TaskComponent task) {
+ return task.getTaskReference().getTaskType().equals(typeKeyWords) && !task.isArchived();
+ }
+
+ @Override
+ public String toString() {
+ return "type=" + typeKeyWords.toString();
+ }
+ }
+```
+###### \java\seedu\address\model\task\UniqueTaskList.java
+``` java
+ /**
+ * Signals that an operation adding/blocking a time slot in the list would fail because
+ * the timeslot is already occupied.
+ */
+
+ public static class TimeslotOverlapException extends DuplicateDataException {
+
+ public TimeslotOverlapException() {
+ super("Operation cannot be done due to overlapping with blocked slots.");
+ }
+ }
+```
+###### \java\seedu\address\model\task\UniqueTaskList.java
+``` java
+ /**
+ * Returns true if the given task requests to use a blocked time slot.
+ */
+ public boolean overlaps(ReadOnlyTask toCheck) {
+ assert toCheck != null;
+ //ignore floating and deadline tasks
+ if(toCheck.getComponentForNonRecurringType().getStartDate().getDateInLong() == TaskDate.DATE_NOT_PRESENT)
+ return false;
+ //Only compare tasks with blocked time slots.
+ for(Task t: internalList){
+ if(t.getName().fullName.equals(BlockCommand.DUMMY_NAME)){
+ if(!(!t.getComponentForNonRecurringType().getEndDate().getDate().after(toCheck.getComponentForNonRecurringType().getStartDate().getDate())||
+ !t.getComponentForNonRecurringType().getStartDate().getDate().before(toCheck.getComponentForNonRecurringType().getEndDate().getDate())))
+ return true;
+ }
+ }
+ //Or if it is block command, check with existing tasks
+ if(toCheck.getName().fullName.equals(BlockCommand.DUMMY_NAME)){
+ for(Task t: internalList){
+ if(t.getTaskType() == TaskType.NON_FLOATING && t.getComponentForNonRecurringType().getStartDate().getDateInLong() != TaskDate.DATE_NOT_PRESENT){
+ if(!(!t.getComponentForNonRecurringType().getEndDate().getDate().after(toCheck.getComponentForNonRecurringType().getStartDate().getDate())||
+ !t.getComponentForNonRecurringType().getStartDate().getDate().before(toCheck.getComponentForNonRecurringType().getEndDate().getDate())))
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+```
+###### \java\seedu\address\model\task\UniqueTaskList.java
+``` java
+ /** Returns true if the specified task component is successfully archived.*/
+ public boolean archive(TaskComponent target) {
+ assert target != null;
+ boolean taskFoundAndArchived = false;
+ System.out.println(internalComponentList.contains(target));
+ for(TaskComponent t : internalComponentList){
+ if(t.equals(target)){
+ t.archive();
+ taskFoundAndArchived = true;
+ t.getTaskReference().completeTaskWhenAllComponentArchived();
+ }
+ }
+ return taskFoundAndArchived;
+ }
+```
+###### \java\seedu\address\model\TaskMaster.java
+``` java
+ public boolean archiveTask(TaskComponent target) throws TaskNotFoundException {
+ // TODO Auto-generated method stub
+ if (tasks.archive(target)) {
+ return true;
+ } else {
+ throw new UniqueTaskList.TaskNotFoundException();
+ }
+ }
+```
+###### \java\seedu\address\ui\BrowserPanel.java
+``` java
+ private void initialize(ObservableList taskList){
+ agenda.setDisplayedDateTime(new TaskDate(new Date(System.currentTimeMillis())));
+ loadTaskList(taskList);
+ }
+
+ public void loadTaskPage(ReadOnlyTask task) {
+ //Deprecated method
+ }
+
+ public void updateAgenda(TaskDate inputDate, List taskList){
+ agenda.setDisplayedDateTime(inputDate);
+ loadTaskList(taskList);
+ }
+
+ public void reloadAgenda(List taskList){
+ loadTaskList(taskList);
+ }
+
+ /**
+ * Frees resources allocated to the browser.
+ */
+ public void freeResources() {
+ agenda = null;
+ }
+
+ public void loadTaskList(List taskList){
+ agenda.addAllToAgenda(taskList);
+ }
+
+ public MyAgenda getAgenda(){
+ return agenda;
+ }
+
+}
+```
+###### \java\seedu\address\ui\CommandBox.java
+``` java
+ @Subscribe
+ private void handleFailedCommandAttempted(FailedCommandAttemptedEvent event){
+ logger.info(LogsCenter.getEventHandlingLogMessage(event,"Failed command: " + previousCommandTest +"\n"));
+ setStyleToIndicateFailedCommand();
+ restoreCommandText();
+ }
+
+ /**
+ * Restores the command box text to the previously entered command
+ */
+ private void restoreCommandText() {
+ commandTextField.setText(previousCommandTest);
+ }
+
+ /**
+ * Sets the command box style to indicate an error
+ */
+ private void setStyleToIndicateIncorrectCommand() {
+ commandTextField.getStyleClass().add("error");
+ }
+
+```
+###### \java\seedu\address\ui\CommandBox.java
+``` java
+ /**
+ * Sets the command box style to indicate a failed attempt
+ */
+ private void setStyleToIndicateFailedCommand() {
+ commandTextField.getStyleClass().add("fail");
+ }
+
+ /**
+ * Returns the current style class the command box used for testing purpose.
+ * @return
+ */
+ public ObservableList getStyleClass(){
+ return commandTextField.getStyleClass();
+ }
+
+
+}
+```
+###### \java\seedu\address\ui\MyAgenda.java
+``` java
+/**
+ * This class is modified from jfxtras agenda for
+ * Happy Jim Task Master Use.
+ */
+public class MyAgenda extends Agenda{
+
+ /** Keeps track of the start and end time of agenda.*/
+ private LocalDateTime agendaStartTime;
+ private LocalDateTime agendaEndTime;
+
+ private HashSet taskSet;
+
+ /** Constructor */
+ public MyAgenda(){
+
+ super();
+
+ taskSet = new HashSet();
+
+ /** Sets preferred size */
+ setPrefSize(550, 700);
+
+ /** Sets css class. */
+ this.getStyleClass().add(MyAgenda.class.getSimpleName());
+
+ /** Disables dragging and resizing appointments. The agenda is only used as a visualization. */
+ allowDraggingProperty().set(false);
+ allowResizeProperty().set(false);
+
+ /** Disables editing via agenda */
+ setEditAppointmentCallback((Callback)(appointment)->{
+ return null;
+ });
+
+ /** Sets the locale used by calendar. */
+ setLocale(Locale.ENGLISH);
+
+ /** Computes and sets value for variables */
+ agendaStartTime = getAgendaStartDateTime();
+ agendaEndTime = getAgendaEndDateTime();
+
+ }
+
+ //=================For change agenda range================================================
+
+ /** Sets the displayed date time of the agenda to specified one. */
+ public void setDisplayedDateTime(TaskDate inputDate){
+ displayedLocalDateTime().set(getConvertedTime(inputDate).truncatedTo(ChronoUnit.DAYS));
+ agendaStartTime = getAgendaStartDateTime();
+ agendaEndTime = getAgendaEndDateTime();
+ }
+
+ /** Loads the task component to be displayed on the agenda. */
+ public void addAllToAgenda(List taskList){
+ appointments().clear();
+ taskSet.clear();
+ for(TaskComponent t:taskList) taskSet.add(t.getTaskReference());
+ for(ReadOnlyTask task: taskSet) addAllOccurrencesInWeek(task);
+
+ }
+
+ /**
+ * Adds all occurrence of a particular task in current week into agenda.
+ * Typically used for daily tasks.
+ * Under the assumption that the task component list will not keep anything earlier than today.
+ */
+ private void addAllOccurrencesInWeek(ReadOnlyTask task){
+
+ //Ignore floating tasks
+ if(task.getTaskType() == TaskType.FLOATING) return;
+ //Ignore deadline tasks
+ if(task.getTaskDateComponent().get(0).hasOnlyEndDate()) return;
+
+ int i = 0;
+ List list = task.getTaskDateComponent();
+ for(TaskComponent taskComponent: list){
+ if(!isOutsideAgenda(taskComponent)){
+ AppointmentImplLocal appointment = getAppointment(taskComponent);
+ appointments().add(appointment);
+ }
+ if(list.size() - 1 == i){
+ AppointmentImplLocal appointment = getAppointment(taskComponent);
+ addCopiesToAgenda(taskComponent, appointment);
+ }
+ i++;
+ }
+ }
+
+ /** Adds copies of future appointments to the future agenda based on recurring type.*/
+ private void addCopiesToAgenda(TaskComponent taskComponent, AppointmentImplLocal appointment){
+ switch(taskComponent.getTaskReference().getRecurringType()){
+ case YEARLY:
+ addYearlyOccurrences(appointment);
+ break;
+ case MONTHLY:
+ addMonthlyOccurrences(appointment);
+ break;
+ case WEEKLY:
+ addWeeklyOccurrences(appointment);
+ break;
+ case DAILY:
+ addDailyOccurrences(appointment);
+ break;
+ default:
+ break;
+ }
+ }
+
+ /** Returns an AppointmentImplLocal object from a task component */
+ private AppointmentImplLocal getAppointment(TaskComponent taskComponent){
+
+ AppointmentImplLocal appointment = new AppointmentImplLocal();
+ appointment.setSummary(taskComponent.getTaskReference().getName().fullName);
+ appointment.setDescription(taskComponent.getTaskReference().tagsString());
+ appointment.setStartLocalDateTime(getConvertedTime(taskComponent.getStartDate()));
+ appointment.setEndLocalDateTime(getConvertedTime(taskComponent.getEndDate()));
+ if(taskComponent.isArchived()){
+ appointment.setAppointmentGroup(new Agenda.AppointmentGroupImpl().withStyleClass("archive"));
+ }else if(taskComponent.getTaskReference().getName().fullName.equals(BlockCommand.DUMMY_NAME)){
+ appointment.setAppointmentGroup(new Agenda.AppointmentGroupImpl().withStyleClass("block"));
+ }else{
+ appointment.setAppointmentGroup(new Agenda.AppointmentGroupImpl().withStyleClass("normal"));
+ }
+ return appointment;
+
+ }
+
+ /** Computes and adds all occurrences of this daily task to agenda.*/
+ private void addDailyOccurrences(AppointmentImplLocal appointment) {
+ if(isOutsideAgenda(appointment)) return;
+ int dayOfWeek = appointment.getStartLocalDateTime().getDayOfWeek().getValue() % 7;
+ if(appointment.getEndLocalDateTime().truncatedTo(ChronoUnit.DAYS).isBefore(agendaStartTime))
+ dayOfWeek = 0;
+ for(int i = dayOfWeek; i <= 6; i++){
+ LocalDateTime start = getAppointmentStartTime(agendaStartTime.truncatedTo(ChronoUnit.DAYS),i,appointment);
+ LocalDateTime end = getAppointmentEndTime(agendaStartTime.truncatedTo(ChronoUnit.DAYS),i,appointment);
+ addToAgenda(appointment, start, end);
+ }
+ }
+
+ /** Computes and adds all occurrences of this weekly task to agenda.*/
+ private void addWeeklyOccurrences(AppointmentImplLocal appointment) {
+ if(isOutsideAgenda(appointment)) return;
+ int dayOfWeek = appointment.getStartLocalDateTime().getDayOfWeek().getValue() % 7;
+ LocalDateTime start = getAppointmentStartTime(agendaStartTime,dayOfWeek,appointment);
+ LocalDateTime end = getAppointmentEndTime(agendaStartTime,dayOfWeek,appointment);
+ addToAgenda(appointment, start, end);
+ }
+
+ /** Computes and adds all occurrences of this monthly task to agenda.*/
+ private void addMonthlyOccurrences(AppointmentImplLocal appointment) {
+ if(isOutsideAgenda(appointment)) return;
+ int dayOffset = appointment.getStartLocalDateTime().getDayOfMonth() - agendaStartTime.getDayOfMonth();
+ if(dayOffset < 0) dayOffset = 6 - (agendaEndTime.getDayOfMonth() - appointment.getStartLocalDateTime().getDayOfMonth());
+ LocalDateTime start = getAppointmentStartTime(agendaStartTime.truncatedTo(ChronoUnit.DAYS),dayOffset,appointment);
+ LocalDateTime end = getAppointmentEndTime(agendaStartTime.truncatedTo(ChronoUnit.DAYS),dayOffset,appointment);
+ addToAgenda(appointment, start, end);
+ }
+
+ /** Computes and adds all occurrences of this yearly task to agenda.*/
+ private void addYearlyOccurrences(AppointmentImplLocal appointment) {
+ if(isOutsideAgenda(appointment)) return;
+ if(appointment.getStartLocalDateTime().getDayOfYear() > agendaStartTime.getDayOfYear()+6
+ ||appointment.getStartLocalDateTime().getDayOfYear() < agendaStartTime.getDayOfYear()) return;
+ int dayOffset = appointment.getStartLocalDateTime().getDayOfYear() - agendaStartTime.getDayOfYear();
+ LocalDateTime start = getAppointmentStartTime(agendaStartTime,dayOffset,appointment);
+ LocalDateTime end = getAppointmentEndTime(agendaStartTime,dayOffset,appointment);
+ addToAgenda(appointment, start, end);
+ }
+
+ //================Utility methods================================================
+
+ /** Returns a LocalDateTime object converted from TaskDate. */
+ private LocalDateTime getConvertedTime(TaskDate t){
+ return LocalDateTime.ofInstant(new Date(t.getDateInLong()).toInstant(), ZoneId.systemDefault());
+ }
+
+ /** Returns the startTime of the agenda. */
+ private LocalDateTime getAgendaStartDateTime(){
+ LocalDateTime displayedDateTime = getDisplayedLocalDateTime().truncatedTo(ChronoUnit.DAYS);
+ int dayOfWeek = displayedDateTime.getDayOfWeek().getValue() % 7;
+ return displayedDateTime.minusDays(dayOfWeek);
+ }
+
+ /** Returns the endTime of the agenda. */
+ private LocalDateTime getAgendaEndDateTime(){
+ LocalDateTime displayedDateTime = getDisplayedLocalDateTime().truncatedTo(ChronoUnit.DAYS);
+ int dayOfWeek = displayedDateTime.getDayOfWeek().getValue() % 7;
+ return displayedDateTime.plusDays(6 - dayOfWeek);
+ }
+
+ /** Returns the startTime of the appointment. */
+ private LocalDateTime getAppointmentStartTime(LocalDateTime startPoint, int dayOffset, AppointmentImplLocal source){
+ return startPoint.plusDays(dayOffset)
+ .plusHours(source.getStartLocalDateTime().getHour())
+ .plusMinutes(source.getStartLocalDateTime().getMinute());
+ }
+
+ /** Returns the endTime of the appointment. */
+ private LocalDateTime getAppointmentEndTime(LocalDateTime startPoint, int dayOffset, AppointmentImplLocal source){
+ return startPoint.plusDays(dayOffset)
+ .plusHours(source.getEndLocalDateTime().getHour())
+ .plusMinutes(source.getEndLocalDateTime().getMinute());
+ }
+
+ /** Returns a new appointment with start and end time specified and contains same data with source. */
+ private AppointmentImplLocal copyAppointment(AppointmentImplLocal src, LocalDateTime start, LocalDateTime end){
+ AppointmentImplLocal newOne = new AppointmentImplLocal().withAppointmentGroup(src.getAppointmentGroup())
+ .withDescription(src.getDescription()).withSummary(src.getSummary());
+
+ newOne.setStartLocalDateTime(start);
+ newOne.setEndLocalDateTime(end);
+ if(start.isAfter(src.getStartLocalDateTime()))
+ newOne.setAppointmentGroup(new Agenda.AppointmentGroupImpl().withStyleClass("normal"));
+ return newOne;
+ }
+
+ /** Adds a new appointment to agenda. */
+ private void addToAgenda(AppointmentImplLocal appointment, LocalDateTime start, LocalDateTime end){
+ AppointmentImplLocal newAppointment = copyAppointment(appointment, start, end);
+ if(!appointments().contains(newAppointment) && !onSameDay(newAppointment, appointment))
+ appointments().add(newAppointment);
+ }
+
+ /** Returns true if it is a future task that is not needed to add to agenda. */
+ private boolean isOutsideAgenda(AppointmentImplLocal appointment){
+ return appointment.getStartLocalDateTime().truncatedTo(ChronoUnit.DAYS).isAfter(agendaEndTime);
+ }
+
+ /** Returns true if it is a future task that is not needed to add to agenda. */
+ private boolean isOutsideAgenda(TaskComponent taskComponent){
+ return getConvertedTime(taskComponent.getStartDate()).truncatedTo(ChronoUnit.DAYS).isAfter(agendaEndTime)
+ || getConvertedTime(taskComponent.getEndDate()).truncatedTo(ChronoUnit.DAYS).isBefore(agendaStartTime);
+ }
+
+ /** Returns true if the appointments are on the same day. */
+ private boolean onSameDay(AppointmentImplLocal src, AppointmentImplLocal toCheck){
+ return src.getStartLocalDateTime().truncatedTo(ChronoUnit.DAYS).equals(toCheck.getStartLocalDateTime().truncatedTo(ChronoUnit.DAYS));
+ }
+
+}
+```
+###### \java\seedu\address\ui\NavbarPanel.java
+``` java
+ private void setEventHandlerForSelectionChangeEvent() {
+ navbarView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
+ if (newValue != null) {
+ logger.fine("Selection in navigation bar panel changed to : '" + newValue + "'");
+ raise(new NavigationSelectionChangedEvent(newValue));
+ }
+ });
+ }
+
+ public void scrollTo(int index) {
+ Platform.runLater(() -> {
+ navbarView.scrollTo(index);
+ navbarView.getSelectionModel().clearAndSelect(index);
+ });
+ }
+
+```
+###### \java\seedu\address\ui\NavbarPanel.java
+``` java
+ public String getNavigationCommand(String navigation){
+ switch(navigation){
+
+ case NAVBAR_DEADLINES:
+ day = new Date(System.currentTimeMillis()+24*60*60*1000);
+ command = FindCommand.COMMAND_WORD +" by "+ formatter.format(day) + " 12am";
+ return command;
+ case NAVBAR_INCOMING_DEADLINES:
+ day = new Date(System.currentTimeMillis()+24*8*60*60*1000);
+ command = FindCommand.COMMAND_WORD +" by "+ formatter.format(day) + " 12am";
+ return command;
+ case NAVBAR_FLOATING_TASKS:
+ command = FindCommand.COMMAND_WORD +" -F";
+ return command;
+ case NAVBAR_COMPLETED:
+ command = FindCommand.COMMAND_WORD +" -C";
+ return command;
+ default:
+ return ListCommand.COMMAND_WORD;
+ }
+ }
+
+
+
+
+ class NavbarViewCell extends ListCell {
+
+ public NavbarViewCell() {
+ }
+
+ @Override
+ protected void updateItem(String li, boolean empty) {
+ super.updateItem(li, empty);
+
+ if (empty || li == null) {
+ setGraphic(null);
+ setText(null);
+ } else {
+ setGraphic(NavbarCard.load(li).getLayout());
+ }
+ }
+ }
+}
+```
+###### \java\seedu\address\ui\TaskCard.java
+``` java
+ private void initializeDate() {
+ if (dateComponent.getStartDate().getDateInLong() == TaskDate.DATE_NOT_PRESENT) {
+ startDate.setText("");
+ } else {
+ startDate.setText(dateComponent.getStartDate().getFormattedDate());
+ }
+
+ if (dateComponent.getEndDate().getDateInLong() == TaskDate.DATE_NOT_PRESENT) {
+ endDate.setText("");
+ } else {
+ endDate.setText(dateComponent.getEndDate().getFormattedDate());
+ }
+ }
+
+ /** Sets cell color for the task list. Style the css here to prevent overriding. */
+ private void setCellColor(){
+ //normal non-floating task
+ cardPane.setStyle("-fx-background-color : rgba(110, 196, 219, 0.3);");
+ //Deadline
+ if(dateComponent.hasOnlyEndDate())
+ cardPane.setStyle("-fx-background-color : rgba(250, 124, 146, 0.3);");
+ //Floating task
+ if(task.getTaskType() == TaskType.FLOATING)
+ cardPane.setStyle("-fx-background-color : rgba(255, 247, 192, 0.3);");
+ //Blocked Slot
+ if(task.getName().fullName.equals("BLOCKED SLOT"))
+ cardPane.setStyle("-fx-background-color : rgba(148, 93, 96, 0.3);");
+ //Completed
+ if(dateComponent.isArchived()){
+ cardPane.setStyle("-fx-background-color : rgba(102,171,140,0.3);");
+ name.setStyle("-fx-text-fill : derive(#373737, 20%);");
+ id.setStyle("-fx-text-fill : derive(#373737, 20%);");
+ startDate.setStyle("-fx-text-fill : derive(#373737, 20%);");
+ endDate.setStyle("-fx-text-fill : derive(#373737, 20%);");
+ recurringType.setStyle("-fx-text-fill : derive(#373737, 20%);");
+ }
+ }
+
+ public HBox getLayout() {
+ return cardPane;
+ }
+
+ @Override
+ public void setNode(Node node) {
+ cardPane = (HBox)node;
+ }
+
+ @Override
+ public String getFxmlPath() {
+ return FXML;
+ }
+}
+```
+###### \java\seedu\address\ui\UiManager.java
+``` java
+ @Subscribe
+ private void handleNavigationSelectionChangedEvent(NavigationSelectionChangedEvent event){
+ logger.info(LogsCenter.getEventHandlingLogMessage(event));
+ mainWindow.getCommandBox().handleNavigationChanged(mainWindow.getNavbarPanel().getNavigationCommand(event.getNewSelection()));
+ }
+
+ @Subscribe
+ private void handleTaskListChangedEvent(TaskListChangedEvent event){
+ logger.info(LogsCenter.getEventHandlingLogMessage(event));
+ mainWindow.getBrowserPanel().reloadAgenda(event.data.getTaskComponentList());
+ }
+
+ @Subscribe
+ private void handleAgendaTimeRangeChangedEvent(AgendaTimeRangeChangedEvent event){
+ logger.info(LogsCenter.getEventHandlingLogMessage(event));
+ mainWindow.getBrowserPanel().updateAgenda(event.getInputDate(), event.getData());
+ }
+
+}
+```
+###### \resources\view\BrowserPanel.fxml
+``` fxml
+
+
+
+
+
+
+
+```
+###### \resources\view\MainWindow.fxml
+``` fxml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
diff --git a/collated/main/A0147967Jreused.md b/collated/main/A0147967Jreused.md
new file mode 100644
index 000000000000..e4ce7198661e
--- /dev/null
+++ b/collated/main/A0147967Jreused.md
@@ -0,0 +1,651 @@
+# A0147967Jreused
+###### \java\seedu\address\ui\AutoCompleteTextField.java
+``` java
+/**
+ * This class extends javafx text field for auto complete
+ * implementation for Happy Jim Task Master.
+ * Reference: https://gist.github.com/floralvikings/10290131
+ */
+public class AutoCompleteTextField extends TextField
+{
+ /** Stores command, syntax and utility words.*/
+ private final SortedSet dictionary;
+
+ /** Pops up the dictionary words. */
+ private ContextMenu dictionaryPopup;
+
+ /** Determines whether to turn on the auto-complete function. */
+ public boolean turnOn = true;
+
+ /** Constructor */
+ public AutoCompleteTextField() {
+
+ super();
+
+ dictionary = new TreeSet<>();
+ setDictionary();
+ dictionaryPopup = new ContextMenu();
+
+
+ textProperty().addListener(new ChangeListener(){
+ @Override
+ public void changed(ObservableValue extends String> observableValue, String oldString, String newString) {
+ if (getText().length() == 0 || getCurrentWord() == null){
+ dictionaryPopup.hide();
+ } else {
+ LinkedList searchResult = new LinkedList<>();
+ searchResult.addAll(dictionary.subSet(getCurrentWord(), getCurrentWord() + Character.MAX_VALUE));
+ if (dictionary.size() > 0){
+ popup(searchResult);
+ if (!dictionaryPopup.isShowing() && turnOn){
+ dictionaryPopup.show(AutoCompleteTextField.this, Side.BOTTOM, getText().length()*8, 0);
+ }
+ } else {
+ dictionaryPopup.hide();
+ }
+ }
+ }
+ });
+
+ focusedProperty().addListener(new ChangeListener() {
+ @Override
+ public void changed(ObservableValue extends Boolean> observableValue, Boolean oldValue, Boolean newValue) {
+ dictionaryPopup.hide();
+ }
+ });
+ }
+
+ /**
+ * Pop out the entry set with the given search results.
+ */
+ private void popup(List searchResult) {
+
+ List menuItems = new LinkedList<>();
+
+ for (String result: searchResult){
+
+ Label entryLabel = new Label(result);
+ CustomMenuItem item = new CustomMenuItem(entryLabel, true);
+ item.setOnAction(new EventHandler(){
+ @Override
+ public void handle(ActionEvent actionEvent) {
+ setText(getPreviousWords() + result + " ");
+ dictionaryPopup.hide();
+ positionCaret(getText().length());
+ }
+ });
+
+ menuItems.add(item);
+ }
+ dictionaryPopup.getItems().clear();
+ dictionaryPopup.getItems().addAll(menuItems);
+ }
+
+ /**
+ * Sets the data of the dictionary used. Naive method.
+ */
+ private void setDictionary(){
+
+ //syntax words
+ dictionary.add("from");
+ dictionary.add("to");
+ dictionary.add("by");
+ dictionary.add("t/");
+ //command word
+ String[] commandWords = {"add","block","cd","clear","delete","done","edit","help","u","r","find","list","select","exit"};
+ for(String s: commandWords) dictionary.add(s);
+ //date
+ String[] dateWords = {"jan","feb","mar","apr","may","jun","jul",
+ "aug","sep","oct","nov","dec","today","tomorrow","yesterday",
+ "monday","tuesday","wednesday","thursday","friday","saturday","sunday",
+ "daily", "weekly", "monthly", "yearly","next",
+ "day", "week", "year"};
+ for(String s: dateWords) dictionary.add(s);
+ }
+
+ /**
+ * Returns current word that should be provided suggestions.
+ */
+ private String getCurrentWord(){
+ if(getText().endsWith(" ")) return null;
+ String[] enteredWords = getText().split(" ");
+ return enteredWords[enteredWords.length - 1];
+ }
+
+ /**
+ * Returns existing text for future text modification.
+ */
+ private String getPreviousWords(){
+ return getText().substring(0, getText().length() - getCurrentWord().length());
+ }
+}
+```
+###### \java\seedu\address\ui\BrowserPanel.java
+``` java
+/**
+ * The Browser Panel of the App modified to display the agenda.
+ */
+public class BrowserPanel extends UiPart{
+
+ private static Logger logger = LogsCenter.getLogger(BrowserPanel.class);
+ private static final String FXML = "BrowserPanel.fxml";
+ private VBox panel;
+ private AnchorPane placeHolderPane;
+
+ @FXML
+ private MyAgenda agenda;
+
+ /**
+ * Constructor is kept private as {@link #load(AnchorPane)} is the only way to create a BrowserPanel.
+ */
+ @Override
+ public void setNode(Node node) {
+ panel = (VBox) node;
+ }
+
+ @Override
+ public String getFxmlPath() {
+ return FXML;
+ //not applicable
+ }
+
+ @Override
+ public void setPlaceholder(AnchorPane pane) {
+ this.placeHolderPane = pane;
+ }
+
+ /**
+ * Factory method for creating a Browser Panel.
+ * This method should be called after the FX runtime is initialized and in FX application thread.
+ * @param placeholder The AnchorPane where the BrowserPanel must be inserted
+ */
+ public static BrowserPanel load(Stage primaryStage, AnchorPane browserPanelPlaceholder,
+ ObservableList taskList){
+ logger.info("Initializing Agenda");
+ BrowserPanel browserPanel =
+ UiPartLoader.loadUiPart(primaryStage, browserPanelPlaceholder, new BrowserPanel());
+ browserPanel.initialize(taskList);
+ FxViewUtil.applyAnchorBoundaryParameters(browserPanel.agenda, 0.0, 0.0, 0.0, 0.0);
+ browserPanel.placeHolderPane.getChildren().add(browserPanel.panel);
+ return browserPanel;
+ }
+
+```
+###### \java\seedu\address\ui\CommandBox.java
+``` java
+ public void handleNavigationChanged(String command) {
+
+ previousCommandTest = command;
+
+ mostRecentResult = logic.execute(previousCommandTest);
+ resultDisplay.postMessage(mostRecentResult.feedbackToUser);
+ logger.info("Result: " + mostRecentResult.feedbackToUser);
+ }
+
+ public void turnOffAutoComplete(){
+ commandTextField.turnOn = false;
+ }
+
+ /**
+ * Sets the command box style to indicate a correct command.
+ */
+ private void setStyleToIndicateCorrectCommand() {
+ commandTextField.getStyleClass().remove("error");
+ commandTextField.getStyleClass().remove("fail");
+ commandTextField.setText("");
+ }
+
+ @Subscribe
+ private void handleIncorrectCommandAttempted(IncorrectCommandAttemptedEvent event){
+ logger.info(LogsCenter.getEventHandlingLogMessage(event,"Invalid command: " + previousCommandTest));
+ setStyleToIndicateIncorrectCommand();
+ restoreCommandText();
+ }
+
+```
+###### \resources\view\DarkTheme.css
+``` css
+.background {
+ -fx-background-color: derive(#6b7a8f, 20%);
+}
+
+.label {
+ -fx-font-size: 11pt;
+ -fx-font-family: "Segoe UI Semibold";
+ -fx-text-fill: #555555;
+ -fx-opacity: 0.9;
+}
+
+.label-bright {
+ -fx-font-size: 11pt;
+ -fx-font-family: "Segoe UI Semibold";
+ -fx-text-fill: white;
+ -fx-opacity: 1;
+}
+
+.label-header {
+ -fx-font-size: 32pt;
+ -fx-font-family: "Segoe UI Light";
+ -fx-text-fill: white;
+ -fx-opacity: 1;
+}
+
+.auto-complete-text-field {
+ -fx-font-size: 12pt;
+ -fx-font-family: "Segoe UI Semibold";
+}
+
+.tab-pane {
+ -fx-padding: 0 0 0 1;
+}
+
+.tab-pane .tab-header-area {
+ -fx-padding: 0 0 0 0;
+ -fx-min-height: 0;
+ -fx-max-height: 0;
+}
+
+.split-pane:horizontal .split-pane-divider {
+ -fx-border-color: transparent #6b7a8f transparent #6b7a8f;
+ -fx-background-color: transparent, derive(#6b7a8f, 10%);
+}
+
+.split-pane {
+ -fx-border-radius: 1;
+ -fx-border-width: 1;
+ -fx-background-color: derive(#6b7a8f, 20%);
+}
+
+.list-cell {
+ -fx-label-padding: 0 0 0 0;
+ -fx-graphic-text-gap : 0;
+ -fx-padding: 0 0 0 0;
+}
+
+.list-cell .label {
+ -fx-text-fill: #010504;
+}
+
+#navbarView .list-cell {
+ -fx-label-padding: 0 0 0 0;
+ -fx-graphic-text-gap : 10;
+ -fx-padding: 0 0 0 0;
+ -fx-background-color : derive(#6b7a8f, 20%);
+}
+#navbarView .list-cell .label {
+ -fx-font-size: 20px;
+ -fx-font-family: "Segoe UI";
+ -fx-text-fill: derive(#F0F0F0, 80%);
+ -fx-opacity: 1;
+ -fx-text-alignment: center;
+}
+
+#navbarView .list-cell:filled:selected:focused, #navbarView .list-cell:filled:selected {
+ -fx-background-color: derive(#30415d, 50%);
+ -fx-text-fill: white;
+}
+
+#navbarView .list-cell:even:filled {
+ -fx-background-color: derive(#f7882f, 20%);
+}
+
+#navbarView .list-cell:odd:filled {
+ -fx-background-color: derive(#f7c331, 50%);
+}
+#navbarView .list-cell:odd:filled .image-view{
+ -fx-image:url('/images/ddl_icon.png');
+}
+#navbarView .list-cell:even:filled .image-view{
+ -fx-image:url('/images/task_icon.png');
+ -fx-alignment:left;
+}
+
+
+.cell_big_label {
+ -fx-font-size: 16px;
+ -fx-text-fill: #010504;
+}
+
+.cell_small_label {
+ -fx-font-size: 11px;
+ -fx-text-fill: #010504;
+}
+
+.anchor-pane {
+ -fx-background-color: derive(#a7b3a5, 20%);
+}
+
+.anchor-pane-with-border {
+ -fx-background-color: derive(#6b7a8f, 20%);
+ -fx-border-color: derive(#6b7a8f, 10%);
+ -fx-border-top-width: 1px;
+ -fx-text-alignment: center;
+}
+
+.status-bar {
+ -fx-background-color: derive(#6b7a8f, 20%);
+ -fx-text-fill: black;
+}
+
+.result-display {
+ -fx-background-color: #F0F0F0;
+}
+
+.result-display .label {
+ -fx-text-fill: black !important;
+ -fx-text-alignment: center;
+}
+
+.status-bar .label {
+ -fx-text-fill: white;
+}
+
+.status-bar-with-border {
+ -fx-background-color: derive(#6b7a8f, 30%);
+ -fx-border-color: derive(#6b7a8f, 25%);
+ -fx-border-width: 1px;
+}
+
+.status-bar-with-border .label {
+ -fx-text-fill: white;
+}
+
+.grid-pane {
+ -fx-background-color: derive(#6b7a8f, 30%);
+ -fx-border-color: derive(#6b7a8f, 30%);
+ -fx-border-width: 1px;
+}
+
+.grid-pane .anchor-pane {
+ -fx-background-color: derive(#6b7a8f, 30%);
+}
+
+.context-menu {
+ -fx-background-color: derive(#6b7a8f, 50%);
+}
+
+.context-menu .label {
+ -fx-text-fill: white;
+}
+
+.menu-bar {
+ -fx-background-color: derive(#6b7a8f, 20%);
+}
+
+.menu-bar .label {
+ -fx-font-size: 14pt;
+ -fx-font-family: "Segoe UI Light";
+ -fx-text-fill: white;
+ -fx-opacity: 0.9;
+}
+
+.menu .left-container {
+ -fx-background-color: black;
+}
+
+/*
+ * Metro style Push Button
+ * Author: Pedro Duque Vieira
+ * http://pixelduke.wordpress.com/2012/10/23/jmetro-windows-8-controls-on-java/
+ */
+.button {
+ -fx-padding: 5 22 5 22;
+ -fx-border-color: #e2e2e2;
+ -fx-border-width: 2;
+ -fx-background-radius: 0;
+ -fx-background-color: #6b7a8f;
+ -fx-font-family: "Segoe UI", Helvetica, Arial, sans-serif;
+ -fx-font-size: 11pt;
+ -fx-text-fill: #d8d8d8;
+ -fx-background-insets: 0 0 0 0, 0, 1, 2;
+}
+
+.button:hover {
+ -fx-background-color: #3a3a3a;
+}
+
+.button:pressed, .button:default:hover:pressed {
+ -fx-background-color: white;
+ -fx-text-fill: #6b7a8f;
+}
+
+.button:focused {
+ -fx-border-color: white, white;
+ -fx-border-width: 1, 1;
+ -fx-border-style: solid, segments(1, 1);
+ -fx-border-radius: 0, 0;
+ -fx-border-insets: 1 1 1 1, 0;
+}
+
+.button:disabled, .button:default:disabled {
+ -fx-opacity: 0.4;
+ -fx-background-color: #6b7a8f;
+ -fx-text-fill: white;
+}
+
+.button:default {
+ -fx-background-color: -fx-focus-color;
+ -fx-text-fill: #ffffff;
+}
+
+.button:default:hover {
+ -fx-background-color: derive(-fx-focus-color, 30%);
+}
+
+.dialog-pane {
+ -fx-background-color: #6b7a8f;
+}
+
+.dialog-pane > *.button-bar > *.container {
+ -fx-background-color: #6b7a8f;
+}
+
+.dialog-pane > *.label.content {
+ -fx-font-size: 14px;
+ -fx-font-weight: bold;
+ -fx-text-fill: white;
+}
+
+.dialog-pane:header *.header-panel {
+ -fx-background-color: derive(#6b7a8f, 25%);
+}
+
+.dialog-pane:header *.header-panel *.label {
+ -fx-font-size: 18px;
+ -fx-font-style: italic;
+ -fx-fill: white;
+ -fx-text-fill: white;
+}
+
+.scroll-bar .thumb {
+ -fx-background-color: derive(#6b7a8f, 50%);
+ -fx-background-insets: 3;
+}
+
+.scroll-bar .increment-button, .scroll-bar .decrement-button {
+ -fx-background-color: transparent;
+ -fx-padding: 0 0 0 0;
+}
+
+.scroll-bar .increment-arrow, .scroll-bar .decrement-arrow {
+ -fx-shape: " ";
+}
+
+.scroll-bar:vertical .increment-arrow, .scroll-bar:vertical .decrement-arrow {
+ -fx-padding: 1 8 1 8;
+}
+
+.scroll-bar:horizontal .increment-arrow, .scroll-bar:horizontal .decrement-arrow {
+ -fx-padding: 8 1 8 1;
+}
+
+#cardPane {
+ -fx-background-color: transparent;
+ -fx-border-color: #d6d6d6;
+ -fx-border-width: 1 1 1 1;
+}
+
+#cardPaneNav {
+ -fx-background-color: transparent;
+ -fx-border-color: derive(#6b7a8f, 10%);
+ -fx-border-width: 0.5 0.5 0.5 0.5;
+}
+
+#commandTypeLabel {
+ -fx-font-size: 11px;
+ -fx-text-fill: #F70D1A;
+}
+
+#filterField, #personListPanel, #personWebpage {
+ -fx-effect: innershadow(gaussian, black, 10, 0, 0, 0);
+}
+```
+###### \resources\view\Extensions.css
+``` css
+.error {
+ -fx-background-color: red;
+}
+
+.fail {
+ -fx-background-color: orange;
+}
+
+.tag-selector {
+ -fx-border-width: 1;
+ -fx-border-color: white;
+ -fx-border-radius: 3;
+ -fx-background-radius: 3;
+}
+
+.tooltip-text {
+ -fx-text-fill: white;
+}
+
+```
+###### \resources\view\MyAgenda.css
+``` css
+.MyAgenda .Week {
+ -fx-background-color: #efefef90;
+}
+
+.MyAgenda .HourLabel {
+ -fx-fill: #25242c;
+ -fx-font-family: "Segue UI Semibold";
+ -fx-alignment: center;
+ -fx-stroke: transparent;
+}
+
+.MyAgenda .HourLine {
+ -fx-fill: transparent;
+ -fx-stroke: LIGHTGRAY;
+}
+
+.MyAgenda .HalfHourLine {
+ -fx-fill: transparent;
+ -fx-stroke: LIGHTGRAY;
+ -fx-stroke-dash-array: 4 4 4 4;
+}
+
+.MyAgenda .DayHeader {
+ -fx-border-color:LIGHTGRAY;
+ -fx-text-fill: #25242c;
+ -fx-font-family:"Segue UI Semibold";
+ -fx-border-width: 0px 0px 0px 1px;
+ -fx-background-color: #efefef90;
+}
+
+
+.MyAgenda .DayHeader .weekend {
+ -fx-text-fill: #25242c;
+ -fx-font-family:"Segue UI Semibold";
+ -fx-font-weight: bold;
+
+}
+
+
+.MyAgenda .Day {
+ -fx-fill: transparent;
+ -fx-stroke: transparent;
+ -fx-border-color:LIGHTGRAY;
+ -fx-border-width: 0px 0px 0px 1px;
+}
+
+.MyAgenda .weekend {
+ -fx-background-color: rgba(221, 221, 214, 0.7);
+}
+
+.MyAgenda .today {
+ -fx-background-color: rgba(247, 155, 46, 0.3);
+ -fx-border-color: RED;
+ -fx-border-width: 1px;
+ -fx-background-insets: -1.4, 0, 1, 2;
+ -fx-background-radius: 6.4, 5, 4, 3;
+}
+
+.MyAgenda .Appointment {
+ -fx-border-color: WHITE;
+ -fx-border-width: 1px;
+}
+
+.MyAgenda .AppointmentTimeLabel {
+ -fx-fill: #32313b;
+ -fx-font-fmaily:"Segue UI Light";
+ -fx-stroke: transparent;
+ -fx-font-size: 0.8em;
+}
+
+.MyAgenda .AppointmentLabel {
+ -fx-fill: #152737;
+ -fx-font-fmaily:"Segue UI Light";
+ -fx-stroke: transparent;
+}
+
+.group0 { -fx-background-color: #AC725E; -fx-fill: #AC725E; }
+.archive { -fx-background-color: rgba(102,171,140,0.8); -fx-fill: rgba(102,171,140,0.8); }
+.group2 { -fx-background-color: #F83A22; -fx-fill: #F83A22; }
+.group3 { -fx-background-color: #FA573C; -fx-fill: #FA573C; }
+.group4 { -fx-background-color: #FF7537; -fx-fill: #FF7537; }
+.group5 { -fx-background-color: #FFAD46; -fx-fill: #FFAD46; }
+.block { -fx-background-color: rgba(148, 93, 96, 0.8); -fx-fill: rgba(148, 93, 96, 0.8); }
+.group7 { -fx-background-color: #16A765; -fx-fill: #16A765; }
+.group8 { -fx-background-color: #7BD148; -fx-fill: #7BD148; }
+.normal { -fx-background-color: rgba(110, 196, 219, 0.8); -fx-fill: rgba(110, 196, 219, 0.8); }
+.group10 { -fx-background-color: #FBE983; -fx-fill: #FBE983; }
+.group11 { -fx-background-color: #FAD165; -fx-fill: #FAD165; }
+.group12 { -fx-background-color: #92E1C0; -fx-fill: #92E1C0; }
+.group13 { -fx-background-color: #9FE1E7; -fx-fill: #9FE1E7; }
+.group14 { -fx-background-color: #9FC6E7; -fx-fill: #9FC6E7; }
+.group15 { -fx-background-color: #4986E7; -fx-fill: #4986E7; }
+.group16 { -fx-background-color: #9A9CFF; -fx-fill: #9A9CFF; }
+.group17 { -fx-background-color: #B99AFF; -fx-fill: #B99AFF; }
+.group18 { -fx-background-color: #C2C2C2; -fx-fill: #C2C2C2; }
+.group19 { -fx-background-color: #CABDBF; -fx-fill: #CABDBF; }
+.group20 { -fx-background-color: #CCA6AC; -fx-fill: #CCA6AC; }
+.group21 { -fx-background-color: #F691B2; -fx-fill: #F691B2; }
+.group22 { -fx-background-color: #CD74E6; -fx-fill: #CD74E6; }
+.group23 { -fx-background-color: #A47AE2; -fx-fill: #A47AE2; }
+
+.MyAgenda .GhostRectangle {
+ -fx-fill: rgba(247, 155, 46, 0.3);
+ -fx-stroke: #00000080;
+ -fx-stroke-width: 1;
+ -fx-stroke-dash-array: 4 4 4 4;
+}
+
+.MyAgenda .Selected {
+ -xfx-color: -fx-focused-base;
+ -fx-background-color: -fx-focus-color, -fx-outer-border, -fx-inner-border, -fx-body-color;
+ -fx-background-insets: -1.4, 0, 1, 2;
+ -fx-background-radius: 6.4, 5, 4, 3;
+}
+
+.MyAgenda .Now {
+ -fx-fill: #FF000088;
+ -fx-stroke: RED;
+ -fx-stroke-width: 2;
+}
+.MyAgenda .History {
+ -fx-stroke: transparent;
+ -fx-fill: #EFEFEF70;
+}
+```
diff --git a/collated/main/A0147995H.md b/collated/main/A0147995H.md
new file mode 100644
index 000000000000..4b8424daf91d
--- /dev/null
+++ b/collated/main/A0147995H.md
@@ -0,0 +1,627 @@
+# A0147995H
+###### \java\seedu\address\logic\commands\EditCommand.java
+``` java
+package seedu.address.logic.commands;
+
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+
+import seedu.address.commons.core.Messages;
+import seedu.address.commons.core.UnmodifiableObservableList;
+import seedu.address.commons.exceptions.IllegalValueException;
+import seedu.address.model.tag.Tag;
+import seedu.address.model.tag.UniqueTagList;
+import seedu.address.model.task.Name;
+import seedu.address.model.task.RecurringType;
+import seedu.address.model.task.Task;
+import seedu.address.model.task.TaskComponent;
+import seedu.address.model.task.TaskDate;
+import seedu.address.model.task.UniqueTaskList.TaskNotFoundException;
+import seedu.address.model.task.UniqueTaskList.TimeslotOverlapException;
+
+public class EditCommand extends Command {
+
+ public static final String COMMAND_WORD = "edit";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": edit a specific task (specified by its index) "
+ + "Parameters: TASK_INDEX [NEW_TASK_NAME]"
+ + "[from DATE to DATE | by DEADLINE]"
+ + "[t/TAGS]\n"
+ + "Example: " + COMMAND_WORD + " 1 a task by today 9pm";
+
+ public static final String MESSAGE_EDIT_TASK_SUCCESS = "Edit Task: %1$s";
+ public static final String MESSAGE_TIMESLOT_OCCUPIED = "This timeslot is already blocked or overlapped with existing tasks.";
+ public static final String MESSAGE_ILLEGAL_TIME_SLOT = "End time must be later than Start time.";
+
+ private final Name taskName;
+ private final UniqueTagList tags;
+ private final TaskDate startDate;
+ private final TaskDate endDate;
+ private final int targetIndex;
+ private final RecurringType recurringType;
+
+ private Name constructName(String taskName) throws IllegalValueException {
+ if (taskName.isEmpty())
+ return null;
+ return new Name(taskName);
+ }
+
+ private UniqueTagList constructTagList(Set tags) throws IllegalValueException {
+ if(tags.size() > 0) {
+ final Set tagSet = new HashSet<>();
+ for (String tagName : tags) {
+ tagSet.add(new Tag(tagName));
+ }
+ return new UniqueTagList(tagSet);
+ }
+ return null;
+ }
+
+ private TaskDate constructTaskDate(Date date) {
+ if(date != null) {
+ return new TaskDate(date.toString());
+ }
+ return null;
+ }
+
+ public EditCommand(int targetIndex, String taskName, Set tags, Date startDate, Date endDate, RecurringType recurringType) throws IllegalValueException {
+ this.targetIndex = targetIndex;
+ this.taskName = constructName(taskName);
+ this.tags = constructTagList(tags);
+ this.startDate = constructTaskDate(startDate);
+ this.endDate = constructTaskDate(endDate);
+ this.recurringType = recurringType;
+ }
+
+
+ @Override
+ public CommandResult execute() {
+ UnmodifiableObservableList lastShownList = model.getFilteredTaskComponentList();
+
+ if (lastShownList.size() < targetIndex) {
+ indicateAttemptToExecuteIncorrectCommand();
+ urManager.popFromUndoQueue();
+ return new CommandResult(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX);
+ }
+
+ if (startDate != null && endDate != null && startDate.getDateInLong() > endDate.getDateInLong()) {
+ return new CommandResult(MESSAGE_ILLEGAL_TIME_SLOT);
+ }
+
+ TaskComponent taskToEdit = lastShownList.get(targetIndex - 1);
+ Task targetTask = (Task) taskToEdit.getTaskReference();
+ try {
+ model.editTask(targetTask, taskName, tags, startDate, endDate, recurringType);
+ } catch (TaskNotFoundException e) {
+ assert false : "The target task cannot be missing";
+ } catch (TimeslotOverlapException e) {
+ indicateAttemptToExecuteFailedCommand();
+ urManager.popFromUndoQueue();
+ return new CommandResult(MESSAGE_TIMESLOT_OCCUPIED);
+ }
+ return new CommandResult(String.format(MESSAGE_EDIT_TASK_SUCCESS, targetTask));
+ }
+
+}
+```
+###### \java\seedu\address\logic\commands\FindCommand.java
+``` java
+package seedu.address.logic.commands;
+
+import java.util.Date;
+import java.util.Set;
+
+/**
+ * Finds and lists all tasks in address book whose name contains any of the argument keywords.
+ * Keyword matching is case sensitive.
+ */
+public class FindCommand extends Command {
+
+ public static final String COMMAND_WORD = "find";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all tasks whose names contain any of "
+ + "the specified keywords (case-sensitive) and displays them as a list with index numbers.\n"
+ + "Parameters: KEYWORD [MORE_KEYWORDS]...\n"
+ + "Example: " + COMMAND_WORD + " take trash ";
+
+ private final Set keywords;
+ private final Date startTime;
+ private final Date endTime;
+ private final Date deadline;
+ private final Set tags;
+
+ public FindCommand(Set keywords, Date startTime, Date endTime, Date deadline, Set tags) {
+ this.keywords = keywords;
+ this.startTime = startTime;
+ this.endTime = endTime;
+ this.deadline = deadline;
+ this.tags = tags;
+ }
+
+ @Override
+ public CommandResult execute() {
+ model.updateFilteredTaskList(keywords, tags, startTime, endTime, deadline);
+ return new CommandResult(getMessageForTaskListShownSummary(model.getFilteredTaskComponentList().size()));
+ }
+
+}
+```
+###### \java\seedu\address\logic\parser\Parser.java
+``` java
+ /**
+ * Parses arguments in the context of the find task command.
+ *
+ * @param args full command args string
+ * @return the prepared command
+ */
+ private Command prepareFind(String args) {
+ if(args == null || args.length() == 0)
+ return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ FindCommand.MESSAGE_USAGE));
+ final Matcher noDateMatcher = FIND_ARGS_WITHOUT_DATE_FORMAT.matcher(args.trim());
+ final Matcher dateMatcher = FIND_ARGS_WITH_DATE_FORMAT.matcher(args.trim());
+ final Matcher tagMatcher = FIND_ARGS_WITH_TAG_FORMAT.matcher(args.trim());
+ final Matcher noKeywordMatcher = FIND_ARGS_WITHOUT_KEYWORD_FORMAT.matcher(args.trim());
+
+ Set keywordSet = new HashSet();
+ Date startTime = null;
+ Date endTime = null;
+ Date deadline = null;
+ Set tagSet = new HashSet();
+
+ boolean dateMatcherMatches = dateMatcher.matches();
+ boolean noDateMatcherMatches = noDateMatcher.matches();
+ boolean tagMatcherMatches = tagMatcher.matches();
+ boolean noKeywordMatcherMathces = noKeywordMatcher.matches();
+
+ if(dateMatcherMatches) {
+ String[] keywords = dateMatcher.group("keywords").split("\\s+");
+ keywordSet = new HashSet<>(Arrays.asList(keywords));
+ try {
+ ArrayList dateSet = extractDateInfo(dateMatcher);
+ if(dateSet.size() == ONLY_DEADLINE) {
+ deadline = dateSet.get(DEADLINE_INDEX);
+ } else if(dateSet.size() == TIME_PERIOD) {
+ startTime = dateSet.get(START_TIME_INDEX);
+ endTime = dateSet.get(END_TIME_INDEX);
+ }
+ } catch(IllegalArgumentException iae) {
+ return new IncorrectCommand(iae.getMessage());
+ }
+
+ try {
+ tagSet = getTagsFromArgs(noDateMatcher.group("tagArguments"));
+ } catch(IllegalValueException ive) {
+ return new IncorrectCommand(ive.getMessage());
+ }
+ } else if(noKeywordMatcherMathces) {
+ try {
+ ArrayList dateSet = extractDateInfo(noKeywordMatcher);
+ if(dateSet.size() == ONLY_DEADLINE) {
+ deadline = dateSet.get(DEADLINE_INDEX);
+ } else if(dateSet.size() == TIME_PERIOD) {
+ startTime = dateSet.get(START_TIME_INDEX);
+ endTime = dateSet.get(END_TIME_INDEX);
+ }
+ } catch(IllegalArgumentException iae) {
+ return new IncorrectCommand(iae.getMessage());
+ }
+
+ try {
+ tagSet = getTagsFromArgs(noKeywordMatcher.group("tagArguments"));
+ } catch(IllegalValueException ive) {
+ return new IncorrectCommand(ive.getMessage());
+ }
+ } else if(tagMatcherMatches) {
+ final Collection tagStrings = Arrays.asList(tagMatcher.group("tagArguments").replaceFirst("t/", "").split(" t/"));
+ tagSet = new HashSet<>(tagStrings);
+ } else if(noDateMatcherMatches) {
+ String[] keywords = noDateMatcher.group("keywords").split("\\s+");
+ keywordSet = new HashSet<>(Arrays.asList(keywords));
+ try {
+ tagSet = getTagsFromArgs(noDateMatcher.group("tagArguments"));
+ } catch(IllegalValueException ive) {
+ return new IncorrectCommand(ive.getMessage());
+ }
+ } else {
+ return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ FindCommand.MESSAGE_USAGE));
+ }
+ return new FindCommand(keywordSet, startTime, endTime, deadline, tagSet);
+ }
+
+ private Command prepareEdit(String args) {
+ if(args == null || args.length() == 0)
+ return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ EditCommand.MESSAGE_USAGE));
+
+ final Matcher noDateMatcher = EDIT_ARGS_WITHOUT_DATE_FORMAT.matcher(args.trim());
+ final Matcher dateMatcher = EDIT_ARGS_WITH_DATE_FORMAT.matcher(args.trim());
+ final Matcher tagMatcher = EDIT_ARGS_WITH_TAG_FORMAT.matcher(args.trim());
+ final Matcher noNameMatcher = EDIT_ARGS_WITHOUT_NAME_FORMAT.matcher(args.trim());
+
+ final int targetIndex;
+ String taskName = "";
+ Date startTime = null;
+ Date endTime = null;
+ Set tagSet = new HashSet();
+ RecurringType recurringType = RecurringType.NONE;
+
+ boolean dateMatcherMatches = dateMatcher.matches();
+ boolean noDateMatcherMatches = noDateMatcher.matches();
+ boolean tagMatcherMatches = tagMatcher.matches();
+ boolean noNameMatcherMathces = noNameMatcher.matches();
+
+ if(dateMatcherMatches) {
+ targetIndex = Integer.parseInt(dateMatcher.group("targetIndex"));
+ taskName = dateMatcher.group("name").replaceFirst("\\s", "");
+
+ try {
+ ArrayList dateSet = extractDateInfo(dateMatcher);
+ if(dateSet.size() == ONLY_DEADLINE) {
+ endTime = dateSet.get(DEADLINE_INDEX);
+ recurringType = checkForRecurringTask(dateMatcher.group("deadline"));
+ } else if(dateSet.size() == TIME_PERIOD) {
+ startTime = dateSet.get(START_TIME_INDEX);
+ endTime = dateSet.get(END_TIME_INDEX);
+ recurringType = checkForRecurringTask(dateMatcher.group("startTime"));
+ }
+ } catch(IllegalArgumentException iae) {
+ return new IncorrectCommand(iae.getMessage());
+ }
+
+ try {
+ tagSet = getTagsFromArgs(noDateMatcher.group("tagArguments"));
+ } catch(IllegalValueException ive) {
+ return new IncorrectCommand(ive.getMessage());
+ }
+ } else if(noNameMatcherMathces) {
+ targetIndex = Integer.parseInt(noNameMatcher.group("targetIndex"));
+
+ try {
+ ArrayList dateSet = extractDateInfo(noNameMatcher);
+ if(dateSet.size() == ONLY_DEADLINE) {
+ endTime = dateSet.get(DEADLINE_INDEX);
+ recurringType = checkForRecurringTask(noNameMatcher.group("deadline"));
+ } else if(dateSet.size() == TIME_PERIOD) {
+ startTime = dateSet.get(START_TIME_INDEX);
+ endTime = dateSet.get(END_TIME_INDEX);
+ recurringType = checkForRecurringTask(noNameMatcher.group("startTime"));
+ }
+ } catch(IllegalArgumentException iae) {
+ return new IncorrectCommand(iae.getMessage());
+ }
+
+ try {
+ tagSet = getTagsFromArgs(noNameMatcher.group("tagArguments"));
+ } catch(IllegalValueException ive) {
+ return new IncorrectCommand(ive.getMessage());
+ }
+ } else if(tagMatcherMatches) {
+ targetIndex = Integer.parseInt(tagMatcher.group("targetIndex"));
+
+ try {
+ tagSet = getTagsFromArgs(tagMatcher.group("tagArguments"));
+ } catch(IllegalValueException ive) {
+ return new IncorrectCommand(ive.getMessage());
+ }
+ } else if(noDateMatcherMatches) {
+ targetIndex = Integer.parseInt(noDateMatcher.group("targetIndex"));
+ taskName = noDateMatcher.group("name").replaceFirst("\\s", "");
+ //-------Parts for detecting recurring information-----------------
+ String[] words = taskName.split(" ");
+ String lastWord = words[words.length - 1];
+ recurringType = checkForRecurringTask(lastWord);
+ if(recurringType != RecurringType.IGNORED){
+ taskName = (words.length == 1) ? "" :
+ taskName.substring(0, taskName.length() - lastWord.length());
+ }
+ //-----------------------------------------------------------------
+ try {
+ tagSet = getTagsFromArgs(noDateMatcher.group("tagArguments"));
+ } catch(IllegalValueException ive) {
+ return new IncorrectCommand(ive.getMessage());
+ }
+ } else {
+ return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ EditCommand.MESSAGE_USAGE));
+ }
+
+ try {
+ return new EditCommand(targetIndex, taskName, tagSet, startTime, endTime, recurringType);
+ } catch (IllegalValueException ive) {
+ return new IncorrectCommand(ive.getMessage());
+ }
+ }
+```
+###### \java\seedu\address\logic\parser\Parser.java
+``` java
+ public static ArrayList extractDateInfo(Matcher m) throws IllegalArgumentException {
+ ArrayList resultSet = new ArrayList();
+ try {
+ String[] time = m.group("startTime").replace(" from ", "").split(" to ");
+ resultSet.clear();
+ try {
+ resultSet.add(getDateFromString(time[START_TIME_INDEX]));
+ resultSet.add(getDateFromString(time[END_TIME_INDEX]));
+ } catch(Exception cnp) {
+ throw new IllegalArgumentException(MESSAGE_ILLEGAL_DATE_INPUT);
+ }
+
+ } catch(Exception ise) {
+ resultSet.clear();
+ try {
+ resultSet.add(getDateFromString(m.group("deadline").replace(" by ", "")));
+ } catch(Exception cnp) {
+ throw new IllegalArgumentException(MESSAGE_ILLEGAL_DATE_INPUT);
+ }
+ }
+ return resultSet;
+ }
+```
+###### \java\seedu\address\model\ModelManager.java
+``` java
+ @Override
+ public synchronized void editTask(Task target, Name name, UniqueTagList tags,
+ TaskDate startDate, TaskDate endDate, RecurringType recurringType) throws TaskNotFoundException, TimeslotOverlapException {
+ taskMaster.updateTask(target, name, tags, startDate, endDate, recurringType);
+ indicateTaskListChanged();
+ updateFilteredListToShowAll();
+ }
+```
+###### \java\seedu\address\model\ModelManager.java
+``` java
+ private class NameQualifier implements Qualifier {
+ private Set nameKeyWords;
+
+ NameQualifier(Set nameKeyWords) {
+ this.nameKeyWords = nameKeyWords;
+ }
+
+ @Override
+ public boolean run(TaskComponent task) {
+ if(nameKeyWords.isEmpty())
+ return true;
+
+ return nameKeyWords.stream()
+ .filter(keyword -> StringUtil.containsIgnoreCase(task.getTaskReference().getName().fullName, keyword))
+ .findAny()
+ .isPresent();
+ }
+
+ @Override
+ public String toString() {
+ return "name=" + String.join(", ", nameKeyWords);
+ }
+ }
+
+ private class TagQualifier implements Qualifier {
+ private Set tagSet;
+
+ TagQualifier(Set tagSet) {
+ this.tagSet = tagSet;
+ }
+
+ private String tagToString(TaskComponent task) {
+ Set tagSet = task.getTaskReference().getTags().toSet();
+ Set tagStringSet = new HashSet();
+ for(Tag t : tagSet) {
+ tagStringSet.add(t.tagName);
+ }
+ return String.join(" ", tagStringSet);
+ }
+
+ @Override
+ public boolean run(TaskComponent task) {
+ if(tagSet.isEmpty()) {
+ return true;
+ }
+ return tagSet.stream()
+ .filter(tag -> StringUtil.containsIgnoreCase(tagToString(task), tag))
+ .findAny()
+ .isPresent();
+ }
+
+ @Override
+ public String toString() {
+ return "tag=" + String.join(", ", tagSet);
+ }
+ }
+
+ private class PeriodQualifier implements Qualifier {
+ private final int START_DATE_INDEX = 0;
+ private final int END_DATE_INDEX = 1;
+
+ private Date startTime;
+ private Date endTime;
+
+ PeriodQualifier(Date startTime, Date endTime) {
+ this.startTime = startTime;
+ this.endTime = endTime;
+ }
+
+ private Date[] extractTaskPeriod(TaskComponent task) {
+ TaskType type = task.getTaskReference().getTaskType();
+ if(type.equals(TaskType.FLOATING)) {
+ return null;
+ }
+
+ if(task.getStartDate().getDateInLong() == TaskDate.DATE_NOT_PRESENT) {
+ return null;
+ }
+
+ Date startDate = new Date(task.getStartDate().getDateInLong());
+ Date endDate = new Date(task.getEndDate().getDateInLong());
+ return new Date[]{ startDate, endDate };
+ }
+
+ @Override
+ public boolean run(TaskComponent task) {
+
+ if(this.endTime == null)
+ return true;
+
+ Date[] timeArray = extractTaskPeriod(task);
+ if(timeArray == null)
+ return false;
+
+ Date startDate = timeArray[START_DATE_INDEX];
+ Date endDate = timeArray[END_DATE_INDEX];
+
+ if((startDate.after(this.startTime)||startDate.equals(this.startTime))
+ && (endDate.before(this.endTime)||endDate.equals(this.endTime)))
+ return true;
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ if(this.startTime == null || this.endTime == null)
+ return "";
+ return "start time=" + this.startTime.toString()
+ + " end time=" + this.endTime.toString();
+ }
+ }
+
+ private class DeadlineQualifier implements Qualifier {
+ private Date deadline;
+
+ DeadlineQualifier(Date deadline) {
+ this.deadline = deadline;
+ }
+
+ @Override
+ public boolean run(TaskComponent task) {
+
+ if(this.deadline == null)
+ return true;
+
+ if(task.getTaskReference().getTaskType().equals(TaskType.FLOATING))
+ return false;
+
+ if(task.getEndDate().getDateInLong() == TaskDate.DATE_NOT_PRESENT)
+ return false;
+
+ Date deadline = new Date(task.getEndDate().getDateInLong());
+
+ if((deadline.before(this.deadline) || this.deadline.equals(deadline))
+ && task.getStartDate().getDateInLong() == TaskDate.DATE_NOT_PRESENT)
+ return true;
+
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ if(this.deadline == null)
+ return "";
+
+ return "deadline=" + this.deadline.toString();
+ }
+ }
+
+ private class FindQualifier implements Qualifier {
+ private NameQualifier nameQualifier;
+ private TagQualifier tagQualifier;
+ private PeriodQualifier periodQualifier;
+ private DeadlineQualifier deadlineQualifier;
+ private TypeQualifier typeQualifier = null;
+ private ArchiveQualifier archiveQualifier;
+
+ FindQualifier(Set keywordSet, Set tagSet, Date startTime, Date endTime, Date deadline) {
+ if(keywordSet.contains("-C")) {
+ this.archiveQualifier = new ArchiveQualifier(true);
+ }
+ if(keywordSet.contains("-F"))
+ this.typeQualifier = new TypeQualifier(TaskType.FLOATING);
+ this.nameQualifier = new NameQualifier(keywordSet);
+ this.tagQualifier = new TagQualifier(tagSet);
+ this.periodQualifier = new PeriodQualifier(startTime, endTime);
+ this.deadlineQualifier = new DeadlineQualifier(deadline);
+ }
+
+ @Override
+ public boolean run(TaskComponent task) {
+ if(this.typeQualifier!=null)
+ return typeQualifier.run(task);
+ if(this.archiveQualifier != null) {
+ return archiveQualifier.run(task);
+ }
+ return nameQualifier.run(task)
+ && tagQualifier.run(task)
+ && periodQualifier.run(task)
+ && deadlineQualifier.run(task);
+ }
+
+ @Override
+ public String toString() {
+ return nameQualifier.toString() + " "
+ + tagQualifier.toString() + " "
+ + periodQualifier.toString() + " "
+ + deadlineQualifier.toString() + " "
+ + archiveQualifier.toString() + " ";
+ }
+ }
+```
+###### \java\seedu\address\model\task\Task.java
+``` java
+ @Override
+ public void updateTask(Name name, UniqueTagList tags, TaskDate startDate, TaskDate endDate, RecurringType recurringType) {
+ if(name != null)
+ this.name = name;
+
+ if(tags != null)
+ this.tags = tags;
+
+ if(this.getComponentForNonRecurringType().getStartDate().equals(new TaskDate(TaskDate.DATE_NOT_PRESENT))
+ && this.getComponentForNonRecurringType().getStartDate().equals(new TaskDate(TaskDate.DATE_NOT_PRESENT))
+ && endDate != null) {
+ this.taskType = TaskType.NON_FLOATING;
+ }
+
+ if(endDate != null)
+ this.getLastAppendedComponent().update(startDate, endDate);
+
+ if(recurringType != RecurringType.IGNORED)
+ this.recurringType = recurringType;
+
+ getLastAppendedComponent().setTaskReferrence(this);
+ }
+```
+###### \java\seedu\address\model\task\TaskComponent.java
+``` java
+ public void update(TaskDate startDate, TaskDate endDate) {
+ TaskDate realStartDate = startDate == null ? new TaskDate(TaskDate.DATE_NOT_PRESENT) : startDate;
+ TaskDate realEndDate = endDate == null ? new TaskDate(TaskDate.DATE_NOT_PRESENT) : endDate;
+ setStartDate(realStartDate);
+ setEndDate(realEndDate);
+ }
+```
+###### \java\seedu\address\model\TaskMaster.java
+``` java
+ public boolean updateTask(Task target, Name name, UniqueTagList tags,
+ TaskDate startDate, TaskDate endDate, RecurringType recurringType) throws TaskNotFoundException, TimeslotOverlapException {
+ if (tasks.updateTask(target, name, tags, startDate, endDate, recurringType)) {
+ if(tags != null) {
+ this.tags.mergeFrom(tags);
+
+ // Create map with values = tag object references in the master list
+ final Map masterTagObjects = new HashMap<>();
+ for (Tag tag : this.tags) {
+ masterTagObjects.put(tag, tag);
+ }
+
+ // Rebuild the list of task tags using references from the master list
+ final Set commonTagReferences = new HashSet<>();
+ for (Tag tag : tags) {
+ commonTagReferences.add(masterTagObjects.get(tag));
+ }
+ target.setTags(new UniqueTagList(commonTagReferences));
+ }
+ return true;
+ } else {
+ throw new UniqueTaskList.TaskNotFoundException();
+ }
+ }
+```
diff --git a/collated/test/A0135782Y.md b/collated/test/A0135782Y.md
new file mode 100644
index 000000000000..503320b3d35e
--- /dev/null
+++ b/collated/test/A0135782Y.md
@@ -0,0 +1,533 @@
+# A0135782Y
+###### \java\seedu\address\logic\LogicManagerTest.java
+``` java
+ @Test
+ public void execute_add_successful_non_floating_from_date_to_date() throws Exception {
+ // setup expectations
+ TestDataHelper helper = new TestDataHelper();
+ Task toBeAdded = helper.nonFloatingFromDateToDate();
+ TaskMaster expectedAB = new TaskMaster();
+ expectedAB.addTask(toBeAdded);
+
+ // execute command and verify result
+ assertCommandBehavior(helper.generateAddCommand(toBeAdded),
+ String.format(AddNonFloatingCommand.MESSAGE_SUCCESS, toBeAdded),
+ expectedAB,
+ expectedAB.getTaskComponentList());
+ assertUndoRedoAble(String.format(AddNonFloatingCommand.MESSAGE_SUCCESS, toBeAdded),
+ expectedAB,
+ expectedAB.getTaskComponentList());
+ }
+
+ @Test
+ public void execute_add_successful_non_floating_by_date() throws Exception {
+ // setup expectations
+ TestDataHelper helper = new TestDataHelper();
+ Task toBeAdded = helper.nonFloatingByDate();
+ TaskMaster expectedAB = new TaskMaster();
+ expectedAB.addTask(toBeAdded);
+
+ // execute command and verify result
+ assertCommandBehavior(helper.generateAddCommand(toBeAdded),
+ String.format(AddNonFloatingCommand.MESSAGE_SUCCESS, toBeAdded),
+ expectedAB,
+ expectedAB.getTaskComponentList());
+ assertUndoRedoAble(String.format(AddNonFloatingCommand.MESSAGE_SUCCESS, toBeAdded),
+ expectedAB,
+ expectedAB.getTaskComponentList());
+ }
+
+```
+###### \java\seedu\address\logic\LogicManagerTest.java
+``` java
+ @Test
+ public void execute_add_recurringTask_byDate_unsuccessful_add_as_nonfloating_instead() throws Exception {
+ TestDataHelper helper = new TestDataHelper();
+ TaskMaster expectedTM = new TaskMaster();
+ Task toAdd = helper.nonFloatingByDate();
+ expectedTM.addTask(toAdd);
+ List expectedComponentList = helper.buildTaskComponentsFromTaskList(expectedTM.getTasks());
+ assertCommandBehavior("add non floating task by XXXX by 20 oct 11am dai t/tag1 t/tag2",
+ String.format(AddNonFloatingCommand.MESSAGE_SUCCESS, toAdd),
+ expectedTM,
+ expectedComponentList);
+ }
+
+ @Test
+ public void execute_add_recurringTask_byDate_successful() throws Exception {
+ TestDataHelper helper = new TestDataHelper();
+ TaskMaster expectedTM = new TaskMaster();
+ Task toAdd = helper.nonFloatingRecurringByDate(RecurringType.DAILY);
+ expectedTM.addTask(toAdd);
+ RecurringTaskManager.getInstance().correctAddingOverdueTasks(toAdd);
+ List expectedComponentList = helper.buildTaskComponentsFromTaskList(expectedTM.getTasks());
+ assertCommandBehavior("add non floating task by XXXX by 20 oct 11am daily t/tag1 t/tag2",
+ String.format(AddNonFloatingCommand.MESSAGE_SUCCESS, toAdd),
+ expectedTM,
+ expectedComponentList);
+ }
+
+ @Test
+ public void execute_add_recurringTask_daily_ByDate_daily_caseInsensitive() throws Exception {
+ TestDataHelper helper = new TestDataHelper();
+ TaskMaster expectedTM = new TaskMaster();
+ Task toAdd = helper.nonFloatingRecurringByDate(RecurringType.DAILY);
+ expectedTM.addTask(toAdd);
+ RecurringTaskManager.getInstance().correctAddingOverdueTasks(toAdd);
+ List expectedComponentList = helper.buildTaskComponentsFromTaskList(expectedTM.getTasks());
+ assertCommandBehavior("add non floating task by XXXX by 20 oct 11am dAIly t/tag1 t/tag2",
+ String.format(AddNonFloatingCommand.MESSAGE_SUCCESS, toAdd),
+ expectedTM,
+ expectedComponentList);
+ }
+
+ @Test
+ public void execute_add_recurringTask_FromDateToDate_unsuccessful_add_as_nonfloating_instead() throws Exception {
+ TestDataHelper helper = new TestDataHelper();
+ TaskMaster expectedTM = new TaskMaster();
+ Task toAdd = helper.nonFloatingFromDateToDate();
+ expectedTM.addTask(toAdd);
+ RecurringTaskManager.getInstance().correctAddingOverdueTasks(toAdd);
+ List expectedComponentList = helper.buildTaskComponentsFromTaskList(expectedTM.getTasks());
+ assertCommandBehavior("add non floating task from XXXX to XXXX from 19 oct 10pm to 20 oct 11am dai t/tag1 t/tag2",
+ String.format(AddNonFloatingCommand.MESSAGE_SUCCESS, toAdd),
+ expectedTM,
+ expectedComponentList);
+ }
+
+ @Test
+ public void execute_add_recurringTask_FromDateToDate_successful() throws Exception {
+ TestDataHelper helper = new TestDataHelper();
+ TaskMaster expectedTM = new TaskMaster();
+ Task toAdd = helper.nonFloatingRecurringFromDateToDate(RecurringType.DAILY);
+ expectedTM.addTask(toAdd);
+ RecurringTaskManager.getInstance().correctAddingOverdueTasks(toAdd);
+ List expectedComponentList = helper.buildTaskComponentsFromTaskList(expectedTM.getTasks());
+ assertCommandBehavior("add non floating task from XXXX to XXXX from 19 oct 10pm to 20 oct 11am daily t/tag1 t/tag2",
+ String.format(AddNonFloatingCommand.MESSAGE_SUCCESS, toAdd),
+ expectedTM,
+ expectedComponentList);
+ }
+
+ @Test
+ public void execute_add_recurringTask_daily_FromDateToDate_daily_caseInsensitive() throws Exception {
+ TestDataHelper helper = new TestDataHelper();
+ TaskMaster expectedTM = new TaskMaster();
+ Task toAdd = helper.nonFloatingRecurringFromDateToDate(RecurringType.DAILY);
+ expectedTM.addTask(toAdd);
+ RecurringTaskManager.getInstance().correctAddingOverdueTasks(toAdd);
+ List expectedComponentList = helper.buildTaskComponentsFromTaskList(expectedTM.getTasks());
+ assertCommandBehavior("add non floating task from XXXX to XXXX from 19 oct 10pm to 20 oct 11am dAIly t/tag1 t/tag2",
+ String.format(AddNonFloatingCommand.MESSAGE_SUCCESS, toAdd),
+ expectedTM,
+ expectedComponentList);
+ }
+```
+###### \java\seedu\address\model\RecurringTaskManagerTest.java
+``` java
+public class RecurringTaskManagerTest {
+ private RecurringTaskManager recurringManager;
+ private TaskMaster taskMaster;
+
+ @Before
+ public void setup() {
+ recurringManager = RecurringTaskManager.getInstance();
+ taskMaster = new TaskMaster();
+ recurringManager.setTaskList(taskMaster.getUniqueTaskList());
+ }
+
+ @Test
+ public void set_null_tasklist_throwAssert() {
+ try{
+ recurringManager.setTaskList(null);
+ fail();
+ } catch (AssertionError ae) {
+ assertTrue(true);
+ }
+ }
+
+ @Test
+ public void set_null_taskList_updateRecurringTask_throwAssert() {
+ try {
+ recurringManager.setTaskList(null);
+ recurringManager.updateAnyRecurringTasks();
+ fail();
+ } catch (AssertionError ae) {
+ assertTrue(true);
+ }
+ }
+
+ @Test
+ public void correctAddingOverdueTasks_usingNullTask_throwAssert() {
+ try {
+ recurringManager.correctAddingOverdueTasks(null);
+ fail();
+ } catch (AssertionError ae) {
+ assertTrue(true);
+ }
+ }
+
+ @Test
+ public void correctAssingOverdueTasks_nonRecurringTask_notCorrected() throws Exception {
+ TaskBuilder builder = new TaskBuilder();
+ TestTask tryCorrect = builder.withName("non recurring").withStartDate("11 oct 11pm")
+ .withEndDate("12 oct 11pm").build();
+ TestTask expectedTask = builder.withName("non recurring").withStartDate("11 oct 11pm")
+ .withEndDate("12 oct 11pm").build();
+ recurringManager.correctAddingOverdueTasks(tryCorrect);
+ assertEquals("Non recurring tasks should not be corrected", tryCorrect, expectedTask);
+ }
+
+ // Boundary test
+ // Test lower bounds
+ @Test
+ public void correctAssignOverdueTasks_dailyRecurring_corrected_lowerBound() throws Exception {
+ TaskBuilder builder = new TaskBuilder();
+ RecurringTaskHelper helper = new RecurringTaskHelper();
+ TestTask tryCorrect = builder.withName("recurring").withStartDate("11 oct 2016 11pm")
+ .withEndDate("12 oct 2016 11pm").withRecurringType(RecurringType.DAILY).build();
+ builder = new TaskBuilder();
+ TestTask expectedTask = builder.withName("recurring").withStartDate("12 oct 2016 11pm")
+ .withEndDate("13 oct 2016 11pm").withRecurringType(RecurringType.DAILY).build();
+ recurringManager.correctAddingOverdueTasks(tryCorrect, helper.getLocalDateByString("2016-10-12"));
+ assertEquals("Recurring tasks should be corrected", tryCorrect, expectedTask);
+ }
+
+ @Test
+ public void correctAssignOverdueTasks_weeklyRecurring_corrected_lowerBound() throws Exception {
+ TaskBuilder builder = new TaskBuilder();
+ RecurringTaskHelper helper = new RecurringTaskHelper();
+ TestTask tryCorrect = builder.withName("recurring").withStartDate("11 oct 2016 11pm")
+ .withEndDate("12 oct 2016 11pm").withRecurringType(RecurringType.WEEKLY).build();
+ builder = new TaskBuilder();
+ TestTask expectedTask = builder.withName("recurring").withStartDate("18 oct 2016 11pm")
+ .withEndDate("19 oct 2016 11pm").withRecurringType(RecurringType.WEEKLY).build();
+ recurringManager.correctAddingOverdueTasks(tryCorrect, helper.getLocalDateByString("2016-10-12"));
+ assertEquals("Recurring tasks should be corrected", tryCorrect, expectedTask);
+ }
+
+ @Test
+ public void correctAssignOverdueTasks_monthlyRecurring_corrected_lowerBound() throws Exception {
+ TaskBuilder builder = new TaskBuilder();
+ RecurringTaskHelper helper = new RecurringTaskHelper();
+ TestTask tryCorrect = builder.withName("recurring").withStartDate("11 oct 2016 11pm")
+ .withEndDate("12 oct 2016 11pm").withRecurringType(RecurringType.MONTHLY).build();
+ builder = new TaskBuilder();
+ TestTask expectedTask = builder.withName("recurring").withStartDate("11 nov 2016 11pm")
+ .withEndDate("12 nov 2016 11pm").withRecurringType(RecurringType.MONTHLY).build();
+ recurringManager.correctAddingOverdueTasks(tryCorrect, helper.getLocalDateByString("2016-11-12"));
+ assertEquals("Recurring tasks should be corrected", tryCorrect, expectedTask);
+ }
+
+ @Test
+ public void correctAssignOverdueTasks_yearlyRecurring_corrected_lowerBound() throws Exception {
+ TaskBuilder builder = new TaskBuilder();
+ RecurringTaskHelper helper = new RecurringTaskHelper();
+ TestTask tryCorrect = builder.withName("recurring").withStartDate("11 oct 2016 11pm")
+ .withEndDate("12 oct 2016 11pm").withRecurringType(RecurringType.YEARLY).build();
+ builder = new TaskBuilder();
+ TestTask expectedTask = builder.withName("recurring").withStartDate("11 oct 2017 11pm")
+ .withEndDate("12 oct 2017 11pm").withRecurringType(RecurringType.YEARLY).build();
+ recurringManager.correctAddingOverdueTasks(tryCorrect, helper.getLocalDateByString("2016-10-12"));
+ assertEquals("Recurring tasks should be corrected", tryCorrect, expectedTask);
+ }
+
+ // Boundary test
+ // Test upper bounds
+ @Test
+ public void correctAssignOverdueTasks_dailyRecurring_corrected_upperBound() throws Exception {
+ TaskBuilder builder = new TaskBuilder();
+ RecurringTaskHelper helper = new RecurringTaskHelper();
+ TestTask tryCorrect = builder.withName("recurring").withStartDate("11 oct 2016 11pm")
+ .withEndDate("12 oct 2016 11pm").withRecurringType(RecurringType.DAILY).build();
+ builder = new TaskBuilder();
+ TestTask expectedTask = builder.withName("recurring").withStartDate("11 oct 2016 11pm")
+ .withEndDate("12 oct 2016 11pm").withRecurringType(RecurringType.DAILY).build();
+ recurringManager.correctAddingOverdueTasks(tryCorrect, helper.getLocalDateByString("2016-10-09"));
+ assertEquals("Recurring tasks should be corrected", tryCorrect, expectedTask);
+ }
+
+ @Test
+ public void correctAssignOverdueTasks_weeklyRecurring_corrected_upperBound() throws Exception {
+ TaskBuilder builder = new TaskBuilder();
+ RecurringTaskHelper helper = new RecurringTaskHelper();
+ TestTask tryCorrect = builder.withName("recurring").withStartDate("11 oct 2016 11pm")
+ .withEndDate("12 oct 2016 11pm").withRecurringType(RecurringType.WEEKLY).build();
+ builder = new TaskBuilder();
+ TestTask expectedTask = builder.withName("recurring").withStartDate("11 oct 2016 11pm")
+ .withEndDate("12 oct 2016 11pm").withRecurringType(RecurringType.WEEKLY).build();
+ recurringManager.correctAddingOverdueTasks(tryCorrect, helper.getLocalDateByString("2016-10-09"));
+ assertEquals("Recurring tasks should be corrected", tryCorrect, expectedTask);
+ }
+
+ @Test
+ public void correctAssignOverdueTasks_monthlyRecurring_corrected_upperBound() throws Exception {
+ TaskBuilder builder = new TaskBuilder();
+ RecurringTaskHelper helper = new RecurringTaskHelper();
+ TestTask tryCorrect = builder.withName("recurring").withStartDate("11 oct 2016 11pm")
+ .withEndDate("12 oct 2016 11pm").withRecurringType(RecurringType.MONTHLY).build();
+ builder = new TaskBuilder();
+ TestTask expectedTask = builder.withName("recurring").withStartDate("11 oct 2016 11pm")
+ .withEndDate("12 oct 2016 11pm").withRecurringType(RecurringType.MONTHLY).build();
+ recurringManager.correctAddingOverdueTasks(tryCorrect, helper.getLocalDateByString("2016-10-09"));
+ assertEquals("Recurring tasks should be corrected", tryCorrect, expectedTask);
+ }
+
+ @Test
+ public void correctAssignOverdueTasks_yearlyRecurring_corrected_upperBound() throws Exception {
+ TaskBuilder builder = new TaskBuilder();
+ RecurringTaskHelper helper = new RecurringTaskHelper();
+ TestTask tryCorrect = builder.withName("recurring").withStartDate("11 oct 2016 11pm")
+ .withEndDate("12 oct 2016 11pm").withRecurringType(RecurringType.MONTHLY).build();
+ builder = new TaskBuilder();
+ TestTask expectedTask = builder.withName("recurring").withStartDate("11 oct 2016 11pm")
+ .withEndDate("12 oct 2016 11pm").withRecurringType(RecurringType.MONTHLY).build();
+ recurringManager.correctAddingOverdueTasks(tryCorrect, helper.getLocalDateByString("2016-10-09"));
+ assertEquals("Recurring tasks should be corrected", tryCorrect, expectedTask);
+ }
+
+ @Test
+ public void updateRecurringTask_daily_noTaskAppended() throws Exception {
+ TaskBuilder builder = new TaskBuilder();
+ RecurringTaskHelper helper = new RecurringTaskHelper();
+ TestTask tryAppend = builder.withName("recurring").withStartDate("11 oct 2016 11pm")
+ .withEndDate("12 oct 2016 11pm").withRecurringType(RecurringType.DAILY).build();
+ recurringManager.appendRecurringTasks(tryAppend, helper.getLastAppendedStartDate(tryAppend),
+ helper.getLastAppendedEndDate(tryAppend), helper.getLocalDateByString("2016-10-11"));
+ assertEquals("Recurring tasks should not append until their date has been elapsed", tryAppend.getTaskDateComponent().size(), 1);
+ }
+
+ @Test
+ public void updateRecurringTask_weekly_noTaskAppended() throws Exception {
+ TaskBuilder builder = new TaskBuilder();
+ RecurringTaskHelper helper = new RecurringTaskHelper();
+ TestTask tryAppend = builder.withName("recurring").withStartDate("11 oct 2016 11pm")
+ .withEndDate("12 oct 2016 11pm").withRecurringType(RecurringType.WEEKLY).build();
+ recurringManager.appendRecurringTasks(tryAppend, helper.getLastAppendedStartDate(tryAppend),
+ helper.getLastAppendedEndDate(tryAppend), helper.getLocalDateByString("2016-10-11"));
+ assertEquals("Recurring tasks should not append until their date has been elapsed", tryAppend.getTaskDateComponent().size(), 1);
+ }
+
+ @Test
+ public void updateRecurringTask_monthly_noTaskAppended() throws Exception {
+ TaskBuilder builder = new TaskBuilder();
+ RecurringTaskHelper helper = new RecurringTaskHelper();
+ TestTask tryAppend = builder.withName("recurring").withStartDate("11 oct 2016 11pm")
+ .withEndDate("12 oct 2016 11pm").withRecurringType(RecurringType.MONTHLY).build();
+ recurringManager.appendRecurringTasks(tryAppend, helper.getLastAppendedStartDate(tryAppend),
+ helper.getLastAppendedEndDate(tryAppend), helper.getLocalDateByString("2016-10-11"));
+ assertEquals("Recurring tasks should not append until their date has been elapsed", tryAppend.getTaskDateComponent().size(), 1);
+ }
+
+ @Test
+ public void updateRecurringTask_yearly_noTaskAppended() throws Exception {
+ TaskBuilder builder = new TaskBuilder();
+ RecurringTaskHelper helper = new RecurringTaskHelper();
+ TestTask tryAppend = builder.withName("recurring").withStartDate("11 oct 2016 11pm")
+ .withEndDate("12 oct 2016 11pm").withRecurringType(RecurringType.YEARLY).build();
+ recurringManager.appendRecurringTasks(tryAppend, helper.getLastAppendedStartDate(tryAppend),
+ helper.getLastAppendedEndDate(tryAppend), helper.getLocalDateByString("2016-10-11"));
+ assertEquals("Recurring tasks should not append until their date has been elapsed", tryAppend.getTaskDateComponent().size(), 1);
+ }
+
+ @Test
+ public void updateRecurringTask_daily_taskAppended() throws Exception {
+ TaskBuilder builder = new TaskBuilder();
+ RecurringTaskHelper helper = new RecurringTaskHelper();
+ TestTask tryAppend = builder.withName("recurring").withStartDate("11 oct 2016 11pm")
+ .withEndDate("12 oct 2016 11pm").withRecurringType(RecurringType.DAILY).build();
+ recurringManager.appendRecurringTasks(tryAppend, helper.getLastAppendedStartDate(tryAppend),
+ helper.getLastAppendedEndDate(tryAppend), helper.getLocalDateByString("2016-10-12"));
+ assertEquals("Recurring tasks should be appended when it is time", tryAppend.getTaskDateComponent().size(), 2);
+ }
+
+ @Test
+ public void updateRecurringTask_weekly_taskAppended() throws Exception {
+ TaskBuilder builder = new TaskBuilder();
+ RecurringTaskHelper helper = new RecurringTaskHelper();
+ TestTask tryAppend = builder.withName("recurring").withStartDate("11 oct 2016 11pm")
+ .withEndDate("12 oct 2016 11pm").withRecurringType(RecurringType.WEEKLY).build();
+ recurringManager.appendRecurringTasks(tryAppend, helper.getLastAppendedStartDate(tryAppend),
+ helper.getLastAppendedEndDate(tryAppend), helper.getLocalDateByString("2016-10-17"));
+ assertEquals("Recurring tasks should be appended when it is time", tryAppend.getTaskDateComponent().size(), 2);
+ }
+
+ @Test
+ public void updateRecurringTask_monthly_taskAppended() throws Exception {
+ TaskBuilder builder = new TaskBuilder();
+ RecurringTaskHelper helper = new RecurringTaskHelper();
+ TestTask tryAppend = builder.withName("recurring").withStartDate("11 oct 2016 11pm")
+ .withEndDate("12 oct 2016 11pm").withRecurringType(RecurringType.MONTHLY).build();
+ recurringManager.appendRecurringTasks(tryAppend, helper.getLastAppendedStartDate(tryAppend),
+ helper.getLastAppendedEndDate(tryAppend), helper.getLocalDateByString("2016-11-12"));
+ assertEquals("Recurring tasks should be appended when it is time", tryAppend.getTaskDateComponent().size(), 2);
+ }
+
+ @Test
+ public void updateRecurringTask_yearly_taskAppended() throws Exception {
+ TaskBuilder builder = new TaskBuilder();
+ RecurringTaskHelper helper = new RecurringTaskHelper();
+ TestTask tryAppend = builder.withName("recurring").withStartDate("11 oct 2016 11pm")
+ .withEndDate("12 oct 2016 11pm").withRecurringType(RecurringType.YEARLY).build();
+ recurringManager.appendRecurringTasks(tryAppend, helper.getLastAppendedStartDate(tryAppend),
+ helper.getLastAppendedEndDate(tryAppend), helper.getLocalDateByString("2017-10-11"));
+ assertEquals("Recurring tasks should be appended when it is time", tryAppend.getTaskDateComponent().size(), 2);
+ }
+
+ class RecurringTaskHelper {
+ public RecurringTaskHelper() {}
+
+ public LocalDate getLocalDateByString(String dateToConsider) {
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+ formatter = formatter.withLocale(Locale.getDefault()); // Locale specifies human language for translating, and cultural norms for lowercase/uppercase and abbreviations and such. Example: Locale.US or Locale.CANADA_FRENCH
+ LocalDate date = LocalDate.parse(dateToConsider, formatter);
+ return date;
+ }
+
+ public Calendar getLastAppendedStartDate(ReadOnlyTask task) {
+ Calendar cal = new GregorianCalendar();
+ cal.setTime(task.getLastAppendedComponent().getStartDate().getDate());
+ return cal;
+ }
+
+ public Calendar getLastAppendedEndDate(ReadOnlyTask task) {
+ Calendar cal = new GregorianCalendar();
+ cal.setTime(task.getLastAppendedComponent().getEndDate().getDate());
+ return cal;
+ }
+ }
+}
+```
+###### \java\seedu\address\model\task\TaskTester.java
+``` java
+/**
+ * Unit Tester that tests the methods of the Task class
+ *
+ */
+public class TaskTester {
+ private Task task;
+
+ @Test
+ public void create_floatingTask_success() throws Exception {
+ task = new Task(new Name("Name"), new UniqueTagList());
+ assertEquals(task.getTaskType(),TaskType.FLOATING);
+ }
+
+ @Test
+ public void create_floatingTask_failure() throws Exception {
+ task = new Task(new Name("Name"), new UniqueTagList(), new TaskDate(10), new TaskDate(20), RecurringType.NONE);
+ assertNotEquals(task.getTaskType(),TaskType.FLOATING);
+ }
+
+ @Test
+ public void create_nonFloatingTask_success() throws Exception {
+ task = new Task(new Name("Name"), new UniqueTagList(), new TaskDate(10), new TaskDate(20), RecurringType.NONE);
+ assertEquals(task.getTaskType(),TaskType.NON_FLOATING);
+ }
+
+ @Test
+ public void create_nonFloatingTask_failire() throws Exception {
+ task = new Task(new Name("Name"), new UniqueTagList());
+ assertNotEquals(task.getTaskType(),TaskType.NON_FLOATING);
+ }
+
+ @Test
+ public void set_floatingTask_recurring_failure() throws Exception {
+ task = new Task(new Name("Name"), new UniqueTagList());
+ try{
+ task.setRecurringType(RecurringType.DAILY);
+ } catch (AssertionError error) {
+ assertTrue(true);
+ }
+ }
+
+ @Test
+ public void set_nonFloatingTask_recurring_successful() throws Exception {
+ task = new Task(new Name("Name"), new UniqueTagList(), new TaskDate("11 oct 11pm"), new TaskDate("11 oct 12pm"), RecurringType.NONE);
+ task.setRecurringType(RecurringType.DAILY);
+ assertEquals(task.getRecurringType(), RecurringType.DAILY);
+ }
+
+ @Test
+ public void set_TaskType_sucessful() throws Exception {
+ task = new Task(new Name("Name"), new UniqueTagList(), new TaskDate("11 oct 11pm"), new TaskDate("11 oct 12pm"), RecurringType.NONE);
+ task.setTaskType(TaskType.COMPLETED);
+ assertEquals("Task type should be mutated", task.getTaskType(), TaskType.COMPLETED);
+ }
+
+ @Test
+ public void append_taskComponent_toNonRecurringTask_notAllowed() throws Exception {
+ task = new Task(new Name("Name"), new UniqueTagList(),
+ new TaskDate("11 oct 11pm"), new TaskDate("11 oct 12pm"), RecurringType.NONE);
+ try {
+ task.appendRecurringDate(new TaskComponent(task,new TaskDate(), new TaskDate()));
+ } catch(AssertionError ae) {
+ assertTrue(true);
+ }
+ }
+
+ @Test
+ public void getLastAppendedComponent_success() throws Exception {
+ task = new Task(new Name("Name"), new UniqueTagList(),
+ new TaskDate("11 oct 11pm"), new TaskDate("11 oct 12pm"), RecurringType.DAILY);
+ TaskComponent toAppend = new TaskComponent(task, new TaskDate("12oct 11pm"), new TaskDate("12 oct 11.01pm"));
+ task.appendRecurringDate(toAppend);
+ TaskComponent component = task.getLastAppendedComponent();
+ assertEquals("Task component just appended must be the last appended component", toAppend, component);
+ }
+}
+```
+###### \java\seedu\address\model\task\UniqueTaskListTest.java
+``` java
+/**
+ * Tests methods that have not been fully covered in the other tests
+ *
+ */
+public class UniqueTaskListTest {
+ UniqueTaskList taskList;
+ @Before
+ public void setup() {
+ taskList = new UniqueTaskList();
+ }
+
+ @Test
+ public void add_duplicate_recurring_tasks_successful() throws Exception {
+ TaskBuilder builder = new TaskBuilder();
+ TestTask toAdd = builder.withName("Recurring Task").withStartDate("11oct 2016 11pm")
+ .withEndDate("12oct 2016 12pm").withRecurringType(RecurringType.DAILY).build();
+ TestTask toAddRecurring = builder.withName("Recurring Task").withStartDate("12oct 2016 11pm")
+ .withEndDate("13oct 2016 12pm").withRecurringType(RecurringType.DAILY).build();
+ taskList.add(toAdd);
+ taskList.add(toAddRecurring);
+ assertEquals(taskList.getInternalTaskList().size(), 1);
+ assertEquals(taskList.getInternalComponentList().size(),2);
+ }
+
+ @Test
+ public void add_duplicate_non_recurring_tasks_throwException() throws Exception {
+ TaskBuilder builder = new TaskBuilder();
+ TestTask toAdd = builder.withName("Recurring Task").withStartDate("11oct 2016 11pm")
+ .withEndDate("12oct 2016 12pm").withRecurringType(RecurringType.DAILY).build();
+ TestTask toAddRecurring = builder.withName("Recurring Task").withStartDate("12oct 2016 11pm")
+ .withEndDate("13oct 2016 12pm").withRecurringType(RecurringType.NONE).build();
+ try {
+ taskList.add(toAdd);
+ taskList.add(toAddRecurring);
+ } catch (DuplicateTaskException dte) {
+ assertTrue(true);
+ }
+ }
+
+ @Test
+ public void remove_task_that_does_not_exist() throws Exception {
+ TaskBuilder builder = new TaskBuilder();
+ TestTask toRemove = builder.withName("Recurring Task").withStartDate("11oct 2016 11pm")
+ .withEndDate("12oct 2016 12pm").withRecurringType(RecurringType.DAILY).build();
+ try {
+ taskList.remove(toRemove);
+ } catch (TaskNotFoundException tnfe) {
+ assertTrue(true);
+ }
+ }
+}
+```
diff --git a/collated/test/A0147967J.md b/collated/test/A0147967J.md
new file mode 100644
index 000000000000..1afbf7d44526
--- /dev/null
+++ b/collated/test/A0147967J.md
@@ -0,0 +1,1183 @@
+# A0147967J
+###### \java\guitests\AddCommandTest.java
+``` java
+ //add one non-floating task
+ taskToAdd = td.project;
+ assertAddNonFloatingSuccess(taskToAdd, currentList);
+ currentList = TestUtil.addTasksToList(currentList, taskToAdd);
+
+ //Unrecognizable recurring type recognized as normaltask
+ //duplicate non-floating not allowed
+ commandBox.runCommand(td.project.getAddNonFloatingCommand() + "not a type");
+ assertResultMessage(AddNonFloatingCommand.MESSAGE_DUPLICATE_TASK);
+ assertTrue(taskListPanel.isListMatching(TestUtil.convertTasksToDateComponents(currentList)));
+
+ //add deadline task
+ taskToAdd = td.paper;
+ assertAddNonFloatingSuccess(taskToAdd, currentList);
+ currentList = TestUtil.addTasksToList(currentList, taskToAdd);
+
+ //add task with overlapping slot allowed
+ taskToAdd = td.movie;
+ assertAddNonFloatingSuccess(taskToAdd, currentList);
+ currentList = TestUtil.addTasksToList(currentList, taskToAdd);
+
+ //add task with illegal time slot
+ commandBox.runCommand("add illegal timeslot from 2 oct 2pm to 2 oct 1pm");
+ assertResultMessage(AddNonFloatingCommand.MESSAGE_ILLEGAL_TIME_SLOT);
+ assertTrue(taskListPanel.isListMatching(TestUtil.convertTasksToDateComponents(currentList)));
+
+ //add to empty list
+ commandBox.runCommand("clear");
+ assertAddSuccess(td.trash);
+ currentList = new TestTask[]{td.trash};
+
+ //invalid command
+ commandBox.runCommand("adds Johnny");
+ assertResultMessage(Messages.MESSAGE_UNKNOWN_COMMAND);
+
+ //======Cases for handling recurring tasks==================================================
+
+ //Out dated Recurring task got updated
+ taskToAdd = td.daily;
+ assertAddCommandSuccess("add Daily Task from yesterday 7am to yesterday 8am daily",
+ taskToAdd, currentList);
+ currentList = TestUtil.addTasksToList(currentList, taskToAdd);
+
+ //Up to date remain stayed
+ taskToAdd = td.weekly;
+ assertAddCommandSuccess(taskToAdd.getAddRecurringCommand(),
+ taskToAdd, currentList);
+
+ }
+
+ private void assertAddSuccess(TestTask taskToAdd, TestTask... currentList) {
+ assertAddCommandSuccess(taskToAdd.getAddFloatingCommand(), taskToAdd, currentList);
+ }
+
+ private void assertAddNonFloatingSuccess(TestTask taskToAdd, TestTask... currentList) {
+ assertAddCommandSuccess(taskToAdd.getAddNonFloatingCommand(), taskToAdd, currentList);
+ }
+
+ private void assertAddCommandSuccess(String command, TestTask taskToAdd, TestTask... currentList){
+
+ commandBox.runCommand(command);
+ //confirm the new card contains the right data
+ TaskCardHandle addedCard = taskListPanel.navigateToTask(taskToAdd.getName().fullName);
+ assertMatching(taskToAdd.getTaskDateComponent().get(0), addedCard);
+
+ //confirm the list now contains all previous floatingTasks plus the new floatingTask
+ TestTask[] expectedList = TestUtil.addTasksToList(currentList, taskToAdd);
+
+ TaskComponent[] taskComponents = TestUtil.convertTasksToDateComponents(expectedList);
+ assertTrue(taskListPanel.isListMatching(taskComponents));
+ }
+}
+```
+###### \java\guitests\BrowserPanelTest.java
+``` java
+/**
+ * Tests if browser panel can display correct tasks.
+ */
+public class BrowserPanelTest extends TaskMasterGuiTest{
+
+ private final long DAY = 24*60*60*1000;
+
+ @Test
+ public void browserPanelTest(){
+
+ //Initial case: Out dated tasks/floating/deadlines not displayed, in the list 10 tasks
+ assertEquals(0, browser.getMyAgenda().appointments().size());
+
+ //Add Non recurring tasks display once
+ TestTask toBeAdded = td.none;
+ ArrayList expectedList = new ArrayList();
+ expectedList.add(toBeAdded.getLastAppendedComponent());
+ commandBox.runCommand(toBeAdded.getAddNonFloatingCommand());
+ assertIsAgendaMatching(expectedList);
+
+ //Add weekly, monthly or yearly display once
+ toBeAdded = td.weekly;
+ expectedList.add(toBeAdded.getLastAppendedComponent());
+ commandBox.runCommand(toBeAdded.getAddRecurringCommand());
+ assertIsAgendaMatching(expectedList);
+
+ toBeAdded = td.monthly;
+ expectedList.add(toBeAdded.getLastAppendedComponent());
+ commandBox.runCommand(toBeAdded.getAddRecurringCommand());
+ assertIsAgendaMatching(expectedList);
+
+ toBeAdded = td.yearly;
+ expectedList.add(toBeAdded.getLastAppendedComponent());
+ commandBox.runCommand(toBeAdded.getAddRecurringCommand());
+ assertIsAgendaMatching(expectedList);
+
+
+ //Add daily tasks, depends on day of week, add copies
+ toBeAdded = td.daily;
+ expectedList.add(toBeAdded.getLastAppendedComponent());
+ expectedList.addAll(getCopies(toBeAdded.getLastAppendedComponent()));
+ commandBox.runCommand(toBeAdded.getAddRecurringCommand());
+ assertIsAgendaMatching(expectedList);
+
+ //Archive current task, style change reflected
+ toBeAdded.getLastAppendedComponent().archive();
+ TaskComponent toBeArchived = toBeAdded.getLastAppendedComponent();
+ expectedList.set(4, toBeArchived);
+ commandBox.runCommand("done 15");
+ assertIsAgendaMatching(expectedList);
+
+ //With block command, style change reflected
+ toBeAdded = td.block;
+ expectedList.add(toBeAdded.getLastAppendedComponent());
+ commandBox.runCommand(toBeAdded.getBlockCommand());
+ assertIsAgendaMatching(expectedList);
+
+ }
+
+ private ArrayList getCopies(TaskComponent t){
+ ArrayList list = new ArrayList();
+ int dayOfWeek = TestUtil.getConvertedTime(t.getStartDate()).getDayOfWeek().getValue()%7;
+ for(int i = 1; i<=6-dayOfWeek;i++){
+ TaskComponent copy = new TaskComponent(t);
+ copy.setStartDate(new TaskDate(t.getStartDate().getDateInLong() + DAY*i));
+ copy.setEndDate(new TaskDate(t.getEndDate().getDateInLong() + DAY*i));
+ list.add(copy);
+ }
+ return list;
+ }
+}
+```
+###### \java\guitests\CompleteCommandTest.java
+``` java
+/**
+ * Tests if the correct task can be marked as done.
+ */
+public class CompleteCommandTest extends TaskMasterGuiTest {
+
+ @Test
+ public void complete() {
+
+ //delete the first in the list
+ TestTask[] currentList = td.getTypicalTasks();
+ TestTask[] completed = new TestTask[3];
+ int targetIndex = 1;
+ completed[0] = currentList[targetIndex-1];
+ assertCompleteSuccess(targetIndex, currentList);
+
+ //delete the last in the list
+ currentList = TestUtil.removeTaskFromList(currentList, targetIndex);
+ targetIndex = currentList.length;
+ completed[2] = currentList[targetIndex-1];
+ assertCompleteSuccess(targetIndex, currentList);
+
+ //delete from the middle of the list
+ currentList = TestUtil.removeTaskFromList(currentList, targetIndex);
+ targetIndex = 3;
+ completed[1] = currentList[targetIndex-1];
+ assertCompleteSuccess(targetIndex, currentList);
+
+ //invalid index
+ commandBox.runCommand("done " + currentList.length + 1);
+ assertResultMessage("The task index provided is invalid");
+
+ //Check changes are reflected in Completed panel
+ //Noted that completed tasks are not listed in the deleting order but adding order.
+ commandBox.runCommand("find -C");
+ assertTrue(taskListPanel.isListMatching(TestUtil.convertTasksToDateComponents(completed)));
+
+ }
+
+ /**
+ * Runs the delete command to delete the floatingTask at specified index and confirms the result is correct.
+ * @param targetIndexOneIndexed e.g. to delete the first floatingTask in the list, 1 should be given as the target index.
+ * @param currentList A copy of the current list of floatingTasks (before deletion).
+ */
+ private void assertCompleteSuccess(int targetIndexOneIndexed, final TestTask[] currentList) {
+ TestTask taskToComplete = currentList[targetIndexOneIndexed-1]; //-1 because array uses zero indexing
+ TestTask[] expectedRemainder = TestUtil.removeTaskFromList(currentList, targetIndexOneIndexed);
+
+ commandBox.runCommand("done " + targetIndexOneIndexed);
+
+ //confirm the list now contains all previous floatingTasks except the deleted floatingTask
+ assertTrue(taskListPanel.isListMatching(TestUtil.convertTasksToDateComponents(expectedRemainder)));
+
+ //confirm the result message is correct
+ assertResultMessage(String.format(MESSAGE_COMPLETE_TASK_SUCCESS, taskToComplete));
+ }
+
+}
+```
+###### \java\guitests\EditCommandTest.java
+``` java
+public class EditCommandTest extends TaskMasterGuiTest {
+
+ @Test
+ public void edit() throws IllegalValueException {
+
+ //Fix Index first to see edit effect
+ //edit deadline
+ int index = 4;
+ TestTask[] currentList = td.getTypicalTasks();
+ TestTask toBeEdited = currentList[index-1];
+ toBeEdited.setEndDate("2 oct 10am");
+ currentList[index-1] = toBeEdited;
+ assertEditSuccess(toBeEdited, "edit 4 by 2 oct 10am", currentList);
+
+ //edit it to time slot
+ toBeEdited = currentList[index-1];
+ toBeEdited.setEndDate("2 oct 9am");
+ toBeEdited.setEndDate("2 oct 11am");
+ currentList[index-1] = toBeEdited;
+ assertEditSuccess(toBeEdited, "edit 4 from 2 oct 9am to 2 oct 11am", currentList);
+
+ //add a tag
+ toBeEdited = currentList[index-1];
+ toBeEdited.setTag(new UniqueTagList(new Tag("testTag")));
+ currentList[index-1] = toBeEdited;
+ assertEditSuccess(toBeEdited, "edit 4 t/testTag", currentList);
+
+ //add tags
+ toBeEdited = currentList[index-1];
+ toBeEdited.setTag(new UniqueTagList(new Tag("testTag"),new Tag("testTag1")));
+ currentList[index-1] = toBeEdited;
+ assertEditSuccess(toBeEdited, "edit 4 t/testTag t/testTag1", currentList);
+
+ //change name
+ toBeEdited = currentList[index-1];
+ toBeEdited.setName(new Name("Test name"));
+ currentList[index-1] = toBeEdited;
+ assertEditSuccess(toBeEdited, "edit 4 Test name", currentList);
+
+ //invalid index
+ commandBox.runCommand("edit " + currentList.length + 1 + " invalid index");
+ assertResultMessage("The task index provided is invalid");
+
+ //invalid command
+ commandBox.runCommand("edits read weblecture");
+ assertResultMessage(Messages.MESSAGE_UNKNOWN_COMMAND);
+
+ //Edit a normal task to a recurring task
+ toBeEdited = currentList[index - 1];
+ toBeEdited.setRecurringType(RecurringType.MONTHLY);
+ currentList[index - 1] = toBeEdited;
+ assertEditSuccess(toBeEdited, "edit 4 monthly", currentList);
+
+ //Edit it back also enabled
+ toBeEdited = currentList[index - 1];
+ toBeEdited.setRecurringType(RecurringType.NONE);;
+ currentList[index - 1] = toBeEdited;
+ assertEditSuccess(toBeEdited, "edit 4 none", currentList);
+
+ }
+
+ private void assertEditSuccess(TestTask editedCopy, String command, TestTask... modifiedList) {
+
+ commandBox.runCommand(command);
+
+ //confirm the new card contains the right data
+ TaskCardHandle editedCard = taskListPanel.navigateToTask(editedCopy.getName().fullName);
+ assertMatching(editedCopy.getTaskDateComponent().get(0), editedCard);
+
+ //confirm the list now contains all the unmodified tasks and the edited task
+ TaskComponent[] taskComponents = TestUtil.convertTasksToDateComponents(modifiedList);
+
+ assertTrue(taskListPanel.isListMatching(taskComponents));
+ }
+
+
+
+}
+```
+###### \java\guitests\FindCommandTest.java
+``` java
+ @Test
+ public void find_byDeadline(){
+ try{
+ //By time-after and sharp
+ assertFindResult("find by 18 oct 5pm", td.labDeadline.getTaskDateComponent().get(0));
+ //By time-before
+ assertFindResult("find by 18 oct 4.59pm");
+ }catch(Exception e){}
+ }
+
+ @Test
+ public void find_byTimeSlot(){
+ //Cover multiple
+ assertFindResult("find from 17 oct 10am to 17 oct 10pm", td.tutorialSlot.getTaskDateComponent().get(0), td.concert.getTaskDateComponent().get(0));
+ //Cover one
+ assertFindResult("find from 17 oct 10am to 17 oct 3pm", td.tutorialSlot.getTaskDateComponent().get(0));
+ //Cover none
+ assertFindResult("find from 17 oct 5pm to 17 oct 6pm");
+ }
+
+ @Test
+ public void find_byTag(){
+ //Existing one tag one result successful
+ assertFindResult("find t/textBook", td.book.getTaskDateComponent().get(0));
+ //Existing multiple tags multiple results successful
+ assertFindResult("find t/CS2105 t/textBook", td.book.getTaskDateComponent().get(0), td.homework.getTaskDateComponent().get(0));
+ //Invalid/Non-existing tag lists nothing
+ assertFindResult("find t/nothing");
+ assertFindResult("find t/--[][]");
+ }
+
+ @Test
+ public void find_byType(){
+ //Floating tasks
+ assertFindResult("find -F",td.trash.getTaskDateComponent().get(0), td.book.getTaskDateComponent().get(0),
+ td.homework.getTaskDateComponent().get(0), td.lecture.getTaskDateComponent().get(0),
+ td.meeting.getTaskDateComponent().get(0), td.george.getTaskDateComponent().get(0));
+ //Completed tasks
+ commandBox.runCommand("done 1");
+ assertFindResult("find -C", td.trash.getTaskDateComponent().get(0));
+ }
+```
+###### \java\guitests\guihandles\BrowserPanelHandle.java
+``` java
+/**
+ * Provides a handle for the panel containing the task list.
+ */
+public class BrowserPanelHandle extends GuiHandle {
+
+ public static final int NOT_FOUND = -1;
+
+ private static final String AGENDA_ID = "#agenda";
+
+ public BrowserPanelHandle(GuiRobot guiRobot, Stage primaryStage) {
+ super(guiRobot, primaryStage, TestApp.APP_TITLE);
+ }
+
+ public MyAgenda getMyAgenda() {
+ return (MyAgenda) getNode(AGENDA_ID);
+ }
+
+ public boolean isContained(AppointmentImplLocal target){
+ for(Appointment a: getMyAgenda().appointments())
+ if(TestUtil.isSameAppointment(a, target)) return true;
+ return false;
+ }
+
+}
+```
+###### \java\guitests\guihandles\NavbarPanelHandle.java
+``` java
+/**
+ * Provides a handle for the panel containing the task list.
+ */
+public class NavbarPanelHandle extends GuiHandle {
+
+ private final String NAVBAR_TASKS = " Tasks";
+ private final String NAVBAR_DEADLINES = " Deadlines";
+ private final String NAVBAR_INCOMING_DEADLINES = " Incoming Deadlines";
+ private final String NAVBAR_FLOATING_TASKS = " Floating Tasks";
+ private final String NAVBAR_COMPLETED = " Completed";
+
+ private final ObservableList navbarElement = FXCollections.observableArrayList(NAVBAR_TASKS, NAVBAR_DEADLINES, NAVBAR_FLOATING_TASKS
+ ,NAVBAR_INCOMING_DEADLINES, NAVBAR_COMPLETED);
+
+ public static final int NOT_FOUND = -1;
+ public static final String CARD_PANE_ID = "#cardPaneNav";
+
+ private static final String TASK_LIST_VIEW_ID = "#navbarView";
+
+ public NavbarPanelHandle(GuiRobot guiRobot, Stage primaryStage) {
+ super(guiRobot, primaryStage, TestApp.APP_TITLE);
+ }
+
+ public List getSelectedNavigation() {
+ ListView navigationList = getListView();
+ return navigationList.getSelectionModel().getSelectedItems();
+ }
+
+ @SuppressWarnings("unchecked")
+ public ListView getListView() {
+ return (ListView) getNode(TASK_LIST_VIEW_ID);
+ }
+
+ /**
+ * Navigates the list view to display and select the task.
+ */
+ public void navigateTo(String nav) {
+ int index = navbarElement.indexOf(nav);
+
+ guiRobot.interact(() -> {
+ getListView().scrollTo(index);
+ guiRobot.sleep(150);
+ getListView().getSelectionModel().select(index);
+ });
+ guiRobot.sleep(550);
+
+ }
+}
+```
+###### \java\guitests\NavbarPanelTest.java
+``` java
+public class NavbarPanelTest extends TaskMasterGuiTest {
+
+ private final String NAVBAR_TASKS = " Tasks";
+ private final String NAVBAR_DEADLINES = " Deadlines";
+ private final String NAVBAR_INCOMING_DEADLINES = " Incoming Deadlines";
+ private final String NAVBAR_FLOATING_TASKS = " Floating Tasks";
+ private final String NAVBAR_COMPLETED = " Completed";
+
+ @Test
+ public void navigateToFloatingTasks() {
+ TaskComponent[] expected;
+ //Navigate to all floating tasks
+ expected = Arrays.copyOfRange(td.getTypicalTaskComponents(), 0, 6);
+ assertResult(NAVBAR_FLOATING_TASKS, expected);
+ }
+
+ @Test
+ public void navigateToDeadlines() {
+ //Navigate to deadlines
+ assertResult(NAVBAR_DEADLINES, td.labDeadline.getComponentForNonRecurringType(),
+ td.essayDeadline.getComponentForNonRecurringType());
+ }
+
+ @Test
+ public void navigateToIncomingDeadlines() {
+ //Navigate to incoming deadlines
+ commandBox.runCommand(td.incoming.getAddNonFloatingCommand());
+ assertResult(NAVBAR_INCOMING_DEADLINES, td.labDeadline.getComponentForNonRecurringType(),
+ td.essayDeadline.getComponentForNonRecurringType(),
+ td.incoming.getComponentForNonRecurringType());
+ }
+
+ @Test
+ public void navigateToAllTasks() {
+ //Navigate to all tasks
+ TaskComponent[] expected = td.getTypicalTaskComponents();
+ assertResult(NAVBAR_TASKS, expected);
+ }
+
+ @Test
+ public void navigateToCompletedOnes() {
+ //Navigate to completed ones
+ commandBox.runCommand("done 1");
+ assertResult(NAVBAR_COMPLETED, td.trash.getComponentForNonRecurringType());
+ }
+
+ private void assertResult(String navigation, TaskComponent... expectedHits) {
+ navbar.navigateTo(navigation);
+ assertListSize(expectedHits.length);
+ if(navigation.equals(NAVBAR_TASKS))
+ assertResultMessage("Listed all tasks");
+ else
+ assertResultMessage(expectedHits.length + " tasks listed!");
+ assertTrue(taskListPanel.isListMatching(expectedHits));
+ }
+
+}
+```
+###### \java\guitests\TaskMasterGuiTest.java
+``` java
+ /**
+ * Asserts the expected task components are reflected in the agenda.
+ * @param expected
+ */
+ protected void assertIsAgendaMatching(ArrayList expectedShown){
+ //Get the updated agenda
+ MyAgenda toBeChecked = browser.getMyAgenda();
+ //Checks the number of items in the agenda
+ assertEquals(expectedShown.size(), toBeChecked.appointments().size());
+ //Checks one-to-one match
+ for(TaskComponent t: expectedShown){
+ assertTrue(browser.isContained(TestUtil.getAppointment(t)));
+ }
+ }
+
+}
+```
+###### \java\guitests\ViewCommandTest.java
+``` java
+/**
+ * Tests view command and agenda time range change event.
+ * Currently only tentative and will add in more later.
+ */
+public class ViewCommandTest extends TaskMasterGuiTest{
+
+ private final long DAY = 24*60*60*1000;
+ @Test
+ public void view(){
+
+ //View today
+ TestTask toBeAdded = td.weekly;
+ ArrayList expectedList = new ArrayList();
+ expectedList.add(toBeAdded.getLastAppendedComponent());
+ commandBox.runCommand(toBeAdded.getAddRecurringCommand());
+ assertViewSuccess("today", expectedList);
+
+ //View next week today
+ TaskComponent updated = toBeAdded.getLastAppendedComponent();
+ updated.setStartDate(new TaskDate(updated.getStartDate().getDateInLong() + 7 * DAY));
+ updated.setEndDate(new TaskDate(updated.getEndDate().getDateInLong() + 7 * DAY));
+ expectedList.set(0, updated);
+ assertViewSuccess("next week today", expectedList);
+ }
+
+ public void assertViewSuccess(String date, ArrayList expectedList){
+
+ commandBox.runCommand("view "+date);
+ assertEquals(TestUtil.getConvertedTime(new TaskDate(date)).truncatedTo(ChronoUnit.DAYS),
+ browser.getMyAgenda().getDisplayedLocalDateTime());
+ assertIsAgendaMatching(expectedList);
+
+ }
+}
+```
+###### \java\seedu\address\logic\LogicManagerTest.java
+``` java
+ @Test
+ public void execute_addNonFloating_sucessful() throws Exception {
+ // setup expectations
+ TestDataHelper helper = new TestDataHelper();
+ Task toBeAdded = helper.nonFloatingFromDateToDate();
+ TaskMaster expectedAB = new TaskMaster();
+ expectedAB.addTask(toBeAdded);
+
+ // execute command and verify result
+ assertCommandBehavior(helper.generateAddCommand(toBeAdded),
+ String.format(AddNonFloatingCommand.MESSAGE_SUCCESS, toBeAdded),
+ expectedAB,
+ expectedAB.getTaskComponentList());
+ assertUndoRedoAble(String.format(AddNonFloatingCommand.MESSAGE_SUCCESS, toBeAdded),
+ expectedAB,
+ expectedAB.getTaskComponentList());
+ }
+```
+###### \java\seedu\address\logic\LogicManagerTest.java
+``` java
+ @Test
+ public void execute_addDuplicate_notAllowed() throws Exception {
+ // setup expectations
+ TestDataHelper helper = new TestDataHelper();
+ Task toBeAdded = helper.adam();
+ TaskMaster expectedAB = new TaskMaster();
+ expectedAB.addTask(toBeAdded);
+
+ // setup starting state
+ model.addTask(toBeAdded); // task already in internal task list
+
+ // execute command and verify result
+ assertCommandBehavior(
+ helper.generateAddCommand(toBeAdded),
+ AddFloatingCommand.MESSAGE_DUPLICATE_TASK,
+ expectedAB,
+ expectedAB.getTaskComponentList());
+ }
+
+
+ @Test
+ public void execute_addOverlapSlot_allowed() throws Exception {
+ // setup expectations
+ TestDataHelper helper = new TestDataHelper();
+ Task toBeAdded = new Task(new Name("Task one"), new UniqueTagList(),
+ new TaskDate("2 oct 2am"), new TaskDate("2 oct 1pm"),
+ RecurringType.NONE);
+ Task toBeAddedAfter = new Task(new Name("Task two"), new UniqueTagList(),
+ new TaskDate("2 oct 10am"), new TaskDate("2 oct 11am"),
+ RecurringType.NONE);
+ TaskMaster expectedAB = new TaskMaster();
+ expectedAB.addTask(toBeAdded);
+ expectedAB.addTask(toBeAddedAfter);
+
+ // setup starting state
+ model.addTask(toBeAdded); // task already in internal task list
+
+ // execute command and verify result
+ assertCommandBehavior(
+ helper.generateAddCommand(toBeAddedAfter),
+ String.format(AddNonFloatingCommand.MESSAGE_SUCCESS, toBeAddedAfter),
+ expectedAB,
+ expectedAB.getTaskComponentList());
+
+ }
+
+ @Test
+ public void execute_addDeadlineOverlap_Successful() throws Exception {
+ // setup expectations
+ TestDataHelper helper = new TestDataHelper();
+ Task toBeAdded = new Task(new Name("Task one"), new UniqueTagList(),
+ new TaskDate("2 oct 2am"), new TaskDate("2 oct 1pm"),
+ RecurringType.NONE);
+ Task toBeAddedAfter = new Task(new Name("Task two"), new UniqueTagList(),
+ new TaskDate(TaskDate.DATE_NOT_PRESENT), new TaskDate("2 oct 11am"),
+ RecurringType.NONE);
+ TaskMaster expectedAB = new TaskMaster();
+ expectedAB.addTask(toBeAdded);
+ expectedAB.addTask(toBeAddedAfter);
+
+ // setup starting state
+ model.addTask(toBeAdded); // task already in internal task list
+
+ // execute command and verify result
+ assertCommandBehavior(
+ helper.generateAddCommand(toBeAddedAfter),
+ String.format(AddNonFloatingCommand.MESSAGE_SUCCESS, toBeAddedAfter),
+ expectedAB,
+ expectedAB.getTaskComponentList());
+ assertUndoRedoAble(String.format(AddNonFloatingCommand.MESSAGE_SUCCESS, toBeAddedAfter),
+ expectedAB,
+ expectedAB.getTaskComponentList());
+
+ }
+
+ @Test
+ public void execute_addIllegalSlot_notAllowed() throws Exception {
+ // setup expectations
+ TestDataHelper helper = new TestDataHelper();
+ Task toBeAdded = new Task(new Name("Task one"), new UniqueTagList(),
+ new TaskDate("2 oct 6am"), new TaskDate("2 oct 5am"),
+ RecurringType.NONE);
+ TaskMaster expectedAB = new TaskMaster();
+
+ // execute command and verify result
+ assertCommandBehavior(
+ helper.generateAddCommand(toBeAdded),
+ AddNonFloatingCommand.MESSAGE_ILLEGAL_TIME_SLOT,
+ expectedAB,
+ expectedAB.getTaskComponentList());
+
+ }
+```
+###### \java\seedu\address\logic\LogicManagerTest.java
+``` java
+ /**
+ * The logic for block command is actually the same as add-non=floating commands.
+ * */
+ @Test
+ public void execute_block_invalidArgsFormat() throws Exception {
+ String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, BlockCommand.MESSAGE_USAGE);
+ assertCommandBehavior(
+ "block 2am to 3am", expectedMessage);
+ }
+
+ @Test
+ public void execute_block_successful() throws Exception {
+ // setup expectations
+ TestDataHelper helper = new TestDataHelper();
+ Task toBeAdded = new Task(new Name(BlockCommand.DUMMY_NAME), new UniqueTagList(),
+ new TaskDate("2 oct 2am"), new TaskDate("2 oct 1pm"),
+ RecurringType.NONE);
+ TaskMaster expectedAB = new TaskMaster();
+ expectedAB.addTask(toBeAdded);
+
+ // execute command and verify result
+ assertCommandBehavior(helper.generateBlockCommand(toBeAdded),
+ String.format(BlockCommand.MESSAGE_SUCCESS, toBeAdded),
+ expectedAB,
+ expectedAB.getTaskComponentList());
+ assertUndoRedoAble(String.format(BlockCommand.MESSAGE_SUCCESS, toBeAdded),
+ expectedAB,
+ expectedAB.getTaskComponentList());
+
+ }
+
+ @Test
+ public void execute_blockOverlapSlot_notAllowed() throws Exception {
+ // setup expectations
+ TestDataHelper helper = new TestDataHelper();
+ Task toBeBlocked = new Task(new Name(BlockCommand.DUMMY_NAME), new UniqueTagList(),
+ new TaskDate("2 oct 2am"), new TaskDate("2 oct 1pm"),
+ RecurringType.NONE);
+ Task toBeAddedAfter = new Task(new Name(BlockCommand.DUMMY_NAME), new UniqueTagList(),
+ new TaskDate("2 oct 10am"), new TaskDate("2 oct 11am"),
+ RecurringType.NONE);
+ TaskMaster expectedAB = new TaskMaster();
+ expectedAB.addTask(toBeBlocked);
+
+ // setup starting state
+ model.addTask(toBeBlocked); // task already in internal task list
+
+ // execute command and verify result
+ assertCommandBehavior(
+ helper.generateAddCommand(toBeAddedAfter),
+ BlockCommand.MESSAGE_TIMESLOT_OCCUPIED,
+ expectedAB,
+ expectedAB.getTaskComponentList());
+
+ }
+
+ @Test
+ public void execute_blockOverlapWithExistingTask_notAllowed() throws Exception {
+ // setup expectations
+ TestDataHelper helper = new TestDataHelper();
+ Task toBeBlocked = new Task(new Name("Test Task"), new UniqueTagList(),
+ new TaskDate("2 oct 2am"), new TaskDate("2 oct 1pm"),
+ RecurringType.NONE);
+ Task toBeAddedAfter = new Task(new Name(BlockCommand.DUMMY_NAME), new UniqueTagList(),
+ new TaskDate("2 oct 10am"), new TaskDate("2 oct 11am"),
+ RecurringType.NONE);
+ TaskMaster expectedAB = new TaskMaster();
+ expectedAB.addTask(toBeBlocked);
+
+ // setup starting state
+ model.addTask(toBeBlocked); // task already in internal task list
+
+ // execute command and verify result
+ assertCommandBehavior(
+ helper.generateAddCommand(toBeAddedAfter),
+ BlockCommand.MESSAGE_TIMESLOT_OCCUPIED,
+ expectedAB,
+ expectedAB.getTaskComponentList());
+
+ }
+
+ @Test
+ public void execute_blockIllegalSlot_notAllowed() throws Exception {
+ // setup expectations
+ TestDataHelper helper = new TestDataHelper();
+ Task toBeBlocked = new Task(new Name(BlockCommand.DUMMY_NAME), new UniqueTagList(),
+ new TaskDate("2 oct 6am"), new TaskDate("2 oct 5am"),
+ RecurringType.NONE);
+ TaskMaster expectedAB = new TaskMaster();
+
+ // execute command and verify result
+ assertCommandBehavior(
+ helper.generateBlockCommand(toBeBlocked),
+ BlockCommand.MESSAGE_ILLEGAL_TIME_SLOT,
+ expectedAB,
+ expectedAB.getTaskComponentList());
+
+ }
+
+ /**
+ * Tests for undo/redo commands.
+ */
+ @Test
+ public void execute_undoredoNothing_notAllowed() throws Exception {
+ // setup expectations
+ TaskMaster expectedAB = new TaskMaster();
+
+ // execute command and verify result
+ assertCommandBehavior(
+ "u",
+ UndoCommand.MESSAGE_FAIL,
+ expectedAB,
+ expectedAB.getTaskComponentList());
+
+ assertCommandBehavior(
+ "r",
+ RedoCommand.MESSAGE_FAIL,
+ expectedAB,
+ expectedAB.getTaskComponentList());
+ }
+
+
+ @Test
+ public void execute_undoredo_Successful() throws Exception {
+ // setup expectations
+ TestDataHelper helper = new TestDataHelper();
+ Task toBeAdded = helper.adam();
+ TaskMaster expectedAB = new TaskMaster();
+ expectedAB.addTask(toBeAdded);
+
+ // execute command and verify result
+ assertCommandBehavior(helper.generateAddCommand(toBeAdded),
+ String.format(AddFloatingCommand.MESSAGE_SUCCESS, toBeAdded),
+ expectedAB,
+ expectedAB.getTaskComponentList());
+
+ expectedAB = new TaskMaster();
+ assertCommandBehavior("u",
+ UndoCommand.MESSAGE_SUCCESS,
+ expectedAB,
+ expectedAB.getTaskComponentList());
+
+ expectedAB.addTask(toBeAdded);
+ assertCommandBehavior(
+ "r",
+ String.format(AddFloatingCommand.MESSAGE_SUCCESS, toBeAdded),
+ expectedAB,
+ expectedAB.getTaskComponentList());
+ }
+
+ @Test
+ public void execute_undoredoReachMaxTimes_notAllowed() throws Exception{
+ TestDataHelper helper = new TestDataHelper();
+ TaskMaster expectedAB = new TaskMaster();
+ for(int i = 1; i < 5; i++){
+ Task t = helper.generateTask(i);
+ logic.execute(helper.generateAddCommand(t));
+ }
+ for(int i = 0; i < 3; i++)
+ logic.execute("u");
+
+ expectedAB.addTask(helper.generateTask(1));
+ assertCommandBehavior(
+ "u",
+ UndoCommand.MESSAGE_FAIL,
+ expectedAB,
+ expectedAB.getTaskComponentList());
+
+ for(int i = 0; i < 3; i++){
+ logic.execute("r");
+ expectedAB.addTask(helper.generateTask(2+i));
+ }
+
+ assertCommandBehavior(
+ "r",
+ RedoCommand.MESSAGE_FAIL,
+ expectedAB,
+ expectedAB.getTaskComponentList());
+ }
+
+ @Test
+ public void execute_undoInvalidCommand_notAllowed() throws Exception{
+
+ TaskMaster expectedAB = new TaskMaster();
+ logic.execute("adds t");
+ assertCommandBehavior(
+ "u",
+ UndoCommand.MESSAGE_FAIL,
+ expectedAB,
+ expectedAB.getTaskComponentList());
+
+ }
+
+ @Test
+ public void execute_undoFailedCommand_notAllowed() throws Exception{
+
+ TestDataHelper helper = new TestDataHelper();
+ TaskMaster expectedAB = new TaskMaster();
+ Task toBeAdded = helper.adam();
+
+ expectedAB.addTask(toBeAdded);
+ model.addTask(toBeAdded);
+
+ logic.execute(helper.generateAddCommand(toBeAdded));
+
+ assertCommandBehavior(
+ "u",
+ UndoCommand.MESSAGE_FAIL,
+ expectedAB,
+ expectedAB.getTaskComponentList());
+
+ }
+
+
+ /***
+ * Tests for ChangeDirectoryCommand
+ */
+ @Test
+ public void execute_changeDirectoryIllegalDirectory_notAllowed() throws Exception{
+
+ TaskMaster expectedAB = new TaskMaster();
+ assertCommandBehavior(
+ "cd random path",
+ ChangeDirectoryCommand.MESSAGE_CONVENSION_ERROR,
+ expectedAB,
+ expectedAB.getTaskComponentList());
+ }
+
+ @Test
+ public void execute_changeDirectoryWrongFileType_notAllowed() throws Exception{
+
+ TaskMaster expectedAB = new TaskMaster();
+ assertCommandBehavior(
+ "cd "+saveFolder.getRoot().getPath()+"cdtest.txt",
+ ChangeDirectoryCommand.MESSAGE_CONVENSION_ERROR,
+ expectedAB,
+ expectedAB.getTaskComponentList());
+ }
+
+ @Test
+ public void execute_changeDirectory_Successful() throws Exception{
+
+ TestDataHelper helper = new TestDataHelper();
+ TaskMaster expectedAB = new TaskMaster();
+ assertCommandBehavior(
+ "cd "+ saveFolder.getRoot().getPath()+"cdtest.xml",
+ String.format(ChangeDirectoryCommand.MESSAGE_SUCCESS, saveFolder.getRoot().getPath()+"cdtest.xml"),
+ expectedAB,
+ expectedAB.getTaskComponentList());
+
+ //Ensure model writes to this file
+ expectedAB.addTask(helper.adam());
+ logic.execute(helper.generateAddCommand(helper.adam()));
+ ReadOnlyTaskMaster retrieved = new StorageManager(saveFolder.getRoot().getPath()+"cdtest.xml",
+ saveFolder.getRoot().getPath() + "TempPreferences.json").readTaskList().get();
+ assertEquals(expectedAB, new TaskMaster(retrieved));
+ assertEquals(model.getTaskMaster(), new TaskMaster(retrieved));
+
+ }
+```
+###### \java\seedu\address\logic\LogicManagerTest.java
+``` java
+ @Test
+ public void execute_completeInvalidArgsFormat_errorMessageShown() throws Exception {
+ String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, CompleteCommand.MESSAGE_USAGE);
+ assertIncorrectIndexFormatBehaviorForCommand("done", expectedMessage);
+ }
+
+ @Test
+ public void execute_completeIndexNotFound_errorMessageShown() throws Exception {
+ assertIndexNotFoundBehaviorForCommand("done");
+ }
+
+ @Test
+ public void execute_complete_removesCorrectTask() throws Exception {
+ TestDataHelper helper = new TestDataHelper();
+
+ Task toComplete = helper.adam();
+ TaskMaster expectedAB = new TaskMaster();
+ expectedAB.addTask(toComplete);
+ model.addTask(toComplete);
+
+ assertCommandBehavior("done 1",
+ String.format(CompleteCommand.MESSAGE_COMPLETE_TASK_SUCCESS, toComplete),
+ expectedAB,
+ new TaskMaster().getTaskComponentList());
+ assertUndoRedoAble(String.format(CompleteCommand.MESSAGE_COMPLETE_TASK_SUCCESS, toComplete),
+ expectedAB,
+ new TaskMaster().getTaskComponentList());
+
+ }
+```
+###### \java\seedu\address\logic\LogicManagerTest.java
+``` java
+ @Test
+ public void execute_findByDateTimeBoundary() throws Exception{
+ TestDataHelper helper = new TestDataHelper();
+ Task pTarget1 = helper.generateTaskWithName("bla bla KEY bla");
+ Task pTarget2 = helper.generateTaskWithName("bla rAnDoM bla bceofeia");
+ Task pTarget3 = helper.generateTaskWithName("key key");
+ Task p1 = helper.generateTaskWithName("sduauo");
+ Task test = helper.nonFloatingByDate();
+ Task test2 = helper.nonFloatingFromDateToDate();
+
+ List fourTasks = helper.generateTasks(pTarget1, p1, pTarget2, pTarget3);
+ TaskMaster expectedAB = helper.generateTaskList(fourTasks);
+ List expectedList = helper.generateTasks(test);
+
+ expectedAB.addTask(test);
+ expectedAB.addTask(test2);
+ helper.addToModel(model, fourTasks);
+ model.addTask(test);
+ model.addTask(test2);
+
+ List componentList = helper.buildTaskComponentsFromTaskList(expectedList);
+
+ //find by exact time successful
+ assertCommandBehavior("find by 20 oct 11am",
+ Command.getMessageForTaskListShownSummary(expectedList.size()),
+ expectedAB,
+ componentList);
+ //find by earlier time boundary lists nothing
+ assertCommandBehavior("find by 20 oct 10.59am",
+ Command.getMessageForTaskListShownSummary(0),
+ expectedAB,
+ new TaskMaster().getTaskComponentList());
+ //find by later time boundary successful
+ assertCommandBehavior("find by 20 oct 11.01pm",
+ Command.getMessageForTaskListShownSummary(expectedList.size()),
+ expectedAB,
+ componentList);
+ }
+
+ @Test
+ public void execute_findFromDateBoundaryToDateBoundary() throws Exception{
+ TestDataHelper helper = new TestDataHelper();
+ Task pTarget1 = helper.generateTaskWithName("bla bla KEY bla");
+ Task pTarget2 = helper.generateTaskWithName("bla rAnDoM bla bceofeia");
+ Task pTarget3 = helper.generateTaskWithName("key key");
+ Task p1 = helper.generateTaskWithName("sduauo");
+ Task test = helper.nonFloatingFromDateToDate();
+ Task test2 = helper.nonFloatingByDate();
+
+ List fourTasks = helper.generateTasks(pTarget1, p1, pTarget2, pTarget3);
+ TaskMaster expectedAB = helper.generateTaskList(fourTasks);
+ List expectedList = helper.generateTasks(test);
+
+ expectedAB.addTask(test);
+ expectedAB.addTask(test2);
+
+ helper.addToModel(model, fourTasks);
+ model.addTask(test);
+ model.addTask(test2);
+ List expectedComponentList = helper.buildTaskComponentsFromTaskList(expectedList);
+
+ //find by exact boundary successful
+ assertCommandBehavior("find from 19 oct 10pm to 20 oct 11am",
+ Command.getMessageForTaskListShownSummary(expectedList.size()),
+ expectedAB,
+ expectedComponentList);
+ //find by smaller boundary lists nothing
+ assertCommandBehavior("find from 19 oct 10.01pm to 20 oct 11am",
+ Command.getMessageForTaskListShownSummary(0),
+ expectedAB,
+ new TaskMaster().getTaskComponentList());
+
+ assertCommandBehavior("find from 19 oct 10pm to 20 oct 10.59am",
+ Command.getMessageForTaskListShownSummary(0),
+ expectedAB,
+ new TaskMaster().getTaskComponentList());
+ //find by lax boundary successful
+ assertCommandBehavior("find from 19 oct 9pm to 20 oct 1pm",
+ Command.getMessageForTaskListShownSummary(expectedList.size()),
+ expectedAB,
+ expectedComponentList);
+ }
+
+ @Test
+ public void execute_findFloatingTasksbyType_Successful() throws Exception{
+ TestDataHelper helper = new TestDataHelper();
+ Task pTarget1 = helper.generateTaskWithName("bla bla KEY bla");
+ Task pTarget2 = helper.generateTaskWithName("bla rAnDoM bla bceofeia");
+ Task pTarget3 = helper.generateTaskWithName("key key");
+ Task p1 = helper.generateTaskWithName("sduauo");
+ Task test = helper.nonFloatingFromDateToDate();
+
+ List threeTasks = helper.generateTasks(pTarget1, pTarget2, pTarget3);
+ TaskMaster expectedAB = helper.generateTaskList(threeTasks);
+ List expectedList = helper.generateTasks(pTarget1, pTarget2, pTarget3);
+
+ expectedAB.addTask(test);
+ expectedAB.addTask(p1);
+
+ helper.addToModel(model, threeTasks);
+ model.addTask(test);
+ model.addTask(p1);
+ logic.execute("done 5");
+
+ List expectedComponentList = helper.buildTaskComponentsFromTaskList(expectedList);
+
+ assertCommandBehavior("find -F",
+ Command.getMessageForTaskListShownSummary(expectedList.size()),
+ expectedAB,
+ expectedComponentList);
+
+ }
+
+ @Test
+ public void execute_findCompletedTasksbyType_Successful() throws Exception{
+ TestDataHelper helper = new TestDataHelper();
+ Task pTarget1 = helper.generateTaskWithName("bla bla KEY bla");
+ Task test = helper.nonFloatingFromDateToDate();
+
+ TaskMaster expectedAB = new TaskMaster();
+ List expectedList = helper.generateTasks(test);
+
+ expectedAB.addTask(pTarget1);
+ expectedAB.addTask(test);
+
+ model.addTask(pTarget1);
+ model.addTask(test);
+
+ List expectedComponentList = helper.buildTaskComponentsFromTaskList(expectedList);
+
+ logic.execute("done 2");
+
+ assertCommandBehavior("find -C",
+ Command.getMessageForTaskListShownSummary(expectedList.size()),
+ expectedAB,
+ expectedComponentList);
+
+ }
+ @Test
+ public void execute_findbyMultipleConstraints_Successful() throws Exception{
+ TestDataHelper helper = new TestDataHelper();
+ Task pTarget1 = helper.generateTaskWithName("bla bla KEY bla");
+ Task test = helper.nonFloatingFromDateToDate();
+
+ TaskMaster expectedAB = new TaskMaster();
+ List expectedList = helper.generateTasks(test);
+
+ expectedAB.addTask(pTarget1);
+ expectedAB.addTask(test);
+
+ model.addTask(pTarget1);
+ model.addTask(test);
+
+ List expectedComponentList = helper.buildTaskComponentsFromTaskList(expectedList);
+
+ assertCommandBehavior("find non floating from 19 oct 1am to 21 oct 3am",
+ Command.getMessageForTaskListShownSummary(expectedList.size()),
+ expectedAB,
+ expectedComponentList);
+
+ }
+
+
+ @Test
+ public void execute_findbyTag_Successful() throws Exception {
+ TestDataHelper helper = new TestDataHelper();
+ Task pTarget1 = helper.generateTaskWithName("bla bla KEY bla");
+ Task test = helper.nonFloatingFromDateToDate();
+
+ TaskMaster expectedAB = new TaskMaster();
+ List expectedList = helper.generateTasks(test);
+
+ expectedAB.addTask(pTarget1);
+ expectedAB.addTask(test);
+
+ model.addTask(pTarget1);
+ model.addTask(test);
+
+ List expectedComponentList = helper.buildTaskComponentsFromTaskList(expectedList);
+
+ assertCommandBehavior("find t/tag1",
+ Command.getMessageForTaskListShownSummary(expectedList.size()),
+ expectedAB,
+ expectedComponentList);
+
+ }
+
+ /**
+ * Tests for view command.
+ */
+ @Test
+ public void execute_view_InvalidInputDate_notAllowed() throws Exception{
+ String expectedMessage = Messages.MESSAGE_ILLEGAL_DATE_INPUT;
+ assertCommandBehavior("view random input", expectedMessage);
+ }
+
+ @Test
+ public void execute_view_successful() throws Exception {
+ String test = "23 oct 12am";
+ TaskDate testDate = new TaskDate(test);
+ assertCommandBehavior("view 23 oct 12am",
+ String.format(ViewCommand.MESSAGE_UPDATE_AGENDA_SUCCESS, formatter.format(testDate.getDate())));
+ assertEquals(testDate, checkDate);
+ assertEquals(latestSavedTaskList.getTaskComponentList(), checkList);
+ assertEquals(model.getTaskMaster().getTaskComponentList(), checkList);
+ }
+```
+###### \java\seedu\address\testutil\TestUtil.java
+``` java
+ /** Returns a LocalDateTime object converted from TaskDate. */
+ public static LocalDateTime getConvertedTime(TaskDate t){
+ return LocalDateTime.ofInstant(new Date(t.getDateInLong()).toInstant(), ZoneId.systemDefault());
+ }
+
+ /** Returns an AppointmentImplLocal object from a task component */
+ public static AppointmentImplLocal getAppointment(TaskComponent taskComponent){
+
+ AppointmentImplLocal appointment = new AppointmentImplLocal();
+ appointment.setSummary(taskComponent.getTaskReference().getName().fullName);
+ appointment.setDescription(taskComponent.getTaskReference().tagsString());
+ appointment.setStartLocalDateTime(getConvertedTime(taskComponent.getStartDate()));
+ appointment.setEndLocalDateTime(getConvertedTime(taskComponent.getEndDate()));
+ if(taskComponent.isArchived()){
+ appointment.setAppointmentGroup(new Agenda.AppointmentGroupImpl().withStyleClass("archive"));
+ }else if(taskComponent.getTaskReference().getName().fullName.equals(BlockCommand.DUMMY_NAME)){
+ appointment.setAppointmentGroup(new Agenda.AppointmentGroupImpl().withStyleClass("block"));
+ }else{
+ appointment.setAppointmentGroup(new Agenda.AppointmentGroupImpl().withStyleClass("normal"));
+ }
+ return appointment;
+
+ }
+
+ public static boolean isSameAppointment(Appointment a, AppointmentImplLocal a2){
+ return a.getAppointmentGroup().getStyleClass().equals(a2.getAppointmentGroup().getStyleClass())
+ && a.getStartLocalDateTime().equals(a2.getStartLocalDateTime())
+ && a.getEndLocalDateTime().equals(a2.getEndLocalDateTime())
+ && a.getDescription().equals(a2.getDescription())
+ && a.getSummary().equals(a2.getSummary());
+ }
+
+}
+```
diff --git a/collated/test/A0147967Jreused.md b/collated/test/A0147967Jreused.md
new file mode 100644
index 000000000000..70e62f48e2b7
--- /dev/null
+++ b/collated/test/A0147967Jreused.md
@@ -0,0 +1,36 @@
+# A0147967Jreused
+###### \java\guitests\AddCommandTest.java
+``` java
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import guitests.guihandles.TaskCardHandle;
+import seedu.address.commons.core.Messages;
+import seedu.address.logic.commands.AddFloatingCommand;
+import seedu.address.logic.commands.AddNonFloatingCommand;
+import seedu.address.model.task.TaskComponent;
+import seedu.address.testutil.TestTask;
+import seedu.address.testutil.TestUtil;
+
+public class AddCommandTest extends TaskMasterGuiTest {
+
+ @Test
+ public void add() {
+ //add one floatingTask
+ TestTask[] currentList = td.getTypicalTasks();
+ TestTask taskToAdd = td.hoon;
+ assertAddSuccess(taskToAdd, currentList);
+ currentList = TestUtil.addTasksToList(currentList, taskToAdd);
+
+ //add another floatingTask
+ taskToAdd = td.ida;
+ assertAddSuccess(taskToAdd, currentList);
+ currentList = TestUtil.addTasksToList(currentList, taskToAdd);
+
+ //add duplicate floatingTask
+ commandBox.runCommand(td.hoon.getAddFloatingCommand());
+ assertResultMessage(AddFloatingCommand.MESSAGE_DUPLICATE_TASK);
+ assertTrue(taskListPanel.isListMatching(TestUtil.convertTasksToDateComponents(currentList)));
+
+```
diff --git a/collated/test/A0147995H.md b/collated/test/A0147995H.md
new file mode 100644
index 000000000000..3d11a7f84ad6
--- /dev/null
+++ b/collated/test/A0147995H.md
@@ -0,0 +1,262 @@
+# A0147995H
+###### \java\seedu\address\logic\LogicManagerTest.java
+``` java
+ /** tests for edit command*/
+ @Test
+ public void execute_edit_invalidTaskData() throws Exception {
+ Task toBeAdded = new Task(new Name("anything"), new UniqueTagList(),
+ new TaskDate("2 oct 2am"), new TaskDate("2 oct 1pm"), RecurringType.NONE);
+
+ TaskMaster expectedAB = new TaskMaster();
+ expectedAB.addTask(toBeAdded);
+ model.addTask(toBeAdded);
+ assertCommandBehavior(
+ "edit 1 []\\[;]", Name.MESSAGE_NAME_CONSTRAINTS, expectedAB, expectedAB.getTaskComponentList());
+ assertCommandBehavior(
+ "edit 1 t/invalid_-[.tag", Tag.MESSAGE_TAG_CONSTRAINTS, expectedAB, expectedAB.getTaskComponentList());
+ }
+
+ @Test
+ public void execute_edit_fromDateIsBehindToDate_notAllowed() throws Exception {
+ // setup expectations
+ Task beforeModification = new Task(new Name("anything"), new UniqueTagList());
+
+ model.addTask(beforeModification);
+ TaskMaster expectedAB = new TaskMaster();
+ expectedAB.addTask(beforeModification);
+
+ // execute command and verify result
+ assertCommandBehavior(
+ "edit 1 from 2 oct 1pm to 2 oct 1am",
+ String.format(EditCommand.MESSAGE_ILLEGAL_TIME_SLOT),
+ expectedAB,
+ expectedAB.getTaskComponentList());
+
+ }
+
+ @Test
+ public void execute_edit_timeSlotOccupied_notAllowed() throws Exception {
+ // setup expectations
+ Task dummyTask = new Task(new Name("BLOCKED SLOT"), new UniqueTagList(),
+ new TaskDate("10 oct 2pm"), new TaskDate("10 oct 5pm"), RecurringType.NONE);
+ Task beforeModification = new Task(new Name("anything"), new UniqueTagList(),
+ new TaskDate("10 oct 10am"), new TaskDate("10 oct 12am"), RecurringType.NONE);
+
+ model.addTask(dummyTask);
+ model.addTask(beforeModification);
+ TaskMaster expectedAB = new TaskMaster();
+ expectedAB.addTask(dummyTask);
+ expectedAB.addTask(beforeModification);
+
+ // execute command and verify result
+ assertCommandBehavior(
+ "edit 2 from 10 oct 1pm to 10 oct 6pm",
+ String.format(EditCommand.MESSAGE_TIMESLOT_OCCUPIED),
+ expectedAB,
+ expectedAB.getTaskComponentList());
+ }
+
+ @Test
+ public void execute_edit_name_for_task_Successful() throws Exception {
+ // setup expectations
+ Task beforeModification = new Task(new Name("anything"), new UniqueTagList());
+ Task afterModification = new Task(new Name("changed"), new UniqueTagList());
+
+ model.addTask(beforeModification);
+ TaskMaster expectedAB = new TaskMaster();
+ expectedAB.addTask(afterModification);
+
+ // execute command and verify result
+ assertCommandBehavior(
+ "edit 1 changed",
+ String.format(EditCommand.MESSAGE_EDIT_TASK_SUCCESS, afterModification),
+ expectedAB,
+ expectedAB.getTaskComponentList());
+ assertUndoRedoAble(String.format(EditCommand.MESSAGE_EDIT_TASK_SUCCESS, afterModification),
+ expectedAB,
+ expectedAB.getTaskComponentList());
+ }
+
+ @Test
+ public void execute_edit_tag_for_taskWithoutTag_Successful() throws Exception {
+ // setup expectations
+ Set tagSet = new HashSet();
+ tagSet.add(new Tag("anytag"));
+ Task beforeModification = new Task(new Name("anything"), new UniqueTagList());
+ Task afterModification = new Task(new Name("anything"), new UniqueTagList(tagSet));
+
+ model.addTask(beforeModification);
+ TaskMaster expectedAB = new TaskMaster();
+ expectedAB.addTask(afterModification);
+
+ // execute command and verify result
+ assertCommandBehavior(
+ "edit 1 t/anytag",
+ String.format(EditCommand.MESSAGE_EDIT_TASK_SUCCESS, afterModification),
+ expectedAB,
+ expectedAB.getTaskComponentList());
+ assertUndoRedoAble(String.format(EditCommand.MESSAGE_EDIT_TASK_SUCCESS, afterModification),
+ expectedAB,
+ expectedAB.getTaskComponentList());
+ }
+
+ @Test
+ public void execute_edit_tag_for_taskWithTag_Successful() throws Exception {
+ // setup expectations
+ Set tagSet = new HashSet();
+ Set newTagSet = new HashSet();
+ tagSet.add(new Tag("anytag"));
+ newTagSet.add(new Tag("anothertag"));
+ Task beforeModification = new Task(new Name("anything"), new UniqueTagList(tagSet));
+ Task afterModification = new Task(new Name("anything"), new UniqueTagList(newTagSet));
+
+ model.addTask(beforeModification);
+ TaskMaster expectedAB = new TaskMaster();
+ expectedAB.getUniqueTagList().add(new Tag("anytag"));
+ expectedAB.addTask(afterModification);
+
+ // execute command and verify result
+ assertCommandBehavior(
+ "edit 1 t/anothertag",
+ String.format(EditCommand.MESSAGE_EDIT_TASK_SUCCESS, afterModification),
+ expectedAB,
+ expectedAB.getTaskComponentList());
+ assertUndoRedoAble(String.format(EditCommand.MESSAGE_EDIT_TASK_SUCCESS, afterModification),
+ expectedAB,
+ expectedAB.getTaskComponentList());
+ }
+
+ @Test
+ public void execute_edit_change_fromDateToDate_for_nonFloatingTask_Successful() throws Exception {
+ // setup expectations
+ Task beforeModification = new Task(new Name("anything"), new UniqueTagList(), new TaskDate("2 oct 3am"), new TaskDate("2 oct 1pm"), RecurringType.NONE);
+ Task afterModification = new Task(new Name("anything"), new UniqueTagList(), new TaskDate("2 oct 2am"), new TaskDate("2 oct 1pm"), RecurringType.NONE);
+
+ model.addTask(beforeModification);
+ TaskMaster expectedAB = new TaskMaster();
+ expectedAB.addTask(afterModification);
+
+ TestDataHelper helper = new TestDataHelper();
+ List expectedComponentList = helper.buildReadOnlyTaskComponentsFromTaskList(expectedAB.getTaskList());
+ // execute command and verify result
+ assertCommandBehavior(
+ "edit 1 from 2 oct 2am to 2 oct 1pm",
+ String.format(EditCommand.MESSAGE_EDIT_TASK_SUCCESS, afterModification),
+ expectedAB,
+ expectedComponentList);
+ assertUndoRedoAble(String.format(EditCommand.MESSAGE_EDIT_TASK_SUCCESS, afterModification),
+ expectedAB,
+ expectedAB.getTaskComponentList());
+ }
+
+ @Test
+ public void execute_edit_change_byDate_for_nonfloatingTask_Successful() throws Exception {
+ // setup expectations
+ Task beforeModification = new Task(new Name("anything"), new UniqueTagList(), new TaskDate(TaskDate.DATE_NOT_PRESENT), new TaskDate("2 oct 2pm"), RecurringType.NONE);
+ Task afterModification = new Task(new Name("anything"), new UniqueTagList(), new TaskDate(TaskDate.DATE_NOT_PRESENT), new TaskDate("2 oct 1pm"), RecurringType.NONE);
+
+ model.addTask(beforeModification);
+ TaskMaster expectedAB = new TaskMaster();
+ expectedAB.addTask(afterModification);
+ TestDataHelper helper = new TestDataHelper();
+ List expectedComponentList = helper.buildReadOnlyTaskComponentsFromTaskList(expectedAB.getTaskList());
+ // execute command and verify result
+ assertCommandBehavior(
+ "edit 1 by 2 oct 1pm",
+ String.format(EditCommand.MESSAGE_EDIT_TASK_SUCCESS, afterModification),
+ expectedAB,
+ expectedComponentList);
+ assertUndoRedoAble(String.format(EditCommand.MESSAGE_EDIT_TASK_SUCCESS, afterModification),
+ expectedAB,
+ expectedAB.getTaskComponentList());
+ }
+
+ @Test
+ public void execute_edit_switch_between_byDate_and_fromDateToDate_for_nonFloatingTask_Successful() throws Exception {
+ // setup expectations
+ Task beforeModification = new Task(new Name("anything"), new UniqueTagList(), new TaskDate("2 oct 4am"), new TaskDate("2 oct 1pm"), RecurringType.NONE);
+ Task afterModification = new Task(new Name("anything"), new UniqueTagList(), new TaskDate(TaskDate.DATE_NOT_PRESENT), new TaskDate("2 oct 1pm"), RecurringType.NONE);
+
+ model.addTask(beforeModification);
+ TaskMaster expectedAB = new TaskMaster();
+ expectedAB.addTask(afterModification);
+ TestDataHelper helper = new TestDataHelper();
+ List expectedComponentList = helper.buildReadOnlyTaskComponentsFromTaskList(expectedAB.getTaskList());
+ // execute command and verify result
+ assertCommandBehavior(
+ "edit 1 by 2 oct 1pm",
+ String.format(EditCommand.MESSAGE_EDIT_TASK_SUCCESS, afterModification),
+ expectedAB,
+ expectedComponentList);
+ assertUndoRedoAble(String.format(EditCommand.MESSAGE_EDIT_TASK_SUCCESS, afterModification),
+ expectedAB,
+ expectedAB.getTaskComponentList());
+ }
+
+ @Test
+ public void execute_edit_add_fromDateToDate_for_floatingTask_Successful() throws Exception {
+ // setup expectations
+ Task beforeModification = new Task(new Name("anything"), new UniqueTagList());
+ Task afterModification = new Task(new Name("anything"), new UniqueTagList(), new TaskDate("2 oct 2am"), new TaskDate("2 oct 1pm"), RecurringType.NONE);
+
+ model.addTask(beforeModification);
+ TaskMaster expectedAB = new TaskMaster();
+ expectedAB.addTask(afterModification);
+
+ TestDataHelper helper = new TestDataHelper();
+ List expectedComponentList = helper.buildReadOnlyTaskComponentsFromTaskList(expectedAB.getTaskList());
+ // execute command and verify result
+ assertCommandBehavior(
+ "edit 1 from 2 oct 2am to 2 oct 1pm",
+ String.format(EditCommand.MESSAGE_EDIT_TASK_SUCCESS, afterModification),
+ expectedAB,
+ expectedComponentList);
+ assertUndoRedoAble(String.format(EditCommand.MESSAGE_EDIT_TASK_SUCCESS, afterModification),
+ expectedAB,
+ expectedAB.getTaskComponentList());
+ }
+
+ @Test
+ public void execute_edit_add_byDate_for_floatingTask_Successful() throws Exception {
+ // setup expectations
+ Task beforeModification = new Task(new Name("anything"), new UniqueTagList());
+ Task afterModification = new Task(new Name("anything"), new UniqueTagList(), new TaskDate(TaskDate.DATE_NOT_PRESENT), new TaskDate("2 oct 1pm"), RecurringType.NONE);
+
+ model.addTask(beforeModification);
+ TaskMaster expectedAB = new TaskMaster();
+ expectedAB.addTask(afterModification);
+ TestDataHelper helper = new TestDataHelper();
+ List expectedComponentList = helper.buildReadOnlyTaskComponentsFromTaskList(expectedAB.getTaskList());
+ // execute command and verify result
+ assertCommandBehavior(
+ "edit 1 by 2 oct 1pm",
+ String.format(EditCommand.MESSAGE_EDIT_TASK_SUCCESS, afterModification),
+ expectedAB,
+ expectedComponentList);
+ assertUndoRedoAble(String.format(EditCommand.MESSAGE_EDIT_TASK_SUCCESS, afterModification),
+ expectedAB,
+ expectedAB.getTaskComponentList());
+ }
+
+ @Test
+ public void execute_edit_set_recurringTask_Successful() throws Exception {
+ // setup expectations
+ Task beforeModification = new Task(new Name("anything"), new UniqueTagList());
+ Task afterModification = new Task(new Name("anything"), new UniqueTagList(), new TaskDate(TaskDate.DATE_NOT_PRESENT), new TaskDate("2 oct 1pm"), RecurringType.DAILY);
+
+ model.addTask(beforeModification);
+ TaskMaster expectedAB = new TaskMaster();
+ expectedAB.addTask(afterModification);
+ TestDataHelper helper = new TestDataHelper();
+ List expectedComponentList = helper.buildReadOnlyTaskComponentsFromTaskList(expectedAB.getTaskList());
+ // execute command and verify result
+ assertCommandBehavior(
+ "edit 1 by 2 oct 1pm daily",
+ String.format(EditCommand.MESSAGE_EDIT_TASK_SUCCESS, afterModification),
+ expectedAB,
+ expectedComponentList);
+ assertUndoRedoAble(String.format(EditCommand.MESSAGE_EDIT_TASK_SUCCESS, afterModification),
+ expectedAB,
+ expectedAB.getTaskComponentList());
+ }
+```
diff --git a/docs/UserGuide.md b/docs/UserGuide.md
index f82f1eac5484..b6b8bb803c5d 100644
--- a/docs/UserGuide.md
+++ b/docs/UserGuide.md
@@ -118,6 +118,8 @@ Format: list
Examples:
* `list`
+
+
#### Edit tasks : `edit`
Format: `edit TASK_ID [NEW_TASK_NAME] [from DATE_TIME to DATE_TIME | by DATE_TIME [daily | weekly | monthly | yearly] ] [tag/EDIT_TAG]...`
@@ -146,6 +148,8 @@ Format: delete TASK_ID
Examples:
* `delete 2`
+
+
#### Archive completed tasks : `done`
Format: done TASK_ID
@@ -198,6 +202,8 @@ Examples:
+
+
#### Find tasks : `find`
Format: `find [KEY_WORD] [from DATE_TIME to DATE_TIME | by DATE_TIME] [t/TAG]...`
@@ -233,6 +239,8 @@ Format: clear
Examples:
* `clear`
+
+
#### Change directory: `cd`
Format: cd FILE_PATH
@@ -243,6 +251,8 @@ Examples:
+
+
#### Exiting the program : `exit`
Exits the program.
Format: `exit`