diff --git a/.classpath b/.classpath
index 3f05f311a90b..0e145b194171 100644
--- a/.classpath
+++ b/.classpath
@@ -17,5 +17,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.project b/.project
index 1c9339c5f927..9c40da49c514 100644
--- a/.project
+++ b/.project
@@ -1,10 +1,15 @@
- addressbook-level4
- Project addressbook-level4 created by Buildship.
+ main
+ Project daily planner created by Praveer, Yufeng, Ming Yi.
+
+ org.eclipse.xtext.ui.shared.xtextBuilder
+
+
+ org.eclipse.jdt.core.javabuilder
@@ -19,5 +24,26 @@
org.eclipse.buildship.core.gradleprojectnatureorg.eclipse.jdt.core.javanature
+ org.eclipse.xtext.ui.shared.xtextNature
+
+
+ 1475758393731
+
+ 26
+
+ org.eclipse.ui.ide.multiFilter
+ 1.0-projectRelativePath-matches-false-false-build
+
+
+
+ 1475758393779
+
+ 26
+
+ org.eclipse.ui.ide.multiFilter
+ 1.0-projectRelativePath-matches-false-false-.gradle
+
+
+
diff --git a/README.md b/README.md
index 249a00b3899c..a325ff19982b 100644
--- a/README.md
+++ b/README.md
@@ -1,22 +1,13 @@
-[![Build Status](https://travis-ci.org/se-edu/addressbook-level4.svg?branch=master)](https://travis-ci.org/se-edu/addressbook-level4)
-[![Coverage Status](https://coveralls.io/repos/github/se-edu/addressbook-level4/badge.svg?branch=master)](https://coveralls.io/github/se-edu/addressbook-level4?branch=master)
+[![Build Status](https://travis-ci.org/CS2103AUG2016-F11-C4/main.svg?branch=master)](https://travis-ci.org/CS2103AUG2016-F11-C4/main)
+[![Coverage Status](https://coveralls.io/repos/github/CS2103AUG2016-F11-C4/main/badge.svg?branch=master)](https://coveralls.io/github/CS2103AUG2016-F11-C4/main?branch=master)
-# Address Book (Level 4)
+# Daily Planner
-
+
-* This is a desktop Address Book application. It has a GUI but most of the user interactions happen using
- a CLI (Command Line Interface).
-* It is a Java sample application intended for students learning Software Engineering while using Java as
- the main programming language.
-* It is **written in OOP fashion**. It provides a **reasonably well-written** code example that is
- **significantly bigger** (around 6 KLoC)than what students usually write in beginner-level SE modules.
-* What's different from [level 3](https://github.com/se-edu/addressbook-level3):
- * A more sophisticated GUI that includes a list panel and an in-built Browser.
- * More test cases, including automated GUI testing.
- * Support for *Build Automation* using Gradle and for *Continuous Integration* using Travis CI.
+* This is a desktop Daily Planner application. It has a GUI but most of the user interactions happen using a CLI (Command Line Interface).
+* It is a To-Do List application that helps the user plan and organise their daily schedule automatically.
-
#### Site Map
* [User Guide](docs/UserGuide.md)
* [Developer Guide](docs/DeveloperGuide.md)
@@ -27,8 +18,8 @@
#### Acknowledgements
-* Some parts of this sample application were inspired by the excellent
- [Java FX tutorial](http://code.makery.ch/library/javafx-8-tutorial/) by *Marco Jakob*.
+The application is based off a sample project created by created by the SE-EDU initiative
+at https://github.com/se-edu/.
#### Licence : [MIT](LICENSE)
diff --git a/build.gradle b/build.gradle
index 46b06c1e42ec..c3510a64be1b 100644
--- a/build.gradle
+++ b/build.gradle
@@ -23,6 +23,10 @@ allprojects {
mavenCentral()
maven { url "https://repo.eclipse.org/content/repositories/egit-releases/" }
maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
+
+ flatDir {
+ dirs 'libs'
+ }
}
// This part is similar to global variables
@@ -52,6 +56,8 @@ allprojects {
compile "com.fasterxml.jackson.core:jackson-databind:$jacksonVersion"
compile "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jacksonDataTypeVersion"
compile "com.google.guava:guava:$guavaVersion"
+
+ compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile "junit:junit:$junitVersion"
testCompile "org.testfx:testfx-core:$testFxVersion"
diff --git a/collated/docs/A0139102U.md b/collated/docs/A0139102U.md
new file mode 100644
index 000000000000..74bbfc1c1f62
--- /dev/null
+++ b/collated/docs/A0139102U.md
@@ -0,0 +1,187 @@
+# A0139102U
+###### \DeveloperGuide.md
+``` md
+## Appendix A : User Stories
+
+Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unlikely to have) - `*`
+
+
+Priority | As a ... | I want to ... | So that I can...
+-------- | :-------- | :--------- | :-----------
+`* * *` | new user | see usage instructions | refer to instructions when I forget how to use the App
+`* * *` | user | add new task | find out tasks are urgently due
+`* * *` | user | set deadlines | be reminded of my due dates for my respective tasks
+`* * *` | user | delete a task | remove entries that are completed
+`* * *` | user | find a task by name | search for a task without having to go through the entire list
+`* * *` | user | edit a task and its particulars | update instantly if there are any changes in the task
+`* *` | user | mark my tasks | know which ones are completed and uncompleted
+`* *` | user | undo my last action | amend my mistakes immediately or put lost information back completely
+`* *` | user | view the tasks | take a quick glance or have my tasks presented according to my needs and preference
+`* *` | busy user | sort my tasks | rank the importance and urgency(time and date) of my activities
+`*` | advanced user | pin my tasks on a new list | remind myself which are the tasks that require my constant attention
+`*` | advanced user | use natural language| specify my dates and time without using a fixed format
+
+## Appendix B : Use Cases
+
+(For all use cases below, the **System** is the `DailyPlanner` and the **Actor** is the `user`, unless specified otherwise)
+
+#### Use case: Add task
+
+**MSS**
+
+1. User requests to add task with minimum input `TASKNAME`
+2. Task Manager adds task and displays the new list of tasks
+Use case ends.
+
+**Extensions**
+
+1a. Name of task missing
+
+>1a1. Daily Planner shows an error message
+ Use case resumes back to step 1
+
+1b. Time slot for task is already filled
+>1b1. Daily Planner warns user that time slot clashes
+ Use case resumes
+
+1c. Format is invalid
+> 3b1. Daily Planner shows an error message
+ Use case resumes
+
+
+#### Use case: Delete task
+
+**MSS**
+
+1. User requests to view tasks for specific time period or completed list
+2. Daily Planner displays all tasks during time period or the completed tasks
+3. User requests to delete a specific task in the list or the clear the completed task list
+4. Daily Planner deletes the task(s)
+Use case ends
+
+**Extensions**
+
+2a. The list is empty
+>Use case ends
+
+3a. Given index is invalid
+>3b1. Task manager shows error message
+ Use case resumes at step 2
+
+
+#### Use case: Completing task
+
+**MSS**
+
+1. User requests to view tasks for specific time period
+2. Daily Planner displays all tasks during time period
+3. User requests to mark a specific task in the list as completed
+4. Daily Planner mark the task as completed
+Use case ends
+
+**Extensions**
+
+2a. The list is empty
+>Use case ends
+
+3a. Given index is invalid
+>3b1. Task manager shows error message
+ Use case resumes at step 2
+
+
+#### Use case: Pin a task
+
+**MSS**
+
+1. User requests to view tasks for specific time period
+2. Task Manager displays all tasks during time period
+3. User requests to pin a specific task in the current schedule list to the pinned list
+4. Task Manager puts the task as on pinned list and displays it there
+Use case ends
+
+**Extensions**
+
+2a. The list is empty
+>Use case ends
+
+3a. Given index is invalid
+>3b1. Task manager shows error message
+ Use case resumes at step 2
+
+
+#### Use case: Unpin a task
+
+**MSS**
+
+1. User refers to the INDEX on the pinned list
+2. User requests to unpin a specific task in the pinned list to the tasks list
+3. Task Manager removes the task from the pinned list
+Use case ends
+
+**Extensions**
+
+2a. The list is empty
+>Use case ends
+
+2a. Given index is invalid
+>3b1. Task manager shows error message
+Use case resumes at step 2
+
+```
+###### \UserGuide.md
+``` md
+### Exiting the program : `exit`
+Exits the program.
+Format: `exit`
+
+
+### Saving the data
+Daily Planner data is saved in the hard disk automatically after any command that changes the data.
+There is no need to save manually.
+
+
+
+
+
+
+
+## Descriptions and Usage
+### Tasks
+The daily planner stores all of the users events as a `Task`.
+
+A `Task` can be viewed, added, searched, edited or deleted with the right command word as can be found in [Commands](#commands).
+
+**Every `Task` must consist of a mandatory `TASKNAME` field and optional `START`, `END`, `CATEGORY` fields.**
+
+#### `START` and `END` Field
+The `START` and `END` field, if specified, tells the Daily Planner when a task should start and end.
+
+Both `START` and `END` fields consist of either a `DATE` field, a `TIME` field or both `DATE` `TIME` fields, separated by a whitespace.
+
+This allows for natural descriptors of starting and ending times.
+
+The `TIME` field must be given in 12hr am or pm format.
+
+The following are all valid `START` and `END` fields:
+```
+
+11/11/2016 5pm
+The 31st of April in the year 2008
+Fri, 21 Nov 1997
+Jan 21, '97
+Sun, Nov 21 5am
+jan 1st 9pm
+february twenty-eighth
+next thursday
+last wednesday
+today
+tomorrow 6pm
+yesterday 3pm
+next week
+next month
+next year
+3 days from now
+three weeks ago
+```
+
+```
diff --git a/collated/docs/A0140124B.md b/collated/docs/A0140124B.md
new file mode 100644
index 000000000000..73d7162a97bf
--- /dev/null
+++ b/collated/docs/A0140124B.md
@@ -0,0 +1,148 @@
+# A0140124B
+###### \DeveloperGuide.md
+``` md
+#### Use case: View task
+
+**MSS**
+
+1. User requests to view tasks for specific time period or completed tasks
+2. Task Manager displays all tasks during time period or tasks that are completed
+
+Use case ends
+
+
+#### Use case: Edit task
+
+**MSS**
+
+1. User requests to list tasks for a specific time period
+2. Task Manager displays all tasks during time period
+3. User requests to edit a specific task in the list
+4. Task manager makes the edits
+Use case ends.
+
+
+**Extensions**
+
+2a. The list is empty
+
+> Use case ends
+
+3a. The given index is invalid
+
+> 3a1. Task Manager shows an error message
+> Use case resumes at step 2
+
+3b. Format is invalid
+> 3b1. Task Manager shows an error message
+> Use case resumes at step 2
+
+
+
+
+
+
+```
+###### \UserGuide.md
+``` md
+### Editing a task: `edit`
+
+Edits a particular task's details
+Format:
+```
+edit [INDEX] [NEWTASKNAME](optional) [NEWSTART](optional) [NEWEND](optional) [NEWCATEGORY](optional)
+```
+Examples:
+`edit 2 tomorrow`
+`s/wednesday 4pm e/6pm` (only changes date and time)
+
+
+### Deleting a task : `delete`
+
+Description: Deletes a task from the planner.
+
+Format: `delete [INDEX]`
+
+> Deletes the task at the specified `INDEX`.
+ The index refers to the index number shown on the list that is currently being viewed
+ The index **must be a positive integer** 1, 2, 3, ...
+
+Examples:
+```delete 5```
+>Deletes task 5 of current list being viewed
+
+### Completing a task : `complete`
+
+Description: Marks a task as completed from the planner.
+
+Format: `complete [INDEX]`
+
+> Marks the task at the specified `INDEX` as completed.
+ The index refers to the index number shown on the list that is currently being viewed
+ The index **must be a positive integer** 1, 2, 3, ...
+
+Examples:
+```complete 5```
+>Task 5 of current list being viewed is marked as completed
+
+### Pin important task on the pinned task list: `pin`
+
+Description: Pins task on the list on the left.
+
+Format: `pin [INDEX]`
+
+> Pins the task at the specified `INDEX`.
+ The index refers to the index number shown on the list that is currently being viewed
+ The index **must be a positive integer** 1, 2, 3, ...
+
+
+Examples:
+```pin 5```
+>Task 5 of current list is being pinned on the pinned list
+
+
+### Remove important task on the pinned task list: `unpin`
+
+Description: Unpins task on the list from the pinned tasks list.
+
+Format: `unpin INDEX`
+
+> Unpins the task at the specified `INDEX`.
+ The index refers to the index number shown on the pinned tasks list(left)
+ The index **must be a positive integer** 1, 2, 3, ...
+
+
+Examples:
+```unpin 5```
+>Task 5 of pinned list is being removed and put back to the schedule list
+
+
+### Undo last command: `undo`
+
+Description: Undo the latest command.
+
+Format: `undo`
+
+>Undo the last command. If the last command was `add`, the task added will be removed if `undo` is invoked
+
+
+Examples:
+`undo`
+
+Undo previous command
+
+
+### Delete every task: `clear`
+
+Description: Clears all tasks.
+
+Format: `clear`
+
+>Clears all task from the list
+
+
+Examples:
+`clear`
+>The list is now empty
+
+```
diff --git a/collated/docs/A0146749N.md b/collated/docs/A0146749N.md
new file mode 100644
index 000000000000..7a7701616dbe
--- /dev/null
+++ b/collated/docs/A0146749N.md
@@ -0,0 +1,165 @@
+# A0146749N
+###### \DeveloperGuide.md
+``` md
+## Appendix C : Non Functional Requirements
+
+1. Should work on any [mainstream OS](#mainstream-os) as long as it has Java `1.8.0_60` or higher installed.
+2. Should be able to hold up to 1000 tasks.
+3. Should come with automated unit tests and open source code.
+4. Should favor DOS style commands over Unix-style commands.
+5. Task input should not be more than 200 characters to be concise.
+
+{More to be added}
+
+## Appendix D : Glossary
+
+##### Mainstream OS
+
+> Windows, Linux, Unix, OS-X
+
+##### Private contact detail
+
+> A contact detail that is not meant to be shared with others
+
+## Appendix E : Product Survey
+Our product survey is researched and presented from our client's point of view - Jim does not use cloud-based platforms and prefers to key in his tasks in a single command line to clicking.
+
+| | Wunderlist | Google Calendar | Todoist |
+|---|---|---|---|
+| Strengths |
Elegant and simple
CLI input but graphical display
Add notes and timestamps to tasks
Ability to create email with list of tasks automatically
Organize tasks in different lists
|
Quick add feature to minimize use of mouse
Ability to allocate tasks to specific timeslots
Ability to view free timeslots
Ability to set reminders
|
CLI input but graphical display
Keyboard shortcuts to minimize use of mouse
Ability to set deadlines and reminders for tasks
Ability to set priorities for tasks to decide which task to do next
|
+| Irrelevant features |
Cross-platform functionality
Syncs between devices
Collaboration with others
|
Collaboration with others
|
Cross-platform functionality
Collaboration with others
|
+| Weaknesses |
No way to allocate tasks to a specific timeslot
No way to view free timeslots
|
Does not provide a desktop software for easy summoning using keyboard shortcuts
No easy way to block multiple timeslots when the exact timing of a task is uncertain and release the blocked slots when the time is finalized
Not good with capturing tasks that need to be done before a specific date/time, or after a specific date/time, and items without specific times
No support for offline access
|
No way to allocate tasks to a specific timeslot
No way to view free timeslots
|
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+###### \UserGuide.md
+``` md
+1. Ensure that you have the latest Java version ‘1.8.0_60’ or later installed in your Computer.
+ > Having any Java 8 version is not enough
+ > The application will not work for any earlier Java versions
+2. Find the project in the `Project Explorer` or `Package Explorer` (usually located at the left side)
+3. Right click on the project
+4. Click `Run As` > `Java Application` and choose the `Main` class. The GUI should appear within split second.
+5. Type the command into the command box and press Enter to execute the command.
+ List of commands:
+ **`help`** : opens instruction
+ **`add`** : adds a task
+ **`show`** : shows all tasks
+ **`find`** : searches for a task
+ **`edit`** : edits a task
+ **`delete`** : deletes a task
+ **`complete`** : marks a task as completed
+ **`pin`** : pin tasks
+ **`unpin`** : removes tasks from the pinned list
+ **`undo`** : reverts the task list back to the previous state
+ **`clear`** : deletes every task on the list
+
+7. Refer to the [Commands](#commands) section below for details of each command.
+
+
+# Features
+
+
+
+## Commands
+### Viewing help : `help`
+Format:
+
+```
+help
+```
+
+> Help is also shown if you enter an incorrect command e.g. `abcd`
+
+### Adding a task : `add`
+
+Adds a task to the planner
+Format:
+```
+1. add [TASKNAME] s/[START](optional) e/[END](optional) c/[CATEGORY]...(optional)
+
+```
+
+Examples:
+
+1. `add travel`
+> Task with no specified timing is added to today's schedule
+
+2. `add meeting s/tomorrow 2pm e/4pm`
+> Fixed task is added from 2pm to 4pm the next day
+
+3. `add math homework e/7 nov 6pm`
+> Task with a deadline is added
+
+
+### Viewing a schedule : `show`
+
+Shows tasks based on query(date or completion)
+
+Format:
+
+```
+show [DATE]/[COMPLETION](optional)
+```
+
+Examples:
+`show`
+>Shows all tasks
+
+`show today`
+>Shows schedule for today
+
+`show complete`
+>Shows completed tasks
+
+`show not complete`
+>Shows uncompleted tasks
+
+`show next wednesday`
+>Shows schedule for next wednesday
+
+
+
+
+The show function will sort the users's schedule and display it.
+
+Tasks are sorted by their urgency(how close it is to its deadline).
+
+The user can then use this recommended schedule to follow the order of the tasks and worryless-ly go about their day.
+
+
+### Searching for a task: `find`
+
+Searches for a particular task and displays more information about it.
+
+Format:
+
+```
+find TASKNAME
+```
+
+Examples:
+
+```
+find cs lecture
+```
+
+> * The search is case insensitive.
+> * The order of the keywords does not matter. e.g. `math assignment` will match `assignment math`
+> * Only the name is searched.
+> * Task matching at least one keyword will be returned (i.e. `OR` search).
+ e.g. `math` will match `math lecture`
+
+```
diff --git a/collated/main/A0139102U.md b/collated/main/A0139102U.md
new file mode 100644
index 000000000000..c1b23dab548d
--- /dev/null
+++ b/collated/main/A0139102U.md
@@ -0,0 +1,796 @@
+# A0139102U
+###### \java\seedu\dailyplanner\commons\util\ArgumentFormatUtil.java
+``` java
+public class ArgumentFormatUtil {
+
+ public static boolean isValidAddArgumentFormat(String trimmedArgs) {
+ if (trimmedArgs.length() != 1 && trimmedArgs.charAt(1) == '/') {
+ return false;
+ }
+ for (int k = 0; k < trimmedArgs.length(); k++) {
+ if (trimmedArgs.charAt(k) == '/') {
+ if (!(k + 1 == trimmedArgs.length())) {
+ if (trimmedArgs.charAt(k + 1) == ' ') {
+ return false;
+ }
+ } else {
+ if (trimmedArgs.charAt(k) == '/')
+ return false;
+ }
+
+ }
+ }
+ return true;
+ }
+
+ public static boolean isValidEditArgumentFormat(String args) {
+ args = args.trim();
+ //If no index, return invalid
+ if(noIndexInArgs(args)) {
+ return false;
+ }
+
+ //Return false if string is only a number
+ if (StringUtils.isNumeric(args)) {
+ return false;
+ }
+
+ //Return false if no space before s/
+ if(args.contains("s/")) {
+ if (ifNoWhiteSpaceBeforeParameter(args, "s/")) {
+ return false;
+ }
+ }
+
+ //Return false if no space before e/
+ if(args.contains("e/")) {
+ if (ifNoWhiteSpaceBeforeParameter(args, "e/")) {
+ return false;
+ }
+ }
+ //Return false if no space before c/
+ if(args.contains("c/")) {
+ if (ifNoWhiteSpaceBeforeParameter(args, "c/")) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static boolean ifNoWhiteSpaceBeforeParameter(String args, String parameter) {
+ return args.charAt(args.indexOf(parameter)-1) != ' ';
+ }
+
+ private static boolean noIndexInArgs(String args) {
+ return !Character.isDigit(args.charAt(0));
+ }
+}
+```
+###### \java\seedu\dailyplanner\commons\util\StringUtil.java
+``` java
+ public static final String EMPTY_STRING = "";
+
+ public static boolean containsIgnoreCase(String source, String query) {
+ String[] split = source.toLowerCase().split("\\s+");
+ List strings = Arrays.asList(split);
+ return strings.stream().filter(s -> s.equals(query.toLowerCase())).count() > 0;
+ }
+
+ /**
+ * Returns a detailed message of the t, including the stack trace.
+ */
+ public static String getDetails(Throwable t) {
+ assert t != null;
+ StringWriter sw = new StringWriter();
+ t.printStackTrace(new PrintWriter(sw));
+ return t.getMessage() + "\n" + sw.toString();
+ }
+
+ /**
+ * Returns true if s represents an unsigned integer e.g. 1, 2, 3, ...
+ * Will return false for null, empty string, "-1", "0", "+1", and " 2 "
+ * (untrimmed) "3 0" (contains whitespace).
+ *
+ * @param s
+ * Should be trimmed.
+ */
+ public static boolean isUnsignedInteger(String s) {
+ return s != null && s.matches("^0*[1-9]\\d*$");
+ }
+
+
+ public static boolean stringContainsAmOrPm(String startString) {
+ return startString.contains("am") || startString.contains("pm");
+ }
+
+ /*
+ * Loops through arguments, adds them to hashmap if valid
+ */
+```
+###### \java\seedu\dailyplanner\history\HistoryManager.java
+``` java
+public class HistoryManager {
+
+ private Stack recordCommand = new Stack();
+
+ public Instruction getLastInstruction() {
+ return recordCommand.pop();
+ }
+
+ public void stackAddInstruction(ReadOnlyTask toPush) {
+ recordCommand.push(new Instruction("A", toPush));
+ }
+
+ public void stackDeleteInstruction(ReadOnlyTask toPush) {
+ recordCommand.push(new Instruction("D", toPush));
+ }
+
+ public void stackEditInstruction(ReadOnlyTask originalTask, ReadOnlyTask editedTask) {
+ recordCommand.push(new Instruction("EA", originalTask));
+ recordCommand.push(new Instruction("ED", editedTask));
+ }
+
+ public void stackUnpinInstruction(ReadOnlyTask taskToUnpin) {
+ recordCommand.push(new Instruction("UP", taskToUnpin));
+ }
+
+ public void stackPinInstruction(ReadOnlyTask taskToUnpin) {
+ recordCommand.push(new Instruction("P", taskToUnpin));
+ }
+
+ public void stackUncompleteInstruction(ReadOnlyTask taskToUncomplete) {
+ recordCommand.push(new Instruction("UC", taskToUncomplete));
+ }
+
+ public void stackCompleteInstruction(ReadOnlyTask taskToComplete) {
+ recordCommand.push(new Instruction("C", taskToComplete));
+ }
+}
+```
+###### \java\seedu\dailyplanner\history\Instruction.java
+``` java
+public class Instruction {
+
+ private String reverseCommand;
+ private ReadOnlyTask task;
+
+ public Instruction(String cmd, ReadOnlyTask task) {
+ this.reverseCommand = cmd;
+ this.task = task;
+ }
+
+ public String getReverse() {
+ return reverseCommand;
+ }
+
+ public void setReverse(String cmd) {
+ this.reverseCommand = cmd;
+ }
+
+ public ReadOnlyTask getTask() {
+ return this.task;
+ }
+}
+```
+###### \java\seedu\dailyplanner\logic\commands\AddCommand.java
+``` java
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a task to the daily planner. "
+ + "Format: add [TASKNAME] s/[STARTDATE] [STARTTIME] e/[ENDDATE] [ENDTIME] c/CATEGORY...\n" + "Example: "
+ + COMMAND_WORD + " CS2103 Assignment s/today 10pm e/11pm c/urgent c/important";
+
+ public static final String MESSAGE_SUCCESS = "New task added: %1$s";
+ public static final String MESSAGE_DUPLICATE_TASK = "This task already exists in the daily planner";
+ public static final String MESSAGE_WARNING_CLASH = "Warning! Current timeslot clashes with the following task: %1$s";
+ private List taskList;
+ private final Task toAdd;
+
+ /**
+ * Convenience constructor using raw values.
+ *
+ * @param endDate
+ *
+ * @throws IllegalValueException
+ * if any of the raw values are invalid
+ */
+
+ public AddCommand(String taskName, DateTime start, DateTime end, Set cats) throws IllegalValueException {
+ final Set catSet = new HashSet<>();
+ for (String catName : cats) {
+ catSet.add(new Category(catName));
+ }
+ this.toAdd = new Task(taskName, start, end, false, false, new UniqueCategoryList(catSet));
+
+ }
+```
+###### \java\seedu\dailyplanner\logic\commands\AddCommand.java
+``` java
+ /**
+ * Returns the index of the task clashing with argument, returns -1 if no
+ * clash
+ */
+ public int getIndexOfClashingTask(Task toCheck) {
+
+ if (!(DateUtil.hasStartandEndTime(toCheck))) {
+ return -1;
+ }
+ Time toAddStartTiming = toCheck.getStart().getTime();
+ Time toAddEndTiming = toCheck.getEnd().getTime();
+
+ for (int i = 0; i < taskList.size(); i++) {
+ ReadOnlyTask storedTask = taskList.get(i);
+ if (DateUtil.hasStartandEndTime(storedTask)) {
+ if (notSameTask(toCheck, storedTask)) {
+ if (isSameStartDate(toCheck, storedTask)) {
+ Time tasksEndTiming = storedTask.getEnd().getTime();
+ Time tasksStartTiming = storedTask.getStart().getTime();
+
+ if (isStartTimeClashing(toAddStartTiming, tasksEndTiming, tasksStartTiming)) {
+ return i;
+ }
+ if (isEndTimeClashing(toAddEndTiming, tasksEndTiming, tasksStartTiming)) {
+ return i;
+ }
+ if (timingSpansEntireTask(toAddStartTiming, toAddEndTiming, tasksEndTiming, tasksStartTiming)) {
+ return i;
+ }
+ }
+ }
+
+ }
+ }
+ return -1;
+ }
+
+ private boolean notSameTask(Task toCheck, ReadOnlyTask storedTask) {
+ return !(toCheck == storedTask);
+ }
+
+ private boolean timingSpansEntireTask(Time toAddStartTiming, Time toAddEndTiming, Time tasksEndTiming,
+ Time tasksStartTiming) {
+ return ((toAddEndTiming.compareTo(tasksEndTiming) > 0) || (toAddEndTiming.compareTo(tasksEndTiming) == 0))
+ && ((toAddStartTiming.compareTo(tasksStartTiming) < 0)
+ || (toAddStartTiming.compareTo(tasksStartTiming) == 0));
+ }
+
+ private boolean isEndTimeClashing(Time toAddEndTiming, Time tasksEndTiming, Time tasksStartTiming) {
+ return (toAddEndTiming.compareTo(tasksStartTiming) > 0) && (toAddEndTiming.compareTo(tasksEndTiming) < 0);
+ }
+
+ private boolean isStartTimeClashing(Time toAddStartTiming, Time tasksEndTiming, Time tasksStartTiming) {
+ return (toAddStartTiming.compareTo(tasksEndTiming) < 0) && (toAddStartTiming.compareTo(tasksStartTiming) > 0);
+ }
+
+ private boolean isSameStartDate(Task toCheck, ReadOnlyTask storedTask) {
+ return toCheck.getStart().getDate().compareTo(storedTask.getStart().getDate()) == 0;
+ }
+
+}
+```
+###### \java\seedu\dailyplanner\logic\commands\ClearCommand.java
+``` java
+ @Override
+ public CommandResult execute() {
+ assert model != null;
+ model.resetData(DailyPlanner.getEmptyDailyPlanner());
+ model.resetPinBoard();
+ model.setLastTaskAddedIndex(0);
+ return new CommandResult(MESSAGE_SUCCESS);
+ }
+}
+```
+###### \java\seedu\dailyplanner\logic\commands\CompleteCommand.java
+``` java
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Marks the task identified by the index number shown in current list\n"
+ + "Format: complete [INDEX] (must be a positive integer)\n" + "Example: " + COMMAND_WORD + " 1";
+
+ public static final String MESSAGE_COMPLETED_TASK_SUCCESS = "Completed Task: %1$s";
+
+ public CompleteCommand(int targetIndex) {
+ this.targetIndex = targetIndex;
+ }
+```
+###### \java\seedu\dailyplanner\logic\commands\DeleteCommand.java
+``` java
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Deletes the task identified by the index number shown in current list\n"
+ + "Format: [INDEX] (must be a positive integer)\n" + "Example: " + COMMAND_WORD + " 1";
+
+ public static final String MESSAGE_DELETE_TASK_SUCCESS = "Deleted Task: %1$s";
+
+ public final int targetIndex;
+
+ public DeleteCommand(int targetIndex) {
+ this.targetIndex = targetIndex;
+ }
+
+ @Override
+ public CommandResult execute() {
+
+ UnmodifiableObservableList lastShownList = model.getFilteredTaskList();
+
+ if (lastShownList.size() < targetIndex) {
+ indicateAttemptToExecuteIncorrectCommand();
+ return new CommandResult(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX);
+ }
+
+ ReadOnlyTask taskToDelete = lastShownList.get(targetIndex - 1);
+
+ try {
+ model.getHistory().stackAddInstruction(taskToDelete);
+ model.deleteTask(taskToDelete);
+ model.updatePinBoard();
+
+ } catch (TaskNotFoundException pnfe) {
+ assert false : "The target task cannot be missing";
+ }
+
+ return new CommandResult(String.format(MESSAGE_DELETE_TASK_SUCCESS, taskToDelete));
+ }
+
+}
+```
+###### \java\seedu\dailyplanner\logic\commands\DeleteCompletedCommand.java
+``` java
+public static final String MESSAGE_USAGE = DeleteCommand.COMMAND_WORD + ": Deletes all completed tasks.\n"
+ + "Format: [INDEX] (must be a positive integer)\n" + "Example: " + DeleteCommand.COMMAND_WORD
+ + " completed";
+
+ public static final String MESSAGE_DELETE_TASK_SUCCESS = "Deleted Task: %1$s";
+
+ public DeleteCompletedCommand() {
+ }
+```
+###### \java\seedu\dailyplanner\logic\commands\DeleteCompletedCommand.java
+``` java
+ @Override
+ public CommandResult execute() {
+
+ final Set keywordSet = new HashSet<>(Arrays.asList(new String[] { "complete" }));
+ model.updateFilteredTaskListByCompletion(keywordSet);
+ UnmodifiableObservableList completedList = model.getFilteredTaskList();
+
+ int size = completedList.size();
+ for (int i = 0; i < size; i++) {
+ ReadOnlyTask taskToDelete = completedList.get(0);
+
+ try {
+ model.getHistory().stackAddInstruction(taskToDelete);
+ model.deleteTask(taskToDelete);
+ model.updatePinBoard();
+
+ } catch (TaskNotFoundException pnfe) {
+ assert false : "The target task cannot be missing";
+ }
+ }
+
+ model.updateFilteredListToShowAll();
+ model.setLastShowDate(StringUtil.EMPTY_STRING);
+ return new CommandResult(String.format(MESSAGE_DELETE_TASK_SUCCESS, "all completed"));
+ }
+}
+```
+###### \java\seedu\dailyplanner\logic\commands\EditCommand.java
+``` java
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Edits the task identified by the index number shown in current list.\n"
+ + "Format: edit [INDEX] (must be a positive integer)[TASKNAME] s/[STARTDATE] [STARTTIME] e/[ENDDATE] [ENDTIME]\n"
+ + "Example: " + COMMAND_WORD + " 2 s/3pm";
+
+ public static final String MESSAGE_DUPLICATE_TASK = "This task already exists in the daily planner";
+ public static final String MESSAGE_EDIT_TASK_SUCCESS = "Edited Task: %1$s";
+
+ public final int targetIndex;
+ private final Optional taskName;
+ private final Optional start;
+ private final Optional end;
+ private Optional categoriesSet;
+
+ public EditCommand(int targetIndex, String taskName, DateTime start, DateTime end, Set cats)
+ throws IllegalValueException {
+ this.targetIndex = targetIndex;
+ this.taskName = Optional.ofNullable(taskName);
+ this.start = Optional.ofNullable(start);
+ this.end = Optional.ofNullable(end);
+ this.categoriesSet = Optional.empty();
+
+ if (cats.size() != 0) {
+ final Set catSet = new HashSet<>();
+
+ for (String catName : cats) {
+ catSet.add(new Category(catName));
+ }
+ this.categoriesSet = Optional.of(new UniqueCategoryList(catSet));
+ }
+ }
+
+```
+###### \java\seedu\dailyplanner\logic\commands\FindCommand.java
+``` java
+ 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"
+ + "Format: KEYWORD [MORE_KEYWORDS]...\n" + "Example: " + COMMAND_WORD + " homework lecture";
+
+ private final Set keywords;
+
+ public FindCommand(Set keywords) {
+ this.keywords = keywords;
+ }
+
+ @Override
+ public CommandResult execute() {
+ model.updateFilteredTaskList(keywords);
+ return new CommandResult(getMessageForTaskListShownSummary(model.getFilteredTaskList().size()));
+ }
+
+}
+```
+###### \java\seedu\dailyplanner\logic\commands\PinCommand.java
+``` java
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Pins the task identified by the index number used in the last task listing on the pin board.\n"
+ + "Format: pin [INDEX] (must be a positive integer)\n" + "Example: " + COMMAND_WORD + " 1";
+
+ public static final String MESSAGE_PINNED_TASK_SUCCESS = "Pinned Task: %1$s";
+ public static final String MESSAGE_DUPLICATE_PINNED_TASK = "Task is already pinned.";
+```
+###### \java\seedu\dailyplanner\logic\commands\SelectCommand.java
+``` java
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Selects the task identified by the index number used in the last task listing.\n"
+ + "Parameters: INDEX (must be a positive integer)\n"
+ + "Example: " + COMMAND_WORD + " 1";
+
+ public static final String MESSAGE_SELECT_TASK_SUCCESS = "Selected Task: %1$s";
+
+ public SelectCommand(int targetIndex) {
+ this.targetIndex = targetIndex;
+ }
+```
+###### \java\seedu\dailyplanner\logic\commands\UncompleteCommand.java
+``` java
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Unmarks a completed task identified by the index number used in the last task listing as incomplete.\n"
+ + "Format: uncomplete [INDEX] (must be a positive integer)\n"
+ + "Example: " + COMMAND_WORD + " 1";
+
+ public static final String MESSAGE_UNCOMPLETED_TASK_SUCCESS = "Uncompleted Task: %1$s";
+
+ public UncompleteCommand(int targetIndex) {
+ this.targetIndex = targetIndex;
+ }
+
+```
+###### \java\seedu\dailyplanner\logic\commands\UndoCommand.java
+``` java
+ @Override
+ public CommandResult execute() {
+ Instruction undoInstruction = model.getHistory().getLastInstruction();
+ ReadOnlyTask taskToUndo = null;
+
+ taskToUndo = undoInstruction.getTask();
+
+ if (undoInstruction.getReverse().equals("A")) {
+ try {
+ model.addTask((Task) taskToUndo);
+ } catch (IllegalValueException e) {
+ e.printStackTrace();
+ }
+ }
+
+ if (undoInstruction.getReverse().equals("D")) {
+ try {
+ model.deleteTask(taskToUndo);
+ } catch (TaskNotFoundException e) {
+ e.printStackTrace();
+ }
+ }
+
+ if (undoInstruction.getReverse().equals("ED")) {
+ try {
+ model.deleteTask(taskToUndo);
+
+ } catch (TaskNotFoundException e) {
+ e.printStackTrace();
+ }
+
+ // Get next instruction from stack to generate and add the old task
+ // back
+ // The instruction is guaranteed to be an "EA" instruction
+ undoInstruction = model.getHistory().getLastInstruction();
+ taskToUndo = null;
+
+ taskToUndo = undoInstruction.getTask();
+
+ try {
+ model.addTask((Task) taskToUndo);
+ } catch (DuplicateTaskException e) {
+ e.printStackTrace();
+ }
+ }
+
+ if (undoInstruction.getReverse().equals("UP")) {
+ int indexInPinBoard = model.getPinnedTaskList().indexOf(taskToUndo);
+ try {
+ model.unpinTask(indexInPinBoard);
+ } catch (TaskNotFoundException e) {
+ e.printStackTrace();
+ }
+ }
+
+ if (undoInstruction.getReverse().equals("P")) {
+ try {
+ model.pinTask(taskToUndo);
+ } catch (TaskNotFoundException e) {
+ e.printStackTrace();
+ }
+ }
+
+ if (undoInstruction.getReverse().equals("UC")) {
+ int indexInTaskList = model.getFilteredTaskList().indexOf(taskToUndo);
+ model.uncompleteTask(indexInTaskList);
+ }
+
+ if (undoInstruction.getReverse().equals("C")) {
+ try {
+ model.markTaskAsComplete(taskToUndo);
+ } catch (TaskNotFoundException e) {
+ e.printStackTrace();
+ }
+ }
+
+ model.updatePinBoard();
+
+ return new CommandResult(String.format(MESSAGE_SUCCESS));
+
+ }
+
+}
+```
+###### \java\seedu\dailyplanner\logic\parser\Parser.java
+``` java
+ case CompleteCommand.COMMAND_WORD:
+ return prepareComplete(arguments);
+
+ case UncompleteCommand.COMMAND_WORD:
+ return prepareUncomplete(arguments);
+
+ case UndoCommand.COMMAND_WORD:
+ return new UndoCommand();
+
+ case PinCommand.COMMAND_WORD:
+ return preparePin(arguments);
+
+ case UnpinCommand.COMMAND_WORD:
+ return prepareUnpin(arguments);
+
+ case ShowCommand.COMMAND_WORD:
+ if (arguments.equals(""))
+ return new ShowCommand();
+ else
+ return prepareShow(arguments);
+
+ default:
+ return new IncorrectCommand(MESSAGE_UNKNOWN_COMMAND);
+ }
+ }
+
+```
+###### \java\seedu\dailyplanner\logic\parser\Parser.java
+``` java
+ /**
+ * Parses arguments in the context of the edit task command.
+ *
+ * @param args
+ * full command args string
+ * @return the prepared command
+ */
+ private Command prepareEdit(String arguments) {
+
+ int index = 0;
+ String taskName = null;
+ DateTime formattedStart = null, formattedEnd = null;
+ Set categories = new HashSet();
+
+ String trimmedArgs = arguments.trim();
+
+ if (!(ArgumentFormatUtil.isValidEditArgumentFormat(arguments))) {
+ return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE));
+ }
+
+ HashMap mapArgs = parseEdit(trimmedArgs);
+
+ // If arguments are in hashmap, pass them to editCommand, if not pass
+ // them as empty string
+ // Change date to "dd/mm/yy/", time to "hh:mm"
+
+ nattyParser natty = new nattyParser();
+
+ if (mapArgs.containsKey("index")) {
+ index = Integer.parseInt(mapArgs.get("index"));
+ }
+ if (mapArgs.containsKey("taskName")) {
+ taskName = mapArgs.get("taskName");
+ }
+ if (mapArgs.containsKey("start")) {
+ formattedStart = extractStart(mapArgs, natty);
+ }
+ if (mapArgs.containsKey("end")) {
+ formattedEnd = extractEnd(formattedStart, mapArgs, natty);
+ }
+ if (mapArgs.containsKey("cats")) {
+ categories = extractCategories(mapArgs);
+ }
+
+ try {
+ return new EditCommand(index, taskName, formattedStart, formattedEnd, categories);
+ } catch (IllegalValueException ive) {
+ return new IncorrectCommand(ive.getMessage());
+ }
+ }
+
+ /**
+ * Parses arguments in the context of the add task command.
+ *
+ * @param args
+ * full command args string
+ * @return the prepared command
+ */
+
+```
+###### \java\seedu\dailyplanner\logic\parser\Parser.java
+``` java
+ /**
+ * Parses arguments in the context of the select task command.
+ *
+ * @param args
+ * full command args string
+ * @return the prepared command
+ */
+ private Command prepareSelect(String args) {
+ String trimmedArg = args.trim();
+ Optional index = parseIndex(trimmedArg);
+ if (!index.isPresent()) {
+ return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, SelectCommand.MESSAGE_USAGE));
+ }
+ return new SelectCommand(index.get());
+ }
+
+ /**
+ * Returns the specified index in the {@code command} IF a positive unsigned
+ * integer is given as the index. Returns an {@code Optional.empty()}
+ * otherwise.
+ */
+ private Optional parseIndex(String command) {
+ final Matcher matcher = TASK_INDEX_ARGS_FORMAT.matcher(command.trim());
+ if (!matcher.matches()) {
+ return Optional.empty();
+ }
+
+ String index = matcher.group("targetIndex");
+ if (!StringUtil.isUnsignedInteger(index)) {
+ return Optional.empty();
+ }
+ return Optional.of(Integer.parseInt(index));
+
+ }
+
+ /**
+ * 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) {
+ String trimmedArg = args.trim();
+ final Matcher matcher = KEYWORDS_ARGS_FORMAT.matcher(trimmedArg);
+ if (!matcher.matches()) {
+ return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE));
+ }
+
+ // keywords delimited by whitespace
+ final String[] keywords = matcher.group("keywords").split("\\s+");
+ final Set keywordSet = new HashSet<>(Arrays.asList(keywords));
+ return new FindCommand(keywordSet);
+ }
+
+}
+```
+###### \java\seedu\dailyplanner\model\Model.java
+``` java
+ /** Marks the given task as complete */
+ void markTaskAsComplete(ReadOnlyTask taskToComplete) throws TaskNotFoundException;
+
+ /** Unmarks the given task as incomplete */
+ void markTaskAsIncomplete(ReadOnlyTask taskToIncomplete) throws TaskNotFoundException;
+
+ /** Pins the given task. */
+ void pinTask(ReadOnlyTask taskToPin) throws TaskNotFoundException;
+
+ /** Unpins the given task. */
+ void unpinTask(int i) throws TaskNotFoundException;
+
+ /** Returns the filtered task list as an {@code UnmodifiableObservableList} */
+ UnmodifiableObservableList getFilteredTaskList();
+
+ /** Returns the list of pinned task as an {@code UnmodifiableObservableList} */
+ UnmodifiableObservableList getPinnedTaskList();
+
+ /** Updates the filter of the filtered task list to show all tasks */
+ void updateFilteredListToShowAll();
+
+ /** Updates the filter of the filtered task list to filter by the given keywords*/
+ void updateFilteredTaskList(Set keywords);
+
+ /** Updates the filter of the filtered task list to filter by the given date*/
+ void updateFilteredTaskListByDate(Set keywords);
+
+ /** Updates the filter of the filtered task list to show only completed tasks*/
+ void updateFilteredTaskListByCompletion(Set keywords);
+
+ /** Returns the index of the last task that was added to the task list */
+ public int getLastTaskAddedIndex();
+
+ /** Sets the stored index of the last task added */
+ public void setLastTaskAddedIndex(int index);
+
+ /** Returns the last task added index as the property itself */
+ public IntegerProperty getLastTaskAddedIndexProperty();
+
+ /** Returns last shown date command */
+ public String getLastShowDate();
+
+ /** Sets last shown date given by show command, this date is dislayed in green beside 'Your Tasks' in GUI*/
+ public void setLastShowDate(String showInput);
+
+ /** Returns the StringProperty holding the last shown date command */
+ public StringProperty getLastShowDateProperty();
+
+ /** Resets the pinboard to an empty pinboard */
+ public void resetPinBoard();
+
+ /** Uncompletes task with given index in taskList */
+ public void uncompleteTask(int indexInTaskList);
+
+ /** Refreshes the pin board after a command is carried out */
+ public void updatePinBoard();
+
+}
+```
+###### \java\seedu\dailyplanner\model\ModelManager.java
+``` java
+ public synchronized void markTaskAsComplete(ReadOnlyTask taskToComplete) throws TaskNotFoundException {
+ dailyPlanner.markTaskAsComplete(taskToComplete);
+ setLastTaskAddedIndex(dailyPlanner.indexOf((Task) taskToComplete));
+ indicateDailyPlannerChanged();
+ }
+
+ public synchronized void markTaskAsIncomplete(ReadOnlyTask taskToIncomplete) throws TaskNotFoundException {
+ int targetIndex = dailyPlanner.indexOf((Task) taskToIncomplete);
+ uncompleteTask(targetIndex);
+ setLastTaskAddedIndex(targetIndex);
+ indicateDailyPlannerChanged();
+ }
+
+```
+###### \java\seedu\dailyplanner\ui\HelpWindow.java
+``` java
+ private void configure(){
+ 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);
+
+ WebView browser = new WebView();
+ browser.getEngine().loadContent("
Help
add
add [TASKNAME] s/[START] e/[END] c/[CATEGORY]..
Except TASKNAME, all the fields above are optional
Examples:
add Math Assignment s/today
add Music Lesson s/12 nov 2pm e/5pm
add Sleepover s/today 9pm e/tomorrow 9am
add CS1020 Revision
delete
delete [INDEX] or delete[COMPLETED]
Examples:
delete 1
delete completed (deletes all completed tasks)
edit
edit [INDEX] [TASKNAME] s/[START] e/[END]
Except INDEX, only one of the other fields has to be entered.
Examples:
edit 1 Math Assignment
edit 5 Music Lesson s/6pm e/7pm
edit 9 Freshmen Camp s/18Dec 1pm e/17112016 9am
edit 11 CS1020 Revision s/tomorrow
find
find [TASKNAME]
Examples:
find CS1020
complete
complete[INDEX]
Examples:
complete 5
show
show [DATE]/[COMPLETED]
Examples:
show complete
show (shows all tasks)
show today
show not complete
undo
undo
Examples:
undo
pin
pin[INDEX]
Examples:
pin 5 (pins a task to the pin board)
" );
+ FxViewUtil.applyAnchorBoundaryParameters(browser, 0.0, 0.0, 0.0, 0.0);
+ mainPane.getChildren().add(browser);
+ }
+
+ public void show() {
+ dialogStage.showAndWait();
+ }
+}
+```
diff --git a/collated/main/A0140124B.md b/collated/main/A0140124B.md
new file mode 100644
index 000000000000..32d2b15e435a
--- /dev/null
+++ b/collated/main/A0140124B.md
@@ -0,0 +1,894 @@
+# A0140124B
+###### \java\seedu\dailyplanner\commons\util\StringUtil.java
+``` java
+ public static void argumentArrayToHashMap(HashMap mapArgs, String[] splitArgs) {
+ for (int i = 0; i < splitArgs.length; i++) {
+ if (splitArgs[i].substring(0, 2).equals("s/")) {
+ extractArgument(mapArgs, splitArgs, i, "start");
+ }
+
+ if (splitArgs[i].substring(0, 2).equals("e/")) {
+ extractArgument(mapArgs, splitArgs, i, "end");
+ }
+
+ if (splitArgs[i].substring(0, 2).equals("c/")) {
+ extractArgument(mapArgs, splitArgs, i, "cats");
+
+ }
+ }
+ }
+
+ private static void extractArgument(HashMap mapArgs, String[] splitArgs, int i, String type) {
+ int j = i + 1;
+ String arg = splitArgs[i].substring(2);
+ while (j < splitArgs.length && !splitArgs[j].contains("/")) {
+ arg += " " + splitArgs[j];
+ j++;
+ }
+ i = j;
+ mapArgs.put(type, arg);
+ }
+ }
+
+```
+###### \java\seedu\dailyplanner\logic\commands\AddCommand.java
+``` java
+ @Override
+ public CommandResult execute() {
+ assert model != null;
+ try {
+ taskList = model.getDailyPlanner().getTaskList();
+ model.getHistory().stackDeleteInstruction(toAdd);
+ model.addTask(toAdd);
+ model.updatePinBoard();
+
+ int indexOfClashingTask = getIndexOfClashingTask(toAdd);
+
+ if (isClash(indexOfClashingTask))
+ return new CommandResult(
+ String.format(MESSAGE_WARNING_CLASH, taskList.get(indexOfClashingTask)));
+
+ return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd));
+ } catch (UniqueTaskList.DuplicateTaskException e) {
+ return new CommandResult(MESSAGE_DUPLICATE_TASK);
+ }
+
+```
+###### \java\seedu\dailyplanner\logic\parser\nattyParser.java
+``` java
+ private com.joestelmach.natty.Parser nattyParserPackage;
+
+ public nattyParser() {
+ nattyParserPackage = new com.joestelmach.natty.Parser();
+ }
+
+ public String parse(String dateAndTime) {
+ Date parsedDateAndTime = getDateObjectFromNatty(dateAndTime);
+ DateFormat df = new SimpleDateFormat("dd/MM/yyyy hh.mma");
+ return df.format(parsedDateAndTime);
+ }
+
+ public String parseDate(String date) {
+
+ if (DateUtil.isValidDayMonthAnd4DigitYearFormat(date)) {
+ return getDDMMYYYYFormat(date);
+ }
+
+ if (DateUtil.isValidDayMonthAnd2DigitYearFormat(date)) {
+ return convertFrom2DigitYearto4DigitYearFormat(date);
+ }
+
+ Date parsedDate = getDateObjectFromNatty(date);
+ DateFormat df = new SimpleDateFormat("dd/MM/yyyy");
+ return df.format(parsedDate);
+ }
+
+ public String parseTime(String time) {
+ Date parsedTime = getDateObjectFromNatty(time);
+ DateFormat df = new SimpleDateFormat("hh.mma");
+ return df.format(parsedTime);
+ }
+
+ private Date getDateObjectFromNatty(String dateAndTime) {
+ List groups = nattyParserPackage.parse(dateAndTime);
+ Date parsedDateAndTime = new Date();
+ for (DateGroup group : groups) {
+ parsedDateAndTime = group.getDates().get(0);
+ break;
+ }
+ return parsedDateAndTime;
+ }
+
+ private String convertFrom2DigitYearto4DigitYearFormat(String date) {
+ String dateWithAddedZero;
+ if (date.charAt(2) != '/') {
+ dateWithAddedZero = "0" + date;
+ } else {
+ dateWithAddedZero = date;
+ }
+ dateWithAddedZero = DateUtil.convertTo4DigitYearFormat(dateWithAddedZero);
+
+ return dateWithAddedZero;
+ }
+
+ private String getDDMMYYYYFormat(String date) {
+ if (date.charAt(2) != '/') {
+ return "0" + date;
+ } else {
+ return date;
+ }
+ }
+
+}
+```
+###### \java\seedu\dailyplanner\logic\parser\Parser.java
+``` java
+ private Command prepareAdd(String args) {
+ String taskName = "";
+ DateTime formattedStart = DateUtil.getEmptyDateTime();
+ DateTime formattedEnd = DateUtil.getEmptyDateTime();
+ Set cats = new HashSet();
+
+ String trimmedArgs = args.trim();
+
+ if (!(ArgumentFormatUtil.isValidAddArgumentFormat(trimmedArgs))) {
+ return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE));
+ }
+
+ HashMap mapArgs = parseAdd(trimmedArgs);
+ nattyParser natty = new nattyParser();
+
+ if (mapArgs.containsKey("taskName")) {
+ taskName = mapArgs.get("taskName");
+ }
+ if (mapArgs.containsKey("start")) {
+ formattedStart = extractStart(mapArgs, natty);
+ }
+ if (mapArgs.containsKey("end")) {
+ formattedEnd = extractEnd(formattedStart, mapArgs, natty);
+ }
+ if (mapArgs.containsKey("cats")) {
+ cats = extractCategories(mapArgs);
+ }
+ try {
+ return new AddCommand(taskName, formattedStart, formattedEnd, cats);
+ } catch (IllegalValueException ive) {
+ return new IncorrectCommand(ive.getMessage());
+ }
+
+ }
+
+ private boolean hasEndDate(String endString) {
+ return endString.length() >= 7 && !Character.isDigit(endString.charAt(0));
+ }
+
+ private DateTime extractStart(HashMap mapArgs, nattyParser natty) {
+ DateTime formattedStart;
+ String startString = mapArgs.get("start");
+ // if field is empty, return empty DateTime
+ if (startString.equals("")) {
+ formattedStart = DateUtil.getEmptyDateTime();
+ }
+ // if start time is given
+ if (StringUtil.stringContainsAmOrPm(startString)) {
+ String start = natty.parse(startString);
+ formattedStart = DateUtil.getDateTimeFromString(start);
+ } else {
+ String start = natty.parseDate(startString);
+ Date startDate = new Date(start);
+ formattedStart = new DateTime(startDate, new Time(""));
+ }
+ return formattedStart;
+ }
+
+ private DateTime extractEnd(DateTime formattedStart, HashMap mapArgs, nattyParser natty) {
+ DateTime formattedEnd;
+ String endString = mapArgs.get("end");
+ // if field is empty, return empty DateTime
+ if (endString.equals("")) {
+ formattedEnd = DateUtil.getEmptyDateTime();
+ }
+ // if end time is given
+ if (StringUtil.stringContainsAmOrPm(endString)) {
+ // if end date is given
+ if (hasEndDate(endString)) {
+ String end = natty.parse(endString);
+ formattedEnd = DateUtil.getDateTimeFromString(end);
+ } else {
+ Date endDate;
+ // if no start date, infer end date as today
+ if (!mapArgs.containsKey("start")) {
+ endDate = DateUtil.todayAsDate();
+ }
+ // if start date present, infer end date as start date
+ else {
+ endDate = formattedStart.getDate();
+ }
+ Time endTime = new Time(natty.parseTime(endString));
+ formattedEnd = new DateTime(endDate, endTime);
+ }
+ } else {
+ String end = natty.parseDate(endString);
+ Date endDate = new Date(end);
+ formattedEnd = new DateTime(endDate, new Time(""));
+ }
+ return formattedEnd;
+ }
+
+ private Set extractCategories(HashMap mapArgs) {
+ Set cats;
+ if (mapArgs.get("cats").equals("")) {
+ cats = new HashSet();
+ }
+ String[] catArray = mapArgs.get("cats").split(" ");
+ cats = new HashSet(Arrays.asList(catArray));
+ return cats;
+ }
+
+ /**
+ * Parses the arguments given by the user in the add command and returns it
+ * to prepareAdd in a HashMap with keys taskName, date, startTime, endTime,
+ */
+
+ private HashMap parseAdd(String arguments) {
+ HashMap mapArgs = new HashMap();
+ String taskName = getTaskNameFromArguments(arguments);
+ mapArgs.put("taskName", taskName);
+ if (arguments.contains("/")) {
+ String[] splitArgs = arguments.substring(taskName.length() + 1).split(" ");
+ // loop through rest of arguments, add them to hashmap if valid
+
+ StringUtil.argumentArrayToHashMap(mapArgs, splitArgs);
+ }
+
+ return mapArgs;
+ }
+
+ /**
+ * Parses the arguments given by the user in the add command and returns it
+ * to prepareEdit in a HashMap with keys taskName, date, startTime, endTime,
+ */
+ private HashMap parseEdit(String arguments) {
+
+ HashMap mapArgs = new HashMap();
+
+ // Extract index
+ String[] splitArgs1 = arguments.split(" ", 2);
+ int indexStringLength = splitArgs1[0].length();
+ String index = arguments.substring(0, indexStringLength);
+ mapArgs.put("index", index);
+
+ arguments = arguments.substring(indexStringLength + 1);
+ if (hasTaskName(arguments)) {
+ String taskName = getTaskNameFromArguments(arguments);
+ mapArgs.put("taskName", taskName);
+ if (arguments.contains("/")) {
+ String[] splitArgs = arguments.substring(taskName.length() + 1).split(" ");
+ StringUtil.argumentArrayToHashMap(mapArgs, splitArgs);
+ }
+ } else if (arguments.contains("/")) {
+ String[] splitArgs = arguments.split(" ");
+ StringUtil.argumentArrayToHashMap(mapArgs, splitArgs);
+ }
+
+ return mapArgs;
+ }
+
+
+ /**
+ * Checks if argument given contains a task name
+ */
+ private boolean hasTaskName(String arguments) {
+ String trimmedArgs = arguments.trim();
+ // if first parameter is a start, end or category field
+ if (trimmedArgs.length() >= 2 && trimmedArgs.charAt(1) == '/') {
+ return false;
+ }
+ return true;
+ }
+```
+###### \java\seedu\dailyplanner\model\ModelManager.java
+``` java
+ public ModelManager(DailyPlanner src, UserPrefs userPrefs) {
+ super();
+ assert src != null;
+ assert userPrefs != null;
+
+ logger.fine("Initializing with daily planner: " + src + " and user prefs " + userPrefs);
+
+ dailyPlanner = new DailyPlanner(src);
+ filteredTasks = new FilteredList<>(dailyPlanner.getTasks());
+ pinnedTasks = new FilteredList<>(dailyPlanner.getPinnedTasks());
+ history = new HistoryManager();
+ lastTaskAddedIndex = new SimpleIntegerProperty(0);
+ lastShowDate = new SimpleStringProperty();
+ }
+
+ public ModelManager() {
+ this(new DailyPlanner(), new UserPrefs());
+ }
+
+ public ModelManager(ReadOnlyDailyPlanner initialData, UserPrefs userPrefs) {
+ dailyPlanner = new DailyPlanner(initialData);
+ filteredTasks = new FilteredList<>(dailyPlanner.getTasks());
+ pinnedTasks = new FilteredList<>(dailyPlanner.getPinnedTasks());
+ history = new HistoryManager();
+ lastTaskAddedIndex = new SimpleIntegerProperty(0);
+ lastShowDate = new SimpleStringProperty();
+ }
+
+```
+###### \java\seedu\dailyplanner\model\task\Date.java
+``` java
+
+/**
+ * Represents a Task's start date or end date in the daily planner.
+ */
+public class Date implements Comparable {
+
+ private final String m_value;
+ private final int m_day;
+ private final int m_month;
+ private final int m_year;
+
+ /**
+ * Guaranteed that value is either an empty string or in the format:
+ * DD/MM/YYYY
+ */
+ public Date(String value) {
+ assert value != null;
+ m_value = value;
+ if (!value.equals("")) {
+ m_day = Integer.parseInt(value.substring(0, 2));
+ m_month = Integer.parseInt(value.substring(3, 5));
+ m_year = Integer.parseInt(value.substring(6));
+ }
+ // default values when date not present
+ else {
+ m_day = 0;
+ m_month = 0;
+ m_year = 3000;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return m_value;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof Date // instanceof handles nulls
+ && m_value.equals(((Date) other).m_value)); // state
+ // check
+ }
+
+ @Override
+ public int hashCode() {
+ return m_value.hashCode();
+ }
+
+ public int getDay() {
+ return m_day;
+ }
+
+ public int getMonth() {
+ return m_month;
+ }
+
+ public int getYear() {
+ return m_year;
+ }
+
+ @Override
+ public int compareTo(Date o) {
+
+ if (m_year != o.m_year) {
+ return m_year - o.m_year;
+ }
+ if (m_month != o.m_month) {
+ return m_month - o.m_month;
+ }
+ if (m_day != o.m_day) {
+ return m_day - o.m_day;
+ }
+
+ return 0;
+ }
+}
+```
+###### \java\seedu\dailyplanner\model\task\Task.java
+``` java
+ private String taskName;
+ private DateTime start;
+ private DateTime end;
+ private boolean isComplete;
+ private boolean isPinned;
+ private UniqueCategoryList tags;
+
+ /**
+ * Every field must be present and not null.
+ */
+ public Task(String name, DateTime start, DateTime end, boolean isComplete, boolean isPinned, UniqueCategoryList tags) {
+ assert !CollectionUtil.isAnyNull(name, start, end, isComplete, isPinned, tags);
+ this.taskName = name;
+ this.start = start;
+ this.end = end;
+ this.tags = new UniqueCategoryList(tags); // protect internal tags from
+ // changes in the arg list
+ this.isComplete = isComplete;
+ this.isPinned = isPinned;
+ }
+
+ /**
+ * Copy constructor.
+ */
+ public Task(ReadOnlyTask source) {
+ this(source.getName(), source.getStart(), source.getEnd(), source.isComplete(), source.isPinned(),
+ source.getCats());
+ }
+
+ @Override
+ public void setName(String name) {
+ this.taskName = name;
+ }
+
+ @Override
+ public void setStart(DateTime startDate) {
+ this.start = startDate;
+ }
+
+ @Override
+ public void setEnd(DateTime endDate) {
+ this.end = endDate;
+ }
+
+ @Override
+ public void markAsComplete() {
+ this.isComplete = true;
+ }
+
+ @Override
+ public void markAsNotComplete() {
+ this.isComplete = false;
+ }
+
+ public void pin() {
+ this.isPinned = true;
+ }
+
+ public void unpin() {
+ this.isPinned = false;
+ }
+
+ public static String calculateDueStatus(DateTime end) {
+ // if there is no end date, return empty string
+ if (end.getDate().equals("")) {
+ return "";
+ }
+ DateTime nowAsDateTime = DateUtil.nowAsDateTime();
+
+ // if end date is strictly before current date
+ if (end.getDate().compareTo(nowAsDateTime.getDate()) < 0 ) {
+ return "OVERDUE";
+ }
+
+ // if end date is today or later but there is no end time
+ if (end.getTime().toString().equals("")) {
+ return "";
+ }
+
+ // if end date is same as current date
+ else if (end.getDate().equals(nowAsDateTime.getDate())) {
+ // if end time is before or equal to current time
+ if (end.getTime().compareTo(nowAsDateTime.getTime()) <= 0) {
+ return "OVERDUE";
+ } else {
+ int endHour = DateUtil.convertTo24HrFormat(end.getTime());
+ int nowHour = DateUtil.convertTo24HrFormat(nowAsDateTime.getTime());
+ int overDueHours = endHour - nowHour;
+ if (overDueHours <= 3) {
+ return "DUE SOON";
+ }
+ }
+ }
+ return "";
+ }
+
+ @Override
+ public String getName() {
+ return taskName;
+ }
+
+ @Override
+ public DateTime getStart() {
+ return start;
+ }
+
+ @Override
+ public DateTime getEnd() {
+ return end;
+ }
+
+ @Override
+ public String getCompletion() {
+ return (isComplete) ? "COMPLETE" : "NOT COMPLETE";
+ }
+
+ @Override
+ public String getDueStatus() {
+ return calculateDueStatus(end);
+ }
+
+ @Override
+ public boolean isComplete() {
+ return isComplete;
+ }
+
+ @Override
+ public void setCompletion(boolean completion) {
+ isComplete = completion;
+ }
+
+ public boolean isPinned() {
+ return isPinned;
+ }
+
+ @Override
+ public UniqueCategoryList getCats() {
+ return new UniqueCategoryList(tags);
+ }
+
+ /**
+ * Replaces this task's tags with the tags in the argument tag list.
+ */
+ public void setCategories(UniqueCategoryList replacement) {
+ tags.setTags(replacement);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof ReadOnlyTask // instanceof handles nulls
+ && this.isSameStateAs((ReadOnlyTask) other));
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(taskName, start, end, isComplete, isPinned, tags);
+ }
+
+ @Override
+ public String toString() {
+ return getAsText();
+ }
+
+ @Override
+ public int compareTo(Task o) {
+ if (!start.equals(o.start)) {
+ return start.compareTo(o.start);
+ } else if (!end.equals(o.end)) {
+ return end.compareTo(o.end);
+ } else
+ return taskName.compareTo(o.taskName);
+ }
+
+}
+```
+###### \java\seedu\dailyplanner\ui\PinnedTaskPanel.java
+``` java
+public class PinnedTaskPanel extends UiPart {
+ private final Logger logger = LogsCenter.getLogger(PinnedTaskPanel.class);
+ private static final String FXML = "PinnedTaskPanel.fxml";
+ private VBox panel;
+ private AnchorPane placeHolderPane;
+
+ @FXML
+ private ListView pinListView;
+
+ public PinnedTaskPanel() {
+ super();
+ }
+
+ @Override
+ public void setNode(Node node) {
+ panel = (VBox) node;
+ }
+
+ @Override
+ public String getFxmlPath() {
+ return FXML;
+ }
+
+ @Override
+ public void setPlaceholder(AnchorPane pane) {
+ this.placeHolderPane = pane;
+ }
+
+ public static PinnedTaskPanel load(Stage primaryStage, AnchorPane PinnedTaskPlaceholder,
+ ObservableList pinnedTaskList) {
+ PinnedTaskPanel pinnedTaskPanel =
+ UiPartLoader.loadUiPart(primaryStage, PinnedTaskPlaceholder, new PinnedTaskPanel());
+ pinnedTaskPanel.configure(pinnedTaskList);
+ return pinnedTaskPanel;
+ }
+
+ private void configure(ObservableList personList) {
+ setConnections(personList);
+ addToPlaceholder();
+ }
+
+ private void setConnections(ObservableList personList) {
+ pinListView.setItems(personList);
+ pinListView.setCellFactory(listView -> new PersonListViewCell());
+ setEventHandlerForSelectionChangeEvent();
+ }
+
+ private void addToPlaceholder() {
+ SplitPane.setResizableWithParent(placeHolderPane, false);
+ placeHolderPane.getChildren().add(panel);
+ }
+
+ private void setEventHandlerForSelectionChangeEvent() {
+ pinListView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
+ if (newValue != null) {
+ logger.fine("Selection in person list panel changed to : '" + newValue + "'");
+ raise(new TaskPanelSelectionChangedEvent(newValue));
+ }
+ });
+ }
+
+ public void scrollTo(int index) {
+ Platform.runLater(() -> {
+ pinListView.scrollTo(index);
+ pinListView.getSelectionModel().clearAndSelect(index);
+ });
+ }
+
+ class PersonListViewCell extends ListCell {
+
+ public PersonListViewCell() {
+ }
+
+ @Override
+ protected void updateItem(ReadOnlyTask person, boolean empty) {
+ super.updateItem(person, empty);
+
+ if (empty || person == null) {
+ setGraphic(null);
+ setText(null);
+ } else {
+ setGraphic(TaskCard.load(person, getIndex() + 1).getLayout());
+ }
+ }
+ }
+
+}
+```
+###### \java\seedu\dailyplanner\ui\ResultDisplay.java
+``` java
+ public void configure() {
+ resultDisplayArea = new TextArea();
+ resultDisplayArea.setEditable(false);
+ resultDisplayArea.setId(RESULT_DISPLAY_ID);
+ resultDisplayArea.getStyleClass().removeAll();
+ resultDisplayArea.getStyleClass().add(STATUS_BAR_STYLE_SHEET);
+ resultDisplayArea.setWrapText(true);
+ resultDisplayArea.setText("");
+ resultDisplayArea.textProperty().bind(displayed);
+ displayed.setValue(INITIAL_RESULT_MESSAGE);
+ FxViewUtil.applyAnchorBoundaryParameters(resultDisplayArea, 0.0, 0.0, 0.0, 0.0);
+ mainPane.getChildren().add(resultDisplayArea);
+ FxViewUtil.applyAnchorBoundaryParameters(mainPane, 0.0, 0.0, 0.0, 0.0);
+ placeHolder.getChildren().add(mainPane);
+ }
+
+ @Override
+ public void setNode(Node node) {
+ mainPane = (AnchorPane) node;
+ }
+
+ @Override
+ public void setPlaceholder(AnchorPane placeholder) {
+ this.placeHolder = placeholder;
+ }
+
+ @Override
+ public String getFxmlPath() {
+ return FXML;
+ }
+
+ public void postMessage(String message) {
+ displayed.setValue(message);
+ }
+
+}
+```
+###### \java\seedu\dailyplanner\ui\TaskCard.java
+``` java
+ @FXML
+ public void initialize() {
+ name.setText(task.getName());
+ name.setWrapText(true);
+ id.setText(displayedIndex + ". ");
+ startDate.setText(task.getStart().getDate().toString());
+ startTime.setText(task.getStart().getTime().toString());
+ endDate.setText(task.getEnd().getDate().toString());
+ endTime.setText(task.getEnd().getTime().toString());
+ tags.setText(task.tagsString());
+ if (task.isComplete()) {
+ isComplete.setText(task.getCompletion());
+ isComplete.setVisible(true);
+ } else {
+ String dueStatus = task.getDueStatus();
+ if(dueStatus.equals("")) {
+ isComplete.setVisible(false);
+ } else {
+ isComplete.setText(dueStatus);
+ if (dueStatus.equals("DUE SOON")) {
+ isComplete.setStyle(DUE_SOON_LABEL_STYLE);
+ } else if (dueStatus.equals("OVERDUE")) {
+ isComplete.setStyle(OVERDUE_LABEL_STYLE);
+ }
+
+ }
+ }
+
+ if (task.getStart().getDate().toString().equals("")) {
+ startAtLabel.setVisible(false);
+ } else {
+ startAtLabel.setText("Starts at: ");
+ }
+
+ if (task.getEnd().getDate().toString().equals("")) {
+ endAtLabel.setVisible(false);
+ } else {
+ endAtLabel.setText("Ends at: ");
+ }
+
+ }
+```
+###### \resources\view\DarkTheme.css
+``` css
+.fordate-label {
+ -fx-font-family: Verdana;
+ -fx-font-size: 15px;
+ -fx-highlight-fill: #00ff00;
+ -fx-highlight-text-fill: #000000;
+ -fx-text-fill: #00ff00;
+
+```
+###### \resources\view\MainWindow.fxml
+``` fxml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+###### \resources\view\TaskListCard.fxml
+``` fxml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+###### \resources\view\TaskListCard.fxml
+``` fxml
+
+
+
+
+
+
+
+
+
+
+```
diff --git a/collated/main/A0146749N.md b/collated/main/A0146749N.md
new file mode 100644
index 000000000000..723a1b7af44a
--- /dev/null
+++ b/collated/main/A0146749N.md
@@ -0,0 +1,859 @@
+# A0146749N
+###### \java\seedu\dailyplanner\commons\util\DateUtil.java
+``` java
+public class DateUtil {
+
+ private static final String STRING_REPRESENTING_NOW = "now";
+
+ /**
+ * Checks if given task has both start time and end time
+ *
+ * @return true if task has both, false otherwise
+ */
+ public static boolean hasStartandEndTime(ReadOnlyTask storedTask) {
+ Time storedStartTime = storedTask.getStart().getTime();
+ Time storedEndTime = storedTask.getEnd().getTime();
+ if (!(storedStartTime.toString().equals("")) && !(storedEndTime.toString().equals(""))) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Checks if date given by keyword falls within a task's start and end dates
+ * Guaranteed that keyword is of the format DD/MM/YYYY
+ */
+ public static boolean withinDateRange(ReadOnlyTask task, String keyword) {
+ int keyDate = SubStringOfStringAsInt(0, 2, keyword);
+ int keyMonth = SubStringOfStringAsInt(3, 5, keyword);
+ int keyYear = subStringOfStringAsInt(6, keyword);
+ Date taskStart = task.getStart().getDate();
+ Date taskEnd = task.getEnd().getDate();
+ if (startDateTimeAndEndDateTimeIsEmpty(taskStart, taskEnd)) {
+ return false;
+ } else if (dateTimeIsEmpty(taskStart)) {
+ taskStart = taskEnd;
+ } else if (dateTimeIsEmpty(taskEnd)) {
+ taskEnd = taskStart;
+ }
+ return isKeyBetweenStartAndEnd(keyDate, keyMonth, keyYear, taskStart, taskEnd);
+ }
+
+ /** Helper method for withinDateRang method */
+ private static boolean isKeyBetweenStartAndEnd(int keyDate, int keyMonth, int keyYear, Date taskStart,
+ Date taskEnd) {
+ Calendar start = Calendar.getInstance();
+ start.set(taskStart.getYear() + 1900, taskStart.getMonth(), taskStart.getDay());
+ Calendar searchKey = Calendar.getInstance();
+ searchKey.set(keyYear + 1900, keyMonth, keyDate);
+ Calendar end = Calendar.getInstance();
+ end.set(taskEnd.getYear() + 1900, taskEnd.getMonth(), taskEnd.getDay());
+ return (start.compareTo(searchKey) <= 0 && end.compareTo(searchKey) >= 0);
+ }
+
+ private static boolean dateTimeIsEmpty(Date taskStart) {
+ return taskStart.toString().equals("");
+ }
+
+ private static boolean startDateTimeAndEndDateTimeIsEmpty(Date taskStart, Date taskEnd) {
+ return dateTimeIsEmpty(taskStart) && dateTimeIsEmpty(taskEnd);
+ }
+
+ private static int SubStringOfStringAsInt(int start, int end, String keyword) {
+ return Integer.parseInt(keyword.substring(start, end));
+ }
+
+ private static int subStringOfStringAsInt(int start, String keyword) {
+ return Integer.parseInt(keyword.substring(start));
+ }
+
+ /** Returns current time as DateTime object */
+ public static DateTime nowAsDateTime() {
+ nattyParser natty = new nattyParser();
+ String dateTimeAsString = natty.parse(STRING_REPRESENTING_NOW);
+ return getDateTimeFromString(dateTimeAsString);
+ }
+
+ /**
+ * Converts given string into DateTime object. Guaranteed that String is in
+ * DD/MM/YYYY HH.MMam or HH.MMpm format
+ */
+ public static DateTime getDateTimeFromString(String dateTimeAsString) {
+ String[] dateTimeArray = dateTimeAsString.split(" ");
+ Date nowDate = new Date(dateTimeArray[0]);
+ Time nowTime = new Time(dateTimeArray[1]);
+ return new DateTime(nowDate, nowTime);
+ }
+
+ /**
+ * Converts time from 12HR to 24HR format
+ */
+ public static int convertTo24HrFormat(Time firstTime) {
+ if (firstTime.m_meridiem.equals("AM") && firstTime.m_hour == 12) {
+ return 0;
+ } else if (firstTime.m_meridiem.equals("PM") && firstTime.m_hour != 12) {
+ return firstTime.m_hour + 12;
+ } else {
+ return firstTime.m_hour;
+ }
+ }
+
+ /**
+ * Checks if string is in dd/mm/yyyy format
+ */
+ public static boolean isValidDayMonthAnd4DigitYearFormat(String date) {
+ return (date.matches("([0-9]{2})/([0-9]{2})/([0-9]{4})") || date.matches("([0-9]{1})/([0-9]{2})/([0-9]{4})"));
+ }
+
+ /**
+ * Checks if string is in dd/mm/yy format
+ */
+ public static boolean isValidDayMonthAnd2DigitYearFormat(String date) {
+ return (date.matches("([0-9]{2})/([0-9]{2})/([0-9]{2})") || date.matches("([0-9]{1})/([0-9]{2})/([0-9]{2})"));
+
+ }
+
+ /** Converts string from dd/mm/yy to dd/mm/20yy format */
+ public static String convertTo4DigitYearFormat(String date) {
+ String dayAndMonth = date.substring(0, 6);
+ String yy = date.substring(6);
+ return dayAndMonth + "20" + yy;
+ }
+
+ /** Returns the date today as a Date object */
+ public static Date todayAsDate() {
+ nattyParser natty = new nattyParser();
+ return new Date(natty.parseDate("today"));
+ }
+
+ /** Returns an empty DateTime object */
+ public static DateTime getEmptyDateTime() {
+ return new DateTime(new Date(""), new Time(""));
+ }
+
+}
+```
+###### \java\seedu\dailyplanner\logic\commands\CompleteCommand.java
+``` java
+ @Override
+ public CommandResult execute() {
+
+ UnmodifiableObservableList lastShownList = model.getFilteredTaskList();
+
+ if (lastShownList.size() < targetIndex) {
+ indicateAttemptToExecuteIncorrectCommand();
+ return new CommandResult(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX);
+ }
+
+ ReadOnlyTask taskToComplete = lastShownList.get(targetIndex - 1);
+
+ try {
+ model.getHistory().stackUncompleteInstruction(taskToComplete);
+ model.markTaskAsComplete(taskToComplete);
+ model.updatePinBoard();
+ } catch (TaskNotFoundException pnfe) {
+ assert false : "The target task cannot be missing";
+ }
+ return new CommandResult(String.format(MESSAGE_COMPLETED_TASK_SUCCESS, taskToComplete));
+ }
+
+}
+```
+###### \java\seedu\dailyplanner\logic\commands\EditCommand.java
+``` java
+ @Override
+ public CommandResult execute() {
+
+ UnmodifiableObservableList lastShownList = model.getFilteredTaskList();
+
+ if (lastShownList.size() < targetIndex) {
+ indicateAttemptToExecuteIncorrectCommand();
+ return new CommandResult(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX);
+ }
+
+ ReadOnlyTask originalTask = lastShownList.get(targetIndex - 1);
+
+ // if any of the fields passed in by user are empty, take it from the
+ // original task
+ String toAddName = (taskName.isPresent()) ? taskName.get() : originalTask.getName();
+ DateTime toAddStart = (start.isPresent()) ? start.get() : originalTask.getStart();
+ DateTime toAddEnd = (end.isPresent()) ? end.get() : originalTask.getEnd();
+ UniqueCategoryList toAddCats = (categoriesSet.isPresent()) ? categoriesSet.get() : originalTask.getCats();
+ Task toAdd = new Task(toAddName, toAddStart, toAddEnd, originalTask.isComplete(), originalTask.isPinned(),
+ toAddCats);
+
+ try {
+ model.getHistory().stackEditInstruction(originalTask, toAdd);
+ model.deleteTask(originalTask);
+ model.addTask(toAdd);
+ model.updatePinBoard();
+ } catch (TaskNotFoundException pnfe) {
+ assert false : "The target task cannot be missing";
+ } catch (UniqueTaskList.DuplicateTaskException e) {
+ return new CommandResult(MESSAGE_DUPLICATE_TASK);
+ }
+ return new CommandResult(String.format(MESSAGE_EDIT_TASK_SUCCESS, originalTask));
+ }
+}
+```
+###### \java\seedu\dailyplanner\logic\commands\PinCommand.java
+``` java
+ public PinCommand(int targetIndex) {
+ this.targetIndex = targetIndex;
+ }
+
+ @Override
+ public CommandResult execute() {
+
+ UnmodifiableObservableList lastShownList = model.getFilteredTaskList();
+
+ if (lastShownList.size() < targetIndex) {
+ indicateAttemptToExecuteIncorrectCommand();
+ return new CommandResult(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX);
+ }
+
+ ReadOnlyTask taskToPin = lastShownList.get(targetIndex - 1);
+
+ if (model.getPinnedTaskList().contains(taskToPin)) {
+ indicateAttemptToExecuteIncorrectCommand();
+ return new CommandResult(MESSAGE_DUPLICATE_PINNED_TASK);
+ }
+
+ try {
+ model.getHistory().stackUnpinInstruction(taskToPin);
+ model.pinTask(taskToPin);
+ model.updatePinBoard();
+ } catch (TaskNotFoundException pnfe) {
+ assert false : "The target task cannot be missing";
+ }
+ return new CommandResult(String.format(MESSAGE_PINNED_TASK_SUCCESS, taskToPin));
+ }
+
+}
+```
+###### \java\seedu\dailyplanner\logic\commands\SelectCommand.java
+``` java
+ @Override
+ public CommandResult execute() {
+
+ UnmodifiableObservableList lastShownList = model.getFilteredTaskList();
+
+ if (lastShownList.size() < targetIndex) {
+ indicateAttemptToExecuteIncorrectCommand();
+ return new CommandResult(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX);
+ }
+
+ EventsCenter.getInstance().post(new JumpToListRequestEvent(targetIndex - 1));
+ return new CommandResult(String.format(MESSAGE_SELECT_TASK_SUCCESS, targetIndex));
+
+ }
+
+}
+```
+###### \java\seedu\dailyplanner\logic\commands\ShowCommand.java
+``` java
+public class ShowCommand extends Command {
+
+ public static final String COMMAND_WORD = "show";
+ public static final String KEYWORD_SHOW_COMPLETED = "complete";
+ public static final String KEYWORD_SHOW_NOT_COMPLETED = "not complete";
+
+ public static final String MESSAGE_SUCCESS = "Showing %1$s tasks";
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Shows all tasks whose completion status"
+ + "matches the keyword or whose start and end date range falls within the keyword date"
+ + "Parameters: KEYWORD [MORE_KEYWORDS]...\n" + "Example: " + COMMAND_WORD + " completed";
+
+ private final Set keywords;
+
+ public ShowCommand() {
+ keywords = null;
+ }
+
+```
+###### \java\seedu\dailyplanner\logic\commands\ShowCommand.java
+``` java
+ public ShowCommand(Set keywords) {
+ this.keywords = keywords;
+ }
+
+ @Override
+ public CommandResult execute() {
+ if (keywords == null) {
+ model.updateFilteredListToShowAll();
+ model.setLastShowDate(StringUtil.EMPTY_STRING);
+ return new CommandResult(String.format(MESSAGE_SUCCESS, "all"));
+ } else {
+ // if command is a show completed command
+ if (keywords.contains(KEYWORD_SHOW_COMPLETED)) {
+ model.updateFilteredTaskListByCompletion(keywords);
+ model.setLastShowDate("completed");
+ }
+ // command is a show not completed command
+ else if (keywords.contains(KEYWORD_SHOW_NOT_COMPLETED)) {
+ model.updateFilteredTaskListByCompletion(keywords);
+ model.setLastShowDate("not completed");
+ }
+ // command is a show by date command
+ else {
+ model.updateFilteredTaskListByDate(keywords);
+ model.setLastShowDate((String) keywords.toArray()[0]);
+ }
+ return new CommandResult(String.format(MESSAGE_SUCCESS, model.getFilteredTaskList().size()));
+ }
+ }
+}
+```
+###### \java\seedu\dailyplanner\logic\commands\UncompleteCommand.java
+``` java
+ @Override
+ public CommandResult execute() {
+
+ UnmodifiableObservableList lastShownList = model.getFilteredTaskList();
+
+ if (lastShownList.size() < targetIndex) {
+ indicateAttemptToExecuteIncorrectCommand();
+ return new CommandResult(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX);
+ }
+
+ ReadOnlyTask taskToUncomplete = lastShownList.get(targetIndex - 1);
+
+ try {
+ model.getHistory().stackCompleteInstruction(taskToUncomplete);
+ model.markTaskAsIncomplete(taskToUncomplete);
+ model.updatePinBoard();
+ } catch (TaskNotFoundException pnfe) {
+ assert false : "The target task cannot be missing";
+ }
+ return new CommandResult(String.format(MESSAGE_UNCOMPLETED_TASK_SUCCESS, taskToUncomplete));
+ }
+
+}
+```
+###### \java\seedu\dailyplanner\logic\commands\UnpinCommand.java
+``` java
+
+/**
+ * Unpins a task identified using it's last displayed index from the daily
+ * planner pinned task board.
+ */
+public class UnpinCommand extends Command {
+
+ public final int targetIndex;
+
+ public static final String COMMAND_WORD = "unpin";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Unpins the task identified by the index number used in the last task listing on the pin board.\n"
+ + "Parameters: INDEX (must be a positive integer)\n" + "Example: " + COMMAND_WORD + " 1";
+
+ public static final String MESSAGE_PIN_TASK_SUCCESS = "Unpinned Task: %1$s";
+
+ public UnpinCommand(int targetIndex) {
+ this.targetIndex = targetIndex;
+ }
+
+ @Override
+ public CommandResult execute() {
+
+ UnmodifiableObservableList pinnedList = model.getPinnedTaskList();
+
+ if (pinnedList.size() < targetIndex) {
+ indicateAttemptToExecuteIncorrectCommand();
+ return new CommandResult(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX);
+ }
+
+ ReadOnlyTask taskToUnpin = pinnedList.get(targetIndex - 1);
+
+ try {
+ model.getHistory().stackPinInstruction(taskToUnpin);
+ model.unpinTask(targetIndex - 1);
+ } catch (TaskNotFoundException pnfe) {
+ assert false : "The target task cannot be missing";
+ }
+ return new CommandResult(String.format(MESSAGE_PIN_TASK_SUCCESS, taskToUnpin));
+ }
+
+}
+```
+###### \java\seedu\dailyplanner\logic\LogicManager.java
+``` java
+ @Override
+ public ObservableList getPinnedTaskList() {
+ return model.getPinnedTaskList();
+ }
+
+ @Override
+ public IntegerProperty getLastTaskAddedIndexProperty() {
+ return model.getLastTaskAddedIndexProperty();
+ }
+
+ @Override
+ public StringProperty getLastShowDateProperty() {
+ return model.getLastShowDateProperty();
+ }
+
+}
+```
+###### \java\seedu\dailyplanner\logic\parser\Parser.java
+``` java
+
+ /**
+ * Parses arguments in the context of the pin task command.
+ *
+ * @param args
+ * full command args string
+ * @return the prepared command
+ */
+ private Command preparePin(String arguments) {
+ String trimmedArg = arguments.trim();
+ Optional index = parseIndex(trimmedArg);
+ if (!index.isPresent()) {
+ return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, PinCommand.MESSAGE_USAGE));
+ }
+ return new PinCommand(index.get());
+ }
+
+ /**
+ * Parses arguments in the context of the unpin task command.
+ *
+ * @param args
+ * full command args string
+ * @return the prepared command
+ */
+ private Command prepareUnpin(String arguments) {
+ String trimmedArg = arguments.trim();
+ Optional index = parseIndex(trimmedArg);
+ if (!index.isPresent()) {
+ return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, UnpinCommand.MESSAGE_USAGE));
+ }
+ return new UnpinCommand(index.get());
+ }
+
+ /**
+ * Parses arguments in the context of the complete task command.
+ *
+ * @param args
+ * full command args string
+ * @return the prepared command
+ */
+ private Command prepareComplete(String arguments) {
+ String trimmedArg = arguments.trim();
+ Optional index = parseIndex(trimmedArg);
+ if (!index.isPresent()) {
+ return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, CompleteCommand.MESSAGE_USAGE));
+ }
+ return new CompleteCommand(index.get());
+ }
+
+ /**
+ * Parses arguments in the context of the uncomplete task command.
+ *
+ * @param args
+ * full command args string
+ * @return the prepared command
+ */
+ private Command prepareUncomplete(String arguments) {
+ String trimmedArg = arguments.trim();
+ Optional index = parseIndex(trimmedArg);
+ if (!index.isPresent()) {
+ return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, UncompleteCommand.MESSAGE_USAGE));
+ }
+ return new UncompleteCommand(index.get());
+ }
+
+```
+###### \java\seedu\dailyplanner\logic\parser\Parser.java
+``` java
+ /**
+ * Extracts the task name from the rest of the arguments
+ */
+ private String getTaskNameFromArguments(String arguments) {
+ if (arguments.contains("/")) {
+ String[] splitArgs = arguments.split("/");
+ return splitArgs[0].substring(0, splitArgs[0].length() - 2);
+ } else {
+ return arguments;
+ }
+ }
+
+ /**
+ * Parses arguments in the context of the delete task command.
+ *
+ * @param args
+ * full command args string
+ * @return the prepared command
+ */
+ private Command prepareDelete(String args) {
+ String trimmedArgs = args.trim();
+
+ if (trimmedArgs.contains("complete")) {
+ return new DeleteCompletedCommand();
+ } else {
+ Optional index = parseIndex(trimmedArgs);
+ if (!index.isPresent()) {
+ return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE));
+ }
+ return new DeleteCommand(index.get());
+ }
+ }
+
+ /**
+ * Parses arguments in the context of the show tasks command.
+ *
+ * @param args
+ * full command args string
+ * @return the prepared command
+ */
+ private Command prepareShow(String args) {
+ final Matcher matcher = KEYWORDS_ARGS_FORMAT.matcher(args.trim());
+ if (!matcher.matches()) {
+ return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, ShowCommand.MESSAGE_USAGE));
+ }
+
+ // keywords delimited by whitespace
+ final String keyword = matcher.group("keywords");
+
+ String[] keywords = new String[1];
+
+ // if command is a show by completion status command
+ if (keyword.contains("complete")) {
+ if (keyword.contains(ShowCommand.KEYWORD_SHOW_NOT_COMPLETED)) {
+ keywords[0] = "not complete";
+ } else {
+ keywords[0] = "complete";
+ }
+ }
+ // command is a show by date command
+ else {
+ nattyParser natty = new nattyParser();
+ keywords[0] = natty.parseDate(keyword);
+ }
+ final Set keywordSet = new HashSet<>(Arrays.asList(keywords));
+ return new ShowCommand(keywordSet);
+ }
+
+```
+###### \java\seedu\dailyplanner\model\DailyPlanner.java
+``` java
+ public ObservableList getPinnedTasks() {
+ return tasks.getInternalPinnedList();
+ }
+```
+###### \java\seedu\dailyplanner\model\DailyPlanner.java
+``` java
+ public void markTaskAsComplete(ReadOnlyTask key) throws UniqueTaskList.TaskNotFoundException {
+ tasks.complete(key);
+ }
+
+
+ public void pinTask(ReadOnlyTask taskToPin) throws TaskNotFoundException {
+ tasks.pin(taskToPin);
+ }
+
+ public void unpinTask(int targetIndex) {
+ tasks.unpin(targetIndex);
+ }
+
+ public void uncompleteTask(int targetIndex) {
+ tasks.uncomplete(targetIndex);
+
+ }
+
+ public void resetPinBoard() {
+ tasks.resetPinBoard();
+ }
+
+ public int indexOf(Task task) {
+ return tasks.getIndexOf(task);
+ }
+
+ public void updatePinBoard() {
+ tasks.updatePinBoard();
+ }
+```
+###### \java\seedu\dailyplanner\model\ModelManager.java
+``` java
+ @Override
+ public void pinTask(ReadOnlyTask taskToPin) throws TaskNotFoundException {
+ dailyPlanner.pinTask(taskToPin);
+ indicateDailyPlannerChanged();
+ }
+
+ @Override
+ public void unpinTask(int targetIndex) throws TaskNotFoundException {
+ dailyPlanner.unpinTask(targetIndex);
+ indicateDailyPlannerChanged();
+ }
+
+ @Override
+ public void uncompleteTask(int targetIndex) {
+ dailyPlanner.uncompleteTask(targetIndex);
+ indicateDailyPlannerChanged();
+ }
+
+ @Override
+ public void updatePinBoard() {
+ dailyPlanner.updatePinBoard();
+ }
+
+```
+###### \java\seedu\dailyplanner\model\ModelManager.java
+``` java
+ @Override
+ public void updateFilteredTaskListByDate(Set keywords) {
+ updateFilteredTaskList(new PredicateExpression(new DateQualifier(keywords)));
+ }
+
+ @Override
+ public void updateFilteredTaskListByCompletion(Set keywords) {
+ updateFilteredTaskList(new PredicateExpression(new CompletionQualifier(keywords)));
+ }
+
+ @Override
+ public int getLastTaskAddedIndex() {
+ return lastTaskAddedIndex.get();
+ }
+
+ @Override
+ public void setLastTaskAddedIndex(int index) {
+ if (index == lastTaskAddedIndex.get()) {
+ lastTaskAddedIndex.set(-1);
+ }
+ lastTaskAddedIndex.set(index);
+ }
+
+ @Override
+ public IntegerProperty getLastTaskAddedIndexProperty() {
+ return lastTaskAddedIndex;
+ }
+
+ @Override
+ public String getLastShowDate() {
+ return lastShowDate.get();
+ }
+
+ @Override
+ public void setLastShowDate(String showInput) {
+ lastShowDate.set(showInput);
+ }
+
+ @Override
+ public StringProperty getLastShowDateProperty() {
+ return lastShowDate;
+ }
+```
+###### \java\seedu\dailyplanner\model\ModelManager.java
+``` java
+ private class CompletionQualifier implements Qualifier {
+ private Set completionKeywords;
+
+ CompletionQualifier(Set completionKeyword) {
+ this.completionKeywords = completionKeyword;
+ }
+
+ @Override
+ public boolean run(ReadOnlyTask task) {
+ return completionKeywords.contains(task.getCompletion().toLowerCase());
+ }
+
+ @Override
+ public String toString() {
+ return "completion=" + String.join(", ", completionKeywords);
+ }
+ }
+
+ private class DateQualifier implements Qualifier {
+ private Set dateKeyWords;
+
+ DateQualifier(Set dateKeyWords) {
+ this.dateKeyWords = dateKeyWords;
+ }
+
+ @Override
+ public boolean run(ReadOnlyTask task) {
+ return dateKeyWords.stream().filter(keyword -> DateUtil.withinDateRange(task, keyword)).findAny()
+ .isPresent();
+ }
+
+ @Override
+ public String toString() {
+ return "date=" + String.join(", ", dateKeyWords);
+ }
+ }
+
+ private class NameQualifier implements Qualifier {
+ private Set nameKeyWords;
+
+ NameQualifier(Set nameKeyWords) {
+ this.nameKeyWords = nameKeyWords;
+ }
+
+ @Override
+ public boolean run(ReadOnlyTask task) {
+ return nameKeyWords.stream().filter(keyword -> StringUtil.containsIgnoreCase(task.getName(), keyword))
+ .findAny().isPresent();
+ }
+
+ @Override
+ public String toString() {
+ return "name=" + String.join(", ", nameKeyWords);
+ }
+ }
+
+ @Override
+ public HistoryManager getHistory() {
+
+ return history;
+ }
+
+}
+```
+###### \java\seedu\dailyplanner\model\task\DateTime.java
+``` java
+/**
+ * Contains a Date object and a Time object. In the daily planner, a task's
+ * start and end are instances of this class
+ *
+ */
+public class DateTime implements Comparable {
+
+ private final Date m_date;
+ private final Time m_time;
+
+ public DateTime(Date date, Time time) {
+ m_date = date;
+ m_time = time;
+ }
+
+ @Override
+ public String toString() {
+ // if date field is empty, time field is also empty
+ // so return empty string
+ if (m_date.toString().equals("")) {
+ return "";
+ }
+ // if date is present, but time is not present
+ else if (m_time.toString().equals("")) {
+ return m_date.toString();
+ } else {
+ return m_date.toString() + " " + m_time.toString();
+ }
+ }
+
+ public Date getDate() {
+ return m_date;
+ }
+
+ public Time getTime() {
+ return m_time;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof DateTime // instanceof handles nulls
+ && m_date.equals(((DateTime) other).m_date) && m_time.equals(((DateTime) other).m_time)); // state
+ // check
+ }
+
+ @Override
+ public int hashCode() {
+ return m_date.hashCode();
+ }
+
+ @Override
+ public int compareTo(DateTime o) {
+ if (!m_date.equals(o.m_date)) {
+ return m_date.compareTo(o.m_date);
+ } else {
+ return m_time.compareTo(o.m_time);
+ }
+ }
+}
+```
+###### \java\seedu\dailyplanner\model\task\Time.java
+``` java
+
+/**
+ * Represents a task's start or end time in the daily planner.
+ */
+public class Time implements Comparable