diff --git a/.project b/.project
index 1c9339c5f927..0d5c12eeb216 100644
--- a/.project
+++ b/.project
@@ -20,4 +20,24 @@
org.eclipse.buildship.core.gradleprojectnatureorg.eclipse.jdt.core.javanature
+
+
+ 1475672512464
+
+ 26
+
+ org.eclipse.ui.ide.multiFilter
+ 1.0-projectRelativePath-matches-false-false-build
+
+
+
+ 1475672512512
+
+ 26
+
+ org.eclipse.ui.ide.multiFilter
+ 1.0-projectRelativePath-matches-false-false-.gradle
+
+
+
diff --git a/.settings/org.eclipse.buildship.core.prefs b/.settings/org.eclipse.buildship.core.prefs
index 4e0fc71ac89f..1f6ca85b0cd0 100644
--- a/.settings/org.eclipse.buildship.core.prefs
+++ b/.settings/org.eclipse.buildship.core.prefs
@@ -1,3 +1,5 @@
+GRADLE_BUILD_COMMANDS=org.eclipse.jdt.core.javabuilder
+GRADLE_NATURES=org.eclipse.jdt.core.javanature
build.commands=org.eclipse.jdt.core.javabuilder
connection.arguments=
connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER)
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/README.md b/README.md
index 249a00b3899c..14e9d9c84b3b 100644
--- a/README.md
+++ b/README.md
@@ -1,34 +1,38 @@
-[](https://travis-ci.org/se-edu/addressbook-level4)
-[](https://coveralls.io/github/se-edu/addressbook-level4?branch=master)
+# MESS (My Efficient Scheduling System)
-# Address Book (Level 4)
+#### Statuses
+* Master: [](https://travis-ci.org/CS2103AUG2016-T14-C4/main)
+[](https://coveralls.io/github/CS2103AUG2016-T14-C4/main?branch=master)
+[](https://www.codacy.com/app/syed/main?utm_source=github.com&utm_medium=referral&utm_content=CS2103AUG2016-T14-C4/main&utm_campaign=Badge_Grade)
-
+* Develop: [](https://travis-ci.org/CS2103AUG2016-T14-C4/main)
+[](https://coveralls.io/github/CS2103AUG2016-T14-C4/main?branch=develop)
+[](https://www.codacy.com/app/syed/main?utm_source=github.com&utm_medium=referral&utm_content=CS2103AUG2016-T14-C4/main&utm_campaign=Badge_Grade)
-* 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.
+
-
+* MESS is a desktop to do list application. It has a GUI but most of the user interactions happen using
+ a CLI (Command Line Interface) and mostly with one line of command as input.
+* It is an application intended for users to put their tasks and events into a scheduling system.
+* It uses Java as the main programming language. It is **written in OOP fashion**.
+* It is developed by a group of undergraduate students from School of Computing, National University of Singapore for the module CS2103 with the help from professor and supervisior.
+
+
#### Site Map
-* [User Guide](docs/UserGuide.md)
-* [Developer Guide](docs/DeveloperGuide.md)
-* [Learning Outcomes](docs/LearningOutcomes.md)
+* [User Guide](docs/UserGuide.md)
+* [Developer Guide](docs/DeveloperGuide.md)
* [About Us](docs/AboutUs.md)
* [Contact Us](docs/ContactUs.md)
+
#### Acknowledgements
+* Orginal source and code base of this application is based on [Addressbook-level4](https://github.com/se-edu/addressbook-level4)forked from se-edu/addressbook-level4 created by [SE-EDU initiative](http://github.com/se-edu/)
+* 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*.
-* 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*.
#### Licence : [MIT](LICENSE)
+
+
diff --git a/build.gradle b/build.gradle
index 46b06c1e42ec..225347298da7 100644
--- a/build.gradle
+++ b/build.gradle
@@ -48,6 +48,8 @@ allprojects {
}
dependencies {
+ compile "org.ocpsoft.prettytime:prettytime:4.0.0.Final"
+ compile "org.ocpsoft.prettytime:prettytime-nlp:4.0.0.Final"
compile "org.controlsfx:controlsfx:$controlsFxVersion"
compile "com.fasterxml.jackson.core:jackson-databind:$jacksonVersion"
compile "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jacksonDataTypeVersion"
@@ -74,10 +76,10 @@ allprojects {
}
shadowJar {
- archiveName = "addressbook.jar"
+ archiveName = "taskmanager.jar"
manifest {
- attributes "Main-Class": "seedu.address.MainApp"
+ attributes "Main-Class": "seedu.task.MainApp"
}
destinationDir = file("${buildDir}/jar/")
@@ -113,8 +115,8 @@ tasks.coveralls {
onlyIf { System.env.'CI' }
}
-class AddressBookTest extends Test {
- public AddressBookTest() {
+class TaskManagerTest extends Test {
+ public TaskManagerTest() {
forkEvery = 1
systemProperty 'testfx.setup.timeout', '60000'
}
@@ -128,7 +130,7 @@ class AddressBookTest extends Test {
}
}
-task guiTests(type: AddressBookTest) {
+task guiTests(type: TaskManagerTest) {
include 'guitests/**'
jacoco {
@@ -137,8 +139,8 @@ task guiTests(type: AddressBookTest) {
}
-task nonGuiTests(type: AddressBookTest) {
- include 'seedu/address/**'
+task nonGuiTests(type: TaskManagerTest) {
+ include 'seedu/task/**'
jacoco {
destinationFile = new File("${buildDir}/jacoco/test.exec")
@@ -146,7 +148,7 @@ task nonGuiTests(type: AddressBookTest) {
}
// Test mode depends on whether headless task has been run
-task allTests(type: AddressBookTest) {
+task allTests(type: TaskManagerTest) {
jacoco {
destinationFile = new File("${buildDir}/jacoco/test.exec")
}
diff --git a/collated/docs/A0141052Y.md b/collated/docs/A0141052Y.md
new file mode 100644
index 000000000000..bc758372d8e8
--- /dev/null
+++ b/collated/docs/A0141052Y.md
@@ -0,0 +1,48 @@
+# A0141052Y
+###### \DeveloperGuide.md
+``` md
+
+1b. The previous action does not support undo
+> Use case ends
+
+```
+###### \DeveloperGuide.md
+``` md
+#### Use case: Find task by tag
+
+**MSS**
+
+1. User enters command and tags to be searched.
+2. MESS shows the tasks containing the tags
+Use case ends.
+
+**Extensions**
+
+1a. The find by tag command format is wrong
+
+>MESS shows an error message.
+ Use case ends.
+
+1b. There is no matched task
+
+>MESS shows no tasks in the task list
+ Use case ends.
+```
+###### \UserGuide.md
+``` md
+#### Finding all tasks by tag name: `find-tag`
+Finds tasks that contains all given tags.
+Format: `find-tag TAG_NAME`
+
+> * The search is case sensitive e.g. `urgent` will not match `URGENT`
+> * Tasks must match all keywords (i.e. `AND` search).
+ e.g. `urgent task` will only match tasks with both `urgent` and `task` tags
+
+#### Activate real time search: `searchbox`
+Activates the real time search, which is located in the same input box used to input commands.
+Format: `searchbox`
+
+> * To exit from real time search, just hit Enter.
+> * Similar functionality to `find`
+
+```
diff --git a/collated/docs/A0144939R.md b/collated/docs/A0144939R.md
new file mode 100644
index 000000000000..a27ef99fa800
--- /dev/null
+++ b/collated/docs/A0144939R.md
@@ -0,0 +1,125 @@
+# A0144939R
+###### \DeveloperGuide.md
+``` md
+#### Use case: Alias command
+
+**MSS**
+
+1. User requests to alias a command
+2. MESS aliases the command to the given symbol
+Use case ends.
+
+**Extensions**
+
+1a. The symbol is already aliased to another command
+> MESS changes the mapping to the new command.
+Use case ends.
+
+1b. The symbol is a standard command name(Eg: add, edit)
+> MESS shows an error message.
+Use case ends.
+
+1c. The command already has an alias
+> MESS maps the given symbol to the command, as a command can have multiple aliases
+Use case ends.
+
+```
+###### \DeveloperGuide.md
+``` md
+#### Use case: Change storage location
+
+**MSS**
+
+1. User requests to change storage file location
+2. MESS changes storage file location and saves in that location/loads from that location if file already contains data
+
+**Extensions**
+2a. The file path may not be valid
+
+> Error message will be displayed
+Use case ends.
+
+2b. The folder may not be writable(Lack permission)
+
+> Error message will be displayed
+Use case ends.
+
+```
+###### \UserGuide.md
+``` md
+
+#### Adding a task or event: `add`
+Adds a task to the to-do list
+Format: `add TASK_NAME [starts START_DATETIME ends CLOSE_DATETIME tag TAG]`
+
+> Date format of START_DATE and CLOSE_DATE includes words like today, tomorrow, 3 days from now, day after tomorrow, noon, 12pm, 6am
+
+> Use ISO date-time format if you wish to type exact date. e.g. 2016/9/1 represents 1st September 2016
+
+* `TASK_NAME` need not be unique.
+* If there is no argument, the task will become floating.
+* `START_DATE` refer to the starting date and time of an event. For a task, the timestamp will be automatically saved as start date and time when the task is created. User can input start date and time for events.
+* `TAG` is for users to write tags for different tasks. Multiple tags are available by typing `tag TAG tag TAG`.
+
+Examples:
+* `add proposal ends tomorrow` Adds a proposal task with a deadline 24 hours from now
+* `add meeting starts tomorrow 1pm ends tomorrow 3pm` Adds a meeting event which start tomorrow at 1pm and ends tomorrow at 3pm
+* `add shopping` Adds a floating task named revision test which has not specify the start and end date
+* `add tutorial tag cs2103` Adds a floating task named tutorial with a tag CS2013
+* `add quiz tag cs2102 tag easy` Adds a floating task named tutorial with a tag CS2012 and easy
+* `add test starts 9/1 2pm ends 9/1 3pm ` Adds a task starts on 1 September 2pm and ends on 1 September 3pm
+
+
+#### Aliasing a command: `alias`
+Aliases a command to a symbol
+Format: `alias COMMAND_NAME SYMBOL`
+
+> Once aliased, the original command will still continue to work. Hence, symbols cannot be command names.
+> One symbol can map to at most one command
+
+* `COMMAND_NAME` Must be a valid command
+* `SYMBOL` refers to the symbol you wish to alias to. SYMBOL cannot be a command name.
+
+Examples:
+* `alias add +` Aliases the add command to the symbol +
+* `alias alias q` Aliases the alias command to the symbol q
+
+```
+###### \UserGuide.md
+``` md
+#### Update entries : `update`
+Update a specific task.
+Format: `update INDEX [name TASKNAME starts STARTDATETIME ends ENDDATETIME tag TAG remove-tag TO_REMOVE_TAG]`
+
+> * INDEX refers to the number appears on the list in front the task name.
+> * The task name is optional, but needs to be preceded by name
+> * The TAG here will be added to the referred task and the original tag remains. If you want to delete a tag, use `remove-tag TO_REMOVE_TAG` to delete tag by name.
+> * TO_REMOVE_TAG refers to the tag (or tags) that you want to be removed by typing the tags' name that you want to delete.
+> * You can choose what to update. It depends on you whether you want to update only one information or update multiple information.
+
+Examples:
+* `update 2 name shopping ends 9pm`
+ update the task name of the second task on the list to shopping and the start time to 3/10/2016 9 p.m.
+
+* `update 1 tag cs2103`
+ add the tag of the first task on to-do list to cs2103
+
+* `update 3 ends three hours later`
+ update the task name of the third task on the list to a deadline three hours after you type this command
+
+* `update 2 tag family remove-tag friends`
+ add a tag family to the second task and remove the tag named friends
+
+```
+###### \UserGuide.md
+``` md
+
+#### Change storage location : `change-to`
+Changes to a new storage location and saves task manager data there
+Note: If new file already has data, then the taskmanager will load that data.
+
+Format: `change-to NEWFILEPATH`
+Example:
+* `change-to data/taskmanager.xml`
+
+```
diff --git a/collated/docs/A0153467Y.md b/collated/docs/A0153467Y.md
new file mode 100644
index 000000000000..f70e9fb06ea7
--- /dev/null
+++ b/collated/docs/A0153467Y.md
@@ -0,0 +1,368 @@
+# A0153467Y
+###### \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 a task with deadline|
+`* * *` | user | add a task without start time and deadline| keep track of general, non-time based tasks
+`* * *` | user | add an event with start time and close time|
+`* * *` | user | add an event/task with tag|
+`* * *` | user | add recurring tasks
+`* * *` | user | delete a task| remove tasks that I do not have to take any further action on
+`* * *` |user | undo my previous action | recover from commands entered by mistake
+`* * *` | user | find a task from to-do list| find details of tasks without having to go through the entire list
+`* * *` | user | update a task | change the specifications of a specific task
+`* * *` | user | filter a list of pinned tasks or task which are pending | Know what tasks need to be done and what tasks are important
+`* * *` | user | specify the file location of the task list | store the list in a more convenient location
+`* *` | user | see the entire to-do list | know the number of task/ event that I have
+`* *` | user | mark a task as completed | distinguish between completed and pending tasks
+`* *` |user | be able to mark certain tasks as important | easily distinguish tasks that require attention/action to be taken
+`* *` | user | alias commands to symbols
+`* *` | user | do a live search for commands
+`* *` | user | find task by their tag
+`*` | user | unmark a completed task| change my mind if that task is actullay not completed
+`*` | user | unpin a pinned task| change my mind if that task is not important anymore
+
+## Appendix B : Use Cases
+
+The activity diagram below shows the simple flow of getting command from user
+
+
+(For all use cases below, the **System** is the `MESS` and the **Actor** is the `user`, unless specified otherwise)
+
+#### Use case: Add task
+
+**MSS**
+
+1. User requests to add a task
+2. MESS adds the task to the list
+Use case ends.
+
+**Extensions**
+
+1a. The input command format is wrong
+> MESS shows an error message with correct input format
+Use case ends.
+
+1b. The task name already exists on the list.
+> MESS shows a message to inform user that task already exists
+Use case ends.
+```
+###### \DeveloperGuide.md
+``` md
+#### Use case: Delete task by index
+
+**MSS**
+
+1. User requests to list tasks
+2. MESS shows a list of tasks
+3. User requests to delete a specific task in the list
+4. MESS deletes the task
+Use case ends.
+
+**Extensions**
+
+1a. The list is empty
+
+> Use case ends
+
+3a. The given index is invalid
+
+>MESS shows an error message
+ Use case ends.
+
+#### Use case: Update task
+
+**MSS**
+
+1. User requests to list tasks
+2. MESS shows a list of tasks
+3. User requests to update a specific task by index in the list
+4. MESS updates the task
+Use case ends.
+
+**Extensions**
+
+1a. The list is empty
+
+> User case ends
+
+3a. The given index is invalid
+
+>MESS shows an error message
+ User case ends.
+
+#### Use case: Pin task
+
+**MSS**
+
+1. User requests to list tasks
+2. MESS shows a list of tasks
+3. User requests to pin a specific task by index in the list
+4. MESS pins the task
+Use case ends.
+
+**Extensions**
+
+1a. The list is empty
+
+> User case ends
+
+3a. The given index is invalid
+
+>MESS shows an error message
+ User case ends.
+
+#### Use case: Unpin task
+
+**MSS**
+
+1. User requests to list tasks
+2. MESS shows a list of tasks
+3. User requests to unpin a pinned task by index in the list
+4. MESS unpins the task
+Use case ends.
+
+**Extensions**
+
+1a. The list is empty
+
+> User case ends
+
+3a. The given index is invalid
+
+> MESS shows an error message
+ User case ends.
+
+3b. The task is not pinned before
+
+> MESS shows an error message
+ User case ends.
+
+#### Use case: Complete task
+
+**MSS**
+
+1. User requests to list tasks
+2. MESS shows a list of tasks
+3. User requests to mark a specific task as completed by index in the list
+4. MESS marks the task
+Use case ends.
+
+**Extensions**
+
+1a. The list is empty
+
+> User case ends
+
+3a. The given index is invalid
+
+>MESS shows an error message
+ User case ends.
+
+#### Use case: Uncomplete task
+
+**MSS**
+
+1. User requests to list tasks
+2. MESS shows a list of tasks
+3. User requests to mark a completed task as not completed by index in the list
+4. MESS marks the task back to not completed
+Use case ends.
+
+**Extensions**
+
+1a. The list is empty
+
+> User case ends
+
+3a. The given index is invalid
+
+>MESS shows an error message
+ User case ends.
+
+3b. The task is not marked as completed before
+
+>MESS shows an error message
+ User case ends.
+
+#### Use case: Undo previous action
+
+**MSS**
+
+1. User requests to undo
+2. MESS undo the previous action
+Use case ends.
+
+**Extensions**
+
+1a. There is no previous action
+
+> Use case ends
+```
+###### \DeveloperGuide.md
+``` md
+## Appendix C : Non Functional Requirements
+
+1. Should work on any mainstream OS as long as it has Java `1.8.0_60` or higher installed
+2. Should be able to process user’s request and show result in 3 seconds
+3. Should be user-friendly for both beginners and advanced users
+4. Should be able to hold up to 1000 tasks
+5. Should be able to work offline
+6. Should only input by keyboard for command line
+7. Should be able to change the storage location of data file
+8. Should come with automated unit tests and open source code
+
+## Appendix D : Glossary
+
+##### Mainstream OS
+
+> Windows, Linux, Unix, OS-X
+
+##### Floating Tasks
+
+> A task that does not have open time and close time
+
+```
+###### \DeveloperGuide.md
+``` md
+### Todo.txt (laptop version)
+#### Strength:
+1. Simple editors(use CLI) to manage tasks
+2. Can be used in every text editor, regardless of OS
+3. Controlled by keyboard shortcuts
+4. Able to show what’s the next important thing to get done
+5. Can add the place or situation on your tasks
+6. File contents is human-readable without requiring any tools other than plain text viewer
+7. A single line in text file represents a single task which is easy to manage
+8. Priority can be set for each task
+9. Can be extended to add-ons
+10. Can be connected to Dropbox
+
+#### Weakness:
+1. UI does not look good
+2. All the output will only be shown in CLI format (no colour, no font change)
+3. Need to update whether the task has finished or not by the user
+4. No notification for task near deadline
+5. Require users to remember too many commands
+
+### Wunderlist (Free version)
+
+#### Strength
+1. Easy to use
+2. Cross-platform application
+3. Notification for tasks set by users
+4. Share to-do list with others
+5. Use hashtag to categorize tasks
+6. Plug-in for Microsoft Outlook and Google Calendar
+7. Can take notes (not only task)
+
+#### Weakness
+1. Cannot create task using one line command
+2. Have limited number subtasks (only premium version has unlimited)
+3. Cannot customize the interface (only premium version can do)
+
+### Todoist (Free version)
+#### Strength
+1. Simple to use
+2. Can share tasks with others
+3. Recurring events
+4. Have both online and offline access
+5. Have different priority level for tasks
+6. Can track your productivity and visualize your achievement trends over time.
+
+#### Weakness
+1. tasks for free version
+2. Cannot use label to categorize tasks for free version
+3. Synchronization to different platform is only available for premium version
+
+#### Summary
+
+In summary, there are a few strengths that the existing products have. They all have simple interfaces so that users can look at their to-do lists in a clear way. Many of them have notifications and priority which can be set for each task which is good for user to find urgent task. Categorize tasks is another key point for most of the products. This can let users to find out the relevant tasks easily. One of the existed product is quite similar to our application which are using one-line command to control the application. However, the interface of this software need to be improved. Therefore, interface, having priority for tasks and tags for tasks are some important features.
+```
+###### \UserGuide.md
+``` md
+## Introduction
+
+Nowadays, everyone has so many tasks to do. Have you ever missed your deadlines or forgotten your schedule? If so, MESS is here to help you! MESS is a to-do list application which reminds you of your tasks or events by showing your list of tasks on the application. No need to be afraid of forgetting your tasks anymore! You can easily use MESS through your keyboard by typing only simple and short commands. MESS can be used offline and even on your office computer. It helps you manage and organise both your tasks and your time.
+
+Want to use MESS immediately? Let's get started!
+```
+###### \UserGuide.md
+``` md
+#### Marking a task as completed: `complete`
+Marks a specific task by index from the to-do list.
+Format: `complete INDEX`
+
+> * INDEX refers to the number appears on the list in front the task name.
+
+
+
+Example:
+* `complete 2`
+ Marks the second task on the list as completed.
+
+#### Unmarking a completed task as not completed: `uncomplete`
+Unmarks a completed specific task as not completed by index from the to-do list.
+Format: `uncomplete INDEX`
+
+> * INDEX refers to the number appears on the list in front the task name.
+
+Example:
+* `uncomplete 2`
+ Unmarks the completed second task on the list as not completed.
+
+#### Pin a task as important: `pin`
+Pin an important task.
+Format: `pin INDEX`
+
+> * INDEX refers to the number appears on the list in front the task name.
+
+
+
+Example:
+* `pin 1`
+pin the first task to show that it is an important task.
+
+#### Unpin a pinned task: `unpin`
+Unpin a previously pinned task.
+Format: `unpin INDEX`
+
+> * INDEX refers to the number appears on the list in front the task name.
+
+Example:
+* `unpin 1`
+unpin the pinned and first task on the list.
+```
+###### \UserGuide.md
+``` md
+#### Undo action : `undo`
+Undo the previous action.
+Format: `undo`
+
+> * Will only undo `add`, `delete` and `update` actions.
+```
+###### \UserGuide.md
+``` md
+## FAQ
+**Q**: Can I add event which have a start date and time to my to-do list ?
+
+**A**: Yes, you can create an event by typing command with a start and end date. For example, you have a trip from 10/10/2016 8:00 to 13/10/2016 21:00. You can type command like this: `add trip starts 8am 10th October ends 9pm 13th October`.
+
+**Q**: If I don't know the deadline of my task yet, can I still add my task?
+
+**A**: Yes, you can still add your task. You can create a floating task by only type in command `add TASK_NAME` if you don't know the deadline of your task.
+
+
+
+In this example, you can see shopping is a floating task without a start time and end time.
+
+**Q**: What can I do if I forget those commands?
+
+**A**: You can use the help command or click help tab on the top which will give you a list of command summary. Even if you type the wrong command format, MESS will show you the correct command hints.
+```
diff --git a/collated/main/A0141052Y.md b/collated/main/A0141052Y.md
new file mode 100644
index 000000000000..05f4bd4ab93e
--- /dev/null
+++ b/collated/main/A0141052Y.md
@@ -0,0 +1,1350 @@
+# A0141052Y
+###### \java\seedu\task\commons\events\ui\SwitchCommandBoxFunctionEvent.java
+``` java
+package seedu.task.commons.events.ui;
+
+import seedu.task.commons.events.BaseEvent;
+
+/**
+ * Indicates that the CommandBox functionality should switch
+ * @author Syed Abdullah
+ *
+ */
+public class SwitchCommandBoxFunctionEvent extends BaseEvent {
+
+ public SwitchCommandBoxFunctionEvent() { }
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName();
+ }
+
+}
+```
+###### \java\seedu\task\logic\commands\FindTagCommand.java
+``` java
+package seedu.task.logic.commands;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import seedu.task.commons.exceptions.IllegalValueException;
+import seedu.task.model.tag.Tag;
+
+public class FindTagCommand extends Command {
+
+ public static final String COMMAND_WORD = "find-tags";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all tasks that contains all of the specified tags (case-sensitive).\n"
+ + "Parameters: TAG_NAME [MORE_TAG_NAMES]...\n"
+ + "Example: " + COMMAND_WORD + " homework cs1101s";
+
+ private final Set tags;
+
+ /**
+ * Constructs a find-tags command
+ * @param tagNames a list of tag names to search for
+ * @throws IllegalValueException thrown if the tag names are not valid
+ */
+ public FindTagCommand(String[] tagNames) throws IllegalValueException {
+ this.tags = new HashSet();
+ for (String tagName : tagNames) {
+ this.tags.add(new Tag(tagName));
+ }
+ }
+
+ @Override
+ public CommandResult execute() {
+ model.updateFilteredListByTags(tags);
+ return new CommandResult(true, getMessageForTaskListShownSummary(model.getFilteredTaskList().size()));
+ }
+}
+```
+###### \java\seedu\task\logic\commands\ListCommand.java
+``` java
+package seedu.task.logic.commands;
+
+import seedu.task.model.Model;
+
+/**
+ * Lists all tasks in the task list to the user.
+ */
+public class ListCommand extends Command {
+
+ public static final String COMMAND_WORD = "list";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Lists a subset of the tasks in the list "
+ + "Parameters: [all|pinned|pending|completed|overdue]...\n"
+ + "Example: " + COMMAND_WORD
+ + " ALL";
+
+ public static final String MESSAGE_SUCCESS = "Filtered tasks listed.";
+
+ private final Model.FilterType listFilter;
+
+ public ListCommand(Model.FilterType filter) {
+ this.listFilter = filter;
+ }
+
+ @Override
+ public CommandResult execute() {
+ model.updateFilteredList(listFilter);
+ return new CommandResult(true, MESSAGE_SUCCESS);
+ }
+}
+```
+###### \java\seedu\task\logic\commands\SearchCommand.java
+``` java
+package seedu.task.logic.commands;
+
+import seedu.task.commons.core.EventsCenter;
+import seedu.task.commons.events.ui.SwitchCommandBoxFunctionEvent;
+
+public class SearchCommand extends Command {
+
+ public static final String MESSAGE_SEARCH_SUCCESS = "Live Search activated!";
+
+ @Override
+ public CommandResult execute() {
+ EventsCenter.getInstance().post(new SwitchCommandBoxFunctionEvent());
+ return new CommandResult(true, MESSAGE_SEARCH_SUCCESS);
+ }
+}
+```
+###### \java\seedu\task\logic\Logic.java
+``` java
+ /**
+ * Updates the task list filter with the specified keyword
+ * @param keyword to be used to filter the tasks
+ */
+ void updateTaskListFilter(String keyword);
+```
+###### \java\seedu\task\logic\LogicManager.java
+``` java
+ @Override
+ public void updateTaskListFilter(String keyword) {
+ model.updateFilteredTaskList(new HashSet(Arrays.asList(keyword)));
+ }
+
+ private void setPreviousCommand(boolean isSuccessful, Command command) {
+ if (isSuccessful && command instanceof UndoableCommand) {
+ this.previousCommand = (UndoableCommand) command;
+ } else {
+ this.previousCommand = null;
+ }
+ }
+}
+```
+###### \java\seedu\task\logic\parser\AddParser.java
+``` java
+package seedu.task.logic.parser;
+
+import static seedu.task.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.StringJoiner;
+
+
+import seedu.task.commons.exceptions.IllegalValueException;
+import seedu.task.logic.commands.AddCommand;
+import seedu.task.logic.commands.Command;
+import seedu.task.logic.commands.IncorrectCommand;
+
+public class AddParser extends BaseParser {
+
+ private final static String FLAG_NAME = "name";
+ private final static String FLAG_START_TIME = "starts";
+ private final static String FLAG_CLOSE_TIME = "ends";
+ private final static String FLAG_TAGS = "tag";
+
+ private final static String[] KEYWORD_ARGS_REQUIRED = new String[]{FLAG_NAME};
+ private final static String[] KEYWORD_ARGS_OPTIONAL = new String[]{FLAG_START_TIME,
+ FLAG_CLOSE_TIME,
+ FLAG_TAGS
+ };
+
+ @Override
+ public Command parse(String command, String arguments) {
+ this.extractArguments(arguments);
+ if (!checkForRequiredArguments(KEYWORD_ARGS_REQUIRED, KEYWORD_ARGS_OPTIONAL, true)) {
+ return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE));
+ }
+
+ try {
+ return new AddCommand(getSingleKeywordArgValue(FLAG_NAME),
+ getSingleKeywordArgValue(FLAG_START_TIME),
+ getSingleKeywordArgValue(FLAG_CLOSE_TIME),
+ getTags());
+ } catch (IllegalValueException ive) {
+ return new IncorrectCommand(ive.getMessage());
+ }
+ }
+
+ private Set getTags() {
+ if (argumentsTable.containsKey(FLAG_TAGS)) {
+ return new HashSet<>(argumentsTable.get(FLAG_TAGS));
+ } else {
+ return new HashSet<>();
+ }
+ }
+
+```
+###### \java\seedu\task\logic\parser\BaseParser.java
+``` java
+package seedu.task.logic.parser;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Optional;
+import java.util.StringJoiner;
+
+import seedu.task.commons.util.StringUtil;
+import seedu.task.logic.commands.Command;
+
+public abstract class BaseParser {
+
+ protected final HashMap> argumentsTable = new HashMap<>();
+
+ /**
+ * Extracts out arguments from the user's input into a HashMap.
+ * The value mapped to the empty string ("") is the non-keyword argument.
+ *
+ * @param args full (or partial) user input arguments
+ */
+ protected void extractArguments(String args) {
+ argumentsTable.clear();
+ String[] segments = args.trim().split(" ");
+ String currentKey = "";
+ StringJoiner joiner = new StringJoiner(" ");
+
+ for (String segment : segments) {
+ joiner.add(segment);
+
+ }
+
+ addToArgumentsTable(currentKey, joiner.toString());
+ }
+
+ /**
+ * Assigns a value to a keyword argument. Does not replace any existing
+ * values associated with the keyword.
+ * @param keyword the keyword of the argument
+ * @param value the value the argument is set to
+ */
+ protected void addToArgumentsTable(String keyword, String value) {
+ ArrayList arrayItems;
+ if (argumentsTable.containsKey(keyword)) {
+ arrayItems = argumentsTable.get(keyword);
+ } else {
+ arrayItems = new ArrayList();
+ }
+
+ arrayItems.add(value);
+ argumentsTable.put(keyword, arrayItems);
+ }
+
+ /***
+ * Checks if the required keyword arguments were supplied by the user
+ * @param requiredArgs list of keyword arguments
+ * @param optionalArgs list of arguments that may appear
+ * @param isStrictSet does not allow for other keyword arguments
+ * @return true if required arguments were supplied, else false
+ */
+ protected boolean checkForRequiredArguments(String[] requiredArgs, String[] optionalArgs, boolean isStrictSet) {
+ for (String arg : requiredArgs) {
+ if (!argumentsTable.containsKey(arg)) {
+ return false;
+ } else {
+ if (argumentsTable.get(arg).get(0).isEmpty()) {
+ return false;
+ }
+ }
+ }
+
+ int numOptional = 0;
+ for (String arg : optionalArgs) {
+ if (argumentsTable.containsKey(arg)) {
+ numOptional++;
+ }
+ }
+
+ if (isStrictSet) {
+ return argumentsTable.size() == numOptional + requiredArgs.length;
+ } else {
+ return argumentsTable.size() >= numOptional + requiredArgs.length;
+ }
+ }
+
+ /**
+ * Retrieves the value for the keyword argument
+ * @param keyword the keyword of the argument
+ * @return the current value of the keyword argument
+ */
+ protected String getSingleKeywordArgValue(String keyword) {
+ if (argumentsTable.containsKey(keyword)) {
+ return argumentsTable.get(keyword).get(0);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns a positive integer, if the user supplied unnamed keyword argument is a positive integer.
+ * Returns an {@code Optional.empty()} otherwise.
+ */
+ protected Optional parseIndex() {
+ String index = getSingleKeywordArgValue("");
+ return parseIndex(index);
+ }
+
+ protected Optional parseIndex(String index) {
+ if (!StringUtil.isUnsignedInteger(index)) {
+ return Optional.empty();
+ }
+ return Optional.of(Integer.parseInt(index));
+ }
+
+ /**
+ * Parses the user's input and determines the appropriate command
+ * @param userInput full user input string
+ * @return the command based on the user input
+ */
+ public abstract Command parse(String command, String arguments);
+}
+```
+###### \java\seedu\task\logic\parser\ClearParser.java
+``` java
+package seedu.task.logic.parser;
+
+import seedu.task.logic.commands.ClearCommand;
+import seedu.task.logic.commands.Command;
+
+public class ClearParser extends BaseParser {
+ @Override
+ public Command parse(String command, String arguments) {
+ return new ClearCommand();
+ }
+}
+```
+###### \java\seedu\task\logic\parser\CompleteParser.java
+``` java
+package seedu.task.logic.parser;
+
+import seedu.task.logic.commands.CompleteCommand;
+import seedu.task.logic.commands.IncorrectCommand;
+
+import static seedu.task.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import java.util.Optional;
+
+import seedu.task.logic.commands.Command;
+
+public class CompleteParser extends BaseParser {
+ @Override
+ public Command parse(String command, String arguments) {
+ this.extractArguments(arguments);
+ Optional idx = parseIndex();
+
+ if (!idx.isPresent()) {
+ return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, CompleteCommand.MESSAGE_USAGE));
+ }
+
+ return new CompleteCommand(idx.get());
+ }
+}
+```
+###### \java\seedu\task\logic\parser\DeleteParser.java
+``` java
+package seedu.task.logic.parser;
+
+import static seedu.task.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import java.util.Optional;
+
+import seedu.task.logic.commands.Command;
+import seedu.task.logic.commands.DeleteCommand;
+import seedu.task.logic.commands.IncorrectCommand;
+
+public class DeleteParser extends BaseParser {
+ @Override
+ public Command parse(String command, String arguments) {
+ this.extractArguments(arguments);
+
+ Optional index = parseIndex();
+ if (!index.isPresent()) {
+ return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE));
+ }
+
+ return new DeleteCommand(index.get());
+ }
+}
+```
+###### \java\seedu\task\logic\parser\ExitParser.java
+``` java
+package seedu.task.logic.parser;
+
+import seedu.task.logic.commands.Command;
+import seedu.task.logic.commands.ExitCommand;
+
+public class ExitParser extends BaseParser {
+ @Override
+ public Command parse(String command, String arguments) {
+ return new ExitCommand();
+ }
+}
+```
+###### \java\seedu\task\logic\parser\FindParser.java
+``` java
+package seedu.task.logic.parser;
+
+import static seedu.task.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import seedu.task.logic.commands.Command;
+import seedu.task.logic.commands.FindCommand;
+import seedu.task.logic.commands.IncorrectCommand;
+
+public class FindParser extends BaseParser {
+ @Override
+ public Command parse(String command, String arguments) {
+ // keywords delimited by whitespace
+
+ if (arguments.trim().isEmpty()) {
+ return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE));
+ }
+
+ final String[] keywords = arguments.split("\\s+");
+ final Set keywordSet = new HashSet<>(Arrays.asList(keywords));
+ return new FindCommand(keywordSet);
+ }
+}
+```
+###### \java\seedu\task\logic\parser\FindTagParser.java
+``` java
+package seedu.task.logic.parser;
+
+import static seedu.task.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import seedu.task.commons.exceptions.IllegalValueException;
+import seedu.task.logic.commands.Command;
+import seedu.task.logic.commands.FindTagCommand;
+import seedu.task.logic.commands.IncorrectCommand;
+
+public class FindTagParser extends BaseParser {
+
+ @Override
+ public Command parse(String command, String arguments) {
+ if (arguments.trim().isEmpty()) {
+ return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindTagCommand.MESSAGE_USAGE));
+ }
+
+ final String[] tagNames = arguments.split("\\s+");
+ try {
+ return new FindTagCommand(tagNames);
+ } catch (IllegalValueException ive) {
+ return new IncorrectCommand(ive.getMessage());
+ }
+ }
+
+}
+```
+###### \java\seedu\task\logic\parser\HelpParser.java
+``` java
+package seedu.task.logic.parser;
+
+import seedu.task.logic.commands.Command;
+import seedu.task.logic.commands.HelpCommand;
+
+public class HelpParser extends BaseParser {
+ @Override
+ public Command parse(String command, String arguments) {
+ return new HelpCommand();
+ }
+}
+```
+###### \java\seedu\task\logic\parser\ListParser.java
+``` java
+package seedu.task.logic.parser;
+
+import static seedu.task.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import seedu.task.logic.commands.Command;
+import seedu.task.logic.commands.IncorrectCommand;
+import seedu.task.logic.commands.ListCommand;
+import seedu.task.model.Model.FilterType;
+
+public class ListParser extends BaseParser {
+
+ private final String FLAG_LIST_TYPE = "";
+
+ private final String[] KEYWORD_ARGS_REQUIRED = new String[]{FLAG_LIST_TYPE};
+ private final String[] KEYWORD_ARGS_OPTIONAL = new String[]{};
+
+ @Override
+ public Command parse(String command, String arguments) {
+ this.extractArguments(arguments);
+ if (!this.checkForRequiredArguments(KEYWORD_ARGS_REQUIRED, KEYWORD_ARGS_OPTIONAL, true)) {
+ return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, ListCommand.MESSAGE_USAGE));
+ }
+
+ FilterType filter = FilterType.ALL;
+
+ switch (getSingleKeywordArgValue(FLAG_LIST_TYPE)) {
+ case "all":
+ break;
+ case "pinned":
+ filter = FilterType.PIN;
+ break;
+ case "pending":
+ filter = FilterType.PENDING;
+ break;
+ case "completed":
+ filter = FilterType.COMPLETED;
+ break;
+ case "overdue":
+ filter = FilterType.OVERDUE;
+ break;
+ default:
+ return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, ListCommand.MESSAGE_USAGE));
+ }
+
+ return new ListCommand(filter);
+ }
+}
+```
+###### \java\seedu\task\logic\parser\ParserMapping.java
+``` java
+package seedu.task.logic.parser;
+
+import java.util.HashMap;
+import java.util.Optional;
+import java.util.logging.Logger;
+import seedu.task.commons.core.LogsCenter;
+import seedu.task.commons.logic.CommandKeys;
+import seedu.task.commons.logic.CommandKeys.Commands;
+
+/**
+ * Provides command word and alias mappings
+ * @author Syed Abdullah
+ *
+ */
+public class ParserMapping {
+ private HashMap> mappingTable = new HashMap<>();
+ private final Logger logger = LogsCenter.getLogger(ParserMapping.class);
+ private final HashMap aliasMappings;
+
+ public ParserMapping(HashMap aliasMappings) {
+ populateMappings();
+ this.aliasMappings = aliasMappings;
+ }
+
+ /**
+ * Populates the command word to command parsers mapping table
+ */
+ private void populateMappings() {
+ mappingTable.put(Commands.ADD, AddParser.class);
+ mappingTable.put(Commands.ALIAS, AliasParser.class);
+ mappingTable.put(Commands.CLEAR, ClearParser.class);
+ mappingTable.put(Commands.COMPLETE, CompleteParser.class);
+ mappingTable.put(Commands.CHANGE_TO, ChangePathParser.class);
+ mappingTable.put(Commands.DELETE, DeleteParser.class);
+ mappingTable.put(Commands.EXIT, ExitParser.class);
+ mappingTable.put(Commands.FIND, FindParser.class);
+ mappingTable.put(Commands.FIND_TAG, FindTagParser.class);
+ mappingTable.put(Commands.HELP, HelpParser.class);
+ mappingTable.put(Commands.LIST, ListParser.class);
+ mappingTable.put(Commands.PIN, PinParser.class);
+ mappingTable.put(Commands.SEARCH_BOX, SearchParser.class);
+ mappingTable.put(Commands.SELECT, SelectParser.class);
+ mappingTable.put(Commands.UNDO, UndoParser.class);
+ mappingTable.put(Commands.UPDATE, UpdateParser.class);
+ mappingTable.put(Commands.UNCOMPLETE, UncompleteParser.class);
+ mappingTable.put(Commands.UNPIN, UnpinParser.class);
+ }
+
+ /**
+ * Retrieves commands for a specified keyword
+ * @param commandWord
+ * @return
+ */
+ public Optional> getParserForCommand(String commandWord) {
+
+ //check if it's an alias
+ if(aliasMappings.containsKey(commandWord) && aliasMappings.get(commandWord) != null) {
+ Commands command = aliasMappings.get(commandWord);
+ return Optional.of(mappingTable.get(command));
+ }
+
+ if (CommandKeys.commandKeyMap.containsKey(commandWord) && CommandKeys.commandKeyMap.get(commandWord) != null) {
+ Commands command = CommandKeys.commandKeyMap.get(commandWord);
+ return Optional.of(mappingTable.get(command));
+ } else {
+ logger.info("[USER COMMAND][" + commandWord + "] not found!");
+ return Optional.empty();
+ }
+ }
+}
+```
+###### \java\seedu\task\logic\parser\ParseSwitcher.java
+``` java
+package seedu.task.logic.parser;
+
+import static seedu.task.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND;
+import static seedu.task.commons.core.Messages.MESSAGE_INTERNAL_ERROR;
+
+import java.util.HashMap;
+import java.util.Optional;
+
+import seedu.task.commons.logic.CommandKeys.Commands;
+import seedu.task.logic.commands.Command;
+import seedu.task.logic.commands.IncorrectCommand;
+
+/**
+ * ParseSwitcher -- parses the raw command from user and delegates the
+ * parsing to the specific command parsers for further parsing.
+ *
+ * @author Syed Abdullah
+ *
+ */
+public class ParseSwitcher {
+ private final ParserMapping parserMappings;
+
+ public ParseSwitcher(HashMap aliasMappings) {
+ parserMappings = new ParserMapping(aliasMappings);
+
+ }
+
+ /**
+ * Parses the user's input and determines the appropriate command
+ * @param userInput full user input string
+ * @return the command based on the user input
+ */
+ public Command parseCommand(String userInput) {
+ String[] commandSegments = userInput.split(" ", 2);
+ final String commandWord = (commandSegments.length > 0) ? commandSegments[0] : "";
+ final String commandArgs = (commandSegments.length > 1) ? commandSegments[1] : "";
+
+ Optional> selectedParser = parserMappings.getParserForCommand(commandSegments[0]);
+
+ if (!selectedParser.isPresent()) {
+ return new IncorrectCommand(MESSAGE_UNKNOWN_COMMAND);
+ } else {
+ BaseParser parser;
+ try {
+ parser = selectedParser.get().newInstance();
+ return parser.parse(commandWord, commandArgs);
+ } catch (InstantiationException | IllegalAccessException e) {
+ e.printStackTrace();
+ return new IncorrectCommand(MESSAGE_INTERNAL_ERROR);
+ }
+ }
+ }
+}
+```
+###### \java\seedu\task\logic\parser\PinParser.java
+``` java
+package seedu.task.logic.parser;
+
+import static seedu.task.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import java.util.Optional;
+
+import seedu.task.logic.commands.Command;
+import seedu.task.logic.commands.IncorrectCommand;
+import seedu.task.logic.commands.PinCommand;
+
+public class PinParser extends BaseParser {
+ @Override
+ public Command parse(String command, String arguments) {
+ this.extractArguments(arguments);
+
+ Optional index = parseIndex();
+ if (!index.isPresent()) {
+ return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, PinCommand.MESSAGE_USAGE));
+ }
+ return new PinCommand(index.get());
+ }
+}
+```
+###### \java\seedu\task\logic\parser\SearchParser.java
+``` java
+package seedu.task.logic.parser;
+
+import seedu.task.logic.commands.Command;
+import seedu.task.logic.commands.SearchCommand;
+
+public class SearchParser extends BaseParser {
+
+ @Override
+ public Command parse(String command, String arguments) {
+ return new SearchCommand();
+ }
+
+}
+```
+###### \java\seedu\task\logic\parser\SelectParser.java
+``` java
+package seedu.task.logic.parser;
+
+import static seedu.task.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import java.util.Optional;
+
+import seedu.task.logic.commands.Command;
+import seedu.task.logic.commands.IncorrectCommand;
+import seedu.task.logic.commands.SelectCommand;
+
+public class SelectParser extends BaseParser {
+ @Override
+ public Command parse(String command, String arguments) {
+ this.extractArguments(arguments);
+
+ Optional index = parseIndex();
+ if (!index.isPresent()) {
+ return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, SelectCommand.MESSAGE_USAGE));
+ }
+
+ return new SelectCommand(index.get());
+ }
+}
+```
+###### \java\seedu\task\logic\parser\UndoParser.java
+``` java
+package seedu.task.logic.parser;
+
+import seedu.task.logic.commands.Command;
+import seedu.task.logic.commands.UndoCommand;
+
+public class UndoParser extends BaseParser {
+ @Override
+ public Command parse(String command, String arguments) {
+ return new UndoCommand();
+ }
+}
+```
+###### \java\seedu\task\logic\parser\UpdateParser.java
+``` java
+package seedu.task.logic.parser;
+
+import static seedu.task.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import java.util.HashSet;
+import java.util.Optional;
+import java.util.Set;
+import java.util.StringJoiner;
+
+import seedu.task.commons.exceptions.IllegalValueException;
+import seedu.task.logic.commands.Command;
+import seedu.task.logic.commands.IncorrectCommand;
+import seedu.task.logic.commands.UpdateCommand;
+
+public class UpdateParser extends BaseParser {
+ private final static String FLAG_NAME = "name";
+ private final static String FLAG_START_TIME = "starts";
+ private final static String FLAG_CLOSE_TIME = "ends";
+ private final static String FLAG_TAGS = "tag";
+ private final static String FLAG_REMOVE_TAGS = "remove-tag";
+ private final static String[] KEYWORD_ARGS_OPTIONAL = new String[]{
+ FLAG_NAME,
+ FLAG_START_TIME,
+ FLAG_CLOSE_TIME,
+ FLAG_TAGS,
+ FLAG_REMOVE_TAGS
+ };
+```
+###### \java\seedu\task\model\history\ListMutation.java
+``` java
+package seedu.task.model.history;
+
+import java.util.HashMap;
+import java.util.Map.Entry;
+import java.util.Set;
+
+/**
+ * Represents a state change for an indexed List
+ * @author Syed Abdullah
+ *
+ * @param
+ */
+public class ListMutation {
+ private final HashMap> mutationMap;
+
+ /**
+ * Constructs an empty ListMutation
+ */
+ public ListMutation() {
+ this.mutationMap = new HashMap>();
+ }
+
+ /**
+ * Add a Mutation of a specified index in the list
+ *
+ * @param index the index of the element in the list
+ * @param mutation the Mutation of the element in the list
+ */
+ public void addMutation(int index, Mutation mutation) {
+ this.mutationMap.put(index, mutation);
+ }
+
+ /**
+ * Adds a new Mutation for an element, treating it as a new mutation for the list
+ *
+ * @param index the index of the element in the list
+ * @param mutation the Mutation of the element in the list
+ */
+ public void addAsNewMutation(int index, Mutation mutation) {
+ this.clear();
+ this.addMutation(index, mutation);
+ }
+
+ /**
+ * Adds new Mutations for consecutive elements inserted into list. Method will create a new Mutation
+ *
+ * @param startIndex the starting position (inclusive)
+ * @param newElements the array of elements that are added
+ */
+ public void addNewElements(int startIndex, T[] newElements) {
+ this.clear();
+ for (int i = startIndex; i < newElements.length; i++) {
+ this.mutationMap.put(i, new Mutation(null, newElements[i - startIndex]));
+ }
+ }
+
+ /**
+ * Mutates an element in the specified index to the next state
+ *
+ * @param index the index of the element in the list
+ * @param currentState the current state of the element
+ * @param nextState the next state of the element
+ */
+ public void mutateElement(int index, T nextState) {
+ Mutation newMutation;
+ if (this.mutationMap.containsKey(index)) {
+ Mutation currentMutation = this.mutationMap.get(index);
+ newMutation = currentMutation.transitionToNextState(nextState);
+ } else {
+ newMutation = new Mutation(null, nextState);
+ }
+ this.mutationMap.put(index, newMutation);
+ }
+
+ /**
+ * Retrieves a mapping of element mutations for the list
+ * @return set of element mutations entries for the list
+ */
+ public Set>> getMutations() {
+ return this.mutationMap.entrySet();
+ }
+
+ /**
+ * Checks if there is any mutations that are recorded.
+ *
+ * @return true if there is any mutation, else false
+ */
+ public boolean hasMutation() {
+ return !this.mutationMap.isEmpty();
+ }
+
+ /**
+ * Clears the ListMutation
+ */
+ public void clear() {
+ this.mutationMap.clear();
+ }
+}
+```
+###### \java\seedu\task\model\history\Mutation.java
+``` java
+package seedu.task.model.history;
+
+/**
+ * Represents an object state change.
+ * @author Syed Abdullah
+ *
+ * @param the object's Class
+ */
+public class Mutation {
+
+ final private T previousState;
+ final private T presentState;
+
+ /**
+ * Constructs a Mutation.
+ *
+ * @param previousState the state of the instance before mutation
+ * @param presentState the state of the instance after mutation
+ */
+ public Mutation(T previousState, T presentState) {
+ this.previousState = previousState;
+ this.presentState = presentState;
+ }
+
+ /**
+ * Get the state before mutation
+ * @return state of object before mutation took place
+ */
+ public T getPreviousState() {
+ return previousState;
+ }
+
+ /**
+ * Get the state after mutation
+ * @return state of object before mutation took place
+ */
+ public T getPresentState() {
+ return presentState;
+ }
+
+ /**
+ * Transitions the current mutation to the next mutation
+ *
+ * @param state the next state to transit to
+ * @return a new Mutation from the current state to the next state
+ */
+ public Mutation transitionToNextState(T state) {
+ return new Mutation(this.getPresentState(), state);
+ }
+
+ /**
+ * Reverses the mutation (e.g. A->B becomes B->A)
+ * @return a reversed mutation
+ */
+ public Mutation reverse() {
+ return new Mutation(this.getPresentState(), this.getPreviousState());
+ }
+}
+```
+###### \java\seedu\task\model\Model.java
+``` java
+ /** Updates the filter of the filtered task list to show based on the preset **/
+ void updateFilteredList(FilterType filter);
+
+ /** Updates the filter to show based on a list of tags provided **/
+ void updateFilteredListByTags(Set tags);
+```
+###### \java\seedu\task\model\ModelManager.java
+``` java
+ @Override
+ public void updateFilteredList(FilterType filter) {
+
+ updateFilteredListToShowAll();
+
+ switch (filter) {
+ case ALL:
+ updateFilteredListToShowAll();
+ break;
+
+ case PIN:
+ updateFilteredTaskList(new PredicateExpression(new PinQualifier(true)));
+ break;
+
+ case COMPLETED:
+ updateFilteredTaskList(new PredicateExpression(new CompletedQualifier(true)));
+ break;
+
+ case PENDING:
+ updateFilteredTaskList(new PredicateExpression(new CompletedQualifier(false)));
+ break;
+
+ case OVERDUE:
+ DateTime now = DateTime.fromEpoch(System.currentTimeMillis());
+ updateFilteredTaskList(new PredicateExpression(new DueDateQualifier(now)));
+ break;
+
+ default:
+ // does nothing
+ break;
+ }
+ }
+
+ public void updateFilteredListByTags(Set tags) {
+ updateFilteredTaskList(new PredicateExpression(new TagQualifier(tags)));
+ }
+```
+###### \java\seedu\task\model\ModelManager.java
+``` java
+ // ========== Methods for sorting ==========================================================================
+
+ private int totalOrderSorting(Task task, Task otherTask) {
+ return task.compareTo(otherTask);
+ }
+```
+###### \java\seedu\task\model\ModelManager.java
+``` java
+ /**
+ * Qualifier that checks for matching tags
+ * @author Syed Abdullah
+ *
+ */
+ private class TagQualifier implements Qualifier {
+ private Set tags;
+
+ TagQualifier(Set tags) {
+ this.tags = tags;
+ }
+
+ @Override
+ public boolean run(ReadOnlyTask task) {
+ return tags.stream()
+ .allMatch(tag -> task.getTags().contains(tag));
+ }
+ }
+
+ /**
+ * Qualifier that checks if Task is not due based on reference time
+ * @author Syed Abdullah
+ *
+ */
+ private class DueDateQualifier implements Qualifier {
+
+ private DateTime referencePoint;
+
+ DueDateQualifier(DateTime reference) {
+ this.referencePoint = reference;
+ }
+
+ @Override
+ public boolean run(ReadOnlyTask task) {
+ DateTime toCompare = task.getCloseTime();
+
+ if (toCompare.isEmpty()) {
+ return false;
+ } else {
+ return (toCompare.compareTo(this.referencePoint) < 1);
+ }
+ }
+ }
+
+ /**
+ * Qualifier to check the Pin property of the underlying Task.
+ * @author Syed Abdullah
+ *
+ */
+ private class PinQualifier implements Qualifier {
+
+ private boolean isPinned;
+
+ PinQualifier(boolean isPinned) {
+ this.isPinned = isPinned;
+ }
+
+ @Override
+ public boolean run(ReadOnlyTask task) {
+ return (task.getImportance() == this.isPinned);
+ }
+ }
+
+ /**
+ * Qualifier to check the Completed property of the underlying Task.
+ * @author Syed Abdullah
+ *
+ */
+ private class CompletedQualifier implements Qualifier {
+
+ private boolean isCompleted;
+
+ CompletedQualifier(boolean isCompleted) {
+ this.isCompleted = isCompleted;
+ }
+
+ @Override
+ public boolean run(ReadOnlyTask task) {
+ return (task.getComplete() == this.isCompleted);
+ }
+ }
+
+```
+###### \java\seedu\task\model\task\DateTime.java
+``` java
+ private static final String DATE_TIME_DISPLAY_FORMAT = "%s (%s)";
+```
+###### \java\seedu\task\model\task\DateTime.java
+``` java
+ /**
+ * Constructs an empty DateTime
+ */
+ public DateTime() {
+ this.value = Optional.empty();
+ }
+
+ /**
+ * Constructs a DateTime from an Instant
+ * @param dateTime the Instant of the time and date to be represented
+ */
+ public DateTime(Instant dateTime) {
+ if (dateTime == null) {
+ this.value = Optional.empty();
+ return;
+ }
+
+ this.value = Optional.of(dateTime.truncatedTo(ChronoUnit.MINUTES));
+ }
+
+ /**
+ * Validates given Date and Time entered by the user.
+ * @param dateTime the String representation of the input from the user
+ * @throws IllegalValueException if given date/time string is invalid.
+ */
+ public static DateTime fromUserInput(String dateTime) throws IllegalValueException {
+ if (dateTime == null || dateTime.equals("")) {
+ return new DateTime(null);
+ }
+
+ if (!isValidDateTime(dateTime)) {
+ throw new IllegalValueException(MESSAGE_DATETIME_CONSTRAINTS);
+ }
+
+ List possibleDates = new PrettyTimeParser().parse(dateTime);
+ return new DateTime(possibleDates.get(0).toInstant());
+ }
+
+ /**
+ * Create a new DateTime object using the number of milliseconds from 01-01-1970
+ * @param epochMilli the number of milliseconds elapsed from the epoch
+ * @return a new DateTime object for the specified epoch offset
+ */
+ public static DateTime fromEpoch(Long epochMilli) {
+ if (epochMilli == null) {
+ return new DateTime(null);
+ }
+
+ return new DateTime(Instant.ofEpochMilli(epochMilli));
+ }
+
+ /**
+ * Creates a new DateTime based off an offset from another DateTime
+ * @param offsetFrom the DateTime to offset from
+ * @param amountToAdd the amount of the specified unit to add to the DateTime
+ * @param unit the time unit of the amount
+ * @return a new DateTime object with a value that is offset from another DateTime
+ */
+ public static DateTime fromDateTimeOffset(DateTime offsetFrom, long amountToAdd, TemporalUnit unit) {
+ if (offsetFrom == null || offsetFrom.isEmpty()) {
+ return new DateTime(null);
+ }
+
+ Instant offsetInstant = offsetFrom.value.get();
+ return new DateTime(offsetInstant.plus(amountToAdd, unit));
+ }
+
+```
+###### \java\seedu\task\model\task\DateTime.java
+``` java
+ /**
+ * Gets a display friendly representation of the DateTime
+ * @return A String containing the display friendly version
+ */
+ public String toDisplayString() {
+ if (this.toString().isEmpty()) {
+ return "";
+ } else {
+ return String.format(DATE_TIME_DISPLAY_FORMAT, this.toString(), this.toPrettyString());
+ }
+ }
+
+ /**
+ * Retrieves an ISO 8601 representation of the DateTime.
+ * @return A String containing the ISO-8601 representation or empty, if there's
+ * no DateTime value
+ */
+ public String toISOString() {
+ if (this.value.isPresent()) {
+ return this.value.get().toString();
+ } else {
+ return "";
+ }
+ }
+
+ public Long getSaveableValue() {
+ if(value.isPresent()) {
+ return this.value.get().toEpochMilli();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Compares between two DateTime instances using Comparable.
+ * Empty DateTimes are ordered behind all possible non-empty DateTimes.
+ */
+ @Override
+ public int compareTo(DateTime o) {
+ Optional time = this.value;
+ Optional otherTime = o.getDateTimeValue();
+
+ if (!time.isPresent() && !otherTime.isPresent()) {
+ return 0;
+ } else if (!time.isPresent()) {
+ return 1;
+ } else if (!otherTime.isPresent()) {
+ return -1;
+ } else {
+ return time.get().compareTo(otherTime.get());
+ }
+ }
+```
+###### \java\seedu\task\model\task\Name.java
+``` java
+ @Override
+ public int compareTo(Name o) {
+ return this.taskName.compareToIgnoreCase(o.taskName);
+ }
+```
+###### \java\seedu\task\model\task\ReadOnlyTask.java
+``` java
+ /**
+ * Equality based on what is shown to the user. Useful for tests.
+ */
+ default boolean isSameVisualStateAs(ReadOnlyTask other) {
+ return other == this // short circuit if same object
+ || (other != null // this is first to avoid NPE below
+
+ // state checks here onwards
+ && other.getName().equals(this.getName())
+ && other.getOpenTime().toDisplayString().equals(this.getOpenTime().toDisplayString())
+ && other.getCloseTime().toDisplayString().equals(this.getCloseTime().toDisplayString())
+ && other.getImportance() == this.getImportance());
+ }
+```
+###### \java\seedu\task\model\task\Task.java
+``` java
+ /**
+ * Retrieves an immutable version of the task. Will not mutate if task is changed afterwards.
+ */
+ public ReadOnlyTask getImmutable() {
+ try {
+ return new Task(this);
+ } catch (IllegalValueException e) {
+ assert false : "Impossible situation, as Task fields has been validated!";
+ return null;
+ }
+ }
+
+```
+###### \java\seedu\task\model\task\Task.java
+``` java
+ /**
+ * Uses Comparator.compareTo.
+ * The comparison is based on:
+ * - Starred tasks have higher priority
+ * - Completed tasks have lower priority
+ * - For tasks with same completion/starred status,
+ * base comparison on endDate and the task name
+ */
+ @Override
+ public int compareTo(Task o) {
+ /*
+ * The (^) operator is also a logical XOR, according to Java Language Standards 15.22.2.
+ * See: https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.22.2
+ */
+ if (this.isCompleted ^ o.getComplete()) {
+ return (this.isCompleted) ? 1 : -1;
+ } else if (this.isImportant ^ o.getImportance()) {
+ return (this.isImportant) ? -1 : 1;
+ }
+
+ int timeComparison = this.closeTime.compareTo(o.getCloseTime());
+
+ if (timeComparison == 0) {
+ return this.name.compareTo(o.getName());
+ } else {
+ return timeComparison;
+ }
+ }
+}
+```
+###### \java\seedu\task\ui\CommandBox.java
+``` java
+ this.liveSearchHandler = new ChangeListener(){
+ @Override
+ public void changed(ObservableValue extends String> observable, String oldValue, String newValue) {
+ Platform.runLater(new Runnable() {
+ @Override public void run() {
+ logic.updateTaskListFilter(newValue);
+ }
+ });
+ }
+ };
+ this.isSearchMode = false;
+```
+###### \java\seedu\task\ui\CommandBox.java
+``` java
+ if (isSearchMode) {
+ // Will always set back on enter and ensure that command is not executed!
+ setToCommandBox();
+ commandTextField.setText("");
+```
+###### \java\seedu\task\ui\CommandBox.java
+``` java
+ @Subscribe
+ private void handleSwitchCommandBoxFunction(SwitchCommandBoxFunctionEvent event) {
+ setToLiveSearch();
+ }
+
+ private void setToLiveSearch() {
+ commandTextField.setPromptText(TEXT_LIVE_SEARCH);
+ this.isSearchMode = true;
+ commandTextField.textProperty().addListener(liveSearchHandler);
+ }
+
+ private void setToCommandBox() {
+ commandTextField.setPromptText(TEXT_COMMAND_BOX);
+ this.isSearchMode = false;
+ commandTextField.textProperty().removeListener(liveSearchHandler);
+ }
+
+```
+###### \java\seedu\task\ui\TabBarItem.java
+``` java
+package seedu.task.ui;
+
+import javafx.fxml.FXML;
+import javafx.scene.Node;
+import javafx.scene.control.Label;
+import javafx.scene.layout.HBox;
+
+public class TabBarItem extends UiPart {
+ private static final String FXML = "TabBarItem.fxml";
+
+ private String tabLabel;
+
+ @FXML
+ private HBox tabBarNameContainer;
+ @FXML
+ private Label tabName;
+
+ public static TabBarItem load(String tabLabel) {
+ TabBarItem tabBar = new TabBarItem();
+ tabBar.tabLabel = tabLabel;
+ return UiPartLoader.loadUiPart(tabBar);
+ }
+
+ @FXML
+ public void initialize() {
+ tabName.setText(tabLabel);
+ }
+
+ public HBox getLayout() {
+ return tabBarNameContainer;
+ }
+
+ @Override
+ public void setNode(Node node) {
+ tabBarNameContainer = (HBox) node;
+ }
+
+ @Override
+ public String getFxmlPath() {
+ return FXML;
+ }
+
+}
+```
diff --git a/collated/main/A0141052Yunused.md b/collated/main/A0141052Yunused.md
new file mode 100644
index 000000000000..ebaae5b97ecd
--- /dev/null
+++ b/collated/main/A0141052Yunused.md
@@ -0,0 +1,25 @@
+# A0141052Yunused
+###### \java\seedu\task\logic\commands\AddCommand.java
+``` java
+ // Recurring tasks scrapped due to lack of time (and unforseen circumstances)
+ /**
+ * Creates a set number of Tasks, with distance between two adjacent tasks being 1 week, and adds it to the
+ * TaskList.
+ * @param timesToRecur number of weeks to recur the Task for
+ * @throws IllegalValueException if there's a duplicate task or if
+ */
+ private void createRecurringTask(int timesToRecur) throws IllegalValueException {
+ for (int i = 0; i < timesToRecur; i++) {
+ DateTime newOpenTime = DateTime.fromDateTimeOffset(toAdd.getOpenTime(), i * 7, ChronoUnit.DAYS);
+ DateTime newCloseTime = DateTime.fromDateTimeOffset(toAdd.getCloseTime(), i * 7, ChronoUnit.DAYS);
+ Task newTask = new Task(
+ toAdd.getName(),
+ newOpenTime,
+ newCloseTime,
+ false,
+ false,
+ toAdd.getTags());
+ model.addTask(newTask);
+ }
+ }
+```
diff --git a/collated/main/A0144939R.md b/collated/main/A0144939R.md
new file mode 100644
index 000000000000..4f5eda75935b
--- /dev/null
+++ b/collated/main/A0144939R.md
@@ -0,0 +1,790 @@
+# A0144939R
+###### \java\seedu\task\commons\events\model\ReloadFromNewFileEvent.java
+``` java
+package seedu.task.commons.events.model;
+
+import java.util.Optional;
+import seedu.task.commons.events.BaseEvent;
+import seedu.task.model.ReadOnlyTaskManager;
+import seedu.task.model.TaskManager;
+
+/** Indicates that the user wishes to load from an existing file*/
+public class ReloadFromNewFileEvent extends BaseEvent {
+
+ public final String filePath;
+ public final ReadOnlyTaskManager taskManager;
+
+ public ReloadFromNewFileEvent(String newFilePath, Optional newTaskManager) {
+ this.filePath = newFilePath;
+ this.taskManager = newTaskManager.orElse(new TaskManager());
+ }
+
+ @Override
+ public String toString() {
+ return "The new file path is "+filePath;
+ }
+
+}
+```
+###### \java\seedu\task\commons\events\storage\ConfigFilePathChangedEvent.java
+``` java
+package seedu.task.commons.events.storage;
+
+import seedu.task.commons.events.BaseEvent;
+
+/** Indicates that the file path was successfully changed in the config*/
+public class ConfigFilePathChangedEvent extends BaseEvent {
+
+ public final String newFilePath;
+
+ public ConfigFilePathChangedEvent(String newFilePath){
+ this.newFilePath = newFilePath;
+ }
+
+ @Override
+ public String toString() {
+ return "The file path in Config has changed to " + newFilePath;
+ }
+
+}
+```
+###### \java\seedu\task\commons\events\storage\FilePathChangedEvent.java
+``` java
+package seedu.task.commons.events.storage;
+
+import seedu.task.commons.events.BaseEvent;
+
+import seedu.task.model.ReadOnlyTaskManager;
+
+/** Indicates that the user has specified a new file path*/
+public class FilePathChangedEvent extends BaseEvent {
+
+ public final String newFilePath;
+ public final ReadOnlyTaskManager taskManager;
+
+ public FilePathChangedEvent(String newFilePath, ReadOnlyTaskManager taskManager) {
+ this.newFilePath = newFilePath;
+ this.taskManager = taskManager;
+ }
+
+ @Override
+ public String toString() {
+ return "The new file path specified is "+newFilePath;
+ }
+
+}
+```
+###### \java\seedu\task\commons\logic\CommandKeys.java
+``` java
+package seedu.task.commons.logic;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.stream.Collectors;
+
+/**
+ * Represents all commands in the TaskManager
+ * @author advaypal
+ *
+ */
+public class CommandKeys {
+
+ public enum Commands {
+ ADD("add"),
+ ALIAS("alias"),
+ CLEAR("clear"),
+ COMPLETE("complete"),
+ CHANGE_TO("change-to"),
+ DELETE("delete"),
+ EXIT("exit"),
+ FIND("find"),
+ FIND_TAG("find-tag"),
+ HELP("help"),
+ LIST("list"),
+ PIN("pin"),
+ SEARCH_BOX("searchbox"),
+ SELECT("select"),
+ UNDO("undo"),
+ UPDATE("update"),
+ UNCOMPLETE("uncomplete"),
+ UNPIN("unpin");
+
+ private String value;
+
+ Commands(String command) {
+ this.value = command;
+ }
+
+ }
+ //create hashmap from command names to ENUM values
+ public static final HashMap commandKeyMap = (HashMap) Arrays.stream(Commands.values()).collect(Collectors.toMap(command -> command.value, command -> command));
+
+}
+```
+###### \java\seedu\task\logic\commands\AddCommand.java
+``` java
+package seedu.task.logic.commands;
+
+import java.time.temporal.ChronoUnit;
+import java.util.HashSet;
+import java.util.Set;
+
+import seedu.task.commons.core.EventsCenter;
+import seedu.task.commons.events.ui.JumpToListRequestEvent;
+import seedu.task.commons.exceptions.IllegalValueException;
+import seedu.task.model.tag.Tag;
+import seedu.task.model.tag.UniqueTagList;
+import seedu.task.model.task.DateTime;
+import seedu.task.model.task.Name;
+import seedu.task.model.task.Task;
+import seedu.task.model.task.UniqueTaskList;
+
+/**
+ * Adds a task to the task list.
+ */
+public class AddCommand extends UndoableCommand {
+
+```
+###### \java\seedu\task\logic\commands\AliasCommand.java
+``` java
+package seedu.task.logic.commands;
+
+import seedu.task.commons.logic.CommandKeys;
+import seedu.task.commons.logic.CommandKeys.Commands;
+
+/**
+ * Alias command
+ */
+public class AliasCommand extends Command {
+
+ public static final String COMMAND_WORD = "alias";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Aliases a command to a given string "
+ + "Parameters: ALIASED VALUE"
+ + "Example: " + COMMAND_WORD
+ + "add a";
+
+ public static final String MESSAGE_SUCCESS = "Command successfully aliased";
+ public static final String MESSAGE_FAILURE = "Error, cannot alias command";
+
+ private String commandString;
+ private String alias;
+
+
+ public AliasCommand(String command, String alias) {
+ this.commandString = command.trim();
+ this.alias = alias.trim();
+ }
+
+
+ @Override
+ public CommandResult execute() {
+ if(isValidAliasCommandPair(alias, commandString)) {
+ Commands command = CommandKeys.commandKeyMap.get(commandString);
+ model.setMapping(command, alias);
+ return new CommandResult(true, MESSAGE_SUCCESS);
+ } else {
+ return new CommandResult(false, MESSAGE_FAILURE);
+ }
+ }
+ /**
+ * Checks if a given alias can be mapped to given command
+ * Note: one command can have multiple aliases
+ * @param alias Alias specified by user
+ * @param command Command the alias aliases to
+ * @return
+ */
+ public boolean isValidAliasCommandPair(String alias, String command) {
+ //check that alias is not null and that alias is not a command
+ //checks that command is valid command
+ if (alias != null && command != null) {
+ return CommandKeys.commandKeyMap.containsKey(commandString) && CommandKeys.commandKeyMap.get(commandString) != null;
+ } else {
+ return false;
+ }
+ }
+
+}
+
+```
+###### \java\seedu\task\logic\commands\ChangePathCommand.java
+``` java
+package seedu.task.logic.commands;
+
+import java.io.File;
+
+import seedu.task.commons.core.EventsCenter;
+import seedu.task.commons.events.storage.FilePathChangedEvent;
+
+/**
+ * Change the file path
+ */
+public class ChangePathCommand extends Command{
+
+ public static final String COMMAND_WORD = "change-to";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Changes save/load location for the TaskManager "
+ + "Parameters: NEW FILE PATH\n"
+ + "Example: " + COMMAND_WORD
+ + "taskmanager.xml";
+
+ public static final String MESSAGE_PATH_CHANGE_SUCCESS = "Success! New File path: %1$s";
+ public static final String MESSAGE_PATH_CHANGE_FAIL = "Error, cannot change path to: %1$s";
+
+ private final String newFilePath;
+
+ public ChangePathCommand(String newFilePath) {
+ this.newFilePath = newFilePath.trim();
+ }
+
+
+
+ @Override
+ public CommandResult execute() {
+ if(isValidFilePath(newFilePath)) {
+ EventsCenter.getInstance().post(new FilePathChangedEvent(newFilePath, model.getTaskManager()));
+ return new CommandResult(true, String.format(MESSAGE_PATH_CHANGE_SUCCESS, newFilePath));
+ } else {
+ return new CommandResult(false, String.format(MESSAGE_PATH_CHANGE_FAIL, newFilePath));
+ }
+ }
+ /**
+ * Checks if the user defined file path is valid.
+ * A file path is defined to be valid if it has a valid parent folder, if it can be written to, and if
+ * it is an xml file.
+ * @param newFilePath The user defined file path
+ * @return boolean variable indicating if file path is valid
+ */
+ private boolean isValidFilePath(String newFilePath) {
+ File file = new File(newFilePath);
+ return (file.getParentFile() != null && file.getParentFile().canWrite() && newFilePath.endsWith(".xml"));
+ }
+}
+```
+###### \java\seedu\task\logic\parser\AddParser.java
+``` java
+ /**
+ * Extracts the arguments and puts them in a HashMap
+ * This method has been overriden to support the different nature of the add command's arguments
+ */
+ @Override
+ protected void extractArguments(String args) {
+ argumentsTable.clear();
+ String[] segments = args.trim().split(" ");
+ String currentKey = "name";
+ StringJoiner joiner = new StringJoiner(" ");
+ for (String segment : segments) {
+ if (isDelimiter(segment)) {
+ addToArgumentsTable(currentKey, joiner.toString().trim());
+ currentKey = segment;
+ joiner = new StringJoiner(" ");
+ } else {
+ joiner.add(segment);
+ }
+ }
+ addToArgumentsTable(currentKey, joiner.toString());
+ }
+
+ /**
+ * Checks if a string is a valid delimiter for the add command
+ * @param argument a string from the user input
+ * @return true if the String is a valid delimiter, and false otherwise
+ */
+ private boolean isDelimiter(String argument) {
+ for(int i = 0; i < KEYWORD_ARGS_OPTIONAL.length; i++) {
+ if(argument.equals(KEYWORD_ARGS_OPTIONAL[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
+```
+###### \java\seedu\task\logic\parser\AliasParser.java
+``` java
+package seedu.task.logic.parser;
+
+import static seedu.task.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import seedu.task.logic.commands.AliasCommand;
+import seedu.task.logic.commands.Command;
+import seedu.task.logic.commands.IncorrectCommand;
+
+public class AliasParser extends BaseParser {
+
+ private final String FLAG_ALIAS_COMMAND = "aliasCommand";
+ private final String FLAG_ALIAS_VALUE ="aliasValue";
+ private final int FLAG_ALIAS_COMMAND_LENGTH = 2;
+
+ @Override
+ public Command parse(String command, String arguments) {
+ this.extractArguments(arguments);
+ String aliasCommand = getSingleKeywordArgValue(FLAG_ALIAS_COMMAND);
+ String aliasValue = getSingleKeywordArgValue(FLAG_ALIAS_VALUE);
+ if( aliasCommand != null && aliasValue != null) {
+ return new AliasCommand(aliasCommand, aliasValue);
+ } else {
+ return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AliasCommand.MESSAGE_USAGE));
+ }
+
+ }
+
+ @Override
+ protected void extractArguments(String args) {
+ argumentsTable.clear();
+ String[] segments = args.trim().split(" ");
+ if(segments.length == FLAG_ALIAS_COMMAND_LENGTH) {
+ addToArgumentsTable(FLAG_ALIAS_COMMAND, segments[0]);
+ addToArgumentsTable(FLAG_ALIAS_VALUE, segments[1]);
+ }
+ //do nothing if false. The parse method will detect if there's an error.
+ }
+
+}
+```
+###### \java\seedu\task\logic\parser\UpdateParser.java
+``` java
+ @Override
+ public Command parse(String command, String arguments) {
+ this.extractArguments(arguments);
+
+ String index = getSingleKeywordArgValue("index");
+ Optional possibleIndex = parseIndex(index);
+
+ if (!possibleIndex.isPresent()) {
+ return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, UpdateCommand.MESSAGE_USAGE));
+ }
+
+ try {
+ return new UpdateCommand(
+ possibleIndex.get(),
+ getSingleKeywordArgValue(FLAG_NAME),
+ getSingleKeywordArgValue(FLAG_START_TIME),
+ getSingleKeywordArgValue(FLAG_CLOSE_TIME),
+ getTags(FLAG_TAGS),
+ getTags(FLAG_REMOVE_TAGS)
+ );
+ } catch (IllegalValueException ive) {
+ return new IncorrectCommand(ive.getMessage());
+ }
+ }
+
+ private Set getTags(String keyword) {
+ if (argumentsTable.containsKey(keyword)) {
+ return new HashSet<>(argumentsTable.get(keyword));
+ } else {
+ return new HashSet<>();
+ }
+ }
+
+ /**
+ * Extracts the arguments and puts them in a HashMap
+ * This method has been overriden to support the different nature of the update command's arguments
+ */
+ @Override
+ protected void extractArguments(String args) {
+ argumentsTable.clear();
+ String[] segments = args.trim().split(" ");
+ String currentKey = "index";
+ StringJoiner joiner = new StringJoiner(" ");
+ for (String segment : segments) {
+ if (isDelimiter(segment)) {
+ addToArgumentsTable(currentKey, joiner.toString().trim());
+ currentKey = segment;
+ joiner = new StringJoiner(" ");
+ } else {
+ joiner.add(segment);
+ }
+ }
+ addToArgumentsTable(currentKey, joiner.toString());
+ }
+
+ /**
+ * Checks if a string is a valid delimiter for the add command
+ * @param argument a string from the user input
+ * @return true if the String is a valid delimiter, and false otherwise
+ */
+ private boolean isDelimiter(String argument) {
+ for(int i = 0; i < KEYWORD_ARGS_OPTIONAL.length; i++) {
+ if(argument.equals(KEYWORD_ARGS_OPTIONAL[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
+```
+###### \java\seedu\task\model\Model.java
+``` java
+ /**
+ * Gets the alias map
+ * @return The command represented by the alias, or null if no mapping exists
+ */
+ public HashMap getAliasMap();
+
+ /**
+ * Sets mapping for given alias
+ * @param command
+ * @param alias
+ */
+ public void setMapping(Commands command, String alias);
+}
+```
+###### \java\seedu\task\model\ModelManager.java
+``` java
+
+ public HashMap getAliasMap() {
+ return userPrefs.getAliasMap();
+ }
+
+ public void setMapping(Commands command, String alias) {
+ userPrefs.setMapping(command, alias);
+ }
+
+ //=========== Filtered Task List Accessors ===============================================================
+
+ @Override
+ public UnmodifiableObservableList getFilteredTaskList() {
+ return new UnmodifiableObservableList<>(filteredTasks);
+ }
+
+ @Override
+ public void updateFilteredListToShowAll() {
+ filteredTasks.setPredicate(null);
+ EventsCenter.getInstance().post(new JumpToListRequestEvent(filteredTasks.size() - 1));
+ }
+
+```
+###### \java\seedu\task\model\ModelManager.java
+``` java
+ @Subscribe
+ public void handleReloadFromNewFileEvent(ReloadFromNewFileEvent event) throws DataConversionException {
+ logger.info(LogsCenter.getEventHandlingLogMessage(event, "Load from new file path requested"));
+ resetData(event.taskManager);
+ }
+}
+```
+###### \java\seedu\task\model\task\DateTime.java
+``` java
+
+ /**
+ * Returns true if a given string is a valid date/time that can be parsed
+ *
+ * @param test output from date/time parser
+ */
+ public static boolean isValidDateTime(String dateTime) {
+ List possibleDates = new PrettyTimeParser().parse(dateTime);
+ return !possibleDates.isEmpty() && (possibleDates.size() == 1);
+ }
+
+ @Override
+ public String toString() {
+
+ if(value.isPresent()) {
+ DateTimeFormatter formatter =
+ DateTimeFormatter.ofLocalizedDateTime( FormatStyle.SHORT )
+ .withLocale( Locale.UK )
+ .withZone( ZoneId.systemDefault() );
+ return formatter.format( value.get() );
+ } else {
+ return "";
+ }
+ }
+
+ public String toPrettyString() {
+ if(value.isPresent()) {
+ return p.format(Date.from(this.value.get()));
+ } else {
+ return "";
+ }
+ }
+
+```
+###### \java\seedu\task\model\task\ReadOnlyTask.java
+``` java
+ DateTime getOpenTime();
+ DateTime getCloseTime();
+ boolean getImportance();
+ boolean getComplete();
+
+ /**
+ * The returned TagList is a deep copy of the internal TagList,
+ * changes on the returned list will not affect the tasks's internal tags.
+ */
+ UniqueTagList getTags();
+
+```
+###### \java\seedu\task\model\task\ReadOnlyTask.java
+``` java
+
+ /**
+ * Returns true if both have the same state. (interfaces cannot override .equals)
+ */
+ default boolean isSameStateAs(ReadOnlyTask other) {
+ return other == this // short circuit if same object
+ || (other != null // this is first to avoid NPE below
+
+ // state checks here onwards
+ && other.getName().equals(this.getName())
+ && other.getOpenTime().equals(this.getOpenTime())
+ && other.getCloseTime().equals(this.getCloseTime())
+ && other.getImportance() == this.getImportance());
+ }
+
+ /**
+ * Formats the task as text, showing all task details.
+ */
+ default String getAsText() {
+ final StringBuilder builder = new StringBuilder();
+ builder.append(getName())
+ .append(" Tags: ");
+ getTags().forEach(builder::append);
+ return builder.toString();
+ }
+
+ /**
+ * Returns a string representation of this Task's tags
+ */
+ default String tagsString() {
+ final StringBuffer buffer = new StringBuffer();
+ final String separator = ", ";
+ getTags().forEach(tag -> buffer.append(tag).append(separator));
+ if (buffer.length() == 0) {
+ return "";
+ } else {
+ return buffer.substring(0, buffer.length() - separator.length());
+ }
+ }
+
+}
+```
+###### \java\seedu\task\model\UserPrefs.java
+``` java
+ private HashMap aliases = new HashMap();
+```
+###### \java\seedu\task\model\UserPrefs.java
+``` java
+ /**
+ * Gets mapping for a given alias.
+ * @param alias: a user defined string
+ * @return The command represented by the alias, or null if no mapping exists
+ */
+ public HashMap getAliasMap() {
+ return aliases;
+ }
+
+ /**
+ * Sets mapping for given alias
+ * @param command
+ * @param alias
+ */
+ public void setMapping(Commands command, String alias) {
+ aliases.put(alias, command);
+ }
+}
+```
+###### \java\seedu\task\storage\StorageManager.java
+``` java
+ @Override
+ public void setTaskManagerFilePath(String filePath) {
+ taskManagerStorage.setTaskManagerFilePath(filePath);
+ }
+```
+###### \java\seedu\task\storage\StorageManager.java
+``` java
+ @Subscribe
+ public void handleFilePathChangedEvent(FilePathChangedEvent event) throws DataConversionException {
+ logger.info(LogsCenter.getEventHandlingLogMessage(event, "File path change requested, attempting to update file path"));
+ String newFilePath = event.newFilePath;
+ String oldFilePath = getTaskManagerFilePath();
+ try {
+ //save data
+ saveTaskManager(event.taskManager);
+
+ //change file path
+ setTaskManagerFilePath(newFilePath);
+
+ if(!isFileAlreadyPresent(newFilePath)) {
+ //save to new location
+ saveTaskManager(event.taskManager);
+ } else {
+ //load from pre existing file
+ Optional newTaskManager = readTaskManager(newFilePath);
+ raise(new ReloadFromNewFileEvent(newFilePath, newTaskManager));
+ }
+
+ config.setTaskManagerFilePath(event.newFilePath);
+ ConfigUtil.saveConfig(config, config.getFilePath());
+ raise(new ConfigFilePathChangedEvent(event.newFilePath));
+
+ } catch (IOException e) {
+
+ logger.info(LogsCenter.getEventHandlingLogMessage(event, "Error occured on saving/loading"));
+ raise(new DataSavingExceptionEvent(e));
+ //clean up
+ setTaskManagerFilePath(oldFilePath);
+
+ } catch(DataConversionException e) {
+
+ logger.info(LogsCenter.getEventHandlingLogMessage(event, "Error occured on loading from new file"));
+ //clean up
+ setTaskManagerFilePath(oldFilePath);
+ }
+ }
+
+ /**
+ * Returns true if the file already exists
+ * @param filePath
+ * @return boolean value indicating whether file already exists
+ */
+ private boolean isFileAlreadyPresent(String filePath) {
+ File newFile = new File(filePath);
+ return newFile.exists() && !newFile.isDirectory();
+ }
+}
+```
+###### \java\seedu\task\storage\XmlAdaptedTask.java
+``` java
+package seedu.task.storage;
+
+import javax.xml.bind.annotation.XmlElement;
+
+import seedu.task.commons.exceptions.IllegalValueException;
+import seedu.task.model.tag.Tag;
+import seedu.task.model.tag.UniqueTagList;
+import seedu.task.model.task.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * JAXB-friendly version of the Task.
+ */
+public class XmlAdaptedTask {
+
+ @XmlElement(required = true)
+ private String name;
+
+ @XmlElement(required = false)
+ private Long openTime;
+
+ @XmlElement(required = false)
+ private Long closeTime;
+
+ @XmlElement(required = false)
+ private boolean isComplete;
+
+ @XmlElement(required = false)
+ private boolean isImportant;
+
+ @XmlElement
+ private List tagged = new ArrayList<>();
+
+ /**
+ * No-arg constructor for JAXB use.
+ */
+ public XmlAdaptedTask() {}
+
+
+ /**
+ * Converts a given Task into this class for JAXB use.
+ *
+ * @param source future changes to this will not affect the created XmlAdaptedTask
+ */
+ public XmlAdaptedTask(ReadOnlyTask source) {
+ name = source.getName().taskName;
+ openTime = source.getOpenTime().getSaveableValue();
+ closeTime = source.getCloseTime().getSaveableValue();
+ isComplete = source.getComplete();
+ isImportant = source.getImportance();
+ tagged = new ArrayList<>();
+ for (Tag tag : source.getTags()) {
+ tagged.add(new XmlAdaptedTag(tag));
+ }
+
+ }
+
+ /**
+ * 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 DateTime openTime = DateTime.fromEpoch(this.openTime);
+ final DateTime closeTime = DateTime.fromEpoch(this.closeTime);
+ final UniqueTagList tags = new UniqueTagList(taskTags);
+ final boolean isImportant = this.isImportant;
+ final boolean isComplete = this.isComplete;
+ return new Task(name, openTime, closeTime, isImportant, isComplete, tags);
+ }
+}
+```
+###### \java\seedu\task\ui\StatusBarFooter.java
+``` java
+ @Subscribe
+ public void handleConfigFilePathChangedEvent(ConfigFilePathChangedEvent event) {
+ logger.info(LogsCenter.getEventHandlingLogMessage(event, "Storage location changed, updating status bar"));
+ setSaveLocation(event.newFilePath);
+ }
+}
+```
+###### \java\seedu\task\ui\TaskCard.java
+``` java
+ @FXML
+ private Label openTime;
+
+ @FXML
+ private Label closeTime;
+```
+###### \java\seedu\task\ui\TaskCard.java
+``` java
+ private void setCardDetails() {
+ name.setText(task.getName().taskName);
+ id.setText(displayedIndex + ". ");
+
+ openTime.setText(task.getOpenTime().toDisplayString());
+ closeTime.setText(task.getCloseTime().toDisplayString());
+
+ tagListPanel = TagListPanel.load(getPrimaryStage(), tagsListPlaceholder, task.getTags().getInternalList());
+ }
+```
+###### \resources\view\TaskListCard.fxml
+``` fxml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+###### \resources\view\TaskListCard.fxml
+``` fxml
+
+
+
+
+
+
+
+
+
+
+
+
+```
diff --git a/collated/main/A0153467Y.md b/collated/main/A0153467Y.md
new file mode 100644
index 000000000000..969c8dd42829
--- /dev/null
+++ b/collated/main/A0153467Y.md
@@ -0,0 +1,507 @@
+# A0153467Y
+###### \java\seedu\task\logic\commands\CompleteCommand.java
+``` java
+package seedu.task.logic.commands;
+
+import seedu.task.commons.core.Messages;
+import seedu.task.commons.core.UnmodifiableObservableList;
+import seedu.task.commons.exceptions.IllegalValueException;
+import seedu.task.model.task.Task;
+import seedu.task.model.task.ReadOnlyTask;
+
+public class CompleteCommand extends UndoableCommand {
+
+ public static final String COMMAND_WORD = "complete";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Marks the task identified by the index number used in the last task listing as completed.\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 static final String MESSAGE_ROLLBACK_SUCCESS = "Undo action on complete task was executed successfully!";
+
+ public final int targetIndex;
+
+ public CompleteCommand(int targetIndex) {
+ this.targetIndex = targetIndex;
+ }
+
+ @Override
+ public CommandResult execute() {
+ UnmodifiableObservableList lastShownList = model.getFilteredTaskList();
+
+ if (lastShownList.size() < targetIndex) {
+ indicateAttemptToExecuteIncorrectCommand();
+ return new CommandResult(false, Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX);
+ }
+
+ ReadOnlyTask orginialTask = lastShownList.get(targetIndex - 1);
+ try {
+ Task completedTask = new Task(orginialTask);
+ completedTask.setIsCompleted(true);
+ model.completeTask(orginialTask, completedTask);
+ } catch (IllegalValueException e) {
+ assert false : "Impossible";
+ }
+
+ return new CommandResult(true, String.format(MESSAGE_COMPLETE_TASK_SUCCESS, orginialTask));
+ }
+
+ @Override
+ public CommandResult rollback() {
+ assert model != null;
+
+ model.rollback();
+
+ return new CommandResult(true, MESSAGE_ROLLBACK_SUCCESS);
+ }
+}
+```
+###### \java\seedu\task\logic\commands\PinCommand.java
+``` java
+package seedu.task.logic.commands;
+
+import seedu.task.commons.core.Messages;
+import seedu.task.commons.core.UnmodifiableObservableList;
+import seedu.task.commons.exceptions.IllegalValueException;
+import seedu.task.model.task.ReadOnlyTask;
+import seedu.task.model.task.Task;
+
+public class PinCommand extends UndoableCommand {
+ public static final String COMMAND_WORD = "pin";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Pin the task identified by the index number used in the last task listing as important.\n"
+ + "Parameters: INDEX (must be a positive integer)\n" + "Example: " + COMMAND_WORD + " 1 ";
+
+ public static final String MESSAGE_PIN_TASK_SUCCESS = "Pinned Task: %1$s";
+ public static final String MESSAGE_ROLLBACK_SUCCESS = "Undo action on pin task was executed successfully!";
+
+ public final int targetIndex;
+
+ public PinCommand(int targetIndex) {
+ this.targetIndex = targetIndex;
+ }
+
+ @Override
+ public CommandResult execute() {
+
+ UnmodifiableObservableList lastShownList = model.getFilteredTaskList();
+
+ if (lastShownList.size() < targetIndex) {
+ indicateAttemptToExecuteIncorrectCommand();
+ return new CommandResult(false, Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX);
+ }
+
+ ReadOnlyTask orginialTask = lastShownList.get(targetIndex - 1);
+ try {
+ Task taskToPin = new Task(orginialTask);
+ taskToPin.setIsImportant(true);
+ model.pinTask(orginialTask, taskToPin);
+ } catch (IllegalValueException e) {
+ assert false : "Not possible for task on list to have illegal value";
+ }
+
+ return new CommandResult(true, String.format(MESSAGE_PIN_TASK_SUCCESS, orginialTask));
+ }
+
+ @Override
+ public CommandResult rollback() {
+ assert model != null;
+
+ model.rollback();
+
+ return new CommandResult(true, MESSAGE_ROLLBACK_SUCCESS);
+ }
+
+}
+```
+###### \java\seedu\task\logic\commands\UncompleteCommand.java
+``` java
+package seedu.task.logic.commands;
+
+import seedu.task.commons.core.Messages;
+import seedu.task.commons.core.UnmodifiableObservableList;
+import seedu.task.commons.exceptions.IllegalValueException;
+import seedu.task.model.task.ReadOnlyTask;
+import seedu.task.model.task.Task;
+
+public class UncompleteCommand extends UndoableCommand {
+
+ public static final String COMMAND_WORD = "uncomplete";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Unmark the completed task identified by the index number used in the last task listing as not completed.\n"
+ + "Parameters: INDEX (must be a positive integer)\n" + "Example: " + COMMAND_WORD + " 1";
+
+ public static final String MESSAGE_UNCOMPLETE_TASK_SUCCESS = "Uncompleted Task: %1$s";
+ public static final String MESSAGE_ROLLBACK_SUCCESS = "Undo action on un-complete task was executed successfully!";
+
+ public final int targetIndex;
+
+ public UncompleteCommand(int targetIndex) {
+ this.targetIndex = targetIndex;
+ }
+
+ @Override
+ public CommandResult execute() {
+ UnmodifiableObservableList lastShownList = model.getFilteredTaskList();
+
+ if (lastShownList.size() < targetIndex) {
+ indicateAttemptToExecuteIncorrectCommand();
+ return new CommandResult(false, Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX);
+ }
+
+ ReadOnlyTask orginialTask = lastShownList.get(targetIndex - 1);
+ try {
+ Task uncompletedTask = new Task(orginialTask);
+ if(uncompletedTask.getComplete()) {
+ uncompletedTask.setIsCompleted(false);
+ }else {
+ return new CommandResult(false, Messages.MESSAGE_INVALID_UNCOMPLETE_TASK);
+ }
+
+ model.uncompleteTask(orginialTask, uncompletedTask);
+ } catch (IllegalValueException e) {
+ assert false : "Impossible";
+ }
+
+ return new CommandResult(true, String.format(MESSAGE_UNCOMPLETE_TASK_SUCCESS, orginialTask));
+ }
+
+ @Override
+ public CommandResult rollback() {
+ assert model != null;
+
+ model.rollback();
+
+ return new CommandResult(true, MESSAGE_ROLLBACK_SUCCESS);
+ }
+}
+```
+###### \java\seedu\task\logic\commands\UnpinCommand.java
+``` java
+package seedu.task.logic.commands;
+
+import seedu.task.commons.core.Messages;
+import seedu.task.commons.core.UnmodifiableObservableList;
+import seedu.task.commons.exceptions.IllegalValueException;
+import seedu.task.model.task.ReadOnlyTask;
+import seedu.task.model.task.Task;
+
+public class UnpinCommand extends UndoableCommand {
+ public static final String COMMAND_WORD = "unpin";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Unpin the pinned 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_UNPIN_TASK_SUCCESS = "Unpinned Task: %1$s";
+ public static final String MESSAGE_ROLLBACK_SUCCESS = "Undo action on unpin task was executed successfully!";
+
+ public final int targetIndex;
+
+ public UnpinCommand(int targetIndex) {
+ this.targetIndex = targetIndex;
+ }
+
+ @Override
+ public CommandResult execute() {
+
+ UnmodifiableObservableList lastShownList = model.getFilteredTaskList();
+
+ if (lastShownList.size() < targetIndex) {
+ indicateAttemptToExecuteIncorrectCommand();
+ return new CommandResult(false, Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX);
+ }
+
+ ReadOnlyTask orginialTask = lastShownList.get(targetIndex - 1);
+ try {
+ Task taskToUnpin = new Task(orginialTask);
+ if(taskToUnpin.getImportance()) {
+ taskToUnpin.setIsImportant(false);
+ model.unpinTask(orginialTask, taskToUnpin);
+ } else {
+ return new CommandResult(false, Messages.MESSAGE_INVALID_UNPIN_TASK);
+ }
+ } catch (IllegalValueException e) {
+ assert false : "Not possible for task on list to have illegal value";
+ }
+ return new CommandResult(true, String.format(MESSAGE_UNPIN_TASK_SUCCESS, orginialTask));
+ }
+
+ @Override
+ public CommandResult rollback() {
+ assert model != null;
+
+ model.rollback();
+
+ return new CommandResult(true, MESSAGE_ROLLBACK_SUCCESS);
+ }
+}
+```
+###### \java\seedu\task\logic\parser\UncompleteParser.java
+``` java
+package seedu.task.logic.parser;
+
+import seedu.task.logic.commands.UncompleteCommand;
+import seedu.task.logic.commands.IncorrectCommand;
+
+import static seedu.task.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import java.util.Optional;
+
+import seedu.task.logic.commands.Command;
+
+public class UncompleteParser extends BaseParser {
+ @Override
+ public Command parse(String command, String arguments) {
+ this.extractArguments(arguments);
+ Optional idx = parseIndex();
+
+ if (!idx.isPresent()) {
+ return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, UncompleteCommand.MESSAGE_USAGE));
+ }
+
+ return new UncompleteCommand(idx.get());
+ }
+}
+```
+###### \java\seedu\task\logic\parser\UnpinParser.java
+``` java
+package seedu.task.logic.parser;
+
+import static seedu.task.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import java.util.Optional;
+
+import seedu.task.logic.commands.Command;
+import seedu.task.logic.commands.IncorrectCommand;
+import seedu.task.logic.commands.UnpinCommand;
+
+public class UnpinParser extends BaseParser {
+ @Override
+ public Command parse(String command, String arguments) {
+ this.extractArguments(arguments);
+
+ Optional index = parseIndex();
+ if (!index.isPresent()) {
+ return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, UnpinCommand.MESSAGE_USAGE));
+ }
+ return new UnpinCommand(index.get());
+ }
+}
+```
+###### \java\seedu\task\model\Model.java
+``` java
+ /** Pins the given task as important */
+ void pinTask(ReadOnlyTask originalTask, Task toPin);
+
+```
+###### \java\seedu\task\model\Model.java
+``` java
+ /** Unpins the given task*/
+ void unpinTask(ReadOnlyTask originalTask, Task toUnpin);
+
+```
+###### \java\seedu\task\model\Model.java
+``` java
+ /** Mark the given task as completed */
+ void completeTask(ReadOnlyTask originalTask, Task completedTask);
+
+```
+###### \java\seedu\task\model\Model.java
+``` java
+ /** Unmark the given important task */
+ void uncompleteTask(ReadOnlyTask originalTask, Task uncompletedTask);
+```
+###### \java\seedu\task\model\ModelManager.java
+``` java
+ @Override
+ public synchronized void completeTask(ReadOnlyTask originalTask, Task completeTask){
+ taskManager.completeTask(originalTask, completeTask);
+ updateFilteredListToShowAll();
+ indicateTaskManagerChanged();
+ }
+```
+###### \java\seedu\task\model\ModelManager.java
+``` java
+ @Override
+ public synchronized void pinTask(ReadOnlyTask originalTask, Task toPin) {
+ taskManager.pinTask(originalTask, toPin);
+ updateFilteredListToShowAll();
+ indicateTaskManagerChanged();
+ }
+
+```
+###### \java\seedu\task\model\ModelManager.java
+``` java
+ @Override
+ public synchronized void uncompleteTask(ReadOnlyTask originalTask, Task uncompleteTask){
+ taskManager.uncompleteTask(originalTask, uncompleteTask);
+ }
+
+ public synchronized void unpinTask(ReadOnlyTask originalTask, Task toUnpin) {
+ taskManager.unpinTask(originalTask, toUnpin);
+ updateFilteredListToShowAll();
+ indicateTaskManagerChanged();
+ }
+```
+###### \java\seedu\task\model\task\Task.java
+``` java
+ @Override
+ public boolean getImportance() {
+ return isImportant;
+ }
+
+ @Override
+ public boolean getComplete() {
+ return isCompleted;
+ }
+```
+###### \java\seedu\task\model\task\Task.java
+``` java
+
+ /**
+ * Sets the task's importance flag
+ * @param isImportant is a variable to show whether the task is important or not
+ */
+ public void setIsImportant(boolean isImportant) {
+ this.isImportant = isImportant;
+ }
+
+ /**
+ * Sets the task's completion flag
+ * @param isCompleted is a variable which shows whether the task is completed or not
+ */
+ public void setIsCompleted(boolean isCompleted) {
+ this.isCompleted = isCompleted;
+ }
+```
+###### \java\seedu\task\model\task\UniqueTaskList.java
+``` java
+ /**
+ * Pins a task as important.
+ *
+ * @param originalTask refers the task selected from the list
+ * @param toPin refers a task which is same as the originalTask except it is pinned
+ */
+ public void pin(ReadOnlyTask originalTask, Task toPin) {
+ assert toPin != null;
+ assert originalTask != null;
+
+ int index = internalList.indexOf(originalTask);
+ assert index >= 0;
+ internalList.set(index, toPin);
+ history.addAsNewMutation(index, new Mutation(originalTask, toPin.getImmutable()));
+ }
+
+```
+###### \java\seedu\task\model\task\UniqueTaskList.java
+``` java
+ /**
+ * Unpin a pinned task.
+ *
+ * @param originalTask refers to the task which is pinned
+ * @param toUnpin refers to the selected task except it is unpinned
+ */
+ public void unpin(ReadOnlyTask originalTask, Task toUnpin) {
+ assert toUnpin != null;
+ assert originalTask != null;
+
+ int index = internalList.indexOf(originalTask);
+ assert index >= 0;
+ internalList.set(index, toUnpin);
+ history.addAsNewMutation(index, new Mutation(originalTask, toUnpin.getImmutable()));
+ }
+
+ /**
+ * Marks a task as completed in the list.
+ *
+ * @param originalTask refers to task which is not marked as completed yet
+ * @param completeTask refers to the same task as originalTask except it is marked as completed
+ */
+ public void complete(ReadOnlyTask originalTask, Task completeTask) {
+ assert originalTask != null;
+ assert completeTask != null;
+
+ int index = internalList.indexOf(originalTask);
+ assert index >= 0;
+
+ internalList.set(index, completeTask);
+ history.addAsNewMutation(index, new Mutation(originalTask, completeTask.getImmutable()));
+ }
+
+```
+###### \java\seedu\task\model\task\UniqueTaskList.java
+``` java
+ /**
+ * Marks a task which is marked as completed to not completed in the list.
+ *
+ * @param originalTask refers to a task marked as completed
+ * @param uncompleteTask refers to the same task as original except it is not marked as complete
+ */
+ public void uncomplete(ReadOnlyTask originalTask, Task uncompleteTask) {
+ assert originalTask != null;
+ assert uncompleteTask != null;
+
+ int index = internalList.indexOf(originalTask);
+ assert index >= 0;
+
+ internalList.set(index, uncompleteTask);
+ history.addAsNewMutation(index, new Mutation(originalTask, uncompleteTask.getImmutable()));
+ }
+```
+###### \java\seedu\task\model\TaskManager.java
+``` java
+ /**
+ * Pins a specfic task to the task list as important.
+ *
+ * @param originalTask the orginial task on the list
+ * @param toPin Task which is pinned
+ */
+ public void pinTask(ReadOnlyTask originalTask, Task toPin) {
+ tasks.pin(originalTask, toPin);
+ }
+
+```
+###### \java\seedu\task\model\TaskManager.java
+``` java
+ /**
+ * Unpins a specfic pinned task to the task list.
+ *
+ * @param originalTask the orginial task on the list
+ * @param toPin Task which is unpinnned
+ */
+ public void unpinTask(ReadOnlyTask originalTask, Task toUnpin) {
+ tasks.unpin(originalTask, toUnpin);
+ }
+
+ /**
+ * Marks a specific task as completed to the task list.
+ *
+ * @param originalTask refers to the task that selected from the list
+ * @param completeTask refers to a task same as original task except being marked as complete
+ */
+
+ public void completeTask(ReadOnlyTask originalTask, Task completeTask) {
+ tasks.complete(originalTask,completeTask);
+ }
+
+```
+###### \java\seedu\task\model\TaskManager.java
+``` java
+ /**
+ * Unmark a specific completed task as not completed to the task list.
+ *
+ * @param originalTask refers to task which is marked as complete
+ * @param uncompleteTask refers to task which is now marked as not complete
+ */
+ public void uncompleteTask(ReadOnlyTask originalTask, Task uncompleteTask) {
+ tasks.uncomplete(originalTask,uncompleteTask);
+ }
+```
+###### \java\seedu\task\ui\HelpWindow.java
+``` java
+ private static final String COMMANDSUMMARY_URL = HelpWindow.class.getClassLoader().getResource("command-summary/CommandSummary.html").toExternalForm();
+```
diff --git a/collated/main/A0153467Yunused.md b/collated/main/A0153467Yunused.md
new file mode 100644
index 000000000000..502a594a0912
--- /dev/null
+++ b/collated/main/A0153467Yunused.md
@@ -0,0 +1,12 @@
+# A0153467Yunused
+###### \java\seedu\task\logic\commands\AddCommand.java
+``` java
+ // Recurring tasks scrapped due to lack of time (and unforseen circumstances)
+ private static final int MAX_NUMBER_OF_RECURRENCE_WEEK=20;
+```
+###### \java\seedu\task\logic\commands\AddCommand.java
+``` java
+ // Recurring tasks scrapped due to lack of time (and unforseen circumstances)
+ public static final String MESSAGE_WRONG_NUMBER_OF_RECURRENCE = "Maximum number of recurrence is 20!";
+ public static final String MESSAGE_NEGATIVE_NUMBER_OF_RECURRENCE = "The number recurrence should be positive!";
+```
diff --git a/collated/test/A0141052Y.md b/collated/test/A0141052Y.md
new file mode 100644
index 000000000000..2f75fc8cb1e5
--- /dev/null
+++ b/collated/test/A0141052Y.md
@@ -0,0 +1,444 @@
+# A0141052Y
+###### \java\guitests\AddCommandTest.java
+``` java
+ @Test
+ public void add() {
+ //add task that will appear on the front of the list
+ TestTask[] currentList = td.getTypicalTasks();
+ TestTask taskToAdd = TypicalTestTasks.first;
+ assertAddSuccess(1, taskToAdd, currentList);
+
+ currentList = TestUtil.insertTaskToList(currentList, taskToAdd, 0);
+ assertResultMessage(String.format(AddCommand.MESSAGE_SUCCESS, taskToAdd));
+
+ //adds a task that will be added at the back
+ taskToAdd = TypicalTestTasks.last;
+ assertAddSuccess(taskToAdd, currentList);
+ currentList = TestUtil.addTasksToList(currentList, taskToAdd);
+ assertResultMessage(String.format(AddCommand.MESSAGE_SUCCESS, taskToAdd));
+
+ //adds a task to the middle, before fiona
+ taskToAdd = TypicalTestTasks.hoon;
+ assertAddSuccess(5, taskToAdd, currentList);
+ currentList = TestUtil.addTasksToList(currentList, taskToAdd);
+ assertResultMessage(String.format(AddCommand.MESSAGE_SUCCESS, taskToAdd));
+
+ //add to empty list
+ commandBox.runCommand("clear");
+
+ //add a task which has endTime < openTime
+ commandBox.runCommand("add testEvent starts tomorrow ends today");
+ assertResultMessage(MESSAGE_DATETIME_CONSTRAINTS);
+
+ //add test with only name
+ }
+```
+###### \java\guitests\AddCommandTest.java
+``` java
+ private void assertAddSuccess(TestTask taskToAdd, TestTask... currentList) {
+ assertAddSuccess(currentList.length + 1, taskToAdd, currentList);
+ }
+ /**
+ * Asserts the success of the add command operation.
+ * @param taskId The expected ID of the Task to be added (according to the GUI)
+ * @param taskToAdd The expected Task to be added
+ * @param currentList The current list of Tasks that are being displayed
+ */
+ private void assertAddSuccess(int taskId, TestTask taskToAdd, TestTask... currentList) {
+ commandBox.runCommand(taskToAdd.getAddCommand());
+
+ //confirm the new card contains the right data
+ TaskCardHandle addedCard = taskListPanel.navigateToTask(taskId - 1);
+ assertMatching(taskToAdd, addedCard);
+
+ //confirm the list now contains all previous tasks plus the new task
+ TestTask[] expectedList = TestUtil.insertTaskToList(currentList, taskToAdd, taskId - 1);
+ assertTrue(taskListPanel.isListMatching(expectedList));
+ }
+
+ private TestTask[] assertAddRecurringSuccess(int numTimes, TestTask taskToAdd, TestTask... currentList) {
+ commandBox.runCommand(taskToAdd.getAddCommand() + " recurs " + numTimes);
+
+ TestTask[] expectedList = currentList.clone();
+
+ //confirm that instances have same distance and the other properties are same
+ for (int i = 0; i <= numTimes; i++) {
+ TestTask recurringTask = new TestTask(taskToAdd); // insulate the passed TestTask from changes
+
+ DateTime newOpenTime = DateTime.fromDateTimeOffset(taskToAdd.getOpenTime(), i * 7, ChronoUnit.DAYS);
+ DateTime newCloseTime = DateTime.fromDateTimeOffset(taskToAdd.getCloseTime(), i * 7, ChronoUnit.DAYS);
+
+ recurringTask.setOpenTime(newOpenTime);
+ recurringTask.setCloseTime(newCloseTime);
+
+ TaskCardHandle addedCard = taskListPanel.navigateToTask(recurringTask);
+ assertMatching(recurringTask, addedCard);
+
+ expectedList = TestUtil.addTasksToList(expectedList, recurringTask);
+ }
+
+ //assert that the listing is correct after checking individually
+ assertTrue(taskListPanel.isListMatching(expectedList));
+
+ return expectedList;
+ }
+
+}
+```
+###### \java\guitests\FindTagCommandTest.java
+``` java
+package guitests;
+
+import org.junit.Test;
+
+import seedu.task.commons.core.Messages;
+import seedu.task.testutil.TestTask;
+import seedu.task.testutil.TypicalTestTasks;
+
+import static org.junit.Assert.assertTrue;
+
+public class FindTagCommandTest extends TaskManagerGuiTest {
+
+ @Test
+ public void findTagNonEmptyList() {
+ assertFindTagResult("find-tag notimportant");
+ assertFindTagResult("find-tag friends", TypicalTestTasks.cs2103, TypicalTestTasks.carl);
+
+ // Find a tag after deletion
+ commandBox.runCommand("delete 1");
+ assertFindTagResult("find-tag friends", TypicalTestTasks.carl);
+ }
+
+ @Test
+ public void findTagEmptyList(){
+ commandBox.runCommand("clear");
+ assertFindTagResult("find-tag friend");
+ }
+
+ @Test
+ public void findInvalidCommand() {
+ commandBox.runCommand("find-tagworld");
+ assertResultMessage(Messages.MESSAGE_UNKNOWN_COMMAND);
+ }
+
+ private void assertFindTagResult(String command, TestTask... expectedHits) {
+ commandBox.runCommand(command);
+ assertListSize(expectedHits.length);
+ assertResultMessage(expectedHits.length + " tasks listed!");
+ assertTrue(taskListPanel.isListMatching(expectedHits));
+ }
+}
+```
+###### \java\guitests\SearchCommandTest.java
+``` java
+package guitests;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import seedu.task.testutil.TestTask;
+import seedu.task.testutil.TypicalTestTasks;
+
+public class SearchCommandTest extends TaskManagerGuiTest {
+ @Test
+ public void search() {
+ TestTask[] currentList = td.getTypicalTasks();
+
+ // test search no name
+ assertSearchResult("", currentList);
+
+ // test search full name
+ assertSearchResult(TypicalTestTasks.daniel.getName().taskName, TypicalTestTasks.daniel);
+
+ // test search partial name
+ assertSearchResult("Have lunch", TypicalTestTasks.daniel);
+
+ // test search no results
+ assertSearchResult("this does not exist");
+ }
+
+ private void resetCommandBox() {
+ commandBox.pressEnter();
+ }
+
+ private void assertSearchResult(String query, TestTask... expectedHits ) {
+ commandBox.runCommand("searchbox");
+ commandBox.enterCommand(query);
+ assertListSize(expectedHits.length);
+ assertTrue(taskListPanel.isListMatching(expectedHits));
+
+ commandBox.runCommand(query);
+ assertListSize(expectedHits.length);
+ assertTrue(taskListPanel.isListMatching(expectedHits));
+
+ resetCommandBox();
+ }
+}
+```
+###### \java\guitests\UpdateCommandTest.java
+``` java
+package guitests;
+
+import guitests.guihandles.TaskCardHandle;
+
+import static org.junit.Assert.*;
+import static seedu.task.logic.commands.UpdateCommand.MESSAGE_UPDATE_TASK_SUCCESS;
+
+import org.junit.Test;
+
+import seedu.task.commons.core.Messages;
+import seedu.task.commons.exceptions.IllegalValueException;
+import seedu.task.model.tag.Tag;
+import seedu.task.model.task.*;
+import seedu.task.testutil.TestTask;
+import seedu.task.testutil.TestUtil;
+import seedu.task.testutil.TypicalTestTasks;
+
+public class UpdateCommandTest extends TaskManagerGuiTest {
+ /**
+ * NOTE TO EXAMINERS:
+ * THIS TEST MAY FAIL DUE TO RACE CONDITIONS. IT IS IN NO PART
+ * DUE TO THE TEST CASES, RATHER, THE TIME BETWEEN TASK UPDATE
+ * AND WHEN THE ASSERTION IS DONE MAY LEAD TO NULLPOINTEREXCEPTION
+ * DUE TO HOW THE RETRIEVAL OF THE TASK UI CARD WORKS.
+ *
+ * EXAMPLE: UI SAYS "11:39" BUT TEST CASE SAVES AS "11:40"
+ *
+ * @throws IllegalValueException
+ */
+ @Test
+ public void update() throws IllegalValueException {
+ TestTask[] currentList = td.getTypicalTasks();
+ int targetIndex = 1;
+
+ // update first task with details that will put it to the back
+ assertUpdateSuccess(targetIndex, currentList.length, TypicalTestTasks.ida, currentList);
+
+ currentList = TestUtil.moveTaskInList(currentList, targetIndex - 1, currentList.length - 1);
+ currentList = TestUtil.replaceTaskFromList(currentList, TypicalTestTasks.ida, currentList.length - 1);
+ targetIndex = currentList.length;
+
+ // update last task with details that will put it to the front
+ assertUpdateSuccess(targetIndex, 1, TypicalTestTasks.first, currentList);
+
+ currentList = TestUtil.moveTaskInList(currentList, targetIndex - 1, 0);
+ currentList = TestUtil.replaceTaskFromList(currentList, TypicalTestTasks.first, 0);
+ targetIndex = 1;
+
+ // add new tags
+ Tag tagToAdd = new Tag("urgent");
+ commandBox.runCommand("update " + targetIndex + " tag urgent");
+ ReadOnlyTask newTask = taskListPanel.getTask(targetIndex - 1);
+ assertTrue(newTask.getTags().contains(tagToAdd));
+
+ // remove existing tags
+ commandBox.runCommand("update " + targetIndex + " remove-tag urgent");
+ newTask = taskListPanel.getTask(targetIndex - 1);
+ assertFalse(newTask.getTags().contains(tagToAdd));
+
+ // update with no changes
+ targetIndex = 4;
+ commandBox.runCommand("update " + targetIndex);
+ TaskCardHandle updatedCard = taskListPanel.navigateToTask(targetIndex - 1);
+ assertMatching(TypicalTestTasks.fiona, updatedCard);
+
+ // update own task without changing name
+ targetIndex = 3;
+ commandBox.runCommand("update " + targetIndex + " name"+ TypicalTestTasks.carl.getArgs());
+ assertTrue(taskListPanel.isListMatching(currentList));
+
+ // invalid index
+ commandBox.runCommand("update " + (currentList.length+1) + " name"+ TypicalTestTasks.ida.getArgs());
+ assertResultMessage(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX);
+ assertTrue(taskListPanel.isListMatching(currentList));
+
+ // no index provided
+ commandBox.runCommand("update");
+ assertTrue(taskListPanel.isListMatching(currentList));
+
+ //invalid command
+ commandBox.runCommand("updates " + currentList.length);
+ assertResultMessage(Messages.MESSAGE_UNKNOWN_COMMAND);
+ assertTrue(taskListPanel.isListMatching(currentList));
+ }
+
+ private void assertUpdateSuccess(int targetIndex, int newIndex, TestTask taskToUpdate, TestTask... currentList) {
+ commandBox.runCommand("update " + targetIndex + " name"+ taskToUpdate.getArgs() );
+
+ //confirm the new card contains the right data
+ TaskCardHandle updatedCard = taskListPanel.navigateToTask(newIndex - 1);
+ assertMatching(taskToUpdate, updatedCard);
+
+ TestTask[] expectedList = TestUtil.moveTaskInList(currentList, targetIndex - 1, newIndex - 1);
+
+ // merge tags
+ taskToUpdate.getTags().mergeFrom(expectedList[newIndex - 1].getTags());
+
+ expectedList[newIndex - 1] = taskToUpdate;
+ assertTrue(taskListPanel.isListMatching(expectedList));
+
+ //confirm the result message is correct
+ assertResultMessage(String.format(MESSAGE_UPDATE_TASK_SUCCESS, taskToUpdate));
+ }
+
+
+}
+```
+###### \java\seedu\task\logic\LogicManagerTest.java
+``` java
+ @Subscribe
+ private void handleSwitchCommandBoxFunctionEvent(SwitchCommandBoxFunctionEvent evt) {
+ hasSwitchedToSearch = true;
+ }
+
+```
+###### \java\seedu\task\logic\LogicManagerTest.java
+``` java
+ /**
+ * Tests the preset filter logic for pinned tasks
+ */
+ @Test
+ public void execute_list_showsPinnedTasks() throws Exception {
+ // prepare tasks
+ TestDataHelper helper = new TestDataHelper();
+
+ Task pinned1 = helper.generateTaskWithName("Pinned");
+ pinned1.setIsImportant(true);
+ Task pinned2 = helper.generateTaskAfterWithName("Pinned Too", pinned1);
+ pinned2.setIsImportant(true);
+ List pinnedTasks = helper.generateTaskList(pinned1, pinned2);
+
+ assertCommandFilteredList("list pinned",
+ ListCommand.MESSAGE_SUCCESS,
+ pinnedTasks,
+ false);
+ }
+
+ /**
+ * Test the preset filter logic for completed tasks
+ */
+ @Test
+ public void execute_list_showsCompletedTasks() throws Exception {
+ // prepare tasks
+ TestDataHelper helper = new TestDataHelper();
+
+ Task complete1 = helper.generateTaskWithName("Completed");
+ complete1.setIsCompleted(true);
+ Task complete2 = helper.generateTaskAfterWithName("Completed Also", complete1);
+ complete2.setIsCompleted(true);
+ List completedTasks = helper.generateTaskList(complete1, complete2);
+
+ assertCommandFilteredList("list completed",
+ ListCommand.MESSAGE_SUCCESS,
+ completedTasks,
+ false);
+ }
+
+ @Test
+ public void execute_list_showsPendingTasks() throws Exception {
+ // prepare tasks
+ TestDataHelper helper = new TestDataHelper();
+
+ Task complete1 = helper.generateTaskWithName("Completed");
+ complete1.setIsCompleted(true);
+ Task complete2 = helper.generateTaskAfterWithName("Completed Also", complete1);
+ complete2.setIsCompleted(true);
+ List completedTasks = helper.generateTaskList(complete1, complete2);
+
+ assertCommandFilteredList("list pending",
+ ListCommand.MESSAGE_SUCCESS,
+ completedTasks,
+ true);
+ }
+
+ /**
+ * Confirms if execution of the command results in a list that only contains a subset of the list.
+ * @param command the command on test
+ * @param message the expected message to be displayed to the user upon execution of command
+ * @param taskList non-generated list of tasks in the task list
+ * @param isInverse if true, it will assert if taskList if NOT displayed, else only taskList is displayed
+ */
+ private void assertCommandFilteredList(String command, String message, List taskList, boolean isInverse) throws Exception {
+ // prepare initial state
+ TestDataHelper helper = new TestDataHelper();
+ helper.addToModel(model, 9);
+ helper.addToModel(model, taskList);
+
+ // prepare expected
+ ReadOnlyTaskManager expectedTaskManager = model.getTaskManager();
+ List expectedTaskList = (isInverse) ? helper.generateTaskList(9) : taskList;
+
+ assertCommandBehavior(command, message, expectedTaskManager, expectedTaskList);
+ }
+
+```
+###### \java\seedu\task\logic\LogicManagerTest.java
+``` java
+ @Test
+ public void execute_find_swithCommandBoxInitiated() throws Exception {
+ assertCommandBehavior("searchbox", SearchCommand.MESSAGE_SEARCH_SUCCESS);
+ assertTrue(hasSwitchedToSearch);
+ }
+```
+###### \java\seedu\task\logic\LogicManagerTest.java
+``` java
+ /**
+ * Generates a Task object with given name and ensures that the Task appears
+ * after the specified Task. No guarantee that other Task will appear before it.
+ */
+ Task generateTaskAfterWithName(String name, Task preceedingTask) throws Exception {
+ return new Task(
+ new Name(name),
+ DateTime.fromUserInput("tomorrow"),
+ DateTime.fromDateTimeOffset(preceedingTask.getCloseTime(), 5, ChronoUnit.MINUTES),
+ false,
+ false,
+ new UniqueTagList(new Tag("tag"))
+ );
+ }
+```
+###### \java\seedu\task\testutil\TestTask.java
+``` java
+ /**
+ * Creates a duplicate (copy) of an existing TestTask
+ * @param task the TestTask to copy from
+ */
+ public TestTask(TestTask task) {
+ this.name = task.getName();
+ this.openTime = task.getOpenTime();
+ this.closeTime = task.getCloseTime();
+ this.isCompleted = task.getComplete();
+ this.isImportant = task.getImportance();
+ this.tags = new UniqueTagList(task.getTags());
+ }
+```
+###### \java\seedu\task\testutil\TestUtil.java
+``` java
+ /**
+ * Moves the Task from an index to another. Does not replace the item in the other index.
+ * @param tasks The array of Tasks
+ * @param oldIndex The index of the Task to move
+ * @param newIndex The destination index of the Task
+ * @return The modified array of Tasks.
+ */
+ public static TestTask[] moveTaskInList(final TestTask[] tasks, int oldIndex, int newIndex) {
+ List listOfTasks = asList(tasks);
+ TestTask taskToMove = listOfTasks.remove(oldIndex);
+ listOfTasks.add(newIndex, taskToMove);
+ return listOfTasks.toArray(new TestTask[listOfTasks.size()]);
+ }
+
+ /**
+ * Adds a Task to the array of Tasks at the specified location.
+ * @param tasks An array of Tasks.
+ * @param taskToAdd The Task to be added
+ * @param index The index of the location in the array where the Task to be added should reside
+ * @return The modified array of Tasks.
+ */
+ public static TestTask[] insertTaskToList(final TestTask[] tasks, TestTask taskToAdd, int index) {
+ List listOfTasks = asList(tasks);
+ listOfTasks.add(index, taskToAdd);
+ return listOfTasks.toArray(new TestTask[listOfTasks.size()]);
+ }
+```
diff --git a/collated/test/A0144939R.md b/collated/test/A0144939R.md
new file mode 100644
index 000000000000..f8d02e17b51e
--- /dev/null
+++ b/collated/test/A0144939R.md
@@ -0,0 +1,297 @@
+# A0144939R
+###### \java\guitests\ChangePathCommandTest.java
+``` java
+package guitests;
+
+
+import java.io.File;
+import org.junit.Test;
+import seedu.task.logic.commands.ChangePathCommand;
+import seedu.task.testutil.TestUtil;
+
+public class ChangePathCommandTest extends TaskManagerGuiTest {
+ @Test
+ public void changePath() throws InterruptedException {
+
+ //Try with non xml file
+ String nonXmlFilePath = TestUtil.getFilePathInSandboxFolder("taskmanager.txt");
+ commandBox.runCommand("change-to " + nonXmlFilePath);
+ assertResultMessage(String.format(ChangePathCommand.MESSAGE_PATH_CHANGE_FAIL, nonXmlFilePath));
+
+ //Try with unwritable file path
+ String unWriteableFilePath = TestUtil.getFilePathInSandboxFolder("unwritable.xml");
+ File unWriteableFolder = new File(unWriteableFilePath).getParentFile();
+
+ // check if test is run on Windows, as Windows has bad support for writeable flags
+ if (unWriteableFolder.setWritable(false)) {
+ Thread.sleep(300);
+ commandBox.runCommand("change-to " + unWriteableFilePath);
+ assertResultMessage(String.format(ChangePathCommand.MESSAGE_PATH_CHANGE_FAIL, unWriteableFilePath));
+ unWriteableFolder.setWritable(true);
+ Thread.sleep(300);
+ } else {
+ unWriteableFolder.setWritable(true);
+ }
+
+ //Try with empty String
+ String emptyPath = TestUtil.getFilePathInSandboxFolder("");
+ commandBox.runCommand("change-to " + emptyPath);
+ assertResultMessage(String.format(ChangePathCommand.MESSAGE_PATH_CHANGE_FAIL, emptyPath));
+
+ //Test successful
+ String newFilePath = TestUtil.getFilePathInSandboxFolder("newFile.xml");
+ commandBox.runCommand("change-to " + newFilePath);
+ assertResultMessage(String.format(ChangePathCommand.MESSAGE_PATH_CHANGE_SUCCESS, newFilePath));
+
+
+ }
+}
+```
+###### \java\seedu\task\logic\LogicManagerTest.java
+``` java
+
+ /**
+ * Tests for add command
+ * @throws Exception
+ */
+ @Test
+ public void execute_add_invalidArgsFormat() throws Exception {
+ String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE);
+ assertCommandBehavior(
+ "add starts today ends tomorrow", expectedMessage);
+ }
+
+ @Test
+ public void execute_add_invalidTaskData() throws Exception {
+ assertCommandBehavior(
+ "add []\\[;]", Name.MESSAGE_NAME_CONSTRAINTS);
+ assertCommandBehavior(
+ "add Do CS2103 tag invalid_-[.tag", Tag.MESSAGE_TAG_CONSTRAINTS);
+ assertCommandBehavior("add Do CS2103 starts hello", DateTime.MESSAGE_DATETIME_CONSTRAINTS);
+ assertCommandBehavior("add Do CS2103 starts hello ends bbye", DateTime.MESSAGE_DATETIME_CONSTRAINTS);
+ assertCommandBehavior("add Do CS2103 ends bbye", DateTime.MESSAGE_DATETIME_CONSTRAINTS);
+ assertCommandBehavior("add Do CS2103 starts tomorrow ends today", Task.MESSAGE_DATETIME_CONSTRAINTS);
+ assertCommandBehavior("add Do CS2103 starts 6 hours from now ends 3 hours from now", Task.MESSAGE_DATETIME_CONSTRAINTS);
+
+
+ }
+ @Test
+ public void execute_add_successful() throws Exception {
+ // setup expectations
+ TestDataHelper helper = new TestDataHelper();
+ Task toBeAdded = helper.adam();
+ TaskManager expectedAB = new TaskManager();
+ expectedAB.addTask(toBeAdded);
+
+ // execute command and verify result
+ assertCommandBehavior(helper.generateAddCommand("add", toBeAdded),
+ String.format(AddCommand.MESSAGE_SUCCESS, toBeAdded),
+ expectedAB,
+ expectedAB.getTaskList());
+
+ }
+ @Test
+ public void execute_addDuplicate_notAllowed() throws Exception {
+ // setup expectations
+ TestDataHelper helper = new TestDataHelper();
+ Task toBeAdded = helper.adam();
+ TaskManager expectedAB = new TaskManager();
+ expectedAB.addTask(toBeAdded);
+
+ // setup starting state
+ model.addTask(toBeAdded); // Task already in internal task list
+
+ // execute command and verify result
+ assertCommandBehavior(
+ helper.generateAddCommand("add", toBeAdded),
+ AddCommand.MESSAGE_DUPLICATE_TASK,
+ expectedAB,
+ expectedAB.getTaskList());
+
+ }
+
+ @Test
+ public void execute_list_showsAllTasks() throws Exception {
+ // prepare expectations
+ TestDataHelper helper = new TestDataHelper();
+ TaskManager expectedAB = helper.generateTaskManager(2);
+ List extends ReadOnlyTask> expectedList = expectedAB.getTaskList();
+
+ // prepare task list state
+ helper.addToModel(model, 2);
+
+ assertCommandBehavior("list all",
+ ListCommand.MESSAGE_SUCCESS,
+ expectedAB,
+ expectedList);
+ }
+
+```
+###### \java\seedu\task\logic\LogicManagerTest.java
+``` java
+ /** Generates the correct add command based on the Task given */
+ public String generateAddCommand(String alias, Task p) {
+ StringBuffer cmd = new StringBuffer();
+
+ cmd.append(alias+" ");
+
+ cmd.append(p.getName().toString());
+
+
+ UniqueTagList tags = p.getTags();
+ for(Tag t: tags){
+ cmd.append(" tag ").append(t.tagName);
+ }
+
+ return cmd.toString();
+ }
+```
+###### \java\seedu\task\testutil\TaskBuilder.java
+``` java
+ public TaskBuilder withOpenTime(String openTime) throws IllegalValueException {
+ this.task.setOpenTime(DateTime.fromUserInput(openTime));
+ return this;
+ }
+
+ public TaskBuilder withCloseTime(String closeTime) throws IllegalValueException {
+ this.task.setCloseTime(DateTime.fromUserInput(closeTime));
+ return this;
+ }
+
+```
+###### \java\seedu\task\testutil\TestTask.java
+``` java
+package seedu.task.testutil;
+
+import seedu.task.model.tag.UniqueTagList;
+import seedu.task.model.task.*;
+
+/**
+ * A mutable task object. For testing only.
+ */
+public class TestTask implements ReadOnlyTask {
+
+ private Name name;
+ private DateTime openTime;
+ private DateTime closeTime;
+ private UniqueTagList tags;
+ private boolean isImportant;
+ private boolean isCompleted;
+
+ public TestTask() {
+ tags = new UniqueTagList();
+ openTime = new DateTime();
+ closeTime = new DateTime();
+ }
+
+```
+###### \java\seedu\task\testutil\TestUtil.java
+``` java
+ private static Task[] getSampleTaskData() {
+ try {
+ return new Task[]{
+ new Task(new Name("Ali Muster"), new DateTime(null), new DateTime(null), false, false, new UniqueTagList()),
+ new Task(new Name("Boris Mueller"), new DateTime(null), new DateTime(null), false, false, new UniqueTagList()),
+ new Task(new Name("Carl Kurz"), new DateTime(null), new DateTime(null), false, false, new UniqueTagList()),
+ new Task(new Name("Daniel Meier"), new DateTime(null), new DateTime(null), false, false, new UniqueTagList()),
+ new Task(new Name("Elle Meyer"), new DateTime(null), new DateTime(null), false, false, new UniqueTagList()),
+ new Task(new Name("Fiona Kunz"), new DateTime(null), new DateTime(null), false, false, new UniqueTagList()),
+ new Task(new Name("George Best"), new DateTime(null), new DateTime(null), false, false, new UniqueTagList()),
+ new Task(new Name("Hoon Meier"), new DateTime(null), new DateTime(null), false, false, new UniqueTagList()),
+ new Task(new Name("Ida Mueller"), new DateTime(null), new DateTime(null), false, false, new UniqueTagList()),
+ };
+ } catch (IllegalValueException e) {
+ assert false;
+ //not possible
+ return null;
+ }
+ }
+```
+###### \java\seedu\task\testutil\TypicalTestTasks.java
+``` java
+package seedu.task.testutil;
+
+import seedu.task.commons.exceptions.IllegalValueException;
+import seedu.task.model.TaskManager;
+import seedu.task.model.task.*;
+
+/**
+ *
+ */
+public class TypicalTestTasks {
+
+ public static TestTask cs2103, laundry, carl, daniel, elle, fiona, george, first, last, hoon, ida, same, recur, recur2, name;
+
+ public TypicalTestTasks() {
+ try {
+ cs2103 = new TaskBuilder().withName("Do CS 2103").withOpenTime("9 hours from now")
+ .withCloseTime("17 hours from now").withTags("friends").withImportance(false).build();
+ laundry = new TaskBuilder().withName("Do Meier laundry").withOpenTime("tomorrow")
+ .withCloseTime("day after tomorrow").withImportance(false).withTags("urgent", "important").build();
+ carl = new TaskBuilder().withName("Meet Carl").withOpenTime("5 days from now")
+ .withCloseTime("14 days from now").withTags("friends").build();
+ daniel = new TaskBuilder().withName("Have lunch with Meier").withOpenTime("6 hours from now")
+ .withCloseTime("8 hours from now").build();
+ elle = new TaskBuilder().withName("Take Ellie out on a date").withOpenTime("6 hours from now")
+ .withCloseTime("12 hours from now").build();
+ fiona = new TaskBuilder().withName("Buy a Shrek and Fiona Toy").withOpenTime("tomorrow")
+ .withCloseTime("day after tomorrow").build();
+ george = new TaskBuilder().withName("Watch George Best Videos").withOpenTime("tomorrow")
+ .withCloseTime("day after tomorrow").build();
+
+ //Manually added
+ first = new TaskBuilder().withName("Alphabet soup making session").withOpenTime("1 hour from now")
+ .withCloseTime("2 hours from now").withTags("first").build();
+ last = new TaskBuilder().withName("Zebra feeding").build();
+
+ hoon = new TaskBuilder().withName("Hoon Meier").withOpenTime("one hour from now")
+ .withCloseTime("tomorrow").withTags("omg").withImportance(false).build();//.withRecurrentWeek(0)
+ ida = new TaskBuilder().withName("Ida Mueller").withOpenTime("two weeks from now")
+ .withCloseTime("one month from now").build();
+ same =new TaskBuilder().withName("Ida Mueller").withOpenTime("one month from now")
+ .withCloseTime("two months from now").build();
+
+ recur = new TaskBuilder().withName("Prepare for future").withOpenTime("10 days from now")
+ .withCloseTime("16 days from now").build();
+
+ recur2 = new TaskBuilder().withName("Do Homework for next semester").withOpenTime("20 days from now")
+ .withCloseTime("24 days from now").build();
+ name = new TaskBuilder().withName("task name").build();
+
+ } catch (IllegalValueException e) {
+ e.printStackTrace();
+ assert false : "not possible";
+ }
+ }
+
+ public static void loadTaskManagerWithSampleData(TaskManager ab) throws IllegalValueException {
+
+ try {
+ ab.addTask(new Task(cs2103));
+ ab.addTask(new Task(laundry));
+ ab.addTask(new Task(carl));
+ ab.addTask(new Task(daniel));
+ ab.addTask(new Task(elle));
+ ab.addTask(new Task(fiona));
+ ab.addTask(new Task(george));
+ } catch (UniqueTaskList.DuplicateTaskException e) {
+ assert false : "not possible";
+ }
+ }
+
+ public TestTask[] getTypicalTasks() {
+ return new TestTask[]{daniel, elle, cs2103, fiona, laundry, george, carl};
+ }
+
+ public TaskManager getTypicalTaskManager(){
+ TaskManager ab = new TaskManager();
+ try {
+ loadTaskManagerWithSampleData(ab);
+ } catch (IllegalValueException e) {
+ e.printStackTrace();
+ assert false: "Invalid sample task data";
+ }
+ return ab;
+ }
+}
+```
diff --git a/collated/test/A0153467Y.md b/collated/test/A0153467Y.md
new file mode 100644
index 000000000000..5d4ca85032c5
--- /dev/null
+++ b/collated/test/A0153467Y.md
@@ -0,0 +1,318 @@
+# A0153467Y
+###### \java\guitests\CompleteCommandTest.java
+``` java
+package guitests;
+
+import static org.junit.Assert.assertTrue;
+import static seedu.task.logic.commands.CompleteCommand.MESSAGE_COMPLETE_TASK_SUCCESS;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import seedu.task.commons.core.Messages;
+import seedu.task.model.task.ReadOnlyTask;
+import seedu.task.testutil.TestTask;
+
+public class CompleteCommandTest extends TaskManagerGuiTest {
+ private TestTask[] currentList;
+
+ @Before
+ public void runOnceBeforeClass() {
+ currentList = td.getTypicalTasks();
+ }
+
+ /**
+ * Complete tests -- Task ID is guaranteed the last one due to preservation of
+ * total ordering within completed tasks.
+ */
+ @Test
+ public void complete() {
+ int targetIndex = 1;
+
+ // mark the first task as complete
+ commandBox.runCommand("complete " + targetIndex);
+ ReadOnlyTask newTask = taskListPanel.getTask(currentList.length - 1);
+ assertTrue(newTask.getComplete());
+
+ // confirm the result message is correct
+ assertResultMessage(String.format(MESSAGE_COMPLETE_TASK_SUCCESS, newTask));
+
+ // mark another task as complete
+ targetIndex = 3;
+ commandBox.runCommand("complete " + targetIndex);
+ ReadOnlyTask otherTask = taskListPanel.getTask(currentList.length - 1);
+ assertTrue(otherTask.getComplete());
+ assertResultMessage(String.format(MESSAGE_COMPLETE_TASK_SUCCESS, otherTask));
+
+ // mark the last task as complete
+ targetIndex = currentList.length;
+ commandBox.runCommand("complete " + targetIndex);
+ newTask = taskListPanel.getTask(currentList.length - 1);
+ assertTrue(newTask.getComplete());
+ assertResultMessage(String.format(MESSAGE_COMPLETE_TASK_SUCCESS, newTask));
+ }
+
+ @Test
+ public void invalidComplete() {
+ // invalid index
+ commandBox.runCommand("complete " + (currentList.length + 1));
+ assertResultMessage(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX);
+
+ // mark at an empty list
+ commandBox.runCommand("clear");
+ commandBox.runCommand("complete " + (currentList.length + 1));
+ assertResultMessage(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX);
+ }
+
+}
+```
+###### \java\guitests\PinCommandTest.java
+``` java
+package guitests;
+
+import static org.junit.Assert.assertTrue;
+import static seedu.task.logic.commands.PinCommand.MESSAGE_PIN_TASK_SUCCESS;
+import static seedu.task.logic.commands.PinCommand.MESSAGE_USAGE;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import seedu.task.commons.core.Messages;
+import seedu.task.model.task.ReadOnlyTask;
+import seedu.task.testutil.TestTask;
+
+public class PinCommandTest extends TaskManagerGuiTest {
+
+ private TestTask[] currentList;
+
+ @Before
+ public void runOnceBeforeClass() {
+ currentList = td.getTypicalTasks();
+ }
+
+ /**
+ * Pin tests -- Task ID is guaranteed due to preservation of
+ * total ordering within pinned tasks.
+ */
+ @Test
+ public void pinTask() {
+ int targetIndex = 1;
+
+ // pin the first task
+ commandBox.runCommand("pin " + targetIndex);
+ ReadOnlyTask newTask = taskListPanel.getTask(targetIndex - 1);
+ assertTrue(newTask.getImportance());
+ // confirm the result message is correct
+ assertResultMessage(String.format(MESSAGE_PIN_TASK_SUCCESS, newTask));
+
+ // pin another task
+ targetIndex = 3;
+ commandBox.runCommand("pin " + targetIndex);
+ ReadOnlyTask otherTask = taskListPanel.getTask(1);
+ assertTrue(otherTask.getImportance());
+ assertResultMessage(String.format(MESSAGE_PIN_TASK_SUCCESS, otherTask));
+
+ // pin the last task
+ targetIndex = currentList.length;
+ commandBox.runCommand("pin " + targetIndex);
+ newTask = taskListPanel.getTask(2);
+ assertTrue(newTask.getImportance());
+ assertResultMessage(String.format(MESSAGE_PIN_TASK_SUCCESS, newTask));
+ }
+
+ @Test
+ public void pinTask_emptyList() {
+ // pin at an empty list
+ commandBox.runCommand("clear");
+ commandBox.runCommand("pin " + (currentList.length + 1));
+ assertResultMessage(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX);
+ }
+
+ @Test
+ public void invalidPinTask() {
+ // invalid index
+ commandBox.runCommand("pin " + (currentList.length + 1));
+ assertResultMessage(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX);
+
+ // invalid command
+ commandBox.runCommand("pin");
+ assertResultMessage(String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT, MESSAGE_USAGE));
+ }
+}
+```
+###### \java\guitests\UncompleteCommandTest.java
+``` java
+package guitests;
+
+import static org.junit.Assert.assertFalse;
+import static seedu.task.logic.commands.UncompleteCommand.MESSAGE_UNCOMPLETE_TASK_SUCCESS;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import seedu.task.commons.core.Messages;
+import seedu.task.model.task.ReadOnlyTask;
+import seedu.task.testutil.TestTask;
+
+public class UncompleteCommandTest extends TaskManagerGuiTest {
+
+ private TestTask[] currentList;
+
+ @Before
+ public void runOnceBeforeClass() {
+ currentList = td.getTypicalTasks();
+ }
+
+ @Test
+ public void uncomplete() {
+ int targetIndex = 1;
+
+ // mark an originally completed task as not complete
+ commandBox.runCommand("complete " + targetIndex);
+ commandBox.runCommand("uncomplete " + currentList.length);
+ ReadOnlyTask newTask = taskListPanel.getTask(targetIndex - 1);
+ assertFalse(newTask.getComplete());
+ assertResultMessage(String.format(MESSAGE_UNCOMPLETE_TASK_SUCCESS, newTask));
+
+ // unmark another task
+ targetIndex = 3;
+ commandBox.runCommand("complete " + targetIndex);
+ commandBox.runCommand("uncomplete " + currentList.length);
+ newTask = taskListPanel.getTask(targetIndex - 1);
+ assertFalse(newTask.getComplete());
+ assertResultMessage(String.format(MESSAGE_UNCOMPLETE_TASK_SUCCESS, newTask));
+
+ // unmark a task which is not marked as complete before
+ targetIndex = currentList.length;
+ commandBox.runCommand("uncomplete " + targetIndex);
+ newTask = taskListPanel.getTask(targetIndex - 1);
+ // this task should still be marked as not complete
+ assertFalse(newTask.getComplete());
+ assertResultMessage(Messages.MESSAGE_INVALID_UNCOMPLETE_TASK);
+ }
+
+ @Test
+ public void invalidUncomplete() {
+ // invalid index
+ commandBox.runCommand("uncomplete " + (currentList.length + 1));
+ assertResultMessage(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX);
+
+ // mark at an empty list
+ commandBox.runCommand("clear");
+ commandBox.runCommand("uncomplete " + (currentList.length + 1));
+ assertResultMessage(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX);
+ }
+
+}
+```
+###### \java\guitests\UnpinCommandTest.java
+``` java
+package guitests;
+
+import static org.junit.Assert.assertFalse;
+import static seedu.task.logic.commands.UnpinCommand.MESSAGE_UNPIN_TASK_SUCCESS;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import seedu.task.commons.core.Messages;
+import seedu.task.model.task.ReadOnlyTask;
+import seedu.task.testutil.TestTask;
+
+public class UnpinCommandTest extends TaskManagerGuiTest {
+
+ private TestTask[] currentList;
+
+ @Before
+ public void runOnceBeforeClass() {
+ currentList = td.getTypicalTasks();
+ }
+
+ @Test
+ public void unpin() {
+ int targetIndex = 1;
+
+ // unpin the first task
+ commandBox.runCommand("pin " + targetIndex);
+ commandBox.runCommand("unpin " + targetIndex);
+ ReadOnlyTask newTask = taskListPanel.getTask(targetIndex - 1);
+ assertFalse(newTask.getImportance());
+ assertResultMessage(String.format(MESSAGE_UNPIN_TASK_SUCCESS, newTask));
+
+ // unpin one more task, also checks that pinning moves task to top
+ targetIndex = currentList.length;
+ commandBox.runCommand("pin " + targetIndex);
+ commandBox.runCommand("unpin " + 1);
+ newTask = taskListPanel.getTask(targetIndex - 1);
+ assertFalse(newTask.getImportance());
+ assertResultMessage(String.format(MESSAGE_UNPIN_TASK_SUCCESS, newTask));
+ }
+
+ @Test
+ public void invalidUnpin() {
+ // unpin a task which is not pinned
+ int targetIndex = 3;
+ commandBox.runCommand("unpin " + targetIndex);
+ ReadOnlyTask newTask = taskListPanel.getTask(targetIndex - 1);
+
+ // check that the task is still not pinned
+ assertFalse(newTask.getImportance());
+ assertResultMessage(Messages.MESSAGE_INVALID_UNPIN_TASK);
+
+ // invalid command
+ commandBox.runCommand("unppinn");
+ assertResultMessage(Messages.MESSAGE_UNKNOWN_COMMAND);
+
+ // invalid index
+ commandBox.runCommand("unpin " + (currentList.length + 1));
+ assertResultMessage(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX);
+ }
+
+ @Test
+ public void unpinTaskEmptyList() {
+ // unpin at an empty list
+ commandBox.runCommand("clear");
+ commandBox.runCommand("unpin " + (currentList.length + 1));
+ assertResultMessage(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX);
+ }
+}
+```
+###### \java\seedu\task\testutil\TaskBuilder.java
+``` java
+ public TaskBuilder withImportance(boolean isImportant) throws IllegalValueException{
+ this.task.setIsImportant(isImportant);
+ return this;
+ }
+```
+###### \java\seedu\task\testutil\TestTask.java
+``` java
+ public void setIsImportant(boolean isImportant){
+ this.isImportant=isImportant;
+ }
+```
+###### \java\seedu\task\testutil\TestTask.java
+``` java
+ public void setIsCompleted(boolean isCompleted){
+ this.isCompleted = isCompleted;
+ }
+
+ @Override
+ public Name getName() {
+ return name;
+ }
+```
+###### \java\seedu\task\testutil\TestTask.java
+``` java
+ @Override
+ public boolean getComplete() {
+ return isCompleted;
+ }
+```
+###### \java\seedu\task\testutil\TestTask.java
+``` java
+
+ @Override
+ public boolean getImportance() {
+ return isImportant;
+ }
+```
diff --git a/collated/test/A0153467Yunused.md b/collated/test/A0153467Yunused.md
new file mode 100644
index 000000000000..482609e253d4
--- /dev/null
+++ b/collated/test/A0153467Yunused.md
@@ -0,0 +1,35 @@
+# A0153467Yunused
+###### \java\guitests\AddCommandTest.java
+``` java
+ @Ignore @Test
+ public void add_recurring_task() {
+ TestTask[] currentList = td.getTypicalTasks();
+
+ //recur a task zero times (i.e. no recurrence at all)
+ TestTask taskToAdd = TypicalTestTasks.recur;
+ currentList = assertAddRecurringSuccess(0, taskToAdd, currentList);
+ assertResultMessage(String.format(AddCommand.MESSAGE_SUCCESS, taskToAdd));
+
+ //recur a task twenty times (maximum amount)
+ taskToAdd = TypicalTestTasks.recur2;
+ currentList = assertAddRecurringSuccess(20, taskToAdd, currentList);
+ assertResultMessage(String.format(AddCommand.MESSAGE_SUCCESS, taskToAdd));
+
+ //recurring task number exceeds the maximum
+ commandBox.runCommand("add testRecurring recurs 21");
+ assertResultMessage(AddCommand.MESSAGE_WRONG_NUMBER_OF_RECURRENCE);
+
+ // recurring number of task is negative
+ commandBox.runCommand("add testRecurring recurs -1");
+ assertResultMessage(AddCommand.MESSAGE_NEGATIVE_NUMBER_OF_RECURRENCE);
+
+ //invalid recurring argument with alphanumeric is not allowed
+ commandBox.runCommand("add testRecurring recurs abc");
+ assertResultMessage(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE));
+
+ //missing recurring argument
+ commandBox.runCommand("add testRecurring recurs ");
+ assertResultMessage(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE));
+ }
+
+```
diff --git a/copyright.txt b/copyright.txt
deleted file mode 100644
index 93aa2a39ce25..000000000000
--- a/copyright.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-Some code adapted from http://code.makery.ch/library/javafx-8-tutorial/ by Marco Jakob
-
-Copyright by Susumu Yoshida - http://www.mcdodesign.com/
-- address_book_32.png
-- AddressApp.ico
-
-Copyright by Jan Jan Kovařík - http://glyphicons.com/
-- calendar.png
-- edit.png
diff --git a/docs/AboutUs.md b/docs/AboutUs.md
index 33df65bea583..e4846ec05ac7 100644
--- a/docs/AboutUs.md
+++ b/docs/AboutUs.md
@@ -1,52 +1,67 @@
# About Us
-We are a team based in the [School of Computing, National University of Singapore](http://www.comp.nus.edu.sg).
+We are a team of students studying CS2103 based in the [School of Computing, National University of Singapore](http://www.comp.nus.edu.sg).
## Project Team
-#### [Damith C. Rajapakse](http://www.comp.nus.edu.sg/~damithch)
-
-**Role**: Project Advisor
+#### [Advay Pal](https://github.com/advaypal)
+
+* Components in charge of: [Storage](https://github.com/CS2103AUG2016-T14-C4/main/blob/master/docs/DeveloperGuide.md#storage-component), [Model](https://github.com/CS2103AUG2016-T14-C4/main/blob/master/docs/DeveloperGuide.md#model-component)
+* Features implemented:
+ * [Support for non floating task and integration of Date Time Model](https://github.com/CS2103AUG2016-T14-C4/main/pull/45)
+ * [Ability to Change Storage](https://github.com/CS2103AUG2016-T14-C4/main/pull/64)
+ * [More natural commands](https://github.com/CS2103AUG2016-T14-C4/main/pull/69)
+ * [Alias Commands](https://github.com/CS2103AUG2016-T14-C4/main/pull/82)
+* Code written: [[functional code](https://github.com/CS2103AUG2016-T14-C4/main/blob/master/collated/main/A0144939R.md)][[test code](https://github.com/CS2103AUG2016-T14-C4/main/blob/master/collated/test/A0144939R.md)][[docs](https://github.com/CS2103AUG2016-T14-C4/main/blob/master/collated/docs/A0144939R.md)]
+* Other major contributions:
+ * Did the initial refactoring from AddressBook to ToDoList [[#39](https://github.com/CS2103AUG2016-T14-C4/main/pull/39) ]
+ * Set up Codacy
-----
-#### [Joshua Lee](http://github.com/lejolly)
-
-Role: Developer
-Responsibilities: UI
+#### [Abdul Haliq](https://github.com/AHaliq)
+
-----
-#### [Leow Yijin](http://github.com/yijinl)
-
-Role: Developer
-Responsibilities: Data
+#### [Syed Abdullah](https://github.com/Skaty)
+
+* Components in charge of: [Logic](https://github.com/CS2103AUG2016-T14-C4/main/blob/master/docs/DeveloperGuide.md#logic-component), [Model](https://github.com/CS2103AUG2016-T14-C4/main/blob/master/docs/DeveloperGuide.md#model-component), [UI](https://github.com/CS2103AUG2016-T14-C4/main/blob/master/docs/DeveloperGuide.md#ui-component)
+* Features implemented:
+ * [Update command: allow selective field changes and non-floating tasks](https://github.com/CS2103AUG2016-T14-C4/main/pull/33)
+ * [Undo command](https://github.com/CS2103AUG2016-T14-C4/main/pull/47)
+ * [Flexible command format](https://github.com/CS2103AUG2016-T14-C4/main/pull/65)
+ * [Live Search](https://github.com/CS2103AUG2016-T14-C4/main/pull/67)
+ * [Preset filters for Task list (e.g. all, completed, pending, pinned)](https://github.com/CS2103AUG2016-T14-C4/main/pull/85)
+* Code written: [[functional code](https://github.com/CS2103AUG2016-T14-C4/main/blob/master/collated/main/A0141052Y.md)][[test code](https://github.com/CS2103AUG2016-T14-C4/main/blob/master/collated/test/A0141052Y.md)][[docs](https://github.com/CS2103AUG2016-T14-C4/main/blob/master/collated/docs/A0141052Y.md)]
+* Other major contributions:
+ * Manage pull requests, merges and releases
+ * UI changes: nicer tags, visual indicator for pinned and completed tasks
-----
-#### [Martin Choo](http://github.com/m133225)
-
-Role: Developer
-Responsibilities: Dev Ops
-
------
-
-#### [Thien Nguyen](https://github.com/ndt93)
- Role: Developer
- Responsibilities: Threading
-
+#### [Tsang Cheuk Ling](https://github.com/SukiTsang)
+
+* Components in charge of: [Logic](https://github.com/CS2103AUG2016-T14-C4/main/blob/master/docs/DeveloperGuide.md#logic-component)
+* Aspects in charge of: Documentation, deliverables and deadlines
+* Features implemented:
+ * [Pin Task](https://github.com/CS2103AUG2016-T14-C4/main/pull/43)
+ * [Unpin Task](https://github.com/CS2103AUG2016-T14-C4/main/pull/63)
+ * [Complete Task](https://github.com/CS2103AUG2016-T14-C4/main/pull/44)
+ * [Uncomplete Task](https://github.com/CS2103AUG2016-T14-C4/main/pull/62)
+ * [Update Task: only write the basic feature](https://github.com/CS2103AUG2016-T14-C4/main/pull/33) -- implementation is done by Syed
+* Code written: [[functional code](https://github.com/CS2103AUG2016-T14-C4/main/blob/master/collated/main/A0153467Y.md)][[test code](https://github.com/CS2103AUG2016-T14-C4/main/blob/master/collated/test/A0153467Y.md)][[docs](https://github.com/CS2103AUG2016-T14-C4/main/blob/master/collated/docs/A0153467Y.md)]
+* Other major contributions:
+ * [Make diagrams in Developer Guide](https://github.com/CS2103AUG2016-T14-C4/main/pull/60)
+ * [Make mock-up picture of MESS UI](https://github.com/CS2103AUG2016-T14-C4/main/pull/90)
+ * [Make help page](https://github.com/CS2103AUG2016-T14-C4/main/pull/94)
+
-----
-#### [You Liang](http://github.com/yl-coder)
-
- Role: Developer
- Responsibilities: UI
-
- -----
+# Project Mentor
+
+#### Cheng Hang
+
-# Contributors
-We welcome contributions. See [Contact Us](ContactUs.md) page for more info.
-* [Akshay Narayan](https://github.com/se-edu/addressbook-level4/pulls?q=is%3Apr+author%3Aokkhoy)
-* [Sam Yong](https://github.com/se-edu/addressbook-level4/pulls?q=is%3Apr+author%3Amauris)
\ No newline at end of file
diff --git a/docs/ContactUs.md b/docs/ContactUs.md
index 866d0de3fddc..d2851671913a 100644
--- a/docs/ContactUs.md
+++ b/docs/ContactUs.md
@@ -5,4 +5,4 @@
* **Contributing** : We welcome pull requests. Follow the process described [here](https://github.com/oss-generic/process)
-* **Email us** : You can also reach us at `damith [at] comp.nus.edu.sg`
\ No newline at end of file
+* **Email us** : You can also reach us at `e0025573 [at] u.nus.edu`
diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md
index bc710ed45eb9..94f635e05b99 100644
--- a/docs/DeveloperGuide.md
+++ b/docs/DeveloperGuide.md
@@ -1,4 +1,4 @@
-# Developer Guide
+# Developer Guide
* [Setting Up](#setting-up)
* [Design](#design)
@@ -9,8 +9,7 @@
* [Appendix B: Use Cases](#appendix-b--use-cases)
* [Appendix C: Non Functional Requirements](#appendix-c--non-functional-requirements)
* [Appendix D: Glossary](#appendix-d--glossary)
-* [Appendix E : Product Survey](#appendix-e-product-survey)
-
+* [Appendix E : Product Survey](#appendix-e--product-survey)
## Setting up
@@ -18,29 +17,28 @@
1. **JDK `1.8.0_60`** or later
- > Having any Java 8 version is not enough.
- This app will not work with earlier versions of Java 8.
-
+ > Having any Java 8 version is not enough.
+ This app will not work with earlier versions of Java 8.
+
2. **Eclipse** IDE
3. **e(fx)clipse** plugin for Eclipse (Do the steps 2 onwards given in
- [this page](http://www.eclipse.org/efxclipse/install.html#for-the-ambitious))
+ [this page](http://www.eclipse.org/efxclipse/install.html#for-the-ambitious))
4. **Buildship Gradle Integration** plugin from the Eclipse Marketplace
-
#### Importing the project into Eclipse
0. Fork this repo, and clone the fork to your computer
-1. Open Eclipse (Note: Ensure you have installed the **e(fx)clipse** and **buildship** plugins as given
- in the prerequisites above)
+1. Open Eclipse (Note: Ensure you have installed the **e(fx)clipse** and **buildship** plugins as given
+ in the prerequisites above)
2. Click `File` > `Import`
3. Click `Gradle` > `Gradle Project` > `Next` > `Next`
4. Click `Browse`, then locate the project's directory
5. Click `Finish`
- > * If you are asked whether to 'keep' or 'overwrite' config files, choose to 'keep'.
- > * Depending on your connection speed and server load, it can even take up to 30 minutes for the set up to finish
- (This is because Gradle downloads library files from servers during the project set up process)
- > * If Eclipse auto-changed any settings files during the import process, you can discard those changes.
+ > * If you are asked whether to 'keep' or 'overwrite' config files, choose to 'keep'.
+ > * Depending on your connection speed and server load, it can even take up to 30 minutes for the set up to finish
+ (This is because Gradle downloads library files from servers during the project set up process)
+ > * If Eclipse auto-changed any settings files during the import process, you can discard those changes.
## Design
@@ -50,18 +48,18 @@
The **_Architecture Diagram_** given above explains the high-level design of the App.
Given below is a quick overview of each component.
-`Main` has only one class called [`MainApp`](../src/main/java/seedu/address/MainApp.java). It is responsible for,
+`Main` has only one class called [`MainApp`](../src/main/java/seedu/task/MainApp.java). It is responsible for,
* At app launch: Initializes the components in the correct sequence, and connect them up with each other.
* At shut down: Shuts down the components and invoke cleanup method where necessary.
[**`Commons`**](#common-classes) represents a collection of classes used by multiple other components.
Two of those classes play important roles at the architecture level.
* `EventsCentre` : This class (written using [Google's Event Bus library](https://github.com/google/guava/wiki/EventBusExplained))
- is used by components to communicate with other components using events (i.e. a form of _Event Driven_ design)
+ is used by components to communicate with other components using events (i.e. a form of _Event Driven_ design)
* `LogsCenter` : Used by many classes to write log messages to the App's log file.
The rest of the App consists four components.
-* [**`UI`**](#ui-component) : The UI of tha App.
+* [**`UI`**](#ui-component) : The UI of the App.
* [**`Logic`**](#logic-component) : The command executor.
* [**`Model`**](#model-component) : Holds the data of the App in-memory.
* [**`Storage`**](#storage-component) : Reads data from, and writes data to, the hard disk.
@@ -75,20 +73,24 @@ interface and exposes its functionality using the `LogicManager.java` class.
The _Sequence Diagram_ below shows how the components interact for the scenario where the user issues the
-command `delete 3`.
+command `add shopping` which refers to adding a floating task named shopping.
+
+
+
+This _Sequence Diagram_ below shows how components interact similarly as above with a different command `delete 1` which refers to delete the first task on the to-do list.
-
+
->Note how the `Model` simply raises a `AddressBookChangedEvent` when the Address Book data are changed,
- instead of asking the `Storage` to save the updates to the hard disk.
+>Note how the `Model` simply raises a `TaskManager ChangedEvent` when the to-do list data are changed,
+instead of asking the `Storage` to save the updates to the hard disk.
The diagram below shows how the `EventsCenter` reacts to that event, which eventually results in the updates
-being saved to the hard disk and the status bar of the UI being updated to reflect the 'Last Updated' time.
-
+being saved to the hard disk and the status bar of the UI being updated to reflect the 'Last Updated' time. The sequence diagram below is the one showing the event handling of add command.
+
> Note how the event is propagated through the `EventsCenter` to the `Storage` and `UI` without `Model` having
- to be coupled to either of them. This is an example of how this Event Driven approach helps us reduce direct
- coupling between components.
+ to be coupled to either of them. This is an example of how this Event Driven approach helps us reduce direct
+ coupling between components.
The sections below give more details of each component.
@@ -96,16 +98,18 @@ The sections below give more details of each component.
-**API** : [`Ui.java`](../src/main/java/seedu/address/ui/Ui.java)
+**API** : [`Ui.java`](../src/main/java/seedu/task/ui/Ui.java)
The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `PersonListPanel`,
`StatusBarFooter`, `BrowserPanel` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class
and they can be loaded using the `UiPartLoader`.
The `UI` component uses JavaFx UI framework. The layout of these UI parts are defined in matching `.fxml` files
- that are in the `src/main/resources/view` folder.
- For example, the layout of the [`MainWindow`](../src/main/java/seedu/address/ui/MainWindow.java) is specified in
- [`MainWindow.fxml`](../src/main/resources/view/MainWindow.fxml)
+that are in the `src/main/resources/view` folder.
+For example, the layout of the [`MainWindow`](../src/main/java/seedu/task/ui/MainWindow.java) is specified in
+[`MainWindow.fxml`](../src/main/resources/view/MainWindow.fxml)
+
+
The `UI` component,
* Executes user commands using the `Logic` component.
@@ -116,43 +120,45 @@ The `UI` component,
-**API** : [`Logic.java`](../src/main/java/seedu/address/logic/Logic.java)
+**API** : [`Logic.java`](../src/main/java/seedu/task/logic/Logic.java)
+
+1. `Logic` uses the `ParseSwitcher` to parse the command.
+2. `ParseSwitcher` uses `ParserMapping` to determine which `BaseParser` object to use.
+3. The `BaseParser` object is executed in `ParserMapping`, which parses the command.
+4. This results in a `Command` object which is executed by the `LogicManager`.
+5. The command execution can affect the `Model` (e.g. adding a person) and/or raise events.
+6. The result of the command execution is encapsulated as a `CommandResult` object which is passed back to the `Ui`.
-1. `Logic` uses the `Parser` class to parse the user command.
-2. This results in a `Command` object which is executed by the `LogicManager`.
-3. The command execution can affect the `Model` (e.g. adding a person) and/or raise events.
-4. The result of the command execution is encapsulated as a `CommandResult` object which is passed back to the `Ui`.
+Given below is the Sequence Diagram for interactions within the `Logic` component for the `execute("delete 1")` API call.
-Given below is the Sequence Diagram for interactions within the `Logic` component for the `execute("delete 1")`
- API call.
-
+
### Model component
-**API** : [`Model.java`](../src/main/java/seedu/address/model/Model.java)
+**API** : [`Model.java`](../src/main/java/seedu/task/model/Model.java)
The `Model`,
* stores a `UserPref` object that represents the user's preferences.
-* stores the Address Book data.
-* exposes a `UnmodifiableObservableList` that can be 'observed' e.g. the UI can be bound to this list
- so that the UI automatically updates when the data in the list change.
+* stores the to-do list data.
+* exposes a `UnmodifiableObservableList` that can be 'observed' e.g. the UI can be bound to this list
+ so that the UI automatically updates when the data in the list change.
* does not depend on any of the other three components.
### Storage component
-**API** : [`Storage.java`](../src/main/java/seedu/address/storage/Storage.java)
+**API** : [`Storage.java`](../src/main/java/seedu/task/storage/Storage.java)
The `Storage` component,
* can save `UserPref` objects in json format and read it back.
-* can save the Address Book data in xml format and read it back.
+* can save the to-do list data in xml format and read it back.
### Common classes
-Classes used by multiple components are in the `seedu.addressbook.commons` package.
+Classes used by multiple components are in the `seedu.task.commons` package.
## Implementation
@@ -162,9 +168,9 @@ We are using `java.util.logging` package for logging. The `LogsCenter` class is
and logging destinations.
* The logging level can be controlled using the `logLevel` setting in the configuration file
- (See [Configuration](#configuration))
+ (See [Configuration](#configuration))
* The `Logger` for a class can be obtained using `LogsCenter.getLogger(Class)` which will log messages according to
- the specified logging level
+ the specified logging level
* Currently log messages are output through: `Console` and to a `.log` file.
**Logging Levels**
@@ -173,52 +179,49 @@ and logging destinations.
* `WARNING` : Can continue, but with caution
* `INFO` : Information showing the noteworthy actions by the App
* `FINE` : Details that is not usually noteworthy but may be useful in debugging
- e.g. print the actual list instead of just its size
+ e.g. print the actual list instead of just its size
### Configuration
-Certain properties of the application can be controlled (e.g App name, logging level) through the configuration file
+Certain properties of the application can be controlled (e.g App name, logging level) through the configuration file
(default: `config.json`):
-
## Testing
Tests can be found in the `./src/test/java` folder.
**In Eclipse**:
> If you are not using a recent Eclipse version (i.e. _Neon_ or later), enable assertions in JUnit tests
- as described [here](http://stackoverflow.com/questions/2522897/eclipse-junit-ea-vm-option).
+ as described [here](http://stackoverflow.com/questions/2522897/eclipse-junit-ea-vm-option).
* To run all tests, right-click on the `src/test/java` folder and choose
- `Run as` > `JUnit Test`
+ `Run as` > `JUnit Test`
* To run a subset of tests, you can right-click on a test package, test class, or a test and choose
- to run as a JUnit test.
+ to run as a JUnit test.
**Using Gradle**:
* See [UsingGradle.md](UsingGradle.md) for how to run tests using Gradle.
We have two types of tests:
-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.
-
+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.
2. **Non-GUI Tests** - These are tests not involving the GUI. They include,
- 1. _Unit tests_ targeting the lowest level methods/classes.
- e.g. `seedu.address.commons.UrlUtilTest`
- 2. _Integration tests_ that are checking the integration of multiple code units
- (those code units are assumed to be working).
- e.g. `seedu.address.storage.StorageManagerTest`
- 3. Hybrids of unit and integration tests. These test are checking multiple code units as well as
- how the are connected together.
- e.g. `seedu.address.logic.LogicManagerTest`
-
+ 1. _Unit tests_ targeting the lowest level methods/classes.
+ e.g. `seedu.task.commons.UrlUtilTest`
+ 2. _Integration tests_ that are checking the integration of multiple code units
+ (those code units are assumed to be working).
+ e.g. `seedu.task.storage.StorageManagerTest`
+ 3. Hybrids of unit and integration tests. These test are checking multiple code units as well as
+ how the are connected together.
+ e.g. `seedu.task.logic.LogicManagerTest`
**Headless GUI Testing** :
Thanks to the [TestFX](https://github.com/TestFX/TestFX) library we use,
- our GUI tests can be run in the _headless_ mode.
- In the headless mode, GUI tests do not show up on the screen.
- That means the developer can do other things on the Computer while the tests are running.
- See [UsingGradle.md](UsingGradle.md#running-tests) to learn how to run tests in headless mode.
-
+our GUI tests can be run in the _headless_ mode.
+In the headless mode, GUI tests do not show up on the screen.
+That means the developer can do other things on the Computer while the tests are running.
+See [UsingGradle.md](UsingGradle.md#running-tests) to learn how to run tests in headless mode.
+
## Dev Ops
### Build Automation
@@ -233,72 +236,341 @@ See [UsingTravis.md](UsingTravis.md) for more details.
### Making a Release
Here are the steps to create a new release.
-
- 1. Generate a JAR file [using Gradle](UsingGradle.md#creating-the-jar-file).
- 2. Tag the repo with the version number. e.g. `v0.1`
- 2. [Crete a new release using GitHub](https://help.github.com/articles/creating-releases/)
- and upload the JAR file your created.
-
+1. Generate a JAR file [using Gradle](UsingGradle.md#creating-the-jar-file).
+2. Tag the repo with the version number. e.g. `v0.1`
+2. [Crete a new release using GitHub](https://help.github.com/articles/creating-releases/)
+ and upload the JAR file your created.
+
### Managing Dependencies
-A project often depends on third-party libraries. For example, Address Book depends on the
-[Jackson library](http://wiki.fasterxml.com/JacksonHome) for XML parsing. Managing these _dependencies_
+A project often depends on third-party libraries. Managing these _dependencies_
can be automated using Gradle. For example, Gradle can download the dependencies automatically, which
is better than these alternatives.
+PrettyTime is another library which is used for time formatting.
a. Include those libraries in the repo (this bloats the repo size)
b. Require developers to download those libraries manually (this creates extra work for developers)
+
+
## 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 a new person |
-`* * *` | user | delete a person | remove entries that I no longer need
-`* * *` | user | find a person by name | locate details of persons without having to go through the entire list
-`* *` | user | hide [private contact details](#private-contact-detail) by default | minimize chance of someone else seeing them by accident
-`*` | user with many persons in the address book | sort persons by name | locate a person easily
-
-{More to be added}
+`* * *` | user | add a task with deadline|
+`* * *` | user | add a task without start time and deadline| keep track of general, non-time based tasks
+`* * *` | user | add an event with start time and close time|
+`* * *` | user | add an event/task with tag|
+`* * *` | user | add recurring tasks
+`* * *` | user | delete a task| remove tasks that I do not have to take any further action on
+`* * *` |user | undo my previous action | recover from commands entered by mistake
+`* * *` | user | find a task from to-do list| find details of tasks without having to go through the entire list
+`* * *` | user | update a task | change the specifications of a specific task
+`* * *` | user | filter a list of pinned tasks or task which are pending | Know what tasks need to be done and what tasks are important
+`* * *` | user | specify the file location of the task list | store the list in a more convenient location
+`* *` | user | see the entire to-do list | know the number of task/ event that I have
+`* *` | user | mark a task as completed | distinguish between completed and pending tasks
+`* *` |user | be able to mark certain tasks as important | easily distinguish tasks that require attention/action to be taken
+`* *` | user | alias commands to symbols
+`* *` | user | do a live search for commands
+`* *` | user | find task by their tag
+`*` | user | unmark a completed task| change my mind if that task is actullay not completed
+`*` | user | unpin a pinned task| change my mind if that task is not important anymore
## Appendix B : Use Cases
-(For all use cases below, the **System** is the `AddressBook` and the **Actor** is the `user`, unless specified otherwise)
+The activity diagram below shows the simple flow of getting command from user
+
-#### Use case: Delete person
+(For all use cases below, the **System** is the `MESS` and the **Actor** is the `user`, unless specified otherwise)
+
+#### Use case: Add task
**MSS**
-1. User requests to list persons
-2. AddressBook shows a list of persons
-3. User requests to delete a specific person in the list
-4. AddressBook deletes the person
+1. User requests to add a task
+2. MESS adds the task to the list
Use case ends.
**Extensions**
-2a. The list is empty
+1a. The input command format is wrong
+> MESS shows an error message with correct input format
+Use case ends.
+
+1b. The task name already exists on the list.
+> MESS shows a message to inform user that task already exists
+Use case ends.
+
+
+#### Use case: Alias command
+
+**MSS**
+
+1. User requests to alias a command
+2. MESS aliases the command to the given symbol
+Use case ends.
+
+**Extensions**
+
+1a. The symbol is already aliased to another command
+> MESS changes the mapping to the new command.
+Use case ends.
+
+1b. The symbol is a standard command name(Eg: add, edit)
+> MESS shows an error message.
+Use case ends.
+
+1c. The command already has an alias
+> MESS maps the given symbol to the command, as a command can have multiple aliases
+Use case ends.
+
+
+
+
+#### Use case: Delete task by index
+
+**MSS**
+
+1. User requests to list tasks
+2. MESS shows a list of tasks
+3. User requests to delete a specific task in the list
+4. MESS deletes the task
+Use case ends.
+
+**Extensions**
+
+1a. The list is empty
> Use case ends
3a. The given index is invalid
-> 3a1. AddressBook shows an error message
- Use case resumes at step 2
+>MESS shows an error message
+ Use case ends.
-{More to be added}
+#### Use case: Update task
-## Appendix C : Non Functional Requirements
+**MSS**
+
+1. User requests to list tasks
+2. MESS shows a list of tasks
+3. User requests to update a specific task by index in the list
+4. MESS updates the task
+Use case ends.
+
+**Extensions**
+
+1a. The list is empty
+
+> User case ends
+
+3a. The given index is invalid
+
+>MESS shows an error message
+ User case ends.
-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 persons.
-3. Should come with automated unit tests and open source code.
-4. Should favor DOS style commands over Unix-style commands.
+#### Use case: Pin task
+
+**MSS**
+
+1. User requests to list tasks
+2. MESS shows a list of tasks
+3. User requests to pin a specific task by index in the list
+4. MESS pins the task
+Use case ends.
+
+**Extensions**
+
+1a. The list is empty
+
+> User case ends
+
+3a. The given index is invalid
-{More to be added}
+>MESS shows an error message
+ User case ends.
+
+#### Use case: Unpin task
+
+**MSS**
+
+1. User requests to list tasks
+2. MESS shows a list of tasks
+3. User requests to unpin a pinned task by index in the list
+4. MESS unpins the task
+Use case ends.
+
+**Extensions**
+
+1a. The list is empty
+
+> User case ends
+
+3a. The given index is invalid
+
+> MESS shows an error message
+ User case ends.
+
+3b. The task is not pinned before
+
+> MESS shows an error message
+ User case ends.
+
+#### Use case: Complete task
+
+**MSS**
+
+1. User requests to list tasks
+2. MESS shows a list of tasks
+3. User requests to mark a specific task as completed by index in the list
+4. MESS marks the task
+Use case ends.
+
+**Extensions**
+
+1a. The list is empty
+
+> User case ends
+
+3a. The given index is invalid
+
+>MESS shows an error message
+ User case ends.
+
+#### Use case: Uncomplete task
+
+**MSS**
+
+1. User requests to list tasks
+2. MESS shows a list of tasks
+3. User requests to mark a completed task as not completed by index in the list
+4. MESS marks the task back to not completed
+Use case ends.
+
+**Extensions**
+
+1a. The list is empty
+
+> User case ends
+
+3a. The given index is invalid
+
+>MESS shows an error message
+ User case ends.
+
+3b. The task is not marked as completed before
+
+>MESS shows an error message
+ User case ends.
+
+#### Use case: Undo previous action
+
+**MSS**
+
+1. User requests to undo
+2. MESS undo the previous action
+Use case ends.
+
+**Extensions**
+
+1a. There is no previous action
+
+> Use case ends
+
+
+1b. The previous action does not support undo
+> Use case ends
+
+
+#### Use case: Find task
+
+**MSS**
+
+1. User requests to find a task
+2. MESS shows the searched task
+Use case ends.
+
+**Extensions**
+
+1a. The find command format is wrong
+
+>MESS shows an error message. r>
+ Use case ends.
+
+1b. There is no matched task
+
+>MESS shows an no-match message
+ Use case ends.
+
+#### Use case: Find task by tag
+
+**MSS**
+
+1. User enters command and tags to be searched.
+2. MESS shows the tasks containing the tags
+Use case ends.
+
+**Extensions**
+
+1a. The find by tag command format is wrong
+
+>MESS shows an error message.
+ Use case ends.
+
+1b. There is no matched task
+
+>MESS shows no tasks in the task list
+ Use case ends.
+
+#### Use case: List all tasks
+
+**MSS**
+
+1. User requests to list all tasks
+2. MESS shows the entire list
+Use case ends.
+
+**Extensions**
+
+2a. The list is empty
+
+> Nothing will be returned.
+Use case ends.
+
+
+#### Use case: Change storage location
+
+**MSS**
+
+1. User requests to change storage file location
+2. MESS changes storage file location and saves in that location/loads from that location if file already contains data
+
+**Extensions**
+2a. The file path may not be valid
+
+> Error message will be displayed
+Use case ends.
+
+2b. The folder may not be writable(Lack permission)
+
+> Error message will be displayed
+Use case ends.
+
+
+
+## Appendix C : Non Functional Requirements
+
+1. Should work on any mainstream OS as long as it has Java `1.8.0_60` or higher installed
+2. Should be able to process user’s request and show result in 3 seconds
+3. Should be user-friendly for both beginners and advanced users
+4. Should be able to hold up to 1000 tasks
+5. Should be able to work offline
+6. Should only input by keyboard for command line
+7. Should be able to change the storage location of data file
+8. Should come with automated unit tests and open source code
## Appendix D : Glossary
@@ -306,11 +578,81 @@ Use case ends.
> Windows, Linux, Unix, OS-X
-##### Private contact detail
+##### Floating Tasks
-> A contact detail that is not meant to be shared with others
+> A task that does not have open time and close time
+
## Appendix E : Product Survey
-{TODO: Add a summary of competing products}
-
+### Google Calendar
+#### Strengths
+1. Simple 'what you see is what you get' interface
+2. Multi-platform; can be run on various devices
+3. Data is synchronized via google account
+4. Color coding for organizing scheduled tasks and events
+5. Global Search; data searched through other google products as well; Gmail, Drive, etc.
+6. Automatic generation of start and end times enforcing structure in all entries
+7. Recurring events
+
+#### Weaknesses
+1. No tagging mechanism for more complex organization
+2. No analysis, summary nor statistics of data the user has entered
+3. No anonymous use; must have a google account
+4. Calendar cannot display a filtered set, thus clutter when high volume of data
+
+
+### Todo.txt (laptop version)
+#### Strength:
+1. Simple editors(use CLI) to manage tasks
+2. Can be used in every text editor, regardless of OS
+3. Controlled by keyboard shortcuts
+4. Able to show what’s the next important thing to get done
+5. Can add the place or situation on your tasks
+6. File contents is human-readable without requiring any tools other than plain text viewer
+7. A single line in text file represents a single task which is easy to manage
+8. Priority can be set for each task
+9. Can be extended to add-ons
+10. Can be connected to Dropbox
+
+#### Weakness:
+1. UI does not look good
+2. All the output will only be shown in CLI format (no colour, no font change)
+3. Need to update whether the task has finished or not by the user
+4. No notification for task near deadline
+5. Require users to remember too many commands
+
+### Wunderlist (Free version)
+
+#### Strength
+1. Easy to use
+2. Cross-platform application
+3. Notification for tasks set by users
+4. Share to-do list with others
+5. Use hashtag to categorize tasks
+6. Plug-in for Microsoft Outlook and Google Calendar
+7. Can take notes (not only task)
+
+#### Weakness
+1. Cannot create task using one line command
+2. Have limited number subtasks (only premium version has unlimited)
+3. Cannot customize the interface (only premium version can do)
+
+### Todoist (Free version)
+#### Strength
+1. Simple to use
+2. Can share tasks with others
+3. Recurring events
+4. Have both online and offline access
+5. Have different priority level for tasks
+6. Can track your productivity and visualize your achievement trends over time.
+
+#### Weakness
+1. tasks for free version
+2. Cannot use label to categorize tasks for free version
+3. Synchronization to different platform is only available for premium version
+
+#### Summary
+
+In summary, there are a few strengths that the existing products have. They all have simple interfaces so that users can look at their to-do lists in a clear way. Many of them have notifications and priority which can be set for each task which is good for user to find urgent task. Categorize tasks is another key point for most of the products. This can let users to find out the relevant tasks easily. One of the existed product is quite similar to our application which are using one-line command to control the application. However, the interface of this software need to be improved. Therefore, interface, having priority for tasks and tags for tasks are some important features.
+
diff --git a/docs/Diagram/ArchitectureDiagram.pptx b/docs/Diagram/ArchitectureDiagram.pptx
new file mode 100644
index 000000000000..a0c3151b3d54
Binary files /dev/null and b/docs/Diagram/ArchitectureDiagram.pptx differ
diff --git a/docs/Diagram/HighLevelSequenceDiagrams.pptx b/docs/Diagram/HighLevelSequenceDiagrams.pptx
new file mode 100644
index 000000000000..d63229022907
Binary files /dev/null and b/docs/Diagram/HighLevelSequenceDiagrams.pptx differ
diff --git a/docs/Diagram/LogicComponentClassDiagram.pptx b/docs/Diagram/LogicComponentClassDiagram.pptx
new file mode 100644
index 000000000000..eb59a9dd1762
Binary files /dev/null and b/docs/Diagram/LogicComponentClassDiagram.pptx differ
diff --git a/docs/Diagram/LogicComponentSequenceDiagram.pptx b/docs/Diagram/LogicComponentSequenceDiagram.pptx
new file mode 100644
index 000000000000..cdc9140d09ef
Binary files /dev/null and b/docs/Diagram/LogicComponentSequenceDiagram.pptx differ
diff --git a/docs/Diagram/ModelComponentClassDiagram.pptx b/docs/Diagram/ModelComponentClassDiagram.pptx
new file mode 100644
index 000000000000..f1181bf73c20
Binary files /dev/null and b/docs/Diagram/ModelComponentClassDiagram.pptx differ
diff --git a/docs/Diagram/StorageComponentClassDiagram.pptx b/docs/Diagram/StorageComponentClassDiagram.pptx
new file mode 100644
index 000000000000..207d06fdc134
Binary files /dev/null and b/docs/Diagram/StorageComponentClassDiagram.pptx differ
diff --git a/docs/Diagram/UiComponentClassDiagram.pptx b/docs/Diagram/UiComponentClassDiagram.pptx
new file mode 100644
index 000000000000..14516361131a
Binary files /dev/null and b/docs/Diagram/UiComponentClassDiagram.pptx differ
diff --git a/docs/Diagram/UiPanelImage.pptx b/docs/Diagram/UiPanelImage.pptx
new file mode 100644
index 000000000000..4277d4246546
Binary files /dev/null and b/docs/Diagram/UiPanelImage.pptx differ
diff --git a/docs/LearningOutcomes.md b/docs/LearningOutcomes.md
deleted file mode 100644
index 31f26a37b480..000000000000
--- a/docs/LearningOutcomes.md
+++ /dev/null
@@ -1,20 +0,0 @@
-# Learning Outcomes
-After studying this code and completing the corresponding exercises, you should be able to,
-
-1. [Use High-Level Designs `[LO-HighLevelDesign]`](#use-high-level-designs-lo-highleveldesign)
-
-
-------------------------------------------------------------------------------------------------------
-
-## Use High-Level Designs `[LO-HighLevelDesign]`
-Note how the [Developer Guide](DeveloperGuide.md#design) describes the high-level design using an
-_Architecture Diagrams_ and high-level sequence diagrams.
-
-#### Exercise: Add more user stories
-
-* ...
-
-------------------------------------------------------------------------------------------------------
-
-{More to be added}
-
diff --git a/docs/UserGuide.md b/docs/UserGuide.md
index 0cf4b84f7470..aa6f7573718f 100644
--- a/docs/UserGuide.md
+++ b/docs/UserGuide.md
@@ -2,133 +2,310 @@
* [Quick Start](#quick-start)
* [Features](#features)
+ * [Add](#adding-a-task-or-event-add)
+ * [Alias](#aliasing-a-command-alias)
+ * [Delete](#deleting-a-task--delete)
+ * [Update](#update-entries--update)
+ * [Complete](#marking-a-task-as-completed-complete)
+ * [Uncomplete](#unmarking-a-completed-task-as-not-completed-uncomplete)
+ * [Pin](#pin-a-task-as-important-pin)
+ * [Unpin](#unpin-a-pinned-task-unpin)
+ * [List](#listing--list-allpinnedpendingcompletedoverdue)
+ * [Find](#finding-all-tasks-and-events-containing-keyword-in-their-name--find)
+ * [Find By Tags](#finding-all-tasks-by-tag-name--find)
+ * [Search Box](#activate-real-time-search-searchbox)
+ * [Undo](#undo-action--undo)
+ * [Help](#viewing-help--help)
+ * [Change-to](#change-storage-location--change-to)
+ * [Exit](#exiting-the-program--exit)
* [FAQ](#faq)
* [Command Summary](#command-summary)
+
+## Introduction
+
+Nowadays, everyone has so many tasks to do. Have you ever missed your deadlines or forgotten your schedule? If so, MESS is here to help you! MESS is a to-do list application which reminds you of your tasks or events by showing your list of tasks on the application. No need to be afraid of forgetting your tasks anymore! You can easily use MESS through your keyboard by typing only simple and short commands. MESS can be used offline and even on your office computer. It helps you manage and organise both your tasks and your time.
+
+Want to use MESS immediately? Let's get started!
+
## Quick Start
0. Ensure you have Java version `1.8.0_60` or later installed in your Computer.
> Having any Java 8 version is not enough.
This app will not work with earlier versions of Java 8.
-
-1. Download the latest `addressbook.jar` from the [releases](../../../releases) tab.
-2. Copy the file to the folder you want to use as the home folder for your Address Book.
-3. Double-click the file to start the app. The GUI should appear in a few seconds.
- >
+
+1. Download the latest `MESS.jar` from the [releases](../../../releases) tab.
+2. Copy the file to the folder you want to use as the home folder for your to-do list.
+3. Double-click the file to start the app. The GUI should appear in a few seconds.
+
4. Type the command in the command box and press Enter to execute it.
- e.g. typing **`help`** and pressing Enter will open the help window.
+ e.g. typing **`help`** and pressing Enter will open the help window.
5. Some example commands you can try:
- * **`list`** : lists all contacts
- * **`add`**` John Doe p/98765432 e/johnd@gmail.com a/John street, block 123, #01-01` :
- adds a contact named `John Doe` to the Address Book.
- * **`delete`**` 3` : deletes the 3rd contact shown in the current list
- * **`exit`** : exits the app
+ * **`list`** : lists all tasks
+ * **`add`**`CS2103 Tutorial`: adds a task named CS2103 Tutorial
+ * **`alias`**`add a`: aliases the add command to the 'a' key
+ * **`find`**`tutorial `: searches the task named tutorial
+ * **`delete`**`1`: delete the first task in the list
+ * **`complete`** `1`: mark the first task as completed
+ * **`uncomplete`** `1`: mark the completed first task on the list as not completed
+ * **`update`**`1 name presentation ends tomorrow` : updates first task on the list to presentation having a deadline tomorrow while the number '1' is the index of task on the list
+ * **`undo`** : undo previous one action
+ * **`pin`**`1` : pin the first task in the list
+ * **`unpin`**`1` : unpin the pinned first task in the list
+ * **`change-to`**`./data/taskmanager.xml`: change the storage location
+ * **`exit`** :exit the program
6. Refer to the [Features](#features) section below for details of each command.
-
+7. Our UI contains different parts. The following picture introduce the UI:
+
## Features
-> **Command Format**
-> * Words in `UPPER_CASE` are the parameters.
-> * Items in `SQUARE_BRACKETS` are optional.
-> * Items with `...` after them can have multiple instances.
-> * The order of parameters is fixed.
+**Command Format**
+* The command is case insensitive.
+* The order of parameters is fixed.
+* Words in `UPPER_CASE` are the parameters.
+* Words in `SQUARE_BRACKET` are optional.
+
-#### Viewing help : `help`
-Format: `help`
+#### Adding a task or event: `add`
+Adds a task to the to-do list
+Format: `add TASK_NAME [starts START_DATETIME ends CLOSE_DATETIME tag TAG]`
-> Help is also shown if you enter an incorrect command e.g. `abcd`
-
-#### Adding a person: `add`
-Adds a person to the address book
-Format: `add NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]...`
+> Date format of START_DATE and CLOSE_DATE includes words like today, tomorrow, 3 days from now, day after tomorrow, noon, 12pm, 6am
-> Persons can have any number of tags (including 0)
+> Use ISO date-time format if you wish to type exact date. e.g. 2016/9/1 represents 1st September 2016
-Examples:
-* `add John Doe p/98765432 e/johnd@gmail.com a/John street, block 123, #01-01`
-* `add Betsy Crowe p/1234567 e/betsycrowe@gmail.com a/Newgate Prison t/criminal t/friend`
+* `TASK_NAME` need not be unique.
+* If there is no argument, the task will become floating.
+* `START_DATE` refer to the starting date and time of an event. For a task, the timestamp will be automatically saved as start date and time when the task is created. User can input start date and time for events.
+* `TAG` is for users to write tags for different tasks. Multiple tags are available by typing `tag TAG tag TAG`.
-#### Listing all persons : `list`
-Shows a list of all persons in the address book.
-Format: `list`
+Examples:
+* `add proposal ends tomorrow` Adds a proposal task with a deadline 24 hours from now
+* `add meeting starts tomorrow 1pm ends tomorrow 3pm` Adds a meeting event which start tomorrow at 1pm and ends tomorrow at 3pm
+* `add shopping` Adds a floating task named revision test which has not specify the start and end date
+* `add tutorial tag cs2103` Adds a floating task named tutorial with a tag CS2013
+* `add quiz tag cs2102 tag easy` Adds a floating task named tutorial with a tag CS2012 and easy
+* `add test starts 9/1 2pm ends 9/1 3pm ` Adds a task starts on 1 September 2pm and ends on 1 September 3pm
-#### Finding all persons containing any keyword in their name: `find`
-Finds persons whose names contain any of the given keywords.
-Format: `find KEYWORD [MORE_KEYWORDS]`
-> * The search is case sensitive. e.g `hans` will not match `Hans`
-> * The order of the keywords does not matter. e.g. `Hans Bo` will match `Bo Hans`
-> * Only the name is searched.
-> * Only full words will be matched e.g. `Han` will not match `Hans`
-> * Persons matching at least one keyword will be returned (i.e. `OR` search).
- e.g. `Hans` will match `Hans Bo`
-
-Examples:
-* `find John`
- Returns `John Doe` but not `john`
-* `find Betsy Tim John`
- Returns Any person having names `Betsy`, `Tim`, or `John`
-
-#### Deleting a person : `delete`
-Deletes the specified person from the address book. Irreversible.
+#### Aliasing a command: `alias`
+Aliases a command to a symbol
+Format: `alias COMMAND_NAME SYMBOL`
+
+> Once aliased, the original command will still continue to work. Hence, symbols cannot be command names.
+> One symbol can map to at most one command
+
+* `COMMAND_NAME` Must be a valid command
+* `SYMBOL` refers to the symbol you wish to alias to. SYMBOL cannot be a command name.
+
+Examples:
+* `alias add +` Aliases the add command to the symbol +
+* `alias alias q` Aliases the alias command to the symbol q
+
+
+
+#### Deleting a task : `delete`
+Deletes a specific task by task index from the to-do list.
Format: `delete INDEX`
-> Deletes the person at the specified `INDEX`.
- The index refers to the index number shown in the most recent listing.
- The index **must be a positive integer** 1, 2, 3, ...
-
-Examples:
-* `list`
- `delete 2`
- Deletes the 2nd person in the address book.
-* `find Betsy`
- `delete 1`
- Deletes the 1st person in the results of the `find` command.
-
-#### Select a person : `select`
-Selects the person identified by the index number used in the last person listing.
-Format: `select INDEX`
-
-> Selects the person and loads the Google search page the person at the specified `INDEX`.
- The index refers to the index number shown in the most recent listing.
- The index **must be a positive integer** 1, 2, 3, ...
-
-Examples:
-* `list`
- `select 2`
- Selects the 2nd person in the address book.
-* `find Betsy`
- `select 1`
- Selects the 1st person in the results of the `find` command.
-
-#### Clearing all entries : `clear`
-Clears all entries from the address book.
-Format: `clear`
+> * INDEX refers to the number appears on the list in front the task name.
-#### Exiting the program : `exit`
-Exits the program.
-Format: `exit`
+
+
+Examples:
+* `delete 1`
+ Deletes the first task in the to-do list.
+
+
+#### Update entries : `update`
+Update a specific task.
+Format: `update INDEX [name TASKNAME starts STARTDATETIME ends ENDDATETIME tag TAG remove-tag TO_REMOVE_TAG]`
+
+> * INDEX refers to the number appears on the list in front the task name.
+> * The task name is optional, but needs to be preceded by name
+> * The TAG here will be added to the referred task and the original tag remains. If you want to delete a tag, use `remove-tag TO_REMOVE_TAG` to delete tag by name.
+> * TO_REMOVE_TAG refers to the tag (or tags) that you want to be removed by typing the tags' name that you want to delete.
+> * You can choose what to update. It depends on you whether you want to update only one information or update multiple information.
+
+Examples:
+* `update 2 name shopping ends 9pm`
+ update the task name of the second task on the list to shopping and the start time to 3/10/2016 9 p.m.
+
+* `update 1 tag cs2103`
+ add the tag of the first task on to-do list to cs2103
+
+* `update 3 ends three hours later`
+ update the task name of the third task on the list to a deadline three hours after you type this command
+
+* `update 2 tag family remove-tag friends`
+ add a tag family to the second task and remove the tag named friends
+
+
+
+
+#### Marking a task as completed: `complete`
+Marks a specific task by index from the to-do list.
+Format: `complete INDEX`
+
+> * INDEX refers to the number appears on the list in front the task name.
+
+
+
+Example:
+* `complete 2`
+ Marks the second task on the list as completed.
+
+#### Unmarking a completed task as not completed: `uncomplete`
+Unmarks a completed specific task as not completed by index from the to-do list.
+Format: `uncomplete INDEX`
+
+> * INDEX refers to the number appears on the list in front the task name.
+
+Example:
+* `uncomplete 2`
+ Unmarks the completed second task on the list as not completed.
+
+#### Pin a task as important: `pin`
+Pin an important task.
+Format: `pin INDEX`
+
+> * INDEX refers to the number appears on the list in front the task name.
+
+
+
+Example:
+* `pin 1`
+pin the first task to show that it is an important task.
-#### Saving the data
-Address book data are saved in the hard disk automatically after any command that changes the data.
+#### Unpin a pinned task: `unpin`
+Unpin a previously pinned task.
+Format: `unpin INDEX`
+
+> * INDEX refers to the number appears on the list in front the task name.
+
+Example:
+* `unpin 1`
+unpin the pinned and first task on the list.
+
+
+#### Listing : `list (all/pinned/pending/completed/overdue)`
+Shows a list of tasks and events in the todo list.
+Format: `list (all/pinned/pending/completed/overdue)`
+
+> * Choose one of the words in the round bracket
+
+`list all`: List out all the task in the list
+`list pinned`: List only pinned task
+`list pending`: List tasks which are still pending
+`list completed`: List tasks which are marked as completed
+`list overdue`: List tasks which are already due
+
+#### Finding all tasks and events containing keyword in their name: `find`
+Finds tasks which have names containing any of the given keywords including substring.
+Format: `find KEYWORD`
+
+> * The search is case insensitive. e.g `meeting` will match `Meeting`
+> * Substring is able to be searched. e.g. `mee` will match `meeting`
+> * The order of the keywords does not matter. e.g. `Hans Bo` will match `Bo Hans`
+> * Only task name is searched.
+> * Tasks matching at least one keyword will be returned (i.e. `OR` search).
+ e.g. `Hans` will match `Hans Bo`
+
+Examples:
+* `find meeting`
+ Returns tasks having name or substring `meeting`
+* `find cs2103`
+ Returns tasks having name or substring `cs2103`
+
+
+#### Finding all tasks by tag name: `find-tag`
+Finds tasks that contains all given tags.
+Format: `find-tag TAG_NAME`
+
+> * The search is case sensitive e.g. `urgent` will not match `URGENT`
+> * Tasks must match all keywords (i.e. `AND` search).
+ e.g. `urgent task` will only match tasks with both `urgent` and `task` tags
+
+#### Activate real time search: `searchbox`
+Activates the real time search, which is located in the same input box used to input commands.
+Format: `searchbox`
+
+> * To exit from real time search, just hit Enter.
+> * Similar functionality to `find`
+
+
+
+
+#### Undo action : `undo`
+Undo the previous action.
+Format: `undo`
+
+> * Will only undo `add`, `delete` and `update` actions.
+
+
+#### Viewing help : `help`
+Show the help menu. Format: `help`
+> Help is also shown if you enter an incorrect command e.g. `123abc`
+
+#### Saving the data
+To-do list data are saved in the hard disk automatically after any command that changes the data.
There is no need to save manually.
+
+
+#### Change storage location : `change-to`
+Changes to a new storage location and saves task manager data there
+Note: If new file already has data, then the taskmanager will load that data.
+
+Format: `change-to NEWFILEPATH`
+Note: ./ for current folder works on both Windows and Unix machines.
+Example:
+* `change-to data/taskmanager.xml`
+* `change-to ./taskmanager.xml`
+
+
+#### Exiting the program : `exit`
+Exits the program.
+Format: `exit`
+
+
## FAQ
+**Q**: Can I add event which have a start date and time to my to-do list ?
+
+**A**: Yes, you can create an event by typing command with a start and end date. For example, you have a trip from 10/10/2016 8:00 to 13/10/2016 21:00. You can type command like this: `add trip starts 8am 10th October ends 9pm 13th October`.
+
+**Q**: If I don't know the deadline of my task yet, can I still add my task?
+
+**A**: Yes, you can still add your task. You can create a floating task by only type in command `add TASK_NAME` if you don't know the deadline of your task.
+
+
+
+In this example, you can see shopping is a floating task without a start time and end time.
+
+**Q**: What can I do if I forget those commands?
+
+**A**: You can use the help command or click help tab on the top which will give you a list of command summary. Even if you type the wrong command format, MESS will show you the correct command hints.
+
-**Q**: How do I transfer my data to another Computer?
-**A**: Install the app in the other computer and overwrite the empty data file it creates with
- the file that contains the data of your previous Address Book folder.
-
## Command Summary
Command | Format
--------- | :--------
-Add | `add NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]...`
-Clear | `clear`
-Delete | `delete INDEX`
-Find | `find KEYWORD [MORE_KEYWORDS]`
-List | `list`
+-------- | :--------
+Add | `add TASK_NAME [starts START_DATE_TIME ends CLOSE_DATE_TIME tag TAG]`
+Alias | `alias add a`
+Delete |`delete INDEX`
+Complete | `complete INDEX`
+Uncomplete | `uncomplete INDEX`
+List | `list (all/pinned/pending/completed/overdue)`
+Find | `find KEYWORD`
+Find tag | `find-tag TAG [MORE_TAG]`
+Update | `update INDEX [name NAME starts START_DATE_TIME ends CLOSE_DATE_TIME tag TAG remove-tag TAG]`
+Undo | `undo`
+Pin | `pin INDEX`
+Unpin | `unpin INDEX`
+Live Search | `searchbox`
+Change Storage | `change-to NEW_PATH`
Help | `help`
-Select | `select INDEX`
+Exit | `exit`
diff --git a/docs/UsingGradle.md b/docs/UsingGradle.md
index 578c5f8634c2..69cb94b6e45c 100644
--- a/docs/UsingGradle.md
+++ b/docs/UsingGradle.md
@@ -35,7 +35,7 @@ Gradle commands look like this:
## Creating the JAR file
* **`shadowJar`**
- Creates the `addressbook.jar` file in the `build/jar` folder, _if the current file is outdated_.
+ Creates the `taskmanager.jar` file in the `build/jar` folder, _if the current file is outdated_.
e.g. `./gradlew shadowJar`
> To force Gradle to create the JAR file even if the current one is up-to-date, you can '`clean`' first.
@@ -56,7 +56,7 @@ If we package only our own class files into the JAR file, it will not work prope
Runs all tests in the `guitests` package
* **`nonGuiTests`**
- Runs all non-GUI tests in the `seedu.address` package
+ Runs all non-GUI tests in the `seedu.task` package
* **`headless`**
Sets the test mode as _headless_.
diff --git a/docs/UsingTravis.md b/docs/UsingTravis.md
index 4844f0682f75..1a82089d7288 100644
--- a/docs/UsingTravis.md
+++ b/docs/UsingTravis.md
@@ -31,7 +31,6 @@ from [Travis CI Documentation](https://docs.travis-ci.com/).
* If repository cannot be found, click `Sync account`
4. Activate the switch.
- 
5. This repo comes with a [`.travis.yml`](.travis.yml) that tells Travis what to do.
So there is no need for you to create one yourself.
6. To see the CI in action, push a commit to the master branch!
@@ -39,9 +38,9 @@ from [Travis CI Documentation](https://docs.travis-ci.com/).

* As the build is run on a provided remote machine, we can only examine the logs it produces:
- 
+ 
7. If the build is successful, you should be able to check the coverage details of the tests
at [Coveralls](http://coveralls.io/)
8. Update the link to the 'build status' badge at the top of the `README.md` to point to the build status of your
- own repo.
\ No newline at end of file
+ own repo.
diff --git a/docs/diagrams/Diagrams.pptx b/docs/diagrams/Diagrams.pptx
deleted file mode 100644
index 3c28abe9c1d3..000000000000
Binary files a/docs/diagrams/Diagrams.pptx and /dev/null differ
diff --git a/docs/images/AbdulHaliq.jpg b/docs/images/AbdulHaliq.jpg
new file mode 100644
index 000000000000..b1c334e21a8b
Binary files /dev/null and b/docs/images/AbdulHaliq.jpg differ
diff --git a/docs/images/ActivityDiagram.jpg b/docs/images/ActivityDiagram.jpg
new file mode 100644
index 000000000000..a2af8b023a47
Binary files /dev/null and b/docs/images/ActivityDiagram.jpg differ
diff --git a/docs/images/AdvayPal.jpg b/docs/images/AdvayPal.jpg
new file mode 100644
index 000000000000..d68dfde95fb4
Binary files /dev/null and b/docs/images/AdvayPal.jpg differ
diff --git a/docs/images/Architecture.png b/docs/images/Architecture.png
index bdc789000f77..93bc2192bb41 100644
Binary files a/docs/images/Architecture.png and b/docs/images/Architecture.png differ
diff --git a/docs/images/ChengHang.jpg b/docs/images/ChengHang.jpg
new file mode 100644
index 000000000000..bb23be08706b
Binary files /dev/null and b/docs/images/ChengHang.jpg differ
diff --git a/docs/images/CompletedTaskImage.png b/docs/images/CompletedTaskImage.png
new file mode 100644
index 000000000000..4931f9d391ed
Binary files /dev/null and b/docs/images/CompletedTaskImage.png differ
diff --git a/docs/images/DamithRajapakse.jpg b/docs/images/DamithRajapakse.jpg
deleted file mode 100644
index 127543883893..000000000000
Binary files a/docs/images/DamithRajapakse.jpg and /dev/null differ
diff --git a/docs/images/DeletePersonSdForLogic.png b/docs/images/DeletePersonSdForLogic.png
deleted file mode 100644
index 6c272fb17af6..000000000000
Binary files a/docs/images/DeletePersonSdForLogic.png and /dev/null differ
diff --git a/docs/images/GuidedPictureForUser.png b/docs/images/GuidedPictureForUser.png
new file mode 100644
index 000000000000..ef86d3cf1cc3
Binary files /dev/null and b/docs/images/GuidedPictureForUser.png differ
diff --git a/docs/images/IndexPicture.png b/docs/images/IndexPicture.png
new file mode 100644
index 000000000000..de70a90ceeff
Binary files /dev/null and b/docs/images/IndexPicture.png differ
diff --git a/docs/images/JoshuaLee.jpg b/docs/images/JoshuaLee.jpg
deleted file mode 100644
index 2d1d94e0cf5d..000000000000
Binary files a/docs/images/JoshuaLee.jpg and /dev/null differ
diff --git a/docs/images/LeowYijin.jpg b/docs/images/LeowYijin.jpg
deleted file mode 100644
index adbf62ad9406..000000000000
Binary files a/docs/images/LeowYijin.jpg and /dev/null differ
diff --git a/docs/images/LogicClassDiagram.png b/docs/images/LogicClassDiagram.png
index a973d02047a2..94dd9d6ba8fd 100644
Binary files a/docs/images/LogicClassDiagram.png and b/docs/images/LogicClassDiagram.png differ
diff --git a/docs/images/MESS.PNG b/docs/images/MESS.PNG
new file mode 100644
index 000000000000..e86fc33c7984
Binary files /dev/null and b/docs/images/MESS.PNG differ
diff --git a/docs/images/MESS_004.png b/docs/images/MESS_004.png
new file mode 100644
index 000000000000..848f445a22d9
Binary files /dev/null and b/docs/images/MESS_004.png differ
diff --git a/docs/images/MartinChoo.jpg b/docs/images/MartinChoo.jpg
deleted file mode 100644
index fd14fb94593a..000000000000
Binary files a/docs/images/MartinChoo.jpg and /dev/null differ
diff --git a/docs/images/ModelClassDiagram.png b/docs/images/ModelClassDiagram.png
index 8cdf11ec93a1..e39de4d9fd41 100644
Binary files a/docs/images/ModelClassDiagram.png and b/docs/images/ModelClassDiagram.png differ
diff --git a/docs/images/PinnedTaskImage.png b/docs/images/PinnedTaskImage.png
new file mode 100644
index 000000000000..8eadf81b5a3c
Binary files /dev/null and b/docs/images/PinnedTaskImage.png differ
diff --git a/docs/images/SDforAddTask.png b/docs/images/SDforAddTask.png
new file mode 100644
index 000000000000..467faede7aba
Binary files /dev/null and b/docs/images/SDforAddTask.png differ
diff --git a/docs/images/SDforAddTaskEventHandling.png b/docs/images/SDforAddTaskEventHandling.png
new file mode 100644
index 000000000000..0d82b0a1d5c3
Binary files /dev/null and b/docs/images/SDforAddTaskEventHandling.png differ
diff --git a/docs/images/SDforDeletePerson.png b/docs/images/SDforDeletePerson.png
deleted file mode 100644
index 1e836f10dcd8..000000000000
Binary files a/docs/images/SDforDeletePerson.png and /dev/null differ
diff --git a/docs/images/SDforDeletePersonEventHandling.png b/docs/images/SDforDeletePersonEventHandling.png
deleted file mode 100644
index ecec0805d32c..000000000000
Binary files a/docs/images/SDforDeletePersonEventHandling.png and /dev/null differ
diff --git a/docs/images/SDforDeleteTask.png b/docs/images/SDforDeleteTask.png
new file mode 100644
index 000000000000..5f32161bf7a1
Binary files /dev/null and b/docs/images/SDforDeleteTask.png differ
diff --git a/docs/images/StorageClassDiagram.png b/docs/images/StorageClassDiagram.png
index 7a4cd2700cbf..e379eaf15625 100644
Binary files a/docs/images/StorageClassDiagram.png and b/docs/images/StorageClassDiagram.png differ
diff --git a/docs/images/SukiTsang.jpg b/docs/images/SukiTsang.jpg
new file mode 100644
index 000000000000..dc362e372ab3
Binary files /dev/null and b/docs/images/SukiTsang.jpg differ
diff --git a/docs/images/SyedAbdullah.jpg b/docs/images/SyedAbdullah.jpg
new file mode 100644
index 000000000000..cbe97e170254
Binary files /dev/null and b/docs/images/SyedAbdullah.jpg differ
diff --git a/docs/images/Ui.png b/docs/images/Ui.png
deleted file mode 100644
index 7121a50a442a..000000000000
Binary files a/docs/images/Ui.png and /dev/null differ
diff --git a/docs/images/UiClassDiagram.png b/docs/images/UiClassDiagram.png
index 459245e267af..d68c89151556 100644
Binary files a/docs/images/UiClassDiagram.png and b/docs/images/UiClassDiagram.png differ
diff --git a/docs/images/UiPanelImage.png b/docs/images/UiPanelImage.png
new file mode 100644
index 000000000000..db90cb4ee29b
Binary files /dev/null and b/docs/images/UiPanelImage.png differ
diff --git a/docs/images/YouLiang.jpg b/docs/images/YouLiang.jpg
deleted file mode 100644
index 17b48a732272..000000000000
Binary files a/docs/images/YouLiang.jpg and /dev/null differ
diff --git a/docs/images/addTaskSdForLogic.png b/docs/images/addTaskSdForLogic.png
new file mode 100644
index 000000000000..101a10d9b86b
Binary files /dev/null and b/docs/images/addTaskSdForLogic.png differ
diff --git a/docs/images/flick_repository_switch.png b/docs/images/flick_repository_switch.png
deleted file mode 100644
index a6009dd44cdb..000000000000
Binary files a/docs/images/flick_repository_switch.png and /dev/null differ
diff --git a/docs/images/travis_build.PNG b/docs/images/travis_build.PNG
new file mode 100644
index 000000000000..912bfecba8db
Binary files /dev/null and b/docs/images/travis_build.PNG differ
diff --git a/docs/images/travis_build.png b/docs/images/travis_build.png
deleted file mode 100644
index 0c4061bc0e23..000000000000
Binary files a/docs/images/travis_build.png and /dev/null differ
diff --git a/src/main/java/seedu/address/commons/core/Messages.java b/src/main/java/seedu/address/commons/core/Messages.java
deleted file mode 100644
index 1deb3a1e4695..000000000000
--- a/src/main/java/seedu/address/commons/core/Messages.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package seedu.address.commons.core;
-
-/**
- * Container for user visible messages.
- */
-public class Messages {
-
- public static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command";
- public static final String MESSAGE_INVALID_COMMAND_FORMAT = "Invalid command format! \n%1$s";
- public static final String MESSAGE_INVALID_PERSON_DISPLAYED_INDEX = "The person index provided is invalid";
- public static final String MESSAGE_PERSONS_LISTED_OVERVIEW = "%1$d persons listed!";
-
-}
diff --git a/src/main/java/seedu/address/commons/events/model/AddressBookChangedEvent.java b/src/main/java/seedu/address/commons/events/model/AddressBookChangedEvent.java
deleted file mode 100644
index 347a8359e0d5..000000000000
--- a/src/main/java/seedu/address/commons/events/model/AddressBookChangedEvent.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package seedu.address.commons.events.model;
-
-import seedu.address.commons.events.BaseEvent;
-import seedu.address.model.ReadOnlyAddressBook;
-
-/** Indicates the AddressBook in the model has changed*/
-public class AddressBookChangedEvent extends BaseEvent {
-
- public final ReadOnlyAddressBook data;
-
- public AddressBookChangedEvent(ReadOnlyAddressBook data){
- this.data = data;
- }
-
- @Override
- public String toString() {
- return "number of persons " + data.getPersonList().size() + ", number of tags " + data.getTagList().size();
- }
-}
diff --git a/src/main/java/seedu/address/commons/events/ui/PersonPanelSelectionChangedEvent.java b/src/main/java/seedu/address/commons/events/ui/PersonPanelSelectionChangedEvent.java
deleted file mode 100644
index 95377b326fa6..000000000000
--- a/src/main/java/seedu/address/commons/events/ui/PersonPanelSelectionChangedEvent.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package seedu.address.commons.events.ui;
-
-import seedu.address.commons.events.BaseEvent;
-import seedu.address.model.person.ReadOnlyPerson;
-
-/**
- * Represents a selection change in the Person List Panel
- */
-public class PersonPanelSelectionChangedEvent extends BaseEvent {
-
-
- private final ReadOnlyPerson newSelection;
-
- public PersonPanelSelectionChangedEvent(ReadOnlyPerson newSelection){
- this.newSelection = newSelection;
- }
-
- @Override
- public String toString() {
- return this.getClass().getSimpleName();
- }
-
- public ReadOnlyPerson getNewSelection() {
- return newSelection;
- }
-}
diff --git a/src/main/java/seedu/address/logic/Logic.java b/src/main/java/seedu/address/logic/Logic.java
deleted file mode 100644
index 4df1bc65cabb..000000000000
--- a/src/main/java/seedu/address/logic/Logic.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package seedu.address.logic;
-
-import javafx.collections.ObservableList;
-import seedu.address.logic.commands.CommandResult;
-import seedu.address.model.person.ReadOnlyPerson;
-
-/**
- * API of the Logic component
- */
-public interface Logic {
- /**
- * Executes the command and returns the result.
- * @param commandText The command as entered by the user.
- * @return the result of the command execution.
- */
- CommandResult execute(String commandText);
-
- /** Returns the filtered list of persons */
- ObservableList getFilteredPersonList();
-
-}
diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/seedu/address/logic/LogicManager.java
deleted file mode 100644
index ce4dc1903cff..000000000000
--- a/src/main/java/seedu/address/logic/LogicManager.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package seedu.address.logic;
-
-import javafx.collections.ObservableList;
-import seedu.address.commons.core.ComponentManager;
-import seedu.address.commons.core.LogsCenter;
-import seedu.address.logic.commands.Command;
-import seedu.address.logic.commands.CommandResult;
-import seedu.address.logic.parser.Parser;
-import seedu.address.model.Model;
-import seedu.address.model.person.ReadOnlyPerson;
-import seedu.address.storage.Storage;
-
-import java.util.logging.Logger;
-
-/**
- * The main LogicManager of the app.
- */
-public class LogicManager extends ComponentManager implements Logic {
- private final Logger logger = LogsCenter.getLogger(LogicManager.class);
-
- private final Model model;
- private final Parser parser;
-
- public LogicManager(Model model, Storage storage) {
- this.model = model;
- this.parser = new Parser();
- }
-
- @Override
- public CommandResult execute(String commandText) {
- logger.info("----------------[USER COMMAND][" + commandText + "]");
- Command command = parser.parseCommand(commandText);
- command.setData(model);
- return command.execute();
- }
-
- @Override
- public ObservableList getFilteredPersonList() {
- return model.getFilteredPersonList();
- }
-}
diff --git a/src/main/java/seedu/address/logic/commands/AddCommand.java b/src/main/java/seedu/address/logic/commands/AddCommand.java
deleted file mode 100644
index 2860a9ab2a85..000000000000
--- a/src/main/java/seedu/address/logic/commands/AddCommand.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package seedu.address.logic.commands;
-
-import seedu.address.commons.exceptions.IllegalValueException;
-import seedu.address.model.person.*;
-import seedu.address.model.tag.Tag;
-import seedu.address.model.tag.UniqueTagList;
-
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Adds a person to the address book.
- */
-public class AddCommand extends Command {
-
- public static final String COMMAND_WORD = "add";
-
- public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a person to the address book. "
- + "Parameters: NAME p/PHONE e/EMAIL a/ADDRESS [t/TAG]...\n"
- + "Example: " + COMMAND_WORD
- + " John Doe p/98765432 e/johnd@gmail.com a/311, Clementi Ave 2, #02-25 t/friends t/owesMoney";
-
- public static final String MESSAGE_SUCCESS = "New person added: %1$s";
- public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book";
-
- private final Person toAdd;
-
- /**
- * Convenience constructor using raw values.
- *
- * @throws IllegalValueException if any of the raw values are invalid
- */
- public AddCommand(String name, String phone, String email, String address, Set tags)
- throws IllegalValueException {
- final Set tagSet = new HashSet<>();
- for (String tagName : tags) {
- tagSet.add(new Tag(tagName));
- }
- this.toAdd = new Person(
- new Name(name),
- new Phone(phone),
- new Email(email),
- new Address(address),
- new UniqueTagList(tagSet)
- );
- }
-
- @Override
- public CommandResult execute() {
- assert model != null;
- try {
- model.addPerson(toAdd);
- return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd));
- } catch (UniquePersonList.DuplicatePersonException e) {
- return new CommandResult(MESSAGE_DUPLICATE_PERSON);
- }
-
- }
-
-}
diff --git a/src/main/java/seedu/address/logic/commands/ClearCommand.java b/src/main/java/seedu/address/logic/commands/ClearCommand.java
deleted file mode 100644
index 522d57189f51..000000000000
--- a/src/main/java/seedu/address/logic/commands/ClearCommand.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package seedu.address.logic.commands;
-
-import seedu.address.model.AddressBook;
-
-/**
- * Clears the address book.
- */
-public class ClearCommand extends Command {
-
- public static final String COMMAND_WORD = "clear";
- public static final String MESSAGE_SUCCESS = "Address book has been cleared!";
-
- public ClearCommand() {}
-
-
- @Override
- public CommandResult execute() {
- assert model != null;
- model.resetData(AddressBook.getEmptyAddressBook());
- return new CommandResult(MESSAGE_SUCCESS);
- }
-}
diff --git a/src/main/java/seedu/address/logic/commands/DeleteCommand.java b/src/main/java/seedu/address/logic/commands/DeleteCommand.java
deleted file mode 100644
index 1bfebe8912a8..000000000000
--- a/src/main/java/seedu/address/logic/commands/DeleteCommand.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package seedu.address.logic.commands;
-
-import seedu.address.commons.core.Messages;
-import seedu.address.commons.core.UnmodifiableObservableList;
-import seedu.address.model.person.ReadOnlyPerson;
-import seedu.address.model.person.UniquePersonList.PersonNotFoundException;
-
-/**
- * Deletes a person identified using it's last displayed index from the address book.
- */
-public class DeleteCommand extends Command {
-
- public static final String COMMAND_WORD = "delete";
-
- public static final String MESSAGE_USAGE = COMMAND_WORD
- + ": Deletes the person identified by the index number used in the last person listing.\n"
- + "Parameters: INDEX (must be a positive integer)\n"
- + "Example: " + COMMAND_WORD + " 1";
-
- public static final String MESSAGE_DELETE_PERSON_SUCCESS = "Deleted Person: %1$s";
-
- public final int targetIndex;
-
- public DeleteCommand(int targetIndex) {
- this.targetIndex = targetIndex;
- }
-
-
- @Override
- public CommandResult execute() {
-
- UnmodifiableObservableList lastShownList = model.getFilteredPersonList();
-
- if (lastShownList.size() < targetIndex) {
- indicateAttemptToExecuteIncorrectCommand();
- return new CommandResult(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
- }
-
- ReadOnlyPerson personToDelete = lastShownList.get(targetIndex - 1);
-
- try {
- model.deletePerson(personToDelete);
- } catch (PersonNotFoundException pnfe) {
- assert false : "The target person cannot be missing";
- }
-
- return new CommandResult(String.format(MESSAGE_DELETE_PERSON_SUCCESS, personToDelete));
- }
-
-}
diff --git a/src/main/java/seedu/address/logic/commands/ListCommand.java b/src/main/java/seedu/address/logic/commands/ListCommand.java
deleted file mode 100644
index 9bdd457a1b01..000000000000
--- a/src/main/java/seedu/address/logic/commands/ListCommand.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package seedu.address.logic.commands;
-
-
-/**
- * Lists all persons in the address book to the user.
- */
-public class ListCommand extends Command {
-
- public static final String COMMAND_WORD = "list";
-
- public static final String MESSAGE_SUCCESS = "Listed all persons";
-
- public ListCommand() {}
-
- @Override
- public CommandResult execute() {
- model.updateFilteredListToShowAll();
- return new CommandResult(MESSAGE_SUCCESS);
- }
-}
diff --git a/src/main/java/seedu/address/logic/commands/SelectCommand.java b/src/main/java/seedu/address/logic/commands/SelectCommand.java
deleted file mode 100644
index 9ca0551f1951..000000000000
--- a/src/main/java/seedu/address/logic/commands/SelectCommand.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package seedu.address.logic.commands;
-
-import seedu.address.commons.core.EventsCenter;
-import seedu.address.commons.core.Messages;
-import seedu.address.commons.events.ui.JumpToListRequestEvent;
-import seedu.address.commons.core.UnmodifiableObservableList;
-import seedu.address.model.person.ReadOnlyPerson;
-
-/**
- * Selects a person identified using it's last displayed index from the address book.
- */
-public class SelectCommand extends Command {
-
- public final int targetIndex;
-
- public static final String COMMAND_WORD = "select";
-
- public static final String MESSAGE_USAGE = COMMAND_WORD
- + ": Selects the person identified by the index number used in the last person listing.\n"
- + "Parameters: INDEX (must be a positive integer)\n"
- + "Example: " + COMMAND_WORD + " 1";
-
- public static final String MESSAGE_SELECT_PERSON_SUCCESS = "Selected Person: %1$s";
-
- public SelectCommand(int targetIndex) {
- this.targetIndex = targetIndex;
- }
-
- @Override
- public CommandResult execute() {
-
- UnmodifiableObservableList lastShownList = model.getFilteredPersonList();
-
- if (lastShownList.size() < targetIndex) {
- indicateAttemptToExecuteIncorrectCommand();
- return new CommandResult(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
- }
-
- EventsCenter.getInstance().post(new JumpToListRequestEvent(targetIndex - 1));
- return new CommandResult(String.format(MESSAGE_SELECT_PERSON_SUCCESS, targetIndex));
-
- }
-
-}
diff --git a/src/main/java/seedu/address/logic/parser/Parser.java b/src/main/java/seedu/address/logic/parser/Parser.java
deleted file mode 100644
index 959b2cd0383c..000000000000
--- a/src/main/java/seedu/address/logic/parser/Parser.java
+++ /dev/null
@@ -1,192 +0,0 @@
-package seedu.address.logic.parser;
-
-import seedu.address.logic.commands.*;
-import seedu.address.commons.util.StringUtil;
-import seedu.address.commons.exceptions.IllegalValueException;
-
-import java.util.*;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
-import static seedu.address.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND;
-
-/**
- * Parses user input.
- */
-public class Parser {
-
- /**
- * Used for initial separation of command word and args.
- */
- private static final Pattern BASIC_COMMAND_FORMAT = Pattern.compile("(?\\S+)(?.*)");
-
- private static final Pattern PERSON_INDEX_ARGS_FORMAT = Pattern.compile("(?.+)");
-
- private static final Pattern KEYWORDS_ARGS_FORMAT =
- Pattern.compile("(?\\S+(?:\\s+\\S+)*)"); // one or more keywords separated by whitespace
-
- private static final Pattern PERSON_DATA_ARGS_FORMAT = // '/' forward slashes are reserved for delimiter prefixes
- Pattern.compile("(?[^/]+)"
- + " (?p?)p/(?[^/]+)"
- + " (?p?)e/(?[^/]+)"
- + " (?p?)a/(?[^/]+)"
- + "(?(?: t/[^/]+)*)"); // variable number of tags
-
- public Parser() {}
-
- /**
- * Parses user input into command for execution.
- *
- * @param userInput full user input string
- * @return the command based on the user input
- */
- public Command parseCommand(String userInput) {
- final Matcher matcher = BASIC_COMMAND_FORMAT.matcher(userInput.trim());
- if (!matcher.matches()) {
- return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE));
- }
-
- final String commandWord = matcher.group("commandWord");
- final String arguments = matcher.group("arguments");
- switch (commandWord) {
-
- case AddCommand.COMMAND_WORD:
- return prepareAdd(arguments);
-
- case SelectCommand.COMMAND_WORD:
- return prepareSelect(arguments);
-
- case DeleteCommand.COMMAND_WORD:
- return prepareDelete(arguments);
-
- case ClearCommand.COMMAND_WORD:
- return new ClearCommand();
-
- case FindCommand.COMMAND_WORD:
- return prepareFind(arguments);
-
- case ListCommand.COMMAND_WORD:
- return new ListCommand();
-
- case ExitCommand.COMMAND_WORD:
- return new ExitCommand();
-
- case HelpCommand.COMMAND_WORD:
- return new HelpCommand();
-
- default:
- return new IncorrectCommand(MESSAGE_UNKNOWN_COMMAND);
- }
- }
-
- /**
- * Parses arguments in the context of the add person command.
- *
- * @param args full command args string
- * @return the prepared command
- */
- private Command prepareAdd(String args){
- final Matcher matcher = PERSON_DATA_ARGS_FORMAT.matcher(args.trim());
- // Validate arg string format
- if (!matcher.matches()) {
- return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE));
- }
- try {
- return new AddCommand(
- matcher.group("name"),
- matcher.group("phone"),
- matcher.group("email"),
- matcher.group("address"),
- getTagsFromArgs(matcher.group("tagArguments"))
- );
- } catch (IllegalValueException ive) {
- return new IncorrectCommand(ive.getMessage());
- }
- }
-
- /**
- * Extracts the new person's tags from the add command's tag arguments string.
- * Merges duplicate tag strings.
- */
- private static Set getTagsFromArgs(String tagArguments) throws IllegalValueException {
- // no tags
- if (tagArguments.isEmpty()) {
- return Collections.emptySet();
- }
- // replace first delimiter prefix, then split
- final Collection tagStrings = Arrays.asList(tagArguments.replaceFirst(" t/", "").split(" t/"));
- return new HashSet<>(tagStrings);
- }
-
- /**
- * Parses arguments in the context of the delete person command.
- *
- * @param args full command args string
- * @return the prepared command
- */
- private Command prepareDelete(String args) {
-
- Optional index = parseIndex(args);
- 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 select person command.
- *
- * @param args full command args string
- * @return the prepared command
- */
- private Command prepareSelect(String args) {
- Optional index = parseIndex(args);
- 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 = PERSON_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 person command.
- *
- * @param args full command args string
- * @return the prepared command
- */
- private Command prepareFind(String args) {
- final Matcher matcher = KEYWORDS_ARGS_FORMAT.matcher(args.trim());
- 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);
- }
-
-}
\ No newline at end of file
diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/seedu/address/model/AddressBook.java
deleted file mode 100644
index 298cc1b82ce8..000000000000
--- a/src/main/java/seedu/address/model/AddressBook.java
+++ /dev/null
@@ -1,163 +0,0 @@
-package seedu.address.model;
-
-import javafx.collections.ObservableList;
-import seedu.address.model.person.Person;
-import seedu.address.model.person.ReadOnlyPerson;
-import seedu.address.model.person.UniquePersonList;
-import seedu.address.model.tag.Tag;
-import seedu.address.model.tag.UniqueTagList;
-
-import java.util.*;
-import java.util.stream.Collectors;
-
-/**
- * Wraps all data at the address-book level
- * Duplicates are not allowed (by .equals comparison)
- */
-public class AddressBook implements ReadOnlyAddressBook {
-
- private final UniquePersonList persons;
- private final UniqueTagList tags;
-
- {
- persons = new UniquePersonList();
- tags = new UniqueTagList();
- }
-
- public AddressBook() {}
-
- /**
- * Persons and Tags are copied into this addressbook
- */
- public AddressBook(ReadOnlyAddressBook toBeCopied) {
- this(toBeCopied.getUniquePersonList(), toBeCopied.getUniqueTagList());
- }
-
- /**
- * Persons and Tags are copied into this addressbook
- */
- public AddressBook(UniquePersonList persons, UniqueTagList tags) {
- resetData(persons.getInternalList(), tags.getInternalList());
- }
-
- public static ReadOnlyAddressBook getEmptyAddressBook() {
- return new AddressBook();
- }
-
-//// list overwrite operations
-
- public ObservableList getPersons() {
- return persons.getInternalList();
- }
-
- public void setPersons(List persons) {
- this.persons.getInternalList().setAll(persons);
- }
-
- public void setTags(Collection tags) {
- this.tags.getInternalList().setAll(tags);
- }
-
- public void resetData(Collection extends ReadOnlyPerson> newPersons, Collection newTags) {
- setPersons(newPersons.stream().map(Person::new).collect(Collectors.toList()));
- setTags(newTags);
- }
-
- public void resetData(ReadOnlyAddressBook newData) {
- resetData(newData.getPersonList(), newData.getTagList());
- }
-
-//// person-level operations
-
- /**
- * Adds a person to the address book.
- * Also checks the new person's tags and updates {@link #tags} with any new tags found,
- * and updates the Tag objects in the person to point to those in {@link #tags}.
- *
- * @throws UniquePersonList.DuplicatePersonException if an equivalent person already exists.
- */
- public void addPerson(Person p) throws UniquePersonList.DuplicatePersonException {
- syncTagsWithMasterList(p);
- persons.add(p);
- }
-
- /**
- * Ensures that every tag in this person:
- * - exists in the master list {@link #tags}
- * - points to a Tag object in the master list
- */
- private void syncTagsWithMasterList(Person person) {
- final UniqueTagList personTags = person.getTags();
- tags.mergeFrom(personTags);
-
- // Create map with values = tag object references in the master list
- final Map masterTagObjects = new HashMap<>();
- for (Tag tag : tags) {
- masterTagObjects.put(tag, tag);
- }
-
- // Rebuild the list of person tags using references from the master list
- final Set commonTagReferences = new HashSet<>();
- for (Tag tag : personTags) {
- commonTagReferences.add(masterTagObjects.get(tag));
- }
- person.setTags(new UniqueTagList(commonTagReferences));
- }
-
- public boolean removePerson(ReadOnlyPerson key) throws UniquePersonList.PersonNotFoundException {
- if (persons.remove(key)) {
- return true;
- } else {
- throw new UniquePersonList.PersonNotFoundException();
- }
- }
-
-//// tag-level operations
-
- public void addTag(Tag t) throws UniqueTagList.DuplicateTagException {
- tags.add(t);
- }
-
-//// util methods
-
- @Override
- public String toString() {
- return persons.getInternalList().size() + " persons, " + tags.getInternalList().size() + " tags";
- // TODO: refine later
- }
-
- @Override
- public List getPersonList() {
- return Collections.unmodifiableList(persons.getInternalList());
- }
-
- @Override
- public List getTagList() {
- return Collections.unmodifiableList(tags.getInternalList());
- }
-
- @Override
- public UniquePersonList getUniquePersonList() {
- return this.persons;
- }
-
- @Override
- public UniqueTagList getUniqueTagList() {
- return this.tags;
- }
-
-
- @Override
- public boolean equals(Object other) {
- return other == this // short circuit if same object
- || (other instanceof AddressBook // instanceof handles nulls
- && this.persons.equals(((AddressBook) other).persons)
- && this.tags.equals(((AddressBook) other).tags));
- }
-
- @Override
- public int hashCode() {
- // use this method for custom fields hashing instead of implementing your own
- return Objects.hash(persons, tags);
- }
-}
diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java
deleted file mode 100644
index d14a27a93b5e..000000000000
--- a/src/main/java/seedu/address/model/Model.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package seedu.address.model;
-
-import seedu.address.commons.core.UnmodifiableObservableList;
-import seedu.address.model.person.Person;
-import seedu.address.model.person.ReadOnlyPerson;
-import seedu.address.model.person.UniquePersonList;
-
-import java.util.Set;
-
-/**
- * The API of the Model component.
- */
-public interface Model {
- /** Clears existing backing model and replaces with the provided new data. */
- void resetData(ReadOnlyAddressBook newData);
-
- /** Returns the AddressBook */
- ReadOnlyAddressBook getAddressBook();
-
- /** Deletes the given person. */
- void deletePerson(ReadOnlyPerson target) throws UniquePersonList.PersonNotFoundException;
-
- /** Adds the given person */
- void addPerson(Person person) throws UniquePersonList.DuplicatePersonException;
-
- /** Returns the filtered person list as an {@code UnmodifiableObservableList} */
- UnmodifiableObservableList getFilteredPersonList();
-
- /** Updates the filter of the filtered person list to show all persons */
- void updateFilteredListToShowAll();
-
- /** Updates the filter of the filtered person list to filter by the given keywords*/
- void updateFilteredPersonList(Set keywords);
-
-}
diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java
deleted file mode 100644
index 869226d02bf1..000000000000
--- a/src/main/java/seedu/address/model/ModelManager.java
+++ /dev/null
@@ -1,153 +0,0 @@
-package seedu.address.model;
-
-import javafx.collections.transformation.FilteredList;
-import seedu.address.commons.core.LogsCenter;
-import seedu.address.commons.core.UnmodifiableObservableList;
-import seedu.address.commons.util.StringUtil;
-import seedu.address.commons.events.model.AddressBookChangedEvent;
-import seedu.address.commons.core.ComponentManager;
-import seedu.address.model.person.Person;
-import seedu.address.model.person.ReadOnlyPerson;
-import seedu.address.model.person.UniquePersonList;
-import seedu.address.model.person.UniquePersonList.PersonNotFoundException;
-
-import java.util.Set;
-import java.util.logging.Logger;
-
-/**
- * Represents the in-memory model of the address book data.
- * All changes to any model should be synchronized.
- */
-public class ModelManager extends ComponentManager implements Model {
- private static final Logger logger = LogsCenter.getLogger(ModelManager.class);
-
- private final AddressBook addressBook;
- private final FilteredList filteredPersons;
-
- /**
- * Initializes a ModelManager with the given AddressBook
- * AddressBook and its variables should not be null
- */
- public ModelManager(AddressBook src, UserPrefs userPrefs) {
- super();
- assert src != null;
- assert userPrefs != null;
-
- logger.fine("Initializing with address book: " + src + " and user prefs " + userPrefs);
-
- addressBook = new AddressBook(src);
- filteredPersons = new FilteredList<>(addressBook.getPersons());
- }
-
- public ModelManager() {
- this(new AddressBook(), new UserPrefs());
- }
-
- public ModelManager(ReadOnlyAddressBook initialData, UserPrefs userPrefs) {
- addressBook = new AddressBook(initialData);
- filteredPersons = new FilteredList<>(addressBook.getPersons());
- }
-
- @Override
- public void resetData(ReadOnlyAddressBook newData) {
- addressBook.resetData(newData);
- indicateAddressBookChanged();
- }
-
- @Override
- public ReadOnlyAddressBook getAddressBook() {
- return addressBook;
- }
-
- /** Raises an event to indicate the model has changed */
- private void indicateAddressBookChanged() {
- raise(new AddressBookChangedEvent(addressBook));
- }
-
- @Override
- public synchronized void deletePerson(ReadOnlyPerson target) throws PersonNotFoundException {
- addressBook.removePerson(target);
- indicateAddressBookChanged();
- }
-
- @Override
- public synchronized void addPerson(Person person) throws UniquePersonList.DuplicatePersonException {
- addressBook.addPerson(person);
- updateFilteredListToShowAll();
- indicateAddressBookChanged();
- }
-
- //=========== Filtered Person List Accessors ===============================================================
-
- @Override
- public UnmodifiableObservableList getFilteredPersonList() {
- return new UnmodifiableObservableList<>(filteredPersons);
- }
-
- @Override
- public void updateFilteredListToShowAll() {
- filteredPersons.setPredicate(null);
- }
-
- @Override
- public void updateFilteredPersonList(Set keywords){
- updateFilteredPersonList(new PredicateExpression(new NameQualifier(keywords)));
- }
-
- private void updateFilteredPersonList(Expression expression) {
- filteredPersons.setPredicate(expression::satisfies);
- }
-
- //========== Inner classes/interfaces used for filtering ==================================================
-
- interface Expression {
- boolean satisfies(ReadOnlyPerson person);
- String toString();
- }
-
- private class PredicateExpression implements Expression {
-
- private final Qualifier qualifier;
-
- PredicateExpression(Qualifier qualifier) {
- this.qualifier = qualifier;
- }
-
- @Override
- public boolean satisfies(ReadOnlyPerson person) {
- return qualifier.run(person);
- }
-
- @Override
- public String toString() {
- return qualifier.toString();
- }
- }
-
- interface Qualifier {
- boolean run(ReadOnlyPerson person);
- String toString();
- }
-
- private class NameQualifier implements Qualifier {
- private Set nameKeyWords;
-
- NameQualifier(Set nameKeyWords) {
- this.nameKeyWords = nameKeyWords;
- }
-
- @Override
- public boolean run(ReadOnlyPerson person) {
- return nameKeyWords.stream()
- .filter(keyword -> StringUtil.containsIgnoreCase(person.getName().fullName, keyword))
- .findAny()
- .isPresent();
- }
-
- @Override
- public String toString() {
- return "name=" + String.join(", ", nameKeyWords);
- }
- }
-
-}
diff --git a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java b/src/main/java/seedu/address/model/ReadOnlyAddressBook.java
deleted file mode 100644
index bfca099b1e81..000000000000
--- a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package seedu.address.model;
-
-
-import seedu.address.model.person.ReadOnlyPerson;
-import seedu.address.model.person.UniquePersonList;
-import seedu.address.model.tag.Tag;
-import seedu.address.model.tag.UniqueTagList;
-
-import java.util.List;
-
-/**
- * Unmodifiable view of an address book
- */
-public interface ReadOnlyAddressBook {
-
- UniqueTagList getUniqueTagList();
-
- UniquePersonList getUniquePersonList();
-
- /**
- * Returns an unmodifiable view of persons list
- */
- List getPersonList();
-
- /**
- * Returns an unmodifiable view of tags list
- */
- List getTagList();
-
-}
diff --git a/src/main/java/seedu/address/model/person/Address.java b/src/main/java/seedu/address/model/person/Address.java
deleted file mode 100644
index a2bd109c005e..000000000000
--- a/src/main/java/seedu/address/model/person/Address.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package seedu.address.model.person;
-
-
-import seedu.address.commons.exceptions.IllegalValueException;
-
-/**
- * Represents a Person's address in the address book.
- * Guarantees: immutable; is valid as declared in {@link #isValidAddress(String)}
- */
-public class Address {
-
- public static final String MESSAGE_ADDRESS_CONSTRAINTS = "Person addresses can be in any format";
- public static final String ADDRESS_VALIDATION_REGEX = ".+";
-
- public final String value;
-
- /**
- * Validates given address.
- *
- * @throws IllegalValueException if given address string is invalid.
- */
- public Address(String address) throws IllegalValueException {
- assert address != null;
- if (!isValidAddress(address)) {
- throw new IllegalValueException(MESSAGE_ADDRESS_CONSTRAINTS);
- }
- this.value = address;
- }
-
- /**
- * Returns true if a given string is a valid person email.
- */
- public static boolean isValidAddress(String test) {
- return test.matches(ADDRESS_VALIDATION_REGEX);
- }
-
- @Override
- public String toString() {
- return value;
- }
-
- @Override
- public boolean equals(Object other) {
- return other == this // short circuit if same object
- || (other instanceof Address // instanceof handles nulls
- && this.value.equals(((Address) other).value)); // state check
- }
-
- @Override
- public int hashCode() {
- return value.hashCode();
- }
-
-}
\ No newline at end of file
diff --git a/src/main/java/seedu/address/model/person/Email.java b/src/main/java/seedu/address/model/person/Email.java
deleted file mode 100644
index 5da4d1078236..000000000000
--- a/src/main/java/seedu/address/model/person/Email.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package seedu.address.model.person;
-
-
-import seedu.address.commons.exceptions.IllegalValueException;
-
-/**
- * Represents a Person's phone number in the address book.
- * Guarantees: immutable; is valid as declared in {@link #isValidEmail(String)}
- */
-public class Email {
-
- public static final String MESSAGE_EMAIL_CONSTRAINTS =
- "Person emails should be 2 alphanumeric/period strings separated by '@'";
- public static final String EMAIL_VALIDATION_REGEX = "[\\w\\.]+@[\\w\\.]+";
-
- public final String value;
-
- /**
- * Validates given email.
- *
- * @throws IllegalValueException if given email address string is invalid.
- */
- public Email(String email) throws IllegalValueException {
- assert email != null;
- email = email.trim();
- if (!isValidEmail(email)) {
- throw new IllegalValueException(MESSAGE_EMAIL_CONSTRAINTS);
- }
- this.value = email;
- }
-
- /**
- * Returns if a given string is a valid person email.
- */
- public static boolean isValidEmail(String test) {
- return test.matches(EMAIL_VALIDATION_REGEX);
- }
-
- @Override
- public String toString() {
- return value;
- }
-
- @Override
- public boolean equals(Object other) {
- return other == this // short circuit if same object
- || (other instanceof Email // instanceof handles nulls
- && this.value.equals(((Email) other).value)); // state check
- }
-
- @Override
- public int hashCode() {
- return value.hashCode();
- }
-
-}
diff --git a/src/main/java/seedu/address/model/person/Person.java b/src/main/java/seedu/address/model/person/Person.java
deleted file mode 100644
index 03ffce7d2e79..000000000000
--- a/src/main/java/seedu/address/model/person/Person.java
+++ /dev/null
@@ -1,90 +0,0 @@
-package seedu.address.model.person;
-
-import seedu.address.commons.util.CollectionUtil;
-import seedu.address.model.tag.UniqueTagList;
-
-import java.util.Objects;
-
-/**
- * Represents a Person in the address book.
- * Guarantees: details are present and not null, field values are validated.
- */
-public class Person implements ReadOnlyPerson {
-
- private Name name;
- private Phone phone;
- private Email email;
- private Address address;
-
- private UniqueTagList tags;
-
- /**
- * Every field must be present and not null.
- */
- public Person(Name name, Phone phone, Email email, Address address, UniqueTagList tags) {
- assert !CollectionUtil.isAnyNull(name, phone, email, address, tags);
- this.name = name;
- this.phone = phone;
- this.email = email;
- this.address = address;
- this.tags = new UniqueTagList(tags); // protect internal tags from changes in the arg list
- }
-
- /**
- * Copy constructor.
- */
- public Person(ReadOnlyPerson source) {
- this(source.getName(), source.getPhone(), source.getEmail(), source.getAddress(), source.getTags());
- }
-
- @Override
- public Name getName() {
- return name;
- }
-
- @Override
- public Phone getPhone() {
- return phone;
- }
-
- @Override
- public Email getEmail() {
- return email;
- }
-
- @Override
- public Address getAddress() {
- return address;
- }
-
- @Override
- public UniqueTagList getTags() {
- return new UniqueTagList(tags);
- }
-
- /**
- * Replaces this person's tags with the tags in the argument tag list.
- */
- public void setTags(UniqueTagList replacement) {
- tags.setTags(replacement);
- }
-
- @Override
- public boolean equals(Object other) {
- return other == this // short circuit if same object
- || (other instanceof ReadOnlyPerson // instanceof handles nulls
- && this.isSameStateAs((ReadOnlyPerson) other));
- }
-
- @Override
- public int hashCode() {
- // use this method for custom fields hashing instead of implementing your own
- return Objects.hash(name, phone, email, address, tags);
- }
-
- @Override
- public String toString() {
- return getAsText();
- }
-
-}
diff --git a/src/main/java/seedu/address/model/person/Phone.java b/src/main/java/seedu/address/model/person/Phone.java
deleted file mode 100644
index d27b2244b727..000000000000
--- a/src/main/java/seedu/address/model/person/Phone.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package seedu.address.model.person;
-
-import seedu.address.commons.exceptions.IllegalValueException;
-
-/**
- * Represents a Person's phone number in the address book.
- * Guarantees: immutable; is valid as declared in {@link #isValidPhone(String)}
- */
-public class Phone {
-
- public static final String MESSAGE_PHONE_CONSTRAINTS = "Person phone numbers should only contain numbers";
- public static final String PHONE_VALIDATION_REGEX = "\\d+";
-
- public final String value;
-
- /**
- * Validates given phone number.
- *
- * @throws IllegalValueException if given phone string is invalid.
- */
- public Phone(String phone) throws IllegalValueException {
- assert phone != null;
- phone = phone.trim();
- if (!isValidPhone(phone)) {
- throw new IllegalValueException(MESSAGE_PHONE_CONSTRAINTS);
- }
- this.value = phone;
- }
-
- /**
- * Returns true if a given string is a valid person phone number.
- */
- public static boolean isValidPhone(String test) {
- return test.matches(PHONE_VALIDATION_REGEX);
- }
-
- @Override
- public String toString() {
- return value;
- }
-
- @Override
- public boolean equals(Object other) {
- return other == this // short circuit if same object
- || (other instanceof Phone // instanceof handles nulls
- && this.value.equals(((Phone) other).value)); // state check
- }
-
- @Override
- public int hashCode() {
- return value.hashCode();
- }
-
-}
diff --git a/src/main/java/seedu/address/model/person/ReadOnlyPerson.java b/src/main/java/seedu/address/model/person/ReadOnlyPerson.java
deleted file mode 100644
index d45be4b5fe36..000000000000
--- a/src/main/java/seedu/address/model/person/ReadOnlyPerson.java
+++ /dev/null
@@ -1,65 +0,0 @@
-package seedu.address.model.person;
-
-import seedu.address.model.tag.UniqueTagList;
-
-/**
- * A read-only immutable interface for a Person in the addressbook.
- * Implementations should guarantee: details are present and not null, field values are validated.
- */
-public interface ReadOnlyPerson {
-
- Name getName();
- Phone getPhone();
- Email getEmail();
- Address getAddress();
-
- /**
- * The returned TagList is a deep copy of the internal TagList,
- * changes on the returned list will not affect the person's internal tags.
- */
- UniqueTagList getTags();
-
- /**
- * Returns true if both have the same state. (interfaces cannot override .equals)
- */
- default boolean isSameStateAs(ReadOnlyPerson other) {
- return other == this // short circuit if same object
- || (other != null // this is first to avoid NPE below
- && other.getName().equals(this.getName()) // state checks here onwards
- && other.getPhone().equals(this.getPhone())
- && other.getEmail().equals(this.getEmail())
- && other.getAddress().equals(this.getAddress()));
- }
-
- /**
- * Formats the person as text, showing all contact details.
- */
- default String getAsText() {
- final StringBuilder builder = new StringBuilder();
- builder.append(getName())
- .append(" Phone: ")
- .append(getPhone())
- .append(" Email: ")
- .append(getEmail())
- .append(" Address: ")
- .append(getAddress())
- .append(" Tags: ");
- getTags().forEach(builder::append);
- return builder.toString();
- }
-
- /**
- * Returns a string representation of this Person's tags
- */
- default String tagsString() {
- final StringBuffer buffer = new StringBuffer();
- final String separator = ", ";
- getTags().forEach(tag -> buffer.append(tag).append(separator));
- if (buffer.length() == 0) {
- return "";
- } else {
- return buffer.substring(0, buffer.length() - separator.length());
- }
- }
-
-}
diff --git a/src/main/java/seedu/address/model/person/UniquePersonList.java b/src/main/java/seedu/address/model/person/UniquePersonList.java
deleted file mode 100644
index 263f1fcc7dd5..000000000000
--- a/src/main/java/seedu/address/model/person/UniquePersonList.java
+++ /dev/null
@@ -1,98 +0,0 @@
-package seedu.address.model.person;
-
-import javafx.collections.FXCollections;
-import javafx.collections.ObservableList;
-import seedu.address.commons.util.CollectionUtil;
-import seedu.address.commons.exceptions.DuplicateDataException;
-
-import java.util.*;
-
-/**
- * A list of persons that enforces uniqueness between its elements and does not allow nulls.
- *
- * Supports a minimal set of list operations.
- *
- * @see Person#equals(Object)
- * @see CollectionUtil#elementsAreUnique(Collection)
- */
-public class UniquePersonList implements Iterable {
-
- /**
- * Signals that an operation would have violated the 'no duplicates' property of the list.
- */
- public static class DuplicatePersonException extends DuplicateDataException {
- protected DuplicatePersonException() {
- super("Operation would result in duplicate persons");
- }
- }
-
- /**
- * Signals that an operation targeting a specified person in the list would fail because
- * there is no such matching person in the list.
- */
- public static class PersonNotFoundException extends Exception {}
-
- private final ObservableList internalList = FXCollections.observableArrayList();
-
- /**
- * Constructs empty PersonList.
- */
- public UniquePersonList() {}
-
- /**
- * Returns true if the list contains an equivalent person as the given argument.
- */
- public boolean contains(ReadOnlyPerson toCheck) {
- assert toCheck != null;
- return internalList.contains(toCheck);
- }
-
- /**
- * Adds a person to the list.
- *
- * @throws DuplicatePersonException if the person to add is a duplicate of an existing person in the list.
- */
- public void add(Person toAdd) throws DuplicatePersonException {
- assert toAdd != null;
- if (contains(toAdd)) {
- throw new DuplicatePersonException();
- }
- internalList.add(toAdd);
- }
-
- /**
- * Removes the equivalent person from the list.
- *
- * @throws PersonNotFoundException if no such person could be found in the list.
- */
- public boolean remove(ReadOnlyPerson toRemove) throws PersonNotFoundException {
- assert toRemove != null;
- final boolean personFoundAndDeleted = internalList.remove(toRemove);
- if (!personFoundAndDeleted) {
- throw new PersonNotFoundException();
- }
- return personFoundAndDeleted;
- }
-
- public ObservableList getInternalList() {
- return internalList;
- }
-
- @Override
- public Iterator iterator() {
- return internalList.iterator();
- }
-
- @Override
- public boolean equals(Object other) {
- return other == this // short circuit if same object
- || (other instanceof UniquePersonList // instanceof handles nulls
- && this.internalList.equals(
- ((UniquePersonList) other).internalList));
- }
-
- @Override
- public int hashCode() {
- return internalList.hashCode();
- }
-}
diff --git a/src/main/java/seedu/address/storage/AddressBookStorage.java b/src/main/java/seedu/address/storage/AddressBookStorage.java
deleted file mode 100644
index 80033086985b..000000000000
--- a/src/main/java/seedu/address/storage/AddressBookStorage.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package seedu.address.storage;
-
-import seedu.address.commons.exceptions.DataConversionException;
-import seedu.address.model.ReadOnlyAddressBook;
-
-import java.io.IOException;
-import java.util.Optional;
-
-/**
- * Represents a storage for {@link seedu.address.model.AddressBook}.
- */
-public interface AddressBookStorage {
-
- /**
- * Returns the file path of the data file.
- */
- String getAddressBookFilePath();
-
- /**
- * Returns AddressBook data as a {@link ReadOnlyAddressBook}.
- * Returns {@code Optional.empty()} if storage file is not found.
- * @throws DataConversionException if the data in storage is not in the expected format.
- * @throws IOException if there was any problem when reading from the storage.
- */
- Optional readAddressBook() throws DataConversionException, IOException;
-
- /**
- * @see #getAddressBookFilePath()
- */
- Optional readAddressBook(String filePath) throws DataConversionException, IOException;
-
- /**
- * Saves the given {@link ReadOnlyAddressBook} to the storage.
- * @param addressBook cannot be null.
- * @throws IOException if there was any problem writing to the file.
- */
- void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException;
-
- /**
- * @see #saveAddressBook(ReadOnlyAddressBook)
- */
- void saveAddressBook(ReadOnlyAddressBook addressBook, String filePath) throws IOException;
-
-}
diff --git a/src/main/java/seedu/address/storage/Storage.java b/src/main/java/seedu/address/storage/Storage.java
deleted file mode 100644
index 91002a8a821a..000000000000
--- a/src/main/java/seedu/address/storage/Storage.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package seedu.address.storage;
-
-import seedu.address.commons.events.model.AddressBookChangedEvent;
-import seedu.address.commons.events.storage.DataSavingExceptionEvent;
-import seedu.address.commons.exceptions.DataConversionException;
-import seedu.address.model.ReadOnlyAddressBook;
-import seedu.address.model.UserPrefs;
-
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.Optional;
-
-/**
- * API of the Storage component
- */
-public interface Storage extends AddressBookStorage, UserPrefsStorage {
-
- @Override
- Optional readUserPrefs() throws DataConversionException, IOException;
-
- @Override
- void saveUserPrefs(UserPrefs userPrefs) throws IOException;
-
- @Override
- String getAddressBookFilePath();
-
- @Override
- Optional readAddressBook() throws DataConversionException, IOException;
-
- @Override
- void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException;
-
- /**
- * Saves the current version of the Address Book to the hard disk.
- * Creates the data file if it is missing.
- * Raises {@link DataSavingExceptionEvent} if there was an error during saving.
- */
- void handleAddressBookChangedEvent(AddressBookChangedEvent abce);
-}
diff --git a/src/main/java/seedu/address/storage/StorageManager.java b/src/main/java/seedu/address/storage/StorageManager.java
deleted file mode 100644
index ba1f72f15c27..000000000000
--- a/src/main/java/seedu/address/storage/StorageManager.java
+++ /dev/null
@@ -1,91 +0,0 @@
-package seedu.address.storage;
-
-import com.google.common.eventbus.Subscribe;
-import seedu.address.commons.core.ComponentManager;
-import seedu.address.commons.core.LogsCenter;
-import seedu.address.commons.events.model.AddressBookChangedEvent;
-import seedu.address.commons.events.storage.DataSavingExceptionEvent;
-import seedu.address.commons.exceptions.DataConversionException;
-import seedu.address.model.ReadOnlyAddressBook;
-import seedu.address.model.UserPrefs;
-
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.Optional;
-import java.util.logging.Logger;
-
-/**
- * Manages storage of AddressBook data in local storage.
- */
-public class StorageManager extends ComponentManager implements Storage {
-
- private static final Logger logger = LogsCenter.getLogger(StorageManager.class);
- private AddressBookStorage addressBookStorage;
- private UserPrefsStorage userPrefsStorage;
-
-
- public StorageManager(AddressBookStorage addressBookStorage, UserPrefsStorage userPrefsStorage) {
- super();
- this.addressBookStorage = addressBookStorage;
- this.userPrefsStorage = userPrefsStorage;
- }
-
- public StorageManager(String addressBookFilePath, String userPrefsFilePath) {
- this(new XmlAddressBookStorage(addressBookFilePath), new JsonUserPrefsStorage(userPrefsFilePath));
- }
-
- // ================ UserPrefs methods ==============================
-
- @Override
- public Optional readUserPrefs() throws DataConversionException, IOException {
- return userPrefsStorage.readUserPrefs();
- }
-
- @Override
- public void saveUserPrefs(UserPrefs userPrefs) throws IOException {
- userPrefsStorage.saveUserPrefs(userPrefs);
- }
-
-
- // ================ AddressBook methods ==============================
-
- @Override
- public String getAddressBookFilePath() {
- return addressBookStorage.getAddressBookFilePath();
- }
-
- @Override
- public Optional readAddressBook() throws DataConversionException, IOException {
- return readAddressBook(addressBookStorage.getAddressBookFilePath());
- }
-
- @Override
- public Optional readAddressBook(String filePath) throws DataConversionException, IOException {
- logger.fine("Attempting to read data from file: " + filePath);
- return addressBookStorage.readAddressBook(filePath);
- }
-
- @Override
- public void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException {
- saveAddressBook(addressBook, addressBookStorage.getAddressBookFilePath());
- }
-
- @Override
- public void saveAddressBook(ReadOnlyAddressBook addressBook, String filePath) throws IOException {
- logger.fine("Attempting to write to data file: " + filePath);
- addressBookStorage.saveAddressBook(addressBook, filePath);
- }
-
-
- @Override
- @Subscribe
- public void handleAddressBookChangedEvent(AddressBookChangedEvent event) {
- logger.info(LogsCenter.getEventHandlingLogMessage(event, "Local data changed, saving to file"));
- try {
- saveAddressBook(event.data);
- } catch (IOException e) {
- raise(new DataSavingExceptionEvent(e));
- }
- }
-
-}
diff --git a/src/main/java/seedu/address/storage/XmlAdaptedPerson.java b/src/main/java/seedu/address/storage/XmlAdaptedPerson.java
deleted file mode 100644
index f2167ec201b4..000000000000
--- a/src/main/java/seedu/address/storage/XmlAdaptedPerson.java
+++ /dev/null
@@ -1,68 +0,0 @@
-package seedu.address.storage;
-
-import seedu.address.commons.exceptions.IllegalValueException;
-import seedu.address.model.person.*;
-import seedu.address.model.tag.Tag;
-import seedu.address.model.tag.UniqueTagList;
-
-import javax.xml.bind.annotation.XmlElement;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * JAXB-friendly version of the Person.
- */
-public class XmlAdaptedPerson {
-
- @XmlElement(required = true)
- private String name;
- @XmlElement(required = true)
- private String phone;
- @XmlElement(required = true)
- private String email;
- @XmlElement(required = true)
- private String address;
-
- @XmlElement
- private List tagged = new ArrayList<>();
-
- /**
- * No-arg constructor for JAXB use.
- */
- public XmlAdaptedPerson() {}
-
-
- /**
- * Converts a given Person into this class for JAXB use.
- *
- * @param source future changes to this will not affect the created XmlAdaptedPerson
- */
- public XmlAdaptedPerson(ReadOnlyPerson source) {
- name = source.getName().fullName;
- phone = source.getPhone().value;
- email = source.getEmail().value;
- address = source.getAddress().value;
- tagged = new ArrayList<>();
- for (Tag tag : source.getTags()) {
- tagged.add(new XmlAdaptedTag(tag));
- }
- }
-
- /**
- * Converts this jaxb-friendly adapted person object into the model's Person object.
- *
- * @throws IllegalValueException if there were any data constraints violated in the adapted person
- */
- public Person toModelType() throws IllegalValueException {
- final List personTags = new ArrayList<>();
- for (XmlAdaptedTag tag : tagged) {
- personTags.add(tag.toModelType());
- }
- final Name name = new Name(this.name);
- final Phone phone = new Phone(this.phone);
- final Email email = new Email(this.email);
- final Address address = new Address(this.address);
- final UniqueTagList tags = new UniqueTagList(personTags);
- return new Person(name, phone, email, address, tags);
- }
-}
diff --git a/src/main/java/seedu/address/storage/XmlAddressBookStorage.java b/src/main/java/seedu/address/storage/XmlAddressBookStorage.java
deleted file mode 100644
index 30cb00270cc4..000000000000
--- a/src/main/java/seedu/address/storage/XmlAddressBookStorage.java
+++ /dev/null
@@ -1,73 +0,0 @@
-package seedu.address.storage;
-
-import seedu.address.commons.core.LogsCenter;
-import seedu.address.commons.exceptions.DataConversionException;
-import seedu.address.commons.util.FileUtil;
-import seedu.address.model.ReadOnlyAddressBook;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.Optional;
-import java.util.logging.Logger;
-
-/**
- * A class to access AddressBook data stored as an xml file on the hard disk.
- */
-public class XmlAddressBookStorage implements AddressBookStorage {
-
- private static final Logger logger = LogsCenter.getLogger(XmlAddressBookStorage.class);
-
- private String filePath;
-
- public XmlAddressBookStorage(String filePath){
- this.filePath = filePath;
- }
-
- public String getAddressBookFilePath(){
- return filePath;
- }
-
- /**
- * Similar to {@link #readAddressBook()}
- * @param filePath location of the data. Cannot be null
- * @throws DataConversionException if the file is not in the correct format.
- */
- public Optional readAddressBook(String filePath) throws DataConversionException, FileNotFoundException {
- assert filePath != null;
-
- File addressBookFile = new File(filePath);
-
- if (!addressBookFile.exists()) {
- logger.info("AddressBook file " + addressBookFile + " not found");
- return Optional.empty();
- }
-
- ReadOnlyAddressBook addressBookOptional = XmlFileStorage.loadDataFromSaveFile(new File(filePath));
-
- return Optional.of(addressBookOptional);
- }
-
- /**
- * Similar to {@link #saveAddressBook(ReadOnlyAddressBook)}
- * @param filePath location of the data. Cannot be null
- */
- public void saveAddressBook(ReadOnlyAddressBook addressBook, String filePath) throws IOException {
- assert addressBook != null;
- assert filePath != null;
-
- File file = new File(filePath);
- FileUtil.createIfMissing(file);
- XmlFileStorage.saveDataToFile(file, new XmlSerializableAddressBook(addressBook));
- }
-
- @Override
- public Optional readAddressBook() throws DataConversionException, IOException {
- return readAddressBook(filePath);
- }
-
- @Override
- public void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException {
- saveAddressBook(addressBook, filePath);
- }
-}
diff --git a/src/main/java/seedu/address/ui/PersonCard.java b/src/main/java/seedu/address/ui/PersonCard.java
deleted file mode 100644
index 259e9ad0d333..000000000000
--- a/src/main/java/seedu/address/ui/PersonCard.java
+++ /dev/null
@@ -1,65 +0,0 @@
-package seedu.address.ui;
-
-import javafx.fxml.FXML;
-import javafx.scene.Node;
-import javafx.scene.control.Label;
-import javafx.scene.layout.HBox;
-import seedu.address.model.person.ReadOnlyPerson;
-
-public class PersonCard extends UiPart{
-
- private static final String FXML = "PersonListCard.fxml";
-
- @FXML
- private HBox cardPane;
- @FXML
- private Label name;
- @FXML
- private Label id;
- @FXML
- private Label phone;
- @FXML
- private Label address;
- @FXML
- private Label email;
- @FXML
- private Label tags;
-
- private ReadOnlyPerson person;
- private int displayedIndex;
-
- public PersonCard(){
-
- }
-
- public static PersonCard load(ReadOnlyPerson person, int displayedIndex){
- PersonCard card = new PersonCard();
- card.person = person;
- card.displayedIndex = displayedIndex;
- return UiPartLoader.loadUiPart(card);
- }
-
- @FXML
- public void initialize() {
- name.setText(person.getName().fullName);
- id.setText(displayedIndex + ". ");
- phone.setText(person.getPhone().value);
- address.setText(person.getAddress().value);
- email.setText(person.getEmail().value);
- tags.setText(person.tagsString());
- }
-
- public HBox getLayout() {
- return cardPane;
- }
-
- @Override
- public void setNode(Node node) {
- cardPane = (HBox)node;
- }
-
- @Override
- public String getFxmlPath() {
- return FXML;
- }
-}
diff --git a/src/main/java/seedu/address/ui/PersonListPanel.java b/src/main/java/seedu/address/ui/PersonListPanel.java
deleted file mode 100644
index 27d9381c47b5..000000000000
--- a/src/main/java/seedu/address/ui/PersonListPanel.java
+++ /dev/null
@@ -1,108 +0,0 @@
-package seedu.address.ui;
-
-import javafx.application.Platform;
-import javafx.collections.ObservableList;
-import javafx.fxml.FXML;
-import javafx.scene.Node;
-import javafx.scene.control.ListCell;
-import javafx.scene.control.ListView;
-import javafx.scene.control.SplitPane;
-import javafx.scene.layout.AnchorPane;
-import javafx.scene.layout.VBox;
-import javafx.stage.Stage;
-import seedu.address.commons.events.ui.PersonPanelSelectionChangedEvent;
-import seedu.address.model.person.ReadOnlyPerson;
-import seedu.address.commons.core.LogsCenter;
-
-import java.util.logging.Logger;
-
-/**
- * Panel containing the list of persons.
- */
-public class PersonListPanel extends UiPart {
- private final Logger logger = LogsCenter.getLogger(PersonListPanel.class);
- private static final String FXML = "PersonListPanel.fxml";
- private VBox panel;
- private AnchorPane placeHolderPane;
-
- @FXML
- private ListView personListView;
-
- public PersonListPanel() {
- 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 PersonListPanel load(Stage primaryStage, AnchorPane personListPlaceholder,
- ObservableList personList) {
- PersonListPanel personListPanel =
- UiPartLoader.loadUiPart(primaryStage, personListPlaceholder, new PersonListPanel());
- personListPanel.configure(personList);
- return personListPanel;
- }
-
- private void configure(ObservableList personList) {
- setConnections(personList);
- addToPlaceholder();
- }
-
- private void setConnections(ObservableList personList) {
- personListView.setItems(personList);
- personListView.setCellFactory(listView -> new PersonListViewCell());
- setEventHandlerForSelectionChangeEvent();
- }
-
- private void addToPlaceholder() {
- SplitPane.setResizableWithParent(placeHolderPane, false);
- placeHolderPane.getChildren().add(panel);
- }
-
- private void setEventHandlerForSelectionChangeEvent() {
- personListView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
- if (newValue != null) {
- logger.fine("Selection in person list panel changed to : '" + newValue + "'");
- raise(new PersonPanelSelectionChangedEvent(newValue));
- }
- });
- }
-
- public void scrollTo(int index) {
- Platform.runLater(() -> {
- personListView.scrollTo(index);
- personListView.getSelectionModel().clearAndSelect(index);
- });
- }
-
- class PersonListViewCell extends ListCell {
-
- public PersonListViewCell() {
- }
-
- @Override
- protected void updateItem(ReadOnlyPerson person, boolean empty) {
- super.updateItem(person, empty);
-
- if (empty || person == null) {
- setGraphic(null);
- setText(null);
- } else {
- setGraphic(PersonCard.load(person, getIndex() + 1).getLayout());
- }
- }
- }
-
-}
diff --git a/src/main/java/seedu/address/MainApp.java b/src/main/java/seedu/task/MainApp.java
similarity index 75%
rename from src/main/java/seedu/address/MainApp.java
rename to src/main/java/seedu/task/MainApp.java
index 36dc72a74b7a..bc9d4557e591 100644
--- a/src/main/java/seedu/address/MainApp.java
+++ b/src/main/java/seedu/task/MainApp.java
@@ -1,26 +1,24 @@
-package seedu.address;
+package seedu.task;
import com.google.common.eventbus.Subscribe;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.stage.Stage;
-import seedu.address.commons.core.Config;
-import seedu.address.commons.core.EventsCenter;
-import seedu.address.commons.core.LogsCenter;
-import seedu.address.commons.core.Version;
-import seedu.address.commons.events.ui.ExitAppRequestEvent;
-import seedu.address.commons.exceptions.DataConversionException;
-import seedu.address.commons.util.StringUtil;
-import seedu.address.logic.Logic;
-import seedu.address.logic.LogicManager;
-import seedu.address.model.*;
-import seedu.address.commons.util.ConfigUtil;
-import seedu.address.storage.Storage;
-import seedu.address.storage.StorageManager;
-import seedu.address.ui.Ui;
-import seedu.address.ui.UiManager;
-
-import java.io.FileNotFoundException;
+import seedu.task.commons.core.Config;
+import seedu.task.commons.core.EventsCenter;
+import seedu.task.commons.core.LogsCenter;
+import seedu.task.commons.core.Version;
+import seedu.task.commons.events.ui.ExitAppRequestEvent;
+import seedu.task.commons.exceptions.DataConversionException;
+import seedu.task.commons.util.ConfigUtil;
+import seedu.task.commons.util.StringUtil;
+import seedu.task.logic.Logic;
+import seedu.task.logic.LogicManager;
+import seedu.task.model.*;
+import seedu.task.storage.Storage;
+import seedu.task.storage.StorageManager;
+import seedu.task.ui.Ui;
+import seedu.task.ui.UiManager;
import java.io.IOException;
import java.util.Map;
import java.util.Optional;
@@ -33,23 +31,21 @@ public class MainApp extends Application {
private static final Logger logger = LogsCenter.getLogger(MainApp.class);
public static final Version VERSION = new Version(1, 0, 0, true);
-
protected Ui ui;
protected Logic logic;
protected Storage storage;
protected Model model;
protected Config config;
protected UserPrefs userPrefs;
-
public MainApp() {}
@Override
public void init() throws Exception {
- logger.info("=============================[ Initializing AddressBook ]===========================");
+ logger.info("=============================[ Initializing MESS ]===========================");
super.init();
config = initConfig(getApplicationParameter("config"));
- storage = new StorageManager(config.getAddressBookFilePath(), config.getUserPrefsFilePath());
+ storage = new StorageManager(config);
userPrefs = initPrefs(config);
@@ -57,7 +53,7 @@ public void init() throws Exception {
model = initModelManager(storage, userPrefs);
- logic = new LogicManager(model, storage);
+ logic = new LogicManager(model);
ui = new UiManager(logic, config, userPrefs);
@@ -70,20 +66,20 @@ private String getApplicationParameter(String parameterName){
}
private Model initModelManager(Storage storage, UserPrefs userPrefs) {
- Optional addressBookOptional;
- ReadOnlyAddressBook initialData;
+ Optional taskManagerOptional;
+ ReadOnlyTaskManager initialData;
try {
- addressBookOptional = storage.readAddressBook();
- if(!addressBookOptional.isPresent()){
- logger.info("Data file not found. Will be starting with an empty AddressBook");
+ taskManagerOptional = storage.readTaskManager();
+ if(!taskManagerOptional.isPresent()){
+ logger.info("Data file not found. Will be starting with an empty TaskList");
}
- initialData = addressBookOptional.orElse(new AddressBook());
+ initialData = taskManagerOptional.orElse(new TaskManager());
} catch (DataConversionException e) {
- logger.warning("Data file not in the correct format. Will be starting with an empty AddressBook");
- initialData = new AddressBook();
+ logger.warning("Data file not in the correct format. Will be starting with an empty TaskList");
+ initialData = new TaskManager();
} catch (IOException e) {
- logger.warning("Problem while reading from the file. . Will be starting with an empty AddressBook");
- initialData = new AddressBook();
+ logger.warning("Problem while reading from the file. . Will be starting with an empty TaskList");
+ initialData = new TaskManager();
}
return new ModelManager(initialData, userPrefs);
@@ -108,13 +104,12 @@ protected Config initConfig(String configFilePath) {
try {
Optional configOptional = ConfigUtil.readConfig(configFilePathUsed);
- initializedConfig = configOptional.orElse(new Config());
+ initializedConfig = configOptional.orElse(new Config(configFilePathUsed));
} catch (DataConversionException e) {
logger.warning("Config file at " + configFilePathUsed + " is not in the correct format. " +
"Using default config properties");
initializedConfig = new Config();
}
-
//Update config file in case it was missing to begin with or there are new/unused fields
try {
ConfigUtil.saveConfig(initializedConfig, configFilePathUsed);
@@ -139,7 +134,7 @@ protected UserPrefs initPrefs(Config config) {
"Using default user prefs");
initializedPrefs = new UserPrefs();
} catch (IOException e) {
- logger.warning("Problem while reading from the file. . Will be starting with an empty AddressBook");
+ logger.warning("Problem while reading from the file. . Will be starting with an empty TaskList");
initializedPrefs = new UserPrefs();
}
@@ -159,13 +154,13 @@ private void initEventsCenter() {
@Override
public void start(Stage primaryStage) {
- logger.info("Starting AddressBook " + MainApp.VERSION);
+ logger.info("Starting TaskManager " + MainApp.VERSION);
ui.start(primaryStage);
}
@Override
public void stop() {
- logger.info("============================ [ Stopping Address Book ] =============================");
+ logger.info("============================ [ Stopping MESS] =============================");
ui.stop();
try {
storage.saveUserPrefs(userPrefs);
diff --git a/src/main/java/seedu/address/commons/core/ComponentManager.java b/src/main/java/seedu/task/commons/core/ComponentManager.java
similarity index 87%
rename from src/main/java/seedu/address/commons/core/ComponentManager.java
rename to src/main/java/seedu/task/commons/core/ComponentManager.java
index 4bc8564e5824..ab80377d93e2 100644
--- a/src/main/java/seedu/address/commons/core/ComponentManager.java
+++ b/src/main/java/seedu/task/commons/core/ComponentManager.java
@@ -1,6 +1,6 @@
-package seedu.address.commons.core;
+package seedu.task.commons.core;
-import seedu.address.commons.events.BaseEvent;
+import seedu.task.commons.events.BaseEvent;
/**
* Base class for *Manager classes
diff --git a/src/main/java/seedu/address/commons/core/Config.java b/src/main/java/seedu/task/commons/core/Config.java
similarity index 61%
rename from src/main/java/seedu/address/commons/core/Config.java
rename to src/main/java/seedu/task/commons/core/Config.java
index 6441c9ef20f4..6adf9b89a8d6 100644
--- a/src/main/java/seedu/address/commons/core/Config.java
+++ b/src/main/java/seedu/task/commons/core/Config.java
@@ -1,4 +1,4 @@
-package seedu.address.commons.core;
+package seedu.task.commons.core;
import java.util.Objects;
import java.util.logging.Level;
@@ -9,16 +9,21 @@
public class Config {
public static final String DEFAULT_CONFIG_FILE = "config.json";
+ private final String currentFilePath;
// Config values customizable through config file
- private String appTitle = "Address App";
+ private String appTitle = "MESS";
private Level logLevel = Level.INFO;
private String userPrefsFilePath = "preferences.json";
- private String addressBookFilePath = "data/addressbook.xml";
- private String addressBookName = "MyAddressBook";
-
-
+ private String taskManagerFilePath = "data/taskmanager.xml";
+ private String taskManagerName = "MESS";
+
public Config() {
+ currentFilePath = Config.DEFAULT_CONFIG_FILE;
+ }
+
+ public Config(String filePath) {
+ currentFilePath = filePath;
}
public String getAppTitle() {
@@ -45,22 +50,22 @@ public void setUserPrefsFilePath(String userPrefsFilePath) {
this.userPrefsFilePath = userPrefsFilePath;
}
- public String getAddressBookFilePath() {
- return addressBookFilePath;
+ public String getTaskManagerFilePath() {
+ return taskManagerFilePath;
}
- public void setAddressBookFilePath(String addressBookFilePath) {
- this.addressBookFilePath = addressBookFilePath;
+ public void setTaskManagerFilePath(String taskManagerFilePath) {
+ this.taskManagerFilePath = taskManagerFilePath;
}
- public String getAddressBookName() {
- return addressBookName;
+ public String getTaskManagerName() {
+ return taskManagerName;
}
- public void setAddressBookName(String addressBookName) {
- this.addressBookName = addressBookName;
+ public void setTaskManagerName(String taskManagerName) {
+ this.taskManagerName = taskManagerName;
}
-
+
@Override
public boolean equals(Object other) {
@@ -76,13 +81,13 @@ public boolean equals(Object other) {
return Objects.equals(appTitle, o.appTitle)
&& Objects.equals(logLevel, o.logLevel)
&& Objects.equals(userPrefsFilePath, o.userPrefsFilePath)
- && Objects.equals(addressBookFilePath, o.addressBookFilePath)
- && Objects.equals(addressBookName, o.addressBookName);
+ && Objects.equals(taskManagerFilePath, o.taskManagerFilePath)
+ && Objects.equals(taskManagerName, o.taskManagerName);
}
@Override
public int hashCode() {
- return Objects.hash(appTitle, logLevel, userPrefsFilePath, addressBookFilePath, addressBookName);
+ return Objects.hash(appTitle, logLevel, userPrefsFilePath, taskManagerFilePath, taskManagerName);
}
@Override
@@ -91,9 +96,13 @@ public String toString(){
sb.append("App title : " + appTitle);
sb.append("\nCurrent log level : " + logLevel);
sb.append("\nPreference file Location : " + userPrefsFilePath);
- sb.append("\nLocal data file location : " + addressBookFilePath);
- sb.append("\nAddressBook name : " + addressBookName);
+ sb.append("\nLocal data file location : " + taskManagerFilePath);
+ sb.append("\ntaskManager name : " + taskManagerName);
return sb.toString();
}
+
+ public String getFilePath() {
+ return currentFilePath;
+ }
}
diff --git a/src/main/java/seedu/address/commons/core/EventsCenter.java b/src/main/java/seedu/task/commons/core/EventsCenter.java
similarity index 92%
rename from src/main/java/seedu/address/commons/core/EventsCenter.java
rename to src/main/java/seedu/task/commons/core/EventsCenter.java
index 9652cd5c227b..78b36152d8f1 100644
--- a/src/main/java/seedu/address/commons/core/EventsCenter.java
+++ b/src/main/java/seedu/task/commons/core/EventsCenter.java
@@ -1,7 +1,8 @@
-package seedu.address.commons.core;
+package seedu.task.commons.core;
import com.google.common.eventbus.EventBus;
-import seedu.address.commons.events.BaseEvent;
+
+import seedu.task.commons.events.BaseEvent;
import java.util.logging.Logger;
diff --git a/src/main/java/seedu/address/commons/core/GuiSettings.java b/src/main/java/seedu/task/commons/core/GuiSettings.java
similarity index 95%
rename from src/main/java/seedu/address/commons/core/GuiSettings.java
rename to src/main/java/seedu/task/commons/core/GuiSettings.java
index e157ac8b8679..861e2be57d22 100644
--- a/src/main/java/seedu/address/commons/core/GuiSettings.java
+++ b/src/main/java/seedu/task/commons/core/GuiSettings.java
@@ -1,4 +1,4 @@
-package seedu.address.commons.core;
+package seedu.task.commons.core;
import java.awt.*;
import java.io.Serializable;
@@ -9,6 +9,7 @@
*/
public class GuiSettings implements Serializable {
+ private static final long serialVersionUID = 1L;
private static final double DEFAULT_HEIGHT = 600;
private static final double DEFAULT_WIDTH = 740;
diff --git a/src/main/java/seedu/address/commons/core/LogsCenter.java b/src/main/java/seedu/task/commons/core/LogsCenter.java
similarity index 97%
rename from src/main/java/seedu/address/commons/core/LogsCenter.java
rename to src/main/java/seedu/task/commons/core/LogsCenter.java
index 17939bab4975..525f2ff3b58f 100644
--- a/src/main/java/seedu/address/commons/core/LogsCenter.java
+++ b/src/main/java/seedu/task/commons/core/LogsCenter.java
@@ -1,10 +1,10 @@
-package seedu.address.commons.core;
-
-import seedu.address.commons.events.BaseEvent;
+package seedu.task.commons.core;
import java.io.IOException;
import java.util.logging.*;
+import seedu.task.commons.events.BaseEvent;
+
/**
* Configures and manages loggers and handlers, including their logging level
* Named {@link Logger}s can be obtained from this class
diff --git a/src/main/java/seedu/task/commons/core/Messages.java b/src/main/java/seedu/task/commons/core/Messages.java
new file mode 100644
index 000000000000..bc8b82c902c0
--- /dev/null
+++ b/src/main/java/seedu/task/commons/core/Messages.java
@@ -0,0 +1,15 @@
+package seedu.task.commons.core;
+
+/**
+ * Container for user visible messages.
+ */
+public class Messages {
+
+ public static final String MESSAGE_INTERNAL_ERROR = "MESS encountered an internal error!";
+ public static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command";
+ public static final String MESSAGE_INVALID_COMMAND_FORMAT = "Invalid command format! \n%1$s";
+ public static final String MESSAGE_INVALID_TASK_DISPLAYED_INDEX = "The task index provided is invalid";
+ public static final String MESSAGE_TASKS_LISTED_OVERVIEW = "%1$d tasks listed!";
+ public static final String MESSAGE_INVALID_UNCOMPLETE_TASK= "The task is not marked as complete before!";
+ public static final String MESSAGE_INVALID_UNPIN_TASK= "The task is not pinned before!";
+}
diff --git a/src/main/java/seedu/address/commons/core/UnmodifiableObservableList.java b/src/main/java/seedu/task/commons/core/UnmodifiableObservableList.java
similarity index 98%
rename from src/main/java/seedu/address/commons/core/UnmodifiableObservableList.java
rename to src/main/java/seedu/task/commons/core/UnmodifiableObservableList.java
index 5c25d8647a8d..603de284114b 100644
--- a/src/main/java/seedu/address/commons/core/UnmodifiableObservableList.java
+++ b/src/main/java/seedu/task/commons/core/UnmodifiableObservableList.java
@@ -1,4 +1,4 @@
-package seedu.address.commons.core;
+package seedu.task.commons.core;
import javafx.beans.InvalidationListener;
import javafx.collections.ListChangeListener;
@@ -95,7 +95,7 @@ public final SortedList sorted(Comparator comparator) {
public final SortedList sorted() {
return sorted(Comparator.nullsFirst((o1, o2) -> {
if (o1 instanceof Comparable) {
- return ((Comparable) o1).compareTo(o2);
+ return ((Comparable) o1).compareTo(o2);
}
return Collator.getInstance().compare(o1.toString(), o2.toString());
}));
diff --git a/src/main/java/seedu/address/commons/core/Version.java b/src/main/java/seedu/task/commons/core/Version.java
similarity index 98%
rename from src/main/java/seedu/address/commons/core/Version.java
rename to src/main/java/seedu/task/commons/core/Version.java
index 7ecb85b18f82..ee15ecf1a016 100644
--- a/src/main/java/seedu/address/commons/core/Version.java
+++ b/src/main/java/seedu/task/commons/core/Version.java
@@ -1,4 +1,4 @@
-package seedu.address.commons.core;
+package seedu.task.commons.core;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
diff --git a/src/main/java/seedu/address/commons/events/BaseEvent.java b/src/main/java/seedu/task/commons/events/BaseEvent.java
similarity index 90%
rename from src/main/java/seedu/address/commons/events/BaseEvent.java
rename to src/main/java/seedu/task/commons/events/BaseEvent.java
index 723a9c69fbd5..e59073c62551 100644
--- a/src/main/java/seedu/address/commons/events/BaseEvent.java
+++ b/src/main/java/seedu/task/commons/events/BaseEvent.java
@@ -1,4 +1,4 @@
-package seedu.address.commons.events;
+package seedu.task.commons.events;
public abstract class BaseEvent {
diff --git a/src/main/java/seedu/task/commons/events/model/ReloadFromNewFileEvent.java b/src/main/java/seedu/task/commons/events/model/ReloadFromNewFileEvent.java
new file mode 100644
index 000000000000..b07ef9c75707
--- /dev/null
+++ b/src/main/java/seedu/task/commons/events/model/ReloadFromNewFileEvent.java
@@ -0,0 +1,25 @@
+//@@author A0144939R
+package seedu.task.commons.events.model;
+
+import java.util.Optional;
+import seedu.task.commons.events.BaseEvent;
+import seedu.task.model.ReadOnlyTaskManager;
+import seedu.task.model.TaskManager;
+
+/** Indicates that the user wishes to load from an existing file*/
+public class ReloadFromNewFileEvent extends BaseEvent {
+
+ public final String filePath;
+ public final ReadOnlyTaskManager taskManager;
+
+ public ReloadFromNewFileEvent(String newFilePath, Optional newTaskManager) {
+ this.filePath = newFilePath;
+ this.taskManager = newTaskManager.orElse(new TaskManager());
+ }
+
+ @Override
+ public String toString() {
+ return "The new file path is "+filePath;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/seedu/task/commons/events/model/TaskManagerChangedEvent.java b/src/main/java/seedu/task/commons/events/model/TaskManagerChangedEvent.java
new file mode 100644
index 000000000000..4a48fe103d2c
--- /dev/null
+++ b/src/main/java/seedu/task/commons/events/model/TaskManagerChangedEvent.java
@@ -0,0 +1,19 @@
+package seedu.task.commons.events.model;
+
+import seedu.task.commons.events.BaseEvent;
+import seedu.task.model.ReadOnlyTaskManager;
+
+/** Indicates the TaskManager in the model has changed*/
+public class TaskManagerChangedEvent extends BaseEvent {
+
+ public final ReadOnlyTaskManager data;
+
+ public TaskManagerChangedEvent(ReadOnlyTaskManager data){
+ this.data = data;
+ }
+
+ @Override
+ public String toString() {
+ return "number of tasks " + data.getTaskList().size() + ", number of tags " + data.getTagList().size();
+ }
+}
diff --git a/src/main/java/seedu/task/commons/events/storage/ConfigFilePathChangedEvent.java b/src/main/java/seedu/task/commons/events/storage/ConfigFilePathChangedEvent.java
new file mode 100644
index 000000000000..95b1dac9b3b9
--- /dev/null
+++ b/src/main/java/seedu/task/commons/events/storage/ConfigFilePathChangedEvent.java
@@ -0,0 +1,20 @@
+//@@author A0144939R
+package seedu.task.commons.events.storage;
+
+import seedu.task.commons.events.BaseEvent;
+
+/** Indicates that the file path was successfully changed in the config*/
+public class ConfigFilePathChangedEvent extends BaseEvent {
+
+ public final String newFilePath;
+
+ public ConfigFilePathChangedEvent(String newFilePath){
+ this.newFilePath = newFilePath;
+ }
+
+ @Override
+ public String toString() {
+ return "The file path in Config has changed to " + newFilePath;
+ }
+
+}
diff --git a/src/main/java/seedu/address/commons/events/storage/DataSavingExceptionEvent.java b/src/main/java/seedu/task/commons/events/storage/DataSavingExceptionEvent.java
similarity index 78%
rename from src/main/java/seedu/address/commons/events/storage/DataSavingExceptionEvent.java
rename to src/main/java/seedu/task/commons/events/storage/DataSavingExceptionEvent.java
index f0a0640ee523..a41da24989fd 100644
--- a/src/main/java/seedu/address/commons/events/storage/DataSavingExceptionEvent.java
+++ b/src/main/java/seedu/task/commons/events/storage/DataSavingExceptionEvent.java
@@ -1,6 +1,6 @@
-package seedu.address.commons.events.storage;
+package seedu.task.commons.events.storage;
-import seedu.address.commons.events.BaseEvent;
+import seedu.task.commons.events.BaseEvent;
/**
* Indicates an exception during a file saving
diff --git a/src/main/java/seedu/task/commons/events/storage/FilePathChangedEvent.java b/src/main/java/seedu/task/commons/events/storage/FilePathChangedEvent.java
new file mode 100644
index 000000000000..3e7af95f8bd2
--- /dev/null
+++ b/src/main/java/seedu/task/commons/events/storage/FilePathChangedEvent.java
@@ -0,0 +1,24 @@
+//@@author A0144939R
+package seedu.task.commons.events.storage;
+
+import seedu.task.commons.events.BaseEvent;
+
+import seedu.task.model.ReadOnlyTaskManager;
+
+/** Indicates that the user has specified a new file path*/
+public class FilePathChangedEvent extends BaseEvent {
+
+ public final String newFilePath;
+ public final ReadOnlyTaskManager taskManager;
+
+ public FilePathChangedEvent(String newFilePath, ReadOnlyTaskManager taskManager) {
+ this.newFilePath = newFilePath;
+ this.taskManager = taskManager;
+ }
+
+ @Override
+ public String toString() {
+ return "The new file path specified is "+newFilePath;
+ }
+
+}
diff --git a/src/main/java/seedu/address/commons/events/ui/ExitAppRequestEvent.java b/src/main/java/seedu/task/commons/events/ui/ExitAppRequestEvent.java
similarity index 70%
rename from src/main/java/seedu/address/commons/events/ui/ExitAppRequestEvent.java
rename to src/main/java/seedu/task/commons/events/ui/ExitAppRequestEvent.java
index 9af6194543a3..c44de8330c13 100644
--- a/src/main/java/seedu/address/commons/events/ui/ExitAppRequestEvent.java
+++ b/src/main/java/seedu/task/commons/events/ui/ExitAppRequestEvent.java
@@ -1,6 +1,6 @@
-package seedu.address.commons.events.ui;
+package seedu.task.commons.events.ui;
-import seedu.address.commons.events.BaseEvent;
+import seedu.task.commons.events.BaseEvent;
/**
* Indicates a request for App termination
diff --git a/src/main/java/seedu/address/commons/events/ui/IncorrectCommandAttemptedEvent.java b/src/main/java/seedu/task/commons/events/ui/IncorrectCommandAttemptedEvent.java
similarity index 68%
rename from src/main/java/seedu/address/commons/events/ui/IncorrectCommandAttemptedEvent.java
rename to src/main/java/seedu/task/commons/events/ui/IncorrectCommandAttemptedEvent.java
index 991f7ae9fa25..09819a2484b9 100644
--- a/src/main/java/seedu/address/commons/events/ui/IncorrectCommandAttemptedEvent.java
+++ b/src/main/java/seedu/task/commons/events/ui/IncorrectCommandAttemptedEvent.java
@@ -1,7 +1,7 @@
-package seedu.address.commons.events.ui;
+package seedu.task.commons.events.ui;
-import seedu.address.commons.events.BaseEvent;
-import seedu.address.logic.commands.Command;
+import seedu.task.commons.events.BaseEvent;
+import seedu.task.logic.commands.Command;
/**
* Indicates an attempt to execute an incorrect command
diff --git a/src/main/java/seedu/address/commons/events/ui/JumpToListRequestEvent.java b/src/main/java/seedu/task/commons/events/ui/JumpToListRequestEvent.java
similarity index 68%
rename from src/main/java/seedu/address/commons/events/ui/JumpToListRequestEvent.java
rename to src/main/java/seedu/task/commons/events/ui/JumpToListRequestEvent.java
index 0580d27aecf5..f7607d905eef 100644
--- a/src/main/java/seedu/address/commons/events/ui/JumpToListRequestEvent.java
+++ b/src/main/java/seedu/task/commons/events/ui/JumpToListRequestEvent.java
@@ -1,9 +1,9 @@
-package seedu.address.commons.events.ui;
+package seedu.task.commons.events.ui;
-import seedu.address.commons.events.BaseEvent;
+import seedu.task.commons.events.BaseEvent;
/**
- * Indicates a request to jump to the list of persons
+ * Indicates a request to jump to the list of tasks
*/
public class JumpToListRequestEvent extends BaseEvent {
diff --git a/src/main/java/seedu/address/commons/events/ui/ShowHelpRequestEvent.java b/src/main/java/seedu/task/commons/events/ui/ShowHelpRequestEvent.java
similarity index 70%
rename from src/main/java/seedu/address/commons/events/ui/ShowHelpRequestEvent.java
rename to src/main/java/seedu/task/commons/events/ui/ShowHelpRequestEvent.java
index a7e40940b2c7..646fe04ba861 100644
--- a/src/main/java/seedu/address/commons/events/ui/ShowHelpRequestEvent.java
+++ b/src/main/java/seedu/task/commons/events/ui/ShowHelpRequestEvent.java
@@ -1,6 +1,6 @@
-package seedu.address.commons.events.ui;
+package seedu.task.commons.events.ui;
-import seedu.address.commons.events.BaseEvent;
+import seedu.task.commons.events.BaseEvent;
/**
* An event requesting to view the help page.
diff --git a/src/main/java/seedu/task/commons/events/ui/SwitchCommandBoxFunctionEvent.java b/src/main/java/seedu/task/commons/events/ui/SwitchCommandBoxFunctionEvent.java
new file mode 100644
index 000000000000..3d598ec27421
--- /dev/null
+++ b/src/main/java/seedu/task/commons/events/ui/SwitchCommandBoxFunctionEvent.java
@@ -0,0 +1,20 @@
+//@@author A0141052Y
+package seedu.task.commons.events.ui;
+
+import seedu.task.commons.events.BaseEvent;
+
+/**
+ * Indicates that the CommandBox functionality should switch
+ * @author Syed Abdullah
+ *
+ */
+public class SwitchCommandBoxFunctionEvent extends BaseEvent {
+
+ public SwitchCommandBoxFunctionEvent() { }
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName();
+ }
+
+}
diff --git a/src/main/java/seedu/task/commons/events/ui/TaskPanelSelectionChangedEvent.java b/src/main/java/seedu/task/commons/events/ui/TaskPanelSelectionChangedEvent.java
new file mode 100644
index 000000000000..ada3b249ef69
--- /dev/null
+++ b/src/main/java/seedu/task/commons/events/ui/TaskPanelSelectionChangedEvent.java
@@ -0,0 +1,26 @@
+package seedu.task.commons.events.ui;
+
+import seedu.task.commons.events.BaseEvent;
+import seedu.task.model.task.ReadOnlyTask;
+
+/**
+ * Represents a selection change in the Task List Panel
+ */
+public class TaskPanelSelectionChangedEvent extends BaseEvent {
+
+
+ private final ReadOnlyTask newSelection;
+
+ public TaskPanelSelectionChangedEvent(ReadOnlyTask newSelection){
+ this.newSelection = newSelection;
+ }
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName();
+ }
+
+ public ReadOnlyTask getNewSelection() {
+ return newSelection;
+ }
+}
diff --git a/src/main/java/seedu/address/commons/exceptions/DataConversionException.java b/src/main/java/seedu/task/commons/exceptions/DataConversionException.java
similarity index 70%
rename from src/main/java/seedu/address/commons/exceptions/DataConversionException.java
rename to src/main/java/seedu/task/commons/exceptions/DataConversionException.java
index 1f689bd8e3f9..95ded0b58484 100644
--- a/src/main/java/seedu/address/commons/exceptions/DataConversionException.java
+++ b/src/main/java/seedu/task/commons/exceptions/DataConversionException.java
@@ -1,9 +1,12 @@
-package seedu.address.commons.exceptions;
+package seedu.task.commons.exceptions;
/**
* Represents an error during conversion of data from one format to another
*/
public class DataConversionException extends Exception {
+
+ private static final long serialVersionUID = 1L;
+
public DataConversionException(Exception cause) {
super(cause);
}
diff --git a/src/main/java/seedu/address/commons/exceptions/DuplicateDataException.java b/src/main/java/seedu/task/commons/exceptions/DuplicateDataException.java
similarity index 72%
rename from src/main/java/seedu/address/commons/exceptions/DuplicateDataException.java
rename to src/main/java/seedu/task/commons/exceptions/DuplicateDataException.java
index 17aa63d5020c..34dfe37ee0d1 100644
--- a/src/main/java/seedu/address/commons/exceptions/DuplicateDataException.java
+++ b/src/main/java/seedu/task/commons/exceptions/DuplicateDataException.java
@@ -1,9 +1,12 @@
-package seedu.address.commons.exceptions;
+package seedu.task.commons.exceptions;
/**
* Signals an error caused by duplicate data where there should be none.
*/
public abstract class DuplicateDataException extends IllegalValueException {
+
+ private static final long serialVersionUID = 1L;
+
public DuplicateDataException(String message) {
super(message);
}
diff --git a/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java b/src/main/java/seedu/task/commons/exceptions/IllegalValueException.java
similarity index 77%
rename from src/main/java/seedu/address/commons/exceptions/IllegalValueException.java
rename to src/main/java/seedu/task/commons/exceptions/IllegalValueException.java
index a473b43bd86f..0f6e81beee4d 100644
--- a/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java
+++ b/src/main/java/seedu/task/commons/exceptions/IllegalValueException.java
@@ -1,9 +1,12 @@
-package seedu.address.commons.exceptions;
+package seedu.task.commons.exceptions;
/**
* Signals that some given data does not fulfill some constraints.
*/
public class IllegalValueException extends Exception {
+
+ private static final long serialVersionUID = 1L;
+
/**
* @param message should contain relevant information on the failed constraint(s)
*/
diff --git a/src/main/java/seedu/task/commons/logic/CommandKeys.java b/src/main/java/seedu/task/commons/logic/CommandKeys.java
new file mode 100644
index 000000000000..38a4ec3cd753
--- /dev/null
+++ b/src/main/java/seedu/task/commons/logic/CommandKeys.java
@@ -0,0 +1,45 @@
+//@@author A0144939R
+package seedu.task.commons.logic;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.stream.Collectors;
+
+/**
+ * Represents all commands in the TaskManager
+ * @author advaypal
+ *
+ */
+public class CommandKeys {
+
+ public enum Commands {
+ ADD("add"),
+ ALIAS("alias"),
+ CLEAR("clear"),
+ COMPLETE("complete"),
+ CHANGE_TO("change-to"),
+ DELETE("delete"),
+ EXIT("exit"),
+ FIND("find"),
+ FIND_TAG("find-tag"),
+ HELP("help"),
+ LIST("list"),
+ PIN("pin"),
+ SEARCH_BOX("searchbox"),
+ SELECT("select"),
+ UNDO("undo"),
+ UPDATE("update"),
+ UNCOMPLETE("uncomplete"),
+ UNPIN("unpin");
+
+ private String value;
+
+ Commands(String command) {
+ this.value = command;
+ }
+
+ }
+ //create hashmap from command names to ENUM values
+ public static final HashMap commandKeyMap = (HashMap) Arrays.stream(Commands.values()).collect(Collectors.toMap(command -> command.value, command -> command));
+
+}
diff --git a/src/main/java/seedu/address/commons/util/AppUtil.java b/src/main/java/seedu/task/commons/util/AppUtil.java
similarity index 81%
rename from src/main/java/seedu/address/commons/util/AppUtil.java
rename to src/main/java/seedu/task/commons/util/AppUtil.java
index 649cc19aaeda..e1f36b8637ff 100644
--- a/src/main/java/seedu/address/commons/util/AppUtil.java
+++ b/src/main/java/seedu/task/commons/util/AppUtil.java
@@ -1,7 +1,7 @@
-package seedu.address.commons.util;
+package seedu.task.commons.util;
import javafx.scene.image.Image;
-import seedu.address.MainApp;
+import seedu.task.MainApp;
/**
* A container for App specific utility functions
diff --git a/src/main/java/seedu/address/commons/util/CollectionUtil.java b/src/main/java/seedu/task/commons/util/CollectionUtil.java
similarity index 96%
rename from src/main/java/seedu/address/commons/util/CollectionUtil.java
rename to src/main/java/seedu/task/commons/util/CollectionUtil.java
index fde8394f31e5..58cfef736267 100644
--- a/src/main/java/seedu/address/commons/util/CollectionUtil.java
+++ b/src/main/java/seedu/task/commons/util/CollectionUtil.java
@@ -1,4 +1,4 @@
-package seedu.address.commons.util;
+package seedu.task.commons.util;
import java.util.Collection;
import java.util.HashSet;
diff --git a/src/main/java/seedu/address/commons/util/ConfigUtil.java b/src/main/java/seedu/task/commons/util/ConfigUtil.java
similarity index 90%
rename from src/main/java/seedu/address/commons/util/ConfigUtil.java
rename to src/main/java/seedu/task/commons/util/ConfigUtil.java
index af42e03df06c..131a05b02603 100644
--- a/src/main/java/seedu/address/commons/util/ConfigUtil.java
+++ b/src/main/java/seedu/task/commons/util/ConfigUtil.java
@@ -1,14 +1,14 @@
-package seedu.address.commons.util;
-
-import seedu.address.commons.core.Config;
-import seedu.address.commons.core.LogsCenter;
-import seedu.address.commons.exceptions.DataConversionException;
+package seedu.task.commons.util;
import java.io.File;
import java.io.IOException;
import java.util.Optional;
import java.util.logging.Logger;
+import seedu.task.commons.core.Config;
+import seedu.task.commons.core.LogsCenter;
+import seedu.task.commons.exceptions.DataConversionException;
+
/**
* A class for accessing the Config File.
*/
@@ -25,9 +25,7 @@ public class ConfigUtil {
public static Optional readConfig(String configFilePath) throws DataConversionException {
assert configFilePath != null;
-
File configFile = new File(configFilePath);
-
if (!configFile.exists()) {
logger.info("Config file " + configFile + " not found");
return Optional.empty();
diff --git a/src/main/java/seedu/address/commons/util/FileUtil.java b/src/main/java/seedu/task/commons/util/FileUtil.java
similarity index 98%
rename from src/main/java/seedu/address/commons/util/FileUtil.java
rename to src/main/java/seedu/task/commons/util/FileUtil.java
index ca8221250de4..3b6c2df1b15a 100644
--- a/src/main/java/seedu/address/commons/util/FileUtil.java
+++ b/src/main/java/seedu/task/commons/util/FileUtil.java
@@ -1,4 +1,4 @@
-package seedu.address.commons.util;
+package seedu.task.commons.util;
import java.io.File;
import java.io.IOException;
diff --git a/src/main/java/seedu/address/commons/util/FxViewUtil.java b/src/main/java/seedu/task/commons/util/FxViewUtil.java
similarity index 92%
rename from src/main/java/seedu/address/commons/util/FxViewUtil.java
rename to src/main/java/seedu/task/commons/util/FxViewUtil.java
index 900efa6bf5c3..d16224296ee3 100644
--- a/src/main/java/seedu/address/commons/util/FxViewUtil.java
+++ b/src/main/java/seedu/task/commons/util/FxViewUtil.java
@@ -1,4 +1,4 @@
-package seedu.address.commons.util;
+package seedu.task.commons.util;
import javafx.scene.Node;
import javafx.scene.layout.AnchorPane;
diff --git a/src/main/java/seedu/address/commons/util/JsonUtil.java b/src/main/java/seedu/task/commons/util/JsonUtil.java
similarity index 97%
rename from src/main/java/seedu/address/commons/util/JsonUtil.java
rename to src/main/java/seedu/task/commons/util/JsonUtil.java
index 80b67de5b7e8..21301576668d 100644
--- a/src/main/java/seedu/address/commons/util/JsonUtil.java
+++ b/src/main/java/seedu/task/commons/util/JsonUtil.java
@@ -1,4 +1,4 @@
-package seedu.address.commons.util;
+package seedu.task.commons.util;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
@@ -20,6 +20,8 @@
public class JsonUtil {
private static class LevelDeserializer extends FromStringDeserializer {
+ private static final long serialVersionUID = 1L;
+
protected LevelDeserializer(Class> vc) {
super(vc);
}
diff --git a/src/main/java/seedu/address/commons/util/StringUtil.java b/src/main/java/seedu/task/commons/util/StringUtil.java
similarity index 75%
rename from src/main/java/seedu/address/commons/util/StringUtil.java
rename to src/main/java/seedu/task/commons/util/StringUtil.java
index 2e94740456a6..aa2edb2c6798 100644
--- a/src/main/java/seedu/address/commons/util/StringUtil.java
+++ b/src/main/java/seedu/task/commons/util/StringUtil.java
@@ -1,18 +1,15 @@
-package seedu.address.commons.util;
+package seedu.task.commons.util;
import java.io.PrintWriter;
import java.io.StringWriter;
-import java.util.Arrays;
-import java.util.List;
/**
* Helper functions for handling strings.
*/
public class StringUtil {
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;
+ String sourceLower = source.toLowerCase();
+ return sourceLower.contains(query.toLowerCase());
}
/**
diff --git a/src/main/java/seedu/address/commons/util/UrlUtil.java b/src/main/java/seedu/task/commons/util/UrlUtil.java
similarity index 70%
rename from src/main/java/seedu/address/commons/util/UrlUtil.java
rename to src/main/java/seedu/task/commons/util/UrlUtil.java
index 6bbab52b9840..218f5f9940ca 100644
--- a/src/main/java/seedu/address/commons/util/UrlUtil.java
+++ b/src/main/java/seedu/task/commons/util/UrlUtil.java
@@ -1,4 +1,4 @@
-package seedu.address.commons.util;
+package seedu.task.commons.util;
import java.net.URL;
@@ -16,9 +16,9 @@ public static boolean compareBaseUrls(URL url1, URL url2) {
return false;
}
return url1.getHost().toLowerCase().replaceFirst("www.", "")
- .equals(url2.getHost().replaceFirst("www.", "").toLowerCase())
+ .equalsIgnoreCase(url2.getHost().replaceFirst("www.", ""))
&& url1.getPath().replaceAll("/", "").toLowerCase()
- .equals(url2.getPath().replaceAll("/", "").toLowerCase());
+ .equalsIgnoreCase(url2.getPath().replaceAll("/", ""));
}
}
diff --git a/src/main/java/seedu/address/commons/util/XmlUtil.java b/src/main/java/seedu/task/commons/util/XmlUtil.java
similarity index 98%
rename from src/main/java/seedu/address/commons/util/XmlUtil.java
rename to src/main/java/seedu/task/commons/util/XmlUtil.java
index 2087e7628a1d..c3dc9725d0a3 100644
--- a/src/main/java/seedu/address/commons/util/XmlUtil.java
+++ b/src/main/java/seedu/task/commons/util/XmlUtil.java
@@ -1,4 +1,4 @@
-package seedu.address.commons.util;
+package seedu.task.commons.util;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
diff --git a/src/main/java/seedu/task/logic/Logic.java b/src/main/java/seedu/task/logic/Logic.java
new file mode 100644
index 000000000000..9026a8bdd037
--- /dev/null
+++ b/src/main/java/seedu/task/logic/Logic.java
@@ -0,0 +1,29 @@
+package seedu.task.logic;
+
+import javafx.collections.ObservableList;
+import seedu.task.logic.commands.CommandResult;
+import seedu.task.model.task.ReadOnlyTask;
+
+/**
+ * API of the Logic component
+ */
+public interface Logic {
+ /**
+ * Executes the command and returns the result.
+ * @param commandText The command as entered by the user.
+ * @return the result of the command execution.
+ */
+ CommandResult execute(String commandText);
+
+ //@@author A0141052Y
+ /**
+ * Updates the task list filter with the specified keyword
+ * @param keyword to be used to filter the tasks
+ */
+ void updateTaskListFilter(String keyword);
+ //@@author
+
+ /** Returns the filtered list of tasks */
+ ObservableList getFilteredTaskList();
+
+}
diff --git a/src/main/java/seedu/task/logic/LogicManager.java b/src/main/java/seedu/task/logic/LogicManager.java
new file mode 100644
index 000000000000..0e89b97a186c
--- /dev/null
+++ b/src/main/java/seedu/task/logic/LogicManager.java
@@ -0,0 +1,62 @@
+package seedu.task.logic;
+
+import javafx.collections.ObservableList;
+
+import seedu.task.commons.core.ComponentManager;
+import seedu.task.commons.core.LogsCenter;
+import seedu.task.logic.commands.Command;
+import seedu.task.logic.commands.CommandResult;
+import seedu.task.logic.commands.UndoableCommand;
+import seedu.task.logic.parser.ParseSwitcher;
+import seedu.task.model.Model;
+import seedu.task.model.task.ReadOnlyTask;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.logging.Logger;
+
+/**
+ * The main LogicManager of the app.
+ */
+public class LogicManager extends ComponentManager implements Logic {
+ private final Logger logger = LogsCenter.getLogger(LogicManager.class);
+
+ private final Model model;
+ private final ParseSwitcher parser;
+ private UndoableCommand previousCommand;
+
+ public LogicManager(Model model) {
+ this.model = model;
+ this.parser = new ParseSwitcher(model.getAliasMap());
+ this.previousCommand = null;
+ }
+
+ @Override
+ public CommandResult execute(String commandText) {
+ logger.info("----------------[USER COMMAND][" + commandText + "]");
+ Command command = parser.parseCommand(commandText);
+ command.setData(model, previousCommand);
+ CommandResult result = command.execute();
+ setPreviousCommand(result.isSuccessful, command);
+ return result;
+ }
+
+ @Override
+ public ObservableList getFilteredTaskList() {
+ return model.getFilteredTaskList();
+ }
+
+ //@@author A0141052Y
+ @Override
+ public void updateTaskListFilter(String keyword) {
+ model.updateFilteredTaskList(new HashSet(Arrays.asList(keyword)));
+ }
+
+ private void setPreviousCommand(boolean isSuccessful, Command command) {
+ if (isSuccessful && command instanceof UndoableCommand) {
+ this.previousCommand = (UndoableCommand) command;
+ } else {
+ this.previousCommand = null;
+ }
+ }
+}
diff --git a/src/main/java/seedu/task/logic/commands/AddCommand.java b/src/main/java/seedu/task/logic/commands/AddCommand.java
new file mode 100644
index 000000000000..19f5aac7a537
--- /dev/null
+++ b/src/main/java/seedu/task/logic/commands/AddCommand.java
@@ -0,0 +1,113 @@
+//@@author A0144939R
+package seedu.task.logic.commands;
+
+import java.time.temporal.ChronoUnit;
+import java.util.HashSet;
+import java.util.Set;
+
+import seedu.task.commons.core.EventsCenter;
+import seedu.task.commons.events.ui.JumpToListRequestEvent;
+import seedu.task.commons.exceptions.IllegalValueException;
+import seedu.task.model.tag.Tag;
+import seedu.task.model.tag.UniqueTagList;
+import seedu.task.model.task.DateTime;
+import seedu.task.model.task.Name;
+import seedu.task.model.task.Task;
+import seedu.task.model.task.UniqueTaskList;
+
+/**
+ * Adds a task to the task list.
+ */
+public class AddCommand extends UndoableCommand {
+
+ //@@author A0153467Y-unused
+ // Recurring tasks scrapped due to lack of time (and unforseen circumstances)
+ private static final int MAX_NUMBER_OF_RECURRENCE_WEEK=20;
+ //@@author
+
+ public static final String COMMAND_WORD = "add";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a task to the task list. "
+ + "Parameters: NAME starts DATETIME ends DATETIME tag TAG1 tag TAG2 recurs NUMBER_TO_RECUR\n"
+ + "Example: " + COMMAND_WORD
+ + " Finish CS2103 ends tomorrow starts today tag important tag urgent";
+ public static final String MESSAGE_SUCCESS = "New task added: %1$s";
+ public static final String MESSAGE_ROLLBACK_SUCCESS = "Added task removed: %1$s";
+ public static final String MESSAGE_DUPLICATE_TASK = "This task already exists in the task list";
+
+ //@@author A0153467Y-unused
+ // Recurring tasks scrapped due to lack of time (and unforseen circumstances)
+ public static final String MESSAGE_WRONG_NUMBER_OF_RECURRENCE = "Maximum number of recurrence is 20!";
+ public static final String MESSAGE_NEGATIVE_NUMBER_OF_RECURRENCE = "The number recurrence should be positive!";
+ //@@author
+ private final Task toAdd;
+
+
+
+ /**
+ * Convenience constructor using raw values.
+ *
+ * @throws IllegalValueException if any of the raw values are invalid
+ */
+ public AddCommand(String name, String openTime, String closeTime, Set tags)
+ throws IllegalValueException {
+ final Set tagSet = new HashSet<>();
+ for (String tagName : tags) {
+ tagSet.add(new Tag(tagName));
+ }
+
+ this.toAdd = new Task(
+ new Name(name),
+ DateTime.fromUserInput(openTime),
+ DateTime.fromUserInput(closeTime),
+ false,
+ false,
+ new UniqueTagList(tagSet)
+ );
+ }
+
+ @Override
+ public CommandResult execute() {
+ assert model != null;
+
+ try {
+ model.addTask(this.toAdd);
+ } catch (UniqueTaskList.DuplicateTaskException e) {
+ return new CommandResult(false, MESSAGE_DUPLICATE_TASK);
+ }
+
+ return new CommandResult(true, String.format(MESSAGE_SUCCESS, toAdd));
+ }
+
+ //@@author A0141052Y-unused
+ // Recurring tasks scrapped due to lack of time (and unforseen circumstances)
+ /**
+ * Creates a set number of Tasks, with distance between two adjacent tasks being 1 week, and adds it to the
+ * TaskList.
+ * @param timesToRecur number of weeks to recur the Task for
+ * @throws IllegalValueException if there's a duplicate task or if
+ */
+ private void createRecurringTask(int timesToRecur) throws IllegalValueException {
+ for (int i = 0; i < timesToRecur; i++) {
+ DateTime newOpenTime = DateTime.fromDateTimeOffset(toAdd.getOpenTime(), i * 7, ChronoUnit.DAYS);
+ DateTime newCloseTime = DateTime.fromDateTimeOffset(toAdd.getCloseTime(), i * 7, ChronoUnit.DAYS);
+ Task newTask = new Task(
+ toAdd.getName(),
+ newOpenTime,
+ newCloseTime,
+ false,
+ false,
+ toAdd.getTags());
+ model.addTask(newTask);
+ }
+ }
+ //@@author
+
+ @Override
+ public CommandResult rollback() {
+ assert model != null;
+ model.rollback();
+
+ return new CommandResult(true, String.format(MESSAGE_ROLLBACK_SUCCESS, toAdd));
+ }
+}
diff --git a/src/main/java/seedu/task/logic/commands/AliasCommand.java b/src/main/java/seedu/task/logic/commands/AliasCommand.java
new file mode 100644
index 000000000000..e0924bd81d3d
--- /dev/null
+++ b/src/main/java/seedu/task/logic/commands/AliasCommand.java
@@ -0,0 +1,60 @@
+//@@author A0144939R
+package seedu.task.logic.commands;
+
+import seedu.task.commons.logic.CommandKeys;
+import seedu.task.commons.logic.CommandKeys.Commands;
+
+/**
+ * Alias command
+ */
+public class AliasCommand extends Command {
+
+ public static final String COMMAND_WORD = "alias";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Aliases a command to a given string "
+ + "Parameters: ALIASED VALUE"
+ + "Example: " + COMMAND_WORD
+ + "add a";
+
+ public static final String MESSAGE_SUCCESS = "Command successfully aliased";
+ public static final String MESSAGE_FAILURE = "Error, cannot alias command";
+
+ private String commandString;
+ private String alias;
+
+
+ public AliasCommand(String command, String alias) {
+ this.commandString = command.trim();
+ this.alias = alias.trim();
+ }
+
+
+ @Override
+ public CommandResult execute() {
+ if(isValidAliasCommandPair(alias, commandString)) {
+ Commands command = CommandKeys.commandKeyMap.get(commandString);
+ model.setMapping(command, alias);
+ return new CommandResult(true, MESSAGE_SUCCESS);
+ } else {
+ return new CommandResult(false, MESSAGE_FAILURE);
+ }
+ }
+ /**
+ * Checks if a given alias can be mapped to given command
+ * Note: one command can have multiple aliases
+ * @param alias Alias specified by user
+ * @param command Command the alias aliases to
+ * @return
+ */
+ public boolean isValidAliasCommandPair(String alias, String command) {
+ //check that alias is not null and that alias is not a command
+ //checks that command is valid command
+ if (alias != null && command != null) {
+ return CommandKeys.commandKeyMap.containsKey(commandString) && CommandKeys.commandKeyMap.get(commandString) != null;
+ } else {
+ return false;
+ }
+ }
+
+}
+
diff --git a/src/main/java/seedu/task/logic/commands/ChangePathCommand.java b/src/main/java/seedu/task/logic/commands/ChangePathCommand.java
new file mode 100644
index 000000000000..989ec48ebf99
--- /dev/null
+++ b/src/main/java/seedu/task/logic/commands/ChangePathCommand.java
@@ -0,0 +1,52 @@
+//@@author A0144939R
+package seedu.task.logic.commands;
+
+import java.io.File;
+
+import seedu.task.commons.core.EventsCenter;
+import seedu.task.commons.events.storage.FilePathChangedEvent;
+
+/**
+ * Change the file path
+ */
+public class ChangePathCommand extends Command{
+
+ public static final String COMMAND_WORD = "change-to";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Changes save/load location for the TaskManager "
+ + "Parameters: NEW FILE PATH\n"
+ + "Example: " + COMMAND_WORD
+ + "taskmanager.xml";
+
+ public static final String MESSAGE_PATH_CHANGE_SUCCESS = "Success! New File path: %1$s";
+ public static final String MESSAGE_PATH_CHANGE_FAIL = "Error, cannot change path to: %1$s";
+
+ private final String newFilePath;
+
+ public ChangePathCommand(String newFilePath) {
+ this.newFilePath = newFilePath.trim();
+ }
+
+
+
+ @Override
+ public CommandResult execute() {
+ if(isValidFilePath(newFilePath)) {
+ EventsCenter.getInstance().post(new FilePathChangedEvent(newFilePath, model.getTaskManager()));
+ return new CommandResult(true, String.format(MESSAGE_PATH_CHANGE_SUCCESS, newFilePath));
+ } else {
+ return new CommandResult(false, String.format(MESSAGE_PATH_CHANGE_FAIL, newFilePath));
+ }
+ }
+ /**
+ * Checks if the user defined file path is valid.
+ * A file path is defined to be valid if it has a valid parent folder, if it can be written to, and if
+ * it is an xml file.
+ * @param newFilePath The user defined file path
+ * @return boolean variable indicating if file path is valid
+ */
+ private boolean isValidFilePath(String newFilePath) {
+ File file = new File(newFilePath);
+ return (file.getParentFile() != null && file.getParentFile().canWrite() && newFilePath.endsWith(".xml"));
+ }
+}
diff --git a/src/main/java/seedu/task/logic/commands/ClearCommand.java b/src/main/java/seedu/task/logic/commands/ClearCommand.java
new file mode 100644
index 000000000000..1ecb744515e2
--- /dev/null
+++ b/src/main/java/seedu/task/logic/commands/ClearCommand.java
@@ -0,0 +1,22 @@
+package seedu.task.logic.commands;
+
+import seedu.task.model.TaskManager;
+
+/**
+ * Clears the task list.
+ */
+public class ClearCommand extends Command {
+
+ public static final String COMMAND_WORD = "clear";
+ public static final String MESSAGE_SUCCESS = "MESS has been cleared!";
+
+ public ClearCommand() {}
+
+
+ @Override
+ public CommandResult execute() {
+ assert model != null;
+ model.resetData(TaskManager.getEmptyTaskManager());
+ return new CommandResult(true, MESSAGE_SUCCESS);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/Command.java b/src/main/java/seedu/task/logic/commands/Command.java
similarity index 60%
rename from src/main/java/seedu/address/logic/commands/Command.java
rename to src/main/java/seedu/task/logic/commands/Command.java
index 7c0ba2fd0161..221febb0b6ae 100644
--- a/src/main/java/seedu/address/logic/commands/Command.java
+++ b/src/main/java/seedu/task/logic/commands/Command.java
@@ -1,24 +1,25 @@
-package seedu.address.logic.commands;
+package seedu.task.logic.commands;
-import seedu.address.commons.core.EventsCenter;
-import seedu.address.commons.core.Messages;
-import seedu.address.commons.events.ui.IncorrectCommandAttemptedEvent;
-import seedu.address.model.Model;
+import seedu.task.commons.core.EventsCenter;
+import seedu.task.commons.core.Messages;
+import seedu.task.commons.events.ui.IncorrectCommandAttemptedEvent;
+import seedu.task.model.Model;
/**
* Represents a command with hidden internal logic and the ability to be executed.
*/
public abstract class Command {
protected Model model;
+ protected UndoableCommand previousCommand;
/**
- * Constructs a feedback message to summarise an operation that displayed a listing of persons.
+ * Constructs a feedback message to summarise an operation that displayed a listing of tasks.
*
* @param displaySize used to generate summary
- * @return summary message for persons displayed
+ * @return summary message for tasks displayed
*/
- public static String getMessageForPersonListShownSummary(int displaySize) {
- return String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, displaySize);
+ public static String getMessageForTaskListShownSummary(int displaySize) {
+ return String.format(Messages.MESSAGE_TASKS_LISTED_OVERVIEW, displaySize);
}
/**
@@ -33,8 +34,9 @@ public static String getMessageForPersonListShownSummary(int displaySize) {
* Commands making use of any of these should override this method to gain
* access to the dependencies.
*/
- public void setData(Model model) {
+ public void setData(Model model, UndoableCommand previousCommand) {
this.model = model;
+ this.previousCommand = previousCommand;
}
/**
diff --git a/src/main/java/seedu/task/logic/commands/CommandInput.java b/src/main/java/seedu/task/logic/commands/CommandInput.java
new file mode 100644
index 000000000000..798ca1da44bd
--- /dev/null
+++ b/src/main/java/seedu/task/logic/commands/CommandInput.java
@@ -0,0 +1,32 @@
+package seedu.task.logic.commands;
+
+import java.util.HashMap;
+import java.util.Optional;
+
+public class CommandInput {
+ private String command;
+ private String mainInput;
+ private HashMap keywordArguments;
+
+ public CommandInput(String command, String mainInput, HashMap keywordArguments) {
+ this.command = command;
+ this.mainInput = mainInput;
+ this.keywordArguments = new HashMap(keywordArguments);
+ }
+
+ public String getCommand() {
+ return command;
+ }
+
+ public String getMainInput() {
+ return mainInput;
+ }
+
+ public Optional getKeywordArgument(String keyword) {
+ if (keywordArguments.containsKey(keyword)) {
+ return Optional.of(keywordArguments.get(keyword));
+ } else {
+ return Optional.empty();
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/CommandResult.java b/src/main/java/seedu/task/logic/commands/CommandResult.java
similarity index 54%
rename from src/main/java/seedu/address/logic/commands/CommandResult.java
rename to src/main/java/seedu/task/logic/commands/CommandResult.java
index f46f2f31353e..ecb958b12600 100644
--- a/src/main/java/seedu/address/logic/commands/CommandResult.java
+++ b/src/main/java/seedu/task/logic/commands/CommandResult.java
@@ -1,4 +1,4 @@
-package seedu.address.logic.commands;
+package seedu.task.logic.commands;
/**
* Represents the result of a command execution.
@@ -6,10 +6,11 @@
public class CommandResult {
public final String feedbackToUser;
+ public final boolean isSuccessful;
- public CommandResult(String feedbackToUser) {
+ public CommandResult(boolean isSuccessful, String feedbackToUser) {
assert feedbackToUser != null;
this.feedbackToUser = feedbackToUser;
+ this.isSuccessful = isSuccessful;
}
-
}
diff --git a/src/main/java/seedu/task/logic/commands/CompleteCommand.java b/src/main/java/seedu/task/logic/commands/CompleteCommand.java
new file mode 100644
index 000000000000..621ef2a1d6db
--- /dev/null
+++ b/src/main/java/seedu/task/logic/commands/CompleteCommand.java
@@ -0,0 +1,56 @@
+//@@author A0153467Y
+package seedu.task.logic.commands;
+
+import seedu.task.commons.core.Messages;
+import seedu.task.commons.core.UnmodifiableObservableList;
+import seedu.task.commons.exceptions.IllegalValueException;
+import seedu.task.model.task.Task;
+import seedu.task.model.task.ReadOnlyTask;
+
+public class CompleteCommand extends UndoableCommand {
+
+ public static final String COMMAND_WORD = "complete";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Marks the task identified by the index number used in the last task listing as completed.\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 static final String MESSAGE_ROLLBACK_SUCCESS = "Undo action on complete task was executed successfully!";
+
+ public final int targetIndex;
+
+ public CompleteCommand(int targetIndex) {
+ this.targetIndex = targetIndex;
+ }
+
+ @Override
+ public CommandResult execute() {
+ UnmodifiableObservableList lastShownList = model.getFilteredTaskList();
+
+ if (lastShownList.size() < targetIndex) {
+ indicateAttemptToExecuteIncorrectCommand();
+ return new CommandResult(false, Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX);
+ }
+
+ ReadOnlyTask orginialTask = lastShownList.get(targetIndex - 1);
+ try {
+ Task completedTask = new Task(orginialTask);
+ completedTask.setIsCompleted(true);
+ model.completeTask(orginialTask, completedTask);
+ } catch (IllegalValueException e) {
+ assert false : "Impossible";
+ }
+
+ return new CommandResult(true, String.format(MESSAGE_COMPLETE_TASK_SUCCESS, orginialTask));
+ }
+
+ @Override
+ public CommandResult rollback() {
+ assert model != null;
+
+ model.rollback();
+
+ return new CommandResult(true, MESSAGE_ROLLBACK_SUCCESS);
+ }
+}
diff --git a/src/main/java/seedu/task/logic/commands/DeleteCommand.java b/src/main/java/seedu/task/logic/commands/DeleteCommand.java
new file mode 100644
index 000000000000..92604aa3d637
--- /dev/null
+++ b/src/main/java/seedu/task/logic/commands/DeleteCommand.java
@@ -0,0 +1,59 @@
+package seedu.task.logic.commands;
+
+import seedu.task.commons.core.Messages;
+import seedu.task.commons.core.UnmodifiableObservableList;
+import seedu.task.model.task.ReadOnlyTask;
+import seedu.task.model.task.UniqueTaskList.TaskNotFoundException;
+
+/**
+ * Deletes a task identified using it's last displayed index from the task list.
+ */
+public class DeleteCommand extends UndoableCommand {
+
+ public static final String COMMAND_WORD = "delete";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Deletes 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_DELETE_TASK_SUCCESS = "Deleted Task: %1$s";
+ public static final String MESSAGE_ROLLBACK_SUCCESS = "Undo task deletion: %1$s";
+
+ public final int targetIndex;
+ private ReadOnlyTask taskToDelete;
+
+ public DeleteCommand(int targetIndex) {
+ this.targetIndex = targetIndex;
+ }
+
+
+ @Override
+ public CommandResult execute() {
+
+ UnmodifiableObservableList lastShownList = model.getFilteredTaskList();
+
+ if (lastShownList.size() < targetIndex) {
+ indicateAttemptToExecuteIncorrectCommand();
+ return new CommandResult(false, Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX);
+ }
+
+ taskToDelete = lastShownList.get(targetIndex - 1);
+
+ try {
+ model.deleteTask(taskToDelete);
+ } catch (TaskNotFoundException pnfe) {
+ assert false : "The target task cannot be missing";
+ }
+
+ return new CommandResult(true, String.format(MESSAGE_DELETE_TASK_SUCCESS, taskToDelete));
+ }
+
+ @Override
+ public CommandResult rollback() {
+ assert model != null;
+ model.rollback();
+
+ return new CommandResult(true, String.format(MESSAGE_ROLLBACK_SUCCESS, taskToDelete));
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/ExitCommand.java b/src/main/java/seedu/task/logic/commands/ExitCommand.java
similarity index 58%
rename from src/main/java/seedu/address/logic/commands/ExitCommand.java
rename to src/main/java/seedu/task/logic/commands/ExitCommand.java
index d98233ce2a0b..5e4009ff3fed 100644
--- a/src/main/java/seedu/address/logic/commands/ExitCommand.java
+++ b/src/main/java/seedu/task/logic/commands/ExitCommand.java
@@ -1,7 +1,7 @@
-package seedu.address.logic.commands;
+package seedu.task.logic.commands;
-import seedu.address.commons.core.EventsCenter;
-import seedu.address.commons.events.ui.ExitAppRequestEvent;
+import seedu.task.commons.core.EventsCenter;
+import seedu.task.commons.events.ui.ExitAppRequestEvent;
/**
* Terminates the program.
@@ -10,14 +10,14 @@ public class ExitCommand extends Command {
public static final String COMMAND_WORD = "exit";
- public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting Address Book as requested ...";
+ public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting MESS as requested ...";
public ExitCommand() {}
@Override
public CommandResult execute() {
EventsCenter.getInstance().post(new ExitAppRequestEvent());
- return new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT);
+ return new CommandResult(true, MESSAGE_EXIT_ACKNOWLEDGEMENT);
}
}
diff --git a/src/main/java/seedu/address/logic/commands/FindCommand.java b/src/main/java/seedu/task/logic/commands/FindCommand.java
similarity index 59%
rename from src/main/java/seedu/address/logic/commands/FindCommand.java
rename to src/main/java/seedu/task/logic/commands/FindCommand.java
index 1d61bf6cc857..01189110a6f1 100644
--- a/src/main/java/seedu/address/logic/commands/FindCommand.java
+++ b/src/main/java/seedu/task/logic/commands/FindCommand.java
@@ -1,19 +1,19 @@
-package seedu.address.logic.commands;
+package seedu.task.logic.commands;
import java.util.Set;
/**
- * Finds and lists all persons in address book whose name contains any of the argument keywords.
+ * Finds and lists all tasks in task list 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 persons whose names contain any of "
+ 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 + " alice bob charlie";
+ + "Example: " + COMMAND_WORD + " 2103 cs 2105";
private final Set keywords;
@@ -23,8 +23,8 @@ public FindCommand(Set keywords) {
@Override
public CommandResult execute() {
- model.updateFilteredPersonList(keywords);
- return new CommandResult(getMessageForPersonListShownSummary(model.getFilteredPersonList().size()));
+ model.updateFilteredTaskList(keywords);
+ return new CommandResult(true, getMessageForTaskListShownSummary(model.getFilteredTaskList().size()));
}
}
diff --git a/src/main/java/seedu/task/logic/commands/FindTagCommand.java b/src/main/java/seedu/task/logic/commands/FindTagCommand.java
new file mode 100644
index 000000000000..e5b3e7290af1
--- /dev/null
+++ b/src/main/java/seedu/task/logic/commands/FindTagCommand.java
@@ -0,0 +1,37 @@
+//@@author A0141052Y
+package seedu.task.logic.commands;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import seedu.task.commons.exceptions.IllegalValueException;
+import seedu.task.model.tag.Tag;
+
+public class FindTagCommand extends Command {
+
+ public static final String COMMAND_WORD = "find-tags";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all tasks that contains all of the specified tags (case-sensitive).\n"
+ + "Parameters: TAG_NAME [MORE_TAG_NAMES]...\n"
+ + "Example: " + COMMAND_WORD + " homework cs1101s";
+
+ private final Set tags;
+
+ /**
+ * Constructs a find-tags command
+ * @param tagNames a list of tag names to search for
+ * @throws IllegalValueException thrown if the tag names are not valid
+ */
+ public FindTagCommand(String[] tagNames) throws IllegalValueException {
+ this.tags = new HashSet();
+ for (String tagName : tagNames) {
+ this.tags.add(new Tag(tagName));
+ }
+ }
+
+ @Override
+ public CommandResult execute() {
+ model.updateFilteredListByTags(tags);
+ return new CommandResult(true, getMessageForTaskListShownSummary(model.getFilteredTaskList().size()));
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/HelpCommand.java b/src/main/java/seedu/task/logic/commands/HelpCommand.java
similarity index 69%
rename from src/main/java/seedu/address/logic/commands/HelpCommand.java
rename to src/main/java/seedu/task/logic/commands/HelpCommand.java
index 65af96940242..10f3b246b3ff 100644
--- a/src/main/java/seedu/address/logic/commands/HelpCommand.java
+++ b/src/main/java/seedu/task/logic/commands/HelpCommand.java
@@ -1,8 +1,8 @@
-package seedu.address.logic.commands;
+package seedu.task.logic.commands;
-import seedu.address.commons.core.EventsCenter;
-import seedu.address.commons.events.ui.ShowHelpRequestEvent;
+import seedu.task.commons.core.EventsCenter;
+import seedu.task.commons.events.ui.ShowHelpRequestEvent;
/**
* Format full help instructions for every command for display.
@@ -16,11 +16,9 @@ public class HelpCommand extends Command {
public static final String SHOWING_HELP_MESSAGE = "Opened help window.";
- public HelpCommand() {}
-
@Override
public CommandResult execute() {
EventsCenter.getInstance().post(new ShowHelpRequestEvent());
- return new CommandResult(SHOWING_HELP_MESSAGE);
+ return new CommandResult(true, SHOWING_HELP_MESSAGE);
}
}
diff --git a/src/main/java/seedu/address/logic/commands/IncorrectCommand.java b/src/main/java/seedu/task/logic/commands/IncorrectCommand.java
similarity index 81%
rename from src/main/java/seedu/address/logic/commands/IncorrectCommand.java
rename to src/main/java/seedu/task/logic/commands/IncorrectCommand.java
index 491d9cb9da35..e596f0a6f6dc 100644
--- a/src/main/java/seedu/address/logic/commands/IncorrectCommand.java
+++ b/src/main/java/seedu/task/logic/commands/IncorrectCommand.java
@@ -1,4 +1,4 @@
-package seedu.address.logic.commands;
+package seedu.task.logic.commands;
/**
@@ -15,7 +15,7 @@ public IncorrectCommand(String feedbackToUser){
@Override
public CommandResult execute() {
indicateAttemptToExecuteIncorrectCommand();
- return new CommandResult(feedbackToUser);
+ return new CommandResult(true, feedbackToUser);
}
}
diff --git a/src/main/java/seedu/task/logic/commands/ListCommand.java b/src/main/java/seedu/task/logic/commands/ListCommand.java
new file mode 100644
index 000000000000..6c1655627a87
--- /dev/null
+++ b/src/main/java/seedu/task/logic/commands/ListCommand.java
@@ -0,0 +1,31 @@
+//@@author A0141052Y
+package seedu.task.logic.commands;
+
+import seedu.task.model.Model;
+
+/**
+ * Lists all tasks in the task list to the user.
+ */
+public class ListCommand extends Command {
+
+ public static final String COMMAND_WORD = "list";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Lists a subset of the tasks in the list "
+ + "Parameters: [all|pinned|pending|completed|overdue]...\n"
+ + "Example: " + COMMAND_WORD
+ + " ALL";
+
+ public static final String MESSAGE_SUCCESS = "Filtered tasks listed.";
+
+ private final Model.FilterType listFilter;
+
+ public ListCommand(Model.FilterType filter) {
+ this.listFilter = filter;
+ }
+
+ @Override
+ public CommandResult execute() {
+ model.updateFilteredList(listFilter);
+ return new CommandResult(true, MESSAGE_SUCCESS);
+ }
+}
diff --git a/src/main/java/seedu/task/logic/commands/PinCommand.java b/src/main/java/seedu/task/logic/commands/PinCommand.java
new file mode 100644
index 000000000000..50a1cd23ee9c
--- /dev/null
+++ b/src/main/java/seedu/task/logic/commands/PinCommand.java
@@ -0,0 +1,57 @@
+//@@author A0153467Y
+package seedu.task.logic.commands;
+
+import seedu.task.commons.core.Messages;
+import seedu.task.commons.core.UnmodifiableObservableList;
+import seedu.task.commons.exceptions.IllegalValueException;
+import seedu.task.model.task.ReadOnlyTask;
+import seedu.task.model.task.Task;
+
+public class PinCommand extends UndoableCommand {
+ public static final String COMMAND_WORD = "pin";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Pin the task identified by the index number used in the last task listing as important.\n"
+ + "Parameters: INDEX (must be a positive integer)\n" + "Example: " + COMMAND_WORD + " 1 ";
+
+ public static final String MESSAGE_PIN_TASK_SUCCESS = "Pinned Task: %1$s";
+ public static final String MESSAGE_ROLLBACK_SUCCESS = "Undo action on pin task was executed successfully!";
+
+ public final int targetIndex;
+
+ public PinCommand(int targetIndex) {
+ this.targetIndex = targetIndex;
+ }
+
+ @Override
+ public CommandResult execute() {
+
+ UnmodifiableObservableList lastShownList = model.getFilteredTaskList();
+
+ if (lastShownList.size() < targetIndex) {
+ indicateAttemptToExecuteIncorrectCommand();
+ return new CommandResult(false, Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX);
+ }
+
+ ReadOnlyTask orginialTask = lastShownList.get(targetIndex - 1);
+ try {
+ Task taskToPin = new Task(orginialTask);
+ taskToPin.setIsImportant(true);
+ model.pinTask(orginialTask, taskToPin);
+ } catch (IllegalValueException e) {
+ assert false : "Not possible for task on list to have illegal value";
+ }
+
+ return new CommandResult(true, String.format(MESSAGE_PIN_TASK_SUCCESS, orginialTask));
+ }
+
+ @Override
+ public CommandResult rollback() {
+ assert model != null;
+
+ model.rollback();
+
+ return new CommandResult(true, MESSAGE_ROLLBACK_SUCCESS);
+ }
+
+}
diff --git a/src/main/java/seedu/task/logic/commands/SearchCommand.java b/src/main/java/seedu/task/logic/commands/SearchCommand.java
new file mode 100644
index 000000000000..13eae2a32dac
--- /dev/null
+++ b/src/main/java/seedu/task/logic/commands/SearchCommand.java
@@ -0,0 +1,16 @@
+//@@author A0141052Y
+package seedu.task.logic.commands;
+
+import seedu.task.commons.core.EventsCenter;
+import seedu.task.commons.events.ui.SwitchCommandBoxFunctionEvent;
+
+public class SearchCommand extends Command {
+
+ public static final String MESSAGE_SEARCH_SUCCESS = "Live Search activated!";
+
+ @Override
+ public CommandResult execute() {
+ EventsCenter.getInstance().post(new SwitchCommandBoxFunctionEvent());
+ return new CommandResult(true, MESSAGE_SEARCH_SUCCESS);
+ }
+}
diff --git a/src/main/java/seedu/task/logic/commands/SelectCommand.java b/src/main/java/seedu/task/logic/commands/SelectCommand.java
new file mode 100644
index 000000000000..5fbda6b2fb54
--- /dev/null
+++ b/src/main/java/seedu/task/logic/commands/SelectCommand.java
@@ -0,0 +1,44 @@
+package seedu.task.logic.commands;
+
+import seedu.task.commons.core.EventsCenter;
+import seedu.task.commons.core.Messages;
+import seedu.task.commons.core.UnmodifiableObservableList;
+import seedu.task.commons.events.ui.JumpToListRequestEvent;
+import seedu.task.model.task.ReadOnlyTask;
+
+/**
+ * Selects a task identified using it's last displayed index from the task list.
+ */
+public class SelectCommand extends Command {
+
+ public final int targetIndex;
+
+ public static final String COMMAND_WORD = "select";
+
+ 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;
+ }
+
+ @Override
+ public CommandResult execute() {
+
+ UnmodifiableObservableList lastShownList = model.getFilteredTaskList();
+
+ if (lastShownList.size() < targetIndex) {
+ indicateAttemptToExecuteIncorrectCommand();
+ return new CommandResult(false, Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX);
+ }
+
+ EventsCenter.getInstance().post(new JumpToListRequestEvent(targetIndex - 1));
+ return new CommandResult(true, String.format(MESSAGE_SELECT_TASK_SUCCESS, targetIndex));
+
+ }
+
+}
diff --git a/src/main/java/seedu/task/logic/commands/UncompleteCommand.java b/src/main/java/seedu/task/logic/commands/UncompleteCommand.java
new file mode 100644
index 000000000000..02d878674993
--- /dev/null
+++ b/src/main/java/seedu/task/logic/commands/UncompleteCommand.java
@@ -0,0 +1,61 @@
+//@@author A0153467Y
+package seedu.task.logic.commands;
+
+import seedu.task.commons.core.Messages;
+import seedu.task.commons.core.UnmodifiableObservableList;
+import seedu.task.commons.exceptions.IllegalValueException;
+import seedu.task.model.task.ReadOnlyTask;
+import seedu.task.model.task.Task;
+
+public class UncompleteCommand extends UndoableCommand {
+
+ public static final String COMMAND_WORD = "uncomplete";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Unmark the completed task identified by the index number used in the last task listing as not completed.\n"
+ + "Parameters: INDEX (must be a positive integer)\n" + "Example: " + COMMAND_WORD + " 1";
+
+ public static final String MESSAGE_UNCOMPLETE_TASK_SUCCESS = "Uncompleted Task: %1$s";
+ public static final String MESSAGE_ROLLBACK_SUCCESS = "Undo action on un-complete task was executed successfully!";
+
+ public final int targetIndex;
+
+ public UncompleteCommand(int targetIndex) {
+ this.targetIndex = targetIndex;
+ }
+
+ @Override
+ public CommandResult execute() {
+ UnmodifiableObservableList lastShownList = model.getFilteredTaskList();
+
+ if (lastShownList.size() < targetIndex) {
+ indicateAttemptToExecuteIncorrectCommand();
+ return new CommandResult(false, Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX);
+ }
+
+ ReadOnlyTask orginialTask = lastShownList.get(targetIndex - 1);
+ try {
+ Task uncompletedTask = new Task(orginialTask);
+ if(uncompletedTask.getComplete()) {
+ uncompletedTask.setIsCompleted(false);
+ }else {
+ return new CommandResult(false, Messages.MESSAGE_INVALID_UNCOMPLETE_TASK);
+ }
+
+ model.uncompleteTask(orginialTask, uncompletedTask);
+ } catch (IllegalValueException e) {
+ assert false : "Impossible";
+ }
+
+ return new CommandResult(true, String.format(MESSAGE_UNCOMPLETE_TASK_SUCCESS, orginialTask));
+ }
+
+ @Override
+ public CommandResult rollback() {
+ assert model != null;
+
+ model.rollback();
+
+ return new CommandResult(true, MESSAGE_ROLLBACK_SUCCESS);
+ }
+}
diff --git a/src/main/java/seedu/task/logic/commands/UndoCommand.java b/src/main/java/seedu/task/logic/commands/UndoCommand.java
new file mode 100644
index 000000000000..6cc0a08d7d13
--- /dev/null
+++ b/src/main/java/seedu/task/logic/commands/UndoCommand.java
@@ -0,0 +1,19 @@
+package seedu.task.logic.commands;
+
+public class UndoCommand extends Command {
+ public static final String COMMAND_WORD = "undo";
+ public static final String MESSAGE_SUCCESS = "Previous action has been undone!";
+ public static final String MESSAGE_NO_ACTION_TO_UNDO = "No action was executed that can be undone!";
+
+ @Override
+ public CommandResult execute() {
+ assert model != null;
+ if (previousCommand == null) {
+ return new CommandResult(false, MESSAGE_NO_ACTION_TO_UNDO);
+ }
+
+ previousCommand.rollback();
+
+ return new CommandResult(true, MESSAGE_SUCCESS);
+ }
+}
diff --git a/src/main/java/seedu/task/logic/commands/UndoableCommand.java b/src/main/java/seedu/task/logic/commands/UndoableCommand.java
new file mode 100644
index 000000000000..56ad546f93c0
--- /dev/null
+++ b/src/main/java/seedu/task/logic/commands/UndoableCommand.java
@@ -0,0 +1,15 @@
+package seedu.task.logic.commands;
+
+/**
+ * Interface for a command that can be undone
+ * @author Syed Abdullah
+ *
+ */
+public abstract class UndoableCommand extends Command {
+ /**
+ * Reverse changes made by command and returns the result message.
+ *
+ * @return feedback message of the operation result for display
+ */
+ public abstract CommandResult rollback();
+}
diff --git a/src/main/java/seedu/task/logic/commands/UnpinCommand.java b/src/main/java/seedu/task/logic/commands/UnpinCommand.java
new file mode 100644
index 000000000000..42fd569adf40
--- /dev/null
+++ b/src/main/java/seedu/task/logic/commands/UnpinCommand.java
@@ -0,0 +1,59 @@
+//@@author A0153467Y
+package seedu.task.logic.commands;
+
+import seedu.task.commons.core.Messages;
+import seedu.task.commons.core.UnmodifiableObservableList;
+import seedu.task.commons.exceptions.IllegalValueException;
+import seedu.task.model.task.ReadOnlyTask;
+import seedu.task.model.task.Task;
+
+public class UnpinCommand extends UndoableCommand {
+ public static final String COMMAND_WORD = "unpin";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Unpin the pinned 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_UNPIN_TASK_SUCCESS = "Unpinned Task: %1$s";
+ public static final String MESSAGE_ROLLBACK_SUCCESS = "Undo action on unpin task was executed successfully!";
+
+ public final int targetIndex;
+
+ public UnpinCommand(int targetIndex) {
+ this.targetIndex = targetIndex;
+ }
+
+ @Override
+ public CommandResult execute() {
+
+ UnmodifiableObservableList lastShownList = model.getFilteredTaskList();
+
+ if (lastShownList.size() < targetIndex) {
+ indicateAttemptToExecuteIncorrectCommand();
+ return new CommandResult(false, Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX);
+ }
+
+ ReadOnlyTask orginialTask = lastShownList.get(targetIndex - 1);
+ try {
+ Task taskToUnpin = new Task(orginialTask);
+ if(taskToUnpin.getImportance()) {
+ taskToUnpin.setIsImportant(false);
+ model.unpinTask(orginialTask, taskToUnpin);
+ } else {
+ return new CommandResult(false, Messages.MESSAGE_INVALID_UNPIN_TASK);
+ }
+ } catch (IllegalValueException e) {
+ assert false : "Not possible for task on list to have illegal value";
+ }
+ return new CommandResult(true, String.format(MESSAGE_UNPIN_TASK_SUCCESS, orginialTask));
+ }
+
+ @Override
+ public CommandResult rollback() {
+ assert model != null;
+
+ model.rollback();
+
+ return new CommandResult(true, MESSAGE_ROLLBACK_SUCCESS);
+ }
+}
diff --git a/src/main/java/seedu/task/logic/commands/UpdateCommand.java b/src/main/java/seedu/task/logic/commands/UpdateCommand.java
new file mode 100644
index 000000000000..c1de3e53a54c
--- /dev/null
+++ b/src/main/java/seedu/task/logic/commands/UpdateCommand.java
@@ -0,0 +1,115 @@
+package seedu.task.logic.commands;
+
+import java.util.HashSet;
+
+import java.util.Set;
+
+import seedu.task.commons.core.Messages;
+import seedu.task.commons.core.UnmodifiableObservableList;
+import seedu.task.commons.exceptions.IllegalValueException;
+import seedu.task.model.tag.Tag;
+import seedu.task.model.tag.UniqueTagList;
+import seedu.task.model.tag.UniqueTagList.TagNotFoundException;
+import seedu.task.model.task.*;
+import seedu.task.model.task.UniqueTaskList.DuplicateTaskException;
+
+/**
+ * Updates a task in the task list.
+ */
+public class UpdateCommand extends UndoableCommand {
+
+ public static final String COMMAND_WORD = "update";
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Update a task in the task list.\n "
+ + "Parameters: INDEX (must be a positive integer) [NAME tag TAGADD remove-tag TAGREMOVE]...\n"
+ + "Example: " + COMMAND_WORD
+ + " 1 cs2103 tag quiz ends tomorrow";
+
+ public static final String MESSAGE_UPDATE_TASK_SUCCESS = "Updated Task: %1$s";
+ public static final String MESSAGE_ROLLBACK_SUCCESS = "Rollback changes to updated task!";
+ public static final String MESSAGE_DUPLICATE_TASK = "This task already exists in the task list";
+
+ public final int targetIndex;
+
+ private final Name newTaskName;
+ private final DateTime newOpenTime;
+ private final DateTime newCloseTime;
+ private final Set removedTags;
+ private final UniqueTagList newTaskTags;
+
+ public UpdateCommand(int targetIndex, String name, String openTime, String closeTime, Set tagsToAdd, Set tagsToRemove) throws IllegalValueException {
+ this.targetIndex = targetIndex;
+ this.newTaskName = (name == null || name.isEmpty()) ? null : new Name(name);
+
+ final Set tagSet = new HashSet<>();
+ for (String tagName : tagsToAdd) {
+ tagSet.add(new Tag(tagName));
+ }
+ this.newOpenTime = DateTime.fromUserInput(openTime);
+ this.newCloseTime = DateTime.fromUserInput(closeTime);
+
+ this.newTaskTags = new UniqueTagList(tagSet);
+ this.removedTags = tagsToRemove;
+ }
+
+ private void mergeTaskTags(ReadOnlyTask taskToUpdate) {
+ newTaskTags.mergeFrom(taskToUpdate.getTags());
+
+ for (String tagName : removedTags) {
+ try {
+ newTaskTags.remove(new Tag(tagName));
+ } catch (TagNotFoundException e) {
+ // do nothing, as update is more lenient
+ } catch (IllegalValueException e) {
+ assert false : "Tag cannot be of other type!";
+ }
+ }
+ }
+
+ @Override
+ public CommandResult execute() {
+ UnmodifiableObservableList lastShownList = model.getFilteredTaskList();
+
+ if (lastShownList.size() < targetIndex) {
+ indicateAttemptToExecuteIncorrectCommand();
+ return new CommandResult(false, Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX);
+ }
+
+ ReadOnlyTask taskToUpdate = lastShownList.get(targetIndex - 1);
+
+ Name updatedTaskName = (newTaskName == null) ? taskToUpdate.getName() : newTaskName;
+
+ DateTime updatedOpenTime = (newOpenTime.isEmpty()) ? taskToUpdate.getOpenTime() : newOpenTime;
+ DateTime updatedCloseTime = (newCloseTime.isEmpty()) ? taskToUpdate.getCloseTime() : newCloseTime;
+ mergeTaskTags(taskToUpdate);
+
+ Task newTask;
+ try {
+ newTask = new Task(
+ updatedTaskName,
+ updatedOpenTime,
+ updatedCloseTime,
+ taskToUpdate.getImportance(),
+ taskToUpdate.getComplete(),
+ newTaskTags
+ );
+ } catch (IllegalValueException e1) {
+ return new CommandResult(false, e1.getMessage());
+ }
+
+ assert model != null;
+ try {
+ model.updateTask(taskToUpdate, newTask);
+ } catch (DuplicateTaskException e) {
+ return new CommandResult(false, MESSAGE_DUPLICATE_TASK);
+ }
+ return new CommandResult(true, String.format(MESSAGE_UPDATE_TASK_SUCCESS, newTask));
+ }
+
+ @Override
+ public CommandResult rollback() {
+ assert model != null;
+ model.rollback();
+
+ return new CommandResult(true, String.format(MESSAGE_ROLLBACK_SUCCESS));
+ }
+}
diff --git a/src/main/java/seedu/task/logic/parser/AddParser.java b/src/main/java/seedu/task/logic/parser/AddParser.java
new file mode 100644
index 000000000000..f660211338aa
--- /dev/null
+++ b/src/main/java/seedu/task/logic/parser/AddParser.java
@@ -0,0 +1,91 @@
+//@@author A0141052Y
+package seedu.task.logic.parser;
+
+import static seedu.task.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.StringJoiner;
+
+
+import seedu.task.commons.exceptions.IllegalValueException;
+import seedu.task.logic.commands.AddCommand;
+import seedu.task.logic.commands.Command;
+import seedu.task.logic.commands.IncorrectCommand;
+
+public class AddParser extends BaseParser {
+
+ private final static String FLAG_NAME = "name";
+ private final static String FLAG_START_TIME = "starts";
+ private final static String FLAG_CLOSE_TIME = "ends";
+ private final static String FLAG_TAGS = "tag";
+
+ private final static String[] KEYWORD_ARGS_REQUIRED = new String[]{FLAG_NAME};
+ private final static String[] KEYWORD_ARGS_OPTIONAL = new String[]{FLAG_START_TIME,
+ FLAG_CLOSE_TIME,
+ FLAG_TAGS
+ };
+
+ @Override
+ public Command parse(String command, String arguments) {
+ this.extractArguments(arguments);
+ if (!checkForRequiredArguments(KEYWORD_ARGS_REQUIRED, KEYWORD_ARGS_OPTIONAL, true)) {
+ return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE));
+ }
+
+ try {
+ return new AddCommand(getSingleKeywordArgValue(FLAG_NAME),
+ getSingleKeywordArgValue(FLAG_START_TIME),
+ getSingleKeywordArgValue(FLAG_CLOSE_TIME),
+ getTags());
+ } catch (IllegalValueException ive) {
+ return new IncorrectCommand(ive.getMessage());
+ }
+ }
+
+ private Set getTags() {
+ if (argumentsTable.containsKey(FLAG_TAGS)) {
+ return new HashSet<>(argumentsTable.get(FLAG_TAGS));
+ } else {
+ return new HashSet<>();
+ }
+ }
+
+ //@@author A0144939R
+ /**
+ * Extracts the arguments and puts them in a HashMap
+ * This method has been overriden to support the different nature of the add command's arguments
+ */
+ @Override
+ protected void extractArguments(String args) {
+ argumentsTable.clear();
+ String[] segments = args.trim().split(" ");
+ String currentKey = "name";
+ StringJoiner joiner = new StringJoiner(" ");
+ for (String segment : segments) {
+ if (isDelimiter(segment)) {
+ addToArgumentsTable(currentKey, joiner.toString().trim());
+ currentKey = segment;
+ joiner = new StringJoiner(" ");
+ } else {
+ joiner.add(segment);
+ }
+ }
+ addToArgumentsTable(currentKey, joiner.toString());
+ }
+
+ /**
+ * Checks if a string is a valid delimiter for the add command
+ * @param argument a string from the user input
+ * @return true if the String is a valid delimiter, and false otherwise
+ */
+ private boolean isDelimiter(String argument) {
+ for(int i = 0; i < KEYWORD_ARGS_OPTIONAL.length; i++) {
+ if(argument.equals(KEYWORD_ARGS_OPTIONAL[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/src/main/java/seedu/task/logic/parser/AliasParser.java b/src/main/java/seedu/task/logic/parser/AliasParser.java
new file mode 100644
index 000000000000..4fa73c81fa6f
--- /dev/null
+++ b/src/main/java/seedu/task/logic/parser/AliasParser.java
@@ -0,0 +1,40 @@
+//@@author A0144939R
+package seedu.task.logic.parser;
+
+import static seedu.task.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import seedu.task.logic.commands.AliasCommand;
+import seedu.task.logic.commands.Command;
+import seedu.task.logic.commands.IncorrectCommand;
+
+public class AliasParser extends BaseParser {
+
+ private final String FLAG_ALIAS_COMMAND = "aliasCommand";
+ private final String FLAG_ALIAS_VALUE ="aliasValue";
+ private final int FLAG_ALIAS_COMMAND_LENGTH = 2;
+
+ @Override
+ public Command parse(String command, String arguments) {
+ this.extractArguments(arguments);
+ String aliasCommand = getSingleKeywordArgValue(FLAG_ALIAS_COMMAND);
+ String aliasValue = getSingleKeywordArgValue(FLAG_ALIAS_VALUE);
+ if( aliasCommand != null && aliasValue != null) {
+ return new AliasCommand(aliasCommand, aliasValue);
+ } else {
+ return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AliasCommand.MESSAGE_USAGE));
+ }
+
+ }
+
+ @Override
+ protected void extractArguments(String args) {
+ argumentsTable.clear();
+ String[] segments = args.trim().split(" ");
+ if(segments.length == FLAG_ALIAS_COMMAND_LENGTH) {
+ addToArgumentsTable(FLAG_ALIAS_COMMAND, segments[0]);
+ addToArgumentsTable(FLAG_ALIAS_VALUE, segments[1]);
+ }
+ //do nothing if false. The parse method will detect if there's an error.
+ }
+
+}
diff --git a/src/main/java/seedu/task/logic/parser/BaseParser.java b/src/main/java/seedu/task/logic/parser/BaseParser.java
new file mode 100644
index 000000000000..7d8ae3634a89
--- /dev/null
+++ b/src/main/java/seedu/task/logic/parser/BaseParser.java
@@ -0,0 +1,121 @@
+//@@author A0141052Y
+package seedu.task.logic.parser;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Optional;
+import java.util.StringJoiner;
+
+import seedu.task.commons.util.StringUtil;
+import seedu.task.logic.commands.Command;
+
+public abstract class BaseParser {
+
+ protected final HashMap> argumentsTable = new HashMap<>();
+
+ /**
+ * Extracts out arguments from the user's input into a HashMap.
+ * The value mapped to the empty string ("") is the non-keyword argument.
+ *
+ * @param args full (or partial) user input arguments
+ */
+ protected void extractArguments(String args) {
+ argumentsTable.clear();
+ String[] segments = args.trim().split(" ");
+ String currentKey = "";
+ StringJoiner joiner = new StringJoiner(" ");
+
+ for (String segment : segments) {
+ joiner.add(segment);
+
+ }
+
+ addToArgumentsTable(currentKey, joiner.toString());
+ }
+
+ /**
+ * Assigns a value to a keyword argument. Does not replace any existing
+ * values associated with the keyword.
+ * @param keyword the keyword of the argument
+ * @param value the value the argument is set to
+ */
+ protected void addToArgumentsTable(String keyword, String value) {
+ ArrayList arrayItems;
+ if (argumentsTable.containsKey(keyword)) {
+ arrayItems = argumentsTable.get(keyword);
+ } else {
+ arrayItems = new ArrayList();
+ }
+
+ arrayItems.add(value);
+ argumentsTable.put(keyword, arrayItems);
+ }
+
+ /***
+ * Checks if the required keyword arguments were supplied by the user
+ * @param requiredArgs list of keyword arguments
+ * @param optionalArgs list of arguments that may appear
+ * @param isStrictSet does not allow for other keyword arguments
+ * @return true if required arguments were supplied, else false
+ */
+ protected boolean checkForRequiredArguments(String[] requiredArgs, String[] optionalArgs, boolean isStrictSet) {
+ for (String arg : requiredArgs) {
+ if (!argumentsTable.containsKey(arg)) {
+ return false;
+ } else {
+ if (argumentsTable.get(arg).get(0).isEmpty()) {
+ return false;
+ }
+ }
+ }
+
+ int numOptional = 0;
+ for (String arg : optionalArgs) {
+ if (argumentsTable.containsKey(arg)) {
+ numOptional++;
+ }
+ }
+
+ if (isStrictSet) {
+ return argumentsTable.size() == numOptional + requiredArgs.length;
+ } else {
+ return argumentsTable.size() >= numOptional + requiredArgs.length;
+ }
+ }
+
+ /**
+ * Retrieves the value for the keyword argument
+ * @param keyword the keyword of the argument
+ * @return the current value of the keyword argument
+ */
+ protected String getSingleKeywordArgValue(String keyword) {
+ if (argumentsTable.containsKey(keyword)) {
+ return argumentsTable.get(keyword).get(0);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns a positive integer, if the user supplied unnamed keyword argument is a positive integer.
+ * Returns an {@code Optional.empty()} otherwise.
+ */
+ protected Optional parseIndex() {
+ String index = getSingleKeywordArgValue("");
+ return parseIndex(index);
+ }
+
+ protected Optional parseIndex(String index) {
+ if (!StringUtil.isUnsignedInteger(index)) {
+ return Optional.empty();
+ }
+ return Optional.of(Integer.parseInt(index));
+ }
+
+ /**
+ * Parses the user's input and determines the appropriate command
+ * @param userInput full user input string
+ * @return the command based on the user input
+ */
+ public abstract Command parse(String command, String arguments);
+}
diff --git a/src/main/java/seedu/task/logic/parser/ChangePathParser.java b/src/main/java/seedu/task/logic/parser/ChangePathParser.java
new file mode 100644
index 000000000000..8b2b434563d7
--- /dev/null
+++ b/src/main/java/seedu/task/logic/parser/ChangePathParser.java
@@ -0,0 +1,11 @@
+package seedu.task.logic.parser;
+
+import seedu.task.logic.commands.ChangePathCommand;
+import seedu.task.logic.commands.Command;
+
+public class ChangePathParser extends BaseParser {
+ @Override
+ public Command parse(String command, String arguments) {
+ return new ChangePathCommand(arguments);
+ }
+}
diff --git a/src/main/java/seedu/task/logic/parser/ClearParser.java b/src/main/java/seedu/task/logic/parser/ClearParser.java
new file mode 100644
index 000000000000..f26df5af5ac5
--- /dev/null
+++ b/src/main/java/seedu/task/logic/parser/ClearParser.java
@@ -0,0 +1,12 @@
+//@@author A0141052Y
+package seedu.task.logic.parser;
+
+import seedu.task.logic.commands.ClearCommand;
+import seedu.task.logic.commands.Command;
+
+public class ClearParser extends BaseParser {
+ @Override
+ public Command parse(String command, String arguments) {
+ return new ClearCommand();
+ }
+}
diff --git a/src/main/java/seedu/task/logic/parser/CompleteParser.java b/src/main/java/seedu/task/logic/parser/CompleteParser.java
new file mode 100644
index 000000000000..79abf7de39eb
--- /dev/null
+++ b/src/main/java/seedu/task/logic/parser/CompleteParser.java
@@ -0,0 +1,25 @@
+//@@author A0141052Y
+package seedu.task.logic.parser;
+
+import seedu.task.logic.commands.CompleteCommand;
+import seedu.task.logic.commands.IncorrectCommand;
+
+import static seedu.task.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import java.util.Optional;
+
+import seedu.task.logic.commands.Command;
+
+public class CompleteParser extends BaseParser {
+ @Override
+ public Command parse(String command, String arguments) {
+ this.extractArguments(arguments);
+ Optional idx = parseIndex();
+
+ if (!idx.isPresent()) {
+ return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, CompleteCommand.MESSAGE_USAGE));
+ }
+
+ return new CompleteCommand(idx.get());
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/seedu/task/logic/parser/DeleteParser.java b/src/main/java/seedu/task/logic/parser/DeleteParser.java
new file mode 100644
index 000000000000..6b4a5418faf0
--- /dev/null
+++ b/src/main/java/seedu/task/logic/parser/DeleteParser.java
@@ -0,0 +1,24 @@
+//@@author A0141052Y
+package seedu.task.logic.parser;
+
+import static seedu.task.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import java.util.Optional;
+
+import seedu.task.logic.commands.Command;
+import seedu.task.logic.commands.DeleteCommand;
+import seedu.task.logic.commands.IncorrectCommand;
+
+public class DeleteParser extends BaseParser {
+ @Override
+ public Command parse(String command, String arguments) {
+ this.extractArguments(arguments);
+
+ Optional index = parseIndex();
+ if (!index.isPresent()) {
+ return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE));
+ }
+
+ return new DeleteCommand(index.get());
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/seedu/task/logic/parser/ExitParser.java b/src/main/java/seedu/task/logic/parser/ExitParser.java
new file mode 100644
index 000000000000..ab96fbf1eed8
--- /dev/null
+++ b/src/main/java/seedu/task/logic/parser/ExitParser.java
@@ -0,0 +1,12 @@
+//@@author A0141052Y
+package seedu.task.logic.parser;
+
+import seedu.task.logic.commands.Command;
+import seedu.task.logic.commands.ExitCommand;
+
+public class ExitParser extends BaseParser {
+ @Override
+ public Command parse(String command, String arguments) {
+ return new ExitCommand();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/seedu/task/logic/parser/FindParser.java b/src/main/java/seedu/task/logic/parser/FindParser.java
new file mode 100644
index 000000000000..36b755624e5d
--- /dev/null
+++ b/src/main/java/seedu/task/logic/parser/FindParser.java
@@ -0,0 +1,27 @@
+//@@author A0141052Y
+package seedu.task.logic.parser;
+
+import static seedu.task.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import seedu.task.logic.commands.Command;
+import seedu.task.logic.commands.FindCommand;
+import seedu.task.logic.commands.IncorrectCommand;
+
+public class FindParser extends BaseParser {
+ @Override
+ public Command parse(String command, String arguments) {
+ // keywords delimited by whitespace
+
+ if (arguments.trim().isEmpty()) {
+ return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE));
+ }
+
+ final String[] keywords = arguments.split("\\s+");
+ final Set keywordSet = new HashSet<>(Arrays.asList(keywords));
+ return new FindCommand(keywordSet);
+ }
+}
diff --git a/src/main/java/seedu/task/logic/parser/FindTagParser.java b/src/main/java/seedu/task/logic/parser/FindTagParser.java
new file mode 100644
index 000000000000..60e99c9dc420
--- /dev/null
+++ b/src/main/java/seedu/task/logic/parser/FindTagParser.java
@@ -0,0 +1,31 @@
+//@@author A0141052Y
+package seedu.task.logic.parser;
+
+import static seedu.task.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import seedu.task.commons.exceptions.IllegalValueException;
+import seedu.task.logic.commands.Command;
+import seedu.task.logic.commands.FindTagCommand;
+import seedu.task.logic.commands.IncorrectCommand;
+
+public class FindTagParser extends BaseParser {
+
+ @Override
+ public Command parse(String command, String arguments) {
+ if (arguments.trim().isEmpty()) {
+ return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindTagCommand.MESSAGE_USAGE));
+ }
+
+ final String[] tagNames = arguments.split("\\s+");
+ try {
+ return new FindTagCommand(tagNames);
+ } catch (IllegalValueException ive) {
+ return new IncorrectCommand(ive.getMessage());
+ }
+ }
+
+}
diff --git a/src/main/java/seedu/task/logic/parser/HelpParser.java b/src/main/java/seedu/task/logic/parser/HelpParser.java
new file mode 100644
index 000000000000..763f7d116546
--- /dev/null
+++ b/src/main/java/seedu/task/logic/parser/HelpParser.java
@@ -0,0 +1,12 @@
+//@@author A0141052Y
+package seedu.task.logic.parser;
+
+import seedu.task.logic.commands.Command;
+import seedu.task.logic.commands.HelpCommand;
+
+public class HelpParser extends BaseParser {
+ @Override
+ public Command parse(String command, String arguments) {
+ return new HelpCommand();
+ }
+}
diff --git a/src/main/java/seedu/task/logic/parser/ListParser.java b/src/main/java/seedu/task/logic/parser/ListParser.java
new file mode 100644
index 000000000000..916c7297ef67
--- /dev/null
+++ b/src/main/java/seedu/task/logic/parser/ListParser.java
@@ -0,0 +1,48 @@
+//@@author A0141052Y
+package seedu.task.logic.parser;
+
+import static seedu.task.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import seedu.task.logic.commands.Command;
+import seedu.task.logic.commands.IncorrectCommand;
+import seedu.task.logic.commands.ListCommand;
+import seedu.task.model.Model.FilterType;
+
+public class ListParser extends BaseParser {
+
+ private final String FLAG_LIST_TYPE = "";
+
+ private final String[] KEYWORD_ARGS_REQUIRED = new String[]{FLAG_LIST_TYPE};
+ private final String[] KEYWORD_ARGS_OPTIONAL = new String[]{};
+
+ @Override
+ public Command parse(String command, String arguments) {
+ this.extractArguments(arguments);
+ if (!this.checkForRequiredArguments(KEYWORD_ARGS_REQUIRED, KEYWORD_ARGS_OPTIONAL, true)) {
+ return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, ListCommand.MESSAGE_USAGE));
+ }
+
+ FilterType filter = FilterType.ALL;
+
+ switch (getSingleKeywordArgValue(FLAG_LIST_TYPE)) {
+ case "all":
+ break;
+ case "pinned":
+ filter = FilterType.PIN;
+ break;
+ case "pending":
+ filter = FilterType.PENDING;
+ break;
+ case "completed":
+ filter = FilterType.COMPLETED;
+ break;
+ case "overdue":
+ filter = FilterType.OVERDUE;
+ break;
+ default:
+ return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, ListCommand.MESSAGE_USAGE));
+ }
+
+ return new ListCommand(filter);
+ }
+}
diff --git a/src/main/java/seedu/task/logic/parser/ParseSwitcher.java b/src/main/java/seedu/task/logic/parser/ParseSwitcher.java
new file mode 100644
index 000000000000..29ac44325179
--- /dev/null
+++ b/src/main/java/seedu/task/logic/parser/ParseSwitcher.java
@@ -0,0 +1,54 @@
+//@@author A0141052Y
+package seedu.task.logic.parser;
+
+import static seedu.task.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND;
+import static seedu.task.commons.core.Messages.MESSAGE_INTERNAL_ERROR;
+
+import java.util.HashMap;
+import java.util.Optional;
+
+import seedu.task.commons.logic.CommandKeys.Commands;
+import seedu.task.logic.commands.Command;
+import seedu.task.logic.commands.IncorrectCommand;
+
+/**
+ * ParseSwitcher -- parses the raw command from user and delegates the
+ * parsing to the specific command parsers for further parsing.
+ *
+ * @author Syed Abdullah
+ *
+ */
+public class ParseSwitcher {
+ private final ParserMapping parserMappings;
+
+ public ParseSwitcher(HashMap aliasMappings) {
+ parserMappings = new ParserMapping(aliasMappings);
+
+ }
+
+ /**
+ * Parses the user's input and determines the appropriate command
+ * @param userInput full user input string
+ * @return the command based on the user input
+ */
+ public Command parseCommand(String userInput) {
+ String[] commandSegments = userInput.split(" ", 2);
+ final String commandWord = (commandSegments.length > 0) ? commandSegments[0] : "";
+ final String commandArgs = (commandSegments.length > 1) ? commandSegments[1] : "";
+
+ Optional> selectedParser = parserMappings.getParserForCommand(commandSegments[0]);
+
+ if (!selectedParser.isPresent()) {
+ return new IncorrectCommand(MESSAGE_UNKNOWN_COMMAND);
+ } else {
+ BaseParser parser;
+ try {
+ parser = selectedParser.get().newInstance();
+ return parser.parse(commandWord, commandArgs);
+ } catch (InstantiationException | IllegalAccessException e) {
+ e.printStackTrace();
+ return new IncorrectCommand(MESSAGE_INTERNAL_ERROR);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/seedu/task/logic/parser/ParserMapping.java b/src/main/java/seedu/task/logic/parser/ParserMapping.java
new file mode 100644
index 000000000000..bb88c90f23bc
--- /dev/null
+++ b/src/main/java/seedu/task/logic/parser/ParserMapping.java
@@ -0,0 +1,71 @@
+//@@author A0141052Y
+package seedu.task.logic.parser;
+
+import java.util.HashMap;
+import java.util.Optional;
+import java.util.logging.Logger;
+import seedu.task.commons.core.LogsCenter;
+import seedu.task.commons.logic.CommandKeys;
+import seedu.task.commons.logic.CommandKeys.Commands;
+
+/**
+ * Provides command word and alias mappings
+ * @author Syed Abdullah
+ *
+ */
+public class ParserMapping {
+ private HashMap> mappingTable = new HashMap<>();
+ private final Logger logger = LogsCenter.getLogger(ParserMapping.class);
+ private final HashMap aliasMappings;
+
+ public ParserMapping(HashMap aliasMappings) {
+ populateMappings();
+ this.aliasMappings = aliasMappings;
+ }
+
+ /**
+ * Populates the command word to command parsers mapping table
+ */
+ private void populateMappings() {
+ mappingTable.put(Commands.ADD, AddParser.class);
+ mappingTable.put(Commands.ALIAS, AliasParser.class);
+ mappingTable.put(Commands.CLEAR, ClearParser.class);
+ mappingTable.put(Commands.COMPLETE, CompleteParser.class);
+ mappingTable.put(Commands.CHANGE_TO, ChangePathParser.class);
+ mappingTable.put(Commands.DELETE, DeleteParser.class);
+ mappingTable.put(Commands.EXIT, ExitParser.class);
+ mappingTable.put(Commands.FIND, FindParser.class);
+ mappingTable.put(Commands.FIND_TAG, FindTagParser.class);
+ mappingTable.put(Commands.HELP, HelpParser.class);
+ mappingTable.put(Commands.LIST, ListParser.class);
+ mappingTable.put(Commands.PIN, PinParser.class);
+ mappingTable.put(Commands.SEARCH_BOX, SearchParser.class);
+ mappingTable.put(Commands.SELECT, SelectParser.class);
+ mappingTable.put(Commands.UNDO, UndoParser.class);
+ mappingTable.put(Commands.UPDATE, UpdateParser.class);
+ mappingTable.put(Commands.UNCOMPLETE, UncompleteParser.class);
+ mappingTable.put(Commands.UNPIN, UnpinParser.class);
+ }
+
+ /**
+ * Retrieves commands for a specified keyword
+ * @param commandWord
+ * @return
+ */
+ public Optional> getParserForCommand(String commandWord) {
+
+ //check if it's an alias
+ if(aliasMappings.containsKey(commandWord) && aliasMappings.get(commandWord) != null) {
+ Commands command = aliasMappings.get(commandWord);
+ return Optional.of(mappingTable.get(command));
+ }
+
+ if (CommandKeys.commandKeyMap.containsKey(commandWord) && CommandKeys.commandKeyMap.get(commandWord) != null) {
+ Commands command = CommandKeys.commandKeyMap.get(commandWord);
+ return Optional.of(mappingTable.get(command));
+ } else {
+ logger.info("[USER COMMAND][" + commandWord + "] not found!");
+ return Optional.empty();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/seedu/task/logic/parser/PinParser.java b/src/main/java/seedu/task/logic/parser/PinParser.java
new file mode 100644
index 000000000000..0c4c8cc49152
--- /dev/null
+++ b/src/main/java/seedu/task/logic/parser/PinParser.java
@@ -0,0 +1,23 @@
+//@@author A0141052Y
+package seedu.task.logic.parser;
+
+import static seedu.task.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import java.util.Optional;
+
+import seedu.task.logic.commands.Command;
+import seedu.task.logic.commands.IncorrectCommand;
+import seedu.task.logic.commands.PinCommand;
+
+public class PinParser extends BaseParser {
+ @Override
+ public Command parse(String command, String arguments) {
+ this.extractArguments(arguments);
+
+ Optional index = parseIndex();
+ if (!index.isPresent()) {
+ return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, PinCommand.MESSAGE_USAGE));
+ }
+ return new PinCommand(index.get());
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/seedu/task/logic/parser/SearchParser.java b/src/main/java/seedu/task/logic/parser/SearchParser.java
new file mode 100644
index 000000000000..f4ea35aea53b
--- /dev/null
+++ b/src/main/java/seedu/task/logic/parser/SearchParser.java
@@ -0,0 +1,14 @@
+//@@author A0141052Y
+package seedu.task.logic.parser;
+
+import seedu.task.logic.commands.Command;
+import seedu.task.logic.commands.SearchCommand;
+
+public class SearchParser extends BaseParser {
+
+ @Override
+ public Command parse(String command, String arguments) {
+ return new SearchCommand();
+ }
+
+}
diff --git a/src/main/java/seedu/task/logic/parser/SelectParser.java b/src/main/java/seedu/task/logic/parser/SelectParser.java
new file mode 100644
index 000000000000..953146da916c
--- /dev/null
+++ b/src/main/java/seedu/task/logic/parser/SelectParser.java
@@ -0,0 +1,24 @@
+//@@author A0141052Y
+package seedu.task.logic.parser;
+
+import static seedu.task.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import java.util.Optional;
+
+import seedu.task.logic.commands.Command;
+import seedu.task.logic.commands.IncorrectCommand;
+import seedu.task.logic.commands.SelectCommand;
+
+public class SelectParser extends BaseParser {
+ @Override
+ public Command parse(String command, String arguments) {
+ this.extractArguments(arguments);
+
+ Optional index = parseIndex();
+ if (!index.isPresent()) {
+ return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, SelectCommand.MESSAGE_USAGE));
+ }
+
+ return new SelectCommand(index.get());
+ }
+}
diff --git a/src/main/java/seedu/task/logic/parser/UncompleteParser.java b/src/main/java/seedu/task/logic/parser/UncompleteParser.java
new file mode 100644
index 000000000000..197e8e979698
--- /dev/null
+++ b/src/main/java/seedu/task/logic/parser/UncompleteParser.java
@@ -0,0 +1,25 @@
+//@@author A0153467Y
+package seedu.task.logic.parser;
+
+import seedu.task.logic.commands.UncompleteCommand;
+import seedu.task.logic.commands.IncorrectCommand;
+
+import static seedu.task.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import java.util.Optional;
+
+import seedu.task.logic.commands.Command;
+
+public class UncompleteParser extends BaseParser {
+ @Override
+ public Command parse(String command, String arguments) {
+ this.extractArguments(arguments);
+ Optional idx = parseIndex();
+
+ if (!idx.isPresent()) {
+ return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, UncompleteCommand.MESSAGE_USAGE));
+ }
+
+ return new UncompleteCommand(idx.get());
+ }
+}
diff --git a/src/main/java/seedu/task/logic/parser/UndoParser.java b/src/main/java/seedu/task/logic/parser/UndoParser.java
new file mode 100644
index 000000000000..d24295eca55d
--- /dev/null
+++ b/src/main/java/seedu/task/logic/parser/UndoParser.java
@@ -0,0 +1,12 @@
+//@@author A0141052Y
+package seedu.task.logic.parser;
+
+import seedu.task.logic.commands.Command;
+import seedu.task.logic.commands.UndoCommand;
+
+public class UndoParser extends BaseParser {
+ @Override
+ public Command parse(String command, String arguments) {
+ return new UndoCommand();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/seedu/task/logic/parser/UnpinParser.java b/src/main/java/seedu/task/logic/parser/UnpinParser.java
new file mode 100644
index 000000000000..5a193c3cee79
--- /dev/null
+++ b/src/main/java/seedu/task/logic/parser/UnpinParser.java
@@ -0,0 +1,23 @@
+//@@author A0153467Y
+package seedu.task.logic.parser;
+
+import static seedu.task.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import java.util.Optional;
+
+import seedu.task.logic.commands.Command;
+import seedu.task.logic.commands.IncorrectCommand;
+import seedu.task.logic.commands.UnpinCommand;
+
+public class UnpinParser extends BaseParser {
+ @Override
+ public Command parse(String command, String arguments) {
+ this.extractArguments(arguments);
+
+ Optional index = parseIndex();
+ if (!index.isPresent()) {
+ return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, UnpinCommand.MESSAGE_USAGE));
+ }
+ return new UnpinCommand(index.get());
+ }
+}
diff --git a/src/main/java/seedu/task/logic/parser/UpdateParser.java b/src/main/java/seedu/task/logic/parser/UpdateParser.java
new file mode 100644
index 000000000000..8eb4d85240b1
--- /dev/null
+++ b/src/main/java/seedu/task/logic/parser/UpdateParser.java
@@ -0,0 +1,98 @@
+//@@author A0141052Y
+package seedu.task.logic.parser;
+
+import static seedu.task.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import java.util.HashSet;
+import java.util.Optional;
+import java.util.Set;
+import java.util.StringJoiner;
+
+import seedu.task.commons.exceptions.IllegalValueException;
+import seedu.task.logic.commands.Command;
+import seedu.task.logic.commands.IncorrectCommand;
+import seedu.task.logic.commands.UpdateCommand;
+
+public class UpdateParser extends BaseParser {
+ private final static String FLAG_NAME = "name";
+ private final static String FLAG_START_TIME = "starts";
+ private final static String FLAG_CLOSE_TIME = "ends";
+ private final static String FLAG_TAGS = "tag";
+ private final static String FLAG_REMOVE_TAGS = "remove-tag";
+ private final static String[] KEYWORD_ARGS_OPTIONAL = new String[]{
+ FLAG_NAME,
+ FLAG_START_TIME,
+ FLAG_CLOSE_TIME,
+ FLAG_TAGS,
+ FLAG_REMOVE_TAGS
+ };
+ //@@author A0144939R
+ @Override
+ public Command parse(String command, String arguments) {
+ this.extractArguments(arguments);
+
+ String index = getSingleKeywordArgValue("index");
+ Optional possibleIndex = parseIndex(index);
+
+ if (!possibleIndex.isPresent()) {
+ return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, UpdateCommand.MESSAGE_USAGE));
+ }
+
+ try {
+ return new UpdateCommand(
+ possibleIndex.get(),
+ getSingleKeywordArgValue(FLAG_NAME),
+ getSingleKeywordArgValue(FLAG_START_TIME),
+ getSingleKeywordArgValue(FLAG_CLOSE_TIME),
+ getTags(FLAG_TAGS),
+ getTags(FLAG_REMOVE_TAGS)
+ );
+ } catch (IllegalValueException ive) {
+ return new IncorrectCommand(ive.getMessage());
+ }
+ }
+
+ private Set getTags(String keyword) {
+ if (argumentsTable.containsKey(keyword)) {
+ return new HashSet<>(argumentsTable.get(keyword));
+ } else {
+ return new HashSet<>();
+ }
+ }
+
+ /**
+ * Extracts the arguments and puts them in a HashMap
+ * This method has been overriden to support the different nature of the update command's arguments
+ */
+ @Override
+ protected void extractArguments(String args) {
+ argumentsTable.clear();
+ String[] segments = args.trim().split(" ");
+ String currentKey = "index";
+ StringJoiner joiner = new StringJoiner(" ");
+ for (String segment : segments) {
+ if (isDelimiter(segment)) {
+ addToArgumentsTable(currentKey, joiner.toString().trim());
+ currentKey = segment;
+ joiner = new StringJoiner(" ");
+ } else {
+ joiner.add(segment);
+ }
+ }
+ addToArgumentsTable(currentKey, joiner.toString());
+ }
+
+ /**
+ * Checks if a string is a valid delimiter for the add command
+ * @param argument a string from the user input
+ * @return true if the String is a valid delimiter, and false otherwise
+ */
+ private boolean isDelimiter(String argument) {
+ for(int i = 0; i < KEYWORD_ARGS_OPTIONAL.length; i++) {
+ if(argument.equals(KEYWORD_ARGS_OPTIONAL[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/seedu/task/model/Model.java b/src/main/java/seedu/task/model/Model.java
new file mode 100644
index 000000000000..a3376d71a34e
--- /dev/null
+++ b/src/main/java/seedu/task/model/Model.java
@@ -0,0 +1,95 @@
+package seedu.task.model;
+
+import java.util.HashMap;
+import java.util.Set;
+
+import seedu.task.commons.core.UnmodifiableObservableList;
+import seedu.task.commons.logic.CommandKeys.Commands;
+import seedu.task.model.tag.Tag;
+import seedu.task.model.task.ReadOnlyTask;
+import seedu.task.model.task.Task;
+import seedu.task.model.task.UniqueTaskList;
+
+/**
+ * The API of the Model component.
+ */
+public interface Model {
+
+ public enum FilterType {
+ ALL,
+ PIN,
+ PENDING,
+ COMPLETED,
+ OVERDUE,
+ }
+
+ /**
+ * Clears existing backing model and replaces with the provided new data.
+ */
+ void resetData(ReadOnlyTaskManager newData);
+
+ /** Returns the Task List */
+ ReadOnlyTaskManager getTaskManager();
+
+ /** Deletes the given task. */
+ void deleteTask(ReadOnlyTask target) throws UniqueTaskList.TaskNotFoundException;
+
+ /** Adds the given task */
+ void addTask(Task task) throws UniqueTaskList.DuplicateTaskException;
+
+ /** Updates the given task */
+ void updateTask(ReadOnlyTask orginalTask, Task updateTask) throws UniqueTaskList.DuplicateTaskException;
+
+ /** Rollback the task list */
+ void rollback();
+
+ //@@author A0153467Y
+ /** Pins the given task as important */
+ void pinTask(ReadOnlyTask originalTask, Task toPin);
+
+ //@@author A0153467Y
+ /** Unpins the given task*/
+ void unpinTask(ReadOnlyTask originalTask, Task toUnpin);
+
+ //@@author A0153467Y
+ /** Mark the given task as completed */
+ void completeTask(ReadOnlyTask originalTask, Task completedTask);
+
+ //@@author A0153467Y
+ /** Unmark the given important task */
+ void uncompleteTask(ReadOnlyTask originalTask, Task uncompletedTask);
+ //@@author
+ /** Returns the filtered task list as an {@code UnmodifiableObservableList} */
+ UnmodifiableObservableList getFilteredTaskList();
+
+ /** Updates the filter of the filtered task list to show all tasks */
+ void updateFilteredListToShowAll();
+
+ //@@author A0141052Y
+ /** Updates the filter of the filtered task list to show based on the preset **/
+ void updateFilteredList(FilterType filter);
+
+ /** Updates the filter to show based on a list of tags provided **/
+ void updateFilteredListByTags(Set tags);
+ //@@author
+
+ /**
+ * Updates the filter of the filtered task list to filter by the given
+ * keywords
+ */
+ void updateFilteredTaskList(Set keywords);
+
+ //@@author A0144939R
+ /**
+ * Gets the alias map
+ * @return The command represented by the alias, or null if no mapping exists
+ */
+ public HashMap getAliasMap();
+
+ /**
+ * Sets mapping for given alias
+ * @param command
+ * @param alias
+ */
+ public void setMapping(Commands command, String alias);
+}
diff --git a/src/main/java/seedu/task/model/ModelManager.java b/src/main/java/seedu/task/model/ModelManager.java
new file mode 100644
index 000000000000..0a5e4d9802a7
--- /dev/null
+++ b/src/main/java/seedu/task/model/ModelManager.java
@@ -0,0 +1,369 @@
+package seedu.task.model;
+
+import javafx.collections.transformation.FilteredList;
+import javafx.collections.transformation.SortedList;
+import seedu.task.commons.core.ComponentManager;
+import seedu.task.commons.core.Config;
+import seedu.task.commons.core.EventsCenter;
+import seedu.task.commons.core.LogsCenter;
+import seedu.task.commons.core.UnmodifiableObservableList;
+import seedu.task.commons.events.model.ReloadFromNewFileEvent;
+import seedu.task.commons.events.model.TaskManagerChangedEvent;
+import seedu.task.commons.events.storage.ConfigFilePathChangedEvent;
+import seedu.task.commons.events.storage.FilePathChangedEvent;
+import seedu.task.commons.exceptions.DataConversionException;
+import seedu.task.commons.events.ui.JumpToListRequestEvent;
+import seedu.task.commons.logic.CommandKeys.Commands;
+import seedu.task.commons.util.ConfigUtil;
+import seedu.task.commons.util.StringUtil;
+import seedu.task.model.tag.Tag;
+import seedu.task.model.task.DateTime;
+import seedu.task.model.task.ReadOnlyTask;
+import seedu.task.model.task.Task;
+import seedu.task.model.task.UniqueTaskList;
+import seedu.task.storage.Storage;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Set;
+import java.util.logging.Logger;
+
+import com.google.common.eventbus.Subscribe;
+
+/**
+ * Represents the in-memory model of the task list data. All changes to any
+ * model should be synchronized.
+ */
+public class ModelManager extends ComponentManager implements Model {
+ private static final Logger logger = LogsCenter.getLogger(ModelManager.class);
+
+ private final TaskManager taskManager;
+ private final FilteredList filteredTasks;
+ private final SortedList sortedTasks;
+ private final UserPrefs userPrefs;
+
+ /**
+ * Initializes a ModelManager with the given TaskManager
+ * TaskManager and its variables should not be null
+ */
+ public ModelManager(TaskManager src, UserPrefs userPrefs) {
+ super();
+ assert src != null;
+ assert userPrefs != null;
+
+ logger.fine("Initializing with task list: " + src + " and user prefs " + userPrefs);
+
+ taskManager = new TaskManager(src);
+ sortedTasks = new SortedList<>(taskManager.getTasks(), this::totalOrderSorting);
+ filteredTasks = new FilteredList<>(sortedTasks);
+ this.userPrefs = userPrefs;
+ }
+
+ public ModelManager() {
+ this(new TaskManager(), new UserPrefs());
+ }
+
+ public ModelManager(ReadOnlyTaskManager initialData, UserPrefs userPrefs) {
+ taskManager = new TaskManager(initialData);
+ sortedTasks = new SortedList<>(taskManager.getTasks(), this::totalOrderSorting);
+ filteredTasks = new FilteredList<>(sortedTasks);
+ this.userPrefs = userPrefs;
+ }
+
+ @Override
+ public void resetData(ReadOnlyTaskManager newData) {
+ taskManager.resetData(newData);
+ indicateTaskManagerChanged();
+ }
+
+ @Override
+ public ReadOnlyTaskManager getTaskManager() {
+ return taskManager;
+ }
+
+ /** Raises an event to indicate the model has changed */
+ private void indicateTaskManagerChanged() {
+ raise(new TaskManagerChangedEvent(taskManager));
+ }
+
+ @Override
+ public synchronized void deleteTask(ReadOnlyTask target) throws UniqueTaskList.TaskNotFoundException {
+ taskManager.removeTask(target);
+ indicateTaskManagerChanged();
+ }
+
+ @Override
+ public synchronized void addTask(Task task) throws UniqueTaskList.DuplicateTaskException {
+ taskManager.addTask(task);
+ updateFilteredListToShowAll();
+ indicateTaskManagerChanged();
+ }
+
+ @Override
+ public synchronized void updateTask(ReadOnlyTask originalTask, Task updateTask)
+ throws UniqueTaskList.DuplicateTaskException {
+ taskManager.updateTask(originalTask, updateTask);
+ updateFilteredListToShowAll();
+ indicateTaskManagerChanged();
+ }
+
+ //@@author A0153467Y
+ @Override
+ public synchronized void completeTask(ReadOnlyTask originalTask, Task completeTask){
+ taskManager.completeTask(originalTask, completeTask);
+ updateFilteredListToShowAll();
+ indicateTaskManagerChanged();
+ }
+ //@@author
+ @Override
+ public synchronized void rollback() {
+ taskManager.rollback();
+ indicateTaskManagerChanged();
+ }
+
+ //@@author A0153467Y
+ @Override
+ public synchronized void pinTask(ReadOnlyTask originalTask, Task toPin) {
+ taskManager.pinTask(originalTask, toPin);
+ updateFilteredListToShowAll();
+ indicateTaskManagerChanged();
+ }
+
+ //@@author A0153467Y
+ @Override
+ public synchronized void uncompleteTask(ReadOnlyTask originalTask, Task uncompleteTask){
+ taskManager.uncompleteTask(originalTask, uncompleteTask);
+ }
+
+ public synchronized void unpinTask(ReadOnlyTask originalTask, Task toUnpin) {
+ taskManager.unpinTask(originalTask, toUnpin);
+ updateFilteredListToShowAll();
+ indicateTaskManagerChanged();
+ }
+ //@@author
+
+ // ========== Methods for aliasing ==========================================================================
+
+ //@@author A0144939R
+
+ public HashMap getAliasMap() {
+ return userPrefs.getAliasMap();
+ }
+
+ public void setMapping(Commands command, String alias) {
+ userPrefs.setMapping(command, alias);
+ }
+
+ //=========== Filtered Task List Accessors ===============================================================
+
+ @Override
+ public UnmodifiableObservableList getFilteredTaskList() {
+ return new UnmodifiableObservableList<>(filteredTasks);
+ }
+
+ @Override
+ public void updateFilteredListToShowAll() {
+ filteredTasks.setPredicate(null);
+ EventsCenter.getInstance().post(new JumpToListRequestEvent(filteredTasks.size() - 1));
+ }
+
+ //@@author A0141052Y
+ @Override
+ public void updateFilteredList(FilterType filter) {
+
+ updateFilteredListToShowAll();
+
+ switch (filter) {
+ case ALL:
+ updateFilteredListToShowAll();
+ break;
+
+ case PIN:
+ updateFilteredTaskList(new PredicateExpression(new PinQualifier(true)));
+ break;
+
+ case COMPLETED:
+ updateFilteredTaskList(new PredicateExpression(new CompletedQualifier(true)));
+ break;
+
+ case PENDING:
+ updateFilteredTaskList(new PredicateExpression(new CompletedQualifier(false)));
+ break;
+
+ case OVERDUE:
+ DateTime now = DateTime.fromEpoch(System.currentTimeMillis());
+ updateFilteredTaskList(new PredicateExpression(new DueDateQualifier(now)));
+ break;
+
+ default:
+ // does nothing
+ break;
+ }
+ }
+
+ public void updateFilteredListByTags(Set tags) {
+ updateFilteredTaskList(new PredicateExpression(new TagQualifier(tags)));
+ }
+ //@@author
+
+ @Override
+ public void updateFilteredTaskList(Set keywords) {
+ updateFilteredTaskList(new PredicateExpression(new NameQualifier(keywords)));
+ }
+
+ private void updateFilteredTaskList(Expression expression) {
+ filteredTasks.setPredicate(expression::satisfies);
+ }
+
+ //@@author A0141052Y
+ // ========== Methods for sorting ==========================================================================
+
+ private int totalOrderSorting(Task task, Task otherTask) {
+ return task.compareTo(otherTask);
+ }
+ //@@author
+
+ // ========== Inner classes/interfaces used for filtering ==================================================
+
+ interface Expression {
+ boolean satisfies(ReadOnlyTask tasm);
+
+ String toString();
+ }
+
+ private class PredicateExpression implements Expression {
+
+ private final Qualifier qualifier;
+
+ PredicateExpression(Qualifier qualifier) {
+ this.qualifier = qualifier;
+ }
+
+ @Override
+ public boolean satisfies(ReadOnlyTask task) {
+ return qualifier.run(task);
+ }
+
+ @Override
+ public String toString() {
+ return qualifier.toString();
+ }
+ }
+
+ interface Qualifier {
+ boolean run(ReadOnlyTask task);
+
+ String toString();
+ }
+
+ 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().taskName, keyword)).findAny()
+ .isPresent();
+ }
+
+ @Override
+ public String toString() {
+ return "name=" + String.join(", ", nameKeyWords);
+ }
+
+ }
+
+ //@@author A0141052Y
+ /**
+ * Qualifier that checks for matching tags
+ * @author Syed Abdullah
+ *
+ */
+ private class TagQualifier implements Qualifier {
+ private Set tags;
+
+ TagQualifier(Set tags) {
+ this.tags = tags;
+ }
+
+ @Override
+ public boolean run(ReadOnlyTask task) {
+ return tags.stream()
+ .allMatch(tag -> task.getTags().contains(tag));
+ }
+ }
+
+ /**
+ * Qualifier that checks if Task is not due based on reference time
+ * @author Syed Abdullah
+ *
+ */
+ private class DueDateQualifier implements Qualifier {
+
+ private DateTime referencePoint;
+
+ DueDateQualifier(DateTime reference) {
+ this.referencePoint = reference;
+ }
+
+ @Override
+ public boolean run(ReadOnlyTask task) {
+ DateTime toCompare = task.getCloseTime();
+
+ if (toCompare.isEmpty()) {
+ return false;
+ } else {
+ return (toCompare.compareTo(this.referencePoint) < 1);
+ }
+ }
+ }
+
+ /**
+ * Qualifier to check the Pin property of the underlying Task.
+ * @author Syed Abdullah
+ *
+ */
+ private class PinQualifier implements Qualifier {
+
+ private boolean isPinned;
+
+ PinQualifier(boolean isPinned) {
+ this.isPinned = isPinned;
+ }
+
+ @Override
+ public boolean run(ReadOnlyTask task) {
+ return (task.getImportance() == this.isPinned);
+ }
+ }
+
+ /**
+ * Qualifier to check the Completed property of the underlying Task.
+ * @author Syed Abdullah
+ *
+ */
+ private class CompletedQualifier implements Qualifier {
+
+ private boolean isCompleted;
+
+ CompletedQualifier(boolean isCompleted) {
+ this.isCompleted = isCompleted;
+ }
+
+ @Override
+ public boolean run(ReadOnlyTask task) {
+ return (task.getComplete() == this.isCompleted);
+ }
+ }
+
+ //@@author A0144939R
+ @Subscribe
+ public void handleReloadFromNewFileEvent(ReloadFromNewFileEvent event) throws DataConversionException {
+ logger.info(LogsCenter.getEventHandlingLogMessage(event, "Load from new file path requested"));
+ resetData(event.taskManager);
+ }
+}
diff --git a/src/main/java/seedu/task/model/ReadOnlyTaskManager.java b/src/main/java/seedu/task/model/ReadOnlyTaskManager.java
new file mode 100644
index 000000000000..4ddc22bea17b
--- /dev/null
+++ b/src/main/java/seedu/task/model/ReadOnlyTaskManager.java
@@ -0,0 +1,30 @@
+package seedu.task.model;
+
+
+import java.util.List;
+
+import seedu.task.model.tag.Tag;
+import seedu.task.model.tag.UniqueTagList;
+import seedu.task.model.task.ReadOnlyTask;
+import seedu.task.model.task.UniqueTaskList;
+
+/**
+ * Unmodifiable view of a task manager
+ */
+public interface ReadOnlyTaskManager {
+
+ UniqueTagList getUniqueTagList();
+
+ UniqueTaskList getUniqueTaskList();
+
+ /**
+ * Returns an unmodifiable view of tasks list
+ */
+ List getTaskList();
+
+ /**
+ * Returns an unmodifiable view of tags list
+ */
+ List getTagList();
+
+}
diff --git a/src/main/java/seedu/task/model/TaskManager.java b/src/main/java/seedu/task/model/TaskManager.java
new file mode 100644
index 000000000000..24ca1fb0cca1
--- /dev/null
+++ b/src/main/java/seedu/task/model/TaskManager.java
@@ -0,0 +1,240 @@
+package seedu.task.model;
+
+import javafx.collections.ObservableList;
+import seedu.task.commons.exceptions.IllegalValueException;
+import seedu.task.model.tag.Tag;
+import seedu.task.model.tag.UniqueTagList;
+import seedu.task.model.task.ReadOnlyTask;
+import seedu.task.model.task.Task;
+import seedu.task.model.task.UniqueTaskList;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * Wraps all data at the task-manager level Duplicates are not allowed (by
+ * .equals comparison)
+ */
+public class TaskManager implements ReadOnlyTaskManager {
+
+ private final UniqueTaskList tasks;
+ private final UniqueTagList tags;
+
+ {
+ tasks = new UniqueTaskList();
+ tags = new UniqueTagList();
+ }
+
+ public TaskManager() {
+ }
+
+ /**
+ * Tasks and Tags are copied into this taskmanager
+ */
+ public TaskManager(ReadOnlyTaskManager toBeCopied) {
+ this(toBeCopied.getUniqueTaskList(), toBeCopied.getUniqueTagList());
+ }
+
+ /**
+ * Tasks and Tags are copied into this taskmanager
+ */
+ public TaskManager(UniqueTaskList tasks, UniqueTagList tags) {
+ resetData(tasks.getInternalList(), tags.getInternalList());
+ }
+
+ public static ReadOnlyTaskManager getEmptyTaskManager() {
+ return new TaskManager();
+ }
+
+ //// list overwrite operations
+
+ public ObservableList getTasks() {
+ return tasks.getInternalList();
+ }
+
+ public void setTasks(List tasks) {
+ this.tasks.getInternalList().setAll(tasks);
+ }
+
+ public void setTags(Collection tags) {
+ this.tags.getInternalList().setAll(tags);
+ }
+
+ public void resetData(Collection extends ReadOnlyTask> newTasks, Collection newTags) {
+ setTasks(newTasks.stream().map(t -> {
+ try {
+ return new Task(t);
+ } catch (IllegalValueException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ return null;
+ }
+
+ }).collect(Collectors.toList()));
+ setTags(newTags);
+ }
+
+ public void resetData(ReadOnlyTaskManager newData) {
+ resetData(newData.getTaskList(), newData.getTagList());
+ }
+
+ //// task-level operations
+
+ /**
+ * Adds a task to the task list. Also checks the new task's tags and updates
+ * {@link #tags} with any new tags found, and updates the Tag objects in the
+ * task to point to those in {@link #tags}.
+ *
+ * @throws UniqueTaskList.DuplicateTaskException
+ * if an equivalent task already exists.
+ */
+ public void addTask(Task p) throws UniqueTaskList.DuplicateTaskException {
+ syncTagsWithMasterList(p);
+ tasks.add(p);
+ }
+
+ /**
+ * Updates a specific task to the task list. Also checks the new task's tags
+ * and updates {@link #tags} with any new tags found, and updates the Tag
+ * objects in the task to point to those in {@link #tags}.
+ *
+ * @throw UniqueTaskList.DuplicateTaskException if the same task already
+ * exists in the list.
+ */
+ public void updateTask(ReadOnlyTask originalTask, Task updateTask) throws UniqueTaskList.DuplicateTaskException {
+ syncTagsWithMasterList(updateTask);
+ tasks.update(originalTask, updateTask);
+ }
+
+ //@@author A0153467Y
+ /**
+ * Pins a specfic task to the task list as important.
+ *
+ * @param originalTask the orginial task on the list
+ * @param toPin Task which is pinned
+ */
+ public void pinTask(ReadOnlyTask originalTask, Task toPin) {
+ tasks.pin(originalTask, toPin);
+ }
+
+ //@@author A0153467Y
+ /**
+ * Unpins a specfic pinned task to the task list.
+ *
+ * @param originalTask the orginial task on the list
+ * @param toPin Task which is unpinnned
+ */
+ public void unpinTask(ReadOnlyTask originalTask, Task toUnpin) {
+ tasks.unpin(originalTask, toUnpin);
+ }
+
+ /**
+ * Marks a specific task as completed to the task list.
+ *
+ * @param originalTask refers to the task that selected from the list
+ * @param completeTask refers to a task same as original task except being marked as complete
+ */
+
+ public void completeTask(ReadOnlyTask originalTask, Task completeTask) {
+ tasks.complete(originalTask,completeTask);
+ }
+
+ //@@author A0153467Y
+ /**
+ * Unmark a specific completed task as not completed to the task list.
+ *
+ * @param originalTask refers to task which is marked as complete
+ * @param uncompleteTask refers to task which is now marked as not complete
+ */
+ public void uncompleteTask(ReadOnlyTask originalTask, Task uncompleteTask) {
+ tasks.uncomplete(originalTask,uncompleteTask);
+ }
+ //@@author
+ /**
+ * Ensures that every tag in this task: - exists in the master list
+ * {@link #tags} - points to a Tag object in the master list
+ */
+ private void syncTagsWithMasterList(Task task) {
+ final UniqueTagList taskTags = task.getTags();
+ tags.mergeFrom(taskTags);
+
+ // Create map with values = tag object references in the master list
+ final Map masterTagObjects = new HashMap<>();
+ for (Tag tag : 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 : taskTags) {
+ commonTagReferences.add(masterTagObjects.get(tag));
+ }
+ task.setTags(new UniqueTagList(commonTagReferences));
+ }
+
+ public boolean removeTask(ReadOnlyTask key) throws UniqueTaskList.TaskNotFoundException {
+ if (tasks.remove(key)) {
+ return true;
+ } else {
+ throw new UniqueTaskList.TaskNotFoundException();
+ }
+ }
+
+ /**
+ * Rollback the previous change made to TaskList
+ * @return true if the operation is successful, else false
+ */
+ public boolean rollback() {
+ tasks.rollback();
+ return true;
+ }
+
+ //// tag-level operations
+
+ public void addTag(Tag t) throws UniqueTagList.DuplicateTagException {
+ tags.add(t);
+ }
+
+ //// util methods
+
+ @Override
+ public String toString() {
+ return tasks.getInternalList().size() + " tasks, " + tags.getInternalList().size() + " tags";
+ // TODO: refine later
+ }
+
+ @Override
+ public List getTaskList() {
+ return Collections.unmodifiableList(tasks.getInternalList());
+ }
+
+ @Override
+ public List getTagList() {
+ return Collections.unmodifiableList(tags.getInternalList());
+ }
+
+ @Override
+ public UniqueTaskList getUniqueTaskList() {
+ return this.tasks;
+ }
+
+ @Override
+ public UniqueTagList getUniqueTagList() {
+ return this.tags;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof TaskManager // instanceof handles nulls
+ && this.tasks.equals(((TaskManager) other).tasks)
+ && this.tags.equals(((TaskManager) other).tags));
+ }
+
+ @Override
+ public int hashCode() {
+ // use this method for custom fields hashing instead of implementing
+ // your own
+ return Objects.hash(tasks, tags);
+ }
+}
diff --git a/src/main/java/seedu/address/model/UserPrefs.java b/src/main/java/seedu/task/model/UserPrefs.java
similarity index 59%
rename from src/main/java/seedu/address/model/UserPrefs.java
rename to src/main/java/seedu/task/model/UserPrefs.java
index da9c8037f495..f645d197a570 100644
--- a/src/main/java/seedu/address/model/UserPrefs.java
+++ b/src/main/java/seedu/task/model/UserPrefs.java
@@ -1,14 +1,18 @@
-package seedu.address.model;
-
-import seedu.address.commons.core.GuiSettings;
+package seedu.task.model;
import java.util.Objects;
+import java.util.HashMap;
+import seedu.task.commons.core.GuiSettings;
+import seedu.task.commons.logic.CommandKeys.Commands;
+
/**
* Represents User's preferences.
*/
public class UserPrefs {
-
+ //@@author A0144939R
+ private HashMap aliases = new HashMap();
+ //@@author
public GuiSettings guiSettings;
public GuiSettings getGuiSettings() {
@@ -50,5 +54,23 @@ public int hashCode() {
public String toString(){
return guiSettings.toString();
}
-
+
+ //@@author A0144939R
+ /**
+ * Gets mapping for a given alias.
+ * @param alias: a user defined string
+ * @return The command represented by the alias, or null if no mapping exists
+ */
+ public HashMap getAliasMap() {
+ return aliases;
+ }
+
+ /**
+ * Sets mapping for given alias
+ * @param command
+ * @param alias
+ */
+ public void setMapping(Commands command, String alias) {
+ aliases.put(alias, command);
+ }
}
diff --git a/src/main/java/seedu/task/model/history/ListMutation.java b/src/main/java/seedu/task/model/history/ListMutation.java
new file mode 100644
index 000000000000..8a09cabd900e
--- /dev/null
+++ b/src/main/java/seedu/task/model/history/ListMutation.java
@@ -0,0 +1,99 @@
+//@@author A0141052Y
+package seedu.task.model.history;
+
+import java.util.HashMap;
+import java.util.Map.Entry;
+import java.util.Set;
+
+/**
+ * Represents a state change for an indexed List
+ * @author Syed Abdullah
+ *
+ * @param
+ */
+public class ListMutation {
+ private final HashMap> mutationMap;
+
+ /**
+ * Constructs an empty ListMutation
+ */
+ public ListMutation() {
+ this.mutationMap = new HashMap>();
+ }
+
+ /**
+ * Add a Mutation of a specified index in the list
+ *
+ * @param index the index of the element in the list
+ * @param mutation the Mutation of the element in the list
+ */
+ public void addMutation(int index, Mutation mutation) {
+ this.mutationMap.put(index, mutation);
+ }
+
+ /**
+ * Adds a new Mutation for an element, treating it as a new mutation for the list
+ *
+ * @param index the index of the element in the list
+ * @param mutation the Mutation of the element in the list
+ */
+ public void addAsNewMutation(int index, Mutation mutation) {
+ this.clear();
+ this.addMutation(index, mutation);
+ }
+
+ /**
+ * Adds new Mutations for consecutive elements inserted into list. Method will create a new Mutation
+ *
+ * @param startIndex the starting position (inclusive)
+ * @param newElements the array of elements that are added
+ */
+ public void addNewElements(int startIndex, T[] newElements) {
+ this.clear();
+ for (int i = startIndex; i < newElements.length; i++) {
+ this.mutationMap.put(i, new Mutation(null, newElements[i - startIndex]));
+ }
+ }
+
+ /**
+ * Mutates an element in the specified index to the next state
+ *
+ * @param index the index of the element in the list
+ * @param currentState the current state of the element
+ * @param nextState the next state of the element
+ */
+ public void mutateElement(int index, T nextState) {
+ Mutation newMutation;
+ if (this.mutationMap.containsKey(index)) {
+ Mutation currentMutation = this.mutationMap.get(index);
+ newMutation = currentMutation.transitionToNextState(nextState);
+ } else {
+ newMutation = new Mutation(null, nextState);
+ }
+ this.mutationMap.put(index, newMutation);
+ }
+
+ /**
+ * Retrieves a mapping of element mutations for the list
+ * @return set of element mutations entries for the list
+ */
+ public Set>> getMutations() {
+ return this.mutationMap.entrySet();
+ }
+
+ /**
+ * Checks if there is any mutations that are recorded.
+ *
+ * @return true if there is any mutation, else false
+ */
+ public boolean hasMutation() {
+ return !this.mutationMap.isEmpty();
+ }
+
+ /**
+ * Clears the ListMutation
+ */
+ public void clear() {
+ this.mutationMap.clear();
+ }
+}
diff --git a/src/main/java/seedu/task/model/history/Mutation.java b/src/main/java/seedu/task/model/history/Mutation.java
new file mode 100644
index 000000000000..4d51a3a7eb43
--- /dev/null
+++ b/src/main/java/seedu/task/model/history/Mutation.java
@@ -0,0 +1,59 @@
+//@@author A0141052Y
+package seedu.task.model.history;
+
+/**
+ * Represents an object state change.
+ * @author Syed Abdullah
+ *
+ * @param the object's Class
+ */
+public class Mutation {
+
+ final private T previousState;
+ final private T presentState;
+
+ /**
+ * Constructs a Mutation.
+ *
+ * @param previousState the state of the instance before mutation
+ * @param presentState the state of the instance after mutation
+ */
+ public Mutation(T previousState, T presentState) {
+ this.previousState = previousState;
+ this.presentState = presentState;
+ }
+
+ /**
+ * Get the state before mutation
+ * @return state of object before mutation took place
+ */
+ public T getPreviousState() {
+ return previousState;
+ }
+
+ /**
+ * Get the state after mutation
+ * @return state of object before mutation took place
+ */
+ public T getPresentState() {
+ return presentState;
+ }
+
+ /**
+ * Transitions the current mutation to the next mutation
+ *
+ * @param state the next state to transit to
+ * @return a new Mutation from the current state to the next state
+ */
+ public Mutation transitionToNextState(T state) {
+ return new Mutation(this.getPresentState(), state);
+ }
+
+ /**
+ * Reverses the mutation (e.g. A->B becomes B->A)
+ * @return a reversed mutation
+ */
+ public Mutation reverse() {
+ return new Mutation(this.getPresentState(), this.getPreviousState());
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/seedu/address/model/tag/Tag.java b/src/main/java/seedu/task/model/tag/Tag.java
similarity index 85%
rename from src/main/java/seedu/address/model/tag/Tag.java
rename to src/main/java/seedu/task/model/tag/Tag.java
index 5bcffdb5ddf1..bc214b05da2a 100644
--- a/src/main/java/seedu/address/model/tag/Tag.java
+++ b/src/main/java/seedu/task/model/tag/Tag.java
@@ -1,10 +1,10 @@
-package seedu.address.model.tag;
+package seedu.task.model.tag;
-import seedu.address.commons.exceptions.IllegalValueException;
+import seedu.task.commons.exceptions.IllegalValueException;
/**
- * Represents a Tag in the address book.
+ * Represents a Tag in the task list.
* Guarantees: immutable; name is valid as declared in {@link #isValidTagName(String)}
*/
public class Tag {
@@ -24,11 +24,10 @@ public Tag() {
*/
public Tag(String name) throws IllegalValueException {
assert name != null;
- name = name.trim();
- if (!isValidTagName(name)) {
+ if (!isValidTagName(name.trim())) {
throw new IllegalValueException(MESSAGE_TAG_CONSTRAINTS);
}
- this.tagName = name;
+ this.tagName = name.trim();
}
/**
diff --git a/src/main/java/seedu/address/model/tag/UniqueTagList.java b/src/main/java/seedu/task/model/tag/UniqueTagList.java
similarity index 83%
rename from src/main/java/seedu/address/model/tag/UniqueTagList.java
rename to src/main/java/seedu/task/model/tag/UniqueTagList.java
index 76fb7ff3dc5d..8e5e333b4455 100644
--- a/src/main/java/seedu/address/model/tag/UniqueTagList.java
+++ b/src/main/java/seedu/task/model/tag/UniqueTagList.java
@@ -1,9 +1,9 @@
-package seedu.address.model.tag;
+package seedu.task.model.tag;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
-import seedu.address.commons.util.CollectionUtil;
-import seedu.address.commons.exceptions.DuplicateDataException;
+import seedu.task.commons.exceptions.DuplicateDataException;
+import seedu.task.commons.util.CollectionUtil;
import java.util.*;
@@ -21,10 +21,20 @@ public class UniqueTagList implements Iterable {
* Signals that an operation would have violated the 'no duplicates' property of the list.
*/
public static class DuplicateTagException extends DuplicateDataException {
+
+ private static final long serialVersionUID = 1L;
+
protected DuplicateTagException() {
super("Operation would result in duplicate tags");
}
}
+
+ /**
+ * Signals that an operation tried to operate on a tag that does not exist
+ */
+ public static class TagNotFoundException extends Exception {
+
+ private static final long serialVersionUID = 1L; }
private final ObservableList internalList = FXCollections.observableArrayList();
@@ -118,6 +128,19 @@ public void add(Tag toAdd) throws DuplicateTagException {
}
internalList.add(toAdd);
}
+
+ /**
+ * Removes a Tag from the list
+ *
+ * @throws TagNotFoundException if Tag to be removed does not exist in the list.
+ */
+ public void remove(Tag toRemove) throws TagNotFoundException {
+ assert toRemove != null;
+ if (!contains(toRemove)) {
+ throw new TagNotFoundException();
+ }
+ internalList.remove(toRemove);
+ }
@Override
public Iterator iterator() {
diff --git a/src/main/java/seedu/task/model/task/DateTime.java b/src/main/java/seedu/task/model/task/DateTime.java
new file mode 100644
index 000000000000..82e3041e529e
--- /dev/null
+++ b/src/main/java/seedu/task/model/task/DateTime.java
@@ -0,0 +1,219 @@
+//@author A0144939R
+package seedu.task.model.task;
+
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.time.format.FormatStyle;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.TemporalUnit;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+import java.util.Optional;
+
+import org.ocpsoft.prettytime.PrettyTime;
+import org.ocpsoft.prettytime.nlp.PrettyTimeParser;
+
+import seedu.task.commons.exceptions.IllegalValueException;
+
+/**
+ * Represents a Date and Time in the task list
+ * Guarantees: immutable; is valid as declared in {@link #isValidDateTime(String)}
+ */
+public class DateTime implements Comparable {
+
+ public static final String MESSAGE_DATETIME_CONSTRAINTS = "You have entered an invalid Date/Time format. For a complete list of all acceptable formats, please view our user guide.";
+
+ //@@author A0141052Y
+ private static final String DATE_TIME_DISPLAY_FORMAT = "%s (%s)";
+ //@@author
+
+ public final Optional value;
+ private static PrettyTime p = new PrettyTime();
+
+ //@@author A0141052Y
+ /**
+ * Constructs an empty DateTime
+ */
+ public DateTime() {
+ this.value = Optional.empty();
+ }
+
+ /**
+ * Constructs a DateTime from an Instant
+ * @param dateTime the Instant of the time and date to be represented
+ */
+ public DateTime(Instant dateTime) {
+ if (dateTime == null) {
+ this.value = Optional.empty();
+ return;
+ }
+
+ this.value = Optional.of(dateTime.truncatedTo(ChronoUnit.MINUTES));
+ }
+
+ /**
+ * Validates given Date and Time entered by the user.
+ * @param dateTime the String representation of the input from the user
+ * @throws IllegalValueException if given date/time string is invalid.
+ */
+ public static DateTime fromUserInput(String dateTime) throws IllegalValueException {
+ if (dateTime == null || dateTime.equals("")) {
+ return new DateTime(null);
+ }
+
+ if (!isValidDateTime(dateTime)) {
+ throw new IllegalValueException(MESSAGE_DATETIME_CONSTRAINTS);
+ }
+
+ List possibleDates = new PrettyTimeParser().parse(dateTime);
+ return new DateTime(possibleDates.get(0).toInstant());
+ }
+
+ /**
+ * Create a new DateTime object using the number of milliseconds from 01-01-1970
+ * @param epochMilli the number of milliseconds elapsed from the epoch
+ * @return a new DateTime object for the specified epoch offset
+ */
+ public static DateTime fromEpoch(Long epochMilli) {
+ if (epochMilli == null) {
+ return new DateTime(null);
+ }
+
+ return new DateTime(Instant.ofEpochMilli(epochMilli));
+ }
+
+ /**
+ * Creates a new DateTime based off an offset from another DateTime
+ * @param offsetFrom the DateTime to offset from
+ * @param amountToAdd the amount of the specified unit to add to the DateTime
+ * @param unit the time unit of the amount
+ * @return a new DateTime object with a value that is offset from another DateTime
+ */
+ public static DateTime fromDateTimeOffset(DateTime offsetFrom, long amountToAdd, TemporalUnit unit) {
+ if (offsetFrom == null || offsetFrom.isEmpty()) {
+ return new DateTime(null);
+ }
+
+ Instant offsetInstant = offsetFrom.value.get();
+ return new DateTime(offsetInstant.plus(amountToAdd, unit));
+ }
+
+ //@@author A0144939R
+
+ /**
+ * Returns true if a given string is a valid date/time that can be parsed
+ *
+ * @param test output from date/time parser
+ */
+ public static boolean isValidDateTime(String dateTime) {
+ List possibleDates = new PrettyTimeParser().parse(dateTime);
+ return !possibleDates.isEmpty() && (possibleDates.size() == 1);
+ }
+
+ @Override
+ public String toString() {
+
+ if(value.isPresent()) {
+ DateTimeFormatter formatter =
+ DateTimeFormatter.ofLocalizedDateTime( FormatStyle.SHORT )
+ .withLocale( Locale.UK )
+ .withZone( ZoneId.systemDefault() );
+ return formatter.format( value.get() );
+ } else {
+ return "";
+ }
+ }
+
+ public String toPrettyString() {
+ if(value.isPresent()) {
+ return p.format(Date.from(this.value.get()));
+ } else {
+ return "";
+ }
+ }
+
+ //@@author A0141052Y
+ /**
+ * Gets a display friendly representation of the DateTime
+ * @return A String containing the display friendly version
+ */
+ public String toDisplayString() {
+ if (this.toString().isEmpty()) {
+ return "";
+ } else {
+ return String.format(DATE_TIME_DISPLAY_FORMAT, this.toString(), this.toPrettyString());
+ }
+ }
+
+ /**
+ * Retrieves an ISO 8601 representation of the DateTime.
+ * @return A String containing the ISO-8601 representation or empty, if there's
+ * no DateTime value
+ */
+ public String toISOString() {
+ if (this.value.isPresent()) {
+ return this.value.get().toString();
+ } else {
+ return "";
+ }
+ }
+
+ public Long getSaveableValue() {
+ if(value.isPresent()) {
+ return this.value.get().toEpochMilli();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Compares between two DateTime instances using Comparable.
+ * Empty DateTimes are ordered behind all possible non-empty DateTimes.
+ */
+ @Override
+ public int compareTo(DateTime o) {
+ Optional time = this.value;
+ Optional otherTime = o.getDateTimeValue();
+
+ if (!time.isPresent() && !otherTime.isPresent()) {
+ return 0;
+ } else if (!time.isPresent()) {
+ return 1;
+ } else if (!otherTime.isPresent()) {
+ return -1;
+ } else {
+ return time.get().compareTo(otherTime.get());
+ }
+ }
+ //@@author
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof DateTime// instanceof handles nulls
+ && this.value.equals(((DateTime) other).value)); // state check
+ }
+
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
+ /**
+ * Returns an optional corresponding to the value of the DateTime object
+ * @return value of DateTime object
+ */
+ public Optional getDateTimeValue() {
+ return this.value;
+ }
+
+ /**
+ * Checks if there is a DateTime specified
+ *
+ * @return true if a DateTime is specified, else false
+ */
+ public boolean isEmpty() {
+ return !this.value.isPresent();
+ }
+}
diff --git a/src/main/java/seedu/address/model/person/Name.java b/src/main/java/seedu/task/model/task/Name.java
similarity index 54%
rename from src/main/java/seedu/address/model/person/Name.java
rename to src/main/java/seedu/task/model/task/Name.java
index 4f30033e70fe..9006fa304e7f 100644
--- a/src/main/java/seedu/address/model/person/Name.java
+++ b/src/main/java/seedu/task/model/task/Name.java
@@ -1,17 +1,17 @@
-package seedu.address.model.person;
+package seedu.task.model.task;
-import seedu.address.commons.exceptions.IllegalValueException;
+import seedu.task.commons.exceptions.IllegalValueException;
/**
- * Represents a Person's name in the address book.
+ * Represents a Task's name in the task list.
* Guarantees: immutable; is valid as declared in {@link #isValidName(String)}
*/
-public class Name {
+public class Name implements Comparable