diff --git a/.project b/.project index 1c9339c5f927..90ff308c4765 100644 --- a/.project +++ b/.project @@ -1,7 +1,7 @@ - addressbook-level4 - Project addressbook-level4 created by Buildship. + main + Project main created by Buildship. diff --git a/.settings/org.eclipse.buildship.core.prefs b/.settings/org.eclipse.buildship.core.prefs index 58b9567e0ca2..4e0fc71ac89f 100644 --- a/.settings/org.eclipse.buildship.core.prefs +++ b/.settings/org.eclipse.buildship.core.prefs @@ -1,6 +1,7 @@ build.commands=org.eclipse.jdt.core.javabuilder connection.arguments= connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER) +connection.gradle.user.home=null connection.java.home=null connection.jvm.arguments= connection.project.dir= diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000000..35068d95fcfa --- /dev/null +++ b/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,4 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/README.md b/README.md index 249a00b3899c..bee6445076d2 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,18 @@ -[![Build Status](https://travis-ci.org/se-edu/addressbook-level4.svg?branch=master)](https://travis-ci.org/se-edu/addressbook-level4) -[![Coverage Status](https://coveralls.io/repos/github/se-edu/addressbook-level4/badge.svg?branch=master)](https://coveralls.io/github/se-edu/addressbook-level4?branch=master) +[![Build Status](https://travis-ci.org/CS2103AUG2016-T15-C3/main.svg?branch=master)](https://travis-ci.org/CS2103AUG2016-T15-C3/main) +[![Coverage Status](https://coveralls.io/repos/github/CS2103AUG2016-T15-C3/main/badge.svg)](https://coveralls.io/github/CS2103AUG2016-T15-C3/main) +[![Codacy Badge](https://api.codacy.com/project/badge/Grade/ba97fe95f1454a2291ad666c07e43d12)](https://www.codacy.com/app/shiya_95/main?utm_source=github.com&utm_medium=referral&utm_content=CS2103AUG2016-T15-C3/main&utm_campaign=Badge_Grade) -# Address Book (Level 4) +# Task!t -
+
-* This is a desktop Address Book application. It has a GUI but most of the user interactions happen using +* This is a desktop Task Manager 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. +* The base code is taken from [Address Book (level 4)](https://github.com/nus-cs2103-AY1617S1/addressbook-level4). Below are some description about Address Book (level 4): + * 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. #### Site Map @@ -29,6 +27,7 @@ * Some parts of this sample application were inspired by the excellent [Java FX tutorial](http://code.makery.ch/library/javafx-8-tutorial/) by *Marco Jakob*. +* The sample project is created by the [SE-EDU](https://github.com/se-edu/) initiative. #### Licence : [MIT](LICENSE) diff --git a/build.gradle b/build.gradle index 46b06c1e42ec..cab8262fd30d 100644 --- a/build.gradle +++ b/build.gradle @@ -35,6 +35,7 @@ allprojects { junitVersion = '4.12' testFxVersion = '4.0.+' monocleVersion = '1.8.0_20' + nattyVersion = '0.12' libDir = 'lib' } @@ -52,6 +53,7 @@ allprojects { compile "com.fasterxml.jackson.core:jackson-databind:$jacksonVersion" compile "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jacksonDataTypeVersion" compile "com.google.guava:guava:$guavaVersion" + compile "com.joestelmach:natty:$nattyVersion" testCompile "junit:junit:$junitVersion" testCompile "org.testfx:testfx-core:$testFxVersion" @@ -74,7 +76,7 @@ allprojects { } shadowJar { - archiveName = "addressbook.jar" + archiveName = "todolist.jar" manifest { attributes "Main-Class": "seedu.address.MainApp" @@ -113,8 +115,8 @@ tasks.coveralls { onlyIf { System.env.'CI' } } -class AddressBookTest extends Test { - public AddressBookTest() { +class ToDoListTest extends Test { + public ToDoListTest() { forkEvery = 1 systemProperty 'testfx.setup.timeout', '60000' } @@ -128,7 +130,7 @@ class AddressBookTest extends Test { } } -task guiTests(type: AddressBookTest) { +task guiTests(type: ToDoListTest) { include 'guitests/**' jacoco { @@ -137,7 +139,7 @@ task guiTests(type: AddressBookTest) { } -task nonGuiTests(type: AddressBookTest) { +task nonGuiTests(type: ToDoListTest) { include 'seedu/address/**' jacoco { @@ -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: ToDoListTest) { jacoco { destinationFile = new File("${buildDir}/jacoco/test.exec") } diff --git a/collated/docs/A0138601M.md b/collated/docs/A0138601M.md new file mode 100644 index 000000000000..6c509261b5c9 --- /dev/null +++ b/collated/docs/A0138601M.md @@ -0,0 +1,297 @@ +# A0138601M +###### /DeveloperGuide.md +``` md +### Model component + +
+ +**API** : [`Model.java`](../src/main/java/seedu/address/model/Model.java) + +The `Model`, +* Stores a `UserPref` object that represents the user's preferences +* Stores the To Do List data +* Exposes a `UnmodifiableObservableList +Use case ends. + +**Extensions** + +2a. The command format is invalid +> 2a1. Task!t shows a 'invalid command' message and display the expected format.
+> Use case ends + +2b. The task already exist +> 2b1. Task!t shows a 'the task already exist' message.
+> Use case resumes at step 1 + +``` +###### /DeveloperGuide.md +``` md +#### Use case: Edit task + +**MSS** + +1. User requests to **view** tasks for a particular date/week/month/date +2. Task!t displays the list of tasks for that date sorted by date and/or time +3. User request to edit a specific task in the list based on task’s index with certain details +4. Task!t updates the task details
+Use case ends. + +**Extensions** + +2a. The list is empty +> 2a1. Task!t shows a 'no task found' message.
+> Use case ends + +3a. The given index is invalid +> 3a1. Task!t shows a 'invalid index' message
+> Use case resumes at step 2 + +3b. The command format is invalid +> 3b1. Task!t shows a 'invalid command' message and display the expected format.
+> Use case resumes at step 2 + +#### Use case: Undo operation + +**MSS** + +1. User requests to undo last operation performed +2. Task!t undo the last operation
+Use case ends. + +**Extensions** + +2a. There is no last operation +> 2a1. Task!t shows a 'there is no last operation' message.
+> Use case ends + +#### Use case: Mark task as done + +**MSS** + +1. User requests to view tasks for a particular date/week/month/date +2. Task!t displays the list of tasks for that date sorted by date and/or time +3. User request to mark specific tasks in the list based on task’s index +4. Task!t marks the tasks
+Use case ends. + +**Extensions** + +2a. The list is empty +> 2a1. Task!t shows a 'no task found' message.
+> Use case ends + +3a. The given index is invalid +> 3a1. Task!t shows a 'invalid index' message
+> Use case resumes at step 2 + +3b. The command format is invalid +> 3b1. Task!t shows a 'invalid format' message and display the expected format
+> Use case ends + +3c. The task is already marked as done +> 3c1. Task!t shows a 'task already marked' message.
+> Use case resumes at step 3 + + + +#### Use case: Delete task + +**MSS** + +1. User requests to **view** tasks for a particular date/week/month/date +2. Task!t displays the list of tasks for that date sorted by date and/or time +3. User requests to delete a specific task in the list based on task’s index +4. Task!t deletes the task
+Use case ends. + +**Extensions** + +2a. The list is empty +> 2a1. Task!t shows a 'no task found' message
+> Use case ends + +3a. The given index is invalid +> 3a1. Task!t shows a 'invalid index' message
+> Use case resumes at step 2 + +#### Use case: Set storage location + +**MSS** + +1. User requests to change storage location wo a specified file path +2. Task!t changes the storage location
+Use case ends. + +**Extensions** + +2a. The given path is invalid +> 2a1. Task!t shows a 'invalid path' message
+> Use case ends + +``` +###### /DeveloperGuide.md +``` md +#### Google Calendar +Pros: +* Able to color-code different events +* Able to set notifications +* Able to set repeat +* Able to add notes (i.e. location, remarks) +* Able to sync on different devices +* Able to share calendar +* Able to create layers to manege events +* Able to undo last action
+ +Cons: +* Unable to check off completed event (only reminder can) +* Unable to label event with categories +* Event name gets truncated
+ +``` +###### /UserGuide.md +``` md + +### Adding a task : `add` +Add a task to Task!t.
+Format: **`add`**` [task_name] [optional parameter 1] [optional parameter 2]...`
+ +> ##### Time parameter +> One of the following parameters must be appended to the add command.
+ +> ###### Specifying start/end time: `from ... to ...` +> This parameter is used to indicate the starting and ending datetime of a task.
+> Format: **`from`**` [datetime] `**`to`**` [datetime]`
+ +> > The date for **`from`** can be omitted if it is on the same ending date.
+``` +###### /UserGuide.md +``` md +> Examples: +> * **`add`**` dinner with mom `**`from`**` 19:30 02 oct 2016 `**`to`**` 20:30 02 oct 2016` +> * **`add`**` dinner with mom `**`from`**` 19:30 `**`to`**` 20:30 02 oct 2016` + +> ###### Specifying deadline : `by` +> This parameter is used to indicate the deadline of a task.
+> Format: **`by`**` [datetime]`
+ +> Examples: +> * **`add`**` submit proposal `**`by`**` 23:59 02 oct 2016` + +> ##### Optional parameters +> The following parameters can be appended to the add command.
+> A task can only have one of each type of parameter.
+> A task can only have either start/end time or deadline parameter.
+ +> ###### Specifying location : `at` +> This parameter is used to indicate the venue of a task.
+> Format: **`at`**` [location]`
+ +> Examples: +> * **`add`**` dinner with mom `**`at`**` home` + +> ###### Specifying remarks : `remarks` +> This parameter is used to add remarks for the task.
+> Format: **`remarks`**` [remarks]`
+ +> Examples: +> * **`add`**` dinner with mom `**`remarks`**` buy flowers` + +Examples: +* **`add`**` Prepare meeting agenda `**`by`**` 11:00 7 oct 2016` +* **`add`**` Jimmy's wedding banquet `**`from`**` 19:00 `**`to`**` 22:00 11 nov 2016` **`at`**` Trinity Church` + +### Listing all tasks : `list` +Shows the list of all tasks sorted temporally with the most recent ones displayed first.
+Format: **`list`**` [optional filter]`
+> [optional filter] available:
+> * today - shows the list of tasks for today's date +> * week - shows the list of tasks for this week (from Sunday to Saturday) +> * month - shows the list of tasks for the current month +> * date (e.g. 12 Oct 2016) - shows the list of tasks for the specified date + +Examples: +* **`list`** +* **`list`**` today` +* **`list`**` 12 Oct 2016` + +``` +###### /UserGuide.md +``` md +### Deleting a task : `delete` +Delete the specified tasks from the to-do list.
+Format: **`delete`**` [index 1,index 2,...]` + +> This command is capable to deleting single and multiple tasks. For multiple tasks, the indexes are separated by a comma.
+> Deletes the task 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`**` today`
+ **`delete`**` 2`
+ Deletes the 2nd task in the results of the **`list`** command. +* **`find`**` mom`
+ **`delete`**` 1,4`
+ Deletes the 1st and 4th tasks in the results of the **`find`** command. + +### Marking task as done : `done` +Mark the tasks identified by the index numbers used in the last task listing.
+Format: **`done`**` [index 1,index 2,...]` + +> This command is capable of marking single and multiple tasks. For multiple tasks, the indexes are separated by a comma.
+> Marks the tasks 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`**` today`
+ **`done`**` 2`
+ Marks the 2nd task in the results of the **`list`** command. +* **`find`**` mom`
+ **`done`**` 1,2`
+ Marks the 1st and 2nd tasks in the results of the **`find`** command. + +### Editing a task : `edit` +Edits the task identified by the index number used in the last task listing.
+Format: **`edit`**` [index] [optional parameter 1] [optional parameter 2]...` + +> Edits the tasks specified based on the details given.
+> [optional parameter] follows the format in **`add`** command.
+> The index refers to the index number shown in the most recent listing.
+> The index **must be a positive integer** 1, 2, 3, ...
+``` diff --git a/collated/docs/A0146682X.md b/collated/docs/A0146682X.md new file mode 100644 index 000000000000..cc77472f99e6 --- /dev/null +++ b/collated/docs/A0146682X.md @@ -0,0 +1,69 @@ +# A0146682X +###### /DeveloperGuide.md +``` md +Pros: +* Able to create many layers - folders, lists, tasks, and subtasks, which allow the user to organize their multi-layered tasks systematically +* Able to add alarm to a task +* Able to repeat a task daily, weekly, monthly, or yearly +* Able to add notes to a task +* Able to attach files to a task +* Able to mark a task as complete by a simple click +* Able to unmark a “completed” task by a simple click +* Able to share a task list with team members by name or email address +* Able to sort a list alphabetically, by due date, or creation date +* Includes two tabs of “today” and “week” which highlight the most recent tasks +* Includes a search bar for tasks +* Able to synchronize tasks to cloud +* Able to star a task to mark its importance +* Able to create a task without a date, thus allowing the user to keep track of reminders +* Keeps track of the creation date of a task +* Worded tabs automatically minimize themselves into icons when the window size is reduced, so as to keep the view uncluttered +* Keeps track of completed tasks for reference + +Cons: +* Unable to add the start and end time of an event. User is only allowed to set the deadline of a task +* Unable to undo a deletion of a task +* Unable to set a specific timing of a due date, e.g. 23:59, 6 am etc. +* Slow to open / close the window since the user can only do it by mouse movements +* Slow to resize the window since the user has to do it by mouse movements +* Does not display today’s date +* Does not display a calendar +* Does not allow the addition of venue to a task + +``` +###### /UserGuide.md +``` md +> > A few variations in [datetime] format are accepted:
+> > - Both 24-Hour and AM/PM formats are accepted:
+> > E.g. 19:30 12 oct 2016
+> > E.g. 7:30 PM 12 oct 2016
+> > E.g. 7:30 pm 12 oct 2016
+ +> > - Minutes can be omitted and assumed to be 00:
+> > E.g. 7 pm 12 oct 2016
+ +> > - Month can be in abbreviations, or number separated by slash from day and year:
+> > E.g. 19:30 12 oct 2016
+> > E.g. 19:30 12/10/2016
+ +> > - Year can be omitted and assumed to be the current year:
+> > E.g. 19:30 12/10
+> > E.g. 7:30 pm 12/10
+ +``` +###### /UserGuide.md +``` md +> Leaving the detail blank after entering a parameter identifier deletes the parameter.
+ +Examples: +* **`list`**
+ **`edit`**` 2 dinner with dad`
+ Edits the name of the the 2nd task in the results of the **`list`** command. +* **`find`**` dinner`
+ **`edit`**` 1 `**`from`**` 1830 `**`to`**` 2000 25 oct 2016 `**`at`**` popeyes`
+ Edits the time and location parameter of the 1st task in the results of the **`find`** command. +* **`find`**` dinner`
+ **`edit`**` 1 `**`at`**
+ Deletes the location parameter of the 1st task in the results of the **`find`** command. + +``` diff --git a/collated/docs/A0153736B.md b/collated/docs/A0153736B.md new file mode 100644 index 000000000000..5d53351e42af --- /dev/null +++ b/collated/docs/A0153736B.md @@ -0,0 +1,126 @@ +# A0153736B +###### /DeveloperGuide.md +``` md +#### Use case: View task + +**MSS** + +1. User requests to view tasks for today/this week/this month/a particular date +2. Task!t display the list of tasks for that date sorted by date and/or time
+Use case ends. + +**Extensions** + +2a. The filter provided is invalid +> 2a1. Task!t shows a 'Filter provided is invalid!' message.
+> Use case ends + +2b. The date provided is invalid +> 2b1. Task!t shows a 'Task date provided is invalid!' message.
+> Use case ends + +#### Use case: Find task + +**MSS** + +1. User requests to find tasks by keyword(s) +2. Task!t displays the list of tasks that contains the keyword in the name
+Use case ends. + +**Extensions** + +2a. No parameter entered after command word +> 2a1. Task!t shows a 'invalid command' message and display the expected format.
+> Use case ends + +2b. Findtype entered after command word is invalid +> 2b1. Task!t shows a 'The findtype provided is invalid.' message.
+> Use case ends + +2c. No keywords entered after findtype +> 2c1. Task!t shows a 'Keywords are not provided.' message.
+> Use case ends + +``` +###### /DeveloperGuide.md +``` md +#### Use case: Redo operation + +**MSS** + +1. User requests to redo the last undo operation performed +2. Task!t redo the last undo operation
+Use case ends. + +**Extensions** + +2a. There is no last undo operation +> 2a1. Task!t shows a 'There is no previous undo operation.' message.
+> Use case ends + +2b. The previous operation is not an undo operation +> 2b1. Task!t shows a 'There is no previous undo operation.' message.
+> Use case ends + +``` +###### /DeveloperGuide.md +``` md +#### Cal +Pros: +* With a very convenient interface for setting events’ date and time quickly +* Able to add other participants’ contacts in an event +* Able to send or receive invitation of an event to others directly through the app +* Able to include map when adding location of an event +* Able to provide recommendation when deciding the location of an event +* Able to set alarm to an event +* Able to set repeat to an event +* Able to connect with Facebook and import Facebook friends’ birthdays into the calendar +* Able to sync with another calendar product (eg. Google calendar)
+ +Cons: +* Does not have a task function itself so it requires to work together with another app Any.do +* Unable to show events of the following days directly through the calendar +* Unable to show events that last for a few days in the calendar +* Does not show public holidays in the calendar +* Unable to add additional notes in an event
+ + +``` +###### /UserGuide.md +``` md +### Finding all tasks containing keyword in the name: `find` +Finds tasks whose names contain the given keywords.
+Format: **`find`**` [findtype] [keyword 1 keyword 2 ...]` + +> The search is case insensitive.
+> Only the task name is searched.
+ +> [findtype] available:
+> * either - demands that tasks containing at least one keyword will be returned +> * all - demands that only tasks containing all keywords will be returned +> * phrase - demands that only tasks containing all the keywords in the exact order will be returned + +Examples: +* **`find`**` either mom dad sister`
+ Returns Any task having `mom`, `dad`, or `sister`in its name +* **`find`**` all mom dad sister`
+ Returns `dinner with mom, dad and sister` but not `dinner with mom and dad` +* **`find`**` phrase mom and dad`
+ Returns `dinner with mom and dad` but not `dinner with dad and mom` + +``` +###### /UserGuide.md +``` md +### Undoing last action performed : `undo` +Undo the last action performed in Task!t. Reversible by `redo`
+Format: **`undo`** + +>Undo can be performed multiple times until there are no actions left to undo. + +### Redoing last undo action performed : `redo` +Redo the most recent undo operation done by the user in Task!t.
+Format: **`redo`** + +>If a command is used after undo, all previous undos will no longer be able to be redone. + +``` diff --git a/collated/docs/A0158963M.md b/collated/docs/A0158963M.md new file mode 100644 index 000000000000..24804dcf366d --- /dev/null +++ b/collated/docs/A0158963M.md @@ -0,0 +1,82 @@ +# A0158963M +###### /DeveloperGuide.md +``` md +### Storage component + +
+ +**API** : [`Storage.java`](../src/main/java/seedu/address/storage/Storage.java) + +The `Storage` component, +* can save `UserPref` objects in json format and read it back. +* can save the ToDoList data in xml format and read it back. +``` +###### /DeveloperGuide.md +``` md +#### S Planner + +Pros: +* Able to sync with another calendar product(i.e. Google calendar) +* Able to set notifications +* Able to set repeat +* Able to add notes +* Able to add location +* Able to set start and end time +* Able to set different time zone +* Able to share as text
+ +Cons: +* Unable to search events +* Unable to view event list +* Unable to mark event as done +* Unable to color-code different events
+ +#### Wunderlist +``` +###### /UserGuide.md +``` md +### Setting the storage location : `setstorage` +Sets the location of the storage file.
+Format: **`setstorage`**` [filepath]` + +> Data file in the previously used storage path will be deleted and create a new file in the new path. +>If the folder doesn't exist, creating a new one. + +Examples: +* **`setstorage`**` user/documents/todolist`.
+ +### Clearing all entries : `clear` +Clears all entries from Task!t.
+Format: `clear` + +### Exiting the program : `exit` +Exits the program.
+Format: `exit` + +### Saving the data +Task!t data are saved in the hard disk automatically after any command that changes the data.
+There is no need to save manually. + +## FAQ + +**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 Task!t. + +## Command Summary + +Command | Format +-------- | :-------- +Add | **`add`**` [task_name] [optional parameter 1] [optional parameter 2]...` +Clear | **`clear`** +Delete | **`delete`**` [index 1,index 2,...]` +Done | **`done`**` [index 1,index 2,...]` +Edit | **`edit`**` [index] [optional parameter 1] [optional parameter 2]...` +Exit | **`exit`** +Find | **`find`**` [findtype] [keyword 1 keyword 2 ...]` +List | **`list`**` [filter]` +Help | **`help`** +Set storage | **`setstorage`**` [filepath]` +Undo | **`undo`** +Redo | **`redo`** +``` diff --git a/collated/main/A0138601M.md b/collated/main/A0138601M.md new file mode 100644 index 000000000000..0c73f8a99abf --- /dev/null +++ b/collated/main/A0138601M.md @@ -0,0 +1,1488 @@ +# A0138601M +###### /java/seedu/todolist/logic/commands/AddCommand.java +``` java + @Override + public CommandResult execute() { + assert model != null; + try { + model.addTask(toAdd); + + indicateJumpToListRequestedEvent(); + return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); + } catch (UniqueTaskList.DuplicateTaskException e) { + return new CommandResult(MESSAGE_DUPLICATE_TASK); + } + } + + private void indicateJumpToListRequestedEvent() { + int indexToJump; + if (model.getCurrentTab().equals(MainWindow.TAB_TASK_INCOMPLETE)) { + indexToJump = model.getIndexFromIncompleteList(toAdd); + } else if (model.getCurrentTab().equals(MainWindow.TAB_TASK_OVERDUE)) { + indexToJump = model.getIndexFromOverdueList(toAdd); + } else { + indexToJump = NOT_FOUND; + } + EventsCenter.getInstance().post(new JumpToListRequestEvent(indexToJump)); + } + +} +``` +###### /java/seedu/todolist/logic/commands/DeleteCommand.java +``` java + private int[] targetIndexes; + + public DeleteCommand(int[] targetIndexes) { + this.targetIndexes = targetIndexes; + } + + @Override + public CommandResult execute() { + UnmodifiableObservableList lastShownList = getLastShownList(); + if (!isValidIndexes(lastShownList, targetIndexes)) { + return new CommandResult(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + + ReadOnlyTask[] tasksToDelete = getAllTaskToDelete(lastShownList); + try { + model.deleteTask(tasksToDelete); + } catch (TaskNotFoundException tnfe) { + assert false : "The target task cannot be missing"; + } + + return new CommandResult(MESSAGE_DELETE_TASK_SUCCESS); + } + + /** + * Get the last shown listing from the selected tab + */ + private UnmodifiableObservableList getLastShownList() { + if (model.getCurrentTab().equals(MainWindow.TAB_TASK_COMPLETE)) { + return model.getFilteredCompleteTaskList(); + } else if (model.getCurrentTab().equals(MainWindow.TAB_TASK_INCOMPLETE)) { + return model.getFilteredIncompleteTaskList(); + } else if (model.getCurrentTab().equals(MainWindow.TAB_TASK_OVERDUE)) { + return model.getFilteredOverdueTaskList(); + } else { + assert false : "Last shown list must come from either completed, incomplete or overdue pane"; + return null; + } + } + + /** + * Returns an array of ReadOnlyTask selected using the indexes in the last shown list + */ + private ReadOnlyTask[] getAllTaskToDelete(UnmodifiableObservableList lastShownList) { + ReadOnlyTask[] tasksToDelete = new ReadOnlyTask[targetIndexes.length]; + for (int i = 0; i < targetIndexes.length; i++) { + tasksToDelete[i] = lastShownList.get(targetIndexes[i] - MULTIPLE_DELETE_OFFSET); + } + return tasksToDelete; + } +``` +###### /java/seedu/todolist/logic/commands/DoneCommand.java +``` java +/** + * Marks a task identified using it's last displayed index from the to do list. + */ +public class DoneCommand extends Command { + public static final String COMMAND_WORD = "done"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Marks the task identified using it's last displayed index from the last task listing.\n" + + "Parameters: INDEX (must be a positive integer)\n" + + "Example: " + COMMAND_WORD + " 1 \n"; + + public static final String MESSAGE_MARK_TASK_SUCCESS = "Task marked!"; + public static final String MESSAGE_MARK_COMPLETED_TASK = "This task is already completed!"; + + public static final int MULTIPLE_MARK_OFFSET = 1; + + private int[] targetIndexes; + + public DoneCommand(int[] targetIndexes) { + this.targetIndexes = targetIndexes; + } + + + @Override + public CommandResult execute() { + if (model.getCurrentTab().equals(MainWindow.TAB_TASK_COMPLETE)) { + return new CommandResult(MESSAGE_MARK_COMPLETED_TASK); + } + + UnmodifiableObservableList lastShownList = getLastShownList(); + if (!isValidIndexes(lastShownList, targetIndexes)) { + return new CommandResult(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + + ReadOnlyTask[] tasksToMark = getAllTaskToMark(lastShownList); + try { + model.markTask(tasksToMark); + } catch (TaskNotFoundException pnfe) { + assert false : "The target task cannot be missing"; + } + + return new CommandResult(MESSAGE_MARK_TASK_SUCCESS); + } + + /** + * Get the last shown listing from the selected tab + */ + private UnmodifiableObservableList getLastShownList() { + if (model.getCurrentTab().equals(MainWindow.TAB_TASK_INCOMPLETE)) { + return model.getFilteredIncompleteTaskList(); + } else if (model.getCurrentTab().equals(MainWindow.TAB_TASK_OVERDUE)) { + return model.getFilteredOverdueTaskList(); + } else { + assert false : "Last shown list must come from either incomplete or overdue pane"; + return null; + } + } + + /** + * Returns an array of ReadOnlyTask selected using the indexes in the last shown list + */ + private ReadOnlyTask[] getAllTaskToMark(UnmodifiableObservableList lastShownList) { + ReadOnlyTask[] tasksToMark = new ReadOnlyTask[targetIndexes.length]; + for (int i = 0; i < targetIndexes.length; i++) { + tasksToMark[i] = lastShownList.get(targetIndexes[i] - MULTIPLE_MARK_OFFSET); + } + return tasksToMark; + } +``` +###### /java/seedu/todolist/logic/LogicManager.java +``` java + @Override + public ObservableList getFilteredCompleteTaskList() { + return model.getFilteredCompleteTaskList(); + } + + @Override + public ObservableList getFilteredIncompleteTaskList() { + return model.getFilteredIncompleteTaskList(); + } + + @Override + public ObservableList getFilteredOverdueTaskList() { + return model.getFilteredOverdueTaskList(); + } + + @Override + public void setCurrentTab(String tab) { + model.setCurrentTab(tab); + } + + @Override + public String getCurrentTab() { + return model.getCurrentTab(); + } +} +``` +###### /java/seedu/todolist/logic/parser/CommandParser.java +``` java + /** + * Extracts the new task's start date and time, and end date and time from the add command's interval arguments string. + * @return a string array that contains start date, start time, end date and end time. + */ + private String[] parseInterval(String interval) { + String[] detailedIntervalComponents = new String[DETAILED_INTERVAL_COMPONENT_COUNT]; + if (!Strings.isNullOrEmpty(interval)) { + detailedIntervalComponents = parseTimedInterval(interval); + } + return detailedIntervalComponents; + } + + /** + * Parses the interval that has date and time + * @return a string array that contains start date, start time, end date and end time. + */ + private String[] parseTimedInterval(String interval) { + if (interval.contains(INTERVAL_SEPARATOR)) { + return parseEventInterval(interval); + } else { + return parseDeadlineInterval(interval); + } + } + + /** + * Parses the interval for event type task + * @return a string array that contains start date, start time, end date and end time. + */ + private String[] parseEventInterval(String interval) { + String[] intervalComponents = parseDatetime(interval); + return new String[] { + intervalComponents[DETAILED_INTERVAL_INDEX_STARTDATE], + intervalComponents[DETAILED_INTERVAL_INDEX_STARTTIME], + intervalComponents[DETAILED_INTERVAL_INDEX_ENDDATE], + intervalComponents[DETAILED_INTERVAL_INDEX_ENDTIME]}; + } + + /** + * Parses the interval for deadline type task + * @return a string array that contains start date, start time, end date and end time. + */ + private String[] parseDeadlineInterval(String interval) { + String[] endDateTime = parseDatetime(interval); + return new String[] { + null, + null, + endDateTime[DATETIME_INDEX_DATE], + endDateTime[DATETIME_INDEX_TIME]}; + } + + /** + * Extracts the new task's date and time from the add command's datetime arguments string using Natty. + * @return a string array that contains date and time + */ + private String[] parseDatetime(String datetime) { + String editedDatetime = datetime; + if (isSlashFormat(datetime)) { + editedDatetime = reverseDayAndMonth(datetime); + } + + ArrayList intervalComponents = new ArrayList(); + Parser nattyParser = new Parser(); + DateGroup group = nattyParser.parse(editedDatetime).get(NATTY_INDEX_FIRST); + Calendar currentDateTime = Calendar.getInstance(); //Get current datetime to compare with natty datetime + for (Date date : group.getDates()) { + intervalComponents.add(parseDate(date)); + intervalComponents.add(parseTime(date, currentDateTime)); + + } + return intervalComponents.toArray(new String[intervalComponents.size()]); + } + + /** + * Returns true if a date is in slash format (i.e. 10/12/2016) + */ + private boolean isSlashFormat(String datetime) { + Matcher matcher = Pattern.compile(TaskDate.DATE_VALIDATION_SLASH_REGEX).matcher(datetime.trim()); + return matcher.matches(); + } + + /** + * Reverse day and month for natty parsing + */ + private String reverseDayAndMonth(String datetime) { + String[] dateAndTime = datetime.trim().split(DateParser.DATE_DELIMITER_SLASH); + + StringBuilder builder = new StringBuilder(); + builder.append(dateAndTime[INDEX_MONTH]); + builder.append(DateParser.DATE_DELIMITER_SLASH); + builder.append(dateAndTime[INDEX_DAY]); + builder.append(DateParser.DATE_DELIMITER_SLASH); + builder.append(dateAndTime[INDEX_REMAINDER]); + + return builder.toString(); + } + + /** + * Extracts the date from Natty's dategroup + */ + private String parseDate(Date date) { + DateFormat dateFormat = new SimpleDateFormat(TaskDate.DATE_DISPLAY_FORMAT); + return dateFormat.format(date); + } + + /** + * Extracts the time from Natty's dategroup + */ + private String parseTime(Date date, Calendar currentDateTime) { + DateFormat timeFormat = new SimpleDateFormat(TaskTime.TIME_DISPLAY_FORMAT); + String parsedTime = timeFormat.format(date); + //ignore if it is a time generated by natty + if (timeFormat.format(currentDateTime.getTime()).equals(parsedTime)) { + parsedTime = null; + } + return parsedTime; + } + + /** + * Parses arguments in the context of the done task command. + * + * @param args full command args string + * @return the prepared command + */ + private Command prepareDone(String args) { + int[] indexes; + try { + indexes = parseIndex(args); + } + catch (IllegalValueException ive) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_TASK_DISPLAYED_INDEX, DoneCommand.MESSAGE_USAGE)); + } + return new DoneCommand(indexes); + } + +``` +###### /java/seedu/todolist/logic/parser/CommandParser.java +``` java + /** + * Parses arguments in the context of the delete task command. + * + * @param args full command args string + * @return the prepared command + */ + private Command prepareDelete(String args) { + int[] indexes; + try { + indexes = parseIndex(args); + } + catch (IllegalValueException ive) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE)); + } + return new DeleteCommand(indexes); + } + + /** + * Extract the indexes from a string of command + * + * @throws IllegalValueException if indexes are invalid + * @return an int array if valid indexes are provided. + */ + private int[] parseIndex(String command) throws IllegalValueException { + assert command != null; + int[] indexes = new int[DEFAULT_INDICES_SIZE]; + + if (command.trim().contains(INDEX_DELIMITER)) { + indexes = parseIndexSeparatedByComma(command); + } else { + if (!StringUtil.isUnsignedInteger(command.trim())) { + throw new IllegalValueException(MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + indexes[DEFAULT_FIRST_INDEX] = Integer.parseInt(command.trim()); + } + Arrays.sort(indexes); + return indexes; + } + + /** + * Extract the indexes from a string of command + * + * @param command is guaranteed to contain commas + * @throws IllegalValueException if indexes are invalid + * @return an int array if valid indexes are provided. + */ + private int[] parseIndexSeparatedByComma(String command) throws IllegalValueException { + String Trimmedcommand = command.trim(); + + String[] indexesString = Trimmedcommand.split(INDEX_DELIMITER); + int[] indexes = new int[indexesString.length]; + for (int i = 0; i < indexesString.length; i++) { + if (!StringUtil.isUnsignedInteger(indexesString[i].trim())) { + throw new IllegalValueException(MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + indexes[i] = Integer.parseInt(indexesString[i].trim()); + } + return indexes; + } + +``` +###### /java/seedu/todolist/model/parser/DateParser.java +``` java +/** + * Converts a String to Date and vice versa. + */ +public class DateParser { + + private static final int DATE_COMPONENT_INDEX_YEAR = 2; + private static final int DATE_COMPONENT_INDEX_MONTH = 1; + private static final int DATE_COMPONENT_INDEX_DAY = 0; + + private static final String MONTH_NOT_FOUND = "Unable to find month "; + private static final String[] MONTH_LIST = {"JANUARY", "FEBRUARY", "MARCH", "APRIL", "MAY", "JUNE", "JULY", + "AUGUST", "SEPTEMBER", "OCTOBER", "NOVEMBER", "DECEMBER"}; + private static final int MONTH_DUMMY_VALUE = 0; + private static final int MONTH_OFFSET_INDEX = 1; + + public static final String DATE_DELIMITER_SLASH = "/"; + public static final String DATE_DELIMITER_SPACE = " "; + + + /** + * Parses string date input into LocalDate date. + * + * @param string date input + * @return LocalDate date based on the string date input + */ + public static LocalDate parseDate(String date) throws DateTimeException { + assert date != null; + LocalDate parsedDate; + if (date.contains(DATE_DELIMITER_SLASH)) { + parsedDate = parseDateWithSlash(date); + } else { + parsedDate = parseDateWithMonthName(date); + } + return parsedDate; + } + + /** + * Parses string date input with slash into LocalDate date. + * + * @param string date input with slash + * @return LocalDate date based on the string date input + */ + private static LocalDate parseDateWithSlash(String date) throws DateTimeException { + String[] dateComponents = date.split(DATE_DELIMITER_SLASH); + + int day, month, year; + day = Integer.parseInt(dateComponents[DATE_COMPONENT_INDEX_DAY]); + month = Integer.parseInt(dateComponents[DATE_COMPONENT_INDEX_MONTH]); + year = Integer.parseInt(dateComponents[DATE_COMPONENT_INDEX_YEAR]); + + return LocalDate.of(year, month, day); + } + + /** + * Parses string date input with month name into LocalDate date. + * + * @param string date input with month name + * @return LocalDate date based on the string date input + */ + private static LocalDate parseDateWithMonthName(String date) throws DateTimeException { + String[] dateComponents = date.split(DATE_DELIMITER_SPACE); + + int day, month, year; + day = Integer.parseInt(dateComponents[DATE_COMPONENT_INDEX_DAY]); + month = getMonthValue(dateComponents[DATE_COMPONENT_INDEX_MONTH]); + year = Integer.parseInt(dateComponents[DATE_COMPONENT_INDEX_YEAR]); + + return LocalDate.of(year, month, day); + } + + /** + * Parses string month name into int month value. + * + * @param string month name + * @return int month value based on string month name + */ + private static int getMonthValue(String month) { + int monthValue = MONTH_DUMMY_VALUE; + for (int i = 0; i < MONTH_LIST.length; i++) { + if (MONTH_LIST[i].contains(month.toUpperCase())) { + monthValue = i + MONTH_OFFSET_INDEX; + } + } + if (monthValue == MONTH_DUMMY_VALUE) { + throw new DateTimeException(MONTH_NOT_FOUND + month); + } + return monthValue; + } +} +``` +###### /java/seedu/todolist/model/parser/TimeParser.java +``` java +/** + * Converts a String to Time and vice versa. + */ +public class TimeParser { + + private static final int TIME_COMPONENT_TOTAL = 2; + private static final int TIME_COMPONENT_INDEX_MINUTE = 1; + private static final int TIME_COMPONENT_INDEX_HOUR = 0; + private static final int TIME_COMPONENT_HOUR_START_INDEX = 0; //Position of the start of hour in a string + private static final int TIME_COMPONENT_PERIOD_LENGTH = 2; //am and pm are 2 characters long + private static final int TIME_COMPONENT_MINUTE_DEFAULT = 0; + private static final int TIME_COMPONENT_PERIOD_OFFSET = 12; + + private static final String TIME_DELIMITER = ":"; + + /** + * Parses string time input into LocalTime time. + * + * @param string time input + * @return LocalTime time based on the string time input + */ + public static LocalTime parseTime(String time) throws DateTimeException { + assert time != null; + LocalTime parsedTime; + + //trim all spaces away + String trimmedTime = time.replaceAll("\\s+", ""); + + if (trimmedTime.toUpperCase().contains("AM")) { + parsedTime = parseTimeWithAMFormat(trimmedTime); + } else if (time.toUpperCase().contains("PM")) { + parsedTime = parseTimeWithPMFormat(trimmedTime); + } else { + parsedTime = parseTimeWithContinentalFormat(trimmedTime); + } + + return parsedTime; + } + + /** + * Parses string time input with AM format into LocalTime time. + * + * @param string time input with AM format + * @return LocalTime time based on the string time input + */ + private static LocalTime parseTimeWithAMFormat(String time) throws DateTimeException { + String timeWithoutPeriod = removePeriod(time); + String[] timeComponents = timeWithoutPeriod.split(":"); + + int hour, minute; + if (timeComponents.length < TIME_COMPONENT_TOTAL) { + hour = Integer.parseInt(timeWithoutPeriod); + minute = TIME_COMPONENT_MINUTE_DEFAULT; + } else { + hour = Integer.parseInt(timeComponents[TIME_COMPONENT_INDEX_HOUR]); + minute = Integer.parseInt(timeComponents[TIME_COMPONENT_INDEX_MINUTE]); + } + + if (hour == TIME_COMPONENT_PERIOD_OFFSET) { + hour = hour - TIME_COMPONENT_PERIOD_OFFSET; + } + + return LocalTime.of(hour, minute); + } + + /** + * Parses string time input with PM format into LocalTime time. + * + * @param string time input with PM format + * @return LocalTime time based on the string time input + */ + private static LocalTime parseTimeWithPMFormat(String time) throws DateTimeException { + String timeWithoutPeriod = removePeriod(time); + String[] timeComponents = timeWithoutPeriod.split(TIME_DELIMITER); + + int hour, minute; + if (timeComponents.length < TIME_COMPONENT_TOTAL) { + hour = Integer.parseInt(timeWithoutPeriod); + minute = TIME_COMPONENT_MINUTE_DEFAULT; + } else { + hour = Integer.parseInt(timeComponents[TIME_COMPONENT_INDEX_HOUR]); + minute = Integer.parseInt(timeComponents[TIME_COMPONENT_INDEX_MINUTE]); + } + + if (hour != TIME_COMPONENT_PERIOD_OFFSET) { + hour = hour + TIME_COMPONENT_PERIOD_OFFSET; + } + return LocalTime.of(hour, minute); + } + + private static String removePeriod(String time) { + return time.substring(TIME_COMPONENT_HOUR_START_INDEX, time.length() - TIME_COMPONENT_PERIOD_LENGTH); + } + + /** + * Parses string time input with continental (24hr) format into LocalTime time. + * + * @param string time input with 24hr format + * @return LocalTime time based on the string time input + */ + private static LocalTime parseTimeWithContinentalFormat(String time) throws DateTimeException { + String[] timeComponents = time.split(":"); + + int hour = Integer.parseInt(timeComponents[TIME_COMPONENT_INDEX_HOUR]); + int minute = Integer.parseInt(timeComponents[TIME_COMPONENT_INDEX_MINUTE]); + + return LocalTime.of(hour, minute); + } + +} +``` +###### /java/seedu/todolist/model/task/Interval.java +``` java +public class Interval implements Comparable { + + public static final String MESSAGE_INTERVAL_CONSTRAINTS_DATE = "End date cannot be earlier than start date"; + public static final String MESSAGE_INTERVAL_CONSTRAINTS_TIME = "End time cannot be earlier than start time"; + + private TaskDate startDate; + private TaskTime startTime; + private TaskDate endDate; + private TaskTime endTime; + + + public Interval() { + + } + + /** + * Constructs and validates the Interval based on which parameters given is not null. + * + * Constructs an event type interval if all parameters are not null. + * Constructs an deadline with time type interval if only endDate and endTime are not null. + * Constructs an deadline without time type interval if only endDate is not null. + * Constructs an float type interval if all parameters are null. + * + * @throws IllegalValueException if given date or time is invalid or set of date and time is an invalid interval. + */ + public Interval(String startDate, String startTime, String endDate, String endTime) throws IllegalValueException { + if (isEvent(startDate, startTime, endDate, endTime)) { + initEvent(startDate, startTime, endDate, endTime); + } else if (isDeadlineWithTime(startDate, startTime, endDate, endTime)) { + initDeadlineWithTime(endDate, endTime); + } else if (isDeadlineWithoutTime(startDate, startTime, endDate, endTime)) { + initDeadlineWithoutTime(endDate); + } else if (isFloat(startDate, startTime, endDate, endTime)) { + initFloat(); + } else { + assert false : "A given interval must represent an event, deadline, or float"; + } + } + + /** + * Initialises an event type interval. + * + * @throws IllegalValueException if given date or time is invalid or set of date and time is an invalid event interval. + * For example, endDate earlier than startDate or endTime earlier than startTime. + */ + private void initEvent(String startDate, String startTime, String endDate, String endTime) throws IllegalValueException { + this.startDate = new TaskDate(startDate); + this.startTime = new TaskTime(startTime); + this.endDate = new TaskDate(endDate); + this.endTime = new TaskTime(endTime); + + if (!isValidDateInterval(this.startDate, this.endDate)) { + throw new IllegalValueException(MESSAGE_INTERVAL_CONSTRAINTS_DATE); + } + + if (!isValidTimeInterval(this.startDate, this.startTime, this.endDate, this.endTime)) { + throw new IllegalValueException(MESSAGE_INTERVAL_CONSTRAINTS_TIME); + } + } + + /** + * Initialises a deadline with time type interval. + * + * @throws IllegalValueException if given date or time is invalid + */ + private void initDeadlineWithTime(String endDate, String endTime) throws IllegalValueException { + this.endDate = new TaskDate(endDate); + this.endTime = new TaskTime(endTime); + } + + /** + * Initialises a deadline without time type interval. + * + * @throws IllegalValueException if given date is invalid + */ + private void initDeadlineWithoutTime(String endDate) throws IllegalValueException { + this.endDate = new TaskDate(endDate); + } + + /** + * Initialises a float type interval. + */ + private void initFloat() { + this.startDate = null; + this.startTime = null; + this.endDate = null; + this.endTime = null; + } + + /** + * Returns true if a given interval has a valid task date interval. (i.e. startDate is earlier than endDate) + */ + private boolean isValidDateInterval(TaskDate startDate, TaskDate endDate) { + return !endDate.isBefore(startDate); + } + + /** + * Returns true if a given interval has a valid task time interval. + * if startDate and endDate are not equal, it is assume to be a valid interval. (i.e. startDate is earlier than endDate) + */ + private boolean isValidTimeInterval(TaskDate startDate, TaskTime startTime, TaskDate endDate, TaskTime endTime) { + return !startDate.equals(endDate) || !endTime.isBefore(startTime); + } + + /** + * Returns true if interval is earlier than current datetime + */ + public boolean isOver() { + Interval now = new Interval(); + now.endDate = TaskDate.now(); + now.endTime = TaskTime.now(); + return this.compareTo(now) < 0; + } + + /** + * Returns true if a given set of datetime is an event. + */ + private boolean isEvent(String startDate, String startTime, String endDate, String endTime) { + return startDate != null + && startTime != null + && endDate != null + && endTime != null; + } + + /** + * Returns true if the interval object is an event. + */ + public boolean isEvent() { + return this.startDate != null + && this.startTime != null + && this.endDate != null + && this.endTime != null; + } + + /** + * Returns true if a given set of datetime is a deadline with time. + */ + private boolean isDeadlineWithTime(String startDate, String startTime, String endDate, String endTime) { + return startDate == null + && startTime == null + && endDate != null + && endTime != null; + } + + /** + * Returns true if the interval object is a deadline with time. + */ + public boolean isDeadlineWithTime() { + return this.startDate == null + && this.startTime == null + && this.endDate != null + && this.endTime != null; + } + + /** + * Returns true if a given set of datetime is a deadline without time. + */ + private boolean isDeadlineWithoutTime(String startDate, String startTime, String endDate, String endTime) { + return startDate == null + && startTime == null + && endDate != null + && endTime == null; + } + + /** + * Returns true if the interval object is a deadline without time. + */ + public boolean isDeadlineWithoutTime() { + return this.startDate == null + && this.startTime == null + && this.endDate != null + && this.endTime == null; + } + + /** + * Returns true if a given interval is a floating task + */ + private boolean isFloat(String startDate, String startTime, String endDate, String endTime) { + return startDate == null + && startTime == null + && endDate == null + && endTime == null; + } + + /** + * Returns true if the interval object is a floating task + */ + public boolean isFloat() { + return this.startDate == null + && this.startTime == null + && this.endDate == null + && this.endTime == null; + } + + public TaskDate getStartDate() { + return this.startDate; + } + + public TaskTime getStartTime() { + return this.startTime; + } + + public TaskDate getEndDate() { + return this.endDate; + } + + public TaskTime getEndTime() { + return this.endTime; + } + + /** + * Returns a formatted string based on the interval type. + * + * Examples: + * Event type - 20 Nov 2016, 3:00PM to 21 Nov 2016, 4:00PM + * Deadline with time type - 20 Nov 2016, 3:00PM + * Deadline without time type - 20 Nov 2016 + */ + public String toString() { + if (this.isEvent()) { + return formatStartDateTime() + " to " + formatEndDateTime(); + } else if (this.isDeadlineWithTime()) { + return formatEndDateTime(); + } else if (this.isDeadlineWithoutTime()) { + return formatEndDate(); + } else { + return null; + } + } + + public String formatStartDateTime() { + return startDate + ", " + startTime; + } + + public String formatEndDateTime() { + return endDate + ", " + endTime; + } + + public String formatEndDate() { + return endDate + ""; + } + + @Override + public int compareTo(Interval interval) { + TaskDate firstDate = this.getDateToCompare(); + TaskDate secondDate = interval.getDateToCompare(); + TaskTime firstTime = this.getTimeToCompare(); + TaskTime secondTime = interval.getTimeToCompare(); + + //If both are float task, they are equal + if (isFloatTask(firstDate) && isFloatTask(secondDate)) { + return 0; + } + + return compareDateTime(firstDate, secondDate, firstTime, secondTime); + } + + /** + * Returns true if TaskDate is null + */ + private boolean isFloatTask(TaskDate date) { + return date == null; + } + + /** + * Returns true is TaskTime is not null + */ + private boolean isTimedTask(TaskTime time) { + return time != null; + } + + /** + * Compares the 2 date-times and determines the ordering + * firstDate and secondDate cannot both be Float Tasks + */ + private int compareDateTime(TaskDate firstDate, TaskDate secondDate, TaskTime firstTime, TaskTime secondTime) { + assert !isFloatTask(firstDate) || !isFloatTask(secondDate); + + int compare; + if (!isFloatTask(firstDate)) { + compare = compareSecondDate(firstDate, secondDate, firstTime, secondTime); + } else { + compare = 1; + } + return compare; + } + + /** + * Compare the secondDate. firstDate must not be null + * Returns 1 if first date-time is later than second date-time + * Returns 0 if both date-times are equal + * Returns -1 otherwise + */ + private int compareSecondDate(TaskDate firstDate, TaskDate secondDate, TaskTime firstTime, TaskTime secondTime) { + assert firstDate != null; + + if (isFloatTask(secondDate)) { + return -1; + } else { + if (firstDate.equals(secondDate)) { //if date are the same, determine order with time + return compareTime(firstTime, secondTime); + } + return firstDate.compareTo(secondDate); + } + } + + /** + * Compares the firstTime and secondTime + * Returns 1 if first time is later than second time + * Returns 0 if both times are equal + * Returns -1 otherwise + */ + private int compareTime(TaskTime firstTime, TaskTime secondTime) { + if (isTimedTask(firstTime)) { + return firstTime.compareTo(secondTime); + } else if (isTimedTask(secondTime)) { + return -secondTime.compareTo(firstTime); //flip comparison + } else { + return 0; + } + } + + /** + * Returns the date to be used for comparison. + */ + private TaskDate getDateToCompare() { + if (this.startDate != null) { + return this.startDate; + } else if (this.endDate != null) { + return this.endDate; + } else { + return null; + } + } + + /** + * Returns the time to be used for comparison. + */ + private TaskTime getTimeToCompare() { + if (this.startTime != null) { + return this.startTime; + } else if (this.endTime != null) { + return this.endTime; + } else { + return null; + } + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Interval // instanceof handles nulls + && TaskDate.isEquals(this.startDate, ((Interval) other).startDate) + && TaskTime.isEquals(this.startTime, ((Interval) other).startTime) + && TaskDate.isEquals(this.endDate, ((Interval) other).endDate) + && TaskTime.isEquals(this.endTime, ((Interval) other).endTime)); + } + +} +``` +###### /java/seedu/todolist/model/task/Status.java +``` java +/** + * Represents a Task's status in the to do list. + * Guarantees: is one of the three values 'complete', 'incomplete' or 'overdue' + */ +public class Status { + public enum Type { + Incomplete("incomplete"), + Complete("complete"), + Overdue("overdue"); + + String statusType; + Type(String type) { + statusType = type; + } + + @Override + public String toString() { + return statusType; + } + } + + private Type status; + + /** + * Constructs a Status object with the default status of type incomplete + */ + public Status() { + setStatus(Status.Type.Incomplete); + } + + /** + * Constructs a Status object with the specified status type + */ + public Status(Type status) { + setStatus(status); + } + + /** + * Constructs a Status object with the specified string name of the type + */ + public Status(String status) { + assert status != null; + + if (status.equals(Type.Incomplete.toString())) { + setStatus(Type.Incomplete); + } else if (status.equals(Type.Complete.toString())) { + setStatus(Type.Complete); + } else if (status.equals(Type.Overdue.toString())) { + setStatus(Type.Overdue); + } else { + assert false : "Status must be either incomeplete, complete or overdue"; + } + } + + public Type getType() { + return this.status; + } + + public void setStatus(Type status) { + this.status = status; + } + + /** + * Returns true if the status is incomplete + */ + public boolean isIncomplete() { + return this.status.equals(Status.Type.Incomplete); + } + + /** + * Returns true if the status is complete + */ + public boolean isComplete() { + return this.status.equals(Status.Type.Complete); + } + + /** + * Returns true if the status is overdue + */ + public boolean isOverdue() { + return this.status.equals(Status.Type.Overdue); + } + + @Override + public String toString() { + return status.toString(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Status // instanceof handles nulls + && this.status == ((Status) other).status); // state check + } + +} +``` +###### /java/seedu/todolist/model/task/TaskTime.java +``` java +/** + * Represents a Task's time in the to do list. + * Guarantees: immutable; is valid as declared in {@link #isValidTime(String)} + */ +public class TaskTime implements Comparable { + + public static final String MESSAGE_TIME_CONSTRAINTS = "Task time should be in 24-hr format or AM/PM format"; + public static final String MESSAGE_TIME_INVALID = "Task time provided is invalid!"; + + //format: 24-hr + public static final String TIME_VALIDATION_REGEX_2 = "(\\p{Digit}){1,2}:(\\p{Digit}){2}"; + //format: AM/PM + public static final String TIME_VALIDATION_REGEX_1 = "(\\p{Digit}){1,2}(:(\\p{Digit}){2})?\\s?[AaPp][Mm]"; + public static final String TIME_VALIDATION_REGEX_FORMAT = TIME_VALIDATION_REGEX_1 + "|" + TIME_VALIDATION_REGEX_2; + + public static final String TIME_DISPLAY_FORMAT = "h:mma"; + + private LocalTime time; + + public TaskTime() { + + } + + /** + * Validates given time. + * + * @throws IllegalValueException if given date is invalid. + */ + public TaskTime(String time) throws IllegalValueException { + assert time != null; + String trimmedTime = time.trim(); + if (!isValidTime(trimmedTime)) { + throw new IllegalValueException(MESSAGE_TIME_CONSTRAINTS); + } + + try { + this.time = TimeParser.parseTime(trimmedTime); + } catch (DateTimeException dateTimeException) { + throw new IllegalValueException(MESSAGE_TIME_INVALID); + } + } + + /** + * Returns true if a given string is a valid task time. + */ + public static boolean isValidTime(String test) { + return test.matches(TIME_VALIDATION_REGEX_FORMAT); + } + + /** + * Returns true if this time is earlier than given time. + */ + public boolean isBefore(TaskTime other) { + return this.time.isBefore(other.getTime()); + } + + public LocalTime getTime() { + return this.time; + } + + /** + * Return the current time + */ + public static TaskTime now() { + TaskTime now = new TaskTime(); + now.time = LocalTime.now(); + return now; + } + + @Override + public String toString() { + return time.format(DateTimeFormatter.ofPattern(TIME_DISPLAY_FORMAT)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof TaskTime // instanceof handles nulls + && this.time.equals(((TaskTime) other).time)); // state check + } + + /** + * Returns true if both TaskTimes are equal. + * Use this method when both TaskTimes could be null + */ + public static boolean isEquals(TaskTime time, TaskTime other) { + if (time == null && other == null) { + //both are null, they are equal + return true; + } + + if (time != null) { + return time.equals(other); + } else { + // if date is null, other cannot be null. + // thus, they are not equal + return false; + } + } + + @Override + public int compareTo(TaskTime time) { + if (time == null) return -1; + + if (this.equals(time)) { + return 0; + } else if (this.isBefore(time)) { + return -1; + } else { + return 1; + } + } +} +``` +###### /java/seedu/todolist/model/task/UniqueTaskList.java +``` java + /** + * Marks the equivalent task(s) in the list. + * + * @throws TaskNotFoundException if no such task could be found in the list. + */ + public boolean mark(ReadOnlyTask... toMark) throws TaskNotFoundException { + assert toMark != null; + boolean taskFound = false; + for (ReadOnlyTask task : toMark) { + taskFound = (internalList.indexOf(task) != -1); + if (!taskFound) { + throw new TaskNotFoundException(); + } + Task taskMarked = new Task(task.getName(), task.getInterval(), task.getLocation(), task.getRemarks(), new Status(Status.Type.Complete)); + internalList.set(internalList.indexOf(task), taskMarked); + } + Collections.sort(internalList); + return taskFound; + } + +``` +###### /java/seedu/todolist/ui/TaskCard.java +``` java + @FXML + public void initialize() { + id.setText(displayedIndex + ". "); + name.setText(task.getName().fullName); + formatIntervalField(); + formatLocationField(); + formatRemarksField(); + } + + /** + * Display the fields accordingly based on the type of task. + */ + private void formatIntervalField() { + if (task.getInterval().isFloat()){ + intervalIcon.setVisible(false); + startDate.setText(""); + endDate.setText(""); + } else if (task.getInterval().isDeadlineWithTime()) { + intervalIcon.setImage(AppUtil.getImage(ICON_ALARM_CLOCK)); + startDate.setManaged(false); + endDate.setText(task.getInterval().formatEndDateTime()); + } else if (task.getInterval().isDeadlineWithoutTime()) { + intervalIcon.setImage(AppUtil.getImage(ICON_ALARM_CLOCK)); + startDate.setManaged(false); + endDate.setText(task.getInterval().formatEndDate()); + } else { + intervalIcon.setImage(AppUtil.getImage(ICON_HOURGLASS)); + startDate.setText(task.getInterval().formatStartDateTime()); + endDate.setText(task.getInterval().formatEndDateTime()); + } + + } + + private void formatLocationField() { + if (!Strings.isNullOrEmpty(task.getLocation().location)) { + locationIcon.setVisible(true); + } + locationParam.setText(task.getLocation().toString()); + } + + private void formatRemarksField() { + if (!Strings.isNullOrEmpty(task.getRemarks().remarks)) { + remarksIcon.setVisible(true); + } + remarks.setText(task.getRemarks().toString()); + } + +``` +###### /resources/view/DarkTheme.css +``` css +.list-view { + -fx-background-color: #cccccc; + -fx-padding: 3; +} + +.list-cell { + -fx-background-color: #eeeeee; + -fx-background-radius: 10; + -fx-border-width: 2px; + -fx-border-color: #cccccc; + -fx-border-radius: 8; +} + +.list-cell:empty { + -fx-background-color: transparent; +} + +.list-cell:filled:selected:focused, .list-cell:filled:selected { + -fx-background-color: #dddddd; +} + +.list-cell .label { + -fx-text-fill: #111111; +} + +``` +###### /resources/view/DarkTheme.css +``` css +#cardPane { + -fx-background-color: transparent; +} + +.tab { + -fx-focus-color: transparent; + -fx-background-color: transparent; + -fx-opacity: 0.7; + -fx-padding: 20 20 5 60; +} + +.tab .tab-label { + -fx-alignment: CENTER; + -fx-font-family: "Segoe UI Semibold"; + -fx-text-fill: #bab6b6; + -fx-font-size: 11pt; +} + +.tab:selected { + -fx-opacity: 1.0; +} + +.tab:selected .tab-label { + -fx-alignment: CENTER; + -fx-text-fill: #ffffff; +} + +#notificationPane .title{ + -fx-background-color: transparent; + -fx-padding: 25 20 10 60; + -fx-background-image: url('../images/notification_title.png'); + -fx-background-repeat: no-repeat; + -fx-background-size: 170; +} + +#incompleteTab { + -fx-background-image: url('../images/incomplete_tab_title.png'); + -fx-background-size: cover; +} + +#completedTab { + -fx-translate-x: -1; + -fx-background-image: url('../images/completed_tab_title.png'); + -fx-background-size: cover; +} + +#overdueTab { + -fx-translate-x: -1; + -fx-padding: 20 30 5 70; + -fx-background-image: url('../images/overdue_tab_title.png'); + -fx-background-size: cover; +} + +#taskListPanelPlaceholder { + -fx-background-color: #ffc425; + -fx-background-radius: 0 3 3 3; +} + +#completeTaskListPanelPlaceholder { + -fx-background-color: #00b159; + -fx-background-radius: 0 3 3 3; +} + +#overdueTaskListPanelPlaceholder { + -fx-background-color: #d11141; + -fx-background-radius: 0 3 3 3; +} + +#commandPaneTitleLabel { + -fx-background-image: url('../images/command_pane_title.png'); + -fx-background-repeat: no-repeat; + -fx-background-size: 170; + -fx-text-fill: #ffffff; + -fx-padding: 25 30 10 70; +} + +#resultDisplayPlaceholder, #commandBoxPlaceholder { + -fx-background-color: #f37735; + -fx-background-radius: 0 3 3 3; +} +``` +###### /resources/view/MainWindow.fxml +``` fxml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +``` +###### /resources/view/TaskListCard.fxml +``` fxml + + + + + + + + + + + + + + + + + + + +``` diff --git a/collated/main/A0146682X.md b/collated/main/A0146682X.md new file mode 100644 index 000000000000..0413dd1bef60 --- /dev/null +++ b/collated/main/A0146682X.md @@ -0,0 +1,244 @@ +# A0146682X +###### /java/seedu/todolist/logic/commands/EditCommand.java +``` java +/** + * Edits the information of an existing task. + */ + +public class EditCommand extends Command { + + public static final String COMMAND_WORD = "edit"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits a task in the list displayed. " + + "Parameters: [index] [NAME] [from DATETIME] [to DATETIME] [at LOCATION] [remarks REMARKS] \n" + + "Example: " + COMMAND_WORD + + " 1 dinner with mom from 13 oct 2016 7pm to 13 oct 2016 8pm at home remarks buy fruits"; + + public static final String MESSAGE_SUCCESS = "Task edited: %1$s"; + + private final int targetIndex; + + private String name; + private String startDate; + private String startTime; + private String endDate; + private String endTime; + private String location; + private String remarks; + + public EditCommand(int targetIndex, String name, String startDate, String startTime, String endDate, String endTime, + String location, String remarks) throws IllegalValueException { + this.targetIndex = targetIndex; + + this.name = name; + this.startDate = startDate; + this.startTime = startTime; + this.endDate = endDate; + this.endTime = endTime; + this.location = location; + this.remarks = remarks; + } + + @Override + public CommandResult execute() { + + UnmodifiableObservableList lastShownList; + + if (model.getCurrentTab().equals(MainWindow.TAB_TASK_COMPLETE)) { + lastShownList = model.getFilteredCompleteTaskList(); + } + else { + lastShownList = model.getFilteredIncompleteTaskList(); + } + + if (targetIndex < 1 || lastShownList.size() < targetIndex) { + indicateAttemptToExecuteIncorrectCommand(); + return new CommandResult(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + + ReadOnlyTask taskToEdit = lastShownList.get(targetIndex - 1); + + name = name.trim(); + + if(name==null||name.isEmpty()) { + name = taskToEdit.getName().toString(); + } + + Interval originalInterval = taskToEdit.getInterval(); + + if (originalInterval != null) { + TaskDate originalStartDate = originalInterval.getStartDate(); + if(originalStartDate!=null && startDate==null) startDate = originalStartDate.toString(); + TaskTime originalStartTime = originalInterval.getStartTime(); + if(originalStartTime!=null && startTime==null) startTime = originalStartTime.toString(); + TaskDate originalEndDate = originalInterval.getEndDate(); + if(originalEndDate!=null && endDate==null) endDate = originalEndDate.toString(); + TaskTime originalEndTime = originalInterval.getEndTime(); + if(originalEndTime!=null && endTime==null) endTime = originalEndTime.toString(); + } + + Location originalLocation = taskToEdit.getLocation(); + if(originalLocation != null && location==null) location = originalLocation.toString(); + + Remarks originalRemarks = taskToEdit.getRemarks(); + if(originalRemarks != null && remarks==null) remarks = originalRemarks.toString(); + + Status originalStatus = taskToEdit.getStatus(); + + Task replacement; + try { + replacement = new Task( + new Name(name), + new Interval(startDate, startTime, endDate, endTime), + new Location(location), + new Remarks(remarks), + new Status(originalStatus.toString()) + ); + } catch (IllegalValueException ive) { + return new CommandResult(ive.getMessage()); + } + + try { + model.editTask(taskToEdit, replacement); + } catch (TaskNotFoundException pnfe) { + assert false : "The target task cannot be missing"; + } + + return new CommandResult(String.format(MESSAGE_SUCCESS, replacement)); + } +} +``` +###### /java/seedu/todolist/logic/parser/CommandParser.java +``` java + /** + * Parses arguments in the context of the edit task command. + * + * @param args full command args string + * @return the prepared command + */ + private Command prepareEdit(String args) { + + Matcher index_matcher = Pattern.compile("\\d+").matcher(args); + index_matcher.find(); + + int index; + + try { + index = Integer.valueOf(index_matcher.group()); + } catch (IllegalStateException e) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE)); + } + + //split command into its index (content[0]) and the string that follows (content[1]) + String[] content = args.split("\\d+", 2); + + //verify the format of the string command is correct + final Matcher matcher = EDIT_DATA_ARGS_FORMAT.matcher(content[1]); + + if (!matcher.matches()) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE)); + } + + String name = matcher.group("name"); + String[] interval = parseInterval(matcher.group("interval")); + String location = matcher.group("location"); + String remarks = matcher.group("remarks"); + + try { + return new EditCommand(index, name, interval[0], interval[1], interval[2], interval[3], location, remarks); + } catch (IllegalValueException ive) { + return new IncorrectCommand(ive.getMessage()); + } + } + +``` +###### /java/seedu/todolist/model/ModelManager.java +``` java + public synchronized void editTask(ReadOnlyTask target, Task replacement) throws TaskNotFoundException { + ToDoList previousToDoList = new ToDoList(this.ToDoList); + ToDoList.editTask(target, replacement); + updateToDoListHistory(previousToDoList); + indicateToDoListChanged(); + } + +``` +###### /java/seedu/todolist/model/task/UniqueTaskList.java +``` java + /** + * Edits the equivalent task in the list. + * + * @throws TaskNotFoundException if no such task could be found in the list. + */ + public boolean edit(ReadOnlyTask toEdit, Task replacement) throws TaskNotFoundException { + assert toEdit != null; + final boolean taskFound = (internalList.indexOf(toEdit) != -1); + internalList.set(internalList.indexOf(toEdit), replacement); + if (!taskFound) { + throw new TaskNotFoundException(); + } + return taskFound; + } + +``` +###### /java/seedu/todolist/model/ToDoList.java +``` java + /** + * Edits a task in the to-do list + */ + public boolean editTask(ReadOnlyTask key, Task replacement) throws UniqueTaskList.TaskNotFoundException { + if (tasks.edit(key, replacement)) { + return true; + } else { + throw new UniqueTaskList.TaskNotFoundException(); + } + } + //@author + + public boolean markTask(ReadOnlyTask... keys) throws UniqueTaskList.TaskNotFoundException { + if (tasks.mark(keys)) { + return true; + } else { + throw new UniqueTaskList.TaskNotFoundException(); + } + } + + public boolean removeTask(ReadOnlyTask... keys) throws UniqueTaskList.TaskNotFoundException { + if (tasks.remove(keys)) { + return true; + } else { + throw new UniqueTaskList.TaskNotFoundException(); + } + } + +//// util methods + + @Override + public String toString() { + return tasks.getInternalList().size() + " tasks"; + // TODO: refine later + } + + @Override + public List getTaskList() { + return Collections.unmodifiableList(tasks.getInternalList()); + } + + @Override + public UniqueTaskList getUniqueTaskList() { + return this.tasks; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof ToDoList // instanceof handles nulls + && this.tasks.equals(((ToDoList) other).tasks)); + } + + @Override + public int hashCode() { + // use this method for custom fields hashing instead of implementing your own + return Objects.hash(tasks); + } +} +``` diff --git a/collated/main/A0153736B.md b/collated/main/A0153736B.md new file mode 100644 index 000000000000..52fc4f70ad7e --- /dev/null +++ b/collated/main/A0153736B.md @@ -0,0 +1,487 @@ +# A0153736B +###### /java/seedu/todolist/logic/commands/FindCommand.java +``` java +/** + * Finds and lists all tasks in to-do list whose name contains any/all/phrase of the argument keywords. + * Keyword matching is case insensitive. + */ +public class FindCommand extends Command { + + public static final String COMMAND_WORD = "find"; + + public static final String MESSAGE_INVALID_FINDTYPE = "The findtype provided is invalid."; + + public static final String MESSAGE_KEYWORDS_NOT_PROVIDED = "Keywords are not provided."; + + public static final String FINDTYPE_EITHER = "either"; + public static final String FINDTYPE_ALL = "all"; + public static final String FINDTYPE_PHRASE = "phrase"; + public static final String VALID_FINDTYPE = FINDTYPE_EITHER + "|" + FINDTYPE_ALL + "|" + FINDTYPE_PHRASE; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all tasks whose names contain any/all/phrase of " + + "the specified keywords (case-insensitive) and displays them as a list with index numbers.\n" + + "Parameters: FINDTYPE KEYWORD [MORE_KEYWORDS]...\n" + + "Example: " + COMMAND_WORD + " " + FINDTYPE_PHRASE + " alice bob charlie"; + + private final List keywords; + private final String findType; + + public FindCommand(List keywords, String findType) { + this.keywords = keywords; + this.findType = findType; + } + + @Override + public CommandResult execute() { + if (!findType.matches(VALID_FINDTYPE)) + return new CommandResult(MESSAGE_INVALID_FINDTYPE); + if (keywords.isEmpty()) + return new CommandResult(MESSAGE_KEYWORDS_NOT_PROVIDED); + + assert model != null; + model.updateFilteredTaskList(keywords, findType); + return new CommandResult(getMessageForTaskListShownSummary(model.getFilteredAllTaskList().size())); + } + +} +``` +###### /java/seedu/todolist/logic/commands/ListCommand.java +``` java +/** + * Lists tasks in the to-do list to the user according to the dateFilter provided. + */ +public class ListCommand extends Command { + + public static final String COMMAND_WORD = "list"; + + public static final String MESSAGE_ALLTASKS_SUCCESS = "Listed all tasks"; + public static final String MESSAGE_FILTER_SUCCESS = "Listed filtered tasks"; + public static final String MESSAGE_FILTER_INVALID = "Filter provided is invalid!"; + + public static final String FILTER_TODAY = "today"; + public static final String FILTER_WEEK = "week"; + public static final String FILTER_MONTH = "month"; + public static final String VALID_FILTER = FILTER_TODAY + "|" + FILTER_WEEK + "|" + FILTER_MONTH; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + + "Displays the list of tasks corresponding to the filter provided.\n" + + "Parameter: [FILTER]\n" + + "Example: " + COMMAND_WORD + " " + FILTER_TODAY; + + private final String dateFilter; + + public ListCommand(String dateFilter) { + this.dateFilter = dateFilter; + } + + @Override + public CommandResult execute() { + if (dateFilter.isEmpty()) { + model.updateFilteredListToShowAll(); + return new CommandResult(MESSAGE_ALLTASKS_SUCCESS); + } + if (!dateFilter.matches(VALID_FILTER) && !isValidDate(dateFilter)) { + return new CommandResult(MESSAGE_FILTER_INVALID); + } + + assert model != null; + try { + model.updateFilteredTaskList(dateFilter); + } catch (DateTimeException dte) { + return new CommandResult(TaskDate.MESSAGE_DATE_INVALID); + } + return new CommandResult(MESSAGE_FILTER_SUCCESS); + } + + /** + * Returns true if the provided String is a valid date + */ + private boolean isValidDate(String test) { + return (test.matches(TaskDate.DATE_VALIDATION_REGEX_FORMAT)); + } +} +``` +###### /java/seedu/todolist/logic/commands/RedoCommand.java +``` java +/** + * Redoes the most recent undo operation done by the user. + * If the undo operation is overwritten by other operations, the to-do-list can't be redone. + */ +public class RedoCommand extends Command { + + public static final String COMMAND_WORD = "redo"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Redoes the previous undo operation by the user\n" + + "Example: " + COMMAND_WORD; + + public static final String MESSAGE_SUCCESS = "The previous undo operation has been redone!"; + + public static final String MESSAGE_WITHOUT_PREVIOUS_OPERATION = "There is no previous undo operation."; + + @Override + public CommandResult execute() { + assert model != null; + try { + model.redoToDoList(); + return new CommandResult(MESSAGE_SUCCESS); + } catch (EmptyStackException ese) { + return new CommandResult(MESSAGE_WITHOUT_PREVIOUS_OPERATION); + } + } +} +``` +###### /java/seedu/todolist/logic/commands/UndoCommand.java +``` java +/** + * Undoes the most recent operation done by the user. + */ +public class UndoCommand extends Command { + + public static final String COMMAND_WORD = "undo"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Undoes the most recent operation done by the user\n" + + "Example: " + COMMAND_WORD; + + public static final String MESSAGE_SUCCESS = "The most recent operation has been undone!"; + + public static final String MESSAGE_WITHOUT_PREVIOUS_OPERATION = "There is no last operation."; + + @Override + public CommandResult execute() { + assert model != null; + try { + model.undoToDoList(); + return new CommandResult(MESSAGE_SUCCESS); + } catch (EmptyStackException ese) { + return new CommandResult(MESSAGE_WITHOUT_PREVIOUS_OPERATION); + } + } +} +``` +###### /java/seedu/todolist/logic/parser/CommandParser.java +``` java + /** + * Parses arguments in the context of the undo command. + * + * @param args full command args string + * @return the prepared command + */ + private Command prepareUndo(String args) { + if (!args.trim().isEmpty()) + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + UndoCommand.MESSAGE_USAGE)); + return new UndoCommand(); + } + + /** + * Parses arguments in the context of the redo command. + * + * @param args full command args string + * @return the prepared command + */ + private Command prepareRedo(String args) { + if (!args.trim().isEmpty()) + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + RedoCommand.MESSAGE_USAGE)); + return new RedoCommand(); + } + + /** + * Parses arguments in the context of the clear command. + * + * @param args full command args string + * @return the prepared command + */ + private Command prepareClear(String args) { + if (!args.trim().isEmpty()) + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + ClearCommand.MESSAGE_USAGE)); + return new ClearCommand(); + } + + /** + * Parses arguments in the context of the find task command. + * + * @param args full command args string + * @return the prepared command + */ + private Command prepareFind(String args) { + 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 List keywordSet = new ArrayList<>(Arrays.asList(keywords)); + keywordSet.remove(keywords[0]); + return new FindCommand(keywordSet, keywords[0]); + } + + /** + * Parses arguments in the context of the list command. + * + * @param args full command args string + * @return the prepared command + */ + private Command prepareList(String args) { + String dateFilter = args.trim(); + + final Matcher matcher = DATE_FILTER_ARGS_FORMAT.matcher(dateFilter); + if (!matcher.matches() && !dateFilter.isEmpty()) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + ListCommand.MESSAGE_USAGE)); + } + + return new ListCommand(dateFilter); + } + +``` +###### /java/seedu/todolist/logic/parser/CommandParser.java +``` java + /** + * Parses arguments in the context of the exit command. + * + * @param args full command args string + * @return the prepared command + */ + private Command prepareExit(String args) { + if (!args.trim().isEmpty()) + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + ExitCommand.MESSAGE_USAGE)); + return new ExitCommand(); + } + + /** + * Parses arguments in the context of the help command. + * + * @param args full command args string + * @return the prepared command + */ + private Command prepareHelp(String args) { + if (!args.trim().isEmpty()) + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + HelpCommand.MESSAGE_USAGE)); + return new HelpCommand(); + } +} +``` +###### /java/seedu/todolist/model/ModelManager.java +``` java + @Override + public synchronized void undoToDoList() throws EmptyStackException { + ToDoList currentToDoList = new ToDoList(this.ToDoList); + ToDoList.resetData(ToDoListHistory.pop()); + ToDoListUndoHistory.push(currentToDoList); + updateFilteredListToShowAll(); + indicateToDoListChanged(); + } + + @Override + public synchronized void redoToDoList() throws EmptyStackException { + ToDoList previousToDoList = new ToDoList(this.ToDoList); + ToDoList.resetData(ToDoListUndoHistory.pop()); + ToDoListHistory.push(previousToDoList); + updateFilteredListToShowAll(); + indicateToDoListChanged(); + } +``` +###### /java/seedu/todolist/model/ModelManager.java +``` java + /** + * Update Stacks of ToDoListHistory and ToDoListUndoHistory when task list of ToDoList is going to be changed. + * @param previousToDoList + */ + private void updateToDoListHistory(ToDoList previousToDoList) { + ToDoListHistory.push(previousToDoList); + ToDoListUndoHistory.clear(); + } +``` +###### /java/seedu/todolist/model/ModelManager.java +``` java + /** + * Determine if task name contain any/all/phrase of provided keywords according to the provided findType. + */ + private class NameQualifier implements Qualifier { + private List nameKeyWords; + private String findType; + + NameQualifier(List nameKeyWords, String findType) { + this.nameKeyWords = nameKeyWords; + this.findType = findType; + } + + @Override + public boolean run(ReadOnlyTask task) { + if (FindCommand.FINDTYPE_ALL.equals(findType)) { + for (String keyword : nameKeyWords) { + if (!StringUtil.containsIgnoreCase(task.getName().fullName, keyword)) { + return false; + } + } + return true; + } + else if (FindCommand.FINDTYPE_PHRASE.equals(findType)) { + String keyword = String.join(" ", nameKeyWords).trim().toLowerCase(); + return task.getName().fullName.toLowerCase().contains(keyword); + } + else { + return nameKeyWords.stream() + .filter(keyword -> StringUtil.containsIgnoreCase(task.getName().fullName, keyword)) + .findAny() + .isPresent(); + } + } + + @Override + public String toString() { + return "name=" + String.join(", ", nameKeyWords); + } + } + + /** + * Determine if task date is within the period of the provided dateFilter. + * + * @throws DateTimeException if the date of the provided dateFilter is invalid + */ + private class DateQualifier implements Qualifier { + private String dateFilter; + + DateQualifier(String dateFilter) { + this.dateFilter = dateFilter; + } + + @Override + public boolean run(ReadOnlyTask task) throws DateTimeException { + if (task.getInterval().isFloat()) { + return false; + } + + LocalDate currentDate = LocalDate.now(); + + int currentDayOfWeek = currentDate.getDayOfWeek().getValue(); + LocalDate currentWeekStart = currentDate.minusDays(currentDayOfWeek); + LocalDate currentWeekEnd = currentDate.plusDays(6-currentDayOfWeek); + + int currentDayOfMonth = currentDate.getDayOfMonth(); + LocalDate currentMonthStart = currentDate.minusDays(currentDayOfMonth+1); + LocalDate currentMonthEnd = currentDate.plusDays(currentDate.lengthOfMonth()-currentDayOfMonth); + + LocalDate taskEndDate = task.getInterval().getEndDate().getDate(); + LocalDate taskStartDate = (task.getInterval().getStartDate() == null? + taskEndDate: task.getInterval().getStartDate().getDate()); + + if (ListCommand.FILTER_TODAY.equals(dateFilter)) { + return (!taskEndDate.isBefore(currentDate) && !taskStartDate.isAfter(currentDate)); + } + else if (ListCommand.FILTER_WEEK.equals(dateFilter)) { + return (!taskEndDate.isBefore(currentWeekStart) && !taskStartDate.isAfter(currentWeekEnd)); + } + else if (ListCommand.FILTER_MONTH.equals(dateFilter)) { + return (!taskEndDate.isBefore(currentMonthStart) && !taskStartDate.isAfter(currentMonthEnd)); + } + else { + LocalDate date = DateParser.parseDate(dateFilter); + return (!taskEndDate.isBefore(date) && !taskStartDate.isAfter(date)); + } + } + + @Override + public String toString() { + return "date=" + dateFilter; + } + } + +} +``` +###### /java/seedu/todolist/model/task/Location.java +``` java +/** + * Represents a Task's location parameter in the address book. + * Guarantees: immutable; is valid as declared in {@link #isValidLocation(String)} + */ +public class Location { + public static final String MESSAGE_LOCATION_PARAMETER_CONSTRAINTS = "Task's location parameter should be spaces or alphanumeric characters"; + public static final String LOCATION_PARAMETER_VALIDATION_REGEX = "[\\p{Alnum} ]+"; + + public final String location; + + /** + * Validates given location parameter. + * + * @throws IllegalValueException if given location parameter string is invalid. + */ + public Location(String location) throws IllegalValueException { + if (location != null) { + String trimmedLocation = location.trim(); + if (!isValidLocation(trimmedLocation)) { + throw new IllegalValueException(MESSAGE_LOCATION_PARAMETER_CONSTRAINTS); + } + this.location = trimmedLocation; + } + else { + this.location = location; + } + } + + /** + * Returns true if a given string is a valid task's location parameter. + */ + public static boolean isValidLocation(String test) { + return test.matches(LOCATION_PARAMETER_VALIDATION_REGEX); + } + + + @Override + public String toString() { + return location; + } +} +``` +###### /java/seedu/todolist/model/task/Remarks.java +``` java +/** + * Represents a Task's remarks parameter in the address book. + * Guarantees: immutable; is valid as declared in {@link #isValidRemarksParameter(String)} + */ +public class Remarks { + public static final String MESSAGE_REMARKS_PARAMETER_CONSTRAINTS = "Task's remarks parameter should be spaces or alphanumeric characters"; + public static final String REMARKS_PARAMETER_VALIDATION_REGEX = "[\\p{Alnum} ]+"; + + public final String remarks; + + /** + * Validates given remarks parameter. + * + * @throws IllegalValueException if given remarks parameter string is invalid. + */ + public Remarks(String remarks) throws IllegalValueException { + if (remarks != null) { + String trimmedRemarks = remarks.trim(); + if (!isValidRemarksParameter(trimmedRemarks)) { + throw new IllegalValueException(MESSAGE_REMARKS_PARAMETER_CONSTRAINTS); + } + this.remarks = trimmedRemarks; + } + else { + this.remarks = remarks; + } + } + + /** + * Returns true if a given string is a valid task's remarks parameter. + */ + public static boolean isValidRemarksParameter(String test) { + return test.matches(REMARKS_PARAMETER_VALIDATION_REGEX); + } + + + @Override + public String toString() { + return remarks; + } +} +``` diff --git a/collated/main/A0158963M.md b/collated/main/A0158963M.md new file mode 100644 index 000000000000..748d12a32e4b --- /dev/null +++ b/collated/main/A0158963M.md @@ -0,0 +1,184 @@ +# A0158963M +###### /java/seedu/todolist/logic/commands/Command.java +``` java + public void setStorage(Storage storage) { + this.storage = storage; + } + + /** + * Raises an event to indicate an attempt to execute an incorrect command + */ + protected void indicateAttemptToExecuteIncorrectCommand() { + EventsCenter.getInstance().post(new IncorrectCommandAttemptedEvent()); + } +} +``` +###### /java/seedu/todolist/logic/commands/SetstorageCommand.java +``` java +/** + * Sets the location of the storage file. + */ +public class SetstorageCommand extends Command{ + + public static final String COMMAND_WORD = "setstorage"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Sets the location of the storage file. \n" + + "Example: " + COMMAND_WORD + " C://user/documents/todolist"; + + public static final String MESSAGE_SUCCESS = "Successfully set the storage location!"; + + public static final String MESSAGE_Invalid_Path = "Given location path is invalid."; + + private final String filepath; + + public SetstorageCommand(String filepath) { + this.filepath = filepath; + } + + + @Override + public CommandResult execute() { + File newfile = new File(filepath); + if(newfile.isDirectory()){ //new filepath is correct + File file = new File(storage.getToDoListFilePath()); // delete original file + if(file.exists()){ + file.delete(); + } + storage.setFilePath(filepath); + return new CommandResult(MESSAGE_SUCCESS); + } + else + return new CommandResult(MESSAGE_Invalid_Path); + } + + +} +``` +###### /java/seedu/todolist/logic/LogicManager.java +``` java + public LogicManager(Model model, Storage storage) { + this.model = model; + this.storage = storage; + this.parser = new CommandParser(); + } + + + @Override + public CommandResult execute(String commandText) { + logger.info("----------------[USER COMMAND][" + commandText + "]"); + Command command = parser.parseCommand(commandText); + command.setData(model); + command.setStorage(storage); + return command.execute(); + } + + @Override + public ObservableList getFilteredAllTaskList() { + return model.getFilteredAllTaskList(); + } + +``` +###### /java/seedu/todolist/logic/parser/CommandParser.java +``` java + /** + * Parses arguments in the context of the setstorage command. + * + * @param args full command args string + * @return the prepared command + */ + private Command prepareSetstorage(String args){ + File file = new File(args.trim()); + if (!file.exists() && !file.isDirectory()){ + file.mkdirs(); + } + + return new SetstorageCommand(args.trim()); + } + +``` +###### /java/seedu/todolist/storage/StorageManager.java +``` java + @Override + public void setFilePath(String filepath){ + String todolistFilePath = filepath + "/todolist.xml"; + ToDoListStorage = new XmlToDoListStorage(todolistFilePath); + } + + @Override + public String getToDoListFilePath() { + return ToDoListStorage.getToDoListFilePath(); + } + + @Override + public Optional readToDoList() throws DataConversionException, FileNotFoundException { + logger.fine("Attempting to read data from file: " + ToDoListStorage.getToDoListFilePath()); + + return ToDoListStorage.readToDoList(ToDoListStorage.getToDoListFilePath()); + } + + @Override + public void saveToDoList(ReadOnlyToDoList ToDoList) throws IOException { + ToDoListStorage.saveToDoList(ToDoList, ToDoListStorage.getToDoListFilePath()); + } + + + @Override + @Subscribe + public void handleToDoListChangedEvent(ToDoListChangedEvent event) { + logger.info(LogsCenter.getEventHandlingLogMessage(event, "Local data changed, saving to file")); + try { + saveToDoList(event.data); + } catch (IOException e) { + raise(new DataSavingExceptionEvent(e)); + } + } + +} +``` +###### /java/seedu/todolist/ui/CommandBox.java +``` java + String[] CommandTestArray = previousCommandTest.split(" "); + String storageCommand = "setstorage"; + if(CommandTestArray[0].equals(storageCommand)){ + statusBarFooter.setSaveLocation(storage.getToDoListFilePath()); + String lastUpdated = (new Date()).toString(); + statusBarFooter.setSyncStatus("Last Updated: " + lastUpdated); + } + resultDisplay.postMessage(mostRecentResult.feedbackToUser); + logger.info("Result: " + mostRecentResult.feedbackToUser); + } + + + /** + * Sets the command box style to indicate a correct command. + */ + private void setStyleToIndicateCorrectCommand() { + resultDisplay.setStyleToIndicateCorrectCommand(); + commandTextField.getStyleClass().remove("error"); + commandTextField.setText(""); + } + + @Subscribe + private void handleIncorrectCommandAttempted(IncorrectCommandAttemptedEvent event){ + logger.info(LogsCenter.getEventHandlingLogMessage(event,"Invalid command: " + previousCommandTest)); + setStyleToIndicateIncorrectCommand(); + restoreCommandText(); + } + + /** + * Restores the command box text to the previously entered command + */ + private void restoreCommandText() { + commandTextField.setText(previousCommandTest); + } + + /** + * Sets the command box style to indicate an error + */ + private void setStyleToIndicateIncorrectCommand() { + resultDisplay.setStyleToIndicateIncorrectCommand(); + commandTextField.getStyleClass().add("error"); + } + +} +``` diff --git a/collated/test/A0138601M.md b/collated/test/A0138601M.md new file mode 100644 index 000000000000..c51e158d6620 --- /dev/null +++ b/collated/test/A0138601M.md @@ -0,0 +1,612 @@ +# A0138601M +###### /java/guitests/AddCommandTest.java +``` java +public class AddCommandTest extends ToDoListGuiTest { + + @Test + public void add() { + + TestTaskList currentList = new TestTaskList(td.getTypicalTasks()); + + //add one upcoming task + TestTask taskToAdd = td.upcomingEvent; + assertAddSuccess(taskToAdd, currentList); + + //add one overdue task + taskToAdd = td.overdueDeadline; + assertAddSuccess(taskToAdd, currentList); + + //add to empty list + commandBox.runCommand("clear"); + currentList.clear(); + taskToAdd = td.eventWithLocation; + assertAddSuccess(taskToAdd, currentList); + + //add task without name + commandBox.runCommand("add"); + assertResultMessage(Name.MESSAGE_NAME_CONSTRAINTS_EMPTY); + + //add task with invalid location + commandBox.runCommand("add invalid location at !@#$"); + assertResultMessage(Location.MESSAGE_LOCATION_PARAMETER_CONSTRAINTS); + + //add task with invalid remarks + commandBox.runCommand("add invalidRemarks remarks !@#$"); + assertResultMessage(Remarks.MESSAGE_REMARKS_PARAMETER_CONSTRAINTS); + + //invalid command + commandBox.runCommand("adds invalidcommand"); + assertResultMessage(Messages.MESSAGE_UNKNOWN_COMMAND); + } + + /** + * Runs the add command to add new tasks and confirms the result is correct. + * @param taskToAdd a new task to be added + * @param currentList A copy of the current list of tasks (before addition). + */ + private void assertAddSuccess(TestTask taskToAdd, TestTaskList currentList) { + commandBox.runCommand(taskToAdd.getAddCommand()); + confirmResult(taskToAdd, currentList); + } + + /** + * Check that the result after add command is as expected + */ + private void confirmResult(TestTask taskToAdd, TestTaskList currentList) { + //confirm the new card contains the right data + TaskCardHandle addedCard = taskListPanel.navigateToTask(taskToAdd); + assertMatching(taskToAdd, addedCard); + + //confirm the list now contains all previous tasks plus the new task + currentList.addTasksToList(taskToAdd); + assertAllListMatching(currentList); + } + +} +``` +###### /java/guitests/DeleteCommandTest.java +``` java +public class DeleteCommandTest extends ToDoListGuiTest { + + @Test + public void delete() { + + TestTaskList currentList = new TestTaskList(td.getTypicalTasks()); + + //delete the first in the list of incomplete tasks + int[] targetIndexes = new int[]{1}; + assertDeleteSuccess(targetIndexes, currentList, Status.Type.Incomplete); + + //delete the last in the list of incomplete tasks + targetIndexes = new int[]{currentList.getIncompleteList().length}; + assertDeleteSuccess(targetIndexes, currentList, Status.Type.Incomplete); + + //delete from the middle of the list of incomplete tasks + targetIndexes = new int[]{currentList.getIncompleteList().length/2}; + assertDeleteSuccess(targetIndexes, currentList, Status.Type.Incomplete); + + //delete multiple of incomplete tasks + targetIndexes = new int[]{2,1}; + assertDeleteSuccess(targetIndexes, currentList, Status.Type.Incomplete); + + //delete completed task + targetIndexes = new int[]{1}; + assertDeleteSuccess(targetIndexes, currentList, Status.Type.Complete); + + //delete overdue task + targetIndexes = new int[]{1}; + assertDeleteSuccess(targetIndexes, currentList, Status.Type.Overdue); + + //invalid index + commandBox.runCommand("delete " + currentList.getIncompleteList().length + 1); + assertResultMessage("The task index provided is invalid"); + + } + + /** + * Runs the delete command to delete incomplete tasks at the specified indices and confirms the result is correct. + * @param targetIndexes a list of indexes to be deleted + * @param currentList A copy of the current list of tasks (before deletion). + */ + private void assertDeleteSuccess(int[] targetIndexes, TestTaskList currentList, Status.Type type) { + currentList.removeTasksFromList(targetIndexes, type); + taskListPanel.clickOnListTab(type); + commandBox.runCommand(getCommand(targetIndexes)); + + //confirm the delete task are no longer in the list + assertAllListMatching(currentList); + + //confirm the result message is correct + assertResultMessage(MESSAGE_DELETE_TASK_SUCCESS); + } + + /** + * Returns the command to be entered + */ + private String getCommand(int[] targetIndexes) { + StringBuilder builder = new StringBuilder(); + builder.append("delete "); + for (int i = 0; i < targetIndexes.length; i++) { + if (i == targetIndexes.length - 1) { + builder.append(targetIndexes[i]); + } else { + builder.append(targetIndexes[i] + ", "); + } + } + return builder.toString(); + } +} +``` +###### /java/guitests/DoneCommandTest.java +``` java +public class DoneCommandTest extends ToDoListGuiTest { + + @Test + public void done() { + + TestTaskList currentList = new TestTaskList(td.getTypicalTasks()); + + //mark the first in the incomplete list + int[] targetIndexes = new int[]{1}; + assertDoneSuccess(targetIndexes, currentList, Status.Type.Incomplete); + + //mark the last in the incomplete list + targetIndexes = new int[]{currentList.getIncompleteList().length}; + assertDoneSuccess(targetIndexes, currentList, Status.Type.Incomplete); + + //mark from the middle of the incomplete list + targetIndexes = new int[]{currentList.getIncompleteList().length/2}; + assertDoneSuccess(targetIndexes, currentList, Status.Type.Incomplete); + + //mark multiple + targetIndexes = new int[]{1,2}; + assertDoneSuccess(targetIndexes, currentList, Status.Type.Incomplete); + + //mark overdue task + targetIndexes = new int[]{1}; + assertDoneSuccess(targetIndexes, currentList, Status.Type.Overdue); + + //mark completed task + taskListPanel.clickOnListTab(Status.Type.Complete); + commandBox.runCommand("done 1"); + assertResultMessage("This task is already completed!"); + + //invalid index from incomplete list + taskListPanel.clickOnListTab(Status.Type.Incomplete); + commandBox.runCommand("done " + currentList.getIncompleteList().length + 1); + assertResultMessage("The task index provided is invalid"); + + } + + /** + * Runs the done command to mark the task at specified index from incomplete list and confirms the result is correct. + * @param targetIndexOneIndexed e.g. to mark the first task in the list, 1 should be given as the target index. + * @param currentList A copy of the current list of tasks (before marking). + */ + private void assertDoneSuccess(int[] targetIndexes, final TestTaskList currentList, Status.Type type) { + currentList.markTasksFromList(targetIndexes, type); + taskListPanel.clickOnListTab(type); + commandBox.runCommand(getCommand(targetIndexes)); + + //confirm the mark task are now in completed list + assertAllListMatching(currentList); + + //confirm the result message is correct + assertResultMessage(MESSAGE_MARK_TASK_SUCCESS); + } + + /** + * Returns the command to be entered + */ + private String getCommand(int[] targetIndexes) { + StringBuilder builder = new StringBuilder(); + builder.append("done "); + for (int i = 0; i < targetIndexes.length; i++) { + if (i == targetIndexes.length - 1) { + builder.append(targetIndexes[i]); + } else { + builder.append(targetIndexes[i] + ", "); + } + } + return builder.toString(); + } + +} +``` +###### /java/guitests/guihandles/TaskListPanelHandle.java +``` java + /** + * Get the list view based on the type of task it is showing + */ + public ListView getListView(Status.Type type) { + switch (type) { + + case Complete : + return getCompleteListView(); + + case Incomplete : + return getIncompleteListView(); + + case Overdue : + return getOverdueListView(); + + default : + assert false : "Type must be either Complete, Incomplete or Overdue"; + return null; + + } + } + + private ListView getIncompleteListView() { + return (ListView) getNode(LIST_VIEW_ID_INCOMPLETE); + } + + private ListView getCompleteListView() { + return (ListView) getNode(LIST_VIEW_ID_COMPLETE); + } + + private ListView getOverdueListView() { + return (ListView) getNode(LIST_VIEW_ID_OVERDUE); + } + + /** + * Get the list tab based on the type of task it is showing + */ + public Node getListTab(Status.Type type) { + switch (type) { + + case Complete : + return getCompleteListTab(); + + case Incomplete : + return getIncompleteListTab(); + + case Overdue : + return getOverdueListTab(); + + default : + assert false : "Type must be either Complete, Incomplete or Overdue"; + return null; + } + } + + private Node getIncompleteListTab() { + return getNode(TAB_ID_INCOMPLETE); + } + + private Node getCompleteListTab() { + return getNode(TAB_ID_COMPLETE); + } + + private Node getOverdueListTab() { + return getNode(TAB_ID_OVERDUE); + } + +``` +###### /java/seedu/task/testutil/TestTask.java +``` java + public void setInterval(Interval interval) { + this.interval = interval; + } +``` +###### /java/seedu/task/testutil/TestTask.java +``` java + public void setStatus(Status status) { + this.status = status; + } +``` +###### /java/seedu/task/testutil/TestTask.java +``` java + @Override + public Interval getInterval() { + return interval; + } + //author + + @Override + public Location getLocation() { + return location; + } + + @Override + public Remarks getRemarks() { + return remarks; + } + +``` +###### /java/seedu/task/testutil/TestTask.java +``` java + @Override + public Status getStatus() { + return status; + } + + @Override + public String toString() { + return getAsText(); + } + + public String getAddCommand() { + StringBuilder sb = new StringBuilder(); + sb.append(KEYWORD_ADD + this.getName().fullName + WHITE_SPACE); + + //append interval + Interval interval = getInterval(); + if (interval.isDeadlineWithTime()) { + sb.append(KEYWORD_BY + interval.getEndDate() + WHITE_SPACE + interval.getEndTime() + WHITE_SPACE); + } else if (interval.isDeadlineWithoutTime()) { + sb.append(KEYWORD_BY + interval.getEndDate() + WHITE_SPACE); + } else { + sb.append(KEYWORD_FROM + this.getInterval().getStartDate() + WHITE_SPACE + this.getInterval().getStartTime() + + KEYWORD_TO + this.getInterval().getEndDate()+ WHITE_SPACE + this.getInterval().getEndTime() + WHITE_SPACE); + } + + //append location + if (this.getLocation() != null) { + sb.append(KEYWORD_AT + this.getLocation() + WHITE_SPACE); + } + + //append remarks + if (this.getRemarks() != null) { + sb.append(KEYWORD_REMARKS + this.getRemarks() + WHITE_SPACE); + } + return sb.toString(); + } + + @Override + public int compareTo(ReadOnlyTask task) { + return this.interval.compareTo(task.getInterval()); + } + +``` +###### /java/seedu/task/testutil/TestTaskList.java +``` java +public class TestTaskList { + private ArrayList testCompleteTasks; + private ArrayList testIncompleteTasks; + private ArrayList testOverdueTasks; + private int numberOfTask; + + /** + * Constructs an empty test task list + */ + public TestTaskList() { + clear(); + } + + /** + * Constructs a test task list from lists of incomplete list and complete list + */ + public TestTaskList(List incompleteList, List completeList, List overdueList) { + testCompleteTasks = new ArrayList(completeList); + testIncompleteTasks = new ArrayList(incompleteList); + testOverdueTasks = new ArrayList(overdueList); + numberOfTask = incompleteList.size() + completeList.size() + overdueList.size(); + } + + /** + * Constructs a test task list from an array of test tasks + */ + public TestTaskList(TestTask[] testTasks) { + this(); + for (TestTask task : testTasks) { + if (task.getStatus().isComplete()) { + testCompleteTasks.add(task); + Collections.sort(testCompleteTasks); + } else if (task.getStatus().isIncomplete()){ + testIncompleteTasks.add(task); + Collections.sort(testIncompleteTasks); + } else if (task.getStatus().isOverdue()){ + testOverdueTasks.add(task); + Collections.sort(testOverdueTasks); + } + } + numberOfTask = testTasks.length; + } + + public TestTask[] getCompleteList() { + TestTask[] completeTasks = new TestTask[testCompleteTasks.size()]; + return testCompleteTasks.toArray(completeTasks); + } + + public TestTask[] getIncompleteList() { + TestTask[] incompleteTasks = new TestTask[testIncompleteTasks.size()]; + return testIncompleteTasks.toArray(incompleteTasks); + } + + public TestTask[] getOverdueList() { + TestTask[] overdueTasks = new TestTask[testOverdueTasks.size()]; + return testOverdueTasks.toArray(overdueTasks); + } + + public int getNumberOfTask() { + return numberOfTask; + } + + /** + * Empty all three list + */ + public void clear() { + testCompleteTasks = new ArrayList(); + testIncompleteTasks = new ArrayList(); + testOverdueTasks = new ArrayList(); + numberOfTask = 0; + } + + /** + * Add tasks to the list of tasks. + * @param tasks an array of tasks. + * @param tasksToAdd The tasks that are to be added into the original array. + * @return The modified array of tasks. + */ + public void addTasksToList(TestTask taskToAdd) { + switch (taskToAdd.getStatus().getType()) { + + case Complete : + testCompleteTasks.add(taskToAdd); + Collections.sort(testCompleteTasks); + break; + + case Incomplete : + testIncompleteTasks.add(taskToAdd); + Collections.sort(testIncompleteTasks); + break; + + case Overdue : + testOverdueTasks.add(taskToAdd); + Collections.sort(testOverdueTasks); + break; + + default : + assert false : "Type must be either Complete, Incomplete or Overdue"; + break; + + } + + numberOfTask++; + } + + /** + * Removes a subset from the list of tasks. + * @param targetIndexes The indexes of task targeted tasks + * @param type determines the list to work on + * @return The modified task list after removal of the subset from the list. + */ + public void removeTasksFromList(int[] targetIndexes, Status.Type type) { + ArrayList targetList = getTargetList(type); + ArrayList tasksToDelete = getTargetTasks(targetIndexes, targetList); + for (int i = 0; i < tasksToDelete.size(); i++) { + targetList.remove(tasksToDelete.get(i)); + numberOfTask--; + } + } + + /** + * Marks a subset from the list of incomplete tasks. + * @param tasksToMark The subset of tasks. + * @return The modified tasks after marking of the subset from tasks. + */ + public void markTasksFromList(int[] targetIndexes, Status.Type type) { + ArrayList targetList = getTargetList(type); + ArrayList tasksToMark = getTargetTasks(targetIndexes, targetList); + for (int i = 0; i < tasksToMark.size(); i++) { + targetList.remove(tasksToMark.get(i)); + testCompleteTasks.add(tasksToMark.get(i)); + } + Collections.sort(testCompleteTasks); + } + + /** + * Gets a list of task that were targeted for operations + * @param targetIndexes The indexes of task targeted tasks + * @param targetList the list to work on + * @return an array of task to be operated on + */ + private ArrayList getTargetTasks(int[] targetIndexes, ArrayList targetList) { + ArrayList targetTasks = new ArrayList(); + for (int index : targetIndexes) { + targetTasks.add(targetList.get(index - 1)); //-1 because array uses zero indexing + } + return targetTasks; + } + + /** + * @param type determine the list to be worked on + * @return the list that were targeted + */ + private ArrayList getTargetList(Status.Type type) { + switch (type) { + + case Complete : + return testCompleteTasks; + + case Incomplete : + return testIncompleteTasks; + + case Overdue : + return testOverdueTasks; + + default : + assert false : "Type must be either Complete, Incomplete or Overdue"; + return null; + } + } + +``` +###### /java/seedu/task/testutil/TypicalTestTasks.java +``` java + public TypicalTestTasks() { + try { + eventWithoutParameter = new TaskBuilder().withName("Event without parameter") + .withInterval(getTodayDate(), "11:58pm", getTodayDate(), "11:59pm") + .withLocation(null) + .withRemarks(null) + .withStatus(Status.Type.Incomplete).build(); + eventWithLocation = new TaskBuilder().withName("Event with location") + .withInterval(getTodayDate(), "23:58", getTodayDate(), "23:59") + .withLocation("Office") + .withRemarks(null) + .withStatus(Status.Type.Incomplete).build(); + eventWithParameters = new TaskBuilder().withName("Event with parameters") + .withInterval("27/1/2017", "20:00", "27/1/2017", "23:59") + .withLocation("Office") + .withRemarks("print document") + .withStatus(Status.Type.Incomplete).build(); + deadlineWithLocation = new TaskBuilder().withName("Deadline with location") + .withInterval(null, null, "28 jan 2017", "12:00pm") + .withLocation("Fairprice") + .withRemarks(null) + .withStatus(Status.Type.Incomplete).build(); + deadlineWithoutTime = new TaskBuilder().withName("Deadline without time") + .withInterval(null, null, "29 jan 2017", null) + .withLocation(null) + .withRemarks("graded assignment") + .withStatus(Status.Type.Incomplete).build(); + floatWithoutParameter = new TaskBuilder().withName("Float without parameter") + .withInterval(null, null, null, null) + .withLocation(null) + .withRemarks(null) + .withStatus(Status.Type.Complete).build(); + floatWithParameters = new TaskBuilder().withName("Float with parameters") + .withInterval(null, null, null, null) + .withLocation("school") + .withRemarks("graded assignment") + .withStatus(Status.Type.Overdue).build(); + + //Manually added + upcomingEvent = new TaskBuilder().withName("Upcoming event") + .withInterval("29/1/2017", "10am", "29 jan 2017", "11:30am") + .withLocation("Orchard") + .withRemarks("prepare agenda") + .withStatus(Status.Type.Incomplete).build(); + overdueDeadline = new TaskBuilder().withName("Overdue deadline") + .withInterval(null, null, "30 oct 2016", "1pm") + .withLocation(null) + .withRemarks("bring towel") + .withStatus(Status.Type.Overdue).build(); + + //Task for testing FindCommand + taskOneToTestFind = new TaskBuilder().withName("One two Three") + .withInterval(null, null, "24 jan 2017", null) + .withLocation(null) + .withRemarks("testing") + .withStatus(Status.Type.Incomplete).build(); + + taskTwoToTestFind = new TaskBuilder().withName("one Three Two four") + .withInterval(null, null, "27 jan 2017", null) + .withLocation("NUS") + .withRemarks(null) + .withStatus(Status.Type.Incomplete).build(); + + taskThreeToTestFind = new TaskBuilder().withName("Four One three two") + .withInterval(null, null, "30 jan 2017", null) + .withLocation("home") + .withRemarks(null) + .withStatus(Status.Type.Incomplete).build(); + + } catch (IllegalValueException e) { + e.printStackTrace(); + assert false : "not possible"; + } + } +``` diff --git a/collated/test/A0146682X.md b/collated/test/A0146682X.md new file mode 100644 index 000000000000..35632ff36092 --- /dev/null +++ b/collated/test/A0146682X.md @@ -0,0 +1,80 @@ +# A0146682X +###### /java/guitests/EditCommandTest.java +``` java +public class EditCommandTest extends ToDoListGuiTest { + + @Test + public void edit() { + + TestTaskList currentList = new TestTaskList(td.getTypicalTasks()); + + //edit the first in the list of incomplete tasks + int targetIndex = 1; + String partialCommand = "test task first"; + assertEditSuccess(targetIndex, partialCommand, currentList, true); + + //edit the last in the list of incomplete tasks + targetIndex = currentList.getIncompleteList().length; + partialCommand = "test task last"; + assertEditSuccess(targetIndex, partialCommand, currentList, true); + + //invalid index + targetIndex = (currentList.getIncompleteList().length + 1); + partialCommand = "test task to fail"; + assertEditSuccess(targetIndex, partialCommand, currentList, true); + } + + /** + * Runs the edit command to edit incomplete tasks at the specified indices and confirms the result is correct. + * @param targetIndexes a list of indexes to be edited + * @param currentList A copy of the current list of tasks (before edition). + */ + private void assertEditSuccess(int targetIndex, String partialCommand, TestTaskList currentList, boolean isFromIncompleteList) { + + int listLength = isFromIncompleteList ? currentList.getIncompleteList().length : currentList.getCompleteList().length; + + commandBox.runCommand(getCommand(targetIndex, partialCommand)); + if(targetIndex < 1 || targetIndex > listLength) assertResultMessage(MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + else { + + TestTask newTask = isFromIncompleteList ? currentList.getIncompleteList()[targetIndex-1] : currentList.getCompleteList()[targetIndex-1]; + try { + newTask.setName(new Name(partialCommand)); + } catch (IllegalValueException ive) { + System.out.println(MESSAGE_USAGE); + } + + assertTrue(taskListPanel.isListMatching(Status.Type.Incomplete, currentList.getIncompleteList())); + assertTrue(taskListPanel.isListMatching(Status.Type.Complete, currentList.getCompleteList())); + assertResultMessage(String.format(MESSAGE_SUCCESS, newTask)); + } + } + + /** + * Returns the command to be entered + */ + private String getCommand(int targetIndex, String string) { + StringBuilder builder = new StringBuilder(); + builder.append("edit "); + builder.append(targetIndex); + builder.append(" " + string); + return builder.toString(); + } +} +``` +###### /java/seedu/task/testutil/TestTaskList.java +``` java + /** + * edits a task in the list tasks. + * @param index the index of task to edit + * @param isFromIncompleteList Whether to edit from incomplete list or complete list + */ + public void editTask(int index, TestTask newTask, boolean isFromIncompleteList) { + if (isFromIncompleteList) { + testIncompleteTasks.set(index-1, newTask); + } + else { + testCompleteTasks.set(index-1, newTask); + } + } +``` diff --git a/collated/test/A0153736B.md b/collated/test/A0153736B.md new file mode 100644 index 000000000000..86ce3abd193f --- /dev/null +++ b/collated/test/A0153736B.md @@ -0,0 +1,320 @@ +# A0153736B +###### /java/guitests/FindCommandTest.java +``` java +public class FindCommandTest extends ToDoListGuiTest { + + @Test + public void findEitherAtNonEmptyList() { + //find non existent task + TestTaskList currentList = new TestTaskList(); + assertFindCommandSuccess("find either NonExistentTask", currentList); + + //find multiple tasks that contain the keyword entered + currentList = new TestTaskList (new TestTask[] {td.eventWithoutParameter, td.eventWithLocation, td.eventWithParameters}); + assertFindCommandSuccess("find either event", currentList); + + //find tasks that contain one or more keywords entered + currentList = new TestTaskList(new TestTask[] {td.deadlineWithoutTime, td.floatWithoutParameter, td.floatWithParameters}); + assertFindCommandSuccess("find either time float", currentList); + + //find after deleting one result + commandBox.runCommand("delete 1"); + currentList = new TestTaskList (new TestTask[] {td.eventWithoutParameter, td.eventWithLocation, td.eventWithParameters}); + assertFindCommandSuccess("find either Event", currentList); + + //find tasks from both incomplete and complete list + commandBox.runCommand("done 1"); + currentList.markTasksFromList(new int[]{1}, Status.Type.Incomplete); + assertFindCommandSuccess("find either Event", currentList); + } + + @Test + public void findEitherAtEmptyList() { + commandBox.runCommand("clear"); + TestTaskList currentList = new TestTaskList(); + assertFindCommandSuccess("find either event", currentList); //no results + } + + @Test + public void findAllAtNonEmptyList() { + commandBox.runCommand(td.taskOneToTestFind.getAddCommand()); + commandBox.runCommand(td.taskTwoToTestFind.getAddCommand()); + commandBox.runCommand(td.taskThreeToTestFind.getAddCommand()); + + //find non existent task + TestTaskList currentList = new TestTaskList(); + assertFindCommandSuccess("find all three four five", currentList); + + //find tasks that contain all keywords entered + currentList = new TestTaskList(new TestTask[] {td.taskOneToTestFind, td.taskTwoToTestFind, + td.taskThreeToTestFind}); + assertFindCommandSuccess("find all two three", currentList); + + //find after deleting one result + commandBox.runCommand("delete 1"); + currentList = new TestTaskList (new TestTask[] {td.taskTwoToTestFind, td.taskThreeToTestFind}); + assertFindCommandSuccess("find all three two", currentList); + + //find tasks from both incomplete and complete list + commandBox.runCommand("done 1"); + currentList.markTasksFromList(new int[]{1}, Status.Type.Incomplete); + assertFindCommandSuccess("find all Two Three", currentList); + } + + @Test + public void findAllAtEmptyList() { + commandBox.runCommand("clear"); + TestTaskList currentList = new TestTaskList(); + assertFindCommandSuccess("find all Two Three", currentList); //no results + } + + @Test + public void findPhraseAtNonEmptyList() { + commandBox.runCommand(td.taskOneToTestFind.getAddCommand()); + commandBox.runCommand(td.taskTwoToTestFind.getAddCommand()); + commandBox.runCommand(td.taskThreeToTestFind.getAddCommand()); + + //find non existent task + TestTaskList currentList = new TestTaskList(); + assertFindCommandSuccess("find phrase three four five", currentList); + + //find tasks that contain the exact keywords entered + currentList = new TestTaskList(new TestTask[] {td.taskOneToTestFind}); + assertFindCommandSuccess("find phrase two three", currentList); + + //find after deleting one result + commandBox.runCommand("delete 1"); + currentList = new TestTaskList (new TestTask[] {td.taskTwoToTestFind, td.taskThreeToTestFind}); + assertFindCommandSuccess("find phrase One three", currentList); + + //find tasks from both incomplete and complete list + commandBox.runCommand("done 1"); + currentList.markTasksFromList(new int[]{1}, Status.Type.Incomplete); + assertFindCommandSuccess("find phrase one three", currentList); + } + + @Test + public void findPhraseAtEmptyList() { + commandBox.runCommand("clear"); + TestTaskList currentList = new TestTaskList(); + assertFindCommandSuccess("find phrase one three", currentList); //no results + } + + @Test + public void enterInvalidCommand() { + commandBox.runCommand("findgeorge"); + assertResultMessage(Messages.MESSAGE_UNKNOWN_COMMAND); + } + + @Test + public void enterEmptyKeywords() { + commandBox.runCommand("find phrase"); + assertResultMessage(FindCommand.MESSAGE_KEYWORDS_NOT_PROVIDED); + } + + @Test + public void enterInvalidFindtype() { + commandBox.runCommand("find something"); + assertResultMessage(FindCommand.MESSAGE_INVALID_FINDTYPE); + } + + private void assertFindCommandSuccess(String command, TestTaskList expectedList) { + commandBox.runCommand(command); + assertTrue(taskListPanel.isListMatching(Status.Type.Incomplete, expectedList.getIncompleteList())); + assertTrue(taskListPanel.isListMatching(Status.Type.Complete, expectedList.getCompleteList())); + assertTrue(taskListPanel.isListMatching(Status.Type.Overdue, expectedList.getOverdueList())); + assertResultMessage(expectedList.getNumberOfTask() + " tasks listed!"); + } +} +``` +###### /java/guitests/ListCommandTest.java +``` java +public class ListCommandTest extends ToDoListGuiTest { + + @Test + public void listAll() { + TestTaskList currentList = new TestTaskList(td.getTypicalTasks()); + assertFindCommandSuccess("list", currentList); + } + + @Test + public void listToday() { + TestTaskList currentList = new TestTaskList(new TestTask[] {td.eventWithoutParameter, td.eventWithLocation}); + assertFindCommandSuccess("list today", currentList); + } + + @Test + public void listWeek() { + TestTaskList currentList = new TestTaskList(new TestTask[] {td.eventWithoutParameter, td.eventWithLocation}); + assertFindCommandSuccess("list week", currentList); + } + + @Test + public void listMonth() { + TestTaskList currentList = new TestTaskList(new TestTask[] {td.eventWithoutParameter, td.eventWithLocation}); + assertFindCommandSuccess("list month", currentList); + } + + @Test + public void listValidDate() { + TestTaskList currentList = new TestTaskList(new TestTask[] {td.deadlineWithLocation}); + assertFindCommandSuccess("list 28 jan 2017", currentList); + } + + @Test + public void listInvalidDate() { + commandBox.runCommand("list 32 oct 2016"); + assertResultMessage(TaskDate.MESSAGE_DATE_INVALID); + } + + @Test + public void listInvalidFilter() { + commandBox.runCommand("list tomorrow"); + assertResultMessage(ListCommand.MESSAGE_FILTER_INVALID); + } + + @Test + public void listInvalidCommand() { + commandBox.runCommand("listtoday"); + assertResultMessage(Messages.MESSAGE_UNKNOWN_COMMAND); + } + + private void assertFindCommandSuccess(String command, TestTaskList expectedList) { + commandBox.runCommand(command); + assertTrue(taskListPanel.isListMatching(Status.Type.Incomplete, expectedList.getIncompleteList())); + assertTrue(taskListPanel.isListMatching(Status.Type.Complete, expectedList.getCompleteList())); + assertTrue(taskListPanel.isListMatching(Status.Type.Overdue, expectedList.getOverdueList())); + if ("list".equals(command)) { + assertResultMessage(ListCommand.MESSAGE_ALLTASKS_SUCCESS); + } + else { + assertResultMessage(ListCommand.MESSAGE_FILTER_SUCCESS); + } + } +} +``` +###### /java/guitests/RedoCommandTest.java +``` java +public class RedoCommandTest extends ToDoListGuiTest { + + @Test + public void redo() { + //without any last undo operation + commandBox.runCommand("redo"); + assertResultMessage(RedoCommand.MESSAGE_WITHOUT_PREVIOUS_OPERATION); + + //redo an undo operation + TestTask taskToAdd = td.overdueDeadline; + TestTaskList currentList = new TestTaskList(td.getTypicalTasks()); + commandBox.runCommand(taskToAdd.getAddCommand()); + commandBox.runCommand("undo"); + currentList.addTasksToList(taskToAdd); + assertRedoCommandSuccess(currentList); + + //redo two undo operations consecutively + TestTask taskAfterEdit = td.upcomingEvent; + commandBox.runCommand(taskAfterEdit.getEditCommand(1)); + commandBox.runCommand("undo"); + commandBox.runCommand("undo"); + assertRedoCommandSuccess(currentList); + currentList.editTask(1, taskAfterEdit, true); + assertRedoCommandSuccess(currentList); + + //redo operation fails when apply delete operation after undo operation + commandBox.runCommand("undo"); + commandBox.runCommand("delete 1"); + commandBox.runCommand("redo"); + assertResultMessage(RedoCommand.MESSAGE_WITHOUT_PREVIOUS_OPERATION); + + //invalid command word + commandBox.runCommand("redo2"); + assertResultMessage(Messages.MESSAGE_UNKNOWN_COMMAND); + + //invalid command argument + commandBox.runCommand("redo 2"); + assertResultMessage(String.format(MESSAGE_INVALID_COMMAND_FORMAT, RedoCommand.MESSAGE_USAGE)); + } + + private void assertRedoCommandSuccess(TestTaskList expectedList) { + commandBox.runCommand("redo"); + assertTrue(taskListPanel.isListMatching(Status.Type.Incomplete, expectedList.getIncompleteList())); + assertTrue(taskListPanel.isListMatching(Status.Type.Complete, expectedList.getCompleteList())); + assertTrue(taskListPanel.isListMatching(Status.Type.Overdue, expectedList.getOverdueList())); + assertResultMessage(RedoCommand.MESSAGE_SUCCESS); + } +} +``` +###### /java/guitests/UndoCommandTest.java +``` java +public class UndoCommandTest extends ToDoListGuiTest { + + @Test + public void undo() { + //without any last operation + commandBox.runCommand("undo"); + assertResultMessage(UndoCommand.MESSAGE_WITHOUT_PREVIOUS_OPERATION); + + //run add and edit operations + TestTask taskToAdd = td.overdueDeadline; + commandBox.runCommand(taskToAdd.getAddCommand()); + TestTask taskAfterEdit = td.upcomingEvent; + commandBox.runCommand(taskAfterEdit.getEditCommand(1)); + + //undo an edit operation + TestTaskList currentList = new TestTaskList(td.getTypicalTasks()); + currentList.addTasksToList(taskToAdd); + assertUndoCommandSuccess(currentList); + + //undo an add operation after undoing an edit operation + currentList = new TestTaskList(td.getTypicalTasks()); + assertUndoCommandSuccess(currentList); + + //run done, delete, invalid and clear operations + commandBox.runCommand("done 1"); + commandBox.runCommand("delete 1"); + commandBox.runCommand("delete " + (currentList.getIncompleteList().length+2)); + commandBox.runCommand("clear"); + + //undo a clear operation + currentList.markTasksFromList(new int[]{1}, Status.Type.Incomplete); + currentList.removeTasksFromList(new int[]{1}, Status.Type.Incomplete); + assertUndoCommandSuccess(currentList); + + //undo a delete operation + currentList = new TestTaskList(td.getTypicalTasks()); + currentList.markTasksFromList(new int[]{1}, Status.Type.Incomplete); + assertUndoCommandSuccess(currentList); + + //undo a done operation + currentList = new TestTaskList(td.getTypicalTasks()); + assertUndoCommandSuccess(currentList); + + //invalid command word + commandBox.runCommand("undo2"); + assertResultMessage(Messages.MESSAGE_UNKNOWN_COMMAND); + + //invalid command argument + commandBox.runCommand("undo 2"); + assertResultMessage(String.format(MESSAGE_INVALID_COMMAND_FORMAT, UndoCommand.MESSAGE_USAGE)); + } + + private void assertUndoCommandSuccess(TestTaskList expectedList) { + commandBox.runCommand("undo"); + assertTrue(taskListPanel.isListMatching(Status.Type.Incomplete, expectedList.getIncompleteList())); + assertTrue(taskListPanel.isListMatching(Status.Type.Complete, expectedList.getCompleteList())); + assertTrue(taskListPanel.isListMatching(Status.Type.Overdue, expectedList.getOverdueList())); + assertResultMessage(UndoCommand.MESSAGE_SUCCESS); + } +} +``` +###### /java/seedu/task/testutil/TypicalTestTasks.java +``` java + private String getTodayDate() { + LocalDate currentDate = LocalDate.now(); + final StringBuilder sb = new StringBuilder(); + sb.append(currentDate.getDayOfMonth() + "/") + .append(currentDate.getMonthValue() + "/") + .append(currentDate.getYear()); + return sb.toString(); + } +``` diff --git a/collated/test/A0158963M.md b/collated/test/A0158963M.md new file mode 100644 index 000000000000..cefbbcf3a8bd --- /dev/null +++ b/collated/test/A0158963M.md @@ -0,0 +1,22 @@ +# A0158963M +###### /java/seedu/task/logic/LogicManagerTest.java +``` java + private void assertsetStorageCommandBehavior(String inputCommand, String expectedMessage, String expectfilePath) + throws Exception { + + // Execute the command + CommandResult result = logic.execute(inputCommand); + + // Confirm the storage should contain the right data + assertEquals(expectedMessage, result.feedbackToUser); + assertEquals(expectfilePath, storage.getToDoListFilePath()); + + } + + @Test + public void execute_setstorage() throws Exception { + String setstorageCommand = "setstorage NewData"; + assertsetStorageCommandBehavior(setstorageCommand, SetstorageCommand.MESSAGE_SUCCESS, "NewData/todolist.xml"); + } + +``` diff --git a/docs/AboutUs.md b/docs/AboutUs.md index d045b6d48116..7dacb41cd89f 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -1,51 +1,66 @@ # 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 group of students 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 +#### [Ang Shi Ya](https://github.com/AngShiYa)
+
+* Components in charge of: [Model] (https://github.com/CS2103AUG2016-T15-C3/main/blob/master/docs/DeveloperGuide.md#model-component) +* Aspects/tools in charge of: Testing, Git +* Features implemented: + * [Add task] (https://github.com/CS2103AUG2016-T15-C3/main/blob/master/docs/UserGuide.md#adding-a-task--add) + * [Mark task] (https://github.com/CS2103AUG2016-T15-C3/main/blob/master/docs/UserGuide.md#marking-task-as-done--done) + * [Delete task] (https://github.com/CS2103AUG2016-T15-C3/main/blob/master/docs/UserGuide.md#deleting-a-task--delete) +* Code written: [[Functional](https://github.com/CS2103AUG2016-T15-C3/main/blob/master/collated/main/A0138601M.md)] [[Test](https://github.com/CS2103AUG2016-T15-C3/main/blob/master/collated/test/A0138601M.md)] [[docs](https://github.com/CS2103AUG2016-T15-C3/main/blob/master/collated/docs/A0138601M.md)] +* Other major contributions: + * Set up Travis, Coveralls and Codacy + * Did the initial refactoring from AddressBook to Task!t [[#1](https://github.com/CS2103AUG2016-T15-C3/main/pull/2)] + * Sort task list displayed by date and time [[#74](https://github.com/CS2103AUG2016-T15-C3/main/pull/75)] + * UI and split into 3 tabs[[#58](https://github.com/CS2103AUG2016-T15-C3/main/issues/58)] [[#110](https://github.com/CS2103AUG2016-T15-C3/main/issues/110)] [[#133](https://github.com/CS2103AUG2016-T15-C3/main/pull/133)] + * Set up Natty [[#107](https://github.com/CS2103AUG2016-T15-C3/main/pull/107)] + * Did the releases for all versions ----- -#### [Joshua Lee](http://github.com/lejolly) -
-Role: Developer
-Responsibilities: UI +#### [Wu Cheuk Kwan Carina](https://github.com/carinackwu)
+
+* Components in charge of: [Logic] (https://github.com/CS2103AUG2016-T15-C3/main/blob/master/docs/DeveloperGuide.md#logic-component) +* Aspects/tools in charge of: Team Lead, Developer +* Features implemented: + * [List tasks] (https://github.com/CS2103AUG2016-T15-C3/main/blob/master/docs/UserGuide.md#listing-all-tasks--list) + * [Find task] (https://github.com/CS2103AUG2016-T15-C3/main/blob/master/docs/UserGuide.md#finding-all-tasks-containing-keyword-in-the-name--find) + * [Undo operation] (https://github.com/CS2103AUG2016-T15-C3/main/blob/master/docs/UserGuide.md#undoing-last-action-performed--undo) + * [Redo operation] (https://github.com/CS2103AUG2016-T15-C3/main/blob/master/docs/UserGuide.md#redoing-last-undo-action-performed--redo) ----- -#### [Leow Yijin](http://github.com/yijinl) -
-Role: Developer
-Responsibilities: Data +#### [Huang Tsai Ti](https://github.com/tyrahuang)
+
+* Components in charge of: [Storage] +(https://github.com/CS2103AUG2016-T15-C3/main/blob/master/docs/DeveloperGuide.md#storage-component) +* Aspects/tools in charge of: Code quality, Developer +* Features implemented: + * [View Help ] +(https://github.com/CS2103AUG2016-T15-C3/main/blob/master/docs/UserGuide.md#Viewing-help--help) + * [Set storage location ] +(https://github.com/CS2103AUG2016-T15-C3/main/blob/master/docs/UserGuide.md#Setting-the-storage-location--setstorage) ----- -#### [Martin Choo](http://github.com/m133225) -Role: Developer
-Responsibilities: Dev Ops +#### [Lai Yingchen](https://github.com/yingchen0505)
+
+* Components in charge of: [UI] (https://github.com/CS2103AUG2016-T15-C3/main/blob/master/docs/DeveloperGuide.md#UI-component) +* Aspects/tools in charge of: Integration, Developer +* Features implemented: + * [Edit task] (https://github.com/CS2103AUG2016-T15-C3/main/blob/master/docs/UserGuide.md#editing-a-task--edit) +* Other major contributions: + * Specifying the detailed requirements and examples of the [features] +(https://github.com/CS2103AUG2016-T15-C3/main/blob/master/docs/UserGuide.md#time-parameter) + * Update UI to fit the app's purpose, remove browser and divide the pane into incomplete and complete task tabs. [#41] +(https://github.com/CS2103AUG2016-T15-C3/main/commit/3459becf3e88958a501855c8632c3dd8d5c02357) + * Rename all mentions of AddressBook to ToDoList, addressbook to todolist [#104] (https://github.com/CS2103AUG2016-T15-C3/main/commit/e8f1f330aef58daaed7da8166b33b66563475b6d) + + #### [Nishant Budhdev](https://github.com/AngShiYa)
+
------ - -#### [Thien Nguyen](https://github.com/ndt93) - Role: Developer
- Responsibilities: Threading - - ----- - -#### [You Liang](http://github.com/yl-coder) -
- Role: Developer
- Responsibilities: UI - - ----- - -# 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/DeveloperGuide.md b/docs/DeveloperGuide.md index 171b4d591e99..4bddc5438643 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -1,4 +1,4 @@ -# Developer Guide +# Developer Guide * [Setting Up](#setting-up) * [Design](#design) @@ -11,7 +11,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 @@ -115,9 +115,10 @@ The `UI` component, 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. +3. The command execution can affect the `Model` (e.g. adding a task) and/or raise events. 4. The result of the command execution is encapsulated as a `CommandResult` object which is passed back to the `Ui` + ### Model component
@@ -126,11 +127,13 @@ The `UI` component, The `Model`, * Stores a `UserPref` object that represents the user's preferences -* Stores the Address Book data -* Exposes a `UnmodifiableObservableList + ### Storage component
@@ -139,11 +142,11 @@ The `Model`, 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 ToDoList data in xml format and read it back. + ### Common classes -Classes used by multiple components are in the `seedu.addressbook.commans` package. +Classes used by multiple components are in the `seedu.todolist.commons` package. ## Implementation @@ -250,49 +253,221 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (un 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 +`* * *` | user | add a task | keep a list of task +`* * *` | user | set timing and deadline for a task | meet all the timing and deadlines +`* * *` | user | set the location for a task | know where to go for a task +`* * *` | user | add remarks to a task | have quick access to additional information related to the task +`* * *` | user | view task list sorted by date and time | keep track of tasks that have to be done on a specific day +`* * *` | user | find an existing task by name using keywords | locate an existing task without having to go through the entire task list +`* * *` | user | modify the information of a task | update the details, requirements and deadline of a task if they are changed +`* * *` | user | undo the most recent operation done by the user | revert the previous state of ToDoList in case if a mistake is made +`* * *` | user | redo the most recent undo operation by the user | revert the state of ToDoList before applying an undo operation +`* * *` | user | mark the completed tasks as done | keep track of which items are done and which are yet to be done +`* * *` | user | delete a task | discard cancelled tasks +`* * *` | user | specify a specific folder as the data storage location | choose a folder that is more convenient for me to access the data +`* *` | user | set notification for a task | remind myself of the deadlines and upcoming events +`*` | user | summon the software quickly by pressing a keyboard shortcut | save time +`*` | user | synchronize my tasklist among all my devices | check my tasks wherever I am + + +## Appendix B : Use Cases -{More to be added} +(For all use cases below, the **System** is the `Task!t` and the **Actor** is the `user`, unless specified otherwise) +#### Use case: Add task -## Appendix B : Use Cases +**MSS** + +1. User requests to add task +2. Task!t displays the task added
+Use case ends. + +**Extensions** + +2a. The command format is invalid +> 2a1. Task!t shows a 'invalid command' message and display the expected format.
+> Use case ends + +2b. The task already exist +> 2b1. Task!t shows a 'the task already exist' message.
+> Use case resumes at step 1 + + +#### Use case: View task + +**MSS** + +1. User requests to view tasks for today/this week/this month/a particular date +2. Task!t display the list of tasks for that date sorted by date and/or time
+Use case ends. + +**Extensions** + +2a. The filter provided is invalid +> 2a1. Task!t shows a 'Filter provided is invalid!' message.
+> Use case ends + +2b. The date provided is invalid +> 2b1. Task!t shows a 'Task date provided is invalid!' message.
+> Use case ends + +#### Use case: Find task + +**MSS** + +1. User requests to find tasks by keyword(s) +2. Task!t displays the list of tasks that contains the keyword in the name
+Use case ends. + +**Extensions** + +2a. No parameter entered after command word +> 2a1. Task!t shows a 'invalid command' message and display the expected format.
+> Use case ends + +2b. Findtype entered after command word is invalid +> 2b1. Task!t shows a 'The findtype provided is invalid.' message.
+> Use case ends + +2c. No keywords entered after findtype +> 2c1. Task!t shows a 'Keywords are not provided.' message.
+> Use case ends + + +#### Use case: Edit task + +**MSS** + +1. User requests to **view** tasks for a particular date/week/month/date +2. Task!t displays the list of tasks for that date sorted by date and/or time +3. User request to edit a specific task in the list based on task’s index with certain details +4. Task!t updates the task details
+Use case ends. + +**Extensions** + +2a. The list is empty +> 2a1. Task!t shows a 'no task found' message.
+> Use case ends + +3a. The given index is invalid +> 3a1. Task!t shows a 'invalid index' message
+> Use case resumes at step 2 + +3b. The command format is invalid +> 3b1. Task!t shows a 'invalid command' message and display the expected format.
+> Use case resumes at step 2 + +#### Use case: Undo operation + +**MSS** + +1. User requests to undo last operation performed +2. Task!t undo the last operation
+Use case ends. + +**Extensions** -(For all use cases below, the **System** is the `AddressBook` and the **Actor** is the `user`, unless specified otherwise) +2a. There is no last operation +> 2a1. Task!t shows a 'there is no last operation' message.
+> Use case ends -#### Use case: Delete person +#### Use case: Mark task as done **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 view tasks for a particular date/week/month/date +2. Task!t displays the list of tasks for that date sorted by date and/or time +3. User request to mark specific tasks in the list based on task’s index +4. Task!t marks the tasks
Use case ends. **Extensions** 2a. The list is empty +> 2a1. Task!t shows a 'no task found' message.
+> Use case ends + +3a. The given index is invalid +> 3a1. Task!t shows a 'invalid index' message
+> Use case resumes at step 2 + +3b. The command format is invalid +> 3b1. Task!t shows a 'invalid format' message and display the expected format
+> Use case ends + +3c. The task is already marked as done +> 3c1. Task!t shows a 'task already marked' message.
+> Use case resumes at step 3 + +#### Use case: Delete task + +**MSS** + +1. User requests to **view** tasks for a particular date/week/month/date +2. Task!t displays the list of tasks for that date sorted by date and/or time +3. User requests to delete a specific task in the list based on task’s index +4. Task!t deletes the task
+Use case ends. + +**Extensions** +2a. The list is empty +> 2a1. Task!t shows a 'no task found' message
> Use case ends 3a. The given index is invalid +> 3a1. Task!t shows a 'invalid index' message
+> Use case resumes at step 2 + +#### Use case: Set storage location + +**MSS** + +1. User requests to change storage location wo a specified file path +2. Task!t changes the storage location
+Use case ends. + +**Extensions** -> 3a1. AddressBook shows an error message
- Use case resumes at step 2 +2a. The given path is invalid +> 2a1. Task!t shows a 'invalid path' message
+> Use case ends + + +#### Use case: Redo operation + +**MSS** + +1. User requests to redo the last undo operation performed +2. Task!t redo the last undo operation
+Use case ends. -{More to be added} +**Extensions** + +2a. There is no last undo operation +> 2a1. Task!t shows a 'There is no previous undo operation.' message.
+> Use case ends + +2b. The previous operation is not an undo operation +> 2b1. Task!t shows a 'There is no previous undo operation.' message.
+> Use case ends + ## Appendix C : Non Functional Requirements 1. Should work on any [mainstream OS](#mainstream-os) as long as it has Java `1.8.0_60` or higher installed. -2. Should be able to hold up to 1000 persons. +2. Should be able to hold up to 1000 tasks. 3. Should come with automated unit tests and open source code. 4. Should favor DOS style commands over Unix-style commands. +5. Should display response within 1 second. +6. Should work stand-alone and should not be a plug-in to another software. +7. Should work without internet connection. +8. Should store data in text file. +9. Should work without requiring an installer. +10. Should work only with Command Line Interface as the main input with minimal use of mouse/clicking. +11. Should follow the Object-oriented paradigm. +12. Should be free and come with open source code -{More to be added} ## Appendix D : Glossary @@ -300,11 +475,96 @@ Use case ends. > Windows, Linux, Unix, OS-X -##### Private contact detail +##### Task -> A contact detail that is not meant to be shared with others +> An item in Task!t, could be an event, a deadline, or a reminder. ## Appendix E : Product Survey + +#### Google Calendar +Pros: +* Able to color-code different events +* Able to set notifications +* Able to set repeat +* Able to add notes (i.e. location, remarks) +* Able to sync on different devices +* Able to share calendar +* Able to create layers to manege events +* Able to undo last action
+ +Cons: +* Unable to check off completed event (only reminder can) +* Unable to label event with categories +* Event name gets truncated
+ + +#### S Planner + +Pros: +* Able to sync with another calendar product(i.e. Google calendar) +* Able to set notifications +* Able to set repeat +* Able to add notes +* Able to add location +* Able to set start and end time +* Able to set different time zone +* Able to share as text
+ +Cons: +* Unable to search events +* Unable to view event list +* Unable to mark event as done +* Unable to color-code different events
+ +#### Wunderlist + +Pros: +* Able to create many layers - folders, lists, tasks, and subtasks, which allow the user to organize their multi-layered tasks systematically +* Able to add alarm to a task +* Able to repeat a task daily, weekly, monthly, or yearly +* Able to add notes to a task +* Able to attach files to a task +* Able to mark a task as complete by a simple click +* Able to unmark a “completed” task by a simple click +* Able to share a task list with team members by name or email address +* Able to sort a list alphabetically, by due date, or creation date +* Includes two tabs of “today” and “week” which highlight the most recent tasks +* Includes a search bar for tasks +* Able to synchronize tasks to cloud +* Able to star a task to mark its importance +* Able to create a task without a date, thus allowing the user to keep track of reminders +* Keeps track of the creation date of a task +* Worded tabs automatically minimize themselves into icons when the window size is reduced, so as to keep the view uncluttered +* Keeps track of completed tasks for reference + +Cons: +* Unable to add the start and end time of an event. User is only allowed to set the deadline of a task +* Unable to undo a deletion of a task +* Unable to set a specific timing of a due date, e.g. 23:59, 6 am etc. +* Slow to open / close the window since the user can only do it by mouse movements +* Slow to resize the window since the user has to do it by mouse movements +* Does not display today’s date +* Does not display a calendar +* Does not allow the addition of venue to a task + + +#### Cal +Pros: +* With a very convenient interface for setting events’ date and time quickly +* Able to add other participants’ contacts in an event +* Able to send or receive invitation of an event to others directly through the app +* Able to include map when adding location of an event +* Able to provide recommendation when deciding the location of an event +* Able to set alarm to an event +* Able to set repeat to an event +* Able to connect with Facebook and import Facebook friends’ birthdays into the calendar +* Able to sync with another calendar product (eg. Google calendar)
+ +Cons: +* Does not have a task function itself so it requires to work together with another app Any.do +* Unable to show events of the following days directly through the calendar +* Unable to show events that last for a few days in the calendar +* Does not show public holidays in the calendar +* Unable to add additional notes in an event
-{TODO: Add a summary of competing products} diff --git a/docs/UserGuide.md b/docs/UserGuide.md index c18832432d1f..52bab0ac70be 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -11,117 +11,237 @@ > 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' tab. -2. Copy the file to the folder you want to use as the home folder for your Address Book. +1. Download the latest `Task!t.jar` from the 'releases' tab. +2. Copy the file to the folder you want to use as the home folder for your Task!t. 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. 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 + * **`list`**` today` : lists all tasks for today + * **`add`**` Buy milk `**`by`**` 30 Oct` : + adds a task named `Buy milk` to Task!t. + * **`delete`**` 3` : deletes the 3rd task shown in the current list * **`exit`** : exits the app 6. Refer to the [Features](#features) section below for details of each command.
## Features -#### Viewing help : `help` -Format: `help` - +### Viewing help : `help` +Format: **`help`** > 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]...` - -> Words in `UPPER_CASE` are the parameters, items in `SQUARE_BRACKETS` are optional, -> items with `...` after them can have multiple instances. Order of parameters are fixed. -> -> Persons can have any number of tags (including 0) - -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` - -#### Listing all persons : `list` -Shows a list of all persons in the address book.
-Format: `list` - -#### 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, the order of the keywords does not matter, only the name is searched, -and persons matching at least one keyword will be returned (i.e. `OR` search). + -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.
-Format: `delete INDEX` - -> Deletes the person at the specified `INDEX`. +### Adding a task : `add` +Add a task to Task!t.
+Format: **`add`**` [task_name] [optional parameter 1] [optional parameter 2]...`
+ +> ##### Time parameter +> One of the following parameters must be appended to the add command.
+ +> ###### Specifying start/end time: `from ... to ...` +> This parameter is used to indicate the starting and ending datetime of a task.
+> Format: **`from`**` [datetime] `**`to`**` [datetime]`
+ + +> > A few variations in [datetime] format are accepted:
+> > - Both 24-Hour and AM/PM formats are accepted:
+> > E.g. 19:30 12 oct 2016
+> > E.g. 7:30 PM 12 oct 2016
+> > E.g. 7:30 pm 12 oct 2016
+ +> > - Minutes can be omitted and assumed to be 00:
+> > E.g. 7 pm 12 oct 2016
+ +> > - Month can be in abbreviations, or number separated by slash from day and year:
+> > E.g. 19:30 12 oct 2016
+> > E.g. 19:30 12/10/2016
+ + +> Examples: +> * **`add`**` dinner with mom `**`from`**` 19:30 02 oct 2016 `**`to`**` 20:30 02 oct 2016` +> * **`add`**` dinner with mom `**`from`**` 19:30 `**`to`**` 20:30 02 oct 2016` + +> ###### Specifying deadline : `by` +> This parameter is used to indicate the deadline of a task.
+> Format: **`by`**` [datetime]`
+ +> Examples: +> * **`add`**` submit proposal `**`by`**` 23:59 02 oct 2016` + +> ##### Optional parameters +> The following parameters can be appended to the add command.
+> A task can only have one of each type of parameter.
+> A task can only have either start/end time or deadline parameter.
+ +> ###### Specifying location : `at` +> This parameter is used to indicate the venue of a task.
+> Format: **`at`**` [location]`
+ +> Examples: +> * **`add`**` dinner with mom `**`at`**` home` + +> ###### Specifying remarks : `remarks` +> This parameter is used to add remarks for the task.
+> Format: **`remarks`**` [remarks]`
+ +> Examples: +> * **`add`**` dinner with mom `**`remarks`**` buy flowers` + +Examples: +* **`add`**` Prepare meeting agenda `**`by`**` 11:00 7 oct 2016` +* **`add`**` Jimmy's wedding banquet `**`from`**` 19:00 `**`to`**` 22:00 11 nov 2016` **`at`**` Trinity Church` + +### Listing all tasks : `list` +Shows the list of all tasks sorted temporally with the most recent ones displayed first.
+Format: **`list`**` [optional filter]`
+> [optional filter] available:
+> * today - shows the list of tasks for today's date +> * week - shows the list of tasks for this week (from Sunday to Saturday) +> * month - shows the list of tasks for the current month +> * date (e.g. 12 Oct 2016) - shows the list of tasks for the specified date + +Examples: +* **`list`** +* **`list`**` today` +* **`list`**` 12 Oct 2016` + + +### Finding all tasks containing keyword in the name: `find` +Finds tasks whose names contain the given keywords.
+Format: **`find`**` [findtype] [keyword 1 keyword 2 ...]` + +> The search is case insensitive.
+> Only the task name is searched.
+ +> [findtype] available:
+> * either - demands that tasks containing at least one keyword will be returned +> * all - demands that only tasks containing all keywords will be returned +> * phrase - demands that only tasks containing all the keywords in the exact order will be returned + +Examples: +* **`find`**` either mom dad sister`
+ Returns Any task having `mom`, `dad`, or `sister`in its name +* **`find`**` all mom dad sister`
+ Returns `dinner with mom, dad and sister` but not `dinner with mom and dad` +* **`find`**` phrase mom and dad`
+ Returns `dinner with mom and dad` but not `dinner with dad and mom` + + +### Deleting a task : `delete` +Delete the specified tasks from the to-do list.
+Format: **`delete`**` [index 1,index 2,...]` + +> This command is capable to deleting single and multiple tasks. For multiple tasks, the indexes are separated by a comma.
+> Deletes the task 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`. +* **`list`**` today`
+ **`delete`**` 2`
+ Deletes the 2nd task in the results of the **`list`** command. +* **`find`**` mom`
+ **`delete`**` 1,4`
+ Deletes the 1st and 4th tasks in the results of the **`find`** command. + +### Marking task as done : `done` +Mark the tasks identified by the index numbers used in the last task listing.
+Format: **`done`**` [index 1,index 2,...]` + +> This command is capable of marking single and multiple tasks. For multiple tasks, the indexes are separated by a comma.
+> Marks the tasks 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`**` today`
+ **`done`**` 2`
+ Marks the 2nd task in the results of the **`list`** command. +* **`find`**` mom`
+ **`done`**` 1,2`
+ Marks the 1st and 2nd tasks in the results of the **`find`** command. + +### Editing a task : `edit` +Edits the task identified by the index number used in the last task listing.
+Format: **`edit`**` [index] [optional parameter 1] [optional parameter 2]...` + +> Edits the tasks specified based on the details given.
+> [optional parameter] follows the format in **`add`** command.
+> The index refers to the index number shown in the most recent listing.
+> The index **must be a positive integer** 1, 2, 3, ...
+ +> Leaving the detail blank after entering a parameter identifier deletes the parameter.
+ +Examples: +* **`list`**
+ **`edit`**` 2 dinner with dad`
+ Edits the name of the the 2nd task in the results of the **`list`** command. +* **`find`**` dinner`
+ **`edit`**` 1 `**`from`**` 1830 `**`to`**` 2000 25 oct 2016 `**`at`**` popeyes`
+ Edits the time and location parameter of the 1st task in the results of the **`find`** command. +* **`find`**` dinner`
+ **`edit`**` 1 `**`at`**
+ Deletes the location parameter of the 1st task in the results of the **`find`** command. + + +### Undoing last action performed : `undo` +Undo the last action performed in Task!t. Reversible by `redo`
+Format: **`undo`** + +>Undo can be performed multiple times until there are no actions left to undo. + +### Redoing last undo action performed : `redo` +Redo the most recent undo operation done by the user in Task!t.
+Format: **`redo`** + +>If a command is used after undo, all previous undos will no longer be able to be redone. + + +### Setting the storage location : `setstorage` +Sets the location of the storage file.
+Format: **`setstorage`**` [filepath]` + +> Data file in the previously used storage path will be deleted and create a new file in the new path. +>If the folder doesn't exist, creating a new one. 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.
+* **`setstorage`**` user/documents/todolist`.
+ +### Clearing all entries : `clear` +Clears all entries from Task!t.
Format: `clear` -#### Exiting the program : `exit` +### Exiting the program : `exit` Exits the program.
Format: `exit` -#### Saving the data -Address book data are saved in the hard disk automatically after any command that changes the data.
+### Saving the data +Task!t data are saved in the hard disk automatically after any command that changes the data.
There is no need to save manually. ## FAQ **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 preious Address Book. - + the file that contains the data of your previous Task!t. + ## 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` -Help | `help` -Select | `select INDEX` +Add | **`add`**` [task_name] [optional parameter 1] [optional parameter 2]...` +Clear | **`clear`** +Delete | **`delete`**` [index 1,index 2,...]` +Done | **`done`**` [index 1,index 2,...]` +Edit | **`edit`**` [index] [optional parameter 1] [optional parameter 2]...` +Exit | **`exit`** +Find | **`find`**` [findtype] [keyword 1 keyword 2 ...]` +List | **`list`**` [filter]` +Help | **`help`** +Set storage | **`setstorage`**` [filepath]` +Undo | **`undo`** +Redo | **`redo`** diff --git a/docs/UsingGradle.md b/docs/UsingGradle.md index 09c5fc6a142f..c43576aef85b 100644 --- a/docs/UsingGradle.md +++ b/docs/UsingGradle.md @@ -34,7 +34,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 `todolist.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.
diff --git a/docs/diagrams/Diagrams.pptx b/docs/diagrams/Diagrams.pptx index 6a03bc10627d..c08fa1ca1679 100644 Binary files a/docs/diagrams/Diagrams.pptx and b/docs/diagrams/Diagrams.pptx differ diff --git a/docs/images/Carina.jpeg b/docs/images/Carina.jpeg new file mode 100644 index 000000000000..acd1d2ad4417 Binary files /dev/null and b/docs/images/Carina.jpeg 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/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 12849cdbb136..6aec9f6a8024 100644 Binary files a/docs/images/LogicClassDiagram.png and b/docs/images/LogicClassDiagram.png differ diff --git a/docs/images/ModelClassDiagram.png b/docs/images/ModelClassDiagram.png index 8cdf11ec93a1..60178340789b 100644 Binary files a/docs/images/ModelClassDiagram.png and b/docs/images/ModelClassDiagram.png differ diff --git a/docs/images/Nishant.jpg b/docs/images/Nishant.jpg new file mode 100644 index 000000000000..89a1d96aae5d Binary files /dev/null and b/docs/images/Nishant.jpg differ diff --git a/docs/images/ShiYa.png b/docs/images/ShiYa.png new file mode 100644 index 000000000000..2a43a6b32cd6 Binary files /dev/null and b/docs/images/ShiYa.png differ diff --git a/docs/images/StorageClassDiagram.png b/docs/images/StorageClassDiagram.png index 7a4cd2700cbf..74bfc8111ec1 100644 Binary files a/docs/images/StorageClassDiagram.png and b/docs/images/StorageClassDiagram.png differ diff --git a/docs/images/Taskit_ui.png b/docs/images/Taskit_ui.png new file mode 100644 index 000000000000..598b99e82998 Binary files /dev/null and b/docs/images/Taskit_ui.png differ diff --git a/docs/images/TsaiTi.jpg b/docs/images/TsaiTi.jpg new file mode 100644 index 000000000000..259b5c1ef149 Binary files /dev/null and b/docs/images/TsaiTi.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/Yingchen.JPG b/docs/images/Yingchen.JPG new file mode 100644 index 000000000000..d05fbccab7f4 Binary files /dev/null and b/docs/images/Yingchen.JPG 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/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/commons/util/UrlUtil.java b/src/main/java/seedu/address/commons/util/UrlUtil.java deleted file mode 100644 index c701fea753d5..000000000000 --- a/src/main/java/seedu/address/commons/util/UrlUtil.java +++ /dev/null @@ -1,24 +0,0 @@ -package seedu.address.commons.util; - -import java.net.URL; - -/** - * An utility class for URL - */ -public class UrlUtil { - - /** - * Returns true if both URLs have the same base URL - */ - public static boolean compareBaseUrls(URL url1, URL url2) { - - if (url1 == null || url2 == null) { - return false; - } - return url1.getHost().toLowerCase().replaceFirst("www.", "") - .equals(url2.getHost().replaceFirst("www.", "").toLowerCase()) - && url1.getPath().replaceAll("/", "").toLowerCase() - .equals(url2.getPath().replaceAll("/", "").toLowerCase()); - } - -} 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/Command.java b/src/main/java/seedu/address/logic/commands/Command.java deleted file mode 100644 index 7c0ba2fd0161..000000000000 --- a/src/main/java/seedu/address/logic/commands/Command.java +++ /dev/null @@ -1,46 +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.IncorrectCommandAttemptedEvent; -import seedu.address.model.Model; - -/** - * Represents a command with hidden internal logic and the ability to be executed. - */ -public abstract class Command { - protected Model model; - - /** - * Constructs a feedback message to summarise an operation that displayed a listing of persons. - * - * @param displaySize used to generate summary - * @return summary message for persons displayed - */ - public static String getMessageForPersonListShownSummary(int displaySize) { - return String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, displaySize); - } - - /** - * Executes the command and returns the result message. - * - * @return feedback message of the operation result for display - */ - public abstract CommandResult execute(); - - /** - * Provides any needed dependencies to the command. - * Commands making use of any of these should override this method to gain - * access to the dependencies. - */ - public void setData(Model model) { - this.model = model; - } - - /** - * Raises an event to indicate an attempt to execute an incorrect command - */ - protected void indicateAttemptToExecuteIncorrectCommand() { - EventsCenter.getInstance().post(new IncorrectCommandAttemptedEvent(this)); - } -} 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/FindCommand.java b/src/main/java/seedu/address/logic/commands/FindCommand.java deleted file mode 100644 index 1d61bf6cc857..000000000000 --- a/src/main/java/seedu/address/logic/commands/FindCommand.java +++ /dev/null @@ -1,30 +0,0 @@ -package seedu.address.logic.commands; - -import java.util.Set; - -/** - * Finds and lists all persons in address book whose name contains any of the argument keywords. - * Keyword matching is case sensitive. - */ -public class FindCommand extends Command { - - public static final String COMMAND_WORD = "find"; - - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all persons 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"; - - private final Set keywords; - - public FindCommand(Set keywords) { - this.keywords = keywords; - } - - @Override - public CommandResult execute() { - model.updateFilteredPersonList(keywords); - return new CommandResult(getMessageForPersonListShownSummary(model.getFilteredPersonList().size())); - } - -} 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 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/model/tag/Tag.java b/src/main/java/seedu/address/model/tag/Tag.java deleted file mode 100644 index 5bcffdb5ddf1..000000000000 --- a/src/main/java/seedu/address/model/tag/Tag.java +++ /dev/null @@ -1,60 +0,0 @@ -package seedu.address.model.tag; - - -import seedu.address.commons.exceptions.IllegalValueException; - -/** - * Represents a Tag in the address book. - * Guarantees: immutable; name is valid as declared in {@link #isValidTagName(String)} - */ -public class Tag { - - public static final String MESSAGE_TAG_CONSTRAINTS = "Tags names should be alphanumeric"; - public static final String TAG_VALIDATION_REGEX = "\\p{Alnum}+"; - - public String tagName; - - public Tag() { - } - - /** - * Validates given tag name. - * - * @throws IllegalValueException if the given tag name string is invalid. - */ - public Tag(String name) throws IllegalValueException { - assert name != null; - name = name.trim(); - if (!isValidTagName(name)) { - throw new IllegalValueException(MESSAGE_TAG_CONSTRAINTS); - } - this.tagName = name; - } - - /** - * Returns true if a given string is a valid tag name. - */ - public static boolean isValidTagName(String test) { - return test.matches(TAG_VALIDATION_REGEX); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof Tag // instanceof handles nulls - && this.tagName.equals(((Tag) other).tagName)); // state check - } - - @Override - public int hashCode() { - return tagName.hashCode(); - } - - /** - * Format state as text for viewing. - */ - public String toString() { - return '[' + tagName + ']'; - } - -} diff --git a/src/main/java/seedu/address/model/tag/UniqueTagList.java b/src/main/java/seedu/address/model/tag/UniqueTagList.java deleted file mode 100644 index d9b097b87b64..000000000000 --- a/src/main/java/seedu/address/model/tag/UniqueTagList.java +++ /dev/null @@ -1,143 +0,0 @@ -package seedu.address.model.tag; - -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 tags that enforces no nulls and uniqueness between its elements. - * - * Supports minimal set of list operations for the app's features. - * - * @see Tag#equals(Object) - * @see CollectionUtil#elementsAreUnique(Collection) - */ -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 { - protected DuplicateTagException() { - super("Operation would result in duplicate tags"); - } - } - - private final ObservableList internalList = FXCollections.observableArrayList(); - - /** - * Constructs empty TagList. - */ - public UniqueTagList() {} - - /** - * Varargs/array constructor, enforces no nulls or duplicates. - */ - public UniqueTagList(Tag... tags) throws DuplicateTagException { - assert !CollectionUtil.isAnyNull((Object[]) tags); - final List initialTags = Arrays.asList(tags); - if (!CollectionUtil.elementsAreUnique(initialTags)) { - throw new DuplicateTagException(); - } - internalList.addAll(initialTags); - } - - /** - * java collections constructor, enforces no null or duplicate elements. - */ - public UniqueTagList(Collection tags) throws DuplicateTagException { - CollectionUtil.assertNoNullElements(tags); - if (!CollectionUtil.elementsAreUnique(tags)) { - throw new DuplicateTagException(); - } - internalList.addAll(tags); - } - - /** - * java set constructor, enforces no nulls. - */ - public UniqueTagList(Set tags) { - CollectionUtil.assertNoNullElements(tags); - internalList.addAll(tags); - } - - /** - * Copy constructor, insulates from changes in source. - */ - public UniqueTagList(UniqueTagList source) { - internalList.addAll(source.internalList); // insulate internal list from changes in argument - } - - /** - * All tags in this list as a Set. This set is mutable and change-insulated against the internal list. - */ - public Set toSet() { - return new HashSet<>(internalList); - } - - /** - * Replaces the Tags in this list with those in the argument tag list. - */ - public void setTags(UniqueTagList replacement) { - this.internalList.clear(); - this.internalList.addAll(replacement.internalList); - } - - /** - * Adds every tag from the argument list that does not yet exist in this list. - */ - public void mergeFrom(UniqueTagList tags) { - final Set alreadyInside = this.toSet(); - for (Tag tag : tags) { - if (!alreadyInside.contains(tag)) { - internalList.add(tag); - } - } - } - - /** - * Checks if the list contains an equivalent Tag as the given argument. - */ - public boolean contains(Tag toCheck) { - assert toCheck != null; - return internalList.contains(toCheck); - } - - /** - * Adds a Tag to the list. - * - * @throws DuplicateTagException if the Tag to add is a duplicate of an existing Tag in the list. - */ - public void add(Tag toAdd) throws DuplicateTagException { - assert toAdd != null; - if (contains(toAdd)) { - throw new DuplicateTagException(); - } - internalList.add(toAdd); - } - - @Override - public Iterator iterator() { - return internalList.iterator(); - } - - public ObservableList getInternalList() { - return internalList; - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof UniqueTagList // instanceof handles nulls - && this.internalList.equals( - ((UniqueTagList) 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 ffe589ac3c4c..000000000000 --- a/src/main/java/seedu/address/storage/AddressBookStorage.java +++ /dev/null @@ -1,34 +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; - - /** - * 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; - -} 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 767490dbf8e2..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, FileNotFoundException; - - @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 a6f2eddcc802..000000000000 --- a/src/main/java/seedu/address/storage/StorageManager.java +++ /dev/null @@ -1,77 +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 XmlAddressBookStorage addressBookStorage; - private JsonUserPrefStorage userPrefStorage; - - - public StorageManager(String addressBookFilePath, String userPrefsFilePath) { - super(); - this.addressBookStorage = new XmlAddressBookStorage(addressBookFilePath); - this.userPrefStorage = new JsonUserPrefStorage(userPrefsFilePath); - } - - // ================ UserPrefs methods ============================== - - @Override - public Optional readUserPrefs() throws DataConversionException, IOException { - return userPrefStorage.readUserPrefs(); - } - - @Override - public void saveUserPrefs(UserPrefs userPrefs) throws IOException { - userPrefStorage.saveUserPrefs(userPrefs); - } - - - // ================ AddressBook methods ============================== - - @Override - public String getAddressBookFilePath() { - return addressBookStorage.getAddressBookFilePath(); - } - - @Override - public Optional readAddressBook() throws DataConversionException, FileNotFoundException { - logger.fine("Attempting to read data from file: " + addressBookStorage.getAddressBookFilePath()); - - return addressBookStorage.readAddressBook(addressBookStorage.getAddressBookFilePath()); - } - - @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException { - addressBookStorage.saveAddressBook(addressBook, addressBookStorage.getAddressBookFilePath()); - } - - - @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/XmlAdaptedTag.java b/src/main/java/seedu/address/storage/XmlAdaptedTag.java deleted file mode 100644 index b9723fafbc67..000000000000 --- a/src/main/java/seedu/address/storage/XmlAdaptedTag.java +++ /dev/null @@ -1,40 +0,0 @@ -package seedu.address.storage; - -import seedu.address.commons.util.CollectionUtil; -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.tag.Tag; - -import javax.xml.bind.annotation.XmlValue; - -/** - * JAXB-friendly adapted version of the Tag. - */ -public class XmlAdaptedTag { - - @XmlValue - public String tagName; - - /** - * No-arg constructor for JAXB use. - */ - public XmlAdaptedTag() {} - - /** - * Converts a given Tag into this class for JAXB use. - * - * @param source future changes to this will not affect the created - */ - public XmlAdaptedTag(Tag source) { - tagName = source.tagName; - } - - /** - * Converts this jaxb-friendly adapted tag object into the model's Tag object. - * - * @throws IllegalValueException if there were any data constraints violated in the adapted person - */ - public Tag toModelType() throws IllegalValueException { - return new Tag(tagName); - } - -} 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/storage/XmlSerializableAddressBook.java b/src/main/java/seedu/address/storage/XmlSerializableAddressBook.java deleted file mode 100644 index 2a496e1494f8..000000000000 --- a/src/main/java/seedu/address/storage/XmlSerializableAddressBook.java +++ /dev/null @@ -1,86 +0,0 @@ -package seedu.address.storage; - -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.tag.Tag; -import seedu.address.model.tag.UniqueTagList; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.ReadOnlyPerson; -import seedu.address.model.person.UniquePersonList; - -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlRootElement; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; - -/** - * An Immutable AddressBook that is serializable to XML format - */ -@XmlRootElement(name = "addressbook") -public class XmlSerializableAddressBook implements ReadOnlyAddressBook { - - @XmlElement - private List persons; - @XmlElement - private List tags; - - { - persons = new ArrayList<>(); - tags = new ArrayList<>(); - } - - /** - * Empty constructor required for marshalling - */ - public XmlSerializableAddressBook() {} - - /** - * Conversion - */ - public XmlSerializableAddressBook(ReadOnlyAddressBook src) { - persons.addAll(src.getPersonList().stream().map(XmlAdaptedPerson::new).collect(Collectors.toList())); - tags = src.getTagList(); - } - - @Override - public UniqueTagList getUniqueTagList() { - try { - return new UniqueTagList(tags); - } catch (UniqueTagList.DuplicateTagException e) { - e.printStackTrace(); - return null; - } - } - - @Override - public UniquePersonList getUniquePersonList() { - UniquePersonList lists = new UniquePersonList(); - for (XmlAdaptedPerson p : persons) { - try { - lists.add(p.toModelType()); - } catch (IllegalValueException e) { - - } - } - return lists; - } - - @Override - public List getPersonList() { - return persons.stream().map(p -> { - try { - return p.toModelType(); - } catch (IllegalValueException e) { - e.printStackTrace(); - return null; - } - }).collect(Collectors.toCollection(ArrayList::new)); - } - - @Override - public List getTagList() { - return Collections.unmodifiableList(tags); - } - -} diff --git a/src/main/java/seedu/address/ui/BrowserPanel.java b/src/main/java/seedu/address/ui/BrowserPanel.java deleted file mode 100644 index 54b88318019b..000000000000 --- a/src/main/java/seedu/address/ui/BrowserPanel.java +++ /dev/null @@ -1,68 +0,0 @@ -package seedu.address.ui; - -import javafx.event.Event; -import javafx.scene.Node; -import javafx.scene.layout.AnchorPane; -import javafx.scene.web.WebView; -import seedu.address.commons.util.FxViewUtil; -import seedu.address.model.person.ReadOnlyPerson; -import seedu.address.commons.core.LogsCenter; - -import java.util.logging.Logger; - -/** - * The Browser Panel of the App. - */ -public class BrowserPanel extends UiPart{ - - private static Logger logger = LogsCenter.getLogger(BrowserPanel.class); - private WebView browser; - - /** - * Constructor is kept private as {@link #load(AnchorPane)} is the only way to create a BrowserPanel. - */ - private BrowserPanel() { - - } - - @Override - public void setNode(Node node) { - //not applicable - } - - @Override - public String getFxmlPath() { - return null; //not applicable - } - - /** - * Factory method for creating a Browser Panel. - * This method should be called after the FX runtime is initialized and in FX application thread. - * @param placeholder The AnchorPane where the BrowserPanel must be inserted - */ - public static BrowserPanel load(AnchorPane placeholder){ - logger.info("Initializing browser"); - BrowserPanel browserPanel = new BrowserPanel(); - browserPanel.browser = new WebView(); - placeholder.setOnKeyPressed(Event::consume); // To prevent triggering events for typing inside the loaded Web page. - FxViewUtil.applyAnchorBoundaryParameters(browserPanel.browser, 0.0, 0.0, 0.0, 0.0); - placeholder.getChildren().add(browserPanel.browser); - return browserPanel; - } - - public void loadPersonPage(ReadOnlyPerson person) { - loadPage("https://www.google.com.sg/#safe=off&q=" + person.getName().fullName.replaceAll(" ", "+")); - } - - public void loadPage(String url){ - browser.getEngine().load(url); - } - - /** - * Frees resources allocated to the browser. - */ - public void freeResources() { - browser = null; - } - -} 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/todolist/MainApp.java similarity index 77% rename from src/main/java/seedu/address/MainApp.java rename to src/main/java/seedu/todolist/MainApp.java index 4b48efe60824..f7d1933ebe34 100644 --- a/src/main/java/seedu/address/MainApp.java +++ b/src/main/java/seedu/todolist/MainApp.java @@ -1,24 +1,24 @@ -package seedu.address; +package seedu.todolist; 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 seedu.todolist.commons.core.Config; +import seedu.todolist.commons.core.EventsCenter; +import seedu.todolist.commons.core.LogsCenter; +import seedu.todolist.commons.core.Version; +import seedu.todolist.commons.events.ui.ExitAppRequestEvent; +import seedu.todolist.commons.exceptions.DataConversionException; +import seedu.todolist.commons.util.ConfigUtil; +import seedu.todolist.commons.util.StringUtil; +import seedu.todolist.logic.Logic; +import seedu.todolist.logic.LogicManager; +import seedu.todolist.model.*; +import seedu.todolist.storage.Storage; +import seedu.todolist.storage.StorageManager; +import seedu.todolist.ui.Ui; +import seedu.todolist.ui.UiManager; import java.io.FileNotFoundException; import java.io.IOException; @@ -41,15 +41,13 @@ public class MainApp extends Application { protected Config config; protected UserPrefs userPrefs; - public MainApp() {} - @Override public void init() throws Exception { - logger.info("=============================[ Initializing AddressBook ]==========================="); + logger.info("=============================[ Initializing ToDoList ]==========================="); super.init(); config = initConfig(getApplicationParameter("config")); - storage = new StorageManager(config.getAddressBookFilePath(), config.getUserPrefsFilePath()); + storage = new StorageManager(config.getToDoListFilePath(), config.getUserPrefsFilePath()); userPrefs = initPrefs(config); @@ -59,7 +57,7 @@ public void init() throws Exception { logic = new LogicManager(model, storage); - ui = new UiManager(logic, config, userPrefs); + ui = new UiManager(logic, config, userPrefs, storage); initEventsCenter(); } @@ -70,20 +68,20 @@ private String getApplicationParameter(String parameterName){ } private Model initModelManager(Storage storage, UserPrefs userPrefs) { - Optional addressBookOptional; - ReadOnlyAddressBook initialData; + Optional ToDoListOptional; + ReadOnlyToDoList initialData; try { - addressBookOptional = storage.readAddressBook(); - if(!addressBookOptional.isPresent()){ - logger.info("Data file not found. Will be starting with an empty AddressBook"); + ToDoListOptional = storage.readToDoList(); + if(!ToDoListOptional.isPresent()){ + logger.info("Data file not found. Will be starting with an empty ToDoList"); } - initialData = addressBookOptional.orElse(new AddressBook()); + initialData = ToDoListOptional.orElse(new ToDoList()); } 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 ToDoList"); + initialData = new ToDoList(); } catch (FileNotFoundException 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 ToDoList"); + initialData = new ToDoList(); } return new ModelManager(initialData, userPrefs); @@ -139,7 +137,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 ToDoList"); initializedPrefs = new UserPrefs(); } @@ -159,7 +157,7 @@ private void initEventsCenter() { @Override public void start(Stage primaryStage) { - logger.info("Starting AddressBook " + MainApp.VERSION); + logger.info("Starting ToDoList " + MainApp.VERSION); ui.start(primaryStage); } diff --git a/src/main/java/seedu/address/commons/core/ComponentManager.java b/src/main/java/seedu/todolist/commons/core/ComponentManager.java similarity index 86% rename from src/main/java/seedu/address/commons/core/ComponentManager.java rename to src/main/java/seedu/todolist/commons/core/ComponentManager.java index 4bc8564e5824..a451356781a6 100644 --- a/src/main/java/seedu/address/commons/core/ComponentManager.java +++ b/src/main/java/seedu/todolist/commons/core/ComponentManager.java @@ -1,6 +1,6 @@ -package seedu.address.commons.core; +package seedu.todolist.commons.core; -import seedu.address.commons.events.BaseEvent; +import seedu.todolist.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/todolist/commons/core/Config.java similarity index 65% rename from src/main/java/seedu/address/commons/core/Config.java rename to src/main/java/seedu/todolist/commons/core/Config.java index 6441c9ef20f4..35a457a32a88 100644 --- a/src/main/java/seedu/address/commons/core/Config.java +++ b/src/main/java/seedu/todolist/commons/core/Config.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package seedu.todolist.commons.core; import java.util.Objects; import java.util.logging.Level; @@ -11,15 +11,11 @@ public class Config { public static final String DEFAULT_CONFIG_FILE = "config.json"; // Config values customizable through config file - private String appTitle = "Address App"; + private String appTitle = "Task!t"; private Level logLevel = Level.INFO; private String userPrefsFilePath = "preferences.json"; - private String addressBookFilePath = "data/addressbook.xml"; - private String addressBookName = "MyAddressBook"; - - - public Config() { - } + private String ToDoListFilePath = "data/todolist.xml"; + private String ToDoListName = "MyToDoList"; public String getAppTitle() { return appTitle; @@ -45,20 +41,20 @@ public void setUserPrefsFilePath(String userPrefsFilePath) { this.userPrefsFilePath = userPrefsFilePath; } - public String getAddressBookFilePath() { - return addressBookFilePath; + public String getToDoListFilePath() { + return ToDoListFilePath; } - public void setAddressBookFilePath(String addressBookFilePath) { - this.addressBookFilePath = addressBookFilePath; + public void setToDoListFilePath(String ToDoListFilePath) { + this.ToDoListFilePath = ToDoListFilePath; } - public String getAddressBookName() { - return addressBookName; + public String getToDoListName() { + return ToDoListName; } - public void setAddressBookName(String addressBookName) { - this.addressBookName = addressBookName; + public void setToDoListName(String ToDoListName) { + this.ToDoListName = ToDoListName; } @@ -76,13 +72,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(ToDoListFilePath, o.ToDoListFilePath) + && Objects.equals(ToDoListName, o.ToDoListName); } @Override public int hashCode() { - return Objects.hash(appTitle, logLevel, userPrefsFilePath, addressBookFilePath, addressBookName); + return Objects.hash(appTitle, logLevel, userPrefsFilePath, ToDoListFilePath, ToDoListName); } @Override @@ -91,8 +87,8 @@ 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 : " + ToDoListFilePath); + sb.append("\nToDoList name : " + ToDoListName); return sb.toString(); } diff --git a/src/main/java/seedu/address/commons/core/EventsCenter.java b/src/main/java/seedu/todolist/commons/core/EventsCenter.java similarity index 92% rename from src/main/java/seedu/address/commons/core/EventsCenter.java rename to src/main/java/seedu/todolist/commons/core/EventsCenter.java index 9652cd5c227b..1ef7bf68afb9 100644 --- a/src/main/java/seedu/address/commons/core/EventsCenter.java +++ b/src/main/java/seedu/todolist/commons/core/EventsCenter.java @@ -1,7 +1,8 @@ -package seedu.address.commons.core; +package seedu.todolist.commons.core; import com.google.common.eventbus.EventBus; -import seedu.address.commons.events.BaseEvent; + +import seedu.todolist.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/todolist/commons/core/GuiSettings.java similarity index 98% rename from src/main/java/seedu/address/commons/core/GuiSettings.java rename to src/main/java/seedu/todolist/commons/core/GuiSettings.java index e157ac8b8679..1830235ec901 100644 --- a/src/main/java/seedu/address/commons/core/GuiSettings.java +++ b/src/main/java/seedu/todolist/commons/core/GuiSettings.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package seedu.todolist.commons.core; import java.awt.*; import java.io.Serializable; diff --git a/src/main/java/seedu/address/commons/core/LogsCenter.java b/src/main/java/seedu/todolist/commons/core/LogsCenter.java similarity index 96% rename from src/main/java/seedu/address/commons/core/LogsCenter.java rename to src/main/java/seedu/todolist/commons/core/LogsCenter.java index 17939bab4975..3967ecc6523d 100644 --- a/src/main/java/seedu/address/commons/core/LogsCenter.java +++ b/src/main/java/seedu/todolist/commons/core/LogsCenter.java @@ -1,10 +1,10 @@ -package seedu.address.commons.core; - -import seedu.address.commons.events.BaseEvent; +package seedu.todolist.commons.core; import java.io.IOException; import java.util.logging.*; +import seedu.todolist.commons.events.BaseEvent; + /** * Configures and manages loggers and handlers, including their logging level * Named {@link Logger}s can be obtained from this class
@@ -15,7 +15,7 @@ public class LogsCenter { private static final int MAX_FILE_COUNT = 5; private static final int MAX_FILE_SIZE_IN_BYTES = (int) (Math.pow(2, 20) * 5); // 5MB - private static final String LOG_FILE = "addressbook.log"; + private static final String LOG_FILE = "todolist.log"; private static Level currentLogLevel = Level.INFO; private static final Logger logger = LogsCenter.getLogger(LogsCenter.class); private static FileHandler fileHandler; diff --git a/src/main/java/seedu/todolist/commons/core/Messages.java b/src/main/java/seedu/todolist/commons/core/Messages.java new file mode 100644 index 000000000000..3bce6bb3843d --- /dev/null +++ b/src/main/java/seedu/todolist/commons/core/Messages.java @@ -0,0 +1,15 @@ +package seedu.todolist.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_TASK_DISPLAYED_INDEX = "The task index provided is invalid"; + public static final String MESSAGE_INCOMPLETE_TASKS_LISTED_OVERVIEW = "%1$d incomplete tasks listed!\n"; + public static final String MESSAGE_COMPLETED_TASKS_LISTED_OVERVIEW = "%1$d completed tasks listed!\n"; + public static final String MESSAGE_OVERDUE_TASKS_LISTED_OVERVIEW = "%1$d overdue tasks listed!"; + +} diff --git a/src/main/java/seedu/address/commons/core/UnmodifiableObservableList.java b/src/main/java/seedu/todolist/commons/core/UnmodifiableObservableList.java similarity index 99% rename from src/main/java/seedu/address/commons/core/UnmodifiableObservableList.java rename to src/main/java/seedu/todolist/commons/core/UnmodifiableObservableList.java index 5c25d8647a8d..c759959accc8 100644 --- a/src/main/java/seedu/address/commons/core/UnmodifiableObservableList.java +++ b/src/main/java/seedu/todolist/commons/core/UnmodifiableObservableList.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package seedu.todolist.commons.core; import javafx.beans.InvalidationListener; import javafx.collections.ListChangeListener; diff --git a/src/main/java/seedu/address/commons/core/Version.java b/src/main/java/seedu/todolist/commons/core/Version.java similarity index 98% rename from src/main/java/seedu/address/commons/core/Version.java rename to src/main/java/seedu/todolist/commons/core/Version.java index 7ecb85b18f82..4b2fc0d7698e 100644 --- a/src/main/java/seedu/address/commons/core/Version.java +++ b/src/main/java/seedu/todolist/commons/core/Version.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package seedu.todolist.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/todolist/commons/events/BaseEvent.java similarity index 90% rename from src/main/java/seedu/address/commons/events/BaseEvent.java rename to src/main/java/seedu/todolist/commons/events/BaseEvent.java index 723a9c69fbd5..6979573d65fb 100644 --- a/src/main/java/seedu/address/commons/events/BaseEvent.java +++ b/src/main/java/seedu/todolist/commons/events/BaseEvent.java @@ -1,4 +1,4 @@ -package seedu.address.commons.events; +package seedu.todolist.commons.events; public abstract class BaseEvent { diff --git a/src/main/java/seedu/todolist/commons/events/model/ToDoListChangedEvent.java b/src/main/java/seedu/todolist/commons/events/model/ToDoListChangedEvent.java new file mode 100644 index 000000000000..0218b4089f2d --- /dev/null +++ b/src/main/java/seedu/todolist/commons/events/model/ToDoListChangedEvent.java @@ -0,0 +1,19 @@ +package seedu.todolist.commons.events.model; + +import seedu.todolist.commons.events.BaseEvent; +import seedu.todolist.model.ReadOnlyToDoList; + +/** Indicates the ToDoList in the model has changed*/ +public class ToDoListChangedEvent extends BaseEvent { + + public final ReadOnlyToDoList data; + + public ToDoListChangedEvent(ReadOnlyToDoList data){ + this.data = data; + } + + @Override + public String toString() { + return "number of tasks " + data.getTaskList().size(); + } +} diff --git a/src/main/java/seedu/address/commons/events/storage/DataSavingExceptionEvent.java b/src/main/java/seedu/todolist/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/todolist/commons/events/storage/DataSavingExceptionEvent.java index f0a0640ee523..24c6ab8c4115 100644 --- a/src/main/java/seedu/address/commons/events/storage/DataSavingExceptionEvent.java +++ b/src/main/java/seedu/todolist/commons/events/storage/DataSavingExceptionEvent.java @@ -1,6 +1,6 @@ -package seedu.address.commons.events.storage; +package seedu.todolist.commons.events.storage; -import seedu.address.commons.events.BaseEvent; +import seedu.todolist.commons.events.BaseEvent; /** * Indicates an exception during a file saving diff --git a/src/main/java/seedu/address/commons/events/ui/ExitAppRequestEvent.java b/src/main/java/seedu/todolist/commons/events/ui/ExitAppRequestEvent.java similarity index 65% rename from src/main/java/seedu/address/commons/events/ui/ExitAppRequestEvent.java rename to src/main/java/seedu/todolist/commons/events/ui/ExitAppRequestEvent.java index c2f071ddbd11..bc7f280fe8e1 100644 --- a/src/main/java/seedu/address/commons/events/ui/ExitAppRequestEvent.java +++ b/src/main/java/seedu/todolist/commons/events/ui/ExitAppRequestEvent.java @@ -1,6 +1,6 @@ -package seedu.address.commons.events.ui; +package seedu.todolist.commons.events.ui; -import seedu.address.commons.events.BaseEvent; +import seedu.todolist.commons.events.BaseEvent; /** * diff --git a/src/main/java/seedu/address/commons/events/ui/IncorrectCommandAttemptedEvent.java b/src/main/java/seedu/todolist/commons/events/ui/IncorrectCommandAttemptedEvent.java similarity index 54% rename from src/main/java/seedu/address/commons/events/ui/IncorrectCommandAttemptedEvent.java rename to src/main/java/seedu/todolist/commons/events/ui/IncorrectCommandAttemptedEvent.java index 991f7ae9fa25..60000f61cf94 100644 --- a/src/main/java/seedu/address/commons/events/ui/IncorrectCommandAttemptedEvent.java +++ b/src/main/java/seedu/todolist/commons/events/ui/IncorrectCommandAttemptedEvent.java @@ -1,15 +1,12 @@ -package seedu.address.commons.events.ui; +package seedu.todolist.commons.events.ui; -import seedu.address.commons.events.BaseEvent; -import seedu.address.logic.commands.Command; +import seedu.todolist.commons.events.BaseEvent; /** * Indicates an attempt to execute an incorrect command */ public class IncorrectCommandAttemptedEvent extends BaseEvent { - public IncorrectCommandAttemptedEvent(Command command) {} - @Override public String toString() { return this.getClass().getSimpleName(); diff --git a/src/main/java/seedu/address/commons/events/ui/JumpToListRequestEvent.java b/src/main/java/seedu/todolist/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/todolist/commons/events/ui/JumpToListRequestEvent.java index 0580d27aecf5..1cf5473dd716 100644 --- a/src/main/java/seedu/address/commons/events/ui/JumpToListRequestEvent.java +++ b/src/main/java/seedu/todolist/commons/events/ui/JumpToListRequestEvent.java @@ -1,9 +1,9 @@ -package seedu.address.commons.events.ui; +package seedu.todolist.commons.events.ui; -import seedu.address.commons.events.BaseEvent; +import seedu.todolist.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/ShowHelpEvent.java b/src/main/java/seedu/todolist/commons/events/ui/ShowHelpEvent.java similarity index 69% rename from src/main/java/seedu/address/commons/events/ui/ShowHelpEvent.java rename to src/main/java/seedu/todolist/commons/events/ui/ShowHelpEvent.java index 8e267fbc027a..a28b77628490 100644 --- a/src/main/java/seedu/address/commons/events/ui/ShowHelpEvent.java +++ b/src/main/java/seedu/todolist/commons/events/ui/ShowHelpEvent.java @@ -1,6 +1,6 @@ -package seedu.address.commons.events.ui; +package seedu.todolist.commons.events.ui; -import seedu.address.commons.events.BaseEvent; +import seedu.todolist.commons.events.BaseEvent; /** * An event requesting to view the help page. diff --git a/src/main/java/seedu/todolist/commons/events/ui/TaskPanelSelectionChangedEvent.java b/src/main/java/seedu/todolist/commons/events/ui/TaskPanelSelectionChangedEvent.java new file mode 100644 index 000000000000..f9d8ffbe72ce --- /dev/null +++ b/src/main/java/seedu/todolist/commons/events/ui/TaskPanelSelectionChangedEvent.java @@ -0,0 +1,26 @@ +package seedu.todolist.commons.events.ui; + +import seedu.todolist.commons.events.BaseEvent; +import seedu.todolist.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/todolist/commons/exceptions/DataConversionException.java similarity index 84% rename from src/main/java/seedu/address/commons/exceptions/DataConversionException.java rename to src/main/java/seedu/todolist/commons/exceptions/DataConversionException.java index 1f689bd8e3f9..9382d35a1770 100644 --- a/src/main/java/seedu/address/commons/exceptions/DataConversionException.java +++ b/src/main/java/seedu/todolist/commons/exceptions/DataConversionException.java @@ -1,4 +1,4 @@ -package seedu.address.commons.exceptions; +package seedu.todolist.commons.exceptions; /** * Represents an error during conversion of data from one format to another diff --git a/src/main/java/seedu/address/commons/exceptions/DuplicateDataException.java b/src/main/java/seedu/todolist/commons/exceptions/DuplicateDataException.java similarity index 84% rename from src/main/java/seedu/address/commons/exceptions/DuplicateDataException.java rename to src/main/java/seedu/todolist/commons/exceptions/DuplicateDataException.java index 17aa63d5020c..be63efcc3f86 100644 --- a/src/main/java/seedu/address/commons/exceptions/DuplicateDataException.java +++ b/src/main/java/seedu/todolist/commons/exceptions/DuplicateDataException.java @@ -1,4 +1,4 @@ -package seedu.address.commons.exceptions; +package seedu.todolist.commons.exceptions; /** * Signals an error caused by duplicate data where there should be none. diff --git a/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java b/src/main/java/seedu/todolist/commons/exceptions/IllegalValueException.java similarity index 88% rename from src/main/java/seedu/address/commons/exceptions/IllegalValueException.java rename to src/main/java/seedu/todolist/commons/exceptions/IllegalValueException.java index a473b43bd86f..ddb702a4747c 100644 --- a/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java +++ b/src/main/java/seedu/todolist/commons/exceptions/IllegalValueException.java @@ -1,4 +1,4 @@ -package seedu.address.commons.exceptions; +package seedu.todolist.commons.exceptions; /** * Signals that some given data does not fulfill some constraints. diff --git a/src/main/java/seedu/address/commons/util/AppUtil.java b/src/main/java/seedu/todolist/commons/util/AppUtil.java similarity index 78% rename from src/main/java/seedu/address/commons/util/AppUtil.java rename to src/main/java/seedu/todolist/commons/util/AppUtil.java index 09a528f1188e..20bd4032836e 100644 --- a/src/main/java/seedu/address/commons/util/AppUtil.java +++ b/src/main/java/seedu/todolist/commons/util/AppUtil.java @@ -1,7 +1,7 @@ -package seedu.address.commons.util; +package seedu.todolist.commons.util; import javafx.scene.image.Image; -import seedu.address.MainApp; +import seedu.todolist.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/todolist/commons/util/CollectionUtil.java similarity index 96% rename from src/main/java/seedu/address/commons/util/CollectionUtil.java rename to src/main/java/seedu/todolist/commons/util/CollectionUtil.java index 53b18e629f3c..de716f0f970d 100644 --- a/src/main/java/seedu/address/commons/util/CollectionUtil.java +++ b/src/main/java/seedu/todolist/commons/util/CollectionUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.todolist.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/todolist/commons/util/ConfigUtil.java similarity index 90% rename from src/main/java/seedu/address/commons/util/ConfigUtil.java rename to src/main/java/seedu/todolist/commons/util/ConfigUtil.java index af42e03df06c..f79c36b83d9d 100644 --- a/src/main/java/seedu/address/commons/util/ConfigUtil.java +++ b/src/main/java/seedu/todolist/commons/util/ConfigUtil.java @@ -1,8 +1,8 @@ -package seedu.address.commons.util; +package seedu.todolist.commons.util; -import seedu.address.commons.core.Config; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.exceptions.DataConversionException; +import seedu.todolist.commons.core.Config; +import seedu.todolist.commons.core.LogsCenter; +import seedu.todolist.commons.exceptions.DataConversionException; import java.io.File; import java.io.IOException; diff --git a/src/main/java/seedu/address/commons/util/FileUtil.java b/src/main/java/seedu/todolist/commons/util/FileUtil.java similarity index 98% rename from src/main/java/seedu/address/commons/util/FileUtil.java rename to src/main/java/seedu/todolist/commons/util/FileUtil.java index ca8221250de4..b7c7b13d49b9 100644 --- a/src/main/java/seedu/address/commons/util/FileUtil.java +++ b/src/main/java/seedu/todolist/commons/util/FileUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.todolist.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/todolist/commons/util/FxViewUtil.java similarity index 92% rename from src/main/java/seedu/address/commons/util/FxViewUtil.java rename to src/main/java/seedu/todolist/commons/util/FxViewUtil.java index 900efa6bf5c3..71b067b86e10 100644 --- a/src/main/java/seedu/address/commons/util/FxViewUtil.java +++ b/src/main/java/seedu/todolist/commons/util/FxViewUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.todolist.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/todolist/commons/util/JsonUtil.java similarity index 98% rename from src/main/java/seedu/address/commons/util/JsonUtil.java rename to src/main/java/seedu/todolist/commons/util/JsonUtil.java index 80b67de5b7e8..2c90d6cc977d 100644 --- a/src/main/java/seedu/address/commons/util/JsonUtil.java +++ b/src/main/java/seedu/todolist/commons/util/JsonUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.todolist.commons.util; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; @@ -18,6 +18,15 @@ * Converts a Java object instance to JSON and vice versa */ public class JsonUtil { + private static ObjectMapper objectMapper = new ObjectMapper().findAndRegisterModules() + .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE) + .setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY) + .registerModule(new SimpleModule("SimpleModule") + .addSerializer(Level.class, new ToStringSerializer()) + .addDeserializer(Level.class, new LevelDeserializer(Level.class))); + private static class LevelDeserializer extends FromStringDeserializer { protected LevelDeserializer(Class vc) { @@ -47,15 +56,6 @@ public Class handledType() { } } - private static ObjectMapper objectMapper = new ObjectMapper().findAndRegisterModules() - .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) - .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) - .setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE) - .setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY) - .registerModule(new SimpleModule("SimpleModule") - .addSerializer(Level.class, new ToStringSerializer()) - .addDeserializer(Level.class, new LevelDeserializer(Level.class))); - /** * Converts a given string representation of a JSON data to instance of a class * @param The generic type to create an instance of diff --git a/src/main/java/seedu/address/commons/util/StringUtil.java b/src/main/java/seedu/todolist/commons/util/StringUtil.java similarity index 96% rename from src/main/java/seedu/address/commons/util/StringUtil.java rename to src/main/java/seedu/todolist/commons/util/StringUtil.java index 0e7f12c80042..242de6928349 100644 --- a/src/main/java/seedu/address/commons/util/StringUtil.java +++ b/src/main/java/seedu/todolist/commons/util/StringUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.todolist.commons.util; import java.io.PrintWriter; import java.io.StringWriter; diff --git a/src/main/java/seedu/todolist/commons/util/UrlUtil.java b/src/main/java/seedu/todolist/commons/util/UrlUtil.java new file mode 100644 index 000000000000..ee352f7ad7d8 --- /dev/null +++ b/src/main/java/seedu/todolist/commons/util/UrlUtil.java @@ -0,0 +1,23 @@ +package seedu.todolist.commons.util; + +import java.net.URL; + +/** + * An utility class for URL + */ +public class UrlUtil { + + /** + * Returns true if both URLs have the same base URL + */ + public static boolean compareBaseUrls(URL url1, URL url2) { + + if (url1 == null || url2 == null) { + return false; + } + return url1.getHost().replaceFirst("www.", ""). + equalsIgnoreCase(url2.getHost().replaceFirst("www.", "")) + && url1.getPath().replaceAll("/", "").equalsIgnoreCase(url2.getPath().replaceAll("/", "")); + } + +} diff --git a/src/main/java/seedu/address/commons/util/XmlUtil.java b/src/main/java/seedu/todolist/commons/util/XmlUtil.java similarity index 98% rename from src/main/java/seedu/address/commons/util/XmlUtil.java rename to src/main/java/seedu/todolist/commons/util/XmlUtil.java index 2087e7628a1d..a7499af5a46b 100644 --- a/src/main/java/seedu/address/commons/util/XmlUtil.java +++ b/src/main/java/seedu/todolist/commons/util/XmlUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.todolist.commons.util; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; diff --git a/src/main/java/seedu/todolist/logic/Logic.java b/src/main/java/seedu/todolist/logic/Logic.java new file mode 100644 index 000000000000..eeb11874215c --- /dev/null +++ b/src/main/java/seedu/todolist/logic/Logic.java @@ -0,0 +1,36 @@ +package seedu.todolist.logic; + +import javafx.collections.ObservableList; +import seedu.todolist.logic.commands.CommandResult; +import seedu.todolist.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); + + /** Returns the filtered list of all tasks */ + ObservableList getFilteredAllTaskList(); + + /** Returns the filtered complete list of tasks */ + ObservableList getFilteredCompleteTaskList(); + + /** Returns the filtered incomplete list of tasks */ + ObservableList getFilteredIncompleteTaskList(); + + /** Returns the filtered overdue list of tasks */ + ObservableList getFilteredOverdueTaskList(); + + /** Sets the current tab */ + void setCurrentTab(String tab); + + /** Gets the current tab */ + String getCurrentTab(); + +} diff --git a/src/main/java/seedu/todolist/logic/LogicManager.java b/src/main/java/seedu/todolist/logic/LogicManager.java new file mode 100644 index 000000000000..baf06871814d --- /dev/null +++ b/src/main/java/seedu/todolist/logic/LogicManager.java @@ -0,0 +1,72 @@ +package seedu.todolist.logic; + +import javafx.collections.ObservableList; +import seedu.todolist.commons.core.ComponentManager; +import seedu.todolist.commons.core.LogsCenter; +import seedu.todolist.logic.commands.Command; +import seedu.todolist.logic.commands.CommandResult; +import seedu.todolist.logic.parser.CommandParser; +import seedu.todolist.model.Model; +import seedu.todolist.model.task.ReadOnlyTask; +import seedu.todolist.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 CommandParser parser; + private final Storage storage; + + //@@author A0158963M + public LogicManager(Model model, Storage storage) { + this.model = model; + this.storage = storage; + this.parser = new CommandParser(); + } + + + @Override + public CommandResult execute(String commandText) { + logger.info("----------------[USER COMMAND][" + commandText + "]"); + Command command = parser.parseCommand(commandText); + command.setData(model); + command.setStorage(storage); + return command.execute(); + } + + @Override + public ObservableList getFilteredAllTaskList() { + return model.getFilteredAllTaskList(); + } + + //@@author A0138601M + @Override + public ObservableList getFilteredCompleteTaskList() { + return model.getFilteredCompleteTaskList(); + } + + @Override + public ObservableList getFilteredIncompleteTaskList() { + return model.getFilteredIncompleteTaskList(); + } + + @Override + public ObservableList getFilteredOverdueTaskList() { + return model.getFilteredOverdueTaskList(); + } + + @Override + public void setCurrentTab(String tab) { + model.setCurrentTab(tab); + } + + @Override + public String getCurrentTab() { + return model.getCurrentTab(); + } +} diff --git a/src/main/java/seedu/todolist/logic/commands/AddCommand.java b/src/main/java/seedu/todolist/logic/commands/AddCommand.java new file mode 100644 index 000000000000..685f73c3dfee --- /dev/null +++ b/src/main/java/seedu/todolist/logic/commands/AddCommand.java @@ -0,0 +1,73 @@ +package seedu.todolist.logic.commands; + +import seedu.todolist.commons.core.EventsCenter; +import seedu.todolist.commons.events.ui.JumpToListRequestEvent; +import seedu.todolist.commons.exceptions.IllegalValueException; +import seedu.todolist.model.task.*; +import seedu.todolist.ui.MainWindow; + +/** + * Adds a task to the to-do list. + */ +public class AddCommand extends Command { + + public static final String COMMAND_WORD = "add"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a task to Task!t. " + + "Parameters: NAME [from DATETIME to DATETIME]/[by DATETIME] [at LOCATION] [remarks REMARKS]\n" + + "Example 1: " + COMMAND_WORD + + " dinner with mom from 12 oct 2016 7pm to 12 oct 2016 8pm at home remarks buy flowers \n" + + "Example 2: " + COMMAND_WORD + + " submit proposal by 25/11/2016 23:59"; + + public static final String MESSAGE_SUCCESS = "New task added: %1$s"; + + public static final String MESSAGE_DUPLICATE_TASK = "This task already exists in Task!t"; + + private static final int NOT_FOUND = -1; + + private final Task toAdd; + + /** + * Convenience constructor using raw values. + * + * @throws IllegalValueException if any of the raw values are invalid + */ + public AddCommand(String name, String startDate, String startTime, String endDate, String endTime, + String location, String remarks) throws IllegalValueException { + this.toAdd = new Task( + new Name(name), + new Interval(startDate, startTime, endDate, endTime), + new Location(location), + new Remarks(remarks), + new Status() + ); + } + + //@@author A0138601M + @Override + public CommandResult execute() { + assert model != null; + try { + model.addTask(toAdd); + + indicateJumpToListRequestedEvent(); + return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); + } catch (UniqueTaskList.DuplicateTaskException e) { + return new CommandResult(MESSAGE_DUPLICATE_TASK); + } + } + + private void indicateJumpToListRequestedEvent() { + int indexToJump; + if (model.getCurrentTab().equals(MainWindow.TAB_TASK_INCOMPLETE)) { + indexToJump = model.getIndexFromIncompleteList(toAdd); + } else if (model.getCurrentTab().equals(MainWindow.TAB_TASK_OVERDUE)) { + indexToJump = model.getIndexFromOverdueList(toAdd); + } else { + indexToJump = NOT_FOUND; + } + EventsCenter.getInstance().post(new JumpToListRequestEvent(indexToJump)); + } + +} diff --git a/src/main/java/seedu/todolist/logic/commands/ClearCommand.java b/src/main/java/seedu/todolist/logic/commands/ClearCommand.java new file mode 100644 index 000000000000..25a37a1aca49 --- /dev/null +++ b/src/main/java/seedu/todolist/logic/commands/ClearCommand.java @@ -0,0 +1,21 @@ +package seedu.todolist.logic.commands; + +import seedu.todolist.model.ToDoList; + +/** + * Clears the address book. + */ +public class ClearCommand extends Command { + + public static final String COMMAND_WORD = "clear"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + "Clears tasks permanently.\n\t" + + "Example: " + COMMAND_WORD; + public static final String MESSAGE_SUCCESS = "All tasks have been cleared!"; + + @Override + public CommandResult execute() { + assert model != null; + model.resetData(ToDoList.getEmptyToDoList()); + return new CommandResult(MESSAGE_SUCCESS); + } +} diff --git a/src/main/java/seedu/todolist/logic/commands/Command.java b/src/main/java/seedu/todolist/logic/commands/Command.java new file mode 100644 index 000000000000..032ec5a40f22 --- /dev/null +++ b/src/main/java/seedu/todolist/logic/commands/Command.java @@ -0,0 +1,55 @@ +package seedu.todolist.logic.commands; + +import seedu.todolist.commons.core.EventsCenter; +import seedu.todolist.commons.core.Messages; +import seedu.todolist.commons.events.ui.IncorrectCommandAttemptedEvent; +import seedu.todolist.model.Model; +import seedu.todolist.storage.Storage; + +/** + * Represents a command with hidden internal logic and the ability to be executed. + */ +public abstract class Command { + protected Model model; + protected Storage storage; + + /** + * Constructs a feedback message to summarize an operation that displayed a listing of tasks. + * + * @param incompleteTaskSize, completedTaskSize and overdueTaskSize used to generate summary + * @return summary message for incomplete, completed and overdue tasks displayed + */ + public static String getMessageForTaskListShownSummary(int incompleteTaskSize, int completedTaskSize, int overdueTaskSize) { + return String.format(Messages.MESSAGE_INCOMPLETE_TASKS_LISTED_OVERVIEW, incompleteTaskSize) + + String.format(Messages.MESSAGE_COMPLETED_TASKS_LISTED_OVERVIEW, completedTaskSize) + + String.format(Messages.MESSAGE_OVERDUE_TASKS_LISTED_OVERVIEW, overdueTaskSize); + } + + /** + * Executes the command and returns the result message. + * + * @return feedback message of the operation result for display + */ + public abstract CommandResult execute(); + + /** + * Provides any needed dependencies to the command. + * Commands making use of any of these should override this method to gain + * access to the dependencies. + */ + public void setData(Model model) { + this.model = model; + } + + //@@author A0158963M + public void setStorage(Storage storage) { + this.storage = storage; + } + + /** + * Raises an event to indicate an attempt to execute an incorrect command + */ + protected void indicateAttemptToExecuteIncorrectCommand() { + EventsCenter.getInstance().post(new IncorrectCommandAttemptedEvent()); + } +} diff --git a/src/main/java/seedu/address/logic/commands/CommandResult.java b/src/main/java/seedu/todolist/logic/commands/CommandResult.java similarity index 87% rename from src/main/java/seedu/address/logic/commands/CommandResult.java rename to src/main/java/seedu/todolist/logic/commands/CommandResult.java index f46f2f31353e..8ea8224c2dfb 100644 --- a/src/main/java/seedu/address/logic/commands/CommandResult.java +++ b/src/main/java/seedu/todolist/logic/commands/CommandResult.java @@ -1,4 +1,4 @@ -package seedu.address.logic.commands; +package seedu.todolist.logic.commands; /** * Represents the result of a command execution. diff --git a/src/main/java/seedu/todolist/logic/commands/DeleteCommand.java b/src/main/java/seedu/todolist/logic/commands/DeleteCommand.java new file mode 100644 index 000000000000..07b72700a31e --- /dev/null +++ b/src/main/java/seedu/todolist/logic/commands/DeleteCommand.java @@ -0,0 +1,91 @@ +package seedu.todolist.logic.commands; + +import seedu.todolist.commons.core.Messages; +import seedu.todolist.commons.core.UnmodifiableObservableList; +import seedu.todolist.model.task.ReadOnlyTask; +import seedu.todolist.model.task.UniqueTaskList.TaskNotFoundException; +import seedu.todolist.ui.MainWindow; + +/** + * Deletes a task identified using it's last displayed index from the to-do list. + */ +public class DeleteCommand extends Command { + + 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: TASK TYPE + INDEXES (must be a positive integer)\n" + + "Example: " + COMMAND_WORD + " 1 \n" + + "Example: " + COMMAND_WORD + " 1, 3, 4"; + + public static final String MESSAGE_DELETE_TASK_SUCCESS = "Task deleted!"; + public static final int MULTIPLE_DELETE_OFFSET = 1; + + //@@author A0138601M + private int[] targetIndexes; + + public DeleteCommand(int[] targetIndexes) { + this.targetIndexes = targetIndexes; + } + + @Override + public CommandResult execute() { + UnmodifiableObservableList lastShownList = getLastShownList(); + if (!isValidIndexes(lastShownList, targetIndexes)) { + return new CommandResult(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + + ReadOnlyTask[] tasksToDelete = getAllTaskToDelete(lastShownList); + try { + model.deleteTask(tasksToDelete); + } catch (TaskNotFoundException tnfe) { + assert false : "The target task cannot be missing"; + } + + return new CommandResult(MESSAGE_DELETE_TASK_SUCCESS); + } + + /** + * Get the last shown listing from the selected tab + */ + private UnmodifiableObservableList getLastShownList() { + if (model.getCurrentTab().equals(MainWindow.TAB_TASK_COMPLETE)) { + return model.getFilteredCompleteTaskList(); + } else if (model.getCurrentTab().equals(MainWindow.TAB_TASK_INCOMPLETE)) { + return model.getFilteredIncompleteTaskList(); + } else if (model.getCurrentTab().equals(MainWindow.TAB_TASK_OVERDUE)) { + return model.getFilteredOverdueTaskList(); + } else { + assert false : "Last shown list must come from either completed, incomplete or overdue pane"; + return null; + } + } + + /** + * Returns an array of ReadOnlyTask selected using the indexes in the last shown list + */ + private ReadOnlyTask[] getAllTaskToDelete(UnmodifiableObservableList lastShownList) { + ReadOnlyTask[] tasksToDelete = new ReadOnlyTask[targetIndexes.length]; + for (int i = 0; i < targetIndexes.length; i++) { + tasksToDelete[i] = lastShownList.get(targetIndexes[i] - MULTIPLE_DELETE_OFFSET); + } + return tasksToDelete; + } + //@@author + + /** + * Check if a particular index can be deleted + * @param targetIndex an array of indexes to be delete from lastShownList + * @return true if all indexes are valid + */ + private boolean isValidIndexes(UnmodifiableObservableList lastShownList, int[] targetIndex) { + for (int index : targetIndex) { + if (lastShownList.size() < index) { + indicateAttemptToExecuteIncorrectCommand(); + return false; + } + } + return true; + } +} diff --git a/src/main/java/seedu/todolist/logic/commands/DoneCommand.java b/src/main/java/seedu/todolist/logic/commands/DoneCommand.java new file mode 100644 index 000000000000..d9a07c6e109e --- /dev/null +++ b/src/main/java/seedu/todolist/logic/commands/DoneCommand.java @@ -0,0 +1,94 @@ +package seedu.todolist.logic.commands; + +import seedu.todolist.commons.core.Messages; +import seedu.todolist.commons.core.UnmodifiableObservableList; +import seedu.todolist.model.task.ReadOnlyTask; +import seedu.todolist.model.task.UniqueTaskList.TaskNotFoundException; +import seedu.todolist.ui.MainWindow; + +//@@author A0138601M +/** + * Marks a task identified using it's last displayed index from the to do list. + */ +public class DoneCommand extends Command { + public static final String COMMAND_WORD = "done"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Marks the task identified using it's last displayed index from the last task listing.\n" + + "Parameters: INDEX (must be a positive integer)\n" + + "Example: " + COMMAND_WORD + " 1 \n"; + + public static final String MESSAGE_MARK_TASK_SUCCESS = "Task marked!"; + public static final String MESSAGE_MARK_COMPLETED_TASK = "This task is already completed!"; + + public static final int MULTIPLE_MARK_OFFSET = 1; + + private int[] targetIndexes; + + public DoneCommand(int[] targetIndexes) { + this.targetIndexes = targetIndexes; + } + + + @Override + public CommandResult execute() { + if (model.getCurrentTab().equals(MainWindow.TAB_TASK_COMPLETE)) { + return new CommandResult(MESSAGE_MARK_COMPLETED_TASK); + } + + UnmodifiableObservableList lastShownList = getLastShownList(); + if (!isValidIndexes(lastShownList, targetIndexes)) { + return new CommandResult(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + + ReadOnlyTask[] tasksToMark = getAllTaskToMark(lastShownList); + try { + model.markTask(tasksToMark); + } catch (TaskNotFoundException pnfe) { + assert false : "The target task cannot be missing"; + } + + return new CommandResult(MESSAGE_MARK_TASK_SUCCESS); + } + + /** + * Get the last shown listing from the selected tab + */ + private UnmodifiableObservableList getLastShownList() { + if (model.getCurrentTab().equals(MainWindow.TAB_TASK_INCOMPLETE)) { + return model.getFilteredIncompleteTaskList(); + } else if (model.getCurrentTab().equals(MainWindow.TAB_TASK_OVERDUE)) { + return model.getFilteredOverdueTaskList(); + } else { + assert false : "Last shown list must come from either incomplete or overdue pane"; + return null; + } + } + + /** + * Returns an array of ReadOnlyTask selected using the indexes in the last shown list + */ + private ReadOnlyTask[] getAllTaskToMark(UnmodifiableObservableList lastShownList) { + ReadOnlyTask[] tasksToMark = new ReadOnlyTask[targetIndexes.length]; + for (int i = 0; i < targetIndexes.length; i++) { + tasksToMark[i] = lastShownList.get(targetIndexes[i] - MULTIPLE_MARK_OFFSET); + } + return tasksToMark; + } + //@@author + + /** + * Check if a particular index can be marked as completed + * @param targetIndex an array of indexes to be marked from lastShownList + * @return true if all indexes are valid + */ + private boolean isValidIndexes(UnmodifiableObservableList lastShownList, int[] targetIndex) { + for (int index : targetIndex) { + if (lastShownList.size() < index) { + indicateAttemptToExecuteIncorrectCommand(); + return false; + } + } + return true; + } +} diff --git a/src/main/java/seedu/todolist/logic/commands/EditCommand.java b/src/main/java/seedu/todolist/logic/commands/EditCommand.java new file mode 100644 index 000000000000..2fd9d1de06f0 --- /dev/null +++ b/src/main/java/seedu/todolist/logic/commands/EditCommand.java @@ -0,0 +1,124 @@ +package seedu.todolist.logic.commands; + +import seedu.todolist.commons.core.Messages; +import seedu.todolist.commons.core.UnmodifiableObservableList; +import seedu.todolist.commons.exceptions.IllegalValueException; +import seedu.todolist.model.task.Interval; +import seedu.todolist.model.task.Location; +import seedu.todolist.model.task.Name; +import seedu.todolist.model.task.ReadOnlyTask; +import seedu.todolist.model.task.Remarks; +import seedu.todolist.model.task.Status; +import seedu.todolist.model.task.Task; +import seedu.todolist.model.task.TaskDate; +import seedu.todolist.model.task.TaskTime; +import seedu.todolist.model.task.UniqueTaskList.TaskNotFoundException; +import seedu.todolist.ui.MainWindow; + +//@@author A0146682X +/** + * Edits the information of an existing task. + */ + +public class EditCommand extends Command { + + public static final String COMMAND_WORD = "edit"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits a task in the list displayed. " + + "Parameters: [index] [NAME] [from DATETIME] [to DATETIME] [at LOCATION] [remarks REMARKS] \n" + + "Example: " + COMMAND_WORD + + " 1 dinner with mom from 13 oct 2016 7pm to 13 oct 2016 8pm at home remarks buy fruits"; + + public static final String MESSAGE_SUCCESS = "Task edited: %1$s"; + + private final int targetIndex; + + private String name; + private String startDate; + private String startTime; + private String endDate; + private String endTime; + private String location; + private String remarks; + + public EditCommand(int targetIndex, String name, String startDate, String startTime, String endDate, String endTime, + String location, String remarks) throws IllegalValueException { + this.targetIndex = targetIndex; + + this.name = name; + this.startDate = startDate; + this.startTime = startTime; + this.endDate = endDate; + this.endTime = endTime; + this.location = location; + this.remarks = remarks; + } + + @Override + public CommandResult execute() { + + UnmodifiableObservableList lastShownList; + + if (model.getCurrentTab().equals(MainWindow.TAB_TASK_COMPLETE)) { + lastShownList = model.getFilteredCompleteTaskList(); + } + else { + lastShownList = model.getFilteredIncompleteTaskList(); + } + + if (targetIndex < 1 || lastShownList.size() < targetIndex) { + indicateAttemptToExecuteIncorrectCommand(); + return new CommandResult(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + + ReadOnlyTask taskToEdit = lastShownList.get(targetIndex - 1); + + name = name.trim(); + + if(name==null||name.isEmpty()) { + name = taskToEdit.getName().toString(); + } + + Interval originalInterval = taskToEdit.getInterval(); + + if (originalInterval != null) { + TaskDate originalStartDate = originalInterval.getStartDate(); + if(originalStartDate!=null && startDate==null) startDate = originalStartDate.toString(); + TaskTime originalStartTime = originalInterval.getStartTime(); + if(originalStartTime!=null && startTime==null) startTime = originalStartTime.toString(); + TaskDate originalEndDate = originalInterval.getEndDate(); + if(originalEndDate!=null && endDate==null) endDate = originalEndDate.toString(); + TaskTime originalEndTime = originalInterval.getEndTime(); + if(originalEndTime!=null && endTime==null) endTime = originalEndTime.toString(); + } + + Location originalLocation = taskToEdit.getLocation(); + if(originalLocation != null && location==null) location = originalLocation.toString(); + + Remarks originalRemarks = taskToEdit.getRemarks(); + if(originalRemarks != null && remarks==null) remarks = originalRemarks.toString(); + + Status originalStatus = taskToEdit.getStatus(); + + Task replacement; + try { + replacement = new Task( + new Name(name), + new Interval(startDate, startTime, endDate, endTime), + new Location(location), + new Remarks(remarks), + new Status(originalStatus.toString()) + ); + } catch (IllegalValueException ive) { + return new CommandResult(ive.getMessage()); + } + + try { + model.editTask(taskToEdit, replacement); + } catch (TaskNotFoundException pnfe) { + assert false : "The target task cannot be missing"; + } + + return new CommandResult(String.format(MESSAGE_SUCCESS, replacement)); + } +} diff --git a/src/main/java/seedu/address/logic/commands/ExitCommand.java b/src/main/java/seedu/todolist/logic/commands/ExitCommand.java similarity index 60% rename from src/main/java/seedu/address/logic/commands/ExitCommand.java rename to src/main/java/seedu/todolist/logic/commands/ExitCommand.java index d98233ce2a0b..a78240cfeb93 100644 --- a/src/main/java/seedu/address/logic/commands/ExitCommand.java +++ b/src/main/java/seedu/todolist/logic/commands/ExitCommand.java @@ -1,7 +1,7 @@ -package seedu.address.logic.commands; +package seedu.todolist.logic.commands; -import seedu.address.commons.core.EventsCenter; -import seedu.address.commons.events.ui.ExitAppRequestEvent; +import seedu.todolist.commons.core.EventsCenter; +import seedu.todolist.commons.events.ui.ExitAppRequestEvent; /** * Terminates the program. @@ -9,11 +9,10 @@ public class ExitCommand extends Command { public static final String COMMAND_WORD = "exit"; - + public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + "Exits the program.\n\t" + + "Example: " + COMMAND_WORD; public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting Address Book as requested ..."; - public ExitCommand() {} - @Override public CommandResult execute() { EventsCenter.getInstance().post(new ExitAppRequestEvent()); diff --git a/src/main/java/seedu/todolist/logic/commands/FindCommand.java b/src/main/java/seedu/todolist/logic/commands/FindCommand.java new file mode 100644 index 000000000000..3e97b52afd99 --- /dev/null +++ b/src/main/java/seedu/todolist/logic/commands/FindCommand.java @@ -0,0 +1,48 @@ +package seedu.todolist.logic.commands; + +import java.util.List; + +//@@author A0153736B +/** + * Finds and lists all tasks in to-do list whose name contains any/all/phrase of the argument keywords. + * Keyword matching is case insensitive. + */ +public class FindCommand extends Command { + + public static final String COMMAND_WORD = "find"; + + public static final String MESSAGE_INVALID_FINDTYPE = "The findtype provided is invalid."; + + public static final String MESSAGE_KEYWORDS_NOT_PROVIDED = "Keywords are not provided."; + + public static final String FINDTYPE_EITHER = "either"; + public static final String FINDTYPE_ALL = "all"; + public static final String FINDTYPE_PHRASE = "phrase"; + public static final String VALID_FINDTYPE = FINDTYPE_EITHER + "|" + FINDTYPE_ALL + "|" + FINDTYPE_PHRASE; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all tasks whose names contain any/all/phrase of " + + "the specified keywords (case-insensitive) and displays them as a list with index numbers.\n" + + "Parameters: FINDTYPE KEYWORD [MORE_KEYWORDS]...\n" + + "Example: " + COMMAND_WORD + " " + FINDTYPE_PHRASE + " alice bob charlie"; + + private final List keywords; + private final String findType; + + public FindCommand(List keywords, String findType) { + this.keywords = keywords; + this.findType = findType; + } + + @Override + public CommandResult execute() { + if (!findType.matches(VALID_FINDTYPE)) + return new CommandResult(MESSAGE_INVALID_FINDTYPE); + if (keywords.isEmpty()) + return new CommandResult(MESSAGE_KEYWORDS_NOT_PROVIDED); + + assert model != null; + model.updateFilteredTaskList(keywords, findType); + return new CommandResult(getMessageForTaskListShownSummary(model.getFilteredIncompleteTaskList().size(), + model.getFilteredCompleteTaskList().size(), model.getFilteredOverdueTaskList().size())); + } +} diff --git a/src/main/java/seedu/address/logic/commands/HelpCommand.java b/src/main/java/seedu/todolist/logic/commands/HelpCommand.java similarity index 77% rename from src/main/java/seedu/address/logic/commands/HelpCommand.java rename to src/main/java/seedu/todolist/logic/commands/HelpCommand.java index 8f9894c1fc24..06a8e98ada70 100644 --- a/src/main/java/seedu/address/logic/commands/HelpCommand.java +++ b/src/main/java/seedu/todolist/logic/commands/HelpCommand.java @@ -1,8 +1,9 @@ -package seedu.address.logic.commands; +package seedu.todolist.logic.commands; -import seedu.address.commons.core.EventsCenter; -import seedu.address.commons.events.ui.ShowHelpEvent; + +import seedu.todolist.commons.core.EventsCenter; +import seedu.todolist.commons.events.ui.ShowHelpEvent; /** * Format full help instructions for every command for display. @@ -16,8 +17,6 @@ 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 ShowHelpEvent()); diff --git a/src/main/java/seedu/address/logic/commands/IncorrectCommand.java b/src/main/java/seedu/todolist/logic/commands/IncorrectCommand.java similarity index 92% rename from src/main/java/seedu/address/logic/commands/IncorrectCommand.java rename to src/main/java/seedu/todolist/logic/commands/IncorrectCommand.java index 491d9cb9da35..aeaa81320a2a 100644 --- a/src/main/java/seedu/address/logic/commands/IncorrectCommand.java +++ b/src/main/java/seedu/todolist/logic/commands/IncorrectCommand.java @@ -1,4 +1,4 @@ -package seedu.address.logic.commands; +package seedu.todolist.logic.commands; /** diff --git a/src/main/java/seedu/todolist/logic/commands/ListCommand.java b/src/main/java/seedu/todolist/logic/commands/ListCommand.java new file mode 100644 index 000000000000..a78d68093b54 --- /dev/null +++ b/src/main/java/seedu/todolist/logic/commands/ListCommand.java @@ -0,0 +1,60 @@ +package seedu.todolist.logic.commands; + +import java.time.DateTimeException; + +import seedu.todolist.model.task.TaskDate; + +//@@author A0153736B +/** + * Lists tasks in the to-do list to the user according to the dateFilter provided. + */ +public class ListCommand extends Command { + + public static final String COMMAND_WORD = "list"; + + public static final String MESSAGE_ALLTASKS_SUCCESS = "Listed all tasks"; + public static final String MESSAGE_FILTER_SUCCESS = "Listed filtered tasks"; + public static final String MESSAGE_FILTER_INVALID = "Filter provided is invalid!"; + + public static final String FILTER_TODAY = "today"; + public static final String FILTER_WEEK = "week"; + public static final String FILTER_MONTH = "month"; + public static final String VALID_FILTER = FILTER_TODAY + "|" + FILTER_WEEK + "|" + FILTER_MONTH; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + + "Displays the list of tasks corresponding to the filter provided.\n" + + "Parameter: [FILTER]\n" + + "Example: " + COMMAND_WORD + " " + FILTER_TODAY; + + private final String dateFilter; + + public ListCommand(String dateFilter) { + this.dateFilter = dateFilter; + } + + @Override + public CommandResult execute() { + if (dateFilter.isEmpty()) { + model.updateFilteredListToShowAll(); + return new CommandResult(MESSAGE_ALLTASKS_SUCCESS); + } + if (!dateFilter.matches(VALID_FILTER) && !isValidDate(dateFilter)) { + return new CommandResult(MESSAGE_FILTER_INVALID); + } + + assert model != null; + try { + model.updateFilteredTaskList(dateFilter); + } catch (DateTimeException dte) { + return new CommandResult(TaskDate.MESSAGE_DATE_INVALID); + } + return new CommandResult(MESSAGE_FILTER_SUCCESS); + } + + /** + * Returns true if the provided String is a valid date + */ + private boolean isValidDate(String test) { + return (test.matches(TaskDate.DATE_VALIDATION_REGEX_FORMAT)); + } +} diff --git a/src/main/java/seedu/todolist/logic/commands/RedoCommand.java b/src/main/java/seedu/todolist/logic/commands/RedoCommand.java new file mode 100644 index 000000000000..2956e52f7baf --- /dev/null +++ b/src/main/java/seedu/todolist/logic/commands/RedoCommand.java @@ -0,0 +1,32 @@ +package seedu.todolist.logic.commands; + +import java.util.EmptyStackException; + +//@@author A0153736B +/** + * Redoes the most recent undo operation done by the user. + * If the undo operation is overwritten by other operations, the to-do-list can't be redone. + */ +public class RedoCommand extends Command { + + public static final String COMMAND_WORD = "redo"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Redoes the previous undo operation by the user\n" + + "Example: " + COMMAND_WORD; + + public static final String MESSAGE_SUCCESS = "The previous undo operation has been redone!"; + + public static final String MESSAGE_WITHOUT_PREVIOUS_OPERATION = "There is no previous undo operation."; + + @Override + public CommandResult execute() { + assert model != null; + try { + model.redoToDoList(); + return new CommandResult(MESSAGE_SUCCESS); + } catch (EmptyStackException ese) { + return new CommandResult(MESSAGE_WITHOUT_PREVIOUS_OPERATION); + } + } +} \ No newline at end of file diff --git a/src/main/java/seedu/todolist/logic/commands/SetstorageCommand.java b/src/main/java/seedu/todolist/logic/commands/SetstorageCommand.java new file mode 100644 index 000000000000..679fbd1e0985 --- /dev/null +++ b/src/main/java/seedu/todolist/logic/commands/SetstorageCommand.java @@ -0,0 +1,41 @@ +package seedu.todolist.logic.commands; +import java.io.File; +//@@author A0158963M +/** + * Sets the location of the storage file. + */ +public class SetstorageCommand extends Command{ + + public static final String COMMAND_WORD = "setstorage"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Sets the location of the storage file. \n" + + "Example: " + COMMAND_WORD + " C://user/documents/todolist"; + + public static final String MESSAGE_SUCCESS = "Successfully set the storage location!"; + + public static final String MESSAGE_Invalid_Path = "Given location path is invalid."; + + private final String filepath; + + public SetstorageCommand(String filepath) { + this.filepath = filepath; + } + + + @Override + public CommandResult execute() { + File newfile = new File(filepath); + if(newfile.isDirectory()){ //new filepath is correct + File file = new File(storage.getToDoListFilePath()); // delete original file + if(file.exists()){ + file.delete(); + } + storage.setFilePath(filepath); + return new CommandResult(MESSAGE_SUCCESS); + } + else + return new CommandResult(MESSAGE_Invalid_Path); + } + + +} \ No newline at end of file diff --git a/src/main/java/seedu/todolist/logic/commands/UndoCommand.java b/src/main/java/seedu/todolist/logic/commands/UndoCommand.java new file mode 100644 index 000000000000..dd3223bb7511 --- /dev/null +++ b/src/main/java/seedu/todolist/logic/commands/UndoCommand.java @@ -0,0 +1,31 @@ +package seedu.todolist.logic.commands; + +import java.util.EmptyStackException; + +//@@author A0153736B +/** + * Undoes the most recent operation done by the user. + */ +public class UndoCommand extends Command { + + public static final String COMMAND_WORD = "undo"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Undoes the most recent operation done by the user\n" + + "Example: " + COMMAND_WORD; + + public static final String MESSAGE_SUCCESS = "The most recent operation has been undone!"; + + public static final String MESSAGE_WITHOUT_PREVIOUS_OPERATION = "There is no last operation."; + + @Override + public CommandResult execute() { + assert model != null; + try { + model.undoToDoList(); + return new CommandResult(MESSAGE_SUCCESS); + } catch (EmptyStackException ese) { + return new CommandResult(MESSAGE_WITHOUT_PREVIOUS_OPERATION); + } + } +} diff --git a/src/main/java/seedu/todolist/logic/parser/CommandParser.java b/src/main/java/seedu/todolist/logic/parser/CommandParser.java new file mode 100644 index 000000000000..3cf45e547699 --- /dev/null +++ b/src/main/java/seedu/todolist/logic/parser/CommandParser.java @@ -0,0 +1,542 @@ +package seedu.todolist.logic.parser; + +import com.google.common.base.Strings; +import com.joestelmach.natty.*; + +import seedu.todolist.commons.core.LogsCenter; +import seedu.todolist.commons.exceptions.IllegalValueException; +import seedu.todolist.commons.util.StringUtil; +import seedu.todolist.logic.commands.*; +import seedu.todolist.model.parser.DateParser; +import seedu.todolist.model.task.TaskDate; +import seedu.todolist.model.task.TaskTime; + +import static seedu.todolist.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.todolist.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; +import static seedu.todolist.commons.core.Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX; + +import java.io.*; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.util.*; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Parses user input. + */ +public class CommandParser { + + private static final Logger logger = LogsCenter.getLogger(CommandParser.class); + /** + * Used for initial separation of command word and args. + */ + private static final Pattern BASIC_COMMAND_FORMAT = Pattern.compile("(?\\S+)(?.*)"); + + private static final Pattern KEYWORDS_ARGS_FORMAT = + Pattern.compile("(?\\S+(?:\\s+\\S+)*)"); // one or more keywords separated by whitespace + + private static final Pattern TASK_DATA_ARGS_FORMAT = + Pattern.compile("(?(.)+?)?" + + "((\\bfrom\\b|\\bby\\b)(?(.)+?))?" + + "((\\bat\\b)(?(.)+?))?" + + "((\\bremarks\\b)(?(.)+?))?"); + + private static final Pattern EDIT_DATA_ARGS_FORMAT = // '/' forward slashes are reserved for delimiter prefixes + Pattern.compile("(?(.)+?)?" + + "((\\bfrom\\b|\\bby\\b)(?(.)+?))?" + + "((\\bat\\b)(?(.)+?))?" + + "((\\bremarks\\b)(?(.)+?))?"); + + private static final String RELATIVE_DATE_FILTER_ARGS_FORMAT = "(?\\S+)"; + private static final Pattern DATE_FILTER_ARGS_FORMAT = + Pattern.compile(TaskDate.DATE_VALIDATION_REGEX_FORMAT + "|" + RELATIVE_DATE_FILTER_ARGS_FORMAT); + + private static final Parser nattyParser = new Parser(); + + public static final int INTERVAL_COMPONENT_COUNT = 2; + public static final int INTERVAL_INDEX_FROM = 0; + public static final int INTERVAL_INDEX_TO = 1; + + public static final int DATETIME_COMPONENT_COUNT = 2; + public static final int DATETIME_INDEX_DATE = 0; + public static final int DATETIME_INDEX_TIME = 1; + + public static final int DETAILED_INTERVAL_COMPONENT_COUNT = 4; + public static final int DETAILED_INTERVAL_INDEX_STARTDATE = 0; + public static final int DETAILED_INTERVAL_INDEX_STARTTIME = 1; + public static final int DETAILED_INTERVAL_INDEX_ENDDATE = 2; + public static final int DETAILED_INTERVAL_INDEX_ENDTIME = 3; + + public static final String INTERVAL_SEPARATOR = " to "; + public static final String INDEX_DELIMITER = ","; + + public static final int NATTY_INDEX_FIRST = 0; + + public static final int DEFAULT_INDICES_SIZE = 1; + public static final int DEFAULT_FIRST_INDEX = 0; + + public static final int INDEX_DAY = 0; + public static final int INDEX_MONTH = 1; + public static final int INDEX_REMAINDER = 2; + + public static final int MAX_MONTH = 12; + + public CommandParser() { + //Warm up natty for faster processing + logger.info("Warming up Natty Parser..."); + nattyParser.parse(LocalDate.now().toString()); + } + /** + * 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 EditCommand.COMMAND_WORD: + return prepareEdit(arguments); + + case DoneCommand.COMMAND_WORD: + return prepareDone(arguments); + + case DeleteCommand.COMMAND_WORD: + return prepareDelete(arguments); + + case UndoCommand.COMMAND_WORD: + return prepareUndo(arguments); + + case RedoCommand.COMMAND_WORD: + return prepareRedo(arguments); + + case ClearCommand.COMMAND_WORD: + return prepareClear(arguments); + + case FindCommand.COMMAND_WORD: + return prepareFind(arguments); + + case ListCommand.COMMAND_WORD: + return prepareList(arguments); + + case SetstorageCommand.COMMAND_WORD: + return prepareSetstorage(arguments); + + case ExitCommand.COMMAND_WORD: + return prepareExit(arguments); + + case HelpCommand.COMMAND_WORD: + return prepareHelp(arguments); + + default: + return new IncorrectCommand(MESSAGE_UNKNOWN_COMMAND); + } + } + + /** + * Parses arguments in the context of the add task command. + * + * @param args full command args string + * @return the prepared command + */ + private Command prepareAdd(String args){ + final Matcher matcher = TASK_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 parseAddCommand( + matcher.group("name"), + parseInterval(matcher.group("interval")), + matcher.group("location"), + matcher.group("remarks") + ); + } catch (IllegalValueException ive) { + return new IncorrectCommand(ive.getMessage()); + } + } + + private AddCommand parseAddCommand(String name, String[] interval, String location, String remarks) throws IllegalValueException { + return new AddCommand( + name, + interval[DETAILED_INTERVAL_INDEX_STARTDATE], + interval[DETAILED_INTERVAL_INDEX_STARTTIME], + interval[DETAILED_INTERVAL_INDEX_ENDDATE], + interval[DETAILED_INTERVAL_INDEX_ENDTIME], + location, + remarks); + } + + //@@author A0138601M + /** + * Extracts the new task's start date and time, and end date and time from the add command's interval arguments string. + * @return a string array that contains start date, start time, end date and end time. + */ + private String[] parseInterval(String interval) { + String[] detailedIntervalComponents = new String[DETAILED_INTERVAL_COMPONENT_COUNT]; + if (!Strings.isNullOrEmpty(interval)) { + detailedIntervalComponents = parseTimedInterval(interval); + } + return detailedIntervalComponents; + } + + /** + * Parses the interval that has date and time + * @return a string array that contains start date, start time, end date and end time. + */ + private String[] parseTimedInterval(String interval) { + if (interval.contains(INTERVAL_SEPARATOR)) { + return parseEventInterval(interval); + } else { + return parseDeadlineInterval(interval); + } + } + + /** + * Parses the interval for event type task + * @return a string array that contains start date, start time, end date and end time. + */ + private String[] parseEventInterval(String interval) { + String[] intervalComponents = parseDatetime(interval); + return new String[] { + intervalComponents[DETAILED_INTERVAL_INDEX_STARTDATE], + intervalComponents[DETAILED_INTERVAL_INDEX_STARTTIME], + intervalComponents[DETAILED_INTERVAL_INDEX_ENDDATE], + intervalComponents[DETAILED_INTERVAL_INDEX_ENDTIME]}; + } + + /** + * Parses the interval for deadline type task + * @return a string array that contains start date, start time, end date and end time. + */ + private String[] parseDeadlineInterval(String interval) { + String[] endDateTime = parseDatetime(interval); + return new String[] { + null, + null, + endDateTime[DATETIME_INDEX_DATE], + endDateTime[DATETIME_INDEX_TIME]}; + } + + /** + * Extracts the new task's date and time from the add command's datetime arguments string using Natty. + * @return a string array that contains date and time + */ + private String[] parseDatetime(String datetime) { + String editedDatetime = datetime; + if (isSlashFormat(datetime)) { + editedDatetime = reverseDayAndMonth(datetime); + } + + ArrayList intervalComponents = new ArrayList(); + Parser nattyParser = new Parser(); + DateGroup group = nattyParser.parse(editedDatetime).get(NATTY_INDEX_FIRST); + Calendar currentDateTime = Calendar.getInstance(); //Get current datetime to compare with natty datetime + for (Date date : group.getDates()) { + intervalComponents.add(parseDate(date)); + intervalComponents.add(parseTime(date, currentDateTime)); + + } + return intervalComponents.toArray(new String[intervalComponents.size()]); + } + + /** + * Returns true if a date is in slash format (i.e. 10/12/2016) + */ + private boolean isSlashFormat(String datetime) { + Matcher matcher = Pattern.compile(TaskDate.DATE_VALIDATION_SLASH_REGEX).matcher(datetime.trim()); + return matcher.matches(); + } + + /** + * Reverse day and month for natty parsing + */ + private String reverseDayAndMonth(String datetime) { + String[] dateAndTime = datetime.trim().split(DateParser.DATE_DELIMITER_SLASH); + + StringBuilder builder = new StringBuilder(); + builder.append(dateAndTime[INDEX_MONTH]); + builder.append(DateParser.DATE_DELIMITER_SLASH); + builder.append(dateAndTime[INDEX_DAY]); + builder.append(DateParser.DATE_DELIMITER_SLASH); + builder.append(dateAndTime[INDEX_REMAINDER]); + + return builder.toString(); + } + + /** + * Extracts the date from Natty's dategroup + */ + private String parseDate(Date date) { + DateFormat dateFormat = new SimpleDateFormat(TaskDate.DATE_DISPLAY_FORMAT); + return dateFormat.format(date); + } + + /** + * Extracts the time from Natty's dategroup + */ + private String parseTime(Date date, Calendar currentDateTime) { + DateFormat timeFormat = new SimpleDateFormat(TaskTime.TIME_DISPLAY_FORMAT); + String parsedTime = timeFormat.format(date); + //ignore if it is a time generated by natty + if (timeFormat.format(currentDateTime.getTime()).equals(parsedTime)) { + parsedTime = null; + } + return parsedTime; + } + + /** + * Parses arguments in the context of the done task command. + * + * @param args full command args string + * @return the prepared command + */ + private Command prepareDone(String args) { + int[] indexes; + try { + indexes = parseIndex(args); + } + catch (IllegalValueException ive) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_TASK_DISPLAYED_INDEX, DoneCommand.MESSAGE_USAGE)); + } + return new DoneCommand(indexes); + } + + //@@author A0146682X + /** + * Parses arguments in the context of the edit task command. + * + * @param args full command args string + * @return the prepared command + */ + private Command prepareEdit(String args) { + + Matcher index_matcher = Pattern.compile("\\d+").matcher(args); + index_matcher.find(); + + int index; + + try { + index = Integer.valueOf(index_matcher.group()); + } catch (IllegalStateException e) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE)); + } + + //split command into its index (content[0]) and the string that follows (content[1]) + String[] content = args.split("\\d+", 2); + + //verify the format of the string command is correct + final Matcher matcher = EDIT_DATA_ARGS_FORMAT.matcher(content[1]); + + if (!matcher.matches()) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE)); + } + + String name = matcher.group("name"); + String[] interval = parseInterval(matcher.group("interval")); + String location = matcher.group("location"); + String remarks = matcher.group("remarks"); + + try { + return new EditCommand(index, name, interval[0], interval[1], interval[2], interval[3], location, remarks); + } catch (IllegalValueException ive) { + return new IncorrectCommand(ive.getMessage()); + } + } + + //@@author A0138601M + /** + * Parses arguments in the context of the delete task command. + * + * @param args full command args string + * @return the prepared command + */ + private Command prepareDelete(String args) { + int[] indexes; + try { + indexes = parseIndex(args); + } + catch (IllegalValueException ive) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE)); + } + return new DeleteCommand(indexes); + } + + /** + * Extract the indexes from a string of command + * + * @throws IllegalValueException if indexes are invalid + * @return an int array if valid indexes are provided. + */ + private int[] parseIndex(String command) throws IllegalValueException { + assert command != null; + int[] indexes = new int[DEFAULT_INDICES_SIZE]; + + if (command.trim().contains(INDEX_DELIMITER)) { + indexes = parseIndexSeparatedByComma(command); + } else { + if (!StringUtil.isUnsignedInteger(command.trim())) { + throw new IllegalValueException(MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + indexes[DEFAULT_FIRST_INDEX] = Integer.parseInt(command.trim()); + } + Arrays.sort(indexes); + return indexes; + } + + /** + * Extract the indexes from a string of command + * + * @param command is guaranteed to contain commas + * @throws IllegalValueException if indexes are invalid + * @return an int array if valid indexes are provided. + */ + private int[] parseIndexSeparatedByComma(String command) throws IllegalValueException { + String Trimmedcommand = command.trim(); + + String[] indexesString = Trimmedcommand.split(INDEX_DELIMITER); + int[] indexes = new int[indexesString.length]; + for (int i = 0; i < indexesString.length; i++) { + if (!StringUtil.isUnsignedInteger(indexesString[i].trim())) { + throw new IllegalValueException(MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + indexes[i] = Integer.parseInt(indexesString[i].trim()); + } + return indexes; + } + + //@@author A0153736B + /** + * Parses arguments in the context of the undo command. + * + * @param args full command args string + * @return the prepared command + */ + private Command prepareUndo(String args) { + if (!args.trim().isEmpty()) + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + UndoCommand.MESSAGE_USAGE)); + return new UndoCommand(); + } + + /** + * Parses arguments in the context of the redo command. + * + * @param args full command args string + * @return the prepared command + */ + private Command prepareRedo(String args) { + if (!args.trim().isEmpty()) + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + RedoCommand.MESSAGE_USAGE)); + return new RedoCommand(); + } + + /** + * Parses arguments in the context of the clear command. + * + * @param args full command args string + * @return the prepared command + */ + private Command prepareClear(String args) { + if (!args.trim().isEmpty()) + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + ClearCommand.MESSAGE_USAGE)); + return new ClearCommand(); + } + + /** + * Parses arguments in the context of the find task command. + * + * @param args full command args string + * @return the prepared command + */ + private Command prepareFind(String args) { + 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 List keywordSet = new ArrayList<>(Arrays.asList(keywords)); + keywordSet.remove(keywords[0]); + return new FindCommand(keywordSet, keywords[0]); + } + + /** + * Parses arguments in the context of the list command. + * + * @param args full command args string + * @return the prepared command + */ + private Command prepareList(String args) { + String dateFilter = args.trim(); + + final Matcher matcher = DATE_FILTER_ARGS_FORMAT.matcher(dateFilter); + if (!matcher.matches() && !dateFilter.isEmpty()) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + ListCommand.MESSAGE_USAGE)); + } + + return new ListCommand(dateFilter); + } + + //@@author A0158963M + /** + * Parses arguments in the context of the setstorage command. + * + * @param args full command args string + * @return the prepared command + */ + private Command prepareSetstorage(String args){ + File file = new File(args.trim()); + if (!file.exists() && !file.isDirectory()){ + file.mkdirs(); + } + + return new SetstorageCommand(args.trim()); + } + + //@@author A0153736B + /** + * Parses arguments in the context of the exit command. + * + * @param args full command args string + * @return the prepared command + */ + private Command prepareExit(String args) { + if (!args.trim().isEmpty()) + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + ExitCommand.MESSAGE_USAGE)); + return new ExitCommand(); + } + + /** + * Parses arguments in the context of the help command. + * + * @param args full command args string + * @return the prepared command + */ + private Command prepareHelp(String args) { + if (!args.trim().isEmpty()) + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + HelpCommand.MESSAGE_USAGE)); + return new HelpCommand(); + } +} \ No newline at end of file diff --git a/src/main/java/seedu/todolist/model/Model.java b/src/main/java/seedu/todolist/model/Model.java new file mode 100644 index 000000000000..d241b9c0c9cc --- /dev/null +++ b/src/main/java/seedu/todolist/model/Model.java @@ -0,0 +1,73 @@ +package seedu.todolist.model; + +import seedu.todolist.commons.core.UnmodifiableObservableList; +import seedu.todolist.model.task.ReadOnlyTask; +import seedu.todolist.model.task.Task; +import seedu.todolist.model.task.UniqueTaskList; + +import java.time.DateTimeException; +import java.util.EmptyStackException; +import java.util.List; + +/** + * The API of the Model component. + */ +public interface Model { + /** Clears existing backing model and replaces with the provided new data. */ + void resetData(ReadOnlyToDoList newData); + + /** Returns the ToDoList */ + ReadOnlyToDoList getToDoList(); + + /** Reverts the previous state of the ToDoList */ + void undoToDoList() throws EmptyStackException; + + /** Reverts the state of the ToDoList before applying an undo operation */ + void redoToDoList() throws EmptyStackException; + + /** Marks the given task(s) as done. */ + void markTask(ReadOnlyTask... tasks) throws UniqueTaskList.TaskNotFoundException; + + /** Deletes the given task(s). */ + void deleteTask(ReadOnlyTask... tasks) throws UniqueTaskList.TaskNotFoundException; + + /** Adds the given task */ + void addTask(Task task) throws UniqueTaskList.DuplicateTaskException; + + /** Edits the given task */ + void editTask(ReadOnlyTask task, Task replacement) throws UniqueTaskList.TaskNotFoundException; + + /** Returns the filtered task list as an {@code UnmodifiableObservableList} */ + UnmodifiableObservableList getFilteredAllTaskList(); + + /** Returns the filtered complete task list as an {@code UnmodifiableObservableList} */ + UnmodifiableObservableList getFilteredCompleteTaskList(); + + /** Returns the filtered incomplete task list as an {@code UnmodifiableObservableList} */ + UnmodifiableObservableList getFilteredIncompleteTaskList(); + + /** Returns the filtered overdue task list as an {@code UnmodifiableObservableList} */ + UnmodifiableObservableList getFilteredOverdueTaskList(); + + /** Updates the filter of the filtered task list to show all tasks */ + void updateFilteredListToShowAll(); + + /** Updates the filter of the filtered task list to filter by the given keywords */ + void updateFilteredTaskList(List keywords, String findType); + + /** Updates the filter of the filtered task list to filter by the given date filter */ + void updateFilteredTaskList(String dateFilter) throws DateTimeException; + + /** Set the current tab the user is looking at */ + void setCurrentTab(String tab); + + /** Get the current tab the user is looking at */ + String getCurrentTab(); + + /** Gets the index of the task from the incomplete list */ + int getIndexFromIncompleteList(Task task); + + /** Gets the index of the task from the overdue list */ + int getIndexFromOverdueList(Task task); + +} diff --git a/src/main/java/seedu/todolist/model/ModelManager.java b/src/main/java/seedu/todolist/model/ModelManager.java new file mode 100644 index 000000000000..22681d9c32b9 --- /dev/null +++ b/src/main/java/seedu/todolist/model/ModelManager.java @@ -0,0 +1,355 @@ +package seedu.todolist.model; + +import javafx.collections.transformation.FilteredList; +import seedu.todolist.commons.core.ComponentManager; +import seedu.todolist.commons.core.LogsCenter; +import seedu.todolist.commons.core.UnmodifiableObservableList; +import seedu.todolist.commons.events.model.ToDoListChangedEvent; +import seedu.todolist.commons.util.StringUtil; +import seedu.todolist.logic.commands.FindCommand; +import seedu.todolist.logic.commands.ListCommand; +import seedu.todolist.model.parser.DateParser; +import seedu.todolist.model.task.ReadOnlyTask; +import seedu.todolist.model.task.Task; +import seedu.todolist.model.task.UniqueTaskList; +import seedu.todolist.model.task.UniqueTaskList.TaskNotFoundException; +import seedu.todolist.ui.MainWindow; + +import java.time.DateTimeException; +import java.time.LocalDate; +import java.util.EmptyStackException; +import java.util.List; +import java.util.Stack; +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 ToDoList ToDoList; + + private final FilteredList filteredAllTasks; + private final FilteredList filteredCompleteTasks; + private final FilteredList filteredIncompleteTasks; + private final FilteredList filteredOverdueTasks; + + private final Stack ToDoListHistory; + private final Stack ToDoListUndoHistory; + + private String currentTab; + + /** + * Initializes a ModelManager with the given ToDoList + * ToDoList and its variables should not be null + */ + public ModelManager(ToDoList src, UserPrefs userPrefs) { + super(); + assert src != null; + assert userPrefs != null; + + logger.fine("Initializing with address book: " + src + " and user prefs " + userPrefs); + + ToDoList = new ToDoList(src); + filteredAllTasks = new FilteredList<>(ToDoList.getAllTasks()); + filteredCompleteTasks = new FilteredList<>(ToDoList.getCompletedTasks()); + filteredIncompleteTasks = new FilteredList<>(ToDoList.getIncompleteTasks()); + filteredOverdueTasks = new FilteredList<>(ToDoList.getOverdueTasks()); + ToDoListHistory = new Stack(); + ToDoListUndoHistory = new Stack(); + currentTab = MainWindow.TAB_TASK_INCOMPLETE; + } + + public ModelManager() { + this(new ToDoList(), new UserPrefs()); + } + + public ModelManager(ReadOnlyToDoList initialData, UserPrefs userPrefs) { + ToDoList = new ToDoList(initialData); + filteredAllTasks = new FilteredList<>(ToDoList.getAllTasks()); + filteredCompleteTasks = new FilteredList<>(ToDoList.getCompletedTasks()); + filteredIncompleteTasks = new FilteredList<>(ToDoList.getIncompleteTasks()); + filteredOverdueTasks = new FilteredList<>(ToDoList.getOverdueTasks()); + ToDoListHistory = new Stack(); + ToDoListUndoHistory = new Stack(); + currentTab = MainWindow.TAB_TASK_INCOMPLETE; + } + + @Override + public void resetData(ReadOnlyToDoList newData) { + ToDoList currentToDoList = new ToDoList(this.ToDoList); + updateToDoListHistory(currentToDoList); + ToDoList.resetData(newData); + indicateToDoListChanged(); + } + + @Override + public ReadOnlyToDoList getToDoList() { + return ToDoList; + } + + //@@author A0153736B + @Override + public synchronized void undoToDoList() throws EmptyStackException { + ToDoList currentToDoList = new ToDoList(this.ToDoList); + ToDoList.resetData(ToDoListHistory.pop()); + ToDoListUndoHistory.push(currentToDoList); + updateFilteredListToShowAll(); + indicateToDoListChanged(); + } + + @Override + public synchronized void redoToDoList() throws EmptyStackException { + ToDoList previousToDoList = new ToDoList(this.ToDoList); + ToDoList.resetData(ToDoListUndoHistory.pop()); + ToDoListHistory.push(previousToDoList); + updateFilteredListToShowAll(); + indicateToDoListChanged(); + } + //@@author + + /** Raises an event to indicate the model has changed */ + private void indicateToDoListChanged() { + raise(new ToDoListChangedEvent(ToDoList)); + } + + @Override + public int getIndexFromIncompleteList(Task task) { + return filteredIncompleteTasks.indexOf(task); + } + + @Override + public int getIndexFromOverdueList(Task task) { + return filteredOverdueTasks.indexOf(task); + } + + @Override + public void setCurrentTab(String tab) { + currentTab = tab; + } + + @Override + public String getCurrentTab() { + return currentTab; + } + + @Override + public synchronized void markTask(ReadOnlyTask... tasks) throws TaskNotFoundException { + ToDoList previousToDoList = new ToDoList(this.ToDoList); + ToDoList.markTask(tasks); + updateToDoListHistory(previousToDoList); + indicateToDoListChanged(); + } + + @Override + public synchronized void deleteTask(ReadOnlyTask... tasks) throws TaskNotFoundException { + ToDoList previousToDoList = new ToDoList(this.ToDoList); + ToDoList.removeTask(tasks); + updateToDoListHistory(previousToDoList); + indicateToDoListChanged(); + } + + @Override + public synchronized void addTask(Task task) throws UniqueTaskList.DuplicateTaskException { + ToDoList previousToDoList = new ToDoList(this.ToDoList); + ToDoList.addTask(task); + updateToDoListHistory(previousToDoList); + updateFilteredListToShowAll(); + indicateToDoListChanged(); + } + + @Override + //@@author A0146682X + public synchronized void editTask(ReadOnlyTask target, Task replacement) throws TaskNotFoundException { + ToDoList previousToDoList = new ToDoList(this.ToDoList); + ToDoList.editTask(target, replacement); + updateToDoListHistory(previousToDoList); + indicateToDoListChanged(); + } + + //@@author A0153736B + /** + * Update Stacks of ToDoListHistory and ToDoListUndoHistory when task list of ToDoList is going to be changed. + * @param previousToDoList + */ + private void updateToDoListHistory(ToDoList previousToDoList) { + ToDoListHistory.push(previousToDoList); + ToDoListUndoHistory.clear(); + } + //@@author + + //=========== Filtered Task List Accessors =============================================================== + + @Override + public UnmodifiableObservableList getFilteredAllTaskList() { + return new UnmodifiableObservableList<>(filteredAllTasks); + } + + @Override + public UnmodifiableObservableList getFilteredCompleteTaskList() { + return new UnmodifiableObservableList<>(filteredCompleteTasks); + } + + @Override + public UnmodifiableObservableList getFilteredIncompleteTaskList() { + return new UnmodifiableObservableList<>(filteredIncompleteTasks); + } + + @Override + public UnmodifiableObservableList getFilteredOverdueTaskList() { + return new UnmodifiableObservableList<>(filteredOverdueTasks); + } + + @Override + public void updateFilteredListToShowAll() { + filteredAllTasks.setPredicate(null); + filteredCompleteTasks.setPredicate(null); + filteredIncompleteTasks.setPredicate(null); + filteredOverdueTasks.setPredicate(null); + } + + @Override + public void updateFilteredTaskList(List keywords, String findType){ + updateFilteredTaskList(new PredicateExpression(new NameQualifier(keywords, findType))); + } + + private void updateFilteredTaskList(Expression expression) { + filteredAllTasks.setPredicate(expression::satisfies); + filteredCompleteTasks.setPredicate(expression::satisfies); + filteredIncompleteTasks.setPredicate(expression::satisfies); + filteredOverdueTasks.setPredicate(expression::satisfies); + } + + @Override + public void updateFilteredTaskList(String filter) throws DateTimeException { + updateFilteredTaskList(new PredicateExpression(new DateQualifier(filter))); + } + + //========== Inner classes/interfaces used for filtering ================================================== + + interface Expression { + boolean satisfies(ReadOnlyTask task); + 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(); + } + + //@@author A0153736B + /** + * Determine if task name contain any/all/phrase of provided keywords according to the provided findType. + */ + private class NameQualifier implements Qualifier { + private List nameKeyWords; + private String findType; + + NameQualifier(List nameKeyWords, String findType) { + this.nameKeyWords = nameKeyWords; + this.findType = findType; + } + + @Override + public boolean run(ReadOnlyTask task) { + if (FindCommand.FINDTYPE_ALL.equals(findType)) { + for (String keyword : nameKeyWords) { + if (!StringUtil.containsIgnoreCase(task.getName().fullName, keyword)) { + return false; + } + } + return true; + } + else if (FindCommand.FINDTYPE_PHRASE.equals(findType)) { + String keyword = String.join(" ", nameKeyWords).trim().toLowerCase(); + return task.getName().fullName.toLowerCase().contains(keyword); + } + else { + return nameKeyWords.stream() + .filter(keyword -> StringUtil.containsIgnoreCase(task.getName().fullName, keyword)) + .findAny() + .isPresent(); + } + } + + @Override + public String toString() { + return "name=" + String.join(", ", nameKeyWords); + } + } + + /** + * Determine if task date is within the period of the provided dateFilter. + * + * @throws DateTimeException if the date of the provided dateFilter is invalid + */ + private class DateQualifier implements Qualifier { + private String dateFilter; + + DateQualifier(String dateFilter) { + this.dateFilter = dateFilter; + } + + @Override + public boolean run(ReadOnlyTask task) throws DateTimeException { + if (task.getInterval().isFloat()) { + return false; + } + + LocalDate currentDate = LocalDate.now(); + + int currentDayOfWeek = currentDate.getDayOfWeek().getValue(); + LocalDate currentWeekStart = currentDate.minusDays(currentDayOfWeek); + LocalDate currentWeekEnd = currentDate.plusDays(6-currentDayOfWeek); + + int currentDayOfMonth = currentDate.getDayOfMonth(); + LocalDate currentMonthStart = currentDate.minusDays(currentDayOfMonth-1); + LocalDate currentMonthEnd = currentDate.plusDays(currentDate.lengthOfMonth()-currentDayOfMonth); + + LocalDate taskEndDate = task.getInterval().getEndDate().getDate(); + LocalDate taskStartDate = (task.getInterval().getStartDate() == null? + taskEndDate: task.getInterval().getStartDate().getDate()); + + if (ListCommand.FILTER_TODAY.equals(dateFilter)) { + return (!taskEndDate.isBefore(currentDate) && !taskStartDate.isAfter(currentDate)); + } + else if (ListCommand.FILTER_WEEK.equals(dateFilter)) { + return (!taskEndDate.isBefore(currentWeekStart) && !taskStartDate.isAfter(currentWeekEnd)); + } + else if (ListCommand.FILTER_MONTH.equals(dateFilter)) { + return (!taskEndDate.isBefore(currentMonthStart) && !taskStartDate.isAfter(currentMonthEnd)); + } + else { + LocalDate date = DateParser.parseDate(dateFilter); + return (!taskEndDate.isBefore(date) && !taskStartDate.isAfter(date)); + } + } + + @Override + public String toString() { + return "date=" + dateFilter; + } + } + +} diff --git a/src/main/java/seedu/todolist/model/ReadOnlyToDoList.java b/src/main/java/seedu/todolist/model/ReadOnlyToDoList.java new file mode 100644 index 000000000000..96980694c978 --- /dev/null +++ b/src/main/java/seedu/todolist/model/ReadOnlyToDoList.java @@ -0,0 +1,20 @@ +package seedu.todolist.model; + +import seedu.todolist.model.task.ReadOnlyTask; +import seedu.todolist.model.task.UniqueTaskList; + +import java.util.List; + +/** + * Unmodifiable view of an address book + */ +public interface ReadOnlyToDoList { + + UniqueTaskList getUniqueTaskList(); + + /** + * Returns an unmodifiable view of tasks list + */ + List getTaskList(); + +} diff --git a/src/main/java/seedu/todolist/model/ToDoList.java b/src/main/java/seedu/todolist/model/ToDoList.java new file mode 100644 index 000000000000..a9f074974696 --- /dev/null +++ b/src/main/java/seedu/todolist/model/ToDoList.java @@ -0,0 +1,144 @@ +package seedu.todolist.model; + +import javafx.collections.ObservableList; +import seedu.todolist.model.task.ReadOnlyTask; +import seedu.todolist.model.task.Status; +import seedu.todolist.model.task.Task; +import seedu.todolist.model.task.UniqueTaskList; + +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 ToDoList implements ReadOnlyToDoList { + + private final UniqueTaskList tasks; + + /** + * Create a new empty uniqueTaskList for this to-do list + */ + public ToDoList() { + tasks = new UniqueTaskList(); + } + + /** + * Tasks are copied into this to-do list + */ + public ToDoList(ReadOnlyToDoList toBeCopied) { + this(toBeCopied.getUniqueTaskList()); + } + + /** + * Tasks are copied into this to-do list + */ + public ToDoList(UniqueTaskList tasksToBeCopied) { + tasks = new UniqueTaskList(); + resetData(tasksToBeCopied.getInternalList()); + } + + public static ReadOnlyToDoList getEmptyToDoList() { + return new ToDoList(); + } + +//// list overwrite operations + + public ObservableList getAllTasks() { + return tasks.getInternalList(); + } + + public ObservableList getIncompleteTasks() { + return tasks.getFilteredTaskList(Status.Type.Incomplete.toString()); + } + + public ObservableList getCompletedTasks() { + return tasks.getFilteredTaskList(Status.Type.Complete.toString()); + } + + public ObservableList getOverdueTasks() { + return tasks.getFilteredTaskList(Status.Type.Overdue.toString()); + } + + public void setTasks(List tasks) { + this.tasks.getInternalList().setAll(tasks); + } + + public void resetData(Collection newTasks) { + setTasks(newTasks.stream().map(Task::new).collect(Collectors.toList())); + } + + public void resetData(ReadOnlyToDoList newData) { + resetData(newData.getTaskList()); + } + +//// task-level operations + + /** + * Adds a task to the to-do list. + */ + public void addTask(Task p) throws UniqueTaskList.DuplicateTaskException { + tasks.add(p); + } + + //@@author A0146682X + /** + * Edits a task in the to-do list + */ + public boolean editTask(ReadOnlyTask key, Task replacement) throws UniqueTaskList.TaskNotFoundException { + if (tasks.edit(key, replacement)) { + return true; + } else { + throw new UniqueTaskList.TaskNotFoundException(); + } + } + //@author + + public boolean markTask(ReadOnlyTask... keys) throws UniqueTaskList.TaskNotFoundException { + if (tasks.mark(keys)) { + return true; + } else { + throw new UniqueTaskList.TaskNotFoundException(); + } + } + + public boolean removeTask(ReadOnlyTask... keys) throws UniqueTaskList.TaskNotFoundException { + if (tasks.remove(keys)) { + return true; + } else { + throw new UniqueTaskList.TaskNotFoundException(); + } + } + +//// util methods + + @Override + public String toString() { + return tasks.getInternalList().size() + " tasks"; + // TODO: refine later + } + + @Override + public List getTaskList() { + return Collections.unmodifiableList(tasks.getInternalList()); + } + + @Override + public UniqueTaskList getUniqueTaskList() { + return this.tasks; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof ToDoList // instanceof handles nulls + && this.tasks.equals(((ToDoList) other).tasks)); + } + + @Override + public int hashCode() { + // use this method for custom fields hashing instead of implementing your own + return Objects.hash(tasks); + } +} diff --git a/src/main/java/seedu/address/model/UserPrefs.java b/src/main/java/seedu/todolist/model/UserPrefs.java similarity index 93% rename from src/main/java/seedu/address/model/UserPrefs.java rename to src/main/java/seedu/todolist/model/UserPrefs.java index da9c8037f495..9a7105a8a5b0 100644 --- a/src/main/java/seedu/address/model/UserPrefs.java +++ b/src/main/java/seedu/todolist/model/UserPrefs.java @@ -1,9 +1,9 @@ -package seedu.address.model; - -import seedu.address.commons.core.GuiSettings; +package seedu.todolist.model; import java.util.Objects; +import seedu.todolist.commons.core.GuiSettings; + /** * Represents User's preferences. */ diff --git a/src/main/java/seedu/todolist/model/parser/DateParser.java b/src/main/java/seedu/todolist/model/parser/DateParser.java new file mode 100644 index 000000000000..44b904126ce4 --- /dev/null +++ b/src/main/java/seedu/todolist/model/parser/DateParser.java @@ -0,0 +1,95 @@ +package seedu.todolist.model.parser; + +import java.time.DateTimeException; +import java.time.LocalDate; + +//@@author A0138601M +/** + * Converts a String to Date and vice versa. + */ +public class DateParser { + + private static final int DATE_COMPONENT_INDEX_YEAR = 2; + private static final int DATE_COMPONENT_INDEX_MONTH = 1; + private static final int DATE_COMPONENT_INDEX_DAY = 0; + + private static final String MONTH_NOT_FOUND = "Unable to find month "; + private static final String[] MONTH_LIST = {"JANUARY", "FEBRUARY", "MARCH", "APRIL", "MAY", "JUNE", "JULY", + "AUGUST", "SEPTEMBER", "OCTOBER", "NOVEMBER", "DECEMBER"}; + private static final int MONTH_DUMMY_VALUE = 0; + private static final int MONTH_OFFSET_INDEX = 1; + + public static final String DATE_DELIMITER_SLASH = "/"; + public static final String DATE_DELIMITER_SPACE = " "; + + + /** + * Parses string date input into LocalDate date. + * + * @param string date input + * @return LocalDate date based on the string date input + */ + public static LocalDate parseDate(String date) throws DateTimeException { + assert date != null; + LocalDate parsedDate; + if (date.contains(DATE_DELIMITER_SLASH)) { + parsedDate = parseDateWithSlash(date); + } else { + parsedDate = parseDateWithMonthName(date); + } + return parsedDate; + } + + /** + * Parses string date input with slash into LocalDate date. + * + * @param string date input with slash + * @return LocalDate date based on the string date input + */ + private static LocalDate parseDateWithSlash(String date) throws DateTimeException { + String[] dateComponents = date.split(DATE_DELIMITER_SLASH); + + int day, month, year; + day = Integer.parseInt(dateComponents[DATE_COMPONENT_INDEX_DAY]); + month = Integer.parseInt(dateComponents[DATE_COMPONENT_INDEX_MONTH]); + year = Integer.parseInt(dateComponents[DATE_COMPONENT_INDEX_YEAR]); + + return LocalDate.of(year, month, day); + } + + /** + * Parses string date input with month name into LocalDate date. + * + * @param string date input with month name + * @return LocalDate date based on the string date input + */ + private static LocalDate parseDateWithMonthName(String date) throws DateTimeException { + String[] dateComponents = date.split(DATE_DELIMITER_SPACE); + + int day, month, year; + day = Integer.parseInt(dateComponents[DATE_COMPONENT_INDEX_DAY]); + month = getMonthValue(dateComponents[DATE_COMPONENT_INDEX_MONTH]); + year = Integer.parseInt(dateComponents[DATE_COMPONENT_INDEX_YEAR]); + + return LocalDate.of(year, month, day); + } + + /** + * Parses string month name into int month value. + * + * @param string month name + * @return int month value based on string month name + */ + private static int getMonthValue(String month) { + int monthValue = MONTH_DUMMY_VALUE; + for (int i = 0; i < MONTH_LIST.length; i++) { + if (MONTH_LIST[i].contains(month.toUpperCase())) { + monthValue = i + MONTH_OFFSET_INDEX; + } + } + if (monthValue == MONTH_DUMMY_VALUE) { + throw new DateTimeException(MONTH_NOT_FOUND + month); + } + return monthValue; + } +} diff --git a/src/main/java/seedu/todolist/model/parser/TimeParser.java b/src/main/java/seedu/todolist/model/parser/TimeParser.java new file mode 100644 index 000000000000..88bc745321d5 --- /dev/null +++ b/src/main/java/seedu/todolist/model/parser/TimeParser.java @@ -0,0 +1,116 @@ +package seedu.todolist.model.parser; + +import java.time.DateTimeException; +import java.time.LocalTime; + +//@@author A0138601M +/** + * Converts a String to Time and vice versa. + */ +public class TimeParser { + + private static final int TIME_COMPONENT_TOTAL = 2; + private static final int TIME_COMPONENT_INDEX_MINUTE = 1; + private static final int TIME_COMPONENT_INDEX_HOUR = 0; + private static final int TIME_COMPONENT_HOUR_START_INDEX = 0; //Position of the start of hour in a string + private static final int TIME_COMPONENT_PERIOD_LENGTH = 2; //am and pm are 2 characters long + private static final int TIME_COMPONENT_MINUTE_DEFAULT = 0; + private static final int TIME_COMPONENT_PERIOD_OFFSET = 12; + + private static final String TIME_DELIMITER = ":"; + + /** + * Parses string time input into LocalTime time. + * + * @param string time input + * @return LocalTime time based on the string time input + */ + public static LocalTime parseTime(String time) throws DateTimeException { + assert time != null; + LocalTime parsedTime; + + //trim all spaces away + String trimmedTime = time.replaceAll("\\s+", ""); + + if (trimmedTime.toUpperCase().contains("AM")) { + parsedTime = parseTimeWithAMFormat(trimmedTime); + } else if (time.toUpperCase().contains("PM")) { + parsedTime = parseTimeWithPMFormat(trimmedTime); + } else { + parsedTime = parseTimeWithContinentalFormat(trimmedTime); + } + + return parsedTime; + } + + /** + * Parses string time input with AM format into LocalTime time. + * + * @param string time input with AM format + * @return LocalTime time based on the string time input + */ + private static LocalTime parseTimeWithAMFormat(String time) throws DateTimeException { + String timeWithoutPeriod = removePeriod(time); + String[] timeComponents = timeWithoutPeriod.split(":"); + + int hour, minute; + if (timeComponents.length < TIME_COMPONENT_TOTAL) { + hour = Integer.parseInt(timeWithoutPeriod); + minute = TIME_COMPONENT_MINUTE_DEFAULT; + } else { + hour = Integer.parseInt(timeComponents[TIME_COMPONENT_INDEX_HOUR]); + minute = Integer.parseInt(timeComponents[TIME_COMPONENT_INDEX_MINUTE]); + } + + if (hour == TIME_COMPONENT_PERIOD_OFFSET) { + hour = hour - TIME_COMPONENT_PERIOD_OFFSET; + } + + return LocalTime.of(hour, minute); + } + + /** + * Parses string time input with PM format into LocalTime time. + * + * @param string time input with PM format + * @return LocalTime time based on the string time input + */ + private static LocalTime parseTimeWithPMFormat(String time) throws DateTimeException { + String timeWithoutPeriod = removePeriod(time); + String[] timeComponents = timeWithoutPeriod.split(TIME_DELIMITER); + + int hour, minute; + if (timeComponents.length < TIME_COMPONENT_TOTAL) { + hour = Integer.parseInt(timeWithoutPeriod); + minute = TIME_COMPONENT_MINUTE_DEFAULT; + } else { + hour = Integer.parseInt(timeComponents[TIME_COMPONENT_INDEX_HOUR]); + minute = Integer.parseInt(timeComponents[TIME_COMPONENT_INDEX_MINUTE]); + } + + if (hour != TIME_COMPONENT_PERIOD_OFFSET) { + hour = hour + TIME_COMPONENT_PERIOD_OFFSET; + } + return LocalTime.of(hour, minute); + } + + private static String removePeriod(String time) { + return time.substring(TIME_COMPONENT_HOUR_START_INDEX, time.length() - TIME_COMPONENT_PERIOD_LENGTH); + } + + /** + * Parses string time input with continental (24hr) format into LocalTime time. + * + * @param string time input with 24hr format + * @return LocalTime time based on the string time input + */ + private static LocalTime parseTimeWithContinentalFormat(String time) throws DateTimeException { + String[] timeComponents = time.split(":"); + + int hour = Integer.parseInt(timeComponents[TIME_COMPONENT_INDEX_HOUR]); + int minute = Integer.parseInt(timeComponents[TIME_COMPONENT_INDEX_MINUTE]); + + return LocalTime.of(hour, minute); + } + +} diff --git a/src/main/java/seedu/todolist/model/task/Interval.java b/src/main/java/seedu/todolist/model/task/Interval.java new file mode 100644 index 000000000000..2eb1fe0b2109 --- /dev/null +++ b/src/main/java/seedu/todolist/model/task/Interval.java @@ -0,0 +1,364 @@ +package seedu.todolist.model.task; + +import seedu.todolist.commons.exceptions.IllegalValueException; + +//@@author A0138601M +public class Interval implements Comparable { + + public static final String MESSAGE_INTERVAL_CONSTRAINTS_DATE = "End date cannot be earlier than start date"; + public static final String MESSAGE_INTERVAL_CONSTRAINTS_TIME = "End time cannot be earlier than start time"; + + private TaskDate startDate; + private TaskTime startTime; + private TaskDate endDate; + private TaskTime endTime; + + + public Interval() { + + } + + /** + * Constructs and validates the Interval based on which parameters given is not null. + * + * Constructs an event type interval if all parameters are not null. + * Constructs an deadline with time type interval if only endDate and endTime are not null. + * Constructs an deadline without time type interval if only endDate is not null. + * Constructs an float type interval if all parameters are null. + * + * @throws IllegalValueException if given date or time is invalid or set of date and time is an invalid interval. + */ + public Interval(String startDate, String startTime, String endDate, String endTime) throws IllegalValueException { + if (isEvent(startDate, startTime, endDate, endTime)) { + initEvent(startDate, startTime, endDate, endTime); + } else if (isDeadlineWithTime(startDate, startTime, endDate, endTime)) { + initDeadlineWithTime(endDate, endTime); + } else if (isDeadlineWithoutTime(startDate, startTime, endDate, endTime)) { + initDeadlineWithoutTime(endDate); + } else if (isFloat(startDate, startTime, endDate, endTime)) { + initFloat(); + } else { + assert false : "A given interval must represent an event, deadline, or float"; + } + } + + /** + * Initialises an event type interval. + * + * @throws IllegalValueException if given date or time is invalid or set of date and time is an invalid event interval. + * For example, endDate earlier than startDate or endTime earlier than startTime. + */ + private void initEvent(String startDate, String startTime, String endDate, String endTime) throws IllegalValueException { + this.startDate = new TaskDate(startDate); + this.startTime = new TaskTime(startTime); + this.endDate = new TaskDate(endDate); + this.endTime = new TaskTime(endTime); + + if (!isValidDateInterval(this.startDate, this.endDate)) { + throw new IllegalValueException(MESSAGE_INTERVAL_CONSTRAINTS_DATE); + } + + if (!isValidTimeInterval(this.startDate, this.startTime, this.endDate, this.endTime)) { + throw new IllegalValueException(MESSAGE_INTERVAL_CONSTRAINTS_TIME); + } + } + + /** + * Initialises a deadline with time type interval. + * + * @throws IllegalValueException if given date or time is invalid + */ + private void initDeadlineWithTime(String endDate, String endTime) throws IllegalValueException { + this.endDate = new TaskDate(endDate); + this.endTime = new TaskTime(endTime); + } + + /** + * Initialises a deadline without time type interval. + * + * @throws IllegalValueException if given date is invalid + */ + private void initDeadlineWithoutTime(String endDate) throws IllegalValueException { + this.endDate = new TaskDate(endDate); + } + + /** + * Initialises a float type interval. + */ + private void initFloat() { + this.startDate = null; + this.startTime = null; + this.endDate = null; + this.endTime = null; + } + + /** + * Returns true if a given interval has a valid task date interval. (i.e. startDate is earlier than endDate) + */ + private boolean isValidDateInterval(TaskDate startDate, TaskDate endDate) { + return !endDate.isBefore(startDate); + } + + /** + * Returns true if a given interval has a valid task time interval. + * if startDate and endDate are not equal, it is assume to be a valid interval. (i.e. startDate is earlier than endDate) + */ + private boolean isValidTimeInterval(TaskDate startDate, TaskTime startTime, TaskDate endDate, TaskTime endTime) { + return !startDate.equals(endDate) || !endTime.isBefore(startTime); + } + + /** + * Returns true if interval is earlier than current datetime + */ + public boolean isOver() { + Interval now = new Interval(); + now.endDate = TaskDate.now(); + now.endTime = TaskTime.now(); + return this.compareTo(now) < 0; + } + + /** + * Returns true if a given set of datetime is an event. + */ + private boolean isEvent(String startDate, String startTime, String endDate, String endTime) { + return startDate != null + && startTime != null + && endDate != null + && endTime != null; + } + + /** + * Returns true if the interval object is an event. + */ + public boolean isEvent() { + return this.startDate != null + && this.startTime != null + && this.endDate != null + && this.endTime != null; + } + + /** + * Returns true if a given set of datetime is a deadline with time. + */ + private boolean isDeadlineWithTime(String startDate, String startTime, String endDate, String endTime) { + return startDate == null + && startTime == null + && endDate != null + && endTime != null; + } + + /** + * Returns true if the interval object is a deadline with time. + */ + public boolean isDeadlineWithTime() { + return this.startDate == null + && this.startTime == null + && this.endDate != null + && this.endTime != null; + } + + /** + * Returns true if a given set of datetime is a deadline without time. + */ + private boolean isDeadlineWithoutTime(String startDate, String startTime, String endDate, String endTime) { + return startDate == null + && startTime == null + && endDate != null + && endTime == null; + } + + /** + * Returns true if the interval object is a deadline without time. + */ + public boolean isDeadlineWithoutTime() { + return this.startDate == null + && this.startTime == null + && this.endDate != null + && this.endTime == null; + } + + /** + * Returns true if a given interval is a floating task + */ + private boolean isFloat(String startDate, String startTime, String endDate, String endTime) { + return startDate == null + && startTime == null + && endDate == null + && endTime == null; + } + + /** + * Returns true if the interval object is a floating task + */ + public boolean isFloat() { + return this.startDate == null + && this.startTime == null + && this.endDate == null + && this.endTime == null; + } + + public TaskDate getStartDate() { + return this.startDate; + } + + public TaskTime getStartTime() { + return this.startTime; + } + + public TaskDate getEndDate() { + return this.endDate; + } + + public TaskTime getEndTime() { + return this.endTime; + } + + /** + * Returns a formatted string based on the interval type. + * + * Examples: + * Event type - 20 Nov 2016, 3:00PM to 21 Nov 2016, 4:00PM + * Deadline with time type - 20 Nov 2016, 3:00PM + * Deadline without time type - 20 Nov 2016 + */ + public String toString() { + if (this.isEvent()) { + return formatStartDateTime() + " to " + formatEndDateTime(); + } else if (this.isDeadlineWithTime()) { + return formatEndDateTime(); + } else if (this.isDeadlineWithoutTime()) { + return formatEndDate(); + } else { + return null; + } + } + + public String formatStartDateTime() { + return startDate + ", " + startTime; + } + + public String formatEndDateTime() { + return endDate + ", " + endTime; + } + + public String formatEndDate() { + return endDate + ""; + } + + @Override + public int compareTo(Interval interval) { + TaskDate firstDate = this.getDateToCompare(); + TaskDate secondDate = interval.getDateToCompare(); + TaskTime firstTime = this.getTimeToCompare(); + TaskTime secondTime = interval.getTimeToCompare(); + + //If both are float task, they are equal + if (isFloatTask(firstDate) && isFloatTask(secondDate)) { + return 0; + } + + return compareDateTime(firstDate, secondDate, firstTime, secondTime); + } + + /** + * Returns true if TaskDate is null + */ + private boolean isFloatTask(TaskDate date) { + return date == null; + } + + /** + * Returns true is TaskTime is not null + */ + private boolean isTimedTask(TaskTime time) { + return time != null; + } + + /** + * Compares the 2 date-times and determines the ordering + * firstDate and secondDate cannot both be Float Tasks + */ + private int compareDateTime(TaskDate firstDate, TaskDate secondDate, TaskTime firstTime, TaskTime secondTime) { + assert !isFloatTask(firstDate) || !isFloatTask(secondDate); + + int compare; + if (!isFloatTask(firstDate)) { + compare = compareSecondDate(firstDate, secondDate, firstTime, secondTime); + } else { + compare = 1; + } + return compare; + } + + /** + * Compare the secondDate. firstDate must not be null + * Returns 1 if first date-time is later than second date-time + * Returns 0 if both date-times are equal + * Returns -1 otherwise + */ + private int compareSecondDate(TaskDate firstDate, TaskDate secondDate, TaskTime firstTime, TaskTime secondTime) { + assert firstDate != null; + + if (isFloatTask(secondDate)) { + return -1; + } else { + if (firstDate.equals(secondDate)) { //if date are the same, determine order with time + return compareTime(firstTime, secondTime); + } + return firstDate.compareTo(secondDate); + } + } + + /** + * Compares the firstTime and secondTime + * Returns 1 if first time is later than second time + * Returns 0 if both times are equal + * Returns -1 otherwise + */ + private int compareTime(TaskTime firstTime, TaskTime secondTime) { + if (isTimedTask(firstTime)) { + return firstTime.compareTo(secondTime); + } else if (isTimedTask(secondTime)) { + return -secondTime.compareTo(firstTime); //flip comparison + } else { + return 0; + } + } + + /** + * Returns the date to be used for comparison. + */ + private TaskDate getDateToCompare() { + if (this.startDate != null) { + return this.startDate; + } else if (this.endDate != null) { + return this.endDate; + } else { + return null; + } + } + + /** + * Returns the time to be used for comparison. + */ + private TaskTime getTimeToCompare() { + if (this.startTime != null) { + return this.startTime; + } else if (this.endTime != null) { + return this.endTime; + } else { + return null; + } + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Interval // instanceof handles nulls + && TaskDate.isEquals(this.startDate, ((Interval) other).startDate) + && TaskTime.isEquals(this.startTime, ((Interval) other).startTime) + && TaskDate.isEquals(this.endDate, ((Interval) other).endDate) + && TaskTime.isEquals(this.endTime, ((Interval) other).endTime)); + } + +} diff --git a/src/main/java/seedu/todolist/model/task/Location.java b/src/main/java/seedu/todolist/model/task/Location.java new file mode 100644 index 000000000000..0dce37006785 --- /dev/null +++ b/src/main/java/seedu/todolist/model/task/Location.java @@ -0,0 +1,46 @@ +package seedu.todolist.model.task; + +import seedu.todolist.commons.exceptions.IllegalValueException; + +//@@author A0153736B +/** + * Represents a Task's location parameter in the address book. + * Guarantees: immutable; is valid as declared in {@link #isValidLocation(String)} + */ +public class Location { + public static final String MESSAGE_LOCATION_PARAMETER_CONSTRAINTS = "Task's location parameter should be spaces or alphanumeric characters"; + public static final String LOCATION_PARAMETER_VALIDATION_REGEX = "[\\p{Alnum} ]+"; + + public final String location; + + /** + * Validates given location parameter. + * + * @throws IllegalValueException if given location parameter string is invalid. + */ + public Location(String location) throws IllegalValueException { + if (location != null) { + String trimmedLocation = location.trim(); + if (!isValidLocation(trimmedLocation)) { + throw new IllegalValueException(MESSAGE_LOCATION_PARAMETER_CONSTRAINTS); + } + this.location = trimmedLocation; + } + else { + this.location = location; + } + } + + /** + * Returns true if a given string is a valid task's location parameter. + */ + public static boolean isValidLocation(String test) { + return test.matches(LOCATION_PARAMETER_VALIDATION_REGEX); + } + + + @Override + public String toString() { + return location; + } +} diff --git a/src/main/java/seedu/address/model/person/Name.java b/src/main/java/seedu/todolist/model/task/Name.java similarity index 59% rename from src/main/java/seedu/address/model/person/Name.java rename to src/main/java/seedu/todolist/model/task/Name.java index 4f30033e70fe..bc7dad31f52d 100644 --- a/src/main/java/seedu/address/model/person/Name.java +++ b/src/main/java/seedu/todolist/model/task/Name.java @@ -1,15 +1,16 @@ -package seedu.address.model.person; +package seedu.todolist.model.task; -import seedu.address.commons.exceptions.IllegalValueException; +import seedu.todolist.commons.exceptions.IllegalValueException; /** - * Represents a Person's name in the address book. + * Represents a Task's name in the address book. * Guarantees: immutable; is valid as declared in {@link #isValidName(String)} */ public class Name { - public static final String MESSAGE_NAME_CONSTRAINTS = "Person names should be spaces or alphanumeric characters"; - public static final String NAME_VALIDATION_REGEX = "[\\p{Alnum} ]+"; + public static final String MESSAGE_NAME_CONSTRAINTS = "Task names should be spaces or alphanumeric characters"; + public static final String MESSAGE_NAME_CONSTRAINTS_EMPTY = "Task names cannot be empty!"; + public static final String NAME_VALIDATION_REGEX = "[\\p{Alnum}\\s'-]+"; public final String fullName; @@ -19,16 +20,18 @@ public class Name { * @throws IllegalValueException if given name string is invalid. */ public Name(String name) throws IllegalValueException { - assert name != null; - name = name.trim(); - if (!isValidName(name)) { + if (name == null) { + throw new IllegalValueException(MESSAGE_NAME_CONSTRAINTS_EMPTY); + } + String trimmedName = name.trim(); + if (!isValidName(trimmedName)) { throw new IllegalValueException(MESSAGE_NAME_CONSTRAINTS); } - this.fullName = name; + this.fullName = trimmedName; } /** - * Returns true if a given string is a valid person name. + * Returns true if a given string is a valid task name. */ public static boolean isValidName(String test) { return test.matches(NAME_VALIDATION_REGEX); @@ -47,9 +50,4 @@ public boolean equals(Object other) { && this.fullName.equals(((Name) other).fullName)); // state check } - @Override - public int hashCode() { - return fullName.hashCode(); - } - } diff --git a/src/main/java/seedu/todolist/model/task/ReadOnlyTask.java b/src/main/java/seedu/todolist/model/task/ReadOnlyTask.java new file mode 100644 index 000000000000..18dda0e48a6e --- /dev/null +++ b/src/main/java/seedu/todolist/model/task/ReadOnlyTask.java @@ -0,0 +1,82 @@ +package seedu.todolist.model.task; + +/** + * A read-only immutable interface for a Task in the to-do list. + * Implementations should guarantee: name is present and not null, field values are validated. + */ +public interface ReadOnlyTask extends Comparable { + + Name getName(); + Interval getInterval(); + Location getLocation(); + Remarks getRemarks(); + Status getStatus(); + + /** + * 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 + && other.getName().equals(this.getName())) // state checks here onwards + && other.getInterval().equals(this.getInterval()); + } + + /** + * Formats the task as text, showing all details. + */ + default String getAsText() { + final StringBuilder builder = new StringBuilder(); + builder.append(getNameAsText() + " ") + .append(getIntervalAsText() + " ") + .append(getLocationAsText() + " ") + .append(getRemarksAsText()) + .append(getStatusAsText()); + return builder.toString(); + } + + /** + * Formats the name as text. + */ + default String getNameAsText() { + return getName().toString(); + } + + /** + * Formats the interval as text. + */ + default String getIntervalAsText() { + Interval interval = getInterval(); + if (!interval.isFloat()) { + return "from " + getInterval().toString(); + } + return ""; + } + + /** + * Formats the location as text. + */ + default String getLocationAsText() { + Location location = getLocation(); + if (location.location != null) { + return "at " + getLocation().toString(); + } + return ""; + } + + /** + * Formats the remarks as text. + */ + default String getRemarksAsText() { + Remarks remarks = getRemarks(); + if (remarks.remarks != null) { + return "remarks " + getRemarks().toString(); + } + return ""; + } + + default String getStatusAsText() { + Status status = getStatus(); + return "to " + status.getType().toString() + " tab"; + } +} diff --git a/src/main/java/seedu/todolist/model/task/Remarks.java b/src/main/java/seedu/todolist/model/task/Remarks.java new file mode 100644 index 000000000000..7bf409a60096 --- /dev/null +++ b/src/main/java/seedu/todolist/model/task/Remarks.java @@ -0,0 +1,46 @@ +package seedu.todolist.model.task; + +import seedu.todolist.commons.exceptions.IllegalValueException; + +//@@author A0153736B +/** + * Represents a Task's remarks parameter in the address book. + * Guarantees: immutable; is valid as declared in {@link #isValidRemarksParameter(String)} + */ +public class Remarks { + public static final String MESSAGE_REMARKS_PARAMETER_CONSTRAINTS = "Task's remarks parameter should be spaces or alphanumeric characters"; + public static final String REMARKS_PARAMETER_VALIDATION_REGEX = "[\\p{Alnum} ]+"; + + public final String remarks; + + /** + * Validates given remarks parameter. + * + * @throws IllegalValueException if given remarks parameter string is invalid. + */ + public Remarks(String remarks) throws IllegalValueException { + if (remarks != null) { + String trimmedRemarks = remarks.trim(); + if (!isValidRemarksParameter(trimmedRemarks)) { + throw new IllegalValueException(MESSAGE_REMARKS_PARAMETER_CONSTRAINTS); + } + this.remarks = trimmedRemarks; + } + else { + this.remarks = remarks; + } + } + + /** + * Returns true if a given string is a valid task's remarks parameter. + */ + public static boolean isValidRemarksParameter(String test) { + return test.matches(REMARKS_PARAMETER_VALIDATION_REGEX); + } + + + @Override + public String toString() { + return remarks; + } +} diff --git a/src/main/java/seedu/todolist/model/task/Status.java b/src/main/java/seedu/todolist/model/task/Status.java new file mode 100644 index 000000000000..3f8d55e827b2 --- /dev/null +++ b/src/main/java/seedu/todolist/model/task/Status.java @@ -0,0 +1,99 @@ +package seedu.todolist.model.task; + +//@@author A0138601M +/** + * Represents a Task's status in the to do list. + * Guarantees: is one of the three values 'complete', 'incomplete' or 'overdue' + */ +public class Status { + public enum Type { + Incomplete("incomplete"), + Complete("complete"), + Overdue("overdue"); + + String statusType; + Type(String type) { + statusType = type; + } + + @Override + public String toString() { + return statusType; + } + } + + private Type status; + + /** + * Constructs a Status object with the default status of type incomplete + */ + public Status() { + setStatus(Status.Type.Incomplete); + } + + /** + * Constructs a Status object with the specified status type + */ + public Status(Type status) { + setStatus(status); + } + + /** + * Constructs a Status object with the specified string name of the type + */ + public Status(String status) { + assert status != null; + + if (status.equals(Type.Incomplete.toString())) { + setStatus(Type.Incomplete); + } else if (status.equals(Type.Complete.toString())) { + setStatus(Type.Complete); + } else if (status.equals(Type.Overdue.toString())) { + setStatus(Type.Overdue); + } else { + assert false : "Status must be either incomeplete, complete or overdue"; + } + } + + public Type getType() { + return this.status; + } + + public void setStatus(Type status) { + this.status = status; + } + + /** + * Returns true if the status is incomplete + */ + public boolean isIncomplete() { + return this.status.equals(Status.Type.Incomplete); + } + + /** + * Returns true if the status is complete + */ + public boolean isComplete() { + return this.status.equals(Status.Type.Complete); + } + + /** + * Returns true if the status is overdue + */ + public boolean isOverdue() { + return this.status.equals(Status.Type.Overdue); + } + + @Override + public String toString() { + return status.toString(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Status // instanceof handles nulls + && this.status == ((Status) other).status); // state check + } + +} diff --git a/src/main/java/seedu/todolist/model/task/Task.java b/src/main/java/seedu/todolist/model/task/Task.java new file mode 100644 index 000000000000..ae8ee23fd8fa --- /dev/null +++ b/src/main/java/seedu/todolist/model/task/Task.java @@ -0,0 +1,98 @@ +package seedu.todolist.model.task; + +import java.util.Objects; + +/** + * Represents a Task in the to do list. + * Guarantees: name is present and not null, field values are validated. + */ +public class Task implements ReadOnlyTask { + + private Name name; + private Interval interval; + private Location location; + private Remarks remarks; + private Status status; + + /** + * Only Name field must be present and not null. Other fields can be null. + */ + public Task(Name name, Interval interval, Location location, Remarks remarks, Status status) { + assert name != null; + this.name = name; + this.interval = interval; + this.location = location; + this.remarks = remarks; + + if (status.isIncomplete() && interval.isOver()) { + Status overdueStatus = new Status(Status.Type.Overdue); + this.status = overdueStatus; + } + else { + this.status = status; + } + } + + /** + * Only Name field must be present and not null. Other fields can be null. Default status is incomplete. + */ + public Task(Name name, Interval interval, Location location, Remarks remarks) { + this(name, interval, location, remarks, new Status()); + } + + /** + * Copy constructor. + */ + public Task(ReadOnlyTask source) { + this(source.getName(), source.getInterval(), source.getLocation(), source.getRemarks(), source.getStatus()); + } + + @Override + public Name getName() { + return name; + } + + @Override + public Interval getInterval() { + return interval; + } + + @Override + public Location getLocation() { + return location; + } + + @Override + public Remarks getRemarks() { + return remarks; + } + + @Override + public Status getStatus() { + return status; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof ReadOnlyTask // instanceof handles nulls + && this.isSameStateAs((ReadOnlyTask) other)); + } + + @Override + public int hashCode() { + // use this method for custom fields hashing instead of implementing your own + return Objects.hash(name, interval, location, remarks); + } + + @Override + public String toString() { + return getAsText(); + } + + @Override + public int compareTo(ReadOnlyTask task) { + return this.interval.compareTo(task.getInterval()); + } + +} diff --git a/src/main/java/seedu/todolist/model/task/TaskDate.java b/src/main/java/seedu/todolist/model/task/TaskDate.java new file mode 100644 index 000000000000..7a35773d892a --- /dev/null +++ b/src/main/java/seedu/todolist/model/task/TaskDate.java @@ -0,0 +1,121 @@ +package seedu.todolist.model.task; + +import java.time.DateTimeException; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; + +import seedu.todolist.commons.exceptions.IllegalValueException; +import seedu.todolist.model.parser.DateParser; + +//author A0138601M +/** + * Represents a Task's date in the to do list. + * Guarantees: immutable; is valid as declared in {@link #isValidDate(String)} + */ +public class TaskDate implements Comparable { + + public static final String MESSAGE_DATE_CONSTRAINTS = "Task date should be in the format 'dd/mm/yyyy' " + + "or 'dd monthname yyyy'."; + public static final String MESSAGE_DATE_INVALID = "Task date provided is invalid!"; + + //format: 'dd/mm/yyyy' + public static final String DATE_VALIDATION_SLASH_REGEX = "((\\p{Digit}){1,2}/(\\p{Digit}){1,2}(/(\\p{Digit}){4})?)"; + //format: 'dd monthname yyyy' + public static final String DATE_VALIDATION_NAME_REGEX = "((\\p{Digit}){1,2}\\s?(\\p{Alpha}){3,9}\\s?((\\p{Digit}){4})?)"; + public static final String DATE_VALIDATION_REGEX_FORMAT = DATE_VALIDATION_SLASH_REGEX + "|" + DATE_VALIDATION_NAME_REGEX; + + public static final String DATE_DISPLAY_FORMAT = "d MMM yyyy"; + + private LocalDate date; + + public TaskDate() { + + } + + /** + * Validates given date. + * + * @throws IllegalValueException if given date is invalid. + */ + public TaskDate(String date) throws IllegalValueException { + assert date != null; + String trimmedDate = date.trim(); + if (!isValidDate(trimmedDate)) { + throw new IllegalValueException(MESSAGE_DATE_CONSTRAINTS); + } + + try { + this.date = DateParser.parseDate(trimmedDate); + } catch (DateTimeException dateTimeException) { + throw new IllegalValueException(MESSAGE_DATE_INVALID); + } + } + + /** + * Returns true if a given string is a valid task date. + */ + private boolean isValidDate(String test) { + return test.matches(DATE_VALIDATION_REGEX_FORMAT); + } + + public LocalDate getDate() { + return this.date; + } + + /** + * Returns true if this date is earlier than given date. + */ + public boolean isBefore(TaskDate other) { + return this.date.isBefore(other.getDate()); + } + + /** + * Return the current date + */ + public static TaskDate now() { + TaskDate now = new TaskDate(); + now.date = LocalDate.now(); + return now; + } + + @Override + public String toString() { + return date.format(DateTimeFormatter.ofPattern(DATE_DISPLAY_FORMAT)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof TaskDate // instanceof handles nulls + && this.date.equals(((TaskDate) other).date)); // state check + } + + /** + * Returns true if both TaskDates are equal. + * Use this method when both TaskDates could be null + */ + public static boolean isEquals(TaskDate date, TaskDate other) { + if (date == null && other == null) { //both are null, they are equal + return true; + } + + if (date != null) { + return date.equals(other); + } else { + // if date is null, other cannot be null. + // thus, they are not equal + return false; + } + } + + @Override + public int compareTo(TaskDate date) { + if (this.equals(date)) { + return 0; + } else if (this.isBefore(date)) { + return -1; + } else { + return 1; + } + } +} diff --git a/src/main/java/seedu/todolist/model/task/TaskTime.java b/src/main/java/seedu/todolist/model/task/TaskTime.java new file mode 100644 index 000000000000..8ecdcf8b163a --- /dev/null +++ b/src/main/java/seedu/todolist/model/task/TaskTime.java @@ -0,0 +1,123 @@ +package seedu.todolist.model.task; + +import java.time.DateTimeException; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; + +import seedu.todolist.commons.exceptions.IllegalValueException; +import seedu.todolist.model.parser.TimeParser; + +//@@author A0138601M +/** + * Represents a Task's time in the to do list. + * Guarantees: immutable; is valid as declared in {@link #isValidTime(String)} + */ +public class TaskTime implements Comparable { + + public static final String MESSAGE_TIME_CONSTRAINTS = "Task time should be in 24-hr format or AM/PM format"; + public static final String MESSAGE_TIME_INVALID = "Task time provided is invalid!"; + + //format: 24-hr + public static final String TIME_VALIDATION_REGEX_2 = "(\\p{Digit}){1,2}:(\\p{Digit}){2}"; + //format: AM/PM + public static final String TIME_VALIDATION_REGEX_1 = "(\\p{Digit}){1,2}(:(\\p{Digit}){2})?\\s?[AaPp][Mm]"; + public static final String TIME_VALIDATION_REGEX_FORMAT = TIME_VALIDATION_REGEX_1 + "|" + TIME_VALIDATION_REGEX_2; + + public static final String TIME_DISPLAY_FORMAT = "h:mma"; + + private LocalTime time; + + public TaskTime() { + + } + + /** + * Validates given time. + * + * @throws IllegalValueException if given date is invalid. + */ + public TaskTime(String time) throws IllegalValueException { + assert time != null; + String trimmedTime = time.trim(); + if (!isValidTime(trimmedTime)) { + throw new IllegalValueException(MESSAGE_TIME_CONSTRAINTS); + } + + try { + this.time = TimeParser.parseTime(trimmedTime); + } catch (DateTimeException dateTimeException) { + throw new IllegalValueException(MESSAGE_TIME_INVALID); + } + } + + /** + * Returns true if a given string is a valid task time. + */ + public static boolean isValidTime(String test) { + return test.matches(TIME_VALIDATION_REGEX_FORMAT); + } + + /** + * Returns true if this time is earlier than given time. + */ + public boolean isBefore(TaskTime other) { + return this.time.isBefore(other.getTime()); + } + + public LocalTime getTime() { + return this.time; + } + + /** + * Return the current time + */ + public static TaskTime now() { + TaskTime now = new TaskTime(); + now.time = LocalTime.now(); + return now; + } + + @Override + public String toString() { + return time.format(DateTimeFormatter.ofPattern(TIME_DISPLAY_FORMAT)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof TaskTime // instanceof handles nulls + && this.time.equals(((TaskTime) other).time)); // state check + } + + /** + * Returns true if both TaskTimes are equal. + * Use this method when both TaskTimes could be null + */ + public static boolean isEquals(TaskTime time, TaskTime other) { + if (time == null && other == null) { + //both are null, they are equal + return true; + } + + if (time != null) { + return time.equals(other); + } else { + // if date is null, other cannot be null. + // thus, they are not equal + return false; + } + } + + @Override + public int compareTo(TaskTime time) { + if (time == null) return -1; + + if (this.equals(time)) { + return 0; + } else if (this.isBefore(time)) { + return -1; + } else { + return 1; + } + } +} diff --git a/src/main/java/seedu/todolist/model/task/UniqueTaskList.java b/src/main/java/seedu/todolist/model/task/UniqueTaskList.java new file mode 100644 index 000000000000..69e4ffcc1e1f --- /dev/null +++ b/src/main/java/seedu/todolist/model/task/UniqueTaskList.java @@ -0,0 +1,140 @@ +package seedu.todolist.model.task; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.collections.transformation.FilteredList; +import seedu.todolist.commons.exceptions.DuplicateDataException; +import seedu.todolist.commons.util.CollectionUtil; + +import java.util.*; + +/** + * A list of tasks that enforces uniqueness between its elements and does not allow nulls. + * + * Supports a minimal set of list operations. + * + * @see Task#equals(Object) + * @see CollectionUtil#elementsAreUnique(Collection) + */ +public class UniqueTaskList implements Iterable { + + private final ObservableList internalList = FXCollections.observableArrayList(); + + /** + * Signals that an operation would have violated the 'no duplicates' property of the list. + */ + public static class DuplicateTaskException extends DuplicateDataException { + protected DuplicateTaskException() { + super("Operation would result in duplicate tasks"); + } + } + + /** + * Signals that an operation targeting a specified task in the list would fail because + * there is no such matching task in the list. + */ + public static class TaskNotFoundException extends Exception {} + + /** + * Returns true if the list contains an equivalent task as the given argument. + */ + public boolean contains(ReadOnlyTask toCheck) { + assert toCheck != null; + return internalList.contains(toCheck); + } + + /** + * Adds a task to the list. + * + * @throws DuplicateTaskException if the task to add is a duplicate of an existing task in the list. + */ + public void add(Task toAdd) throws DuplicateTaskException { + assert toAdd != null; + if (contains(toAdd)) { + throw new DuplicateTaskException(); + } + internalList.add(toAdd); + Collections.sort(internalList); + } + + /** + * Removes the equivalent task(s) from the list. + * + * @throws TaskNotFoundException if no such task could be found in the list. + */ + public boolean remove(ReadOnlyTask... toRemove) throws TaskNotFoundException { + assert toRemove != null; + boolean taskFoundAndDeleted = false; + for (ReadOnlyTask task : toRemove) { + taskFoundAndDeleted = internalList.remove(task); + if (!taskFoundAndDeleted) { + throw new TaskNotFoundException(); + } + } + return taskFoundAndDeleted; + } + + //@@author A0138601M + /** + * Marks the equivalent task(s) in the list. + * + * @throws TaskNotFoundException if no such task could be found in the list. + */ + public boolean mark(ReadOnlyTask... toMark) throws TaskNotFoundException { + assert toMark != null; + boolean taskFound = false; + for (ReadOnlyTask task : toMark) { + taskFound = (internalList.indexOf(task) != -1); + if (!taskFound) { + throw new TaskNotFoundException(); + } + Task taskMarked = new Task(task.getName(), task.getInterval(), task.getLocation(), task.getRemarks(), new Status(Status.Type.Complete)); + internalList.set(internalList.indexOf(task), taskMarked); + } + Collections.sort(internalList); + return taskFound; + } + + //@@author A0146682X + /** + * Edits the equivalent task in the list. + * + * @throws TaskNotFoundException if no such task could be found in the list. + */ + public boolean edit(ReadOnlyTask toEdit, Task replacement) throws TaskNotFoundException { + assert toEdit != null; + final boolean taskFound = (internalList.indexOf(toEdit) != -1); + internalList.set(internalList.indexOf(toEdit), replacement); + if (!taskFound) { + throw new TaskNotFoundException(); + } + return taskFound; + } + + //@@author + public ObservableList getInternalList() { + return internalList; + } + + public FilteredList getFilteredTaskList(String filter) { + return internalList.filtered(p -> p.getStatus().toString().equals(filter)); + } + + @Override + public Iterator iterator() { + return internalList.iterator(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof UniqueTaskList // instanceof handles nulls + && this.internalList.equals( + ((UniqueTaskList) other).internalList)); + } + + @Override + public int hashCode() { + return internalList.hashCode(); + } +} diff --git a/src/main/java/seedu/address/storage/JsonUserPrefStorage.java b/src/main/java/seedu/todolist/storage/JsonUserPrefStorage.java similarity index 89% rename from src/main/java/seedu/address/storage/JsonUserPrefStorage.java rename to src/main/java/seedu/todolist/storage/JsonUserPrefStorage.java index 3a145ce35f15..b3c62bbc0afd 100644 --- a/src/main/java/seedu/address/storage/JsonUserPrefStorage.java +++ b/src/main/java/seedu/todolist/storage/JsonUserPrefStorage.java @@ -1,9 +1,9 @@ -package seedu.address.storage; +package seedu.todolist.storage; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.commons.util.FileUtil; -import seedu.address.model.UserPrefs; +import seedu.todolist.commons.core.LogsCenter; +import seedu.todolist.commons.exceptions.DataConversionException; +import seedu.todolist.commons.util.FileUtil; +import seedu.todolist.model.UserPrefs; import java.io.File; import java.io.IOException; diff --git a/src/main/java/seedu/todolist/storage/Storage.java b/src/main/java/seedu/todolist/storage/Storage.java new file mode 100644 index 000000000000..9c011b3751ea --- /dev/null +++ b/src/main/java/seedu/todolist/storage/Storage.java @@ -0,0 +1,42 @@ +package seedu.todolist.storage; + +import seedu.todolist.commons.events.model.ToDoListChangedEvent; +import seedu.todolist.commons.events.storage.DataSavingExceptionEvent; +import seedu.todolist.commons.exceptions.DataConversionException; +import seedu.todolist.model.ReadOnlyToDoList; +import seedu.todolist.model.UserPrefs; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Optional; + +/** + * API of the Storage component + */ +public interface Storage extends ToDoListStorage, UserPrefsStorage { + + @Override + Optional readUserPrefs() throws DataConversionException, IOException; + + @Override + void saveUserPrefs(UserPrefs userPrefs) throws IOException; + + @Override + String getToDoListFilePath(); + + @Override + Optional readToDoList() throws DataConversionException, FileNotFoundException; + + @Override + void saveToDoList(ReadOnlyToDoList ToDoList) 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 handleToDoListChangedEvent(ToDoListChangedEvent abce); + + + void setFilePath(String filepath); +} diff --git a/src/main/java/seedu/todolist/storage/StorageManager.java b/src/main/java/seedu/todolist/storage/StorageManager.java new file mode 100644 index 000000000000..40edc2196185 --- /dev/null +++ b/src/main/java/seedu/todolist/storage/StorageManager.java @@ -0,0 +1,85 @@ +package seedu.todolist.storage; + +import com.google.common.eventbus.Subscribe; + +import seedu.todolist.commons.core.ComponentManager; +import seedu.todolist.commons.core.LogsCenter; +import seedu.todolist.commons.events.model.ToDoListChangedEvent; +import seedu.todolist.commons.events.storage.DataSavingExceptionEvent; +import seedu.todolist.commons.exceptions.DataConversionException; +import seedu.todolist.model.ReadOnlyToDoList; +import seedu.todolist.model.UserPrefs; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Optional; +import java.util.logging.Logger; + +/** + * Manages storage of ToDoList data in local storage. + */ +public class StorageManager extends ComponentManager implements Storage { + + private static final Logger logger = LogsCenter.getLogger(StorageManager.class); + private XmlToDoListStorage ToDoListStorage; + private JsonUserPrefStorage userPrefStorage; + + + public StorageManager(String ToDoListFilePath, String userPrefsFilePath) { + super(); + this.ToDoListStorage = new XmlToDoListStorage(ToDoListFilePath); + this.userPrefStorage = new JsonUserPrefStorage(userPrefsFilePath); + } + + // ================ UserPrefs methods ============================== + + @Override + public Optional readUserPrefs() throws DataConversionException, IOException { + return userPrefStorage.readUserPrefs(); + } + + @Override + public void saveUserPrefs(UserPrefs userPrefs) throws IOException { + userPrefStorage.saveUserPrefs(userPrefs); + } + + + // ================ ToDoList methods ============================== + + //@@author A0158963M + @Override + public void setFilePath(String filepath){ + String todolistFilePath = filepath + "/todolist.xml"; + ToDoListStorage = new XmlToDoListStorage(todolistFilePath); + } + + @Override + public String getToDoListFilePath() { + return ToDoListStorage.getToDoListFilePath(); + } + + @Override + public Optional readToDoList() throws DataConversionException, FileNotFoundException { + logger.fine("Attempting to read data from file: " + ToDoListStorage.getToDoListFilePath()); + + return ToDoListStorage.readToDoList(ToDoListStorage.getToDoListFilePath()); + } + + @Override + public void saveToDoList(ReadOnlyToDoList ToDoList) throws IOException { + ToDoListStorage.saveToDoList(ToDoList, ToDoListStorage.getToDoListFilePath()); + } + + + @Override + @Subscribe + public void handleToDoListChangedEvent(ToDoListChangedEvent event) { + logger.info(LogsCenter.getEventHandlingLogMessage(event, "Local data changed, saving to file")); + try { + saveToDoList(event.data); + } catch (IOException e) { + raise(new DataSavingExceptionEvent(e)); + } + } + +} diff --git a/src/main/java/seedu/todolist/storage/ToDoListStorage.java b/src/main/java/seedu/todolist/storage/ToDoListStorage.java new file mode 100644 index 000000000000..5bd078a406c8 --- /dev/null +++ b/src/main/java/seedu/todolist/storage/ToDoListStorage.java @@ -0,0 +1,34 @@ +package seedu.todolist.storage; + +import seedu.todolist.commons.exceptions.DataConversionException; +import seedu.todolist.model.ReadOnlyToDoList; + +import java.io.IOException; +import java.util.Optional; + +/** + * Represents a storage for {@link seedu.todolist.model.ToDoList}. + */ +public interface ToDoListStorage { + + /** + * Returns the file path of the data file. + */ + String getToDoListFilePath(); + + /** + * Returns ToDoList data as a {@link ReadOnlyToDoList}. + * 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 readToDoList() throws DataConversionException, IOException; + + /** + * Saves the given {@link ReadOnlyToDoList} to the storage. + * @param ToDoList cannot be null. + * @throws IOException if there was any problem writing to the file. + */ + void saveToDoList(ReadOnlyToDoList ToDoList) throws IOException; + +} diff --git a/src/main/java/seedu/address/storage/UserPrefsStorage.java b/src/main/java/seedu/todolist/storage/UserPrefsStorage.java similarity index 71% rename from src/main/java/seedu/address/storage/UserPrefsStorage.java rename to src/main/java/seedu/todolist/storage/UserPrefsStorage.java index ad2dc935187c..29e189fdd3e9 100644 --- a/src/main/java/seedu/address/storage/UserPrefsStorage.java +++ b/src/main/java/seedu/todolist/storage/UserPrefsStorage.java @@ -1,13 +1,13 @@ -package seedu.address.storage; +package seedu.todolist.storage; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.UserPrefs; +import seedu.todolist.commons.exceptions.DataConversionException; +import seedu.todolist.model.UserPrefs; import java.io.IOException; import java.util.Optional; /** - * Represents a storage for {@link seedu.address.model.UserPrefs}. + * Represents a storage for {@link seedu.todolist.model.UserPrefs}. */ public interface UserPrefsStorage { @@ -20,7 +20,7 @@ public interface UserPrefsStorage { Optional readUserPrefs() throws DataConversionException, IOException; /** - * Saves the given {@link seedu.address.model.UserPrefs} to the storage. + * Saves the given {@link seedu.todolist.model.UserPrefs} to the storage. * @param userPrefs cannot be null. * @throws IOException if there was any problem writing to the file. */ diff --git a/src/main/java/seedu/todolist/storage/XmlAdaptedTask.java b/src/main/java/seedu/todolist/storage/XmlAdaptedTask.java new file mode 100644 index 000000000000..641a7374b95a --- /dev/null +++ b/src/main/java/seedu/todolist/storage/XmlAdaptedTask.java @@ -0,0 +1,86 @@ +package seedu.todolist.storage; + +import seedu.todolist.commons.exceptions.IllegalValueException; +import seedu.todolist.model.task.*; + +import javax.xml.bind.annotation.XmlElement; + +/** + * JAXB-friendly version of the Task. + */ +public class XmlAdaptedTask { + + @XmlElement(required = true) + private String name; + + @XmlElement + private String startDate; + + @XmlElement + private String startTime; + + @XmlElement + private String endDate; + + @XmlElement + private String endTime; + + @XmlElement + private String location; + + @XmlElement + private String remarks; + + @XmlElement + private String status; + + /** + * 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().fullName; + startDate = setDate(source.getInterval().getStartDate()); + startTime = setTime(source.getInterval().getStartTime()); + endDate = setDate(source.getInterval().getEndDate()); + endTime = setTime(source.getInterval().getEndTime()); + location = source.getLocation().toString(); + remarks = source.getRemarks().toString(); + status = source.getStatus().toString(); + } + + private String setDate(TaskDate date) { + if (date != null) { + return date.toString(); + } + return null; + } + + private String setTime(TaskTime time) { + if (time != null) { + return time.toString(); + } + return null; + } + + /** + * 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 Name name = new Name(this.name); + final Interval interval = new Interval(this.startDate, this.startTime, this.endDate, this.endTime); + final Location location = new Location(this.location); + final Remarks remarks = new Remarks(this.remarks); + final Status status = new Status(this.status); + return new Task(name, interval, location, remarks, status); + } +} diff --git a/src/main/java/seedu/address/storage/XmlFileStorage.java b/src/main/java/seedu/todolist/storage/XmlFileStorage.java similarity index 62% rename from src/main/java/seedu/address/storage/XmlFileStorage.java rename to src/main/java/seedu/todolist/storage/XmlFileStorage.java index 27a5210cadaf..425e45157af8 100644 --- a/src/main/java/seedu/address/storage/XmlFileStorage.java +++ b/src/main/java/seedu/todolist/storage/XmlFileStorage.java @@ -1,23 +1,23 @@ -package seedu.address.storage; +package seedu.todolist.storage; -import seedu.address.commons.util.XmlUtil; -import seedu.address.commons.exceptions.DataConversionException; +import seedu.todolist.commons.exceptions.DataConversionException; +import seedu.todolist.commons.util.XmlUtil; import javax.xml.bind.JAXBException; import java.io.File; import java.io.FileNotFoundException; /** - * Stores addressbook data in an XML file + * Stores todolist data in an XML file */ public class XmlFileStorage { /** - * Saves the given addressbook data to the specified file. + * Saves the given todolist data to the specified file. */ - public static void saveDataToFile(File file, XmlSerializableAddressBook addressBook) + public static void saveDataToFile(File file, XmlSerializableToDoList ToDoList) throws FileNotFoundException { try { - XmlUtil.saveDataToFile(file, addressBook); + XmlUtil.saveDataToFile(file, ToDoList); } catch (JAXBException e) { assert false : "Unexpected exception " + e.getMessage(); } @@ -26,10 +26,10 @@ public static void saveDataToFile(File file, XmlSerializableAddressBook addressB /** * Returns address book in the file or an empty address book */ - public static XmlSerializableAddressBook loadDataFromSaveFile(File file) throws DataConversionException, + public static XmlSerializableToDoList loadDataFromSaveFile(File file) throws DataConversionException, FileNotFoundException { try { - return XmlUtil.getDataFromFile(file, XmlSerializableAddressBook.class); + return XmlUtil.getDataFromFile(file, XmlSerializableToDoList.class); } catch (JAXBException e) { throw new DataConversionException(e); } diff --git a/src/main/java/seedu/todolist/storage/XmlSerializableToDoList.java b/src/main/java/seedu/todolist/storage/XmlSerializableToDoList.java new file mode 100644 index 000000000000..bf7f3a76de80 --- /dev/null +++ b/src/main/java/seedu/todolist/storage/XmlSerializableToDoList.java @@ -0,0 +1,63 @@ +package seedu.todolist.storage; + +import seedu.todolist.commons.exceptions.IllegalValueException; +import seedu.todolist.model.ReadOnlyToDoList; +import seedu.todolist.model.task.ReadOnlyTask; +import seedu.todolist.model.task.UniqueTaskList; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * An Immutable ToDoList that is serializable to XML format + */ +@XmlRootElement(name = "todolist") +public class XmlSerializableToDoList implements ReadOnlyToDoList { + + @XmlElement + private List tasks; + + /** + * Empty constructor required for marshalling + */ + public XmlSerializableToDoList() { + tasks = new ArrayList<>(); + } + + /** + * Conversion + */ + public XmlSerializableToDoList(ReadOnlyToDoList src) { + tasks = new ArrayList<>(); + tasks.addAll(src.getTaskList().stream().map(XmlAdaptedTask::new).collect(Collectors.toList())); + } + + @Override + public UniqueTaskList getUniqueTaskList() { + UniqueTaskList lists = new UniqueTaskList(); + for (XmlAdaptedTask p : tasks) { + try { + lists.add(p.toModelType()); + } catch (IllegalValueException e) { + + } + } + return lists; + } + + @Override + public List getTaskList() { + return tasks.stream().map(p -> { + try { + return p.toModelType(); + } catch (IllegalValueException e) { + e.printStackTrace(); + return null; + } + }).collect(Collectors.toCollection(ArrayList::new)); + } + +} diff --git a/src/main/java/seedu/todolist/storage/XmlToDoListStorage.java b/src/main/java/seedu/todolist/storage/XmlToDoListStorage.java new file mode 100644 index 000000000000..225cb6c6485d --- /dev/null +++ b/src/main/java/seedu/todolist/storage/XmlToDoListStorage.java @@ -0,0 +1,73 @@ +package seedu.todolist.storage; + +import seedu.todolist.commons.core.LogsCenter; +import seedu.todolist.commons.exceptions.DataConversionException; +import seedu.todolist.commons.util.FileUtil; +import seedu.todolist.model.ReadOnlyToDoList; + +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 ToDoList data stored as an xml file on the hard disk. + */ +public class XmlToDoListStorage implements ToDoListStorage { + + private static final Logger logger = LogsCenter.getLogger(XmlToDoListStorage.class); + + private String filePath; + + public XmlToDoListStorage(String filePath){ + this.filePath = filePath; + } + + public String getToDoListFilePath(){ + return filePath; + } + + /** + * Similar to {@link #readToDoList()} + * @param filePath location of the data. Cannot be null + * @throws DataConversionException if the file is not in the correct format. + */ + public Optional readToDoList(String filePath) throws DataConversionException, FileNotFoundException { + assert filePath != null; + + File ToDoListFile = new File(filePath); + + if (!ToDoListFile.exists()) { + logger.info("ToDoList file " + ToDoListFile + " not found"); + return Optional.empty(); + } + + ReadOnlyToDoList ToDoListOptional = XmlFileStorage.loadDataFromSaveFile(new File(filePath)); + + return Optional.of(ToDoListOptional); + } + + /** + * Similar to {@link #saveToDoList(ReadOnlyToDoList)} + * @param filePath location of the data. Cannot be null + */ + public void saveToDoList(ReadOnlyToDoList ToDoList, String filePath) throws IOException { + assert ToDoList != null; + assert filePath != null; + + File file = new File(filePath); + FileUtil.createIfMissing(file); + XmlFileStorage.saveDataToFile(file, new XmlSerializableToDoList(ToDoList)); + } + + @Override + public Optional readToDoList() throws DataConversionException, IOException { + return readToDoList(filePath); + } + + @Override + public void saveToDoList(ReadOnlyToDoList ToDoList) throws IOException { + saveToDoList(ToDoList, filePath); + } +} diff --git a/src/main/java/seedu/address/ui/CommandBox.java b/src/main/java/seedu/todolist/ui/CommandBox.java similarity index 71% rename from src/main/java/seedu/address/ui/CommandBox.java rename to src/main/java/seedu/todolist/ui/CommandBox.java index 2e1409a3016c..5936a32b4498 100644 --- a/src/main/java/seedu/address/ui/CommandBox.java +++ b/src/main/java/seedu/todolist/ui/CommandBox.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.todolist.ui; import com.google.common.eventbus.Subscribe; import javafx.fxml.FXML; @@ -7,12 +7,14 @@ import javafx.scene.control.TextField; import javafx.scene.layout.AnchorPane; import javafx.stage.Stage; -import seedu.address.commons.events.ui.IncorrectCommandAttemptedEvent; -import seedu.address.logic.Logic; -import seedu.address.logic.commands.*; -import seedu.address.commons.util.FxViewUtil; -import seedu.address.commons.core.LogsCenter; - +import seedu.todolist.commons.core.LogsCenter; +import seedu.todolist.commons.events.ui.IncorrectCommandAttemptedEvent; +import seedu.todolist.commons.util.FxViewUtil; +import seedu.todolist.logic.Logic; +import seedu.todolist.logic.commands.*; +import seedu.todolist.storage.Storage; + +import java.util.Date; import java.util.logging.Logger; public class CommandBox extends UiPart { @@ -22,25 +24,29 @@ public class CommandBox extends UiPart { private AnchorPane placeHolderPane; private AnchorPane commandPane; private ResultDisplay resultDisplay; - String previousCommandTest; + private String previousCommandTest; private Logic logic; + private Storage storage; + private StatusBarFooter statusBarFooter; @FXML private TextField commandTextField; private CommandResult mostRecentResult; public static CommandBox load(Stage primaryStage, AnchorPane commandBoxPlaceholder, - ResultDisplay resultDisplay, Logic logic) { + ResultDisplay resultDisplay, Logic logic, Storage storage, StatusBarFooter statusBarFooter) { CommandBox commandBox = UiPartLoader.loadUiPart(primaryStage, commandBoxPlaceholder, new CommandBox()); - commandBox.configure(resultDisplay, logic); + commandBox.configure(resultDisplay, logic, storage, statusBarFooter); commandBox.addToPlaceholder(); return commandBox; } - public void configure(ResultDisplay resultDisplay, Logic logic) { + public void configure(ResultDisplay resultDisplay, Logic logic, Storage storage, StatusBarFooter statusBarFooter) { this.resultDisplay = resultDisplay; this.logic = logic; + this.storage = storage; + this.statusBarFooter = statusBarFooter; registerAsAnEventHandler(this); } @@ -77,6 +83,15 @@ private void handleCommandInputChanged() { */ setStyleToIndicateCorrectCommand(); mostRecentResult = logic.execute(previousCommandTest); + + //@@author A0158963M + String[] CommandTestArray = previousCommandTest.split(" "); + String storageCommand = "setstorage"; + if(CommandTestArray[0].equals(storageCommand)){ + statusBarFooter.setSaveLocation(storage.getToDoListFilePath()); + String lastUpdated = (new Date()).toString(); + statusBarFooter.setSyncStatus("Last Updated: " + lastUpdated); + } resultDisplay.postMessage(mostRecentResult.feedbackToUser); logger.info("Result: " + mostRecentResult.feedbackToUser); } @@ -86,6 +101,7 @@ private void handleCommandInputChanged() { * Sets the command box style to indicate a correct command. */ private void setStyleToIndicateCorrectCommand() { + resultDisplay.setStyleToIndicateCorrectCommand(); commandTextField.getStyleClass().remove("error"); commandTextField.setText(""); } @@ -108,6 +124,7 @@ private void restoreCommandText() { * Sets the command box style to indicate an error */ private void setStyleToIndicateIncorrectCommand() { + resultDisplay.setStyleToIndicateIncorrectCommand(); commandTextField.getStyleClass().add("error"); } diff --git a/src/main/java/seedu/address/ui/HelpWindow.java b/src/main/java/seedu/todolist/ui/HelpWindow.java similarity index 90% rename from src/main/java/seedu/address/ui/HelpWindow.java rename to src/main/java/seedu/todolist/ui/HelpWindow.java index 45b765ab6a0c..b0689d27bf09 100644 --- a/src/main/java/seedu/address/ui/HelpWindow.java +++ b/src/main/java/seedu/todolist/ui/HelpWindow.java @@ -1,12 +1,12 @@ -package seedu.address.ui; +package seedu.todolist.ui; import javafx.scene.Node; import javafx.scene.Scene; import javafx.scene.layout.AnchorPane; import javafx.scene.web.WebView; import javafx.stage.Stage; -import seedu.address.commons.util.FxViewUtil; -import seedu.address.commons.core.LogsCenter; +import seedu.todolist.commons.core.LogsCenter; +import seedu.todolist.commons.util.FxViewUtil; import java.util.logging.Logger; @@ -20,7 +20,7 @@ public class HelpWindow extends UiPart { private static final String FXML = "HelpWindow.fxml"; private static final String TITLE = "Help"; private static final String USERGUIDE_URL = - "https://github.com/se-edu/addressbook-level4/blob/master/docs/UserGuide.md"; + "https://github.com/CS2103AUG2016-T15-C3/main/blob/master/docs/UserGuide.md"; private AnchorPane mainPane; diff --git a/src/main/java/seedu/address/ui/MainWindow.java b/src/main/java/seedu/todolist/ui/MainWindow.java similarity index 55% rename from src/main/java/seedu/address/ui/MainWindow.java rename to src/main/java/seedu/todolist/ui/MainWindow.java index 2c76aced3b04..dcf8ee4c7a26 100644 --- a/src/main/java/seedu/address/ui/MainWindow.java +++ b/src/main/java/seedu/todolist/ui/MainWindow.java @@ -1,19 +1,22 @@ -package seedu.address.ui; +package seedu.todolist.ui; import javafx.fxml.FXML; import javafx.scene.Node; import javafx.scene.Scene; import javafx.scene.control.MenuItem; +import javafx.scene.control.TabPane; import javafx.scene.input.KeyCombination; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.VBox; import javafx.stage.Stage; -import seedu.address.commons.core.Config; -import seedu.address.commons.core.GuiSettings; -import seedu.address.commons.events.ui.ExitAppRequestEvent; -import seedu.address.logic.Logic; -import seedu.address.model.UserPrefs; -import seedu.address.model.person.ReadOnlyPerson; +import seedu.todolist.commons.core.Config; +import seedu.todolist.commons.core.GuiSettings; +import seedu.todolist.commons.events.ui.ExitAppRequestEvent; +import seedu.todolist.logic.Logic; +import seedu.todolist.model.UserPrefs; +import seedu.todolist.model.task.ReadOnlyTask; +import seedu.todolist.model.task.Status; +import seedu.todolist.storage.Storage; /** * The Main Window. Provides the basic application layout containing @@ -21,27 +24,32 @@ */ public class MainWindow extends UiPart { - private static final String ICON = "/images/address_book_32.png"; + private static final String ICON = "/images/taskit_logo.png"; private static final String FXML = "MainWindow.fxml"; public static final int MIN_HEIGHT = 600; public static final int MIN_WIDTH = 450; + public static final String TAB_TASK_COMPLETE = "Completed"; + public static final String TAB_TASK_INCOMPLETE = "Incomplete"; + public static final String TAB_TASK_OVERDUE = "Overdue"; private Logic logic; // Independent Ui parts residing in this Ui container - private BrowserPanel browserPanel; - private PersonListPanel personListPanel; + private TaskListPanel incompleteListPanel; + private TaskListPanel completeTaskListPanel; + private TaskListPanel overdueTaskListPanel; private ResultDisplay resultDisplay; private StatusBarFooter statusBarFooter; private CommandBox commandBox; private Config config; private UserPrefs userPrefs; + private Storage storage; // Handles to elements of this Ui container private VBox rootLayout; private Scene scene; - private String addressBookName; + private String ToDoListName; @FXML private AnchorPane browserPlaceholder; @@ -53,18 +61,22 @@ public class MainWindow extends UiPart { private MenuItem helpMenuItem; @FXML - private AnchorPane personListPanelPlaceholder; + private AnchorPane taskListPanelPlaceholder; + + @FXML + private AnchorPane completeTaskListPanelPlaceholder; + + @FXML + private AnchorPane overdueTaskListPanelPlaceholder; @FXML private AnchorPane resultDisplayPlaceholder; @FXML private AnchorPane statusbarPlaceholder; - - - public MainWindow() { - super(); - } + + @FXML + private TabPane allTasksTabPane; @Override public void setNode(Node node) { @@ -76,21 +88,22 @@ public String getFxmlPath() { return FXML; } - public static MainWindow load(Stage primaryStage, Config config, UserPrefs prefs, Logic logic) { + public static MainWindow load(Stage primaryStage, Config config, UserPrefs prefs, Logic logic, Storage storage) { MainWindow mainWindow = UiPartLoader.loadUiPart(primaryStage, new MainWindow()); - mainWindow.configure(config.getAppTitle(), config.getAddressBookName(), config, prefs, logic); + mainWindow.configure(config.getAppTitle(), config.getToDoListName(), config, prefs, logic, storage); return mainWindow; } - private void configure(String appTitle, String addressBookName, Config config, UserPrefs prefs, - Logic logic) { + private void configure(String appTitle, String ToDoListName, Config config, UserPrefs prefs, + Logic logic, Storage storage) { //Set dependencies this.logic = logic; - this.addressBookName = addressBookName; + this.ToDoListName = ToDoListName; this.config = config; this.userPrefs = prefs; + this.storage = storage; //Configure the UI setTitle(appTitle); @@ -107,12 +120,16 @@ private void setAccelerators() { helpMenuItem.setAccelerator(KeyCombination.valueOf("F1")); } - void fillInnerParts() { - browserPanel = BrowserPanel.load(browserPlaceholder); - personListPanel = PersonListPanel.load(primaryStage, getPersonListPlaceholder(), logic.getFilteredPersonList()); + protected void fillInnerParts() { + incompleteListPanel = TaskListPanel.load(primaryStage, getTaskListPlaceholder(), + logic.getFilteredIncompleteTaskList(), Status.Type.Incomplete); + completeTaskListPanel = TaskListPanel.load(primaryStage, getCompleteTaskListPlaceholder(), + logic.getFilteredCompleteTaskList(), Status.Type.Complete); + overdueTaskListPanel = TaskListPanel.load(primaryStage, getOverdueTaskListPlaceholder(), + logic.getFilteredOverdueTaskList(), Status.Type.Overdue); resultDisplay = ResultDisplay.load(primaryStage, getResultDisplayPlaceholder()); - statusBarFooter = StatusBarFooter.load(primaryStage, getStatusbarPlaceholder(), config.getAddressBookFilePath()); - commandBox = CommandBox.load(primaryStage, getCommandBoxPlaceholder(), resultDisplay, logic); + statusBarFooter = StatusBarFooter.load(primaryStage, getStatusbarPlaceholder(), config.getToDoListFilePath()); + commandBox = CommandBox.load(primaryStage, getCommandBoxPlaceholder(), resultDisplay, logic, storage, statusBarFooter); } private AnchorPane getCommandBoxPlaceholder() { @@ -127,8 +144,16 @@ private AnchorPane getResultDisplayPlaceholder() { return resultDisplayPlaceholder; } - public AnchorPane getPersonListPlaceholder() { - return personListPanelPlaceholder; + public AnchorPane getTaskListPlaceholder() { + return taskListPanelPlaceholder; + } + + public AnchorPane getCompleteTaskListPlaceholder() { + return completeTaskListPanelPlaceholder; + } + + public AnchorPane getOverdueTaskListPlaceholder() { + return overdueTaskListPanelPlaceholder; } public void hide() { @@ -181,16 +206,39 @@ public void show() { private void handleExit() { raise(new ExitAppRequestEvent()); } + + @FXML + public void OnTabSelectionChanged() { + try { + logic.setCurrentTab(allTasksTabPane.getSelectionModel().getSelectedItem().getText()); + } catch (NullPointerException ex) { + //Default tab is incomplete tab + } + } - public PersonListPanel getPersonListPanel() { - return this.personListPanel; + public TaskListPanel getTaskListPanel(String currentTab) { + + switch (currentTab) { + + case TAB_TASK_COMPLETE : + return this.completeTaskListPanel; + + case TAB_TASK_INCOMPLETE : + return this.incompleteListPanel; + + case TAB_TASK_OVERDUE : + return this.overdueTaskListPanel; + + default : + assert false : "Tab panel " + currentTab + " does not exist"; + return null; + + } } - public void loadPersonPage(ReadOnlyPerson person) { - browserPanel.loadPersonPage(person); + public void loadTaskPage(ReadOnlyTask task) { } public void releaseResources() { - browserPanel.freeResources(); } } diff --git a/src/main/java/seedu/address/ui/ResultDisplay.java b/src/main/java/seedu/todolist/ui/ResultDisplay.java similarity index 76% rename from src/main/java/seedu/address/ui/ResultDisplay.java rename to src/main/java/seedu/todolist/ui/ResultDisplay.java index 37284ee6c696..61a578a00328 100644 --- a/src/main/java/seedu/address/ui/ResultDisplay.java +++ b/src/main/java/seedu/todolist/ui/ResultDisplay.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.todolist.ui; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; @@ -6,7 +6,7 @@ import javafx.scene.control.TextArea; import javafx.scene.layout.AnchorPane; import javafx.stage.Stage; -import seedu.address.commons.util.FxViewUtil; +import seedu.todolist.commons.util.FxViewUtil; /** * A ui for the status bar that is displayed at the header of the application. @@ -61,5 +61,21 @@ public String getFxmlPath() { public void postMessage(String message) { displayed.setValue(message); } + + /** + * Sets the result display style to indicate a correct command. + */ + public void setStyleToIndicateCorrectCommand() { + resultDisplayArea.getStyleClass().remove("error"); + resultDisplayArea.lookup(".content").getStyleClass().remove("error"); + } + + /** + * Sets the result display style to indicate an incorrect command. + */ + public void setStyleToIndicateIncorrectCommand() { + resultDisplayArea.getStyleClass().add("error"); + resultDisplayArea.lookup(".content").getStyleClass().add("error"); + } } diff --git a/src/main/java/seedu/address/ui/StatusBarFooter.java b/src/main/java/seedu/todolist/ui/StatusBarFooter.java similarity index 88% rename from src/main/java/seedu/address/ui/StatusBarFooter.java rename to src/main/java/seedu/todolist/ui/StatusBarFooter.java index f74f66be6fc9..6d31f842ca6f 100644 --- a/src/main/java/seedu/address/ui/StatusBarFooter.java +++ b/src/main/java/seedu/todolist/ui/StatusBarFooter.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.todolist.ui; import com.google.common.eventbus.Subscribe; import javafx.fxml.FXML; @@ -7,9 +7,10 @@ import javafx.scene.layout.GridPane; import javafx.stage.Stage; import org.controlsfx.control.StatusBar; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.events.model.AddressBookChangedEvent; -import seedu.address.commons.util.FxViewUtil; + +import seedu.todolist.commons.core.LogsCenter; +import seedu.todolist.commons.events.model.ToDoListChangedEvent; +import seedu.todolist.commons.util.FxViewUtil; import java.util.Date; import java.util.logging.Logger; @@ -54,7 +55,7 @@ private void addMainPane() { placeHolder.getChildren().add(mainPane); } - private void setSaveLocation(String location) { + protected void setSaveLocation(String location) { this.saveLocationStatus.setText(location); } @@ -64,7 +65,7 @@ private void addSaveLocation() { saveLocStatusBarPane.getChildren().add(saveLocationStatus); } - private void setSyncStatus(String status) { + protected void setSyncStatus(String status) { this.syncStatus.setText(status); } @@ -90,7 +91,7 @@ public String getFxmlPath() { } @Subscribe - public void handleAddressBookChangedEvent(AddressBookChangedEvent abce) { + public void handleToDoListChangedEvent(ToDoListChangedEvent abce) { String lastUpdated = (new Date()).toString(); logger.info(LogsCenter.getEventHandlingLogMessage(abce, "Setting last updated status to " + lastUpdated)); setSyncStatus("Last Updated: " + lastUpdated); diff --git a/src/main/java/seedu/todolist/ui/TaskCard.java b/src/main/java/seedu/todolist/ui/TaskCard.java new file mode 100644 index 000000000000..c4df706d5855 --- /dev/null +++ b/src/main/java/seedu/todolist/ui/TaskCard.java @@ -0,0 +1,112 @@ +package seedu.todolist.ui; + +import com.google.common.base.Strings; + +import javafx.fxml.FXML; +import javafx.scene.Node; +import javafx.scene.control.Label; +import javafx.scene.image.ImageView; +import javafx.scene.layout.HBox; +import seedu.todolist.commons.util.AppUtil; +import seedu.todolist.model.task.ReadOnlyTask; + +public class TaskCard extends UiPart{ + + private static final String FXML = "TaskListCard.fxml"; + + private static final String ICON_ALARM_CLOCK = "/images/alarm_clock.png"; + private static final String ICON_HOURGLASS = "/images/hourglass.png"; + + @FXML + private HBox cardPane; + @FXML + private Label name; + @FXML + private Label id; + @FXML + private ImageView intervalIcon; + @FXML + private Label startDate; + @FXML + private Label endDate; + @FXML + private ImageView locationIcon; + @FXML + private Label locationParam; //location is a keyword + @FXML + private ImageView remarksIcon; + @FXML + private Label remarks; + + private ReadOnlyTask task; + private int displayedIndex; + + public static TaskCard load(ReadOnlyTask task, int displayedIndex){ + TaskCard card = new TaskCard(); + card.task = task; + card.displayedIndex = displayedIndex; + return UiPartLoader.loadUiPart(card); + } + //@@author A0138601M + @FXML + public void initialize() { + id.setText(displayedIndex + ". "); + name.setText(task.getName().fullName); + formatIntervalField(); + formatLocationField(); + formatRemarksField(); + } + + /** + * Display the fields accordingly based on the type of task. + */ + private void formatIntervalField() { + if (task.getInterval().isFloat()){ + intervalIcon.setVisible(false); + startDate.setText(""); + endDate.setText(""); + } else if (task.getInterval().isDeadlineWithTime()) { + intervalIcon.setImage(AppUtil.getImage(ICON_ALARM_CLOCK)); + startDate.setManaged(false); + endDate.setText(task.getInterval().formatEndDateTime()); + } else if (task.getInterval().isDeadlineWithoutTime()) { + intervalIcon.setImage(AppUtil.getImage(ICON_ALARM_CLOCK)); + startDate.setManaged(false); + endDate.setText(task.getInterval().formatEndDate()); + } else { + intervalIcon.setImage(AppUtil.getImage(ICON_HOURGLASS)); + startDate.setText(task.getInterval().formatStartDateTime()); + endDate.setText(task.getInterval().formatEndDateTime()); + } + + } + + private void formatLocationField() { + if (!Strings.isNullOrEmpty(task.getLocation().location)) { + locationIcon.setVisible(true); + } + locationParam.setText(task.getLocation().toString()); + } + + private void formatRemarksField() { + if (!Strings.isNullOrEmpty(task.getRemarks().remarks)) { + remarksIcon.setVisible(true); + } + remarks.setText(task.getRemarks().toString()); + } + + //@@author + 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/todolist/ui/TaskListPanel.java b/src/main/java/seedu/todolist/ui/TaskListPanel.java new file mode 100644 index 000000000000..4816d749898a --- /dev/null +++ b/src/main/java/seedu/todolist/ui/TaskListPanel.java @@ -0,0 +1,130 @@ +package seedu.todolist.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.todolist.commons.core.LogsCenter; +import seedu.todolist.commons.events.ui.TaskPanelSelectionChangedEvent; +import seedu.todolist.model.task.ReadOnlyTask; +import seedu.todolist.model.task.Status; + +import java.util.logging.Logger; + +/** + * Panel containing the list of tasks. + */ +public class TaskListPanel extends UiPart { + private final Logger logger = LogsCenter.getLogger(TaskListPanel.class); + private static final String FXML = "TaskListPanel.fxml"; + private VBox panel; + private AnchorPane placeHolderPane; + + @FXML + private ListView taskListView; + + @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 TaskListPanel load(Stage primaryStage, AnchorPane taskListPlaceholder, + ObservableList taskList, Status.Type type) { + TaskListPanel taskListPanel = + UiPartLoader.loadUiPart(primaryStage, taskListPlaceholder, new TaskListPanel()); + taskListPanel.configure(taskList, type); + return taskListPanel; + } + + /** + * Assign an id based on the type of task it is supposed to display + * @param type represents the status type of a task + */ + private void initializeTaskListID(Status.Type type) { + + switch (type) { + case Complete : + taskListView.setId("completeTaskListView"); + break; + + case Incomplete : + taskListView.setId("taskListView"); + break; + + case Overdue : + taskListView.setId("overdueTaskListView"); + break; + + default : + assert false : "Task should be either Complete, Incomplete or Overdue"; + break; + + } + } + + //@@author + private void configure(ObservableList taskList, Status.Type type) { + setConnections(taskList); + initializeTaskListID(type); + addToPlaceholder(); + } + + private void setConnections(ObservableList taskList) { + taskListView.setItems(taskList); + taskListView.setCellFactory(listView -> new TaskListViewCell()); + setEventHandlerForSelectionChangeEvent(); + } + + private void addToPlaceholder() { + SplitPane.setResizableWithParent(placeHolderPane, false); + placeHolderPane.getChildren().add(panel); + } + + private void setEventHandlerForSelectionChangeEvent() { + taskListView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> { + if (newValue != null) { + logger.fine("Selection in task list panel changed to : '" + newValue + "'"); + raise(new TaskPanelSelectionChangedEvent(newValue)); + } + }); + } + + public void scrollTo(int index) { + Platform.runLater(() -> { + taskListView.scrollTo(index); + taskListView.getSelectionModel().clearAndSelect(index); + }); + } + + class TaskListViewCell extends ListCell { + + @Override + protected void updateItem(ReadOnlyTask task, boolean empty) { + super.updateItem(task, empty); + + if (empty || task == null) { + setGraphic(null); + setText(null); + } else { + setGraphic(TaskCard.load(task, getIndex() + 1).getLayout()); + } + } + } + +} diff --git a/src/main/java/seedu/address/ui/Ui.java b/src/main/java/seedu/todolist/ui/Ui.java similarity index 88% rename from src/main/java/seedu/address/ui/Ui.java rename to src/main/java/seedu/todolist/ui/Ui.java index e6a67fe8c027..fa1c2ccfb764 100644 --- a/src/main/java/seedu/address/ui/Ui.java +++ b/src/main/java/seedu/todolist/ui/Ui.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.todolist.ui; import javafx.stage.Stage; diff --git a/src/main/java/seedu/address/ui/UiManager.java b/src/main/java/seedu/todolist/ui/UiManager.java similarity index 76% rename from src/main/java/seedu/address/ui/UiManager.java rename to src/main/java/seedu/todolist/ui/UiManager.java index 3e2ff71827db..c68f57a3a7fd 100644 --- a/src/main/java/seedu/address/ui/UiManager.java +++ b/src/main/java/seedu/todolist/ui/UiManager.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.todolist.ui; import com.google.common.eventbus.Subscribe; import javafx.application.Platform; @@ -6,17 +6,18 @@ import javafx.scene.control.Alert.AlertType; import javafx.scene.image.Image; import javafx.stage.Stage; -import seedu.address.MainApp; -import seedu.address.commons.core.ComponentManager; -import seedu.address.commons.core.Config; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.events.storage.DataSavingExceptionEvent; -import seedu.address.commons.events.ui.JumpToListRequestEvent; -import seedu.address.commons.events.ui.PersonPanelSelectionChangedEvent; -import seedu.address.commons.events.ui.ShowHelpEvent; -import seedu.address.commons.util.StringUtil; -import seedu.address.logic.Logic; -import seedu.address.model.UserPrefs; +import seedu.todolist.MainApp; +import seedu.todolist.commons.core.ComponentManager; +import seedu.todolist.commons.core.Config; +import seedu.todolist.commons.core.LogsCenter; +import seedu.todolist.commons.events.storage.DataSavingExceptionEvent; +import seedu.todolist.commons.events.ui.JumpToListRequestEvent; +import seedu.todolist.commons.events.ui.ShowHelpEvent; +import seedu.todolist.commons.events.ui.TaskPanelSelectionChangedEvent; +import seedu.todolist.commons.util.StringUtil; +import seedu.todolist.logic.Logic; +import seedu.todolist.model.UserPrefs; +import seedu.todolist.storage.Storage; import java.util.logging.Logger; @@ -25,18 +26,20 @@ */ public class UiManager extends ComponentManager implements Ui { private static final Logger logger = LogsCenter.getLogger(UiManager.class); - private static final String ICON_APPLICATION = "/images/address_book_32.png"; + private static final String ICON_APPLICATION = "/images/taskit_logo.png"; private Logic logic; private Config config; private UserPrefs prefs; private MainWindow mainWindow; + private Storage storage; - public UiManager(Logic logic, Config config, UserPrefs prefs) { + public UiManager(Logic logic, Config config, UserPrefs prefs, Storage storage) { super(); this.logic = logic; this.config = config; this.prefs = prefs; + this.storage = storage; } @Override @@ -48,7 +51,7 @@ public void start(Stage primaryStage) { primaryStage.getIcons().add(getImage(ICON_APPLICATION)); try { - mainWindow = MainWindow.load(primaryStage, config, prefs, logic); + mainWindow = MainWindow.load(primaryStage, config, prefs, logic, storage); mainWindow.show(); //This should be called before creating other UI parts mainWindow.fillInnerParts(); @@ -74,7 +77,7 @@ private Image getImage(String imagePath) { return new Image(MainApp.class.getResourceAsStream(imagePath)); } - void showAlertDialogAndWait(Alert.AlertType type, String title, String headerText, String contentText) { + private void showAlertDialogAndWait(Alert.AlertType type, String title, String headerText, String contentText) { showAlertDialogAndWait(mainWindow.getPrimaryStage(), type, title, headerText, contentText); } @@ -114,13 +117,13 @@ private void handleShowHelpEvent(ShowHelpEvent event) { @Subscribe private void handleJumpToListRequestEvent(JumpToListRequestEvent event) { logger.info(LogsCenter.getEventHandlingLogMessage(event)); - mainWindow.getPersonListPanel().scrollTo(event.targetIndex); + mainWindow.getTaskListPanel(logic.getCurrentTab()).scrollTo(event.targetIndex); } @Subscribe - private void handlePersonPanelSelectionChangedEvent(PersonPanelSelectionChangedEvent event){ + private void handleTaskPanelSelectionChangedEvent(TaskPanelSelectionChangedEvent event){ logger.info(LogsCenter.getEventHandlingLogMessage(event)); - mainWindow.loadPersonPage(event.getNewSelection()); + mainWindow.loadTaskPage(event.getNewSelection()); } } diff --git a/src/main/java/seedu/address/ui/UiPart.java b/src/main/java/seedu/todolist/ui/UiPart.java similarity index 92% rename from src/main/java/seedu/address/ui/UiPart.java rename to src/main/java/seedu/todolist/ui/UiPart.java index 0a4ceb33e9b7..306fddcfa6b5 100644 --- a/src/main/java/seedu/address/ui/UiPart.java +++ b/src/main/java/seedu/todolist/ui/UiPart.java @@ -1,13 +1,13 @@ -package seedu.address.ui; +package seedu.todolist.ui; import javafx.scene.Node; import javafx.scene.Scene; import javafx.scene.layout.AnchorPane; import javafx.stage.Modality; import javafx.stage.Stage; -import seedu.address.commons.core.EventsCenter; -import seedu.address.commons.events.BaseEvent; -import seedu.address.commons.util.AppUtil; +import seedu.todolist.commons.core.EventsCenter; +import seedu.todolist.commons.events.BaseEvent; +import seedu.todolist.commons.util.AppUtil; /** * Base class for UI parts. @@ -18,11 +18,7 @@ public abstract class UiPart { /** * The primary stage for the UI Part. */ - Stage primaryStage; - - public UiPart(){ - - } + protected Stage primaryStage; /** * Raises the event via {@link EventsCenter#post(BaseEvent)} diff --git a/src/main/java/seedu/address/ui/UiPartLoader.java b/src/main/java/seedu/todolist/ui/UiPartLoader.java similarity index 97% rename from src/main/java/seedu/address/ui/UiPartLoader.java rename to src/main/java/seedu/todolist/ui/UiPartLoader.java index f880685a5b15..ba827c8910f0 100644 --- a/src/main/java/seedu/address/ui/UiPartLoader.java +++ b/src/main/java/seedu/todolist/ui/UiPartLoader.java @@ -1,10 +1,10 @@ -package seedu.address.ui; +package seedu.todolist.ui; import javafx.fxml.FXMLLoader; import javafx.scene.Node; import javafx.scene.layout.AnchorPane; import javafx.stage.Stage; -import seedu.address.MainApp; +import seedu.todolist.MainApp; /** * A utility class to load UiParts from FXML files. diff --git a/src/main/resources/images/address_book_32.png b/src/main/resources/images/address_book_32.png deleted file mode 100644 index 29810cf1fd93..000000000000 Binary files a/src/main/resources/images/address_book_32.png and /dev/null differ diff --git a/src/main/resources/images/alarm_clock.png b/src/main/resources/images/alarm_clock.png new file mode 100644 index 000000000000..283aaedc46c8 Binary files /dev/null and b/src/main/resources/images/alarm_clock.png differ diff --git a/src/main/resources/images/clock.png b/src/main/resources/images/clock.png deleted file mode 100644 index 0807cbf64519..000000000000 Binary files a/src/main/resources/images/clock.png and /dev/null differ diff --git a/src/main/resources/images/clock_icon.png b/src/main/resources/images/clock_icon.png new file mode 100755 index 000000000000..4e3a63680b98 Binary files /dev/null and b/src/main/resources/images/clock_icon.png differ diff --git a/src/main/resources/images/command_pane_title.png b/src/main/resources/images/command_pane_title.png new file mode 100644 index 000000000000..ec8db15055ef Binary files /dev/null and b/src/main/resources/images/command_pane_title.png differ diff --git a/src/main/resources/images/completed_tab_title.png b/src/main/resources/images/completed_tab_title.png new file mode 100644 index 000000000000..e81f0fa6a06a Binary files /dev/null and b/src/main/resources/images/completed_tab_title.png differ diff --git a/src/main/resources/images/hourglass.png b/src/main/resources/images/hourglass.png new file mode 100644 index 000000000000..9fcb0f48c1be Binary files /dev/null and b/src/main/resources/images/hourglass.png differ diff --git a/src/main/resources/images/incomplete_tab_title.png b/src/main/resources/images/incomplete_tab_title.png new file mode 100644 index 000000000000..6f035d94b01c Binary files /dev/null and b/src/main/resources/images/incomplete_tab_title.png differ diff --git a/src/main/resources/images/location_icon.png b/src/main/resources/images/location_icon.png new file mode 100755 index 000000000000..740bade98450 Binary files /dev/null and b/src/main/resources/images/location_icon.png differ diff --git a/src/main/resources/images/notification_title.png b/src/main/resources/images/notification_title.png new file mode 100644 index 000000000000..5a4312ed7b63 Binary files /dev/null and b/src/main/resources/images/notification_title.png differ diff --git a/src/main/resources/images/overdue_tab_title.png b/src/main/resources/images/overdue_tab_title.png new file mode 100644 index 000000000000..c2250afd7f8a Binary files /dev/null and b/src/main/resources/images/overdue_tab_title.png differ diff --git a/src/main/resources/images/remarks_icon.png b/src/main/resources/images/remarks_icon.png new file mode 100755 index 000000000000..2053ae62e2e6 Binary files /dev/null and b/src/main/resources/images/remarks_icon.png differ diff --git a/src/main/resources/images/taskit_logo.png b/src/main/resources/images/taskit_logo.png new file mode 100644 index 000000000000..df193cacc521 Binary files /dev/null and b/src/main/resources/images/taskit_logo.png differ diff --git a/src/main/resources/view/CommandBox.fxml b/src/main/resources/view/CommandBox.fxml index 575de420b994..d6de4d641634 100644 --- a/src/main/resources/view/CommandBox.fxml +++ b/src/main/resources/view/CommandBox.fxml @@ -3,7 +3,7 @@ + fx:controller="seedu.todolist.ui.CommandBox" stylesheets="@DarkTheme.css"> diff --git a/src/main/resources/view/DarkTheme.css b/src/main/resources/view/DarkTheme.css index 8043b344253a..69bc95448d14 100644 --- a/src/main/resources/view/DarkTheme.css +++ b/src/main/resources/view/DarkTheme.css @@ -28,16 +28,16 @@ -fx-font-family: "Segoe UI Semibold"; } -.tab-pane { - -fx-padding: 0 0 0 1; -} - .tab-pane .tab-header-area { -fx-padding: 0 0 0 0; -fx-min-height: 0; -fx-max-height: 0; } +.tab-pane *.tab-header-background { + -fx-background-color: transparent; +} + .table-view { -fx-base: #1d1d1d; -fx-control-inner-background: #1d1d1d; @@ -75,45 +75,56 @@ -fx-background-color: -fx-focus-color; } -.split-pane:horizontal .split-pane-divider { - -fx-border-color: transparent #1d1d1d transparent #1d1d1d; - -fx-background-color: transparent, derive(#1d1d1d, 10%); +.split-pane .split-pane-divider { + -fx-background-color: transparent, derive(#1d1d1d, 5%); + -fx-padding: 0 0.1em 0 0.1em; } .split-pane { -fx-border-radius: 1; -fx-border-width: 1; - -fx-background-color: derive(#1d1d1d, 20%); + -fx-background-color: derive(#1d1d1d, 10%); +} + +/*@@author A0138601M */ +.list-view { + -fx-background-color: #cccccc; + -fx-padding: 3; } .list-cell { - -fx-label-padding: 0 0 0 0; - -fx-graphic-text-gap : 0; - -fx-padding: 0 0 0 0; + -fx-background-color: #eeeeee; + -fx-background-radius: 10; + -fx-border-width: 2px; + -fx-border-color: #cccccc; + -fx-border-radius: 8; +} + +.list-cell:empty { + -fx-background-color: transparent; +} + +.list-cell:filled:selected:focused, .list-cell:filled:selected { + -fx-background-color: #dddddd; } .list-cell .label { - -fx-text-fill: #010504; + -fx-text-fill: #111111; } +/*@@author */ .cell_big_label { - -fx-font-size: 16px; + -fx-font-size: 18px; -fx-text-fill: #010504; } .cell_small_label { - -fx-font-size: 11px; + -fx-font-size: 13px; -fx-text-fill: #010504; } .anchor-pane { - -fx-background-color: derive(#1d1d1d, 20%); -} - -.anchor-pane-with-border { - -fx-background-color: derive(#1d1d1d, 20%); - -fx-border-color: derive(#1d1d1d, 10%); - -fx-border-top-width: 1px; + -fx-background-color: derive(#1d1d1d, 10%); } .status-bar { @@ -131,11 +142,14 @@ .status-bar .label { -fx-text-fill: white; + -fx-font-size: 11pt; + -fx-font-family: "Segoe UI Light"; + -fx-padding: 0 0 0 10; } .status-bar-with-border { - -fx-background-color: derive(#1d1d1d, 30%); - -fx-border-color: derive(#1d1d1d, 25%); + -fx-background-color: derive(#1d1d1d, 20%); + -fx-border-color: derive(#1d1d1d, 20%); -fx-border-width: 1px; } @@ -163,6 +177,9 @@ .menu-bar { -fx-background-color: derive(#1d1d1d, 20%); + -fx-border-color: derive(#1d1d1d, 30%); + -fx-border-top-width: 1px; + -fx-border-bottom-width: 1px; } .menu-bar .label { @@ -272,17 +289,84 @@ -fx-padding: 8 1 8 1; } -#cardPane { +/*@@author A0138601M */ +#cardPane { + -fx-background-color: transparent; +} + +.tab { + -fx-focus-color: transparent; -fx-background-color: transparent; - -fx-border-color: #d6d6d6; - -fx-border-width: 1 1 1 1; + -fx-opacity: 0.7; + -fx-padding: 20 20 5 60; } -#commandTypeLabel { - -fx-font-size: 11px; - -fx-text-fill: #F70D1A; +.tab .tab-label { + -fx-alignment: CENTER; + -fx-font-family: "Segoe UI Semibold"; + -fx-text-fill: #bab6b6; + -fx-font-size: 11pt; +} + +.tab:selected { + -fx-opacity: 1.0; +} + +.tab:selected .tab-label { + -fx-alignment: CENTER; + -fx-text-fill: #ffffff; +} + +#notificationPane .title{ + -fx-background-color: transparent; + -fx-padding: 25 20 10 60; + -fx-background-image: url('../images/notification_title.png'); + -fx-background-repeat: no-repeat; + -fx-background-size: 170; +} + +#incompleteTab { + -fx-background-image: url('../images/incomplete_tab_title.png'); + -fx-background-size: cover; +} + +#completedTab { + -fx-translate-x: -1; + -fx-background-image: url('../images/completed_tab_title.png'); + -fx-background-size: cover; +} + +#overdueTab { + -fx-translate-x: -1; + -fx-padding: 20 30 5 70; + -fx-background-image: url('../images/overdue_tab_title.png'); + -fx-background-size: cover; +} + +#taskListPanelPlaceholder { + -fx-background-color: #ffc425; + -fx-background-radius: 0 3 3 3; +} + +#completeTaskListPanelPlaceholder { + -fx-background-color: #00b159; + -fx-background-radius: 0 3 3 3; +} + +#overdueTaskListPanelPlaceholder { + -fx-background-color: #d11141; + -fx-background-radius: 0 3 3 3; +} + +#commandPaneTitleLabel { + -fx-background-image: url('../images/command_pane_title.png'); + -fx-background-repeat: no-repeat; + -fx-background-size: 170; + -fx-text-fill: #ffffff; + -fx-padding: 25 30 10 70; } -#filterField, #personListPanel, #personWebpage { - -fx-effect: innershadow(gaussian, black, 10, 0, 0, 0); +#resultDisplayPlaceholder, #commandBoxPlaceholder { + -fx-background-color: #f37735; + -fx-background-radius: 0 3 3 3; } \ No newline at end of file diff --git a/src/main/resources/view/DefaultBrowserPlaceHolderScreen.fxml b/src/main/resources/view/DefaultBrowserPlaceHolderScreen.fxml deleted file mode 100644 index bc761118235a..000000000000 --- a/src/main/resources/view/DefaultBrowserPlaceHolderScreen.fxml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - diff --git a/src/main/resources/view/Extensions.css b/src/main/resources/view/Extensions.css index 606c927d9a42..585be5460220 100644 --- a/src/main/resources/view/Extensions.css +++ b/src/main/resources/view/Extensions.css @@ -1,9 +1,8 @@ .error { - -fx-background-color: red; + -fx-background-color: #fccece; } - .tag-selector { -fx-border-width: 1; -fx-border-color: white; diff --git a/src/main/resources/view/HelpWindow.fxml b/src/main/resources/view/HelpWindow.fxml index c4cbd84cac28..1cd7a2431f60 100644 --- a/src/main/resources/view/HelpWindow.fxml +++ b/src/main/resources/view/HelpWindow.fxml @@ -1,7 +1,7 @@ - + diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml index 2f9235c621d8..341c629fc71e 100644 --- a/src/main/resources/view/MainWindow.fxml +++ b/src/main/resources/view/MainWindow.fxml @@ -1,56 +1,89 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/PersonListCard.fxml b/src/main/resources/view/PersonListCard.fxml deleted file mode 100644 index 13d4b149651b..000000000000 --- a/src/main/resources/view/PersonListCard.fxml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/resources/view/ResultDisplay.fxml b/src/main/resources/view/ResultDisplay.fxml index cc650d739e22..d15ef674da7c 100644 --- a/src/main/resources/view/ResultDisplay.fxml +++ b/src/main/resources/view/ResultDisplay.fxml @@ -2,8 +2,8 @@ - + diff --git a/src/main/resources/view/StatusBarFooter.fxml b/src/main/resources/view/StatusBarFooter.fxml index 2656558b6eb7..af128f57342d 100644 --- a/src/main/resources/view/StatusBarFooter.fxml +++ b/src/main/resources/view/StatusBarFooter.fxml @@ -1,7 +1,7 @@ - + diff --git a/src/main/resources/view/TaskListCard.fxml b/src/main/resources/view/TaskListCard.fxml new file mode 100644 index 000000000000..cf18b80e8771 --- /dev/null +++ b/src/main/resources/view/TaskListCard.fxml @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/PersonListPanel.fxml b/src/main/resources/view/TaskListPanel.fxml similarity index 59% rename from src/main/resources/view/PersonListPanel.fxml rename to src/main/resources/view/TaskListPanel.fxml index 000c4c999b65..53519aab43e2 100644 --- a/src/main/resources/view/PersonListPanel.fxml +++ b/src/main/resources/view/TaskListPanel.fxml @@ -3,12 +3,12 @@ - + - + diff --git a/src/test/data/ConfigUtilTest/ExtraValuesConfig.json b/src/test/data/ConfigUtilTest/ExtraValuesConfig.json index 578b4445204b..9dc26863f56e 100644 --- a/src/test/data/ConfigUtilTest/ExtraValuesConfig.json +++ b/src/test/data/ConfigUtilTest/ExtraValuesConfig.json @@ -2,7 +2,7 @@ "appTitle" : "Typical App Title", "logLevel" : "INFO", "userPrefsFilePath" : "C:\\preferences.json", - "addressBookFilePath" : "addressbook.xml", - "addressBookName" : "TypicalAddressBookName", + "ToDoListFilePath" : "todolist.xml", + "ToDoListName" : "TypicalToDoListName", "extra" : "extra value" } \ No newline at end of file diff --git a/src/test/data/ConfigUtilTest/TypicalConfig.json b/src/test/data/ConfigUtilTest/TypicalConfig.json index 195b2bf33033..8806e7cc1d96 100644 --- a/src/test/data/ConfigUtilTest/TypicalConfig.json +++ b/src/test/data/ConfigUtilTest/TypicalConfig.json @@ -2,6 +2,6 @@ "appTitle" : "Typical App Title", "logLevel" : "INFO", "userPrefsFilePath" : "C:\\preferences.json", - "addressBookFilePath" : "addressbook.xml", - "addressBookName" : "TypicalAddressBookName" + "ToDoListFilePath" : "todolist.xml", + "ToDoListName" : "TypicalToDoListName" } \ No newline at end of file diff --git a/src/test/data/ManualTesting/SampleData.xml b/src/test/data/ManualTesting/SampleData.xml new file mode 100644 index 000000000000..b52cee58c8f6 --- /dev/null +++ b/src/test/data/ManualTesting/SampleData.xml @@ -0,0 +1,440 @@ + + + + Thailand trip + 11 Oct 2016 + 10:00AM + 13 Oct 2016 + 8:00PM + pack luggage + complete + + + submit proposal to boss + 11 Oct 2016 + 11:59PM + print hardcopy + complete + + + celebrate Mary birthday + 12 Oct 2016 + 12:00PM + 12 Oct 2016 + 4:00PM + cafe + buy presents + complete + + + clean room + 12 Oct 2016 + 5:00PM + home + complete + + + dinner with dad + 12 Oct 2016 + 7:00PM + 12 Oct 2016 + 8:00PM + home + buy food + complete + + + prepare meeting agenda + 13 Oct 2016 + 11:00AM + complete + + + group project meeting + 13 Oct 2016 + 2:00PM + 13 Oct 2016 + 4:00PM + discussion room + complete + + + make cake with Mary + 13 Oct 2016 + 5:00PM + 13 Oct 2016 + 8:00PM + home + buy ingredients + complete + + + searching hotel on the internet + 14 Oct 2016 + complete + + + haircut + 16 Oct 2016 + 3:00PM + 16 Oct 2016 + 3:30PM + salon + complete + + + doing exercises + 16 Oct 2016 + 5:00PM + 16 Oct 2016 + 5:30PM + gym room + complete + + + trip to Malaysia + 17 Oct 2016 + 12:00PM + 21 Oct 2016 + 6:00PM + complete + + + visit Tom + 18 Oct 2016 + 3:00PM + 18 Oct 2016 + 5:00PM + Malaysia + complete + + + buy shoes + 20 Oct 2016 + online shop + complete + + + midterm exam + 21 Oct 2016 + 6:30PM + 21 Oct 2016 + 8:00PM + Lecture Hall + complete + + + buy airticket + 22 Oct 2016 + complete + + + project deadline + 23 Oct 2016 + 5:00PM + complete + + + watch movie + 23 Oct 2016 + 7:30PM + 23 Oct 2016 + 10:00PM + cinema + bring 3D glasses + complete + + + hiking + 25 Oct 2016 + 10:00AM + 25 Oct 2016 + 4:00PM + Habourfront + complete + + + Revision on CS2103 + 26 Oct 2016 + 10:00PM + 26 Oct 2016 + 11:00PM + complete + + + attend tutorial + 27 Oct 2016 + 3:00PM + 27 Oct 2016 + 4:00PM + NUS + complete + + + lecture quiz + 27 Oct 2016 + 5:00PM + 27 Oct 2016 + 6:00PM + bring pen + complete + + + dinner with Sally + 28 Oct 2016 + 6:00PM + 28 Oct 2016 + 8:00PM + Bugis + complete + + + submit homework + 28 Oct 2016 + 11:59PM + complete + + + wash clothes + 29 Oct 2016 + complete + + + buy breakfast + 30 Oct 2016 + Fairprice + complete + + + Presentation + 31 Oct 2016 + 8:00AM + 31 Oct 2016 + 9:00AM + NUS + prepare script + complete + + + lab demo + 31 Oct 2016 + 10:00AM + 31 Oct 2016 + 11:00AM + Com1 + complete + + + hand in lab assignment + 31 Oct 2016 + 5:00PM + complete + + + make reservation for dinner + 31 Oct 2016 + 10:00PM + want a table near window + complete + + + Help sister buy shampoo + 31 Oct 2016 + overdue + + + ABC project meeting + 1 Nov 2016 + 10:00AM + 1 Nov 2016 + 11:30AM + Orchard + prepare agenda + complete + + + mom visiting + 1 Nov 2016 + 12:00PM + 3 Nov 2016 + 4:00PM + complete + + + submit lab report + 1 Nov 2016 + 11:59PM + IVLE + follow report format + overdue + + + Go gym + 2 Nov 2016 + bring towel + complete + + + prepare tutorial tasks + 3 Nov 2016 + complete + + + prepare make breakfast for brother + 4 Nov 2016 + 7:00AM + 4 Nov 2016 + 8:00AM + home + buy eggs + complete + + + attend lecture + 4 Nov 2016 + 2:00PM + 4 Nov 2016 + 4:00PM + bring laptop + complete + + + go dancing class + 4 Nov 2016 + 6:00PM + 4 Nov 2016 + 7:30PM + esplanade + complete + + + calling mom + 5 Nov 2016 + 11:00AM + 5 Nov 2016 + 11:30AM + tell her I will not be back for dinner tomorrow + complete + + + go to church + 5 Nov 2016 + 5:00PM + 5 Nov 2016 + 7:00PM + CityHall + complete + + + meet up with Mary + 6 Nov 2016 + 12:00PM + 6 Nov 2016 + 1:00PM + East Coast + buy present + complete + + + finding food recipe + 7 Nov 2016 + 11:00AM + 7 Nov 2016 + 11:30AM + no spicy food + complete + + + appointment with doctor + 7 Nov 2016 + 3:30PM + 7 Nov 2016 + 4:30PM + VivoCity + complete + + + record TV show + 7 Nov 2016 + 8:00PM + overdue + + + play football + 10 Nov 2016 + 6:00PM + 10 Nov 2016 + 7:00PM + playground + bring ball + incomplete + + + cooking dinner myself + 10 Nov 2016 + 8:00PM + 10 Nov 2016 + 9:30PM + home + incomplete + + + football competition + 11 Nov 2016 + 12:00PM + 11 Nov 2016 + 2:00PM + playground + invite friends + incomplete + + + attend Jimmy 21st birthday celebration + 15 Nov 2016 + 7:00PM + 15 Nov 2016 + 10:00PM + Aloha Loyang + incomplete + + + submit module feedback + 18 Nov 2016 + 6:00PM + incomplete + + + Mary wedding + 1 Dec 2016 + 10:00AM + 1 Dec 2016 + 2:00PM + Trinity Church + incomplete + + + high school homecoming dinner + 10 Dec 2016 + 6:30PM + 10 Dec 2016 + 8:00PM + incomplete + + + buy movie ticket + 20 Dec 2016 + NEX shaw + with popcorn combo + incomplete + + + prepare christmas present + 24 Dec 2016 + buy for elder sis + incomplete + + + Prepare for Seattle trip + incomplete + + diff --git a/src/test/data/ManualTesting/TestScript.md b/src/test/data/ManualTesting/TestScript.md new file mode 100644 index 000000000000..25845722027d --- /dev/null +++ b/src/test/data/ManualTesting/TestScript.md @@ -0,0 +1,171 @@ +###Test Script (Manual Testing) + + +####Loading the sample data +1. Create a data folder in the same directory as Task!t.jar. +2. Place SampleData.xml inside the data folder. +3. Rename SampleData.xml to todolist.xml +4. Double click Task!t.jar. The data should be loaded. + + +####Open help window +1 Press F1 or type `help` in the command box. +``` +Result: A new window will open up and you will be directed to the user guide. +``` +####Adding a new task +#####Adding an event type task +1 Type `add watch movie from friday 7pm to friday 9pm at NEX remarks buy popcorn` +``` +Result: A new task will be added to the incomplete tab. +> Date and time will be displayed at the right hand side with an image of an hourglass. +> Location and remarks will be displayed below the task name with a location marker icon and speech icon respectively. +``` +#####Adding a deadline type task +1. Type `add buy phone cover by 9 Dec 2016 remarks book online` +``` +Result: A new task will be added to the incomplete tab. +> The tasks will be sorted by date time. +> Time will be displayed as '9 Dec 2016' at the right hand side with with an image of an alarm clock. +> Remarks will be displayed below the task name with a speech icon. +``` +#####Adding a float type task +1. Type `add buy milk` +``` +Result: A new task will be added to the incomplete tab. +> Float type task will be displayed after event and deadline type task. +``` + + +####Finding a task +#####Find task that contains either keywords +1. Type `find either football dinner` +``` +Result: 7 tasks displayed! 4 tasks in incomplete tab, 3 tasks in completed tab, 0 tasks in overdue tab. +``` +#####Find task that contains all keywords +1. Type `find all submit lab` +``` +Result: 1 tasks displayed! 0 tasks in incomplete tab, 0 tasks in completed tab, 1 tasks in overdue tab. +``` +#####Find task that fits exactly a phrase +1. Type `find phrase christmas present` +``` +Result: 1 tasks displayed! 1 tasks in incomplete tab, 0 tasks in completed tab, 0 tasks in overdue tab. +``` + + +####Listing the tasks +#####Listing today's task +1. Type `list today` +``` +Result: Shows all tasks with today's date. +``` +#####Listing this week's task +1. Type `list week` +``` +Result: Shows all tasks for the current week, from Sunday to Saturday. +``` +#####Listing this month's task +1. Type `list month` +``` +Result: Shows all tasks for the current month. +``` +#####Listing tasks on a specific date +1. Type `list 1 Dec 2016` +``` +Result: 1 tasks displayed! 1 tasks in incomplete tab, 0 tasks in completed tab, 0 tasks in overdue tab.. +``` +#####Listing all task +1. Type `list` +``` +Result: All tasks will be listed. +``` + + +####Editing a task +#####Editing a task name +1. Type `edit 2 cook dinner with friends` +``` +Result: The task name for index 2 will change to 'cook dinner with friends'. Other details remain unchanged. +``` +#####Editing a task date +1. Type `edit 2 from 10 Nov 2016 5pm to 10 Nov 2016 6pm` +``` +Result: The task date for index 2 will change to '9 Nov 2016 5pm to 9 Nov 2016 6pm'. Other details remain unchanged. +``` +#####Editing a task location +1. Type `edit 3 at Stadium` +``` +Result: A location will be changed for task in index 3 to Stadium. Other details remain unchanged. +``` +#####Editing a task remark +1. Type `edit 2 remarks buy rice` +``` +Result: The remarks for the task at index 2 will be added. Other details remain unchanged. +``` + + +####Mark a task as completed +#####Marking a single task +1. Type `done 1` +``` +Result: Incomplete tab will display 12 tasks. Click completed tab, it will display 43 tasks. The task should be at index 43. +``` +2. Stay in complete tab and type `done 1` +``` +Result: The command box will display an error message 'This task is already completed!'. +``` +#####Marking multiple tasks +1. Click incomplete tab and type `done 1,2` +``` +Result: Incomplete tab will display 10 tasks. Click completed tab, it will display 45 tasks. The tasks should be at index 44, 45. +``` + + +####Undo an action +1. Type `undo` +``` +Result: Your previous action will be undone. In this case, your tasks 'cook dinner with friends' and 'play football' will return to the incomplete tab. +``` +2. Type `undo` again +``` +Result: Your previous action will be undone again. In this case, your task 'football competition' will return to the incomplete tab. +``` + +####Redoing a task +1. Type `redo` +``` +Result: Your previous undo will be redo-ed. In this case, your task 'football competition' will return to the complete tab + +``` +2. Type `redo` again +``` +Result: Your previous undo will be redone. In this case, your tasks 'cook dinner with friends' and 'football competition' will return to the complete tab. +``` + +####Deleting a task +#####Deleting a single task +1. Type `delete 1` +``` +Result: 'watch movie' will be deleted +``` +#####Deleting multiple tasks +1. Type `delete 1,2` +``` +Result: 'attending Jimmy 21st birthday celebration' and 'submit module feedback' will be deleted. +``` + +####Clear +#####Delete all data from every tab +1. Type `clear` +``` +Result: Data in all tabs should be deleted +``` + +####Exit +#####Exit the application +1. Type `exit` +``` +Result: Application should close. +``` diff --git a/src/test/data/XmlAddressBookStorageTest/NotXmlFormatAddressBook.xml b/src/test/data/XmlToDoListStorageTest/NotXmlFormatToDoList.xml similarity index 100% rename from src/test/data/XmlAddressBookStorageTest/NotXmlFormatAddressBook.xml rename to src/test/data/XmlToDoListStorageTest/NotXmlFormatToDoList.xml diff --git a/src/test/data/XmlUtilTest/tempAddressBook.xml b/src/test/data/XmlUtilTest/tempToDoList.xml similarity index 92% rename from src/test/data/XmlUtilTest/tempAddressBook.xml rename to src/test/data/XmlUtilTest/tempToDoList.xml index 41eeb8eb391a..9148a43b73f4 100644 --- a/src/test/data/XmlUtilTest/tempAddressBook.xml +++ b/src/test/data/XmlUtilTest/tempToDoList.xml @@ -1,5 +1,5 @@ - + 1 John @@ -12,4 +12,4 @@ Friends - + diff --git a/src/test/data/XmlUtilTest/validAddressBook.xml b/src/test/data/XmlUtilTest/validAddressBook.xml deleted file mode 100644 index eafca730fb1e..000000000000 --- a/src/test/data/XmlUtilTest/validAddressBook.xml +++ /dev/null @@ -1,57 +0,0 @@ - - - - Hans Muster - 9482424 - hans@google.com -
4th street
-
- - Ruth Mueller - 87249245 - ruth@google.com -
81th street
-
- - Heinz Kurz - 95352563 - heinz@yahoo.com -
wall street
-
- - Cornelia Meier - 87652533 - cornelia@google.com -
10th street
-
- - Werner Meyer - 9482224 - werner@gmail.com -
michegan ave
-
- - Lydia Kunz - 9482427 - lydia@gmail.com -
little tokyo
-
- - Anna Best - 9482442 - anna@google.com -
4th street
-
- - Stefan Meier - 8482424 - stefan@mail.com -
little india
-
- - Martin Mueller - 8482131 - hans@google.com -
chicago ave
-
-
diff --git a/src/test/data/XmlUtilTest/validToDoList.xml b/src/test/data/XmlUtilTest/validToDoList.xml new file mode 100644 index 000000000000..f341430a249d --- /dev/null +++ b/src/test/data/XmlUtilTest/validToDoList.xml @@ -0,0 +1,43 @@ + + + + Student Campaign + 13 Oct 2016 + 09:30AM + 18 Oct 2016 + 06:00PM + NUS + print flyers + incomplete + + + Dinner with mum + 14 Oct 2016 + 07:00PM + 14 Oct 2016 + 08:30PM + Orchard + make reservation + incomplete + + + Dinner with dad + 24 Oct 2016 + 07:00PM + 24 Oct 2016 + 08:30PM + Orchard + make reservation + incomplete + + + Christmas dinner + 25 Dec 2016 + 07:00PM + 25 Dec 2016 + 08:30PM + Orchard + make reservation + incomplete + + diff --git a/src/test/java/guitests/AddCommandTest.java b/src/test/java/guitests/AddCommandTest.java index 3b2e1844bd0d..2bac6c820968 100644 --- a/src/test/java/guitests/AddCommandTest.java +++ b/src/test/java/guitests/AddCommandTest.java @@ -1,53 +1,75 @@ package guitests; -import guitests.guihandles.PersonCardHandle; +import guitests.guihandles.TaskCardHandle; import org.junit.Test; -import seedu.address.logic.commands.AddCommand; -import seedu.address.commons.core.Messages; -import seedu.address.testutil.TestPerson; -import seedu.address.testutil.TestUtil; -import static org.junit.Assert.assertTrue; +import seedu.task.testutil.TestTask; +import seedu.task.testutil.TestTaskList; +import seedu.todolist.commons.core.Messages; +import seedu.todolist.model.task.Location; +import seedu.todolist.model.task.Name; +import seedu.todolist.model.task.Remarks; -public class AddCommandTest extends AddressBookGuiTest { +//@@author A0138601M +public class AddCommandTest extends ToDoListGuiTest { @Test public void add() { - //add one person - TestPerson[] currentList = td.getTypicalPersons(); - TestPerson personToAdd = td.hoon; - assertAddSuccess(personToAdd, currentList); - currentList = TestUtil.addPersonsToList(currentList, personToAdd); - - //add another person - personToAdd = td.ida; - assertAddSuccess(personToAdd, currentList); - currentList = TestUtil.addPersonsToList(currentList, personToAdd); - - //add duplicate person - commandBox.runCommand(td.hoon.getAddCommand()); - assertResultMessage(AddCommand.MESSAGE_DUPLICATE_PERSON); - assertTrue(personListPanel.isListMatching(currentList)); + + TestTaskList currentList = new TestTaskList(td.getTypicalTasks()); + + //add one upcoming task + TestTask taskToAdd = td.upcomingEvent; + assertAddSuccess(taskToAdd, currentList); + + //add one overdue task + taskToAdd = td.overdueDeadline; + assertAddSuccess(taskToAdd, currentList); //add to empty list commandBox.runCommand("clear"); - assertAddSuccess(td.alice); + currentList.clear(); + taskToAdd = td.eventWithLocation; + assertAddSuccess(taskToAdd, currentList); + + //add task without name + commandBox.runCommand("add"); + assertResultMessage(Name.MESSAGE_NAME_CONSTRAINTS_EMPTY); + //add task with invalid location + commandBox.runCommand("add invalid location at !@#$"); + assertResultMessage(Location.MESSAGE_LOCATION_PARAMETER_CONSTRAINTS); + + //add task with invalid remarks + commandBox.runCommand("add invalidRemarks remarks !@#$"); + assertResultMessage(Remarks.MESSAGE_REMARKS_PARAMETER_CONSTRAINTS); + //invalid command - commandBox.runCommand("adds Johnny"); + commandBox.runCommand("adds invalidcommand"); assertResultMessage(Messages.MESSAGE_UNKNOWN_COMMAND); } - private void assertAddSuccess(TestPerson personToAdd, TestPerson... currentList) { - commandBox.runCommand(personToAdd.getAddCommand()); - + /** + * Runs the add command to add new tasks and confirms the result is correct. + * @param taskToAdd a new task to be added + * @param currentList A copy of the current list of tasks (before addition). + */ + private void assertAddSuccess(TestTask taskToAdd, TestTaskList currentList) { + commandBox.runCommand(taskToAdd.getAddCommand()); + confirmResult(taskToAdd, currentList); + } + + /** + * Check that the result after add command is as expected + */ + private void confirmResult(TestTask taskToAdd, TestTaskList currentList) { //confirm the new card contains the right data - PersonCardHandle addedCard = personListPanel.navigateToPerson(personToAdd.getName().fullName); - assertMatching(personToAdd, addedCard); + TaskCardHandle addedCard = taskListPanel.navigateToTask(taskToAdd); + assertMatching(taskToAdd, addedCard); - //confirm the list now contains all previous persons plus the new person - TestPerson[] expectedList = TestUtil.addPersonsToList(currentList, personToAdd); - assertTrue(personListPanel.isListMatching(expectedList)); + //confirm the list now contains all previous tasks plus the new task + currentList.addTasksToList(taskToAdd); + assertAllListMatching(currentList); } } diff --git a/src/test/java/guitests/ClearCommandTest.java b/src/test/java/guitests/ClearCommandTest.java index 9d52b427659c..93c0f20ea1b4 100644 --- a/src/test/java/guitests/ClearCommandTest.java +++ b/src/test/java/guitests/ClearCommandTest.java @@ -2,30 +2,48 @@ import org.junit.Test; -import static org.junit.Assert.assertTrue; +import seedu.task.testutil.TestTaskList; +import seedu.todolist.commons.core.Messages; +import seedu.todolist.logic.commands.ClearCommand; +import seedu.todolist.model.task.Status; -public class ClearCommandTest extends AddressBookGuiTest { +import static seedu.todolist.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +public class ClearCommandTest extends ToDoListGuiTest { @Test public void clear() { //verify a non-empty list can be cleared - assertTrue(personListPanel.isListMatching(td.getTypicalPersons())); - assertClearCommandSuccess(); + TestTaskList currentList = new TestTaskList(td.getTypicalTasks()); + assertAllListMatching(currentList); + assertClearCommandSuccess(currentList); //verify other commands can work after a clear command - commandBox.runCommand(td.hoon.getAddCommand()); - assertTrue(personListPanel.isListMatching(td.hoon)); + commandBox.runCommand(td.upcomingEvent.getAddCommand()); + currentList.addTasksToList(td.upcomingEvent); + assertAllListMatching(currentList); + commandBox.runCommand("delete 1"); - assertListSize(0); + currentList.removeTasksFromList(new int[]{1}, Status.Type.Incomplete); + assertAllListMatching(currentList); //verify clear command works when the list is empty - assertClearCommandSuccess(); + assertClearCommandSuccess(currentList); + + //invalid command word + commandBox.runCommand("clear2"); + assertResultMessage(Messages.MESSAGE_UNKNOWN_COMMAND); + + //invalid command argument + commandBox.runCommand("clear 2"); + assertResultMessage(String.format(MESSAGE_INVALID_COMMAND_FORMAT, ClearCommand.MESSAGE_USAGE)); } - private void assertClearCommandSuccess() { + private void assertClearCommandSuccess(TestTaskList currentList) { commandBox.runCommand("clear"); - assertListSize(0); - assertResultMessage("Address book has been cleared!"); + currentList.clear(); + assertAllListMatching(currentList); + assertResultMessage("All tasks have been cleared!"); } } diff --git a/src/test/java/guitests/CommandBoxTest.java b/src/test/java/guitests/CommandBoxTest.java index 1379198bf8b0..e67947e81e2d 100644 --- a/src/test/java/guitests/CommandBoxTest.java +++ b/src/test/java/guitests/CommandBoxTest.java @@ -4,11 +4,11 @@ import static org.junit.Assert.assertEquals; -public class CommandBoxTest extends AddressBookGuiTest { +public class CommandBoxTest extends ToDoListGuiTest { @Test public void commandBox_commandSucceeds_textCleared() { - commandBox.runCommand(td.benson.getAddCommand()); + commandBox.runCommand(td.eventWithLocation.getAddCommand()); assertEquals(commandBox.getCommandInput(), ""); } diff --git a/src/test/java/guitests/DeleteCommandTest.java b/src/test/java/guitests/DeleteCommandTest.java index 10c7b9e0dbea..801f9ee5b327 100644 --- a/src/test/java/guitests/DeleteCommandTest.java +++ b/src/test/java/guitests/DeleteCommandTest.java @@ -1,54 +1,79 @@ package guitests; import org.junit.Test; -import seedu.address.testutil.TestPerson; -import seedu.address.testutil.TestUtil; +import seedu.task.testutil.TestTaskList; +import seedu.todolist.model.task.Status; -import static org.junit.Assert.assertTrue; -import static seedu.address.logic.commands.DeleteCommand.MESSAGE_DELETE_PERSON_SUCCESS; +import static seedu.todolist.logic.commands.DeleteCommand.MESSAGE_DELETE_TASK_SUCCESS; -public class DeleteCommandTest extends AddressBookGuiTest { +//@@author A0138601M +public class DeleteCommandTest extends ToDoListGuiTest { @Test public void delete() { - //delete the first in the list - TestPerson[] currentList = td.getTypicalPersons(); - int targetIndex = 1; - assertDeleteSuccess(targetIndex, currentList); + TestTaskList currentList = new TestTaskList(td.getTypicalTasks()); + + //delete the first in the list of incomplete tasks + int[] targetIndexes = new int[]{1}; + assertDeleteSuccess(targetIndexes, currentList, Status.Type.Incomplete); - //delete the last in the list - currentList = TestUtil.removePersonFromList(currentList, targetIndex); - targetIndex = currentList.length; - assertDeleteSuccess(targetIndex, currentList); + //delete the last in the list of incomplete tasks + targetIndexes = new int[]{currentList.getIncompleteList().length}; + assertDeleteSuccess(targetIndexes, currentList, Status.Type.Incomplete); - //delete from the middle of the list - currentList = TestUtil.removePersonFromList(currentList, targetIndex); - targetIndex = currentList.length/2; - assertDeleteSuccess(targetIndex, currentList); + //delete from the middle of the list of incomplete tasks + targetIndexes = new int[]{currentList.getIncompleteList().length/2}; + assertDeleteSuccess(targetIndexes, currentList, Status.Type.Incomplete); + + //delete multiple of incomplete tasks + targetIndexes = new int[]{2,1}; + assertDeleteSuccess(targetIndexes, currentList, Status.Type.Incomplete); + + //delete completed task + targetIndexes = new int[]{1}; + assertDeleteSuccess(targetIndexes, currentList, Status.Type.Complete); + + //delete overdue task + targetIndexes = new int[]{1}; + assertDeleteSuccess(targetIndexes, currentList, Status.Type.Overdue); //invalid index - commandBox.runCommand("delete " + currentList.length + 1); - assertResultMessage("The person index provided is invalid"); + commandBox.runCommand("delete " + currentList.getIncompleteList().length + 1); + assertResultMessage("The task index provided is invalid"); } /** - * Runs the delete command to delete the person at specified index and confirms the result is correct. - * @param targetIndexOneIndexed e.g. to delete the first person in the list, 1 should be given as the target index. - * @param currentList A copy of the current list of persons (before deletion). + * Runs the delete command to delete incomplete tasks at the specified indices and confirms the result is correct. + * @param targetIndexes a list of indexes to be deleted + * @param currentList A copy of the current list of tasks (before deletion). */ - private void assertDeleteSuccess(int targetIndexOneIndexed, final TestPerson[] currentList) { - TestPerson personToDelete = currentList[targetIndexOneIndexed-1]; //-1 because array uses zero indexing - TestPerson[] expectedRemainder = TestUtil.removePersonFromList(currentList, targetIndexOneIndexed); - - commandBox.runCommand("delete " + targetIndexOneIndexed); - - //confirm the list now contains all previous persons except the deleted person - assertTrue(personListPanel.isListMatching(expectedRemainder)); + private void assertDeleteSuccess(int[] targetIndexes, TestTaskList currentList, Status.Type type) { + currentList.removeTasksFromList(targetIndexes, type); + taskListPanel.clickOnListTab(type); + commandBox.runCommand(getCommand(targetIndexes)); + + //confirm the delete task are no longer in the list + assertAllListMatching(currentList); //confirm the result message is correct - assertResultMessage(String.format(MESSAGE_DELETE_PERSON_SUCCESS, personToDelete)); + assertResultMessage(MESSAGE_DELETE_TASK_SUCCESS); + } + + /** + * Returns the command to be entered + */ + private String getCommand(int[] targetIndexes) { + StringBuilder builder = new StringBuilder(); + builder.append("delete "); + for (int i = 0; i < targetIndexes.length; i++) { + if (i == targetIndexes.length - 1) { + builder.append(targetIndexes[i]); + } else { + builder.append(targetIndexes[i] + ", "); + } + } + return builder.toString(); } - } diff --git a/src/test/java/guitests/DoneCommandTest.java b/src/test/java/guitests/DoneCommandTest.java new file mode 100644 index 000000000000..cfbd2df5b72d --- /dev/null +++ b/src/test/java/guitests/DoneCommandTest.java @@ -0,0 +1,83 @@ +package guitests; + +import org.junit.Test; + +import seedu.task.testutil.TestTaskList; +import seedu.todolist.model.task.Status; + +import static seedu.todolist.logic.commands.DoneCommand.MESSAGE_MARK_TASK_SUCCESS; + +//@@author A0138601M +public class DoneCommandTest extends ToDoListGuiTest { + + @Test + public void done() { + + TestTaskList currentList = new TestTaskList(td.getTypicalTasks()); + + //mark the first in the incomplete list + int[] targetIndexes = new int[]{1}; + assertDoneSuccess(targetIndexes, currentList, Status.Type.Incomplete); + + //mark the last in the incomplete list + targetIndexes = new int[]{currentList.getIncompleteList().length}; + assertDoneSuccess(targetIndexes, currentList, Status.Type.Incomplete); + + //mark from the middle of the incomplete list + targetIndexes = new int[]{currentList.getIncompleteList().length/2}; + assertDoneSuccess(targetIndexes, currentList, Status.Type.Incomplete); + + //mark multiple + targetIndexes = new int[]{1,2}; + assertDoneSuccess(targetIndexes, currentList, Status.Type.Incomplete); + + //mark overdue task + targetIndexes = new int[]{1}; + assertDoneSuccess(targetIndexes, currentList, Status.Type.Overdue); + + //mark completed task + taskListPanel.clickOnListTab(Status.Type.Complete); + commandBox.runCommand("done 1"); + assertResultMessage("This task is already completed!"); + + //invalid index from incomplete list + taskListPanel.clickOnListTab(Status.Type.Incomplete); + commandBox.runCommand("done " + currentList.getIncompleteList().length + 1); + assertResultMessage("The task index provided is invalid"); + + } + + /** + * Runs the done command to mark the task at specified index from incomplete list and confirms the result is correct. + * @param targetIndexOneIndexed e.g. to mark the first task in the list, 1 should be given as the target index. + * @param currentList A copy of the current list of tasks (before marking). + */ + private void assertDoneSuccess(int[] targetIndexes, final TestTaskList currentList, Status.Type type) { + currentList.markTasksFromList(targetIndexes, type); + taskListPanel.clickOnListTab(type); + commandBox.runCommand(getCommand(targetIndexes)); + + //confirm the mark task are now in completed list + assertAllListMatching(currentList); + + //confirm the result message is correct + assertResultMessage(MESSAGE_MARK_TASK_SUCCESS); + } + + /** + * Returns the command to be entered + */ + private String getCommand(int[] targetIndexes) { + StringBuilder builder = new StringBuilder(); + builder.append("done "); + for (int i = 0; i < targetIndexes.length; i++) { + if (i == targetIndexes.length - 1) { + builder.append(targetIndexes[i]); + } else { + builder.append(targetIndexes[i] + ", "); + } + } + return builder.toString(); + } + +} diff --git a/src/test/java/guitests/EditCommandTest.java b/src/test/java/guitests/EditCommandTest.java new file mode 100644 index 000000000000..990d7d7cb728 --- /dev/null +++ b/src/test/java/guitests/EditCommandTest.java @@ -0,0 +1,76 @@ +package guitests; + +import static org.junit.Assert.*; +import static seedu.todolist.logic.commands.EditCommand.MESSAGE_SUCCESS; +import static seedu.todolist.logic.commands.EditCommand.MESSAGE_USAGE; +import static seedu.todolist.commons.core.Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX; + +import org.junit.Test; + +import seedu.task.testutil.TestTask; +import seedu.task.testutil.TestTaskList; +import seedu.todolist.commons.exceptions.IllegalValueException; +import seedu.todolist.model.task.Name; +import seedu.todolist.model.task.Status; + +//@@author A0146682X +public class EditCommandTest extends ToDoListGuiTest { + + @Test + public void edit() { + + TestTaskList currentList = new TestTaskList(td.getTypicalTasks()); + + //edit the first in the list of incomplete tasks + int targetIndex = 1; + String partialCommand = "test task first"; + assertEditSuccess(targetIndex, partialCommand, currentList, true); + + //edit the last in the list of incomplete tasks + targetIndex = currentList.getIncompleteList().length; + partialCommand = "test task last"; + assertEditSuccess(targetIndex, partialCommand, currentList, true); + + //invalid index + targetIndex = (currentList.getIncompleteList().length + 1); + partialCommand = "test task to fail"; + assertEditSuccess(targetIndex, partialCommand, currentList, true); + } + + /** + * Runs the edit command to edit incomplete tasks at the specified indices and confirms the result is correct. + * @param targetIndexes a list of indexes to be edited + * @param currentList A copy of the current list of tasks (before edition). + */ + private void assertEditSuccess(int targetIndex, String partialCommand, TestTaskList currentList, boolean isFromIncompleteList) { + + int listLength = isFromIncompleteList ? currentList.getIncompleteList().length : currentList.getCompleteList().length; + + commandBox.runCommand(getCommand(targetIndex, partialCommand)); + if(targetIndex < 1 || targetIndex > listLength) assertResultMessage(MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + else { + + TestTask newTask = isFromIncompleteList ? currentList.getIncompleteList()[targetIndex-1] : currentList.getCompleteList()[targetIndex-1]; + try { + newTask.setName(new Name(partialCommand)); + } catch (IllegalValueException ive) { + System.out.println(MESSAGE_USAGE); + } + + assertTrue(taskListPanel.isListMatching(Status.Type.Incomplete, currentList.getIncompleteList())); + assertTrue(taskListPanel.isListMatching(Status.Type.Complete, currentList.getCompleteList())); + assertResultMessage(String.format(MESSAGE_SUCCESS, newTask)); + } + } + + /** + * Returns the command to be entered + */ + private String getCommand(int targetIndex, String string) { + StringBuilder builder = new StringBuilder(); + builder.append("edit "); + builder.append(targetIndex); + builder.append(" " + string); + return builder.toString(); + } +} diff --git a/src/test/java/guitests/FindCommandTest.java b/src/test/java/guitests/FindCommandTest.java index 441a6dbed666..b0bd42d48455 100644 --- a/src/test/java/guitests/FindCommandTest.java +++ b/src/test/java/guitests/FindCommandTest.java @@ -1,39 +1,141 @@ package guitests; import org.junit.Test; -import seedu.address.commons.core.Messages; -import seedu.address.testutil.TestPerson; + +import seedu.task.testutil.TestTask; +import seedu.task.testutil.TestTaskList; +import seedu.todolist.commons.core.Messages; +import seedu.todolist.logic.commands.FindCommand; +import seedu.todolist.model.task.Status; import static org.junit.Assert.assertTrue; -public class FindCommandTest extends AddressBookGuiTest { +//@@author A0153736B +public class FindCommandTest extends ToDoListGuiTest { @Test - public void find_nonEmptyList() { - assertFindResult("find Mark"); //no results - assertFindResult("find Meier", td.benson, td.daniel); //multiple results + public void findEitherAtNonEmptyList() { + //find non existent task + TestTaskList currentList = new TestTaskList(); + assertFindCommandSuccess("find either NonExistentTask", currentList); + + //find multiple tasks that contain the keyword entered + currentList = new TestTaskList (new TestTask[] {td.eventWithoutParameter, td.eventWithLocation, td.eventWithParameters}); + assertFindCommandSuccess("find either event", currentList); + + //find tasks that contain one or more keywords entered + currentList = new TestTaskList(new TestTask[] {td.deadlineWithoutTime, td.floatWithoutParameter, td.floatWithParameters}); + assertFindCommandSuccess("find either time float", currentList); //find after deleting one result commandBox.runCommand("delete 1"); - assertFindResult("find Meier",td.daniel); + currentList = new TestTaskList (new TestTask[] {td.eventWithoutParameter, td.eventWithLocation, td.eventWithParameters}); + assertFindCommandSuccess("find either Event", currentList); + + //find tasks from both incomplete and complete list + commandBox.runCommand("done 1"); + currentList.markTasksFromList(new int[]{1}, Status.Type.Incomplete); + assertFindCommandSuccess("find either Event", currentList); } @Test - public void find_emptyList(){ + public void findEitherAtEmptyList() { commandBox.runCommand("clear"); - assertFindResult("find Jean"); //no results + TestTaskList currentList = new TestTaskList(); + assertFindCommandSuccess("find either event", currentList); //no results + } + + @Test + public void findAllAtNonEmptyList() { + commandBox.runCommand(td.taskOneToTestFind.getAddCommand()); + commandBox.runCommand(td.taskTwoToTestFind.getAddCommand()); + commandBox.runCommand(td.taskThreeToTestFind.getAddCommand()); + + //find non existent task + TestTaskList currentList = new TestTaskList(); + assertFindCommandSuccess("find all three four five", currentList); + + //find tasks that contain all keywords entered + currentList = new TestTaskList(new TestTask[] {td.taskOneToTestFind, td.taskTwoToTestFind, + td.taskThreeToTestFind}); + assertFindCommandSuccess("find all two three", currentList); + + //find after deleting one result + commandBox.runCommand("delete 1"); + currentList = new TestTaskList (new TestTask[] {td.taskTwoToTestFind, td.taskThreeToTestFind}); + assertFindCommandSuccess("find all three two", currentList); + + //find tasks from both incomplete and complete list + commandBox.runCommand("done 1"); + currentList.markTasksFromList(new int[]{1}, Status.Type.Incomplete); + assertFindCommandSuccess("find all Two Three", currentList); + } + + @Test + public void findAllAtEmptyList() { + commandBox.runCommand("clear"); + TestTaskList currentList = new TestTaskList(); + assertFindCommandSuccess("find all Two Three", currentList); //no results + } + + @Test + public void findPhraseAtNonEmptyList() { + commandBox.runCommand(td.taskOneToTestFind.getAddCommand()); + commandBox.runCommand(td.taskTwoToTestFind.getAddCommand()); + commandBox.runCommand(td.taskThreeToTestFind.getAddCommand()); + + //find non existent task + TestTaskList currentList = new TestTaskList(); + assertFindCommandSuccess("find phrase three four five", currentList); + + //find tasks that contain the exact keywords entered + currentList = new TestTaskList(new TestTask[] {td.taskOneToTestFind}); + assertFindCommandSuccess("find phrase two three", currentList); + + //find after deleting one result + commandBox.runCommand("delete 1"); + currentList = new TestTaskList (new TestTask[] {td.taskTwoToTestFind, td.taskThreeToTestFind}); + assertFindCommandSuccess("find phrase One three", currentList); + + //find tasks from both incomplete and complete list + commandBox.runCommand("done 1"); + currentList.markTasksFromList(new int[]{1}, Status.Type.Incomplete); + assertFindCommandSuccess("find phrase one three", currentList); + } + + @Test + public void findPhraseAtEmptyList() { + commandBox.runCommand("clear"); + TestTaskList currentList = new TestTaskList(); + assertFindCommandSuccess("find phrase one three", currentList); //no results } @Test - public void find_invalidCommand_fail() { + public void enterInvalidCommand() { commandBox.runCommand("findgeorge"); assertResultMessage(Messages.MESSAGE_UNKNOWN_COMMAND); } + + @Test + public void enterEmptyKeywords() { + commandBox.runCommand("find phrase"); + assertResultMessage(FindCommand.MESSAGE_KEYWORDS_NOT_PROVIDED); + } + + @Test + public void enterInvalidFindtype() { + commandBox.runCommand("find something"); + assertResultMessage(FindCommand.MESSAGE_INVALID_FINDTYPE); + } - private void assertFindResult(String command, TestPerson... expectedHits ) { + private void assertFindCommandSuccess(String command, TestTaskList expectedList) { commandBox.runCommand(command); - assertListSize(expectedHits.length); - assertResultMessage(expectedHits.length + " persons listed!"); - assertTrue(personListPanel.isListMatching(expectedHits)); + assertTrue(taskListPanel.isListMatching(Status.Type.Incomplete, expectedList.getIncompleteList())); + assertTrue(taskListPanel.isListMatching(Status.Type.Complete, expectedList.getCompleteList())); + assertTrue(taskListPanel.isListMatching(Status.Type.Overdue, expectedList.getOverdueList())); + assertResultMessage(String.format(Messages.MESSAGE_INCOMPLETE_TASKS_LISTED_OVERVIEW, expectedList.getIncompleteList().length) + + String.format(Messages.MESSAGE_COMPLETED_TASKS_LISTED_OVERVIEW, expectedList.getCompleteList().length) + + String.format(Messages.MESSAGE_OVERDUE_TASKS_LISTED_OVERVIEW, expectedList.getOverdueList().length)); + } } diff --git a/src/test/java/guitests/GuiRobot.java b/src/test/java/guitests/GuiRobot.java index 44aa9edb48aa..7c76810b34fb 100644 --- a/src/test/java/guitests/GuiRobot.java +++ b/src/test/java/guitests/GuiRobot.java @@ -1,8 +1,9 @@ package guitests; import javafx.scene.input.KeyCodeCombination; +import seedu.task.testutil.TestUtil; + import org.testfx.api.FxRobot; -import seedu.address.testutil.TestUtil; /** * Robot used to simulate user actions on the GUI. diff --git a/src/test/java/guitests/HelpWindowTest.java b/src/test/java/guitests/HelpWindowTest.java index 258d9d628d80..00d84f7e47c6 100644 --- a/src/test/java/guitests/HelpWindowTest.java +++ b/src/test/java/guitests/HelpWindowTest.java @@ -1,22 +1,35 @@ package guitests; import guitests.guihandles.HelpWindowHandle; +import seedu.todolist.commons.core.Messages; +import seedu.todolist.logic.commands.HelpCommand; +import seedu.todolist.model.task.Status; + import org.junit.Test; import static org.junit.Assert.assertTrue; +import static seedu.todolist.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -public class HelpWindowTest extends AddressBookGuiTest { +public class HelpWindowTest extends ToDoListGuiTest { @Test public void openHelpWindow() { - personListPanel.clickOnListView(); + taskListPanel.clickOnListView(Status.Type.Incomplete); assertHelpWindowOpen(mainMenu.openHelpWindowUsingAccelerator()); assertHelpWindowOpen(mainMenu.openHelpWindowUsingMenu()); assertHelpWindowOpen(commandBox.runHelpCommand()); + + //invalid command word + commandBox.runCommand("help2"); + assertResultMessage(Messages.MESSAGE_UNKNOWN_COMMAND); + + //invalid command argument + commandBox.runCommand("help 2"); + assertResultMessage(String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE)); } diff --git a/src/test/java/guitests/ListCommandTest.java b/src/test/java/guitests/ListCommandTest.java new file mode 100644 index 000000000000..83f07182fe43 --- /dev/null +++ b/src/test/java/guitests/ListCommandTest.java @@ -0,0 +1,77 @@ +package guitests; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import seedu.task.testutil.TestTask; +import seedu.task.testutil.TestTaskList; +import seedu.todolist.commons.core.Messages; +import seedu.todolist.logic.commands.ListCommand; +import seedu.todolist.model.task.Status; +import seedu.todolist.model.task.TaskDate; + +//@@author A0153736B +public class ListCommandTest extends ToDoListGuiTest { + + @Test + public void listAll() { + TestTaskList currentList = new TestTaskList(td.getTypicalTasks()); + assertFindCommandSuccess("list", currentList); + } + + @Test + public void listToday() { + TestTaskList currentList = new TestTaskList(new TestTask[] {td.eventWithoutParameter, td.eventWithLocation}); + assertFindCommandSuccess("list today", currentList); + } + + @Test + public void listWeek() { + TestTaskList currentList = new TestTaskList(new TestTask[] {td.eventWithoutParameter, td.eventWithLocation}); + assertFindCommandSuccess("list week", currentList); + } + + @Test + public void listMonth() { + TestTaskList currentList = new TestTaskList(new TestTask[] {td.eventWithoutParameter, td.eventWithLocation}); + assertFindCommandSuccess("list month", currentList); + } + + @Test + public void listValidDate() { + TestTaskList currentList = new TestTaskList(new TestTask[] {td.deadlineWithLocation}); + assertFindCommandSuccess("list 28 jan 2017", currentList); + } + + @Test + public void listInvalidDate() { + commandBox.runCommand("list 32 oct 2016"); + assertResultMessage(TaskDate.MESSAGE_DATE_INVALID); + } + + @Test + public void listInvalidFilter() { + commandBox.runCommand("list tomorrow"); + assertResultMessage(ListCommand.MESSAGE_FILTER_INVALID); + } + + @Test + public void listInvalidCommand() { + commandBox.runCommand("listtoday"); + assertResultMessage(Messages.MESSAGE_UNKNOWN_COMMAND); + } + + private void assertFindCommandSuccess(String command, TestTaskList expectedList) { + commandBox.runCommand(command); + assertTrue(taskListPanel.isListMatching(Status.Type.Incomplete, expectedList.getIncompleteList())); + assertTrue(taskListPanel.isListMatching(Status.Type.Complete, expectedList.getCompleteList())); + assertTrue(taskListPanel.isListMatching(Status.Type.Overdue, expectedList.getOverdueList())); + if ("list".equals(command)) { + assertResultMessage(ListCommand.MESSAGE_ALLTASKS_SUCCESS); + } + else { + assertResultMessage(ListCommand.MESSAGE_FILTER_SUCCESS); + } + } +} diff --git a/src/test/java/guitests/RedoCommandTest.java b/src/test/java/guitests/RedoCommandTest.java new file mode 100644 index 000000000000..60293f875aaf --- /dev/null +++ b/src/test/java/guitests/RedoCommandTest.java @@ -0,0 +1,62 @@ +package guitests; + +import static org.junit.Assert.assertTrue; +import static seedu.todolist.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import org.junit.Test; + +import seedu.task.testutil.TestTask; +import seedu.task.testutil.TestTaskList; +import seedu.todolist.commons.core.Messages; +import seedu.todolist.logic.commands.RedoCommand; +import seedu.todolist.model.task.Status; + +//@@author A0153736B +public class RedoCommandTest extends ToDoListGuiTest { + + @Test + public void redo() { + //without any last undo operation + commandBox.runCommand("redo"); + assertResultMessage(RedoCommand.MESSAGE_WITHOUT_PREVIOUS_OPERATION); + + //redo an undo operation + TestTask taskToAdd = td.overdueDeadline; + TestTaskList currentList = new TestTaskList(td.getTypicalTasks()); + commandBox.runCommand(taskToAdd.getAddCommand()); + commandBox.runCommand("undo"); + currentList.addTasksToList(taskToAdd); + assertRedoCommandSuccess(currentList); + + //redo two undo operations consecutively + TestTask taskAfterEdit = td.upcomingEvent; + commandBox.runCommand(taskAfterEdit.getEditCommand(1)); + commandBox.runCommand("undo"); + commandBox.runCommand("undo"); + assertRedoCommandSuccess(currentList); + currentList.editTask(1, taskAfterEdit, true); + assertRedoCommandSuccess(currentList); + + //redo operation fails when apply delete operation after undo operation + commandBox.runCommand("undo"); + commandBox.runCommand("delete 1"); + commandBox.runCommand("redo"); + assertResultMessage(RedoCommand.MESSAGE_WITHOUT_PREVIOUS_OPERATION); + + //invalid command word + commandBox.runCommand("redo2"); + assertResultMessage(Messages.MESSAGE_UNKNOWN_COMMAND); + + //invalid command argument + commandBox.runCommand("redo 2"); + assertResultMessage(String.format(MESSAGE_INVALID_COMMAND_FORMAT, RedoCommand.MESSAGE_USAGE)); + } + + private void assertRedoCommandSuccess(TestTaskList expectedList) { + commandBox.runCommand("redo"); + assertTrue(taskListPanel.isListMatching(Status.Type.Incomplete, expectedList.getIncompleteList())); + assertTrue(taskListPanel.isListMatching(Status.Type.Complete, expectedList.getCompleteList())); + assertTrue(taskListPanel.isListMatching(Status.Type.Overdue, expectedList.getOverdueList())); + assertResultMessage(RedoCommand.MESSAGE_SUCCESS); + } +} diff --git a/src/test/java/guitests/SelectCommandTest.java b/src/test/java/guitests/SelectCommandTest.java deleted file mode 100644 index 5273552056ce..000000000000 --- a/src/test/java/guitests/SelectCommandTest.java +++ /dev/null @@ -1,58 +0,0 @@ -package guitests; - -import org.junit.Test; -import seedu.address.model.person.ReadOnlyPerson; - -import static org.junit.Assert.assertEquals; - -public class SelectCommandTest extends AddressBookGuiTest { - - - @Test - public void selectPerson_nonEmptyList() { - - assertSelectionInvalid(10); //invalid index - assertNoPersonSelected(); - - assertSelectionSuccess(1); //first person in the list - int personCount = td.getTypicalPersons().length; - assertSelectionSuccess(personCount); //last person in the list - int middleIndex = personCount / 2; - assertSelectionSuccess(middleIndex); //a person in the middle of the list - - assertSelectionInvalid(personCount + 1); //invalid index - assertPersonSelected(middleIndex); //assert previous selection remains - - /* Testing other invalid indexes such as -1 should be done when testing the SelectCommand */ - } - - @Test - public void selectPerson_emptyList(){ - commandBox.runCommand("clear"); - assertListSize(0); - assertSelectionInvalid(1); //invalid index - } - - private void assertSelectionInvalid(int index) { - commandBox.runCommand("select " + index); - assertResultMessage("The person index provided is invalid"); - } - - private void assertSelectionSuccess(int index) { - commandBox.runCommand("select " + index); - assertResultMessage("Selected Person: "+index); - assertPersonSelected(index); - } - - private void assertPersonSelected(int index) { - assertEquals(personListPanel.getSelectedPersons().size(), 1); - ReadOnlyPerson selectedPerson = personListPanel.getSelectedPersons().get(0); - assertEquals(personListPanel.getPerson(index-1), selectedPerson); - //TODO: confirm the correct page is loaded in the Browser Panel - } - - private void assertNoPersonSelected() { - assertEquals(personListPanel.getSelectedPersons().size(), 0); - } - -} diff --git a/src/test/java/guitests/AddressBookGuiTest.java b/src/test/java/guitests/ToDoListGuiTest.java similarity index 57% rename from src/test/java/guitests/AddressBookGuiTest.java rename to src/test/java/guitests/ToDoListGuiTest.java index b9cf9ca092eb..a627aecf5010 100644 --- a/src/test/java/guitests/AddressBookGuiTest.java +++ b/src/test/java/guitests/ToDoListGuiTest.java @@ -8,12 +8,15 @@ import org.junit.Rule; import org.junit.rules.TestName; import org.testfx.api.FxToolkit; -import seedu.address.TestApp; -import seedu.address.commons.core.EventsCenter; -import seedu.address.model.AddressBook; -import seedu.address.model.person.ReadOnlyPerson; -import seedu.address.testutil.TestUtil; -import seedu.address.testutil.TypicalTestPersons; + +import seedu.task.TestApp; +import seedu.task.testutil.TestTaskList; +import seedu.task.testutil.TestUtil; +import seedu.task.testutil.TypicalTestTasks; +import seedu.todolist.commons.core.EventsCenter; +import seedu.todolist.model.ToDoList; +import seedu.todolist.model.task.ReadOnlyTask; +import seedu.todolist.model.task.Status; import java.util.concurrent.TimeoutException; @@ -21,9 +24,9 @@ import static org.junit.Assert.assertTrue; /** - * A GUI Test class for AddressBook. + * A GUI Test class for ToDoList. */ -public abstract class AddressBookGuiTest { +public abstract class ToDoListGuiTest { /* The TestName Rule makes the current test name available inside test methods */ @Rule @@ -31,7 +34,7 @@ public abstract class AddressBookGuiTest { TestApp testApp; - protected TypicalTestPersons td = new TypicalTestPersons(); + protected TypicalTestTasks td = new TypicalTestTasks(); /* * Handles to GUI elements present at the start up are created in advance @@ -39,7 +42,7 @@ public abstract class AddressBookGuiTest { */ protected MainGuiHandle mainGui; protected MainMenuHandle mainMenu; - protected PersonListPanelHandle personListPanel; + protected TaskListPanelHandle taskListPanel; protected ResultDisplayHandle resultDisplay; protected CommandBoxHandle commandBox; private Stage stage; @@ -59,7 +62,7 @@ public void setup() throws Exception { FxToolkit.setupStage((stage) -> { mainGui = new MainGuiHandle(new GuiRobot(), stage); mainMenu = mainGui.getMainMenu(); - personListPanel = mainGui.getPersonListPanel(); + taskListPanel = mainGui.getTaskListPanel(); resultDisplay = mainGui.getResultDisplay(); commandBox = mainGui.getCommandBox(); this.stage = stage; @@ -75,9 +78,9 @@ public void setup() throws Exception { * Override this in child classes to set the initial local data. * Return null to use the data in the file specified in {@link #getDataFileLocation()} */ - protected AddressBook getInitialData() { - AddressBook ab = TestUtil.generateEmptyAddressBook(); - TypicalTestPersons.loadAddressBookWithSampleData(ab); + protected ToDoList getInitialData() { + ToDoList ab = TestUtil.generateEmptyToDoList(); + TypicalTestTasks.loadToDoListWithSampleData(ab); return ab; } @@ -95,18 +98,36 @@ public void cleanup() throws TimeoutException { } /** - * Asserts the person shown in the card is same as the given person + * Asserts the task shown in the card is same as the given task */ - public void assertMatching(ReadOnlyPerson person, PersonCardHandle card) { - assertTrue(TestUtil.compareCardAndPerson(card, person)); + protected void assertMatching(ReadOnlyTask task, TaskCardHandle card) { + assertTrue(TestUtil.compareCardAndTask(card, task)); + } + + protected void assertAllListMatching(TestTaskList currentList) { + assertTrue(taskListPanel.isListMatching(Status.Type.Incomplete, currentList.getIncompleteList())); + assertTrue(taskListPanel.isListMatching(Status.Type.Complete, currentList.getCompleteList())); + assertTrue(taskListPanel.isListMatching(Status.Type.Overdue, currentList.getOverdueList())); } /** - * Asserts the size of the person list is equal to the given number. + * Asserts the size of the incomplete task list is equal to the given number. */ - protected void assertListSize(int size) { - int numberOfPeople = personListPanel.getNumberOfPeople(); - assertEquals(size, numberOfPeople); + private void assertListSize(int size, Status.Type type) { + int numberOfTask = taskListPanel.getNumberOfTask(type); + assertEquals(size, numberOfTask); + } + + protected void assertIncompleteListSize(int size) { + assertListSize(size, Status.Type.Incomplete); + } + + protected void assertCompleteListSize(int size) { + assertListSize(size, Status.Type.Complete); + } + + protected void assertOverdueListSize(int size) { + assertListSize(size, Status.Type.Overdue); } /** diff --git a/src/test/java/guitests/UndoCommandTest.java b/src/test/java/guitests/UndoCommandTest.java new file mode 100644 index 000000000000..ca8e93c8e364 --- /dev/null +++ b/src/test/java/guitests/UndoCommandTest.java @@ -0,0 +1,74 @@ +package guitests; + +import static org.junit.Assert.assertTrue; +import static seedu.todolist.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import org.junit.Test; + +import seedu.task.testutil.TestTask; +import seedu.task.testutil.TestTaskList; +import seedu.todolist.commons.core.Messages; +import seedu.todolist.logic.commands.UndoCommand; +import seedu.todolist.model.task.Status; + +//@@author A0153736B +public class UndoCommandTest extends ToDoListGuiTest { + + @Test + public void undo() { + //without any last operation + commandBox.runCommand("undo"); + assertResultMessage(UndoCommand.MESSAGE_WITHOUT_PREVIOUS_OPERATION); + + //run add and edit operations + TestTask taskToAdd = td.overdueDeadline; + commandBox.runCommand(taskToAdd.getAddCommand()); + TestTask taskAfterEdit = td.upcomingEvent; + commandBox.runCommand(taskAfterEdit.getEditCommand(1)); + + //undo an edit operation + TestTaskList currentList = new TestTaskList(td.getTypicalTasks()); + currentList.addTasksToList(taskToAdd); + assertUndoCommandSuccess(currentList); + + //undo an add operation after undoing an edit operation + currentList = new TestTaskList(td.getTypicalTasks()); + assertUndoCommandSuccess(currentList); + + //run done, delete, invalid and clear operations + commandBox.runCommand("done 1"); + commandBox.runCommand("delete 1"); + commandBox.runCommand("delete " + (currentList.getIncompleteList().length+2)); + commandBox.runCommand("clear"); + + //undo a clear operation + currentList.markTasksFromList(new int[]{1}, Status.Type.Incomplete); + currentList.removeTasksFromList(new int[]{1}, Status.Type.Incomplete); + assertUndoCommandSuccess(currentList); + + //undo a delete operation + currentList = new TestTaskList(td.getTypicalTasks()); + currentList.markTasksFromList(new int[]{1}, Status.Type.Incomplete); + assertUndoCommandSuccess(currentList); + + //undo a done operation + currentList = new TestTaskList(td.getTypicalTasks()); + assertUndoCommandSuccess(currentList); + + //invalid command word + commandBox.runCommand("undo2"); + assertResultMessage(Messages.MESSAGE_UNKNOWN_COMMAND); + + //invalid command argument + commandBox.runCommand("undo 2"); + assertResultMessage(String.format(MESSAGE_INVALID_COMMAND_FORMAT, UndoCommand.MESSAGE_USAGE)); + } + + private void assertUndoCommandSuccess(TestTaskList expectedList) { + commandBox.runCommand("undo"); + assertTrue(taskListPanel.isListMatching(Status.Type.Incomplete, expectedList.getIncompleteList())); + assertTrue(taskListPanel.isListMatching(Status.Type.Complete, expectedList.getCompleteList())); + assertTrue(taskListPanel.isListMatching(Status.Type.Overdue, expectedList.getOverdueList())); + assertResultMessage(UndoCommand.MESSAGE_SUCCESS); + } +} diff --git a/src/test/java/guitests/guihandles/GuiHandle.java b/src/test/java/guitests/guihandles/GuiHandle.java index 5e7e0f6de911..c84b0925821a 100644 --- a/src/test/java/guitests/guihandles/GuiHandle.java +++ b/src/test/java/guitests/guihandles/GuiHandle.java @@ -7,8 +7,8 @@ import javafx.scene.input.KeyCode; import javafx.stage.Stage; import javafx.stage.Window; -import seedu.address.TestApp; -import seedu.address.commons.core.LogsCenter; +import seedu.task.TestApp; +import seedu.todolist.commons.core.LogsCenter; import java.util.logging.Logger; diff --git a/src/test/java/guitests/guihandles/MainGuiHandle.java b/src/test/java/guitests/guihandles/MainGuiHandle.java index 45802c5135c7..d3cb3ec6a078 100644 --- a/src/test/java/guitests/guihandles/MainGuiHandle.java +++ b/src/test/java/guitests/guihandles/MainGuiHandle.java @@ -2,7 +2,7 @@ import guitests.GuiRobot; import javafx.stage.Stage; -import seedu.address.TestApp; +import seedu.task.TestApp; /** * Provides a handle for the main GUI. @@ -13,10 +13,10 @@ public MainGuiHandle(GuiRobot guiRobot, Stage primaryStage) { super(guiRobot, primaryStage, TestApp.APP_TITLE); } - public PersonListPanelHandle getPersonListPanel() { - return new PersonListPanelHandle(guiRobot, primaryStage); + public TaskListPanelHandle getTaskListPanel() { + return new TaskListPanelHandle(guiRobot, primaryStage); } - + public ResultDisplayHandle getResultDisplay() { return new ResultDisplayHandle(guiRobot, primaryStage); } diff --git a/src/test/java/guitests/guihandles/MainMenuHandle.java b/src/test/java/guitests/guihandles/MainMenuHandle.java index 0aeb047a0e1d..6f9f078ce402 100644 --- a/src/test/java/guitests/guihandles/MainMenuHandle.java +++ b/src/test/java/guitests/guihandles/MainMenuHandle.java @@ -3,7 +3,7 @@ import guitests.GuiRobot; import javafx.scene.input.KeyCode; import javafx.stage.Stage; -import seedu.address.TestApp; +import seedu.task.TestApp; import java.util.Arrays; diff --git a/src/test/java/guitests/guihandles/PersonCardHandle.java b/src/test/java/guitests/guihandles/PersonCardHandle.java deleted file mode 100644 index fae22a45ae2f..000000000000 --- a/src/test/java/guitests/guihandles/PersonCardHandle.java +++ /dev/null @@ -1,63 +0,0 @@ -package guitests.guihandles; - -import guitests.GuiRobot; -import javafx.scene.Node; -import javafx.stage.Stage; -import seedu.address.model.person.ReadOnlyPerson; - -/** - * Provides a handle to a person card in the person list panel. - */ -public class PersonCardHandle extends GuiHandle { - private static final String NAME_FIELD_ID = "#name"; - private static final String ADDRESS_FIELD_ID = "#address"; - private static final String PHONE_FIELD_ID = "#phone"; - private static final String EMAIL_FIELD_ID = "#email"; - - private Node node; - - public PersonCardHandle(GuiRobot guiRobot, Stage primaryStage, Node node){ - super(guiRobot, primaryStage, null); - this.node = node; - } - - protected String getTextFromLabel(String fieldId) { - return getTextFromLabel(fieldId, node); - } - - public String getFullName() { - return getTextFromLabel(NAME_FIELD_ID); - } - - public String getAddress() { - return getTextFromLabel(ADDRESS_FIELD_ID); - } - - public String getPhone() { - return getTextFromLabel(PHONE_FIELD_ID); - } - - public String getEmail() { - return getTextFromLabel(EMAIL_FIELD_ID); - } - - public boolean isSamePerson(ReadOnlyPerson person){ - return getFullName().equals(person.getName().fullName) && getPhone().equals(person.getPhone().value) - && getEmail().equals(person.getEmail().value) && getAddress().equals(person.getAddress().value); - } - - @Override - public boolean equals(Object obj) { - if(obj instanceof PersonCardHandle) { - PersonCardHandle handle = (PersonCardHandle) obj; - return getFullName().equals(handle.getFullName()) - && getAddress().equals(handle.getAddress()); //TODO: compare the rest - } - return super.equals(obj); - } - - @Override - public String toString() { - return getFullName() + " " + getAddress(); - } -} diff --git a/src/test/java/guitests/guihandles/PersonListPanelHandle.java b/src/test/java/guitests/guihandles/PersonListPanelHandle.java deleted file mode 100644 index 3451992cf735..000000000000 --- a/src/test/java/guitests/guihandles/PersonListPanelHandle.java +++ /dev/null @@ -1,172 +0,0 @@ -package guitests.guihandles; - - -import guitests.GuiRobot; -import javafx.geometry.Point2D; -import javafx.scene.Node; -import javafx.scene.control.ListView; -import javafx.stage.Stage; -import seedu.address.TestApp; -import seedu.address.model.person.Person; -import seedu.address.model.person.ReadOnlyPerson; -import seedu.address.testutil.TestUtil; - -import java.util.List; -import java.util.Optional; -import java.util.Set; - -import static org.junit.Assert.assertTrue; - -/** - * Provides a handle for the panel containing the person list. - */ -public class PersonListPanelHandle extends GuiHandle { - - public static final int NOT_FOUND = -1; - public static final String CARD_PANE_ID = "#cardPane"; - - private static final String PERSON_LIST_VIEW_ID = "#personListView"; - - public PersonListPanelHandle(GuiRobot guiRobot, Stage primaryStage) { - super(guiRobot, primaryStage, TestApp.APP_TITLE); - } - - public List getSelectedPersons() { - ListView personList = getListView(); - return personList.getSelectionModel().getSelectedItems(); - } - - public ListView getListView() { - return (ListView) getNode(PERSON_LIST_VIEW_ID); - } - - /** - * Returns true if the list is showing the person details correctly and in correct order. - * @param persons A list of person in the correct order. - */ - public boolean isListMatching(ReadOnlyPerson... persons) { - return this.isListMatching(0, persons); - } - - /** - * Clicks on the ListView. - */ - public void clickOnListView() { - Point2D point= TestUtil.getScreenMidPoint(getListView()); - guiRobot.clickOn(point.getX(), point.getY()); - } - - /** - * Returns true if the {@code persons} appear as the sub list (in that order) at position {@code startPosition}. - */ - public boolean containsInOrder(int startPosition, ReadOnlyPerson... persons) { - List personsInList = getListView().getItems(); - - // Return false if the list in panel is too short to contain the given list - if (startPosition + persons.length > personsInList.size()){ - return false; - } - - // Return false if any of the persons doesn't match - for (int i = 0; i < persons.length; i++) { - if (!personsInList.get(startPosition + i).getName().fullName.equals(persons[i].getName().fullName)){ - return false; - } - } - - return true; - } - - /** - * Returns true if the list is showing the person details correctly and in correct order. - * @param startPosition The starting position of the sub list. - * @param persons A list of person in the correct order. - */ - public boolean isListMatching(int startPosition, ReadOnlyPerson... persons) throws IllegalArgumentException { - if (persons.length + startPosition != getListView().getItems().size()) { - throw new IllegalArgumentException("List size mismatched\n" + - "Expected " + (getListView().getItems().size() - 1) + " persons"); - } - assertTrue(this.containsInOrder(startPosition, persons)); - for (int i = 0; i < persons.length; i++) { - final int scrollTo = i + startPosition; - guiRobot.interact(() -> getListView().scrollTo(scrollTo)); - guiRobot.sleep(200); - if (!TestUtil.compareCardAndPerson(getPersonCardHandle(startPosition + i), persons[i])) { - return false; - } - } - return true; - } - - - public PersonCardHandle navigateToPerson(String name) { - guiRobot.sleep(500); //Allow a bit of time for the list to be updated - final Optional person = getListView().getItems().stream().filter(p -> p.getName().fullName.equals(name)).findAny(); - if (!person.isPresent()) { - throw new IllegalStateException("Name not found: " + name); - } - - return navigateToPerson(person.get()); - } - - /** - * Navigates the listview to display and select the person. - */ - public PersonCardHandle navigateToPerson(ReadOnlyPerson person) { - int index = getPersonIndex(person); - - guiRobot.interact(() -> { - getListView().scrollTo(index); - guiRobot.sleep(150); - getListView().getSelectionModel().select(index); - }); - guiRobot.sleep(100); - return getPersonCardHandle(person); - } - - - /** - * Returns the position of the person given, {@code NOT_FOUND} if not found in the list. - */ - public int getPersonIndex(ReadOnlyPerson targetPerson) { - List personsInList = getListView().getItems(); - for (int i = 0; i < personsInList.size(); i++) { - if(personsInList.get(i).getName().equals(targetPerson.getName())){ - return i; - } - } - return NOT_FOUND; - } - - /** - * Gets a person from the list by index - */ - public ReadOnlyPerson getPerson(int index) { - return getListView().getItems().get(index); - } - - public PersonCardHandle getPersonCardHandle(int index) { - return getPersonCardHandle(new Person(getListView().getItems().get(index))); - } - - public PersonCardHandle getPersonCardHandle(ReadOnlyPerson person) { - Set nodes = getAllCardNodes(); - Optional personCardNode = nodes.stream() - .filter(n -> new PersonCardHandle(guiRobot, primaryStage, n).isSamePerson(person)) - .findFirst(); - if (personCardNode.isPresent()) { - return new PersonCardHandle(guiRobot, primaryStage, personCardNode.get()); - } else { - return null; - } - } - - protected Set getAllCardNodes() { - return guiRobot.lookup(CARD_PANE_ID).queryAll(); - } - - public int getNumberOfPeople() { - return getListView().getItems().size(); - } -} diff --git a/src/test/java/guitests/guihandles/ResultDisplayHandle.java b/src/test/java/guitests/guihandles/ResultDisplayHandle.java index 110b4682b184..b06a4d7bc6a8 100644 --- a/src/test/java/guitests/guihandles/ResultDisplayHandle.java +++ b/src/test/java/guitests/guihandles/ResultDisplayHandle.java @@ -3,7 +3,7 @@ import guitests.GuiRobot; import javafx.scene.control.TextArea; import javafx.stage.Stage; -import seedu.address.TestApp; +import seedu.task.TestApp; /** * A handler for the ResultDisplay of the UI diff --git a/src/test/java/guitests/guihandles/TaskCardHandle.java b/src/test/java/guitests/guihandles/TaskCardHandle.java new file mode 100644 index 000000000000..e40367364275 --- /dev/null +++ b/src/test/java/guitests/guihandles/TaskCardHandle.java @@ -0,0 +1,46 @@ +package guitests.guihandles; + +import guitests.GuiRobot; +import javafx.scene.Node; +import javafx.stage.Stage; +import seedu.todolist.model.task.ReadOnlyTask; + +/** + * Provides a handle to a task card in the task list panel. + */ +public class TaskCardHandle extends GuiHandle { + private static final String NAME_FIELD_ID = "#name"; + + private Node node; + + public TaskCardHandle(GuiRobot guiRobot, Stage primaryStage, Node node){ + super(guiRobot, primaryStage, null); + this.node = node; + } + + protected String getTextFromLabel(String fieldId) { + return getTextFromLabel(fieldId, node); + } + + public String getFullName() { + return getTextFromLabel(NAME_FIELD_ID); + } + + public boolean isSameTask(ReadOnlyTask task){ + return getFullName().equals(task.getName().fullName); + } + + @Override + public boolean equals(Object obj) { + if(obj instanceof TaskCardHandle) { + TaskCardHandle handle = (TaskCardHandle) obj; + return getFullName().equals(handle.getFullName()); //TODO: compare the rest + } + return super.equals(obj); + } + + @Override + public String toString() { + return getFullName(); + } +} diff --git a/src/test/java/guitests/guihandles/TaskListPanelHandle.java b/src/test/java/guitests/guihandles/TaskListPanelHandle.java new file mode 100644 index 000000000000..f67f5a801d90 --- /dev/null +++ b/src/test/java/guitests/guihandles/TaskListPanelHandle.java @@ -0,0 +1,246 @@ +package guitests.guihandles; + + +import guitests.GuiRobot; +import javafx.geometry.Point2D; +import javafx.scene.Node; +import javafx.scene.control.ListView; +import javafx.stage.Stage; +import seedu.task.TestApp; +import seedu.task.testutil.TestTask; +import seedu.task.testutil.TestUtil; +import seedu.todolist.model.task.ReadOnlyTask; +import seedu.todolist.model.task.Status; +import seedu.todolist.model.task.Task; + +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import static org.junit.Assert.assertTrue; + +/** + * Provides a handle for the panel containing the task list. + */ +public class TaskListPanelHandle extends GuiHandle { + + public static final int NOT_FOUND = -1; + public static final String CARD_PANE_ID = "#cardPane"; + + private static final String LIST_VIEW_ID_INCOMPLETE = "#taskListView"; + private static final String LIST_VIEW_ID_COMPLETE = "#completeTaskListView"; + private static final String LIST_VIEW_ID_OVERDUE = "#overdueTaskListView"; + + private static final String TAB_ID_INCOMPLETE = "#incompleteTab"; + private static final String TAB_ID_COMPLETE = "#completedTab"; + private static final String TAB_ID_OVERDUE = "#overdueTab"; + + public TaskListPanelHandle(GuiRobot guiRobot, Stage primaryStage) { + super(guiRobot, primaryStage, TestApp.APP_TITLE); + } + + public List getSelectedTasks(Status.Type type) { + + ListView taskList = getListView(type); + return taskList.getSelectionModel().getSelectedItems(); + } + + //@@author A0138601M + /** + * Get the list view based on the type of task it is showing + */ + public ListView getListView(Status.Type type) { + switch (type) { + + case Complete : + return getCompleteListView(); + + case Incomplete : + return getIncompleteListView(); + + case Overdue : + return getOverdueListView(); + + default : + assert false : "Type must be either Complete, Incomplete or Overdue"; + return null; + + } + } + + private ListView getIncompleteListView() { + return (ListView) getNode(LIST_VIEW_ID_INCOMPLETE); + } + + private ListView getCompleteListView() { + return (ListView) getNode(LIST_VIEW_ID_COMPLETE); + } + + private ListView getOverdueListView() { + return (ListView) getNode(LIST_VIEW_ID_OVERDUE); + } + + /** + * Get the list tab based on the type of task it is showing + */ + public Node getListTab(Status.Type type) { + switch (type) { + + case Complete : + return getCompleteListTab(); + + case Incomplete : + return getIncompleteListTab(); + + case Overdue : + return getOverdueListTab(); + + default : + assert false : "Type must be either Complete, Incomplete or Overdue"; + return null; + } + } + + private Node getIncompleteListTab() { + return getNode(TAB_ID_INCOMPLETE); + } + + private Node getCompleteListTab() { + return getNode(TAB_ID_COMPLETE); + } + + private Node getOverdueListTab() { + return getNode(TAB_ID_OVERDUE); + } + + //@@author + /** + * Clicks on the ListView. + */ + public void clickOnListView(Status.Type type) { + Point2D point = TestUtil.getScreenMidPoint(getListView(type)); + guiRobot.clickOn(point.getX(), point.getY()); + } + + /** + * Clicks on the list tab. + */ + public void clickOnListTab(Status.Type type) { + Point2D point = TestUtil.getScreenMidPoint(getListTab(type)); + guiRobot.clickOn(point.getX(), point.getY()); + } + + /** + * Returns true if the {@code tasks} appear as the sub list (in that order). + */ + public boolean containsInOrder(Status.Type type, ReadOnlyTask... tasks) { + List tasksInList = getListView(type).getItems(); + + // Return false if the list in panel is too short to contain the given list + if (tasks.length > tasksInList.size()){ + return false; + } + + // Return false if any of the tasks doesn't match + for (int i = 0; i < tasks.length; i++) { + if (!tasksInList.get(i).equals(tasks[i])){ + return false; + } + } + + return true; + } + + /** + * Returns true if the list is showing the task details correctly and in correct order. + * @param tasks A list of task in the correct order. + */ + public boolean isListMatching(Status.Type type, ReadOnlyTask... tasks) throws IllegalArgumentException { + if (tasks.length != getListView(type).getItems().size()) { + throw new IllegalArgumentException("List size mismatched\n" + + "Expected " + (getListView(type).getItems().size() - 1) + " tasks" + tasks.length); + } + assertTrue(this.containsInOrder(type, tasks)); + for (int i = 0; i < tasks.length; i++) { + final int scrollTo = i; + guiRobot.interact(() -> getListView(type).scrollTo(scrollTo)); + guiRobot.sleep(200); + if (!TestUtil.compareCardAndTask(getTaskCardHandle(i, type), tasks[i])) { + return false; + } + } + return true; + } + + public TaskCardHandle navigateToTask(TestTask taskToFind) { + guiRobot.sleep(500); //Allow a bit of time for the list to be updated + Status.Type type = taskToFind.getStatus().getType(); + clickOnListTab(type); + final Optional task = getListView(type).getItems().stream().filter(p -> p.equals(taskToFind)).findAny(); + if (!task.isPresent()) { + throw new IllegalStateException("Task not found: " + taskToFind.getName().fullName); + } + + return navigateToTask(task.get(), type); + } + + /** + * Navigates the listview to display and select the task. + */ + public TaskCardHandle navigateToTask(ReadOnlyTask task, Status.Type type) { + int index = getTaskIndex(task, type); + + guiRobot.interact(() -> { + getListView(type).scrollTo(index); + guiRobot.sleep(150); + getListView(type).getSelectionModel().select(index); + }); + guiRobot.sleep(100); + return getTaskCardHandle(task); + } + + + /** + * Returns the position of the task given, {@code NOT_FOUND} if not found in the list. + */ + public int getTaskIndex(ReadOnlyTask targetTask, Status.Type type) { + List tasksInList = getListView(type).getItems(); + for (int i = 0; i < tasksInList.size(); i++) { + if(tasksInList.get(i).getName().equals(targetTask.getName())){ + return i; + } + } + return NOT_FOUND; + } + + /** + * Gets a task from the list by index + */ + public ReadOnlyTask getTask(int index, Status.Type type) { + return getListView(type).getItems().get(index); + } + + public TaskCardHandle getTaskCardHandle(int index, Status.Type type) { + return getTaskCardHandle(new Task(getListView(type).getItems().get(index))); + } + + public TaskCardHandle getTaskCardHandle(ReadOnlyTask task) { + Set nodes = getAllCardNodes(); + Optional taskCardNode = nodes.stream() + .filter(n -> new TaskCardHandle(guiRobot, primaryStage, n).isSameTask(task)) + .findFirst(); + if (taskCardNode.isPresent()) { + return new TaskCardHandle(guiRobot, primaryStage, taskCardNode.get()); + } else { + return null; + } + } + + protected Set getAllCardNodes() { + return guiRobot.lookup(CARD_PANE_ID).queryAll(); + } + + public int getNumberOfTask(Status.Type type) { + return getListView(type).getItems().size(); + } +} diff --git a/src/test/java/seedu/address/logic/LogicManagerTest.java b/src/test/java/seedu/address/logic/LogicManagerTest.java deleted file mode 100644 index c469fcef8ba8..000000000000 --- a/src/test/java/seedu/address/logic/LogicManagerTest.java +++ /dev/null @@ -1,512 +0,0 @@ -package seedu.address.logic; - -import com.google.common.eventbus.Subscribe; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; -import seedu.address.commons.core.EventsCenter; -import seedu.address.logic.commands.*; -import seedu.address.commons.events.ui.JumpToListRequestEvent; -import seedu.address.commons.events.ui.ShowHelpEvent; -import seedu.address.commons.events.model.AddressBookChangedEvent; -import seedu.address.model.AddressBook; -import seedu.address.model.Model; -import seedu.address.model.ModelManager; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.*; -import seedu.address.model.tag.Tag; -import seedu.address.model.tag.UniqueTagList; -import seedu.address.storage.StorageManager; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static seedu.address.commons.core.Messages.*; - -public class LogicManagerTest { - - /** - * See https://github.com/junit-team/junit4/wiki/rules#temporaryfolder-rule - */ - @Rule - public TemporaryFolder saveFolder = new TemporaryFolder(); - - private Model model; - private Logic logic; - - //These are for checking the correctness of the events raised - private ReadOnlyAddressBook latestSavedAddressBook; - private boolean helpShown; - private int targetedJumpIndex; - - @Subscribe - private void handleLocalModelChangedEvent(AddressBookChangedEvent abce) { - latestSavedAddressBook = new AddressBook(abce.data); - } - - @Subscribe - private void handleShowHelpEvent(ShowHelpEvent she) { - helpShown = true; - } - - @Subscribe - private void handleJumpToListRequestEvent(JumpToListRequestEvent je) { - targetedJumpIndex = je.targetIndex; - } - - @Before - public void setup() { - model = new ModelManager(); - String tempAddressBookFile = saveFolder.getRoot().getPath() + "TempAddressBook.xml"; - String tempPreferencesFile = saveFolder.getRoot().getPath() + "TempPreferences.json"; - logic = new LogicManager(model, new StorageManager(tempAddressBookFile, tempPreferencesFile)); - EventsCenter.getInstance().registerHandler(this); - - latestSavedAddressBook = new AddressBook(model.getAddressBook()); // last saved assumed to be up to date before. - helpShown = false; - targetedJumpIndex = -1; // non yet - } - - @After - public void teardown() { - EventsCenter.clearSubscribers(); - } - - @Test - public void execute_invalid() throws Exception { - String invalidCommand = " "; - assertCommandBehavior(invalidCommand, - String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE)); - } - - /** - * Executes the command and confirms that the result message is correct. - * Both the 'address book' and the 'last shown list' are expected to be empty. - * @see #assertCommandBehavior(String, String, ReadOnlyAddressBook, List) - */ - private void assertCommandBehavior(String inputCommand, String expectedMessage) throws Exception { - assertCommandBehavior(inputCommand, expectedMessage, new AddressBook(), Collections.emptyList()); - } - - /** - * Executes the command and confirms that the result message is correct and - * also confirms that the following three parts of the LogicManager object's state are as expected:
- * - the internal address book data are same as those in the {@code expectedAddressBook}
- * - the backing list shown by UI matches the {@code shownList}
- * - {@code expectedAddressBook} was saved to the storage file.
- */ - private void assertCommandBehavior(String inputCommand, String expectedMessage, - ReadOnlyAddressBook expectedAddressBook, - List expectedShownList) throws Exception { - - //Execute the command - CommandResult result = logic.execute(inputCommand); - - //Confirm the ui display elements should contain the right data - assertEquals(expectedMessage, result.feedbackToUser); - assertEquals(expectedShownList, model.getFilteredPersonList()); - - //Confirm the state of data (saved and in-memory) is as expected - assertEquals(expectedAddressBook, model.getAddressBook()); - assertEquals(expectedAddressBook, latestSavedAddressBook); - } - - - @Test - public void execute_unknownCommandWord() throws Exception { - String unknownCommand = "uicfhmowqewca"; - assertCommandBehavior(unknownCommand, MESSAGE_UNKNOWN_COMMAND); - } - - @Test - public void execute_help() throws Exception { - assertCommandBehavior("help", HelpCommand.SHOWING_HELP_MESSAGE); - assertTrue(helpShown); - } - - @Test - public void execute_exit() throws Exception { - assertCommandBehavior("exit", ExitCommand.MESSAGE_EXIT_ACKNOWLEDGEMENT); - } - - @Test - public void execute_clear() throws Exception { - TestDataHelper helper = new TestDataHelper(); - model.addPerson(helper.generatePerson(1)); - model.addPerson(helper.generatePerson(2)); - model.addPerson(helper.generatePerson(3)); - - assertCommandBehavior("clear", ClearCommand.MESSAGE_SUCCESS, new AddressBook(), Collections.emptyList()); - } - - - @Test - public void execute_add_invalidArgsFormat() throws Exception { - String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE); - assertCommandBehavior( - "add wrong args wrong args", expectedMessage); - assertCommandBehavior( - "add Valid Name 12345 e/valid@email.butNoPhonePrefix a/valid, address", expectedMessage); - assertCommandBehavior( - "add Valid Name p/12345 valid@email.butNoPrefix a/valid, address", expectedMessage); - assertCommandBehavior( - "add Valid Name p/12345 e/valid@email.butNoAddressPrefix valid, address", expectedMessage); - } - - @Test - public void execute_add_invalidPersonData() throws Exception { - assertCommandBehavior( - "add []\\[;] p/12345 e/valid@e.mail a/valid, address", Name.MESSAGE_NAME_CONSTRAINTS); - assertCommandBehavior( - "add Valid Name p/not_numbers e/valid@e.mail a/valid, address", Phone.MESSAGE_PHONE_CONSTRAINTS); - assertCommandBehavior( - "add Valid Name p/12345 e/notAnEmail a/valid, address", Email.MESSAGE_EMAIL_CONSTRAINTS); - assertCommandBehavior( - "add Valid Name p/12345 e/valid@e.mail a/valid, address t/invalid_-[.tag", Tag.MESSAGE_TAG_CONSTRAINTS); - - } - - @Test - public void execute_add_successful() throws Exception { - // setup expectations - TestDataHelper helper = new TestDataHelper(); - Person toBeAdded = helper.adam(); - AddressBook expectedAB = new AddressBook(); - expectedAB.addPerson(toBeAdded); - - // execute command and verify result - assertCommandBehavior(helper.generateAddCommand(toBeAdded), - String.format(AddCommand.MESSAGE_SUCCESS, toBeAdded), - expectedAB, - expectedAB.getPersonList()); - - } - - @Test - public void execute_addDuplicate_notAllowed() throws Exception { - // setup expectations - TestDataHelper helper = new TestDataHelper(); - Person toBeAdded = helper.adam(); - AddressBook expectedAB = new AddressBook(); - expectedAB.addPerson(toBeAdded); - - // setup starting state - model.addPerson(toBeAdded); // person already in internal address book - - // execute command and verify result - assertCommandBehavior( - helper.generateAddCommand(toBeAdded), - AddCommand.MESSAGE_DUPLICATE_PERSON, - expectedAB, - expectedAB.getPersonList()); - - } - - - @Test - public void execute_list_showsAllPersons() throws Exception { - // prepare expectations - TestDataHelper helper = new TestDataHelper(); - AddressBook expectedAB = helper.generateAddressBook(2); - List expectedList = expectedAB.getPersonList(); - - // prepare address book state - helper.addToModel(model, 2); - - assertCommandBehavior("list", - ListCommand.MESSAGE_SUCCESS, - expectedAB, - expectedList); - } - - - /** - * Confirms the 'invalid argument index number behaviour' for the given command - * targeting a single person in the shown list, using visible index. - * @param commandWord to test assuming it targets a single person in the last shown list based on visible index. - */ - private void assertIncorrectIndexFormatBehaviorForCommand(String commandWord, String expectedMessage) throws Exception { - assertCommandBehavior(commandWord , expectedMessage); //index missing - assertCommandBehavior(commandWord + " +1", expectedMessage); //index should be unsigned - assertCommandBehavior(commandWord + " -1", expectedMessage); //index should be unsigned - assertCommandBehavior(commandWord + " 0", expectedMessage); //index cannot be 0 - assertCommandBehavior(commandWord + " not_a_number", expectedMessage); - } - - /** - * Confirms the 'invalid argument index number behaviour' for the given command - * targeting a single person in the shown list, using visible index. - * @param commandWord to test assuming it targets a single person in the last shown list based on visible index. - */ - private void assertIndexNotFoundBehaviorForCommand(String commandWord) throws Exception { - String expectedMessage = MESSAGE_INVALID_PERSON_DISPLAYED_INDEX; - TestDataHelper helper = new TestDataHelper(); - List personList = helper.generatePersonList(2); - - // set AB state to 2 persons - model.resetData(new AddressBook()); - for (Person p : personList) { - model.addPerson(p); - } - - assertCommandBehavior(commandWord + " 3", expectedMessage, model.getAddressBook(), personList); - } - - @Test - public void execute_selectInvalidArgsFormat_errorMessageShown() throws Exception { - String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, SelectCommand.MESSAGE_USAGE); - assertIncorrectIndexFormatBehaviorForCommand("select", expectedMessage); - } - - @Test - public void execute_selectIndexNotFound_errorMessageShown() throws Exception { - assertIndexNotFoundBehaviorForCommand("select"); - } - - @Test - public void execute_select_jumpsToCorrectPerson() throws Exception { - TestDataHelper helper = new TestDataHelper(); - List threePersons = helper.generatePersonList(3); - - AddressBook expectedAB = helper.generateAddressBook(threePersons); - helper.addToModel(model, threePersons); - - assertCommandBehavior("select 2", - String.format(SelectCommand.MESSAGE_SELECT_PERSON_SUCCESS, 2), - expectedAB, - expectedAB.getPersonList()); - assertEquals(1, targetedJumpIndex); - assertEquals(model.getFilteredPersonList().get(1), threePersons.get(1)); - } - - - @Test - public void execute_deleteInvalidArgsFormat_errorMessageShown() throws Exception { - String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE); - assertIncorrectIndexFormatBehaviorForCommand("delete", expectedMessage); - } - - @Test - public void execute_deleteIndexNotFound_errorMessageShown() throws Exception { - assertIndexNotFoundBehaviorForCommand("delete"); - } - - @Test - public void execute_delete_removesCorrectPerson() throws Exception { - TestDataHelper helper = new TestDataHelper(); - List threePersons = helper.generatePersonList(3); - - AddressBook expectedAB = helper.generateAddressBook(threePersons); - expectedAB.removePerson(threePersons.get(1)); - helper.addToModel(model, threePersons); - - assertCommandBehavior("delete 2", - String.format(DeleteCommand.MESSAGE_DELETE_PERSON_SUCCESS, threePersons.get(1)), - expectedAB, - expectedAB.getPersonList()); - } - - - @Test - public void execute_find_invalidArgsFormat() throws Exception { - String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE); - assertCommandBehavior("find ", expectedMessage); - } - - @Test - public void execute_find_onlyMatchesFullWordsInNames() throws Exception { - TestDataHelper helper = new TestDataHelper(); - Person pTarget1 = helper.generatePersonWithName("bla bla KEY bla"); - Person pTarget2 = helper.generatePersonWithName("bla KEY bla bceofeia"); - Person p1 = helper.generatePersonWithName("KE Y"); - Person p2 = helper.generatePersonWithName("KEYKEYKEY sduauo"); - - List fourPersons = helper.generatePersonList(p1, pTarget1, p2, pTarget2); - AddressBook expectedAB = helper.generateAddressBook(fourPersons); - List expectedList = helper.generatePersonList(pTarget1, pTarget2); - helper.addToModel(model, fourPersons); - - assertCommandBehavior("find KEY", - Command.getMessageForPersonListShownSummary(expectedList.size()), - expectedAB, - expectedList); - } - - @Test - public void execute_find_isNotCaseSensitive() throws Exception { - TestDataHelper helper = new TestDataHelper(); - Person p1 = helper.generatePersonWithName("bla bla KEY bla"); - Person p2 = helper.generatePersonWithName("bla KEY bla bceofeia"); - Person p3 = helper.generatePersonWithName("key key"); - Person p4 = helper.generatePersonWithName("KEy sduauo"); - - List fourPersons = helper.generatePersonList(p3, p1, p4, p2); - AddressBook expectedAB = helper.generateAddressBook(fourPersons); - List expectedList = fourPersons; - helper.addToModel(model, fourPersons); - - assertCommandBehavior("find KEY", - Command.getMessageForPersonListShownSummary(expectedList.size()), - expectedAB, - expectedList); - } - - @Test - public void execute_find_matchesIfAnyKeywordPresent() throws Exception { - TestDataHelper helper = new TestDataHelper(); - Person pTarget1 = helper.generatePersonWithName("bla bla KEY bla"); - Person pTarget2 = helper.generatePersonWithName("bla rAnDoM bla bceofeia"); - Person pTarget3 = helper.generatePersonWithName("key key"); - Person p1 = helper.generatePersonWithName("sduauo"); - - List fourPersons = helper.generatePersonList(pTarget1, p1, pTarget2, pTarget3); - AddressBook expectedAB = helper.generateAddressBook(fourPersons); - List expectedList = helper.generatePersonList(pTarget1, pTarget2, pTarget3); - helper.addToModel(model, fourPersons); - - assertCommandBehavior("find key rAnDoM", - Command.getMessageForPersonListShownSummary(expectedList.size()), - expectedAB, - expectedList); - } - - - /** - * A utility class to generate test data. - */ - class TestDataHelper{ - - Person adam() throws Exception { - Name name = new Name("Adam Brown"); - Phone privatePhone = new Phone("111111"); - Email email = new Email("adam@gmail.com"); - Address privateAddress = new Address("111, alpha street"); - Tag tag1 = new Tag("tag1"); - Tag tag2 = new Tag("tag2"); - UniqueTagList tags = new UniqueTagList(tag1, tag2); - return new Person(name, privatePhone, email, privateAddress, tags); - } - - /** - * Generates a valid person using the given seed. - * Running this function with the same parameter values guarantees the returned person will have the same state. - * Each unique seed will generate a unique Person object. - * - * @param seed used to generate the person data field values - */ - Person generatePerson(int seed) throws Exception { - return new Person( - new Name("Person " + seed), - new Phone("" + Math.abs(seed)), - new Email(seed + "@email"), - new Address("House of " + seed), - new UniqueTagList(new Tag("tag" + Math.abs(seed)), new Tag("tag" + Math.abs(seed + 1))) - ); - } - - /** Generates the correct add command based on the person given */ - String generateAddCommand(Person p) { - StringBuffer cmd = new StringBuffer(); - - cmd.append("add "); - - cmd.append(p.getName().toString()); - cmd.append(" p/").append(p.getPhone()); - cmd.append(" e/").append(p.getEmail()); - cmd.append(" a/").append(p.getAddress()); - - UniqueTagList tags = p.getTags(); - for(Tag t: tags){ - cmd.append(" t/").append(t.tagName); - } - - return cmd.toString(); - } - - /** - * Generates an AddressBook with auto-generated persons. - */ - AddressBook generateAddressBook(int numGenerated) throws Exception{ - AddressBook addressBook = new AddressBook(); - addToAddressBook(addressBook, numGenerated); - return addressBook; - } - - /** - * Generates an AddressBook based on the list of Persons given. - */ - AddressBook generateAddressBook(List persons) throws Exception{ - AddressBook addressBook = new AddressBook(); - addToAddressBook(addressBook, persons); - return addressBook; - } - - /** - * Adds auto-generated Person objects to the given AddressBook - * @param addressBook The AddressBook to which the Persons will be added - */ - void addToAddressBook(AddressBook addressBook, int numGenerated) throws Exception{ - addToAddressBook(addressBook, generatePersonList(numGenerated)); - } - - /** - * Adds the given list of Persons to the given AddressBook - */ - void addToAddressBook(AddressBook addressBook, List personsToAdd) throws Exception{ - for(Person p: personsToAdd){ - addressBook.addPerson(p); - } - } - - /** - * Adds auto-generated Person objects to the given model - * @param model The model to which the Persons will be added - */ - void addToModel(Model model, int numGenerated) throws Exception{ - addToModel(model, generatePersonList(numGenerated)); - } - - /** - * Adds the given list of Persons to the given model - */ - void addToModel(Model model, List personsToAdd) throws Exception{ - for(Person p: personsToAdd){ - model.addPerson(p); - } - } - - /** - * Generates a list of Persons based on the flags. - */ - List generatePersonList(int numGenerated) throws Exception{ - List persons = new ArrayList<>(); - for(int i = 1; i <= numGenerated; i++){ - persons.add(generatePerson(i)); - } - return persons; - } - - List generatePersonList(Person... persons) { - return Arrays.asList(persons); - } - - /** - * Generates a Person object with given name. Other fields will have some dummy values. - */ - Person generatePersonWithName(String name) throws Exception { - return new Person( - new Name(name), - new Phone("1"), - new Email("1@email"), - new Address("House of 1"), - new UniqueTagList(new Tag("tag")) - ); - } - } -} diff --git a/src/test/java/seedu/address/storage/XmlAddressBookStorageTest.java b/src/test/java/seedu/address/storage/XmlAddressBookStorageTest.java deleted file mode 100644 index 650bd692e50a..000000000000 --- a/src/test/java/seedu/address/storage/XmlAddressBookStorageTest.java +++ /dev/null @@ -1,99 +0,0 @@ -package seedu.address.storage; - - -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.junit.rules.TemporaryFolder; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.commons.util.FileUtil; -import seedu.address.model.AddressBook; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Person; -import seedu.address.testutil.TypicalTestPersons; - -import java.io.IOException; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; - -public class XmlAddressBookStorageTest { - private static String TEST_DATA_FOLDER = FileUtil.getPath("./src/test/data/XmlAddressBookStorageTest/"); - - @Rule - public ExpectedException thrown = ExpectedException.none(); - - @Rule - public TemporaryFolder testFolder = new TemporaryFolder(); - - @Test - public void readAddressBook_nullFilePath_assertionFailure() throws Exception { - thrown.expect(AssertionError.class); - readAddressBook(null); - } - - private java.util.Optional readAddressBook(String filePath) throws Exception { - return new XmlAddressBookStorage(filePath).readAddressBook(addToTestDataPathIfNotNull(filePath)); - } - - private String addToTestDataPathIfNotNull(String prefsFileInTestDataFolder) { - return prefsFileInTestDataFolder != null - ? TEST_DATA_FOLDER + prefsFileInTestDataFolder - : null; - } - - @Test - public void read_missingFile_emptyResult() throws Exception { - assertFalse(readAddressBook("NonExistentFile.xml").isPresent()); - } - - @Test - public void read_notXmlFormat_exceptionThrown() throws Exception { - - thrown.expect(DataConversionException.class); - readAddressBook("NotXmlFormatAddressBook.xml"); - - /* IMPORTANT: Any code below an exception-throwing line (like the one above) will be ignored. - * That means you should not have more than one exception test in one method - */ - } - - @Test - public void readAndSaveAddressBook_allInOrder_success() throws Exception { - String filePath = testFolder.getRoot().getPath() + "TempAddressBook.xml"; - TypicalTestPersons td = new TypicalTestPersons(); - AddressBook original = td.getTypicalAddressBook(); - XmlAddressBookStorage xmlAddressBookStorage = new XmlAddressBookStorage(filePath); - - //Save in new file and read back - xmlAddressBookStorage.saveAddressBook(original, filePath); - ReadOnlyAddressBook readBack = xmlAddressBookStorage.readAddressBook(filePath).get(); - assertEquals(original, new AddressBook(readBack)); - - //Modify data, overwrite exiting file, and read back - original.addPerson(new Person(TypicalTestPersons.hoon)); - original.removePerson(new Person(TypicalTestPersons.alice)); - xmlAddressBookStorage.saveAddressBook(original, filePath); - readBack = xmlAddressBookStorage.readAddressBook(filePath).get(); - assertEquals(original, new AddressBook(readBack)); - - } - - @Test - public void saveAddressBook_nullAddressBook_assertionFailure() throws IOException { - thrown.expect(AssertionError.class); - saveAddressBook(null, "SomeFile.xml"); - } - - private void saveAddressBook(ReadOnlyAddressBook addressBook, String filePath) throws IOException { - new XmlAddressBookStorage(filePath).saveAddressBook(addressBook, addToTestDataPathIfNotNull(filePath)); - } - - @Test - public void saveAddressBook_nullFilePath_assertionFailure() throws IOException { - thrown.expect(AssertionError.class); - saveAddressBook(new AddressBook(), null); - } - - -} diff --git a/src/test/java/seedu/address/testutil/AddressBookBuilder.java b/src/test/java/seedu/address/testutil/AddressBookBuilder.java deleted file mode 100644 index a623b81c878f..000000000000 --- a/src/test/java/seedu/address/testutil/AddressBookBuilder.java +++ /dev/null @@ -1,35 +0,0 @@ -package seedu.address.testutil; - -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.tag.Tag; -import seedu.address.model.AddressBook; -import seedu.address.model.person.Person; -import seedu.address.model.person.UniquePersonList; - -/** - * A utility class to help with building Addressbook objects. - * Example usage:
- * {@code AddressBook ab = new AddressBookBuilder().withPerson("John", "Doe").withTag("Friend").build();} - */ -public class AddressBookBuilder { - - private AddressBook addressBook; - - public AddressBookBuilder(AddressBook addressBook){ - this.addressBook = addressBook; - } - - public AddressBookBuilder withPerson(Person person) throws UniquePersonList.DuplicatePersonException { - addressBook.addPerson(person); - return this; - } - - public AddressBookBuilder withTag(String tagName) throws IllegalValueException { - addressBook.addTag(new Tag(tagName)); - return this; - } - - public AddressBook build(){ - return addressBook; - } -} diff --git a/src/test/java/seedu/address/testutil/PersonBuilder.java b/src/test/java/seedu/address/testutil/PersonBuilder.java deleted file mode 100644 index 8b02a1668ef6..000000000000 --- a/src/test/java/seedu/address/testutil/PersonBuilder.java +++ /dev/null @@ -1,49 +0,0 @@ -package seedu.address.testutil; - -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.tag.Tag; -import seedu.address.model.person.*; - -/** - * - */ -public class PersonBuilder { - - private TestPerson person; - - public PersonBuilder() { - this.person = new TestPerson(); - } - - public PersonBuilder withName(String name) throws IllegalValueException { - this.person.setName(new Name(name)); - return this; - } - - public PersonBuilder withTags(String ... tags) throws IllegalValueException { - for (String tag: tags) { - person.getTags().add(new Tag(tag)); - } - return this; - } - - public PersonBuilder withAddress(String address) throws IllegalValueException { - this.person.setAddress(new Address(address)); - return this; - } - - public PersonBuilder withPhone(String phone) throws IllegalValueException { - this.person.setPhone(new Phone(phone)); - return this; - } - - public PersonBuilder withEmail(String email) throws IllegalValueException { - this.person.setEmail(new Email(email)); - return this; - } - - public TestPerson build() { - return this.person; - } - -} diff --git a/src/test/java/seedu/address/testutil/TestPerson.java b/src/test/java/seedu/address/testutil/TestPerson.java deleted file mode 100644 index 19ee5ded1cd3..000000000000 --- a/src/test/java/seedu/address/testutil/TestPerson.java +++ /dev/null @@ -1,76 +0,0 @@ -package seedu.address.testutil; - -import seedu.address.model.tag.UniqueTagList; -import seedu.address.model.person.*; - -/** - * A mutable person object. For testing only. - */ -public class TestPerson implements ReadOnlyPerson { - - private Name name; - private Address address; - private Email email; - private Phone phone; - private UniqueTagList tags; - - public TestPerson() { - tags = new UniqueTagList(); - } - - public void setName(Name name) { - this.name = name; - } - - public void setAddress(Address address) { - this.address = address; - } - - public void setEmail(Email email) { - this.email = email; - } - - public void setPhone(Phone phone) { - this.phone = phone; - } - - @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 tags; - } - - @Override - public String toString() { - return getAsText(); - } - - public String getAddCommand() { - StringBuilder sb = new StringBuilder(); - sb.append("add " + this.getName().fullName + " "); - sb.append("p/" + this.getPhone().value + " "); - sb.append("e/" + this.getEmail().value + " "); - sb.append("a/" + this.getAddress().value + " "); - this.getTags().getInternalList().stream().forEach(s -> sb.append("t/" + s.tagName + " ")); - return sb.toString(); - } -} diff --git a/src/test/java/seedu/address/testutil/TypicalTestPersons.java b/src/test/java/seedu/address/testutil/TypicalTestPersons.java deleted file mode 100644 index 773f64a98cc3..000000000000 --- a/src/test/java/seedu/address/testutil/TypicalTestPersons.java +++ /dev/null @@ -1,61 +0,0 @@ -package seedu.address.testutil; - -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.AddressBook; -import seedu.address.model.person.*; - -/** - * - */ -public class TypicalTestPersons { - - public static TestPerson alice, benson, carl, daniel, elle, fiona, george, hoon, ida; - - public TypicalTestPersons() { - try { - alice = new PersonBuilder().withName("Alice Pauline").withAddress("123, Jurong West Ave 6, #08-111") - .withEmail("alice@gmail.com").withPhone("85355255") - .withTags("friends").build(); - benson = new PersonBuilder().withName("Benson Meier").withAddress("311, Clementi Ave 2, #02-25") - .withEmail("johnd@gmail.com").withPhone("98765432") - .withTags("owesMoney", "friends").build(); - carl = new PersonBuilder().withName("Carl Kurz").withPhone("95352563").withEmail("heinz@yahoo.com").withAddress("wall street").build(); - daniel = new PersonBuilder().withName("Daniel Meier").withPhone("87652533").withEmail("cornelia@google.com").withAddress("10th street").build(); - elle = new PersonBuilder().withName("Elle Meyer").withPhone("9482224").withEmail("werner@gmail.com").withAddress("michegan ave").build(); - fiona = new PersonBuilder().withName("Fiona Kunz").withPhone("9482427").withEmail("lydia@gmail.com").withAddress("little tokyo").build(); - george = new PersonBuilder().withName("George Best").withPhone("9482442").withEmail("anna@google.com").withAddress("4th street").build(); - - //Manually added - hoon = new PersonBuilder().withName("Hoon Meier").withPhone("8482424").withEmail("stefan@mail.com").withAddress("little india").build(); - ida = new PersonBuilder().withName("Ida Mueller").withPhone("8482131").withEmail("hans@google.com").withAddress("chicago ave").build(); - } catch (IllegalValueException e) { - e.printStackTrace(); - assert false : "not possible"; - } - } - - public static void loadAddressBookWithSampleData(AddressBook ab) { - - try { - ab.addPerson(new Person(alice)); - ab.addPerson(new Person(benson)); - ab.addPerson(new Person(carl)); - ab.addPerson(new Person(daniel)); - ab.addPerson(new Person(elle)); - ab.addPerson(new Person(fiona)); - ab.addPerson(new Person(george)); - } catch (UniquePersonList.DuplicatePersonException e) { - assert false : "not possible"; - } - } - - public TestPerson[] getTypicalPersons() { - return new TestPerson[]{alice, benson, carl, daniel, elle, fiona, george}; - } - - public AddressBook getTypicalAddressBook(){ - AddressBook ab = new AddressBook(); - loadAddressBookWithSampleData(ab); - return ab; - } -} diff --git a/src/test/java/seedu/address/TestApp.java b/src/test/java/seedu/task/TestApp.java similarity index 73% rename from src/test/java/seedu/address/TestApp.java rename to src/test/java/seedu/task/TestApp.java index 756642b6c180..9c8de5478bcf 100644 --- a/src/test/java/seedu/address/TestApp.java +++ b/src/test/java/seedu/task/TestApp.java @@ -1,13 +1,14 @@ -package seedu.address; +package seedu.task; import javafx.stage.Screen; import javafx.stage.Stage; -import seedu.address.commons.core.Config; -import seedu.address.commons.core.GuiSettings; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.UserPrefs; -import seedu.address.storage.XmlSerializableAddressBook; -import seedu.address.testutil.TestUtil; +import seedu.task.testutil.TestUtil; +import seedu.todolist.MainApp; +import seedu.todolist.commons.core.Config; +import seedu.todolist.commons.core.GuiSettings; +import seedu.todolist.model.ReadOnlyToDoList; +import seedu.todolist.model.UserPrefs; +import seedu.todolist.storage.XmlSerializableToDoList; import java.util.function.Supplier; @@ -21,13 +22,13 @@ public class TestApp extends MainApp { protected static final String DEFAULT_PREF_FILE_LOCATION_FOR_TESTING = TestUtil.getFilePathInSandboxFolder("pref_testing.json"); public static final String APP_TITLE = "Test App"; protected static final String ADDRESS_BOOK_NAME = "Test"; - protected Supplier initialDataSupplier = () -> null; + protected Supplier initialDataSupplier = () -> null; protected String saveFileLocation = SAVE_LOCATION_FOR_TESTING; public TestApp() { } - public TestApp(Supplier initialDataSupplier, String saveFileLocation) { + public TestApp(Supplier initialDataSupplier, String saveFileLocation) { super(); this.initialDataSupplier = initialDataSupplier; this.saveFileLocation = saveFileLocation; @@ -35,7 +36,7 @@ public TestApp(Supplier initialDataSupplier, String saveFil // If some initial local data has been provided, write those to the file if (initialDataSupplier.get() != null) { TestUtil.createDataFileWithData( - new XmlSerializableAddressBook(this.initialDataSupplier.get()), + new XmlSerializableToDoList(this.initialDataSupplier.get()), this.saveFileLocation); } } @@ -44,9 +45,9 @@ public TestApp(Supplier initialDataSupplier, String saveFil protected Config initConfig(String configFilePath) { Config config = super.initConfig(configFilePath); config.setAppTitle(APP_TITLE); - config.setAddressBookFilePath(saveFileLocation); + config.setToDoListFilePath(saveFileLocation); config.setUserPrefsFilePath(DEFAULT_PREF_FILE_LOCATION_FOR_TESTING); - config.setAddressBookName(ADDRESS_BOOK_NAME); + config.setToDoListName(ADDRESS_BOOK_NAME); return config; } diff --git a/src/test/java/seedu/task/commons/core/ConfigTest.java b/src/test/java/seedu/task/commons/core/ConfigTest.java new file mode 100644 index 000000000000..bef9008148a3 --- /dev/null +++ b/src/test/java/seedu/task/commons/core/ConfigTest.java @@ -0,0 +1,33 @@ +package seedu.task.commons.core; + +import static org.junit.Assert.*; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import seedu.todolist.commons.core.Config; + +public class ConfigTest { + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void toString_defaultObject_stringReturned() { + String defaultConfigAsString = "App title : Task!t\n" + + "Current log level : INFO\n" + + "Preference file Location : preferences.json\n" + + "Local data file location : data/todolist.xml\n" + + "ToDoList name : MyToDoList"; + + assertEquals(defaultConfigAsString, new Config().toString()); + } + + @Test + public void equalsMethod(){ + Config defaultConfig = new Config(); + assertNotNull(defaultConfig); + assertTrue(defaultConfig.equals(defaultConfig)); + } + +} diff --git a/src/test/java/seedu/address/commons/core/VersionTest.java b/src/test/java/seedu/task/commons/core/VersionTest.java similarity index 97% rename from src/test/java/seedu/address/commons/core/VersionTest.java rename to src/test/java/seedu/task/commons/core/VersionTest.java index 87ac01f6c92d..092ecf58a6a8 100644 --- a/src/test/java/seedu/address/commons/core/VersionTest.java +++ b/src/test/java/seedu/task/commons/core/VersionTest.java @@ -1,9 +1,11 @@ -package seedu.address.commons.core; +package seedu.task.commons.core; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import seedu.todolist.commons.core.Version; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -119,7 +121,8 @@ public void versionComparable_validVersion_hashCodeIsCorrect() { @Test public void versionComparable_validVersion_equalIsCorrect() { - Version one, another; + Version one; + Version another; one = new Version(0, 0, 0, false); another = new Version(0, 0, 0, false); diff --git a/src/test/java/seedu/task/commons/util/AppUtilTest.java b/src/test/java/seedu/task/commons/util/AppUtilTest.java new file mode 100644 index 000000000000..d62223219a09 --- /dev/null +++ b/src/test/java/seedu/task/commons/util/AppUtilTest.java @@ -0,0 +1,26 @@ +package seedu.task.commons.util; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import seedu.todolist.commons.util.AppUtil; + +import static org.junit.Assert.assertNotNull; + +public class AppUtilTest { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void getImage_exitingImage(){ + assertNotNull(AppUtil.getImage("/images/taskit_logo.png")); + } + + @Test + public void getImage_nullGiven_assertionError(){ + thrown.expect(NullPointerException.class); + AppUtil.getImage("/images/address_book_32.png"); + } +} diff --git a/src/test/java/seedu/address/commons/util/ConfigUtilTest.java b/src/test/java/seedu/task/commons/util/ConfigUtilTest.java similarity index 92% rename from src/test/java/seedu/address/commons/util/ConfigUtilTest.java rename to src/test/java/seedu/task/commons/util/ConfigUtilTest.java index 6699343c4a82..af2b1c4c0f7d 100644 --- a/src/test/java/seedu/address/commons/util/ConfigUtilTest.java +++ b/src/test/java/seedu/task/commons/util/ConfigUtilTest.java @@ -1,12 +1,15 @@ -package seedu.address.commons.util; +package seedu.task.commons.util; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; -import seedu.address.commons.core.Config; -import seedu.address.commons.exceptions.DataConversionException; + +import seedu.todolist.commons.core.Config; +import seedu.todolist.commons.exceptions.DataConversionException; +import seedu.todolist.commons.util.ConfigUtil; +import seedu.todolist.commons.util.FileUtil; import java.io.File; import java.io.IOException; @@ -76,8 +79,8 @@ private Config getTypicalConfig() { config.setAppTitle("Typical App Title"); config.setLogLevel(Level.INFO); config.setUserPrefsFilePath("C:\\preferences.json"); - config.setAddressBookFilePath("addressbook.xml"); - config.setAddressBookName("TypicalAddressBookName"); + config.setToDoListFilePath("todolist.xml"); + config.setToDoListName("TypicalToDoListName"); return config; } diff --git a/src/test/java/seedu/address/commons/util/FileUtilTest.java b/src/test/java/seedu/task/commons/util/FileUtilTest.java similarity index 92% rename from src/test/java/seedu/address/commons/util/FileUtilTest.java rename to src/test/java/seedu/task/commons/util/FileUtilTest.java index 8de2621799cf..efbc428842b6 100644 --- a/src/test/java/seedu/address/commons/util/FileUtilTest.java +++ b/src/test/java/seedu/task/commons/util/FileUtilTest.java @@ -1,11 +1,13 @@ -package seedu.address.commons.util; +package seedu.task.commons.util; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; -import seedu.address.testutil.SerializableTestClass; -import seedu.address.testutil.TestUtil; + +import seedu.task.testutil.SerializableTestClass; +import seedu.task.testutil.TestUtil; +import seedu.todolist.commons.util.FileUtil; import java.io.File; import java.io.IOException; diff --git a/src/test/java/seedu/address/commons/util/JsonUtilTest.java b/src/test/java/seedu/task/commons/util/JsonUtilTest.java similarity index 85% rename from src/test/java/seedu/address/commons/util/JsonUtilTest.java rename to src/test/java/seedu/task/commons/util/JsonUtilTest.java index fc3902188807..52489ff0f2d9 100644 --- a/src/test/java/seedu/address/commons/util/JsonUtilTest.java +++ b/src/test/java/seedu/task/commons/util/JsonUtilTest.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.task.commons.util; /** * Tests JSON Read and Write diff --git a/src/test/java/seedu/address/commons/util/StringUtilTest.java b/src/test/java/seedu/task/commons/util/StringUtilTest.java similarity index 93% rename from src/test/java/seedu/address/commons/util/StringUtilTest.java rename to src/test/java/seedu/task/commons/util/StringUtilTest.java index bd209b1cbcab..daab098ed3e5 100644 --- a/src/test/java/seedu/address/commons/util/StringUtilTest.java +++ b/src/test/java/seedu/task/commons/util/StringUtilTest.java @@ -1,7 +1,9 @@ -package seedu.address.commons.util; +package seedu.task.commons.util; import org.junit.Test; +import seedu.todolist.commons.util.StringUtil; + import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; diff --git a/src/test/java/seedu/address/commons/util/UrlUtilTest.java b/src/test/java/seedu/task/commons/util/UrlUtilTest.java similarity index 74% rename from src/test/java/seedu/address/commons/util/UrlUtilTest.java rename to src/test/java/seedu/task/commons/util/UrlUtilTest.java index 21efd4e1d231..23eb7c057ae3 100644 --- a/src/test/java/seedu/address/commons/util/UrlUtilTest.java +++ b/src/test/java/seedu/task/commons/util/UrlUtilTest.java @@ -1,7 +1,9 @@ -package seedu.address.commons.util; +package seedu.task.commons.util; import org.junit.Test; +import seedu.todolist.commons.util.UrlUtil; + import java.net.MalformedURLException; import java.net.URL; @@ -40,4 +42,13 @@ public void compareBaseUrls_differentUrl_fail() throws MalformedURLException { URL url2 = new URL("https://www.google.com/A/acb"); assertFalse(UrlUtil.compareBaseUrls(url1, url2)); } + + @Test + public void compareBaseUrls_null_false() throws MalformedURLException { + URL url1 = new URL("https://www.Google.com/a/ac_b/"); + URL url2 = new URL("https://www.google.com/A/acb"); + assertFalse(UrlUtil.compareBaseUrls(url1, null)); + assertFalse(UrlUtil.compareBaseUrls(null, url2)); + assertFalse(UrlUtil.compareBaseUrls(null, null)); + } } diff --git a/src/test/java/seedu/address/commons/util/XmlUtilTest.java b/src/test/java/seedu/task/commons/util/XmlUtilTest.java similarity index 59% rename from src/test/java/seedu/address/commons/util/XmlUtilTest.java rename to src/test/java/seedu/task/commons/util/XmlUtilTest.java index dc4fd886c23e..63ef58539778 100644 --- a/src/test/java/seedu/address/commons/util/XmlUtilTest.java +++ b/src/test/java/seedu/task/commons/util/XmlUtilTest.java @@ -1,12 +1,15 @@ -package seedu.address.commons.util; +package seedu.task.commons.util; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; -import seedu.address.model.AddressBook; -import seedu.address.storage.XmlSerializableAddressBook; -import seedu.address.testutil.AddressBookBuilder; -import seedu.address.testutil.TestUtil; + +import seedu.task.testutil.ToDoListBuilder; +import seedu.task.testutil.TestUtil; +import seedu.todolist.commons.util.FileUtil; +import seedu.todolist.commons.util.XmlUtil; +import seedu.todolist.model.ToDoList; +import seedu.todolist.storage.XmlSerializableToDoList; import javax.xml.bind.JAXBException; import java.io.File; @@ -19,8 +22,8 @@ public class XmlUtilTest { private static final String TEST_DATA_FOLDER = FileUtil.getPath("src/test/data/XmlUtilTest/"); private static final File EMPTY_FILE = new File(TEST_DATA_FOLDER + "empty.xml"); private static final File MISSING_FILE = new File(TEST_DATA_FOLDER + "missing.xml"); - private static final File VALID_FILE = new File(TEST_DATA_FOLDER + "validAddressBook.xml"); - private static final File TEMP_FILE = new File(TestUtil.getFilePathInSandboxFolder("tempAddressBook.xml")); + private static final File VALID_FILE = new File(TEST_DATA_FOLDER + "validToDoList.xml"); + private static final File TEMP_FILE = new File(TestUtil.getFilePathInSandboxFolder("tempToDoList.xml")); @Rule public ExpectedException thrown = ExpectedException.none(); @@ -28,7 +31,7 @@ public class XmlUtilTest { @Test public void getDataFromFile_nullFile_AssertionError() throws Exception { thrown.expect(AssertionError.class); - XmlUtil.getDataFromFile(null, AddressBook.class); + XmlUtil.getDataFromFile(null, ToDoList.class); } @Test @@ -40,26 +43,25 @@ public void getDataFromFile_nullClass_AssertionError() throws Exception { @Test public void getDataFromFile_missingFile_FileNotFoundException() throws Exception { thrown.expect(FileNotFoundException.class); - XmlUtil.getDataFromFile(MISSING_FILE, AddressBook.class); + XmlUtil.getDataFromFile(MISSING_FILE, ToDoList.class); } @Test public void getDataFromFile_emptyFile_DataFormatMismatchException() throws Exception { thrown.expect(JAXBException.class); - XmlUtil.getDataFromFile(EMPTY_FILE, AddressBook.class); + XmlUtil.getDataFromFile(EMPTY_FILE, ToDoList.class); } @Test public void getDataFromFile_validFile_validResult() throws Exception { - XmlSerializableAddressBook dataFromFile = XmlUtil.getDataFromFile(VALID_FILE, XmlSerializableAddressBook.class); - assertEquals(9, dataFromFile.getPersonList().size()); - assertEquals(0, dataFromFile.getTagList().size()); + XmlSerializableToDoList dataFromFile = XmlUtil.getDataFromFile(VALID_FILE, XmlSerializableToDoList.class); + assertEquals(4, dataFromFile.getTaskList().size()); } @Test public void saveDataToFile_nullFile_AssertionError() throws Exception { thrown.expect(AssertionError.class); - XmlUtil.saveDataToFile(null, new AddressBook()); + XmlUtil.saveDataToFile(null, new ToDoList()); } @Test @@ -71,23 +73,23 @@ public void saveDataToFile_nullClass_AssertionError() throws Exception { @Test public void saveDataToFile_missingFile_FileNotFoundException() throws Exception { thrown.expect(FileNotFoundException.class); - XmlUtil.saveDataToFile(MISSING_FILE, new AddressBook()); + XmlUtil.saveDataToFile(MISSING_FILE, new ToDoList()); } @Test public void saveDataToFile_validFile_dataSaved() throws Exception { TEMP_FILE.createNewFile(); - XmlSerializableAddressBook dataToWrite = new XmlSerializableAddressBook(new AddressBook()); + XmlSerializableToDoList dataToWrite = new XmlSerializableToDoList(new ToDoList()); XmlUtil.saveDataToFile(TEMP_FILE, dataToWrite); - XmlSerializableAddressBook dataFromFile = XmlUtil.getDataFromFile(TEMP_FILE, XmlSerializableAddressBook.class); - assertEquals((new AddressBook(dataToWrite)).toString(),(new AddressBook(dataFromFile)).toString()); + XmlSerializableToDoList dataFromFile = XmlUtil.getDataFromFile(TEMP_FILE, XmlSerializableToDoList.class); + assertEquals((new ToDoList(dataToWrite)).toString(),(new ToDoList(dataFromFile)).toString()); //TODO: use equality instead of string comparisons - AddressBookBuilder builder = new AddressBookBuilder(new AddressBook()); - dataToWrite = new XmlSerializableAddressBook(builder.withPerson(TestUtil.generateSamplePersonData().get(0)).withTag("Friends").build()); + ToDoListBuilder builder = new ToDoListBuilder(new ToDoList()); + dataToWrite = new XmlSerializableToDoList(builder.withTask(TestUtil.generateSampleTaskData().get(0)).build()); XmlUtil.saveDataToFile(TEMP_FILE, dataToWrite); - dataFromFile = XmlUtil.getDataFromFile(TEMP_FILE, XmlSerializableAddressBook.class); - assertEquals((new AddressBook(dataToWrite)).toString(),(new AddressBook(dataFromFile)).toString()); + dataFromFile = XmlUtil.getDataFromFile(TEMP_FILE, XmlSerializableToDoList.class); + assertEquals((new ToDoList(dataToWrite)).toString(),(new ToDoList(dataFromFile)).toString()); } } diff --git a/src/test/java/seedu/task/logic/LogicManagerTest.java b/src/test/java/seedu/task/logic/LogicManagerTest.java new file mode 100644 index 000000000000..b7030f8be16c --- /dev/null +++ b/src/test/java/seedu/task/logic/LogicManagerTest.java @@ -0,0 +1,466 @@ +package seedu.task.logic; + +import com.google.common.eventbus.Subscribe; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import seedu.todolist.commons.core.EventsCenter; +import seedu.todolist.commons.events.model.ToDoListChangedEvent; +import seedu.todolist.commons.events.ui.JumpToListRequestEvent; +import seedu.todolist.commons.events.ui.ShowHelpEvent; +import seedu.todolist.logic.Logic; +import seedu.todolist.logic.LogicManager; +import seedu.todolist.logic.commands.*; +import seedu.todolist.model.ToDoList; +import seedu.todolist.model.Model; +import seedu.todolist.model.ModelManager; +import seedu.todolist.model.ReadOnlyToDoList; +import seedu.todolist.model.task.*; +import seedu.todolist.storage.Storage; +import seedu.todolist.storage.StorageManager; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static seedu.todolist.commons.core.Messages.*; + +public class LogicManagerTest { + + /** + * See https://github.com/junit-team/junit4/wiki/rules#temporaryfolder-rule + */ + @Rule + public TemporaryFolder saveFolder = new TemporaryFolder(); + + private Model model; + private Logic logic; + private Storage storage; + + //These are for checking the correctness of the events raised + private ReadOnlyToDoList latestSavedToDoList; + private boolean helpShown; + private int targetedJumpIndex; + + @Subscribe + private void handleLocalModelChangedEvent(ToDoListChangedEvent abce) { + latestSavedToDoList = new ToDoList(abce.data); + } + + @Subscribe + private void handleShowHelpEvent(ShowHelpEvent she) { + helpShown = true; + } + + @Subscribe + private void handleJumpToListRequestEvent(JumpToListRequestEvent je) { + targetedJumpIndex = je.targetIndex; + } + + @Before + public void setUp() { + model = new ModelManager(); + String tempToDoListFile = saveFolder.getRoot().getPath() + "TempToDoList.xml"; + String tempPreferencesFile = saveFolder.getRoot().getPath() + "TempPreferences.json"; + storage = new StorageManager(tempToDoListFile, tempPreferencesFile); + logic = new LogicManager(model, storage); + EventsCenter.getInstance().registerHandler(this); + + latestSavedToDoList = new ToDoList(model.getToDoList()); // last saved assumed to be up to date before. + helpShown = false; + targetedJumpIndex = -1; // non yet + } + + @After + public void tearDown() { + EventsCenter.clearSubscribers(); + } + + @Test + public void execute_invalid() throws Exception { + String invalidCommand = " "; + assertCommandBehavior(invalidCommand, + String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE)); + } + + /** + * Executes the command and confirms that the result message is correct. + * Both the 'address book' and the 'last shown list' are expected to be empty. + * @see #assertCommandBehavior(String, String, ReadOnlyToDoList, List) + */ + private void assertCommandBehavior(String inputCommand, String expectedMessage) throws Exception { + assertCommandBehavior(inputCommand, expectedMessage, new ToDoList(), Collections.emptyList()); + } + + /** + * Executes the command and confirms that the result message is correct and + * also confirms that the following three parts of the LogicManager object's state are as expected:
+ * - the internal address book data are same as those in the {@code expectedToDoList}
+ * - the backing list shown by UI matches the {@code shownList}
+ * - {@code expectedToDoList} was saved to the storage file.
+ */ + private void assertCommandBehavior(String inputCommand, String expectedMessage, + ReadOnlyToDoList expectedToDoList, + List expectedShownList) throws Exception { + + //Execute the command + CommandResult result = logic.execute(inputCommand); + + //Confirm the ui display elements should contain the right data + assertEquals(expectedMessage, result.feedbackToUser); + assertEquals(expectedShownList, model.getFilteredAllTaskList()); + + //Confirm the state of data (saved and in-memory) is as expected + assertEquals(expectedToDoList, model.getToDoList()); + assertEquals(expectedToDoList, latestSavedToDoList); + } + + //@@author A0158963M + private void assertsetStorageCommandBehavior(String inputCommand, String expectedMessage, String expectfilePath) + throws Exception { + + // Execute the command + CommandResult result = logic.execute(inputCommand); + + // Confirm the storage should contain the right data + assertEquals(expectedMessage, result.feedbackToUser); + assertEquals(expectfilePath, storage.getToDoListFilePath()); + + } + + @Test + public void execute_setstorage() throws Exception { + String setstorageCommand = "setstorage NewData"; + assertsetStorageCommandBehavior(setstorageCommand, SetstorageCommand.MESSAGE_SUCCESS, "NewData/todolist.xml"); + } + + //@@author + @Test + public void execute_unknownCommandWord() throws Exception { + String unknownCommand = "uicfhmowqewca"; + assertCommandBehavior(unknownCommand, MESSAGE_UNKNOWN_COMMAND); + } + + @Test + public void execute_help() throws Exception { + assertCommandBehavior("help", HelpCommand.SHOWING_HELP_MESSAGE); + assertTrue(helpShown); + } + + @Test + public void execute_exit() throws Exception { + assertCommandBehavior("exit", ExitCommand.MESSAGE_EXIT_ACKNOWLEDGEMENT); + } + + @Test + public void execute_clear() throws Exception { + TestDataHelper helper = new TestDataHelper(); + model.addTask(helper.generateTask(1)); + model.addTask(helper.generateTask(2)); + model.addTask(helper.generateTask(3)); + + assertCommandBehavior("clear", ClearCommand.MESSAGE_SUCCESS, new ToDoList(), Collections.emptyList()); + } + + @Test + public void execute_add_invalidTaskData() throws Exception { + assertCommandBehavior( + "add [];'() from 15 oct 2016 6pm to 16 oct 2016 12pm at NUS remarks do smth", Name.MESSAGE_NAME_CONSTRAINTS); + assertCommandBehavior( + "add new task from 16 oct 2016 12pm to 15 oct 2016 6pm at NUS remarks do smth", Interval.MESSAGE_INTERVAL_CONSTRAINTS_DATE); + assertCommandBehavior( + "add new task from 16 oct 2016 6pm to 16 oct 2016 12pm at NUS remarks do smth", Interval.MESSAGE_INTERVAL_CONSTRAINTS_TIME); + } + + @Test + public void execute_add_successful() throws Exception { + // setup expectations + TestDataHelper helper = new TestDataHelper(); + Task toBeAdded = helper.adam(); + ToDoList expectedAB = new ToDoList(); + expectedAB.addTask(toBeAdded); + + // execute command and verify result + assertCommandBehavior(helper.generateAddCommand(toBeAdded), + String.format(AddCommand.MESSAGE_SUCCESS, toBeAdded), + expectedAB, + expectedAB.getTaskList()); + + } + + + @Test + public void execute_list_showsAllTasks() throws Exception { + // prepare expectations + TestDataHelper helper = new TestDataHelper(); + ToDoList expectedAB = helper.generateToDoList(2); + List expectedList = expectedAB.getTaskList(); + + // prepare address book state + helper.addToModel(model, 2); + + assertCommandBehavior("list", + ListCommand.MESSAGE_ALLTASKS_SUCCESS, + expectedAB, + expectedList); + } + + + /** + * Confirms the 'invalid argument index number behaviour' for the given command + * targeting a single task in the shown list, using visible index. + * @param commandWord to test assuming it targets a single task in the last shown list based on visible index. + */ + private void assertIncorrectIndexFormatBehaviorForCommand(String commandWord, String expectedMessage) throws Exception { + assertCommandBehavior(commandWord , expectedMessage); //index missing + assertCommandBehavior(commandWord + " +1", expectedMessage); //index should be unsigned + assertCommandBehavior(commandWord + " -1", expectedMessage); //index should be unsigned + assertCommandBehavior(commandWord + " 0", expectedMessage); //index cannot be 0 + assertCommandBehavior(commandWord + " not_a_number", expectedMessage); + } + + /** + * Confirms the 'invalid argument index number behaviour' for the given command + * targeting a single task in the shown list, using visible index. + * @param commandWord to test assuming it targets a single task in the last shown list based on visible index. + */ + private void assertIndexNotFoundBehaviorForCommand(String commandWord) throws Exception { + String expectedMessage = MESSAGE_INVALID_TASK_DISPLAYED_INDEX; + TestDataHelper helper = new TestDataHelper(); + List taskList = helper.generateTaskList(2); + + // set AB state to 2 tasks + model.resetData(new ToDoList()); + for (Task p : taskList) { + model.addTask(p); + } + + assertCommandBehavior(commandWord + " 3", expectedMessage, model.getToDoList(), taskList); + } + + @Test + public void execute_deleteInvalidArgsFormat_errorMessageShown() throws Exception { + String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE); + assertIncorrectIndexFormatBehaviorForCommand("delete", expectedMessage); + } + + @Test + public void execute_deleteIndexNotFound_errorMessageShown() throws Exception { + assertIndexNotFoundBehaviorForCommand("delete"); + } + + @Test + public void execute_delete_removesCorrectTask() throws Exception { + TestDataHelper helper = new TestDataHelper(); + List threeTasks = helper.generateTaskList(3); + + ToDoList expectedAB = helper.generateToDoList(threeTasks); + expectedAB.removeTask(threeTasks.get(1)); + helper.addToModel(model, threeTasks); + + assertCommandBehavior("delete 2", + String.format(DeleteCommand.MESSAGE_DELETE_TASK_SUCCESS, threeTasks.get(1)), + expectedAB, + expectedAB.getTaskList()); + } + + + @Test + public void execute_find_invalidArgsFormat() throws Exception { + String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE); + assertCommandBehavior("find ", expectedMessage); + } + + @Test + public void execute_find_onlyMatchesFullWordsInNames() throws Exception { + TestDataHelper helper = new TestDataHelper(); + Task pTarget1 = helper.generateTaskWithName("bla bla KEY bla"); + Task pTarget2 = helper.generateTaskWithName("bla KEY bla bceofeia"); + Task p1 = helper.generateTaskWithName("KE Y"); + Task p2 = helper.generateTaskWithName("KEYKEYKEY sduauo"); + + List fourTasks = helper.generateTaskList(p1, pTarget1, p2, pTarget2); + ToDoList expectedAB = helper.generateToDoList(fourTasks); + List expectedList = helper.generateTaskList(pTarget1, pTarget2); + helper.addToModel(model, fourTasks); + + assertCommandBehavior("find either KEY", + Command.getMessageForTaskListShownSummary(0, 0, expectedList.size()), + expectedAB, + expectedList); + } + + @Test + public void execute_find_isNotCaseSensitive() throws Exception { + TestDataHelper helper = new TestDataHelper(); + Task p1 = helper.generateTaskWithName("bla bla KEY bla"); + Task p2 = helper.generateTaskWithName("bla KEY bla bceofeia"); + Task p3 = helper.generateTaskWithName("key key"); + Task p4 = helper.generateTaskWithName("KEy sduauo"); + + List fourTasks = helper.generateTaskList(p3, p1, p4, p2); + ToDoList expectedAB = helper.generateToDoList(fourTasks); + List expectedList = fourTasks; + helper.addToModel(model, fourTasks); + + assertCommandBehavior("find either KEY", + Command.getMessageForTaskListShownSummary(0, 0, expectedList.size()), + expectedAB, + expectedList); + } + + @Test + public void execute_find_matchesIfAnyKeywordPresent() throws Exception { + TestDataHelper helper = new TestDataHelper(); + Task pTarget1 = helper.generateTaskWithName("bla bla KEY bla"); + Task pTarget2 = helper.generateTaskWithName("bla rAnDoM bla bceofeia"); + Task pTarget3 = helper.generateTaskWithName("key key"); + Task p1 = helper.generateTaskWithName("sduauo"); + + List fourTasks = helper.generateTaskList(pTarget1, p1, pTarget2, pTarget3); + ToDoList expectedAB = helper.generateToDoList(fourTasks); + List expectedList = helper.generateTaskList(pTarget1, pTarget2, pTarget3); + helper.addToModel(model, fourTasks); + + assertCommandBehavior("find either key rAnDoM", + Command.getMessageForTaskListShownSummary(0, 0, expectedList.size()), + expectedAB, + expectedList); + } + + + /** + * A utility class to generate test data. + */ + private class TestDataHelper{ + + private Task adam() throws Exception { + Name name = new Name("Dinner"); + Interval interval = new Interval("12 oct 2016", "7pm", "12 oct 2016", "8pm"); + Location location = new Location("home"); + Remarks remarks = new Remarks("buy flowers"); + Status status = new Status(); + return new Task(name, interval, location, remarks, status); + } + + /** + * Generates a valid task using the given seed. + * Running this function with the same parameter values guarantees the returned task will have the same state. + * Each unique seed will generate a unique Task object. + * + * @param seed used to generate the task data field values + */ + private Task generateTask(int seed) throws Exception { + return new Task( + new Name("Task " + seed), + new Interval(seed + " oct 2017", seed + "pm", seed + " oct 2017", (seed + 1) + "pm"), + new Location("at" + seed), + new Remarks("remarks" + seed) + ); + } + + /** Generates the correct add command based on the task given */ + private String generateAddCommand(Task p) { + StringBuffer cmd = new StringBuffer(); + + cmd.append("add "); + cmd.append(p.getName().toString()); + cmd.append(" from "); + cmd.append(p.getInterval().getStartDate() + " " + p.getInterval().getStartTime()); + cmd.append(" to "); + cmd.append(p.getInterval().getEndDate() + " " + p.getInterval().getEndTime()); + cmd.append(" at "); + cmd.append(p.getLocation().toString()); + cmd.append(" remarks "); + cmd.append(p.getRemarks().toString()); + + return cmd.toString(); + } + + /** + * Generates an ToDoList with auto-generated tasks. + */ + private ToDoList generateToDoList(int numGenerated) throws Exception{ + ToDoList ToDoList = new ToDoList(); + addToToDoList(ToDoList, numGenerated); + return ToDoList; + } + + /** + * Generates an ToDoList based on the list of Tasks given. + */ + private ToDoList generateToDoList(List tasks) throws Exception{ + ToDoList ToDoList = new ToDoList(); + addToToDoList(ToDoList, tasks); + return ToDoList; + } + + /** + * Adds auto-generated Task objects to the given ToDoList + * @param ToDoList The ToDoList to which the Tasks will be added + */ + private void addToToDoList(ToDoList ToDoList, int numGenerated) throws Exception{ + addToToDoList(ToDoList, generateTaskList(numGenerated)); + } + + /** + * Adds the given list of Tasks to the given ToDoList + */ + private void addToToDoList(ToDoList ToDoList, List tasksToAdd) throws Exception{ + for(Task p: tasksToAdd){ + ToDoList.addTask(p); + } + } + + /** + * Adds auto-generated Task objects to the given model + * @param model The model to which the Tasks will be added + */ + private void addToModel(Model model, int numGenerated) throws Exception{ + addToModel(model, generateTaskList(numGenerated)); + } + + /** + * Adds the given list of Tasks to the given model + */ + private void addToModel(Model model, List tasksToAdd) throws Exception{ + for(Task p: tasksToAdd){ + model.addTask(p); + } + } + + /** + * Generates a list of Tasks based on the flags. + */ + private List generateTaskList(int numGenerated) throws Exception{ + List tasks = new ArrayList<>(); + for(int i = 1; i <= numGenerated; i++){ + tasks.add(generateTask(i)); + } + return tasks; + } + + private List generateTaskList(Task... tasks) { + return Arrays.asList(tasks); + } + + /** + * Generates a Task object with given name. Other fields will have some dummy values. + */ + private Task generateTaskWithName(String name) throws Exception { + return new Task( + new Name(name), + new Interval("21 oct 2016", "3pm", "21 oct 2016", "4pm"), + new Location("location"), + new Remarks("remarks"), + new Status() + ); + } + } +} diff --git a/src/test/java/seedu/task/model/DateParserTest.java b/src/test/java/seedu/task/model/DateParserTest.java new file mode 100644 index 000000000000..f2cdb7c88810 --- /dev/null +++ b/src/test/java/seedu/task/model/DateParserTest.java @@ -0,0 +1,25 @@ +package seedu.task.model; + +import static org.junit.Assert.*; + +import java.time.DateTimeException; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import seedu.todolist.model.parser.DateParser;; + +public class DateParserTest { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void invalidMonth_exceptionThrown() throws Exception { + thrown.expect(DateTimeException.class); + DateParser.parseDate("25/20/2016"); + fail(); + } + +} \ No newline at end of file diff --git a/src/test/java/seedu/task/model/TaskDateTest.java b/src/test/java/seedu/task/model/TaskDateTest.java new file mode 100644 index 000000000000..6e68c38c41c4 --- /dev/null +++ b/src/test/java/seedu/task/model/TaskDateTest.java @@ -0,0 +1,31 @@ +package seedu.task.model; + +import static org.junit.Assert.*; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import seedu.todolist.commons.exceptions.IllegalValueException; +import seedu.todolist.model.task.TaskDate; + +public class TaskDateTest { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void invalidDateFormat_exceptionThrown() throws Exception { + thrown.expect(IllegalValueException.class); + new TaskDate("1\3\2016"); + fail(); + } + + @Test + public void invalidDate_exceptionThrown() throws Exception { + thrown.expect(IllegalValueException.class); + new TaskDate("in/valid/date"); + fail(); + } + +} diff --git a/src/test/java/seedu/task/model/TaskTimeTest.java b/src/test/java/seedu/task/model/TaskTimeTest.java new file mode 100644 index 000000000000..10b2b5020b54 --- /dev/null +++ b/src/test/java/seedu/task/model/TaskTimeTest.java @@ -0,0 +1,31 @@ +package seedu.task.model; + +import static org.junit.Assert.*; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import seedu.todolist.commons.exceptions.IllegalValueException; +import seedu.todolist.model.task.TaskTime; + +public class TaskTimeTest { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void invalidTimeFormat_exceptionThrown() throws Exception { + thrown.expect(IllegalValueException.class); + new TaskTime("1:30:00"); + fail(); + } + + @Test + public void invalidTime_exceptionThrown() throws Exception { + thrown.expect(IllegalValueException.class); + new TaskTime("one:thirtypm"); + fail(); + } + +} diff --git a/src/test/java/seedu/address/model/UnmodifiableObservableListTest.java b/src/test/java/seedu/task/model/UnmodifiableObservableListTest.java similarity index 89% rename from src/test/java/seedu/address/model/UnmodifiableObservableListTest.java rename to src/test/java/seedu/task/model/UnmodifiableObservableListTest.java index 0334d7e42073..3a3fea2d0db1 100644 --- a/src/test/java/seedu/address/model/UnmodifiableObservableListTest.java +++ b/src/test/java/seedu/task/model/UnmodifiableObservableListTest.java @@ -1,27 +1,28 @@ -package seedu.address.model; +package seedu.task.model; import javafx.collections.FXCollections; +import seedu.todolist.commons.core.UnmodifiableObservableList; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; -import seedu.address.commons.core.UnmodifiableObservableList; import java.util.*; import static org.junit.Assert.assertSame; -import static seedu.address.testutil.TestUtil.assertThrows; +import static seedu.task.testutil.TestUtil.assertThrows; public class UnmodifiableObservableListTest { @Rule public ExpectedException thrown = ExpectedException.none(); - List backing; - UnmodifiableObservableList list; + private List backing; + private UnmodifiableObservableList list; @Before - public void setup() { + public void setUp() { backing = new ArrayList<>(); backing.add(10); list = new UnmodifiableObservableList<>(FXCollections.observableList(backing)); diff --git a/src/test/java/seedu/address/storage/JsonUserPrefStorageTest.java b/src/test/java/seedu/task/storage/JsonUserPrefStorageTest.java similarity index 94% rename from src/test/java/seedu/address/storage/JsonUserPrefStorageTest.java rename to src/test/java/seedu/task/storage/JsonUserPrefStorageTest.java index de6d2348e0b7..688cc51bd364 100644 --- a/src/test/java/seedu/address/storage/JsonUserPrefStorageTest.java +++ b/src/test/java/seedu/task/storage/JsonUserPrefStorageTest.java @@ -1,13 +1,15 @@ -package seedu.address.storage; +package seedu.task.storage; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.commons.util.FileUtil; -import seedu.address.model.UserPrefs; + +import seedu.todolist.commons.exceptions.DataConversionException; +import seedu.todolist.commons.util.FileUtil; +import seedu.todolist.model.UserPrefs; +import seedu.todolist.storage.JsonUserPrefStorage; import java.io.File; import java.io.IOException; diff --git a/src/test/java/seedu/address/storage/StorageManagerTest.java b/src/test/java/seedu/task/storage/StorageManagerTest.java similarity index 63% rename from src/test/java/seedu/address/storage/StorageManagerTest.java rename to src/test/java/seedu/task/storage/StorageManagerTest.java index 0e919872665b..b172406e5a68 100644 --- a/src/test/java/seedu/address/storage/StorageManagerTest.java +++ b/src/test/java/seedu/task/storage/StorageManagerTest.java @@ -1,14 +1,16 @@ -package seedu.address.storage; +package seedu.task.storage; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; -import seedu.address.model.AddressBook; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.UserPrefs; -import seedu.address.testutil.TypicalTestPersons; + +import seedu.task.testutil.TypicalTestTasks; +import seedu.todolist.model.ToDoList; +import seedu.todolist.model.ReadOnlyToDoList; +import seedu.todolist.model.UserPrefs; +import seedu.todolist.storage.StorageManager; import static org.junit.Assert.assertEquals; @@ -21,7 +23,7 @@ public class StorageManagerTest { @Before - public void setup() { + public void setUp() { storageManager = new StorageManager(getTempFilePath("ab"), getTempFilePath("prefs")); } @@ -47,12 +49,12 @@ public void prefsReadSave() throws Exception { } @Test - public void addressBookReadSave() throws Exception { - AddressBook original = new TypicalTestPersons().getTypicalAddressBook(); - storageManager.saveAddressBook(original); - ReadOnlyAddressBook retrieved = storageManager.readAddressBook().get(); - assertEquals(original, new AddressBook(retrieved)); - //More extensive testing of AddressBook saving/reading is done in XmlAddressBookStorageTest + public void ToDoListReadSave() throws Exception { + ToDoList original = new TypicalTestTasks().getTypicalToDoList(); + storageManager.saveToDoList(original); + ReadOnlyToDoList retrieved = storageManager.readToDoList().get(); + assertEquals(original, new ToDoList(retrieved)); + //More extensive testing of ToDoList saving/reading is done in XmlToDoListStorageTest } diff --git a/src/test/java/seedu/task/storage/XmlToDoListStorageTest.java b/src/test/java/seedu/task/storage/XmlToDoListStorageTest.java new file mode 100644 index 000000000000..baf05f81f5da --- /dev/null +++ b/src/test/java/seedu/task/storage/XmlToDoListStorageTest.java @@ -0,0 +1,101 @@ +package seedu.task.storage; + + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; + +import seedu.task.testutil.TypicalTestTasks; +import seedu.todolist.commons.exceptions.DataConversionException; +import seedu.todolist.commons.util.FileUtil; +import seedu.todolist.model.ToDoList; +import seedu.todolist.model.ReadOnlyToDoList; +import seedu.todolist.model.task.Task; +import seedu.todolist.storage.XmlToDoListStorage; + +import java.io.IOException; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +public class XmlToDoListStorageTest { + private static String TEST_DATA_FOLDER = FileUtil.getPath("./src/test/data/XmlToDoListStorageTest/"); + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Rule + public TemporaryFolder testFolder = new TemporaryFolder(); + + @Test + public void readToDoList_nullFilePath_assertionFailure() throws Exception { + thrown.expect(AssertionError.class); + readToDoList(null); + } + + private java.util.Optional readToDoList(String filePath) throws Exception { + return new XmlToDoListStorage(filePath).readToDoList(addToTestDataPathIfNotNull(filePath)); + } + + private String addToTestDataPathIfNotNull(String prefsFileInTestDataFolder) { + return prefsFileInTestDataFolder != null + ? TEST_DATA_FOLDER + prefsFileInTestDataFolder + : null; + } + + @Test + public void read_missingFile_emptyResult() throws Exception { + assertFalse(readToDoList("NonExistentFile.xml").isPresent()); + } + + @Test + public void read_notXmlFormat_exceptionThrown() throws Exception { + + thrown.expect(DataConversionException.class); + readToDoList("NotXmlFormatToDoList.xml"); + + /* IMPORTANT: Any code below an exception-throwing line (like the one above) will be ignored. + * That means you should not have more than one exception test in one method + */ + } + + @Test + public void readAndSaveToDoList_allInOrder_success() throws Exception { + String filePath = testFolder.getRoot().getPath() + "TempToDoList.xml"; + TypicalTestTasks td = new TypicalTestTasks(); + ToDoList original = td.getTypicalToDoList(); + XmlToDoListStorage xmlToDoListStorage = new XmlToDoListStorage(filePath); + + //Save in new file and read back + xmlToDoListStorage.saveToDoList(original, filePath); + ReadOnlyToDoList readBack = xmlToDoListStorage.readToDoList(filePath).get(); + assertEquals(original, new ToDoList(readBack)); + + //Modify data, overwrite exiting file, and read back + original.addTask(new Task(TypicalTestTasks.upcomingEvent)); + original.removeTask(new Task(TypicalTestTasks.eventWithLocation)); + xmlToDoListStorage.saveToDoList(original, filePath); + readBack = xmlToDoListStorage.readToDoList(filePath).get(); + assertEquals(original, new ToDoList(readBack)); + + } + + @Test + public void saveToDoList_nullToDoList_assertionFailure() throws IOException { + thrown.expect(AssertionError.class); + saveToDoList(null, "SomeFile.xml"); + } + + private void saveToDoList(ReadOnlyToDoList ToDoList, String filePath) throws IOException { + new XmlToDoListStorage(filePath).saveToDoList(ToDoList, addToTestDataPathIfNotNull(filePath)); + } + + @Test + public void saveToDoList_nullFilePath_assertionFailure() throws IOException { + thrown.expect(AssertionError.class); + saveToDoList(new ToDoList(), null); + } + + +} diff --git a/src/test/java/seedu/address/testutil/SerializableTestClass.java b/src/test/java/seedu/task/testutil/SerializableTestClass.java similarity index 96% rename from src/test/java/seedu/address/testutil/SerializableTestClass.java rename to src/test/java/seedu/task/testutil/SerializableTestClass.java index ef58ef857179..5cd9e77baecd 100644 --- a/src/test/java/seedu/address/testutil/SerializableTestClass.java +++ b/src/test/java/seedu/task/testutil/SerializableTestClass.java @@ -1,4 +1,4 @@ -package seedu.address.testutil; +package seedu.task.testutil; import java.time.LocalDateTime; import java.util.ArrayList; @@ -27,8 +27,6 @@ public class SerializableTestClass { private List listOfLocalDateTimes; private HashMap mapOfIntegerToString; - public SerializableTestClass() {} - public static String getNameTestValue() { return NAME_TEST_VALUE; } diff --git a/src/test/java/seedu/task/testutil/TaskBuilder.java b/src/test/java/seedu/task/testutil/TaskBuilder.java new file mode 100644 index 000000000000..03e769530c08 --- /dev/null +++ b/src/test/java/seedu/task/testutil/TaskBuilder.java @@ -0,0 +1,46 @@ +package seedu.task.testutil; + +import seedu.todolist.commons.exceptions.IllegalValueException; +import seedu.todolist.model.task.*; + +/** + * + */ +public class TaskBuilder { + + private TestTask task; + + public TaskBuilder() { + this.task = new TestTask(); + } + + public TaskBuilder withName(String name) throws IllegalValueException { + this.task.setName(new Name(name)); + return this; + } + + public TaskBuilder withInterval(String startDate, String startTime, String endDate, String endTime) throws IllegalValueException { + this.task.setInterval(new Interval(startDate, startTime, endDate, endTime)); + return this; + } + + public TaskBuilder withLocation(String location) throws IllegalValueException { + this.task.setLocation(new Location(location)); + return this; + } + + public TaskBuilder withRemarks(String remarks) throws IllegalValueException { + this.task.setRemarks(new Remarks(remarks)); + return this; + } + + public TaskBuilder withStatus(Status.Type status) throws IllegalValueException { + this.task.setStatus(new Status(status)); + return this; + } + + public TestTask build() { + return this.task; + } + +} diff --git a/src/test/java/seedu/task/testutil/TestTask.java b/src/test/java/seedu/task/testutil/TestTask.java new file mode 100644 index 000000000000..ed3bd2e2ee38 --- /dev/null +++ b/src/test/java/seedu/task/testutil/TestTask.java @@ -0,0 +1,144 @@ +package seedu.task.testutil; + +import seedu.todolist.model.task.*; + +/** + * A mutable task object. For testing only. + */ +public class TestTask implements ReadOnlyTask { + + private static final String KEYWORD_ADD = "add "; + private static final String KEYWORD_FROM = "from "; + private static final String KEYWORD_TO = " to "; + private static final String KEYWORD_BY = "by "; + private static final String KEYWORD_AT = "at "; + private static final String KEYWORD_REMARKS = "remarks "; + + private static final String WHITE_SPACE = " "; + + + private Name name; + private Interval interval; + private Location location; + private Remarks remarks; + private Status status; + + public void setName(Name name) { + this.name = name; + } + + //@@author A0138601M + public void setInterval(Interval interval) { + this.interval = interval; + } + //@@author + + public void setLocation(Location location) { + this.location = location; + } + + public void setRemarks(Remarks remarks) { + this.remarks = remarks; + } + + //@@author A0138601M + public void setStatus(Status status) { + this.status = status; + } + //@@author + + @Override + public Name getName() { + return name; + } + + //@@author A0138601M + @Override + public Interval getInterval() { + return interval; + } + //author + + @Override + public Location getLocation() { + return location; + } + + @Override + public Remarks getRemarks() { + return remarks; + } + + //@@author A0138601M + @Override + public Status getStatus() { + return status; + } + + @Override + public String toString() { + return getAsText(); + } + + public String getAddCommand() { + StringBuilder sb = new StringBuilder(); + sb.append(KEYWORD_ADD + this.getName().fullName + WHITE_SPACE); + + //append interval + Interval interval = getInterval(); + if (interval.isDeadlineWithTime()) { + sb.append(KEYWORD_BY + interval.getEndDate() + WHITE_SPACE + interval.getEndTime() + WHITE_SPACE); + } else if (interval.isDeadlineWithoutTime()) { + sb.append(KEYWORD_BY + interval.getEndDate() + WHITE_SPACE); + } else { + sb.append(KEYWORD_FROM + this.getInterval().getStartDate() + WHITE_SPACE + this.getInterval().getStartTime() + + KEYWORD_TO + this.getInterval().getEndDate()+ WHITE_SPACE + this.getInterval().getEndTime() + WHITE_SPACE); + } + + //append location + if (this.getLocation() != null) { + sb.append(KEYWORD_AT + this.getLocation() + WHITE_SPACE); + } + + //append remarks + if (this.getRemarks() != null) { + sb.append(KEYWORD_REMARKS + this.getRemarks() + WHITE_SPACE); + } + return sb.toString(); + } + + @Override + public int compareTo(ReadOnlyTask task) { + return this.interval.compareTo(task.getInterval()); + } + + //@@author + public String getEditCommand(int index) { + StringBuilder sb = new StringBuilder(); + sb.append("edit " + index + " " + this.getName().fullName + " "); + + //append interval + Interval interval = getInterval(); + if (interval.isDeadlineWithTime()) { + sb.append("by " + interval.getEndDate() + " " + interval.getEndTime() + " "); + } + else if (interval.isDeadlineWithoutTime()) { + sb.append("by " + interval.getEndDate() + " "); + } + else { + sb.append("from " + this.getInterval().getStartDate() + " " + this.getInterval().getStartTime() + + " to " + this.getInterval().getEndDate()+ " " + this.getInterval().getEndTime() + " "); + } + + //append location + if (this.getLocation() != null) { + sb.append("at " + this.getLocation() + " "); + } + + //append remarks + if (this.getRemarks() != null) { + sb.append("remarks " + this.getRemarks()); + } + return sb.toString(); + } +} diff --git a/src/test/java/seedu/task/testutil/TestTaskList.java b/src/test/java/seedu/task/testutil/TestTaskList.java new file mode 100644 index 000000000000..c431fb5aa48c --- /dev/null +++ b/src/test/java/seedu/task/testutil/TestTaskList.java @@ -0,0 +1,196 @@ +package seedu.task.testutil; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import seedu.todolist.model.task.Status; + +//@@author A0138601M +public class TestTaskList { + private ArrayList testCompleteTasks; + private ArrayList testIncompleteTasks; + private ArrayList testOverdueTasks; + private int numberOfTask; + + /** + * Constructs an empty test task list + */ + public TestTaskList() { + clear(); + } + + /** + * Constructs a test task list from lists of incomplete list and complete list + */ + public TestTaskList(List incompleteList, List completeList, List overdueList) { + testCompleteTasks = new ArrayList(completeList); + testIncompleteTasks = new ArrayList(incompleteList); + testOverdueTasks = new ArrayList(overdueList); + numberOfTask = incompleteList.size() + completeList.size() + overdueList.size(); + } + + /** + * Constructs a test task list from an array of test tasks + */ + public TestTaskList(TestTask[] testTasks) { + this(); + for (TestTask task : testTasks) { + if (task.getStatus().isComplete()) { + testCompleteTasks.add(task); + Collections.sort(testCompleteTasks); + } else if (task.getStatus().isIncomplete()){ + testIncompleteTasks.add(task); + Collections.sort(testIncompleteTasks); + } else if (task.getStatus().isOverdue()){ + testOverdueTasks.add(task); + Collections.sort(testOverdueTasks); + } + } + numberOfTask = testTasks.length; + } + + public TestTask[] getCompleteList() { + TestTask[] completeTasks = new TestTask[testCompleteTasks.size()]; + return testCompleteTasks.toArray(completeTasks); + } + + public TestTask[] getIncompleteList() { + TestTask[] incompleteTasks = new TestTask[testIncompleteTasks.size()]; + return testIncompleteTasks.toArray(incompleteTasks); + } + + public TestTask[] getOverdueList() { + TestTask[] overdueTasks = new TestTask[testOverdueTasks.size()]; + return testOverdueTasks.toArray(overdueTasks); + } + + public int getNumberOfTask() { + return numberOfTask; + } + + /** + * Empty all three list + */ + public void clear() { + testCompleteTasks = new ArrayList(); + testIncompleteTasks = new ArrayList(); + testOverdueTasks = new ArrayList(); + numberOfTask = 0; + } + + /** + * Add tasks to the list of tasks. + * @param tasks an array of tasks. + * @param tasksToAdd The tasks that are to be added into the original array. + * @return The modified array of tasks. + */ + public void addTasksToList(TestTask taskToAdd) { + switch (taskToAdd.getStatus().getType()) { + + case Complete : + testCompleteTasks.add(taskToAdd); + Collections.sort(testCompleteTasks); + break; + + case Incomplete : + testIncompleteTasks.add(taskToAdd); + Collections.sort(testIncompleteTasks); + break; + + case Overdue : + testOverdueTasks.add(taskToAdd); + Collections.sort(testOverdueTasks); + break; + + default : + assert false : "Type must be either Complete, Incomplete or Overdue"; + break; + + } + + numberOfTask++; + } + + /** + * Removes a subset from the list of tasks. + * @param targetIndexes The indexes of task targeted tasks + * @param type determines the list to work on + * @return The modified task list after removal of the subset from the list. + */ + public void removeTasksFromList(int[] targetIndexes, Status.Type type) { + ArrayList targetList = getTargetList(type); + ArrayList tasksToDelete = getTargetTasks(targetIndexes, targetList); + for (int i = 0; i < tasksToDelete.size(); i++) { + targetList.remove(tasksToDelete.get(i)); + numberOfTask--; + } + } + + /** + * Marks a subset from the list of incomplete tasks. + * @param tasksToMark The subset of tasks. + * @return The modified tasks after marking of the subset from tasks. + */ + public void markTasksFromList(int[] targetIndexes, Status.Type type) { + ArrayList targetList = getTargetList(type); + ArrayList tasksToMark = getTargetTasks(targetIndexes, targetList); + for (int i = 0; i < tasksToMark.size(); i++) { + targetList.remove(tasksToMark.get(i)); + testCompleteTasks.add(tasksToMark.get(i)); + } + Collections.sort(testCompleteTasks); + } + + /** + * Gets a list of task that were targeted for operations + * @param targetIndexes The indexes of task targeted tasks + * @param targetList the list to work on + * @return an array of task to be operated on + */ + private ArrayList getTargetTasks(int[] targetIndexes, ArrayList targetList) { + ArrayList targetTasks = new ArrayList(); + for (int index : targetIndexes) { + targetTasks.add(targetList.get(index - 1)); //-1 because array uses zero indexing + } + return targetTasks; + } + + /** + * @param type determine the list to be worked on + * @return the list that were targeted + */ + private ArrayList getTargetList(Status.Type type) { + switch (type) { + + case Complete : + return testCompleteTasks; + + case Incomplete : + return testIncompleteTasks; + + case Overdue : + return testOverdueTasks; + + default : + assert false : "Type must be either Complete, Incomplete or Overdue"; + return null; + } + } + + //@@author A0146682X + /** + * edits a task in the list tasks. + * @param index the index of task to edit + * @param isFromIncompleteList Whether to edit from incomplete list or complete list + */ + public void editTask(int index, TestTask newTask, boolean isFromIncompleteList) { + if (isFromIncompleteList) { + testIncompleteTasks.set(index-1, newTask); + } + else { + testCompleteTasks.set(index-1, newTask); + } + } + //@@author +} diff --git a/src/test/java/seedu/address/testutil/TestUtil.java b/src/test/java/seedu/task/testutil/TestUtil.java similarity index 57% rename from src/test/java/seedu/address/testutil/TestUtil.java rename to src/test/java/seedu/task/testutil/TestUtil.java index 17c92d66398a..69bc14128706 100644 --- a/src/test/java/seedu/address/testutil/TestUtil.java +++ b/src/test/java/seedu/task/testutil/TestUtil.java @@ -1,7 +1,7 @@ -package seedu.address.testutil; +package seedu.task.testutil; import com.google.common.io.Files; -import guitests.guihandles.PersonCardHandle; +import guitests.guihandles.TaskCardHandle; import javafx.geometry.Bounds; import javafx.geometry.Point2D; import javafx.scene.Node; @@ -12,15 +12,14 @@ import junit.framework.AssertionFailedError; import org.loadui.testfx.GuiTest; import org.testfx.api.FxToolkit; -import seedu.address.TestApp; -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.commons.util.FileUtil; -import seedu.address.commons.util.XmlUtil; -import seedu.address.model.AddressBook; -import seedu.address.model.person.*; -import seedu.address.model.tag.Tag; -import seedu.address.model.tag.UniqueTagList; -import seedu.address.storage.XmlSerializableAddressBook; + +import seedu.task.TestApp; +import seedu.todolist.commons.exceptions.IllegalValueException; +import seedu.todolist.commons.util.FileUtil; +import seedu.todolist.commons.util.XmlUtil; +import seedu.todolist.model.ToDoList; +import seedu.todolist.model.task.*; +import seedu.todolist.storage.XmlSerializableToDoList; import java.io.File; import java.io.IOException; @@ -38,6 +37,13 @@ */ public class TestUtil { + /** + * Folder used for temp files created during testing. Ignored by Git. + */ + public static String SANDBOX_FOLDER = FileUtil.getPath("./src/test/data/sandbox/"); + + public static final Task[] sampleTaskData = getSampleTaskData(); + public static String LS = System.lineSeparator(); public static void assertThrows(Class expected, Runnable executable) { @@ -55,25 +61,18 @@ public static void assertThrows(Class expected, Runnable ex String.format("Expected %s to be thrown, but nothing was thrown.", expected.getName())); } - /** - * Folder used for temp files created during testing. Ignored by Git. - */ - public static String SANDBOX_FOLDER = FileUtil.getPath("./src/test/data/sandbox/"); - - public static final Person[] samplePersonData = getSamplePersonData(); - - private static Person[] getSamplePersonData() { + private static Task[] getSampleTaskData() { try { - return new Person[]{ - new Person(new Name("Ali Muster"), new Phone("9482424"), new Email("hans@google.com"), new Address("4th street"), new UniqueTagList()), - new Person(new Name("Boris Mueller"), new Phone("87249245"), new Email("ruth@google.com"), new Address("81th street"), new UniqueTagList()), - new Person(new Name("Carl Kurz"), new Phone("95352563"), new Email("heinz@yahoo.com"), new Address("wall street"), new UniqueTagList()), - new Person(new Name("Daniel Meier"), new Phone("87652533"), new Email("cornelia@google.com"), new Address("10th street"), new UniqueTagList()), - new Person(new Name("Elle Meyer"), new Phone("9482224"), new Email("werner@gmail.com"), new Address("michegan ave"), new UniqueTagList()), - new Person(new Name("Fiona Kunz"), new Phone("9482427"), new Email("lydia@gmail.com"), new Address("little tokyo"), new UniqueTagList()), - new Person(new Name("George Best"), new Phone("9482442"), new Email("anna@google.com"), new Address("4th street"), new UniqueTagList()), - new Person(new Name("Hoon Meier"), new Phone("8482424"), new Email("stefan@mail.com"), new Address("little india"), new UniqueTagList()), - new Person(new Name("Ida Mueller"), new Phone("8482131"), new Email("hans@google.com"), new Address("chicago ave"), new UniqueTagList()) + return new Task[]{ + new Task(new Name("Ali Muster"), new Interval("12 oct 2016", "7pm", "12 oct 2016", "8pm"), new Location("home"), new Remarks("buy flowers"), new Status()), + new Task(new Name("Boris Mueller"), new Interval("12 oct 2016", "7pm", "12 oct 2016", "8pm"), new Location("Trinity Church"), new Remarks("bring umbrella"), new Status()), + new Task(new Name("Carl Kurz"), new Interval("12 oct 2016", "7pm", "12 oct 2016", "8pm"), new Location("school"), new Remarks("ask Toms opinion"), new Status()), + new Task(new Name("Daniel Meier"), new Interval("12 oct 2016", "7pm", "12 oct 2016", "8pm"), new Location("NUS"), new Remarks("buy drinks"), new Status()), + new Task(new Name("Elle Meyer"), new Interval("12 oct 2016", "7pm", "12 oct 2016", "8pm"), new Location("Kent Ridge MRT Station"), new Remarks("prepare presents"), new Status()), + new Task(new Name("Fiona Kunz"), new Interval("12 oct 2016", "7pm", "12 oct 2016", "8pm"), new Location("96 Bus Stop"), new Remarks("do rehearsal"), new Status()), + new Task(new Name("George Best"), new Interval("12 oct 2016", "7pm", "12 oct 2016", "8pm"), new Location("canteen"), new Remarks("check with mum"), new Status()), + new Task(new Name("Hoon Meier"), new Interval("12 oct 2016", "7pm", "12 oct 2016", "8pm"), new Location("Utown"), new Remarks("compare the price"), new Status()), + new Task(new Name("Ida Mueller"), new Interval("12 oct 2016", "7pm", "12 oct 2016", "8pm"), new Location("university"), new Remarks("bring jacket"), new Status()) }; } catch (IllegalValueException e) { assert false; @@ -82,23 +81,8 @@ private static Person[] getSamplePersonData() { } } - public static final Tag[] sampleTagData = getSampleTagData(); - - private static Tag[] getSampleTagData() { - try { - return new Tag[]{ - new Tag("relatives"), - new Tag("friends") - }; - } catch (IllegalValueException e) { - assert false; - return null; - //not possible - } - } - - public static List generateSamplePersonData() { - return Arrays.asList(samplePersonData); + public static List generateSampleTaskData() { + return Arrays.asList(sampleTaskData); } /** @@ -117,7 +101,7 @@ public static String getFilePathInSandboxFolder(String fileName) { } public static void createDataFileWithSampleData(String filePath) { - createDataFileWithData(generateSampleStorageAddressBook(), filePath); + createDataFileWithData(generateSampleStorageToDoList(), filePath); } public static void createDataFileWithData(T data, String filePath) { @@ -134,12 +118,12 @@ public static void main(String... s) { createDataFileWithSampleData(TestApp.SAVE_LOCATION_FOR_TESTING); } - public static AddressBook generateEmptyAddressBook() { - return new AddressBook(new UniquePersonList(), new UniqueTagList()); + public static ToDoList generateEmptyToDoList() { + return new ToDoList(new UniqueTaskList()); } - public static XmlSerializableAddressBook generateSampleStorageAddressBook() { - return new XmlSerializableAddressBook(generateEmptyAddressBook()); + public static XmlSerializableToDoList generateSampleStorageToDoList() { + return new XmlSerializableToDoList(generateEmptyToDoList()); } /** @@ -273,49 +257,15 @@ public static Object getLastElement(List list) { } /** - * Removes a subset from the list of persons. - * @param persons The list of persons - * @param personsToRemove The subset of persons. - * @return The modified persons after removal of the subset from persons. - */ - public static TestPerson[] removePersonsFromList(final TestPerson[] persons, TestPerson... personsToRemove) { - List listOfPersons = asList(persons); - listOfPersons.removeAll(asList(personsToRemove)); - return listOfPersons.toArray(new TestPerson[listOfPersons.size()]); - } - - - /** - * Returns a copy of the list with the person at specified index removed. - * @param list original list to copy from - * @param targetIndexInOneIndexedFormat e.g. if the first element to be removed, 1 should be given as index. - */ - public static TestPerson[] removePersonFromList(final TestPerson[] list, int targetIndexInOneIndexedFormat) { - return removePersonsFromList(list, list[targetIndexInOneIndexedFormat-1]); - } - - /** - * Replaces persons[i] with a person. - * @param persons The array of persons. - * @param person The replacement person - * @param index The index of the person to be replaced. + * Replaces tasks[i] with a task. + * @param tasks The array of tasks. + * @param task The replacement task + * @param index The index of the task to be replaced. * @return */ - public static TestPerson[] replacePersonFromList(TestPerson[] persons, TestPerson person, int index) { - persons[index] = person; - return persons; - } - - /** - * Appends persons to the array of persons. - * @param persons A array of persons. - * @param personsToAdd The persons that are to be appended behind the original array. - * @return The modified array of persons. - */ - public static TestPerson[] addPersonsToList(final TestPerson[] persons, TestPerson... personsToAdd) { - List listOfPersons = asList(persons); - listOfPersons.addAll(asList(personsToAdd)); - return listOfPersons.toArray(new TestPerson[listOfPersons.size()]); + public static TestTask[] replaceTaskFromList(TestTask[] tasks, TestTask task, int index) { + tasks[index] = task; + return tasks; } private static List asList(T[] objs) { @@ -326,29 +276,8 @@ private static List asList(T[] objs) { return list; } - public static boolean compareCardAndPerson(PersonCardHandle card, ReadOnlyPerson person) { - return card.isSamePerson(person); - } - - public static Tag[] getTagList(String tags) { - - if (tags.equals("")) { - return new Tag[]{}; - } - - final String[] split = tags.split(", "); - - final List collect = Arrays.asList(split).stream().map(e -> { - try { - return new Tag(e.replaceFirst("Tag: ", "")); - } catch (IllegalValueException e1) { - //not possible - assert false; - return null; - } - }).collect(Collectors.toList()); - - return collect.toArray(new Tag[split.length]); + public static boolean compareCardAndTask(TaskCardHandle card, ReadOnlyTask task) { + return card.isSameTask(task); } } diff --git a/src/test/java/seedu/task/testutil/ToDoListBuilder.java b/src/test/java/seedu/task/testutil/ToDoListBuilder.java new file mode 100644 index 000000000000..7bd3c0f629da --- /dev/null +++ b/src/test/java/seedu/task/testutil/ToDoListBuilder.java @@ -0,0 +1,28 @@ +package seedu.task.testutil; + +import seedu.todolist.model.ToDoList; +import seedu.todolist.model.task.Task; +import seedu.todolist.model.task.UniqueTaskList; + +/** + * A utility class to help with building ToDoList objects. + * Example usage:
+ * {@code ToDoList ab = new ToDoListBuilder().withTask("John", "Doe").withTag("Friend").build();} + */ +public class ToDoListBuilder { + + private ToDoList ToDoList; + + public ToDoListBuilder(ToDoList ToDoList){ + this.ToDoList = ToDoList; + } + + public ToDoListBuilder withTask(Task task) throws UniqueTaskList.DuplicateTaskException { + ToDoList.addTask(task); + return this; + } + + public ToDoList build(){ + return ToDoList; + } +} diff --git a/src/test/java/seedu/task/testutil/TypicalTestTasks.java b/src/test/java/seedu/task/testutil/TypicalTestTasks.java new file mode 100644 index 000000000000..49c0d9c07188 --- /dev/null +++ b/src/test/java/seedu/task/testutil/TypicalTestTasks.java @@ -0,0 +1,132 @@ +package seedu.task.testutil; + +import java.time.LocalDate; + +import seedu.todolist.commons.exceptions.IllegalValueException; +import seedu.todolist.model.ToDoList; +import seedu.todolist.model.task.*; + +/** + * + */ +public class TypicalTestTasks { + + public static TestTask eventWithoutParameter, eventWithLocation, eventWithParameters, + deadlineWithLocation, deadlineWithoutTime, floatWithoutParameter, + floatWithParameters, upcomingEvent, overdueDeadline, invalidLocation, + duplicateNameFloat, taskOneToTestFind, taskTwoToTestFind, taskThreeToTestFind; + + //@@author A0138601M + public TypicalTestTasks() { + try { + eventWithoutParameter = new TaskBuilder().withName("Event without parameter") + .withInterval(getTodayDate(), "11:58pm", getTodayDate(), "11:59pm") + .withLocation(null) + .withRemarks(null) + .withStatus(Status.Type.Incomplete).build(); + eventWithLocation = new TaskBuilder().withName("Event with location") + .withInterval(getTodayDate(), "23:58", getTodayDate(), "23:59") + .withLocation("Office") + .withRemarks(null) + .withStatus(Status.Type.Incomplete).build(); + eventWithParameters = new TaskBuilder().withName("Event with parameters") + .withInterval("27/1/2017", "20:00", "27/1/2017", "23:59") + .withLocation("Office") + .withRemarks("print document") + .withStatus(Status.Type.Incomplete).build(); + deadlineWithLocation = new TaskBuilder().withName("Deadline with location") + .withInterval(null, null, "28 jan 2017", "12:00pm") + .withLocation("Fairprice") + .withRemarks(null) + .withStatus(Status.Type.Incomplete).build(); + deadlineWithoutTime = new TaskBuilder().withName("Deadline without time") + .withInterval(null, null, "29 jan 2017", null) + .withLocation(null) + .withRemarks("graded assignment") + .withStatus(Status.Type.Incomplete).build(); + floatWithoutParameter = new TaskBuilder().withName("Float without parameter") + .withInterval(null, null, null, null) + .withLocation(null) + .withRemarks(null) + .withStatus(Status.Type.Complete).build(); + floatWithParameters = new TaskBuilder().withName("Float with parameters") + .withInterval(null, null, null, null) + .withLocation("school") + .withRemarks("graded assignment") + .withStatus(Status.Type.Overdue).build(); + + //Manually added + upcomingEvent = new TaskBuilder().withName("Upcoming event") + .withInterval("29/1/2017", "10am", "29 jan 2017", "11:30am") + .withLocation("Orchard") + .withRemarks("prepare agenda") + .withStatus(Status.Type.Incomplete).build(); + overdueDeadline = new TaskBuilder().withName("Overdue deadline") + .withInterval(null, null, "30 oct 2016", "1pm") + .withLocation(null) + .withRemarks("bring towel") + .withStatus(Status.Type.Overdue).build(); + + //Task for testing FindCommand + taskOneToTestFind = new TaskBuilder().withName("One two Three") + .withInterval(null, null, "24 jan 2017", null) + .withLocation(null) + .withRemarks("testing") + .withStatus(Status.Type.Incomplete).build(); + + taskTwoToTestFind = new TaskBuilder().withName("one Three Two four") + .withInterval(null, null, "27 jan 2017", null) + .withLocation("NUS") + .withRemarks(null) + .withStatus(Status.Type.Incomplete).build(); + + taskThreeToTestFind = new TaskBuilder().withName("Four One three two") + .withInterval(null, null, "30 jan 2017", null) + .withLocation("home") + .withRemarks(null) + .withStatus(Status.Type.Incomplete).build(); + + } catch (IllegalValueException e) { + e.printStackTrace(); + assert false : "not possible"; + } + } + //@@author + + //@@author A0153736B + private String getTodayDate() { + LocalDate currentDate = LocalDate.now(); + final StringBuilder sb = new StringBuilder(); + sb.append(currentDate.getDayOfMonth() + "/") + .append(currentDate.getMonthValue() + "/") + .append(currentDate.getYear()); + return sb.toString(); + } + //@@author + + public static void loadToDoListWithSampleData(ToDoList ab) { + + try { + ab.addTask(new Task(eventWithoutParameter)); + ab.addTask(new Task(eventWithLocation)); + ab.addTask(new Task(eventWithParameters)); + ab.addTask(new Task(deadlineWithLocation)); + ab.addTask(new Task(deadlineWithoutTime)); + ab.addTask(new Task(floatWithoutParameter)); + ab.addTask(new Task(floatWithParameters)); + } catch (UniqueTaskList.DuplicateTaskException e) { + assert false : "not possible"; + } + } + + public TestTask[] getTypicalTasks() { + return new TestTask[]{eventWithoutParameter, eventWithLocation, eventWithParameters, + deadlineWithLocation, deadlineWithoutTime, floatWithoutParameter, floatWithParameters}; + } + + public ToDoList getTypicalToDoList(){ + ToDoList ab = new ToDoList(); + loadToDoListWithSampleData(ab); + return ab; + } +}