diff --git a/.gitignore b/.gitignore index 45a20de82e87..f1bf0307730f 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,6 @@ lib/* *.log *.log.* *.csv -config.json src/test/data/sandbox/ preferences.json .DS_Store diff --git a/.project b/.project index 1c9339c5f927..216da24c80e5 100644 --- a/.project +++ b/.project @@ -1,7 +1,7 @@ - addressbook-level4 - Project addressbook-level4 created by Buildship. + FlexiTrack + Project FlexiTrack created by Buildship. diff --git a/Collate-TUI.jar b/Collate-TUI.jar new file mode 100644 index 000000000000..50bdfe6902f8 Binary files /dev/null and b/Collate-TUI.jar differ diff --git a/NewUser.txt b/NewUser.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/README.md b/README.md index 249a00b3899c..7436bc18027e 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,10 @@ -[![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-F09-C3/main.svg?branch=master)] +(https://travis-ci.org/CS2103AUG2016-F09-C3/main.svg?branch=master) +[![Coverage Status](https://coveralls.io/repos/github/CS2103AUG2016-F09-C3/main/badge.svg?branch=master)](https://coveralls.io/github/CS2103AUG2016-F09-C3/main?branch=master) -# Address Book (Level 4) +# FlexiTrack (v0.5)
- -* This is a desktop Address Book application. It has a GUI but most of the user interactions happen using - a CLI (Command Line Interface). -* It is a Java sample application intended for students learning Software Engineering while using Java as - the main programming language. -* It is **written in OOP fashion**. It provides a **reasonably well-written** code example that is - **significantly bigger** (around 6 KLoC)than what students usually write in beginner-level SE modules. -* What's different from [level 3](https://github.com/se-edu/addressbook-level3): - * A more sophisticated GUI that includes a list panel and an in-built Browser. - * More test cases, including automated GUI testing. - * Support for *Build Automation* using Gradle and for *Continuous Integration* using Travis CI. - #### Site Map * [User Guide](docs/UserGuide.md) @@ -29,6 +18,9 @@ * 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*. + +* This program uses Natty Time (http://natty.joestelmach.com/) +* This program is build based off Address Book Level 4's code sample #### Licence : [MIT](LICENSE) diff --git a/build.gradle b/build.gradle index 46b06c1e42ec..e5ff2588731d 100644 --- a/build.gradle +++ b/build.gradle @@ -60,6 +60,8 @@ allprojects { exclude group: "junit", module: "junit" } testCompile "org.testfx:openjfx-monocle:$monocleVersion" + + compile group: 'com.joestelmach', name: 'natty', version: '0.12' } sourceSets { @@ -74,10 +76,10 @@ allprojects { } shadowJar { - archiveName = "addressbook.jar" + archiveName = "flexitrack.jar" manifest { - attributes "Main-Class": "seedu.address.MainApp" + attributes "Main-Class": "seedu.flexitrack.MainApp" } destinationDir = file("${buildDir}/jar/") @@ -113,8 +115,8 @@ tasks.coveralls { onlyIf { System.env.'CI' } } -class AddressBookTest extends Test { - public AddressBookTest() { +class FlexiTrackTest extends Test { + public FlexiTrackTest() { forkEvery = 1 systemProperty 'testfx.setup.timeout', '60000' } @@ -128,7 +130,7 @@ class AddressBookTest extends Test { } } -task guiTests(type: AddressBookTest) { +task guiTests(type: FlexiTrackTest) { include 'guitests/**' jacoco { @@ -137,8 +139,8 @@ task guiTests(type: AddressBookTest) { } -task nonGuiTests(type: AddressBookTest) { - include 'seedu/address/**' +task nonGuiTests(type: FlexiTrackTest) { + include 'seedu/flexitrack/**' jacoco { destinationFile = new File("${buildDir}/jacoco/test.exec") @@ -146,7 +148,7 @@ task nonGuiTests(type: AddressBookTest) { } // Test mode depends on whether headless task has been run -task allTests(type: AddressBookTest) { +task allTests(type: FlexiTrackTest) { jacoco { destinationFile = new File("${buildDir}/jacoco/test.exec") } diff --git a/collated/docs/A0127686R.md b/collated/docs/A0127686R.md new file mode 100644 index 000000000000..c38ed9a85ffe --- /dev/null +++ b/collated/docs/A0127686R.md @@ -0,0 +1,123 @@ +# A0127686R +###### /DeveloperGuide.md +``` md +## Appendix E : Product Survey + +####Google Calendar + +#####Pros: +> Help to organise schedules and pre-planned them in advance +> Able to show schedules in multiple view (3 days, day, month or week) format +> Easy to synchronise planned schedules across multiple devices + + +#####Cons: +> Unable to use single line command to add new events +> Each event creation take up around 2 minutes of the user time to fill up the details +> Unable to do partial search for a particular event name +> Confusing timing display on each event. (E.g 11p for night event and 11 for morning event) + + +####Any.Do + +#####Pros: +> Help to add a quick task to the list +> Able to invite friends in the same event or task +> Able to send notification to the user before the event start +> Able to add new task(s) to personalised folder + +``` +###### /UserGuide.md +``` md +## Features + +> **Command Format** +> * Words in `square brackets ([])` are the parameters. +> * Items within `arrow signs (<>)` are optional. +> * Items with `...` after them can have multiple instances. +> * The order of parameters is fixed except for edit command. + +#### Adding a task: `add` +#### Shortcut : `a` +Adds a task to the FlexiTrack.
+Format: `add [task title] < by/ [deadline] >` + +Examples: +* `add CS2103 tutorial 3 ` +* `add CS2103 tutorial 3 by/ Saturday` +* `a CS2103 tutorial 3 by/ tmr 9am` + +#### Adding an event: `add` +#### Shortcut : `a` +Adds a event to the FlexiTrack.
+Format: `add [event title] from/ [starting time] to/ [ending time]` + +Examples: +* `add Bintan trip from/ Saturday to/ Sunday` +* `a CS2103 Lecture from/ Friday 2pm to/ Friday 4pm ` + +``` +###### /UserGuide.md +``` md +#### Finding a task or an event containing any keyword in their title: `find` +#### Shortcut : `f` +Finds a task ot an event whose title contain any of the given keywords.
+Format: `find KEYWORD [MORE_KEYWORDS]` + +> * The search is non case sensitive. e.g `soccer` will match `Soccer` +> * The order of the keywords does not matter. e.g. `soccer dinner` will match `dinner soccer` +> * Only the task/event title is searched. +> * Only full words will be matched e.g. `socc` will not match `soccer` (unless 'f/' keyword is used) +> * Task or event matching at least one keyword will be returned (i.e. `OR` search). + e.g. `soccer` will match `soccer training` +> * Search by exact task name can be activated with the shortcut 'f/' before the task name. + +Examples: +* `find Soccer`
+ Returns `Soccer training` but not `soccer training` +* `find assignment dinner mid-term`
+ Returns Any task/event having `assignment`, `dinner`, or `mid-term` in the title +* `f attend CS2103 lecture`
+ Returns Any task/event having `attend`, `CS2103`, or `lecture` + +#### Finding a specific task or an event containing an exact phrase in their title: `find f/` +#### Shortcut : `f f/` +Finds a task of an event whose title contain any of the given keywords.
+Format: `find f/ EXACT PHRASE` + +``` +###### /UserGuide.md +``` md +## Time Format +FlexiTrack support various timing input. Here are some examples! + +#### Exact timing +User Input | Timing information read by FlexiTrack +---------- | :--------------- +21 June 2018 4pm | Jun 21 16:00:00 +1st January 7.20 | Jan 01 07:20 +April 22nd 5am | Apr 22 05:00 + +#### Relative timing +If today is 1st of February a relative timing input is also possible with FlexiTrack + +User Input | Timing information read by FlexiTrack +---------- | :--------------- +Tomorrow 4pm | Feb 02 16:00:00 +Next week 720am | Feb 08 07:20 +3 weeks 2 pm | Feb 22 14:00 +next month 8am | Mar 01 08:00 + +#### Notes on FlexiTrack timing +1. FlexiTrack does support year. However, make sure that you also specify the hour of the timing + as FlexiTrack will choose timing over year when it is uncertain. +2. When you do not specify the exact timing, FlexiTrack will assign your task to be 8:00 for due + date and starting time, and 17:00 for ending time. + +## 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 FlexiTrack folder. + +``` diff --git a/collated/docs/A0127855W.md b/collated/docs/A0127855W.md new file mode 100644 index 000000000000..65af38847ac2 --- /dev/null +++ b/collated/docs/A0127855W.md @@ -0,0 +1,144 @@ +# A0127855W +###### /DeveloperGuide.md +``` md + +### Undo/Redo Implementation + +Each undoable command has an execute() method and an executeUndo() method. + +2 static Command stacks are maintained. One in the UndoCommand class (doneCommandStack) and the other in the RedoCommand class (undoneCommandStack). From application startup, whenever an undoable command is entered, the created command object is first populated with the data of whatever the command changes, before the execute() method is called. The changes are then applied and the entire command object is pushed into the doneCommandStack. + +Subsequently, when the UndoCommand is entered, the last command entered is popped out of the doneCommandStack and the executeUndo() method is called. The command is then pushed into the undoneCommandStack. + +When the RedoCommand is entered, the last command undone is popped out of the undoneCommand Stack and the execute() method is called, effectively reexecuting the undone command. The command is then pushed into the doneCommandStack to ensure that the we can undo and redo the same command over and over. + +This method of implemeting the undo/redo functionality was chosen due to its simplicity of implementation, as well as the relatively light memory usage, especially when compared to the 'save entire state' method of implementation. + +``` +###### /UserGuide.md +``` md +#### Find free time slots: `gap` +#### Shortcut: `g` +Find and list free time slots in the schedule that is equal to or longer than the specified timing (in hours).
+Format: `find time [number of hours] < [number of slots to find] >` + +> If there is there is a time slot longer than the required free time slot, + then the free time period will be return to you +> By default, find time will only give a single free slot when the number of slots required is not keyed in. + +Examples: +* `gap 3 `
+ You have a minimum of 3 hours free time slot between: today 5pm - 9pm. +* `g 5 3 `
+ You have a minimum of 5 hours free time slot between: Monday 2pm - 9pm, Tuesday 1pm - 6pm and Saturday 9am - 5pm. + +#### Deleting a task or event : `delete` +#### Shortcut : `d` +Deletes the specified task/event from the FlexiTrack.
+Format: `delete [index]` + +> Deletes the task/event 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: +* `delete 2`
+ Deletes the 2nd task/event in the address book. +* `d 1`
+ Deletes the 1st task/event in the results of the `find` command. + +``` +###### /UserGuide.md +``` md +#### Clear the FlexiTrack : `clear` +#### Shortcut : `c` +Clears the FlexiTrack, resetting it to a blank slate.
+Format: `clear` + +> The command can be undone as long as the user does not exit FlexiTrack after clearing. + +#### Edit a task or event: `edit` +#### Shortcut : `e` +Edits the specified task/event from the FlexiTrack.
+Format: `edit [index] ` + + +> * Edits the task/event 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, ... +> * Edit parameters must fit the type of task / event being edited. e.g. duedate should only be edited on a task. +> * Floating tasks can be converted into tasks or events by editing the appropriate parameter. +> * User cannot edit a floating task into an event with only a starting time but no ending time or vice versa. + +Examples: +* `edit 2 n/ Name Edited`
+ Edits the title of the task/event. +* `e 1 from/ today to/ tomorrow`
+ Edits the start and end times of the specified event. +``` +###### /UserGuide.md +``` md +#### List: `list` +#### Shortcut : `l` +Lists tasks and events that match the specified filter.
+Format: `list ` + +> Accepted filters include: +> * future +> * next week +> * mark +> * block +> Unmarked floating tasks will be listed. + +Examples: +* `list next month`
+ Returns a list of next month's tasks and events + +#### Undo operations : `undo` +#### Shortcut : `ud` +Undo the previous operation.
+Format: `undo` + +> The command will only undo commands entered during the current session of FlexiTrack +> Undo works for: add, delete, clear, mark, unmark, block. + +#### Redo operations : `redo` +#### Shortcut : `rd` +Redo the previously undone operation.
+Format: `redo` + +> The command will only redo commands undone during the current session of FlexiTrack + +``` +###### /UserGuide.md +``` md +#### Exiting the program : `exit` +#### Shortcut : `q` +Exits the program.
+Format: `exit` + +``` +###### /UserGuide.md +``` md +## Command Summary + +Command | Shortcut | Format +-------- | ---- | :-------- +Add task | a | `add [task title] ` +Add event | a | `add [event title] from/ [starting time] to/ [ending time]` +Block | b | `block [description] from/ [starting time] to/ [ending time]` +Find time | g | `gap [number of hours] < [number of slots to find] >` +Delete | d | `delete [index]` +Clear | c | `clear` +Edit | e | `edit [index] ` +Mark | m | `mark [index]` +Unmark | u | `unmark [index]` +Find | f | `find [key words] < [key words] >` +List | l | `list ` +Select | s | `select [index]` +Undo | ud | `undo` +Redo | rd | `redo` +Change Storage Path | cs | `cs [path]` +Exit | q | `exit` +Help | h | `help ` +``` diff --git a/collated/docs/A0138455Y.md b/collated/docs/A0138455Y.md new file mode 100644 index 000000000000..31e0727611f7 --- /dev/null +++ b/collated/docs/A0138455Y.md @@ -0,0 +1,73 @@ +# A0138455Y +###### /UserGuide.md +``` md +#### Block multiple time slot for an event : `block` +#### Shortcut : `b` +Block another time slot for an unconfirmed existing event.
+Format: `block [Description] from/ [starting time] to/ [ending time]` + +> The new block period must not overlapping current block task. +> New event will not be allow to add in if the period of the new event overlapping any blocked task from block list. + +Examples: +* `block for cs2103 project from/ 5pm to/ 7pm`
+``` +###### /UserGuide.md +``` md +#### Mark a task as complete : `mark` +#### Shortcut : `m` +Mark an existing task to complete and move it to the bottom of the list.
+Format: `mark [index]` + +> Mark the task/event 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: +* `mark 5`
+ +#### Mark a task as complete : `unmark` +#### Shortcut : `u` +Mark an existing task to complete and move it to the bottom of the list.
+Format: `unmark [index]` + +> Unmark the taks/event 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: +* `unmark 5`
+``` +###### /UserGuide.md +``` md +#### Specify storage location: `cs` +Specify the storage location where the program save the data.
+Format: `cs [path]` + +Examples: +* `cs data/newStorage `
+* `cs newDataStorage `
+ +Limitation: This feature Only allow user to change storage path within the FlexiTrack folder. + +> [path] can only contains alphanumeric or following special character '\', '/', '-', ':', '.', '_'. +``` +###### /UserGuide.md +``` md +#### Viewing help : `help` +#### Shortcut : `h` +Format: `help` or `help [command word]` + +Examples: +* `help add` +* `help edit` +* `h delete` + +> Help is also shown if you enter an incorrect command e.g. `abcd` +> default help message will show a list of all command word, e.g. enter `help` + +#### Saving the data +Address book data are saved in the hard disk automatically after any command that changes the data.
+There is no need to save manually. + +``` diff --git a/collated/docs/A0147092E.md b/collated/docs/A0147092E.md new file mode 100644 index 000000000000..8510b4a0a209 --- /dev/null +++ b/collated/docs/A0147092E.md @@ -0,0 +1,30 @@ +# A0147092E +###### /UserGuide.md +``` md +#### Adding an recurring task: `add` +#### Shortcut : `a` +Adds a task(recursive) to the FlexiTrack.
+Format: `add [task title] fr/ [number of occurrence]` + +Examples: +* `add Plan meet-up for assignment fr/ 5` +* `add Watch DareDevil Season 1 EP fr/ 10` + +#### Adding an recurring task (with deadline): `add` +#### Shortcut : `a` +Adds a task(recursive) to the FlexiTrack.
+Format: `add [task title] fr/ [number of occurrence] ty/ ["daily" | "weekly" | "monthly"] from/ [starting time] to/ [ending time]` + +Examples: +* `add Submit PC1222 Labsheet fr/ 5 ty/ week by/ Tuesday 5pm` +* `add complete CS2103 post-lecture quiz fr/ 10 ty/ week by/ Sunday 10pm` + +#### Adding an recurring event: `add` +#### Shortcut : `a` +Adds a event(recursive) to the FlexiTrack.
+Format: `add [event title] fr/ [number of occurrence] ty/ ["daily" | "weekly" | "monthly"] from/ [starting time] to/ [ending time]` + +Examples: +* `add attend PC1222 tutorial fr/ 5 ty/ weekly from/ Tuesday 5pm to/ Tuesday 6pm` +* `add attend CS2103 lecture fr/ 10 ty/ weekly from/ Fri 2pm to/ Fri 4pm` +``` diff --git a/collated/main/A0127686R.md b/collated/main/A0127686R.md new file mode 100644 index 000000000000..28a4a2da6090 --- /dev/null +++ b/collated/main/A0127686R.md @@ -0,0 +1,1517 @@ +# A0127686R +###### /java/seedu/flexitrack/logic/commands/AddCommand.java +``` java + @Override + public void executeUndo() { + Task toDelete = toAdd; + + try { + model.deleteTask(toDelete); + } catch (TaskNotFoundException pnfe) { + assert false : "The target task cannot be missing"; + } + } + +``` +###### /java/seedu/flexitrack/logic/commands/Command.java +``` java + /** + * Record the command to perform undo + * + * @param command + */ + public void recordCommand(Command command){ + UndoCommand.doneCommandStack.add(command); + } + + int getNumOfOccurrrence(){ + return 0; + } + + void setNumOfOccurrrence(int numOfOccurrrence) { + assert numOfOccurrrence != 0; + } +} +``` +###### /java/seedu/flexitrack/logic/commands/EditCommand.java +``` java + @Override + public void executeUndo() { + Task toDelete = editedTask; + Task toAddBack = taskStore; + + try { + model.deleteTask(toDelete); + } catch (TaskNotFoundException pnfe) { + assert false : "The target task cannot be missing"; + } + + try { + model.addTask(toAddBack); + } catch (DuplicateTaskException e) { + indicateAttemptToExecuteIncorrectCommand(); + } + } + +``` +###### /java/seedu/flexitrack/logic/commands/GapCommand.java +``` java +package seedu.flexitrack.logic.commands; + +import java.util.List; + +import seedu.flexitrack.model.task.DateTimeInfo; + +/** + * Find a gap with minimum time specified by the user. + */ +public class GapCommand extends Command { + + public static final String COMMAND_WORD = "gap"; + public static final String COMMAND_SHORTCUT = "g"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ", shortcut [" + COMMAND_SHORTCUT + "]" + + ": Find the earliest specified timing available.\n" + + "1. To find a gap with default number of gap searched (3) - Parameters: [specified timing] \n" + + " Example: " + COMMAND_WORD + " 3 hours \n" + + "2. To find a specific number of gap - Parameters: [specified timing] n/[number of gap to find] \n" + + " Example: " + COMMAND_WORD + " 30 minutes n/4\n" + + "Note that :\n" + + " 1. The specified timing and number of gap need to be in digit.\n" + + " 2. Timing for minutes can't be longer than 59 minutes, for hour it can't be longer than 23 hours" + + "and for days it can't be more than 29 days\n" + + "Example of accepted specified timing: 20m, 30 minutes, 2 hours, 12 h, 2 days.\n" + + "Example of rejected specified timing: 70 minutes, 2 hours 30 minutes"; + + public static final String MESSAGE_SUCCESS = "The earliest %1$s free time are found... "; + public static final String WORD_DAY = "day"; + public static final String WORD_HOUR = "hour"; + public static final String WORD_MINUTE = "minute"; + public static final String INITIAL_DAY = "d"; + public static final String INITIAL_HOUR = "h"; + public static final String INITIAL_MINUTE = "m"; + public static final int DEFAULT_NUMBER_OF_SLOT = 3; + public static final int REF_NO_DAY = 2; + public static final int REF_NO_HOUR = 1; + public static final int REF_NO_MINUTE = 0; + + private final int keyword; + private final int length; + private final int numberOfSlot; + + public GapCommand(int keyword, int length, int numberOfSlot) { + this.keyword = keyword; + this.length = length; + this.numberOfSlot = numberOfSlot; + } + + @Override + public CommandResult execute() { + List listOfTiming = model.findSpecifiedGapTiming(keyword, length, numberOfSlot); + String theListOfDates = putResultIntoString(listOfTiming); + String keywordString = putKeywordIntoString(); + return new CommandResult((String.format(MESSAGE_SUCCESS, keywordString) + theListOfDates)); + } + + /** + * Put the user specified timing back into string + * + * @return the specified timing by the users + */ + private String putKeywordIntoString() { + String keywordString = ""; + switch (keyword) { + case 0: + keywordString = "minute"; + break; + case 1: + keywordString = "hour"; + break; + case 2: + keywordString = "day"; + break; + } + keywordString = length + " " + keywordString; + if (length > 1) { + keywordString = keywordString + "s"; + } + return keywordString; + } + + /** + * Rearrange and put into String the starting and ending time of the gap + * @param listOfTiming + * @return The timing of the gap in string, ready to be shown to the user + */ + private String putResultIntoString(List listOfTiming) { + String theListOfDates = ""; + int iter = 0; + for (; iter < listOfTiming.size() - 1; iter++) { + if (iter == 0 && listOfTiming.get(iter).toString().equals(DateTimeInfo.getCurrentTime().toString())) { + theListOfDates = theListOfDates + "\nBetween: now "; + } else { + theListOfDates = theListOfDates + "\nBetween: " + listOfTiming.get(iter).toString(); + } + iter = iter + 1; + theListOfDates = theListOfDates + " to: " + listOfTiming.get(iter).toString(); + + } + if ((iter + 1) / 2 < numberOfSlot) { + theListOfDates = theListOfDates + "\nFree from: " + listOfTiming.get(iter).toString() + " onwards. "; + } + return theListOfDates; + } + +} +``` +###### /java/seedu/flexitrack/logic/commands/ListCommand.java +``` java +package seedu.flexitrack.logic.commands; + +import java.util.logging.Logger; + +import seedu.flexitrack.commons.core.LogsCenter; +import seedu.flexitrack.model.ModelManager; + +/** + * Lists all task in the FlexiTrack to the user. + */ +public class ListCommand extends Command { + + public static final String COMMAND_WORD = "list"; + public static final String COMMAND_SHORTCUT = "l"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ", Shortcut [" + COMMAND_SHORTCUT + "]" + + ": List all the to do lists in FlexiTrack.\n" + + COMMAND_WORD + " future : List all the to do lists that is due or start in the future.\n" + + COMMAND_WORD + " past : List all the to do lists in the past.\n" + + COMMAND_WORD + " mark: List all the to do lists that has been marked.\n" + + COMMAND_WORD + " unmark: List all the to do lists in that has not been marked.\n" + + "Example: " + COMMAND_WORD + " past \n"; + public static final String MESSAGE_SUCCESS = "Listed all tasks"; + public static final String LIST_FUTURE_COMMAND = "future"; + public static final String LIST_PAST_COMMAND = "past"; + public static final String LIST_MARK_COMMAND = "mark"; + public static final String LIST_UNMARK_COMMAND = "unmark"; + public static final String LIST_BLOCK_COMMAND = "block"; + public static final String LIST_LAST_COMMAND = "last"; + public static final String LIST_NEXT_COMMAND = "next"; + public static final String LIST_LAST_WEEK_COMMAND = "last week"; + public static final String LIST_LAST_MONTH_COMMAND = "last month"; + public static final String LIST_NEXT_WEEK_COMMAND = "next week"; + public static final String LIST_NEXT_MONTH_COMMAND = "next month"; + public static final String LIST_UNSPECIFIED_COMMAND = ""; + + public final String arguments; + + private static final Logger logger = LogsCenter.getLogger(ModelManager.class); + + public ListCommand(String args) { + this.arguments = args; + logger.info("----------------[LIST COMMAND STORE ARGS][" + args.toUpperCase() + "]"); + } + + @Override + public CommandResult execute() { + if (arguments.equals(LIST_UNSPECIFIED_COMMAND)) { + model.updateFilteredListToShowAll(); + } else { + model.updateFilteredListToFitUserInput(arguments); + } + return new CommandResult(MESSAGE_SUCCESS); + } + +} +``` +###### /java/seedu/flexitrack/logic/commands/MarkCommand.java +``` java + @Override + public void executeUndo() { + Task toDelete = markedTask; + Task toAddBack = taskStore; + + try { + model.deleteTask(toDelete); + } catch (TaskNotFoundException pnfe) { + assert false : "The target task cannot be missing"; + } + + try { + model.addTask(toAddBack); + } catch (DuplicateTaskException e) { + indicateAttemptToExecuteIncorrectCommand(); + } + } + +``` +###### /java/seedu/flexitrack/logic/commands/UndoCommand.java +``` java +package seedu.flexitrack.logic.commands; + +import java.util.Stack; +import java.util.logging.Logger; + +import seedu.flexitrack.commons.core.LogsCenter; +import seedu.flexitrack.model.ModelManager; +import seedu.flexitrack.model.task.ReadOnlyTask; + +/** + * Clears the FlexiTrack. + */ +public class UndoCommand extends Command { + + public static final String COMMAND_WORD = "undo"; + public static final String COMMAND_SHORTCUT = "ud"; // TODO: impiment ctrl + Z + public static final String MESSAGE_USAGE = COMMAND_WORD + ", Shortcut [" + COMMAND_SHORTCUT + "]" + + ": Clear the to do lists in FlexiTrack.\n" + "Example: " + COMMAND_WORD; + public static final String MESSAGE_SUCCESS = "Your last command has been undone!"; + public static final String MESSAGE_NOT_SUCCESS = "You have no command to undo!"; + + private static final Logger logger = LogsCenter.getLogger(ModelManager.class); + + // Stores all the commands done from app launch + static Stack doneCommandStack = new Stack(); + + public UndoCommand() { + } + + @Override + public CommandResult execute() { + Command undo = null; + + logger.info("----------------[UNDO COMMAND][ executing undo ]"); + + if (doneCommandStack.size() == 0) { + return new CommandResult(String.format(MESSAGE_NOT_SUCCESS)); + } + + + undo = doneCommandStack.pop(); + if (undo instanceof AddCommand && undo.getNumOfOccurrrence() !=0 ){ + int numOfOccurrrence = undo.getNumOfOccurrrence(); + undo.setNumOfOccurrrence(0); + for (int i = 1; i < numOfOccurrrence; i++) { + undo.executeUndo(); + RedoCommand.undoneCommandStack.push(undo); + undo = doneCommandStack.pop(); + } + undo.setNumOfOccurrrence(numOfOccurrrence); + } + + undo.executeUndo(); + RedoCommand.undoneCommandStack.push(undo); + model.indicateFlexiTrackerChanged(); + return new CommandResult(MESSAGE_SUCCESS + "\n" + undo.getUndoMessage()); + } + +} +``` +###### /java/seedu/flexitrack/logic/commands/UnmarkCommand.java +``` java + @Override + public void executeUndo() { + Task toDelete = unMarkedTask; + Task toAddBack = taskStore; + + try { + model.deleteTask(toDelete); + } catch (TaskNotFoundException pnfe) { + assert false : "The target task cannot be missing"; + } + + try { + model.addTask(toAddBack); + } catch (DuplicateTaskException e) { + indicateAttemptToExecuteIncorrectCommand(); + } + } + +``` +###### /java/seedu/flexitrack/logic/parser/Parser.java +``` java + private final Logger logger = LogsCenter.getLogger(Parser.class); + + private static final HashMap SHORTCUT_LIST_MAP = new HashMap(); // more + static { + SHORTCUT_LIST_MAP.put("f", ListCommand.LIST_FUTURE_COMMAND); + SHORTCUT_LIST_MAP.put("p", ListCommand.LIST_PAST_COMMAND); + SHORTCUT_LIST_MAP.put("m", ListCommand.LIST_MARK_COMMAND); + SHORTCUT_LIST_MAP.put("u", ListCommand.LIST_UNMARK_COMMAND); + SHORTCUT_LIST_MAP.put("b", ListCommand.LIST_BLOCK_COMMAND); + SHORTCUT_LIST_MAP.put("l", ListCommand.LIST_LAST_COMMAND); + SHORTCUT_LIST_MAP.put("n", ListCommand.LIST_NEXT_COMMAND); + SHORTCUT_LIST_MAP.put("lw", ListCommand.LIST_LAST_WEEK_COMMAND); + SHORTCUT_LIST_MAP.put("lm", ListCommand.LIST_LAST_MONTH_COMMAND); + SHORTCUT_LIST_MAP.put("nw", ListCommand.LIST_NEXT_WEEK_COMMAND); + SHORTCUT_LIST_MAP.put("nm", ListCommand.LIST_NEXT_MONTH_COMMAND); + } + // '/' forward slashes are reserved for delimiter prefixes + private static final Pattern TASK_FIND_GAP_WITH_NUMBER_ARGS_FORMAT = Pattern + .compile("(?[^/]+)" + "[Nn]/(?[^/]+)"); + private static final Pattern TASK_FIND_GAP_ARGS_FORMAT = Pattern.compile("(?[^/]+)"); + + private static final Pattern TASK_EVENT_TYPE_DATA_ARGS_FORMAT = Pattern + .compile("(?.+)" + "[fF][rR][oO][mM]/(?[^/]+)" + "[Tt][Oo]/(?[^/]+)"); + private static final Pattern TASK_DEADLINE_TYPE_DATA_ARGS_FORMAT = Pattern + .compile("(?.+)" + "[Bb][Yy]/(?[^/]+)"); + private static final Pattern TASK_FLOATING_TYPE_DATA_ARGS_FORMAT = Pattern.compile("(?.+)"); + +``` +###### /java/seedu/flexitrack/logic/parser/Parser.java +``` java + /** + * Prepare the user input arguments to be passed GapCommand class + * + * @param args + * @return a new Gap Command class + */ + private Command prepareGap(String args) { + args.toLowerCase(); + + Matcher matcher = TASK_FIND_GAP_WITH_NUMBER_ARGS_FORMAT.matcher(args.trim()); + int numberOfSlot = GapCommand.DEFAULT_NUMBER_OF_SLOT; + + if (matcher.matches()) { + args = matcher.group("info").trim(); + try { + numberOfSlot = Integer.parseInt(matcher.group("numberOfGaps").trim()); + } catch (NumberFormatException nfe) { + logger.info("----------------[GAP COMMAND PARSER][" + " user input length without a digit " + "]"); + return new IncorrectCommand( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, MESSAGE_NUMBER_NEED_TO_BE_IN_DIGIT)); + } + } else { + if (args.trim().equals("") || args.trim().contains("n/")) { + logger.info("----------------[GAP COMMAND PARSER][" + " user used n/ without specifying the number " + "]"); + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, GapCommand.MESSAGE_USAGE)); + } + matcher = TASK_FIND_GAP_ARGS_FORMAT.matcher(args.trim()); + matcher.matches(); + args = matcher.group("info").trim(); + } + + return issueGapCommandIfArgsIsValid(args, numberOfSlot); + } + + /** + * Test if the args is valid, if it is convert the length and key keywords into integer representing them + * and issue a new Gap Command, else log and return invalid command + * + * @param args Arguments of the length and keyword + * @param numberOfSlot The number of time slot to be find + * @return GapCommand or IncorrectCommand + */ + private Command issueGapCommandIfArgsIsValid(String args, int numberOfSlot) { + if (isGapArgumentValid(args)) { + try { + int keyword = extractKeywordFromArgs(args); + int length = extractLength(args); + return new GapCommand(keyword, length, numberOfSlot); + + } catch (NumberFormatException nfe) { + logger.info("----------------[GAP COMMAND PARSER][" + " Invalid user input " + "]"); + return new IncorrectCommand( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, MESSAGE_NUMBER_NEED_TO_BE_IN_DIGIT)); + } + } else { + logger.info("----------------[GAP COMMAND PARSER][" + " Invalid user input " + "]"); + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, GapCommand.MESSAGE_USAGE)); + } + } + + /** + * Extract the length of the duration from string in keywords to number + * + * @param args + * @return length of duration in integer + * @throws NumberFormatException + */ + private int extractLength(String args) throws NumberFormatException { + String length = args.replace(GapCommand.WORD_DAY + "s", "").replace(GapCommand.WORD_HOUR + "s", "") + .replace(GapCommand.WORD_MINUTE + "s", "").replace(GapCommand.WORD_DAY, "") + .replace(GapCommand.WORD_HOUR, "").replace(GapCommand.WORD_MINUTE, "") + .replace(GapCommand.INITIAL_DAY, "").replace(GapCommand.INITIAL_MINUTE, "") + .replace(GapCommand.INITIAL_HOUR, ""); + if (length.trim().equals("")) { + return 1; + } else { + return Integer.parseInt(length.trim()); + } + } + + /** + * Extract the keyword from the arguments and return it in integer reference + * number + * + * @param args + * @return number representing each key word. + */ + private int extractKeywordFromArgs(String args) { + if (args.contains(GapCommand.WORD_DAY) || args.contains(GapCommand.WORD_DAY + "s") + || args.contains(GapCommand.INITIAL_DAY)) { + return GapCommand.REF_NO_DAY; + } + if (args.contains(GapCommand.WORD_HOUR) || args.contains(GapCommand.WORD_HOUR + "s") + || args.contains(GapCommand.INITIAL_HOUR)) { + return GapCommand.REF_NO_HOUR; + } else { + return GapCommand.REF_NO_MINUTE; + } + } + + /** + * Find out if the argument is a valid argument for a GapCommand. The + * argument could not have more than one timing key words ( minute, hour or + * day ) + * + * @param args + * @return true if the argument is a valid argument + */ + private boolean isGapArgumentValid(String args) { + int numberOfMatch = 0; + + if (args.contains(GapCommand.WORD_DAY) || args.contains(GapCommand.WORD_DAY + "s") + || args.contains(GapCommand.INITIAL_DAY)) { + numberOfMatch = numberOfMatch + 1; + } + if (args.contains(GapCommand.WORD_HOUR) || args.contains(GapCommand.WORD_HOUR + "s") + || args.contains(GapCommand.INITIAL_HOUR)) { + numberOfMatch = numberOfMatch + 1; + } + if (args.contains(GapCommand.WORD_MINUTE) || args.contains(GapCommand.WORD_MINUTE + "s") + || args.contains(GapCommand.INITIAL_MINUTE)) { + numberOfMatch = numberOfMatch + 1; + } + return numberOfMatch == 1; + } + +``` +###### /java/seedu/flexitrack/logic/parser/Parser.java +``` java + /** + * Check if the arguments are valid for list Command + * + * @param arguments The user inputed argument + * @return New ListCommand containing arguments + */ + private Command prepareList(String arguments) { + String parsedArguments = parseListWord(arguments.trim()); + parsedArguments = parsedArguments.trim(); + try { + if (isValideListFormat(parsedArguments)) { + return new ListCommand(parsedArguments); + } + } catch (IllegalValueException e) { + logger.info("----------------[LIST COMMAND PARSER][" + " Invalid user input " + "]"); + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, ListCommand.MESSAGE_USAGE)); + } + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, ListCommand.MESSAGE_USAGE)); + } + + /** + * if the argument is a shortcut, return the parsed proper word + * + * @param listWord The list argument key word inputed by the user + * @return The pair proper word, if not found return default + */ + private String parseListWord(String listWord) { + assert listWord != null; + return SHORTCUT_LIST_MAP.getOrDefault(listWord, listWord); + } + +``` +###### /java/seedu/flexitrack/logic/parser/Parser.java +``` java + /** + * @param arguments + * @return + * @throws IllegalValueException + */ + private boolean isValideListFormat(String arguments) throws IllegalValueException { + String dateInfo = (arguments.replace(ListCommand.LIST_FUTURE_COMMAND, "").replace(ListCommand.LIST_PAST_COMMAND, ""). + replace(ListCommand.LIST_UNMARK_COMMAND, "").replace(ListCommand.LIST_MARK_COMMAND, ""). + replace(ListCommand.LIST_LAST_MONTH_COMMAND, "").replace(ListCommand.LIST_LAST_WEEK_COMMAND, ""). + replace(ListCommand.LIST_NEXT_MONTH_COMMAND, "").replace(ListCommand.LIST_NEXT_WEEK_COMMAND, ""). + replace(ListCommand.LIST_BLOCK_COMMAND, "").trim()); + if ( !dateInfo.equals("") ){ + DateTimeInfoParser timeArgs = new DateTimeInfoParser(dateInfo); + assert timeArgs != null; + } + return (arguments.contains(ListCommand.LIST_FUTURE_COMMAND) || arguments.contains(ListCommand.LIST_UNMARK_COMMAND) + || arguments.contains(ListCommand.LIST_PAST_COMMAND) || arguments.contains(ListCommand.LIST_MARK_COMMAND) + || arguments.contains(ListCommand.LIST_UNSPECIFIED_COMMAND) || arguments.contains(ListCommand.LIST_LAST_WEEK_COMMAND) + || arguments.contains(ListCommand.LIST_LAST_MONTH_COMMAND) || arguments.contains(ListCommand.LIST_NEXT_MONTH_COMMAND) + || arguments.contains(ListCommand.LIST_NEXT_WEEK_COMMAND) || arguments.contains(ListCommand.LIST_BLOCK_COMMAND)); + } + +``` +###### /java/seedu/flexitrack/logic/parser/Parser.java +``` java + /** + * 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) { +``` +###### /java/seedu/flexitrack/logic/parser/Parser.java +``` java + args = args.trim(); + final Matcher matcherEvent = TASK_EVENT_TYPE_DATA_ARGS_FORMAT.matcher(args); + final Matcher matcherDeadline = TASK_DEADLINE_TYPE_DATA_ARGS_FORMAT.matcher(args); + final Matcher matcherFloating = TASK_FLOATING_TYPE_DATA_ARGS_FORMAT.matcher(args); +``` +###### /java/seedu/flexitrack/logic/parser/Parser.java +``` java + if (matcherEvent.matches()) { + return addEventTask(matcherEvent); + } else if (matcherDeadline.matches()) { + return addDeadlineTask(matcherDeadline); + } else if (matcherFloating.matches()) { + return addFloatingTask(matcherFloating); + } else { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); + } + } +``` +###### /java/seedu/flexitrack/logic/parser/Parser.java +``` java + /** + * Add a floating task, null date for dudate, starting time and ending time. + * + * @return new AddCommand for Floating task + * @throws IllegalValueException + */ + private AddCommand addFloatingTask(Matcher matcher) throws IllegalValueException { + return new AddCommand(matcher.group("name"), EMPTY_TIME_INFO, EMPTY_TIME_INFO, EMPTY_TIME_INFO); + } + + /** + * Add a deadline task, null date for starting time and ending time. + * + * @return new AddCommand for deadline task + * @throws IllegalValueException + */ + private AddCommand addDeadlineTask(Matcher matcher) throws IllegalValueException { + return new AddCommand(matcher.group("name"), matcher.group("dueDate"), EMPTY_TIME_INFO, EMPTY_TIME_INFO); + } + + /** + * Add an event task, null date for dudate. + * + * @return new AddCommand for event task + * @return + * @throws IllegalValueException + */ + private AddCommand addEventTask(Matcher matcher) throws IllegalValueException { + return new AddCommand(matcher.group("name"), EMPTY_TIME_INFO, matcher.group("startTime"), + matcher.group("endTime")); + } + +``` +###### /java/seedu/flexitrack/model/FlexiTrack.java +``` java + /** + * Find the next available time slots that has minimum gap as specified by + * the users. If there are less gap then specified, return the starting time + * where there are no more events + * + * @param keyword Represent the keyword in number. 0 represent minute and 4 represent years + * @param length The length of the duration + * @param numberOfSlot The number of slot to be found + * @return The list of dates where gap are available + */ + public List findNextAvailableSlots(int keyword, int length, int numberOfSlot) { + DateTimeInfo dateNow = DateTimeInfo.getCurrentTime(); + List listOfPossibleTiming = new ArrayList(); + + for (Task task : task.getInternalList()) { + + if (listOfPossibleTiming.size() > (numberOfSlot * 2 - 1)) { + return listOfPossibleTiming; + } + + if (canTheGapBeFound(task, dateNow, keyword, length)) { + listOfPossibleTiming.add(dateNow); + listOfPossibleTiming.add(task.getStartTime()); + dateNow = task.getEndTime(); + } + + if (dateNow.isInTheFuture(task.getEndTime())) { + dateNow = task.getEndTime(); + } + } + listOfPossibleTiming.add(dateNow); + return listOfPossibleTiming; + } + + /** + * Process the data to see if there is any gap available + * + * @param task The current task of interest + * @param dateNow The current Date + * @param keyword Represent the keyword in number. 0 represent minute and 4 represent years + * @param length The length of the duration + * @return true when there is an available gap + */ + private boolean canTheGapBeFound(Task task, DateTimeInfo dateNow, int keyword, int length) { + if (task.getIsEvent() && dateNow.isInTheFuture(task.getStartTime())) { + return isTheLengthOfTheGapSatisfied(task, dateNow, keyword, length); + } + return false; + } + + /** + * Process the task data to calculate if there is enough gap between the events + * + * @param task The current task of interest + * @param dateNow The current date + * @param keyword Represent the keyword in number. 0 represent minute and 4 represent years + * @param length The length of the duration + * @return True if the event starts after the current timing. + */ + private boolean isTheLengthOfTheGapSatisfied(Task task, DateTimeInfo dateNow, int keyword, int length) { + int[] differenceInTime = new int[5]; + differenceInTime = DateTimeInfo.durationBetweenTwoTiming(dateNow.toString(), task.getStartingTimeInString()); + if (differenceInTime[0] >= 0) { + differenceInTime[keyword] = differenceInTime[keyword] - length; + return doesTheGapAtLeastAsLongAsTimingSpecified(keyword, differenceInTime); + } + return false; + } + + /** + * Decide if the gap between two timing is long enough to satisfied the user + * + * @param keyword Represent the keyword in number. 0 represent minute and 4 represent years + * @param differenceInTime The difference in time + * @return True if the gap is longer than what user specified. + */ + private boolean doesTheGapAtLeastAsLongAsTimingSpecified(int keyword, int[] differenceInTime) { + int countNumberOfZero = 0; + for (int i = keyword; i < 5; i++) { + if (differenceInTime[i] > 0) { + return true; + } else if (differenceInTime[i] == 0) { + countNumberOfZero = countNumberOfZero + 1; + } + } + return (countNumberOfZero == (5 - keyword)) ? true : false; + } + + //@@ A0127686R + /** + * Find the index of a task given + * + * @param task The task of interest + * @return The index of the task in int + */ + public int findIndexOfTask(Task task) { + return this.task.findIndexOfTask(task); + + } + +} +``` +###### /java/seedu/flexitrack/model/Model.java +``` java + /** Updates the filter of the filtered task list to show future tasks */ + void updateFilteredListToFitUserInput( String args ); + +``` +###### /java/seedu/flexitrack/model/Model.java +``` java + /** + * Refresh the TaskList Panel to reflect the changes made + */ + void indicateFlexiTrackerChanged(); + + /** + * Find gap with length and number of slots specified by the user + * @param keyword Represent the keyword in number. 0 represent minute and 4 represent years + * @param length The length of the duration + * @param numberOfSlot The number of slot to be found + * @return The list of the Starting time and ending time for each gap found + */ + List findSpecifiedGapTiming(int keyword, int length, int numberOfSlot); + +} +``` +###### /java/seedu/flexitrack/model/ModelManager.java +``` java + @Override + public void updateFilteredListToFitUserInput(String args){ + updateFilteredTaskList(new PredicateExpression(new DateQualifier(args))); + } + +``` +###### /java/seedu/flexitrack/model/ModelManager.java +``` java + /** + * Helper class to process if a particular task should be shown on the list + */ + private class DateQualifier implements Qualifier { + private String keyWords; + private String dateInfo; + + DateQualifier(String keyWord) { + this.keyWords = keyWord; + this.dateInfo = trimKeyWords(keyWord); + } + + /** + * Delete all the list command that is not date from the keywords + * + * @param keyrordArgs A string containing the keywords and other input + * @return The trimmed keyword in a string + */ + private String trimKeyWords(String keyrordArgs) { + return keyrordArgs.replace(ListCommand.LIST_FUTURE_COMMAND, "").replace(ListCommand.LIST_PAST_COMMAND, "") + .replace(ListCommand.LIST_UNMARK_COMMAND, "").replace(ListCommand.LIST_MARK_COMMAND, "") + .replace(ListCommand.LIST_LAST_WEEK_COMMAND, "").replace(ListCommand.LIST_LAST_MONTH_COMMAND, "") + .replace(ListCommand.LIST_NEXT_WEEK_COMMAND, "").replace(ListCommand.LIST_NEXT_MONTH_COMMAND, "") + .replace(ListCommand.LIST_BLOCK_COMMAND, "").trim(); + } + + @Override + public boolean run(ReadOnlyTask task) { + + if (!isTaskGoingToBeShown(task)) { + return false; + } + + if (keyWords.contains(ListCommand.LIST_UNMARK_COMMAND)) { + return !task.getIsDone(); + } else if (keyWords.contains(ListCommand.LIST_MARK_COMMAND)) { + return task.getIsDone(); + } else if (keyWords.contains(ListCommand.LIST_BLOCK_COMMAND)) { + return task.getIsBlock(); + } + + + return isTaskGoingToBeShown(task); + + } + + /** + * Check of the task will be shown to the user in the panel list + * + * @param task The current task of interest + * @return True if the task met all the requirement and will be shown + */ + private boolean isTaskGoingToBeShown(ReadOnlyTask task) { + if (keyWords.contains(ListCommand.LIST_FUTURE_COMMAND)) { + return isTaskInTheFuture(task); + } else if (keyWords.contains(ListCommand.LIST_PAST_COMMAND)) { + return isTaskInThePast(task); + } else if (keyWords.contains(ListCommand.LIST_LAST_COMMAND) + || keyWords.contains(ListCommand.LIST_NEXT_COMMAND)) { + return isTaskWithinTheSpecifiedTiming(task); + } else if (!dateInfo.equals("")) { + return doesTaskCrossTheParticularStatedDate(task); + } else { + return true; + } + } + + /** + * Process If the task happens on a particular date + * + * @param task The current task of interest + * @return True if task contain or cross the date + */ + private boolean doesTaskCrossTheParticularStatedDate(ReadOnlyTask task) { + DateTimeInfo dateTimeInfo=null; + try { + dateTimeInfo = new DateTimeInfo (dateInfo); + } catch (IllegalValueException e) { + logger.warning("----------------[ModelManager][" + " THIS ERROR SHOULD NOT OCCUR. METHOD USING A VALID INPUT " + "]"); + } + return dateTimeInfo.isOnTheDate(task); + } + + /** + * Process if the task happens between now and the time stated + * + * @param task + * The current task of interest + * @return True if task is within the stated time + */ + private boolean isTaskWithinTheSpecifiedTiming(ReadOnlyTask task) { + return DateTimeInfo.withInTheDuration(keyWords, task, DateTimeInfo.getCurrentTime().toString()); + } + + /** + * Process if a particular task has passed + * + * @param task The current task of interest + * @return True if it has passed + */ + private boolean isTaskInThePast(ReadOnlyTask task) { + return DateTimeInfo.getCurrentTime().isInThePast(task.getEndingTimeOrDueDate()); + } + + /** + * Process if a particular task has not passed yet + * + * @param task The current task of interest + * @return True if it has not passed yet + */ + private boolean isTaskInTheFuture(ReadOnlyTask task) { + if (task.getIsNotFloatingTask()) { + return DateTimeInfo.getCurrentTime().isInTheFuture(task.getEndingTimeOrDueDate()); + } else { + return !task.getIsDone(); + } + } + + } + + +} +``` +###### /java/seedu/flexitrack/model/task/DateTimeInfo.java +``` java +package seedu.flexitrack.model.task; + +import java.util.Date; +import java.util.List; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.joestelmach.natty.DateGroup; + +import seedu.flexitrack.commons.core.LogsCenter; +import seedu.flexitrack.commons.exceptions.IllegalValueException; +import seedu.flexitrack.logic.commands.ListCommand; +import seedu.flexitrack.logic.parser.Parser; + +/** + * Represents a DateTimeInfo class in FlexiTrack + */ +public class DateTimeInfo implements Comparable { + public static final String MESSAGE_DATETIMEINFO_CONSTRAINTS = "Invalid time inputed. Please check your spelling!"; + public static final String MESSAGE_FROM_IS_AFTER_TO = "Please check the timing inputed! The given starting time is after the ending time."; + + private static final Pattern TIME_TYPE_DATA_ARGS_FORMAT = Pattern.compile("(?.+)"); + private static final int AVERAGE_DAYS_IN_A_MONTH = 30; + private static final int DAYS_IN_A_WEEK = 7; + private static final boolean FAIL_DUE_TO_EXCEPTION = false; + + private final Logger logger = LogsCenter.getLogger(Parser.class); + + private String setTime; + private DateTimeInfoParser timeInfo; + + public DateTimeInfo(String givenTime) throws IllegalValueException { + setDateGroupTime(givenTime); + } + + /** + * Set the setTime (DateGroup object) as the date inputed by the user + * + * @param givenTime + * @throws IllegalValueException + */ + public void setDateGroupTime(String givenTime) throws IllegalValueException { + assert givenTime != null; + final Matcher matcher = TIME_TYPE_DATA_ARGS_FORMAT.matcher(givenTime.trim()); + matcher.matches(); + DateTimeInfoParser parsedTiming; + try { + parsedTiming = new DateTimeInfoParser(matcher.group("info")); + timeInfo = parsedTiming; + this.setTime = parsedTiming.getParsedTimingInfo(); + formatTimingIntoString(); + } catch (IllegalValueException e) { + throw new IllegalValueException(MESSAGE_DATETIMEINFO_CONSTRAINTS); + } + } + + /** + * A getter method for DateTimeInfoParser + * + * @return DateTimeInfoParser of the timing of interest + */ + public DateTimeInfoParser getTimeInfo() { + return timeInfo; + } + + private void formatTimingIntoString() { + this.setTime = getDateMonthYear() + " " + setTime.substring(12, 17); + } + + /** + * Change the format of the timing saved in setTime + * + */ + void formatStartOrDueDateTime() { + if (timeInfo.isTimeInferred()) { + setTime = setTime.substring(0, 12) + "08:00"; + changeTimeInfo(); + } + } + + /** + * If the time is inferred, replace "08:00" with "17:00" + */ + void formatEndTime(DateTimeInfo startTime) { + if (timeInfo.isDateInferred()) { + setTime = startTime.setTime.substring(0, 12) + setTime.substring(12); + } + if (timeInfo.isTimeInferred()) { + setTime = setTime.substring(0, 12) + "17:00"; + } + changeTimeInfo(); + } + + /** + * Update timeInfo after changing the setTime to the Default value + * + */ + private void changeTimeInfo() { + try { + timeInfo = new DateTimeInfoParser(setTime); + } catch (IllegalValueException e) { + logger.warning("----------------[DateTimeInfo][" + " THIS ERROR SHOULD NOT OCCUR. METHOD USING A VALID INPUT " + "]"); + } + } + + /** + * Extract the month, date and year of a particular date + * + * @return timing in MMM DD YYYY format + */ + private String getDateMonthYear() { + return setTime.substring(5, 12) + setTime.substring(25, 29); + } + + /** + * Validate the timing inputed + * + * @param test A date to be tested + * @return True if it is a valid timing + */ + public static boolean isValidDateTimeInfo(List test) { + return (!test.isEmpty()) ? true : false; + } + + /** + * Change the months which is specified in string to integer + * + * @param month The month in written 3 letters string + * @return Month in integer + */ + public static int whatMonth(String month) { + switch (month) { + case "Jan": + return 1; + case "Feb": + return 2; + case "Mar": + return 3; + case "Apr": + return 4; + case "May": + return 5; + case "Jun": + return 6; + case "Jul": + return 7; + case "Aug": + return 8; + case "Sep": + return 9; + case "Oct": + return 10; + case "Nov": + return 11; + case "Dec": + return 12; + default: + return 0; + } + } + + /** + * To check if the minute inputed in 'from' is before the minute inputed in + * 'to' + * + * @param starting + * Time + * @param ending + * Time + * @return the duration of the event + */ + public static String durationOfTheEvent(String startingTime, String endingTime) { + return timeDifferenceInString(durationBetweenTwoTiming(startingTime, endingTime)); + } + + /** + * prepare variables needed to calculate the duration between two timing + * + * @param startingTime + * @param endingTime + * @return the duration of the event in an array. 0 represents minutes and 4 + * represents years + */ + public static int[] durationBetweenTwoTiming(String startingTime, String endingTime) { + int years = differenceInYears(startingTime, endingTime); + int months = differenceInMonths(startingTime, endingTime); + int days = differenceInDays(startingTime, endingTime); + int hours = differenceInHours(startingTime, endingTime); + int minutes = differenceInMinutes(startingTime, endingTime); + + return combineDuratingOfEvent(years, months, days, hours, minutes); + } + + /** + * Put together the time difference that is represented in array into a String to be shown to the user + * + * @param timeDifference The Time difference between two timing + * @return String of the message to be shown to the users + */ + private static String timeDifferenceInString(int[] timeDifference) { + String duration = new String(""); + if (timeDifference[0] == -1) { + return MESSAGE_FROM_IS_AFTER_TO; + } else if (timeDifference[0] > 0) { + duration = " " + timeDifference[0] + " minute" + ((timeDifference[0] == 1) ? "" + duration : "s"); + } + if (timeDifference[1] > 0) { + duration = " " + timeDifference[1] + " hour" + ((timeDifference[1] == 1) ? "" + duration : "s" + duration); + } + if (timeDifference[2] > 0) { + duration = " " + timeDifference[2] + " day" + ((timeDifference[2] == 1) ? "" + duration : "s" + duration); + } + if (timeDifference[3] > 0) { + duration = " " + timeDifference[3] + " month" + ((timeDifference[3] == 1) ? "" + duration : "s" + duration); + } + if (timeDifference[4] > 0) { + duration = " " + timeDifference[4] + " year" + ((timeDifference[4] == 1) ? "" + duration : "s" + duration); + } + if (duration.equals("")) { + return "Event starts and end at the same time."; + } else { + return "Duration of the event is: " + duration.trim() + "."; + } + } + + /** + * Calculate the duration of the event + * + * @param years + * @param months + * @param days + * @param hours + * @param minutes + * @return the duration of the event in a string + */ + private static int[] combineDuratingOfEvent(int years, int months, int days, int hours, int minutes) { + int[] timeDifference = new int[5]; + + if (minutes < 0) { + minutes = Math.floorMod(minutes, 60); + hours = hours - 1; + } + timeDifference[0] = minutes; + if (hours < 0) { + hours = Math.floorMod(hours, 24); + days = days - 1; + } + timeDifference[1] = hours; + if (days < 0) { + days = Math.floorMod(days, 31); + months = months - 1; + } + timeDifference[2] = days; + if (months < 0) { + months = Math.floorMod(months, 12); + years = years - 1; + } + timeDifference[3] = months; + if (years < 0) { + timeDifference[0] = -1; + return timeDifference; + } else if (years > 0) { + timeDifference[4] = years; + } + return timeDifference; + + } + + /** + * Calculate the minute difference between the end and the start + * + * @param startingTime The starting time + * @param endingTime The ending time + * @return The minutes difference + */ + private static int differenceInMinutes(String startingTime, String endingTime) { + int startMinute = Integer.parseInt(startingTime.substring(15, 17)); + int endMinute = Integer.parseInt(endingTime.substring(15, 17)); + return endMinute - startMinute; + } + + /** + * Calculate the hour difference between the end and the start + * + * @param startingTime The starting time + * @param endingTime The ending time + * @return The hours difference + */ + private static int differenceInHours(String startingTime, String endingTime) { + int startHours = Integer.parseInt(startingTime.substring(12, 14)); + int endHours = Integer.parseInt(endingTime.substring(12, 14)); + return endHours - startHours; + } + + /** + * Calculate the day difference between the end and the start + * + * @param startingTime The starting time + * @param endingTime The ending time + * @return The days difference + */ + private static int differenceInDays(String startingTime, String endingTime) { + int startDate = Integer.parseInt(startingTime.substring(4, 6)); + int endDate = Integer.parseInt(endingTime.substring(4, 6)); + return endDate - startDate; + } + + /** + * Calculate the year difference between the end and the start + * + * @param startingTime The starting time + * @param endingTime The ending time + * @return The years difference + */ + private static int differenceInYears(String startingTime, String endingTime) { + int startYear = Integer.parseInt(startingTime.substring(7, 11)); + int endYear = Integer.parseInt(endingTime.substring(7, 11)); + return endYear - startYear; + } + + /** + * Calculate the month difference between the end and the start + * + * @param startingTime The starting time + * @param endingTime The ending time + * @return The months difference + */ + private static int differenceInMonths(String startingTime, String endingTime) { + String startMonth = startingTime.substring(0, 3); + String endMonth = endingTime.substring(0, 3); + int monthDifference = whatMonth(endMonth) - whatMonth(startMonth); + return monthDifference; + } + + @Override + public String toString() { + return setTime; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof DateTimeInfo // instanceof handles nulls + && this.setTime.equals(((DateTimeInfo) other).setTime)); // state check + } + + @Override + public int hashCode() { + return setTime.hashCode(); + } + + /** + * @return true if the date not specified; + */ + public boolean isDateNull() { + return this.setTime.equals("Feb 29 2000 00:00"); + } + + /** + * Process the task if the task is in the future + * + * @param Date + * Timing to be compared to the current timing + * @return True if the timing timeNow is after the timing Date + */ + public boolean isInTheFuture(DateTimeInfo Date) { + int[] duration = durationBetweenTwoTiming(this.toString(), Date.toString()); + return duration[0] != -1; + } + + /** + * Process the task if the task is in the past + * + * @param Date Timing to be compared to the current timing + * @return True if the timing timeNow is before the timing Date + */ + public boolean isInThePast(DateTimeInfo Date) { + return !this.isInTheFuture(Date); + } + + /** + * Prepare the keyword and process if the task is within the specified date. + * For a task the due date is the dateInfo date. For an event, the specified + * DateInfo date is within the event duration (inclusive the starting and + * the ending date) + * + * @param dateInfo The date of interest + * @param task The task of interest + * @return True if the task is within the specified date + */ + public boolean isOnTheDate(ReadOnlyTask task) { + return task.getDueDate().toString().contains(toString().substring(0, 12)) + || task.getEndTime().toString().contains(toString().substring(0, 12)) + || task.getStartTime().toString().contains(toString().substring(0, 12)) + || isTaskAnEventPassingThisDate(task, toString().substring(0, 12)); + } + + /** + * Process the data if it the task is a event and it is passing through the + * date specified. + * + * @param task The event of interest + * @param dateInfo The date of interest + * @return True if a task is an event and the day interest is within the + * starting date and the ending date + */ + static boolean isTaskAnEventPassingThisDate(ReadOnlyTask task, String dateInfo) { + if (!task.getIsEvent()) { + return false; + } + DateTimeInfo dateSpecified; + try { + dateSpecified = new DateTimeInfo(dateInfo); + return task.getStartTime().isInTheFuture(dateSpecified) && dateSpecified.isInTheFuture(task.getEndTime()); + } catch (IllegalValueException e) { + new IllegalValueException(MESSAGE_DATETIMEINFO_CONSTRAINTS); + } + return FAIL_DUE_TO_EXCEPTION; + } + + /** + * Process if the a task specified is with in the duration stated. + * + * @param keyWords The keywords of the list command enter by the user + * @param task the task of interest + * @return True if the date is within the duration + */ + public static boolean withInTheDuration(String keyWords, ReadOnlyTask task, String dateNow) { + boolean isWithInTime = false; + if (keyWords.contains(ListCommand.LIST_LAST_WEEK_COMMAND)) { + return isNotFloatingTaskAndWithinTheTime(task, dateNow, -DAYS_IN_A_WEEK); + } else if (keyWords.contains(ListCommand.LIST_LAST_MONTH_COMMAND)) { + return isNotFloatingTaskAndWithinTheTime(task, dateNow, -AVERAGE_DAYS_IN_A_MONTH); + } else if (keyWords.contains(ListCommand.LIST_NEXT_MONTH_COMMAND)) { + return isNotFloatingTaskAndWithinTheTime(task, dateNow, AVERAGE_DAYS_IN_A_MONTH); + } else if (keyWords.contains(ListCommand.LIST_NEXT_WEEK_COMMAND)) { + return isNotFloatingTaskAndWithinTheTime(task, dateNow, DAYS_IN_A_WEEK); + } + return isWithInTime; + } + + /** + * Process if the task given is either a deadline task or an event within + * the specified timing + * + * @param task The task of interest + * @param dateNow The current date + * @param expectedDays The longest timing accepted + * @return True if the task is not a floating task and it is within the + * specified timing + */ + private static boolean isNotFloatingTaskAndWithinTheTime(ReadOnlyTask task, String dateNow, int expectedDays) { + return (task.getIsNotFloatingTask()) + ? isTimeDifferenceLessThanSpecified(dateNow, task.getStartingTimeOrDueDate().toString(), expectedDays) + : false; + } + + /** + * Provide an easy access to the current timing in String + * + * @return String of the current time MMM DD YYYY HH:MM format. + */ + public static DateTimeInfo getCurrentTime() { + DateTimeInfo dateNow = null; + try { + dateNow = new DateTimeInfo("now"); + } catch (IllegalValueException e) { + assert false; + } + return dateNow; + } + + /** + * Process if the two given time is less than the time duration. + * + * @param startTime The starting time of the duration + * @param endTime The ending time of the duration + * @param limitTimeDuration The maximum duration allowed + * @return True if the time Difference is less than specified + */ + private static boolean isTimeDifferenceLessThanSpecified(String startTime, String endTime, int limitTimeDuration) { + if (limitTimeDuration < 0) { + limitTimeDuration = limitTimeDuration * (-1); + return isTimeDifferenceLessThanSpecified(endTime, startTime, limitTimeDuration); + } + + int years = differenceInYears(startTime, endTime); + int months = differenceInMonths(startTime, endTime); + int days = differenceInDays(startTime, endTime); + int hours = differenceInHours(startTime, endTime); + + return isTimeLessThanSpecified(limitTimeDuration, years, months, days, hours); + } + + /** + * Helper method to check the duration of a specified timing is with in the + * time duration + * + * @param limitTimeDuration The maximum time duration + * @param years The difference in years between the two timing + * @param months The difference in months between the two timing + * @param days The difference in days between the two timing + * @param hours The difference in hours between the two timing + * @return True if the time is within the specified timing + */ + private static boolean isTimeLessThanSpecified(int limitTimeDuration, int years, int months, int days, int hours) { + if (hours < 0) { + hours = Math.floorMod(hours, 24); + days = days - 1; + } + if (days < 0) { + days = Math.floorMod(days, 30); + months = months - 1; + } + if (months < 0 || months > 1) { + return false; + } + if (years < 0 || years > 0) { + return false; + } + if ((days <= limitTimeDuration && months == 0) + || (limitTimeDuration == AVERAGE_DAYS_IN_A_MONTH && months == 1)) { + return true; + } else { + return false; + } + } + + // @@ author + @Override + public int compareTo(DateTimeInfo dateTimeInfo2) { + Date dateObject1 = convertToDateObject(this.setTime); + Date dateObject2 = convertToDateObject(dateTimeInfo2.setTime); + return dateObject1.compareTo(dateObject2); + } + + private Date convertToDateObject(String dateString) { + try { + DateTimeInfoParser parsedTiming = new DateTimeInfoParser(dateString); + return parsedTiming.getParsedDateTime(); + } catch (IllegalValueException ive) { + return null; + } + + } + + public void checkDueDateOrStartTime() { + this.formatStartOrDueDateTime(); + } + + public void checkEndTime(DateTimeInfo startTime) { + this.formatEndTime(startTime); + } + +} +``` +###### /java/seedu/flexitrack/model/task/DateTimeInfoParser.java +``` java +/** + * Represents the Parser and Time related DateTimeInfo. + */ +public class DateTimeInfoParser { + + public static final String MESSAGE_DATETIMEINFO_CONSTRAINTS = "Invalid time inputed. Please check your spelling!"; + + private DateGroup timingInfo; + + public DateTimeInfoParser(String givenTime) throws IllegalValueException { + try { + Parser parser = new Parser(); + List dateParser = parser.parse(givenTime); + setTimingInfo(dateParser); + } catch (IndexOutOfBoundsException e) { + throw new IllegalValueException(MESSAGE_DATETIMEINFO_CONSTRAINTS); + } + } + + /** + * Set the timingInfo variable with the time specified + * + * @param dateParser + */ + private void setTimingInfo(List dateParser) { + this.timingInfo = dateParser.get(0); + } + + public String getParsedTimingInfo() { + return timingInfo.getDates().toString(); + } + + public boolean isTimeInferred() { + return timingInfo.isTimeInferred(); + } + + public boolean isDateInferred() { + return timingInfo.isDateInferred(); + } + +``` +###### /java/seedu/flexitrack/model/task/Name.java +``` java + @Override + public String toString(){ + return isDonePrefix + isBlockPrefix + name; + } + + public String getNameOnly(){ + return name; + } + + public String setAsMark(){ + return isDonePrefix = DONE_PREFIX; + } + + public String setAsUnmark(){ + name = name.replace(DONE_PREFIX, ""); + return isDonePrefix = ""; + } +} +``` +###### /java/seedu/flexitrack/model/task/ReadOnlyTask.java +``` java + default public boolean getIsNotFloatingTask(){ + return (this.getIsEvent() || this.getIsTask()); + } + + default public DateTimeInfo getStartingTimeOrDueDate(){ + if (this.getIsTask()){ + return this.getDueDate(); + } else { + return this.getStartTime(); + } + } + + default public DateTimeInfo getEndingTimeOrDueDate(){ + if (this.getIsTask()){ + return this.getDueDate(); + } else { + return this.getEndTime(); + } + } + +} +``` diff --git a/collated/main/A0127855W.md b/collated/main/A0127855W.md new file mode 100644 index 000000000000..10b359a0a167 --- /dev/null +++ b/collated/main/A0127855W.md @@ -0,0 +1,576 @@ +# A0127855W +###### /java/seedu/flexitrack/logic/commands/AddCommand.java +``` java + @Override + public String getUndoMessage(){ + return String.format(MESSAGE_UNDO_SUCCESS, toAdd); + } + + @Override + int getNumOfOccurrrence() { + return numOfOccurrrence; + } + + @Override + void setNumOfOccurrrence(int numOfOccurrrence) { + this.numOfOccurrrence = numOfOccurrrence; + } +} +``` +###### /java/seedu/flexitrack/logic/commands/BlockCommand.java +``` java + @Override + public String getUndoMessage(){ + return String.format(MESSAGE_UNDO_SUCCESS, toBlock); + } + +} +``` +###### /java/seedu/flexitrack/logic/commands/ClearCommand.java +``` java + @Override + public String getUndoMessage(){ + return String.format(MESSAGE_UNDO_SUCCESS); + } +} +``` +###### /java/seedu/flexitrack/logic/commands/DeleteCommand.java +``` java + @Override + public String getUndoMessage(){ + return String.format(MESSAGE_UNDO_SUCCESS, taskStore); + } +} +``` +###### /java/seedu/flexitrack/logic/commands/EditCommand.java +``` java +package seedu.flexitrack.logic.commands; + +import static seedu.flexitrack.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import java.util.HashMap; +import java.util.logging.Logger; + +import seedu.flexitrack.commons.core.EventsCenter; +import seedu.flexitrack.commons.core.LogsCenter; +import seedu.flexitrack.commons.core.Messages; +import seedu.flexitrack.commons.core.UnmodifiableObservableList; +import seedu.flexitrack.commons.exceptions.IllegalValueException; +import seedu.flexitrack.model.task.DateTimeInfo; +import seedu.flexitrack.model.task.ReadOnlyTask; +import seedu.flexitrack.model.task.Task; +import seedu.flexitrack.model.task.UniqueTaskList.DuplicateTaskException; +import seedu.flexitrack.model.task.UniqueTaskList.IllegalEditException; +import seedu.flexitrack.model.task.UniqueTaskList.TaskNotFoundException; + +/** + * Edits a task identified using it's last displayed index from the FlexiTrack. + */ +public class EditCommand extends Command { + + private static final Logger logger = LogsCenter.getLogger(EditCommand.class); + public static final String COMMAND_WORD = "edit"; + public static final String COMMAND_SHORTCUT = "e"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ", Shortcut [" + COMMAND_SHORTCUT + "]" + + ": Edits the specified task attributes of the task identified by the index number used in the last task listing.\n" + + "Parameters to edit an event: [index] (must be a positive integer) from/ [starting time] to/ [ending time]\n" + + "Example: " + COMMAND_WORD + " 1 " + "from/ 01062016 to/ 01/072016\n" + + "Parameters to edit a task: [index] (must be a positive integer) by/ [due date]\n" + "Example: " + + COMMAND_WORD + " 1 " + "by/ 01062016"; + + public static final String MESSAGE_EDIT_TASK_SUCCESS = "Edited: %1$s into %2$s"; + private static final String MESSAGE_UNDO_SUCCESS = "Undid edit: %1$s into %2$s"; + + public static final HashMap EDIT_PARAMETER_PASSING_MASK = new HashMap(); + static { + EDIT_PARAMETER_PASSING_MASK.put("name", 0); + EDIT_PARAMETER_PASSING_MASK.put("dueDate", 1); + EDIT_PARAMETER_PASSING_MASK.put("startTime", 2); + EDIT_PARAMETER_PASSING_MASK.put("endTime", 3); + } + + public final int targetIndex; + public final String[] arguments; + + private Task taskStore; + private Task editedTask; + + public EditCommand(int targetIndex, String[] arguments) { + this.targetIndex = targetIndex; + this.arguments = arguments; + } + + /** + * Constructor for the undo method + */ + public EditCommand() { + this.targetIndex = 0 ; + this.arguments = null; + } + + @Override + public CommandResult execute() { + + UnmodifiableObservableList lastShownList = model.getFilteredTaskList(); + + String duration = null; + + try { + taskStore = lastShownList.get(targetIndex - 1).copy(); + editedTask = model.editTask(lastShownList.get(targetIndex - 1), arguments); + editedTask = editedTask.copy(); + recordCommand(this); + } catch (IndexOutOfBoundsException ioobe) { + logger.info("Exception: Index Out of Bounds\n"); + indicateAttemptToExecuteIncorrectCommand(); + return new CommandResult(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } catch (IllegalEditException iee) { + logger.info("Exception: Illegal Edit"); + indicateAttemptToExecuteIncorrectCommand(); + return new CommandResult(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE)); + } catch (IllegalValueException ive) { + logger.info("Exception: Illegal Value"); + assert false : "Illegal value entered"; + } + + if (editedTask.getIsEvent()) { + duration = DateTimeInfo.durationOfTheEvent(editedTask.getStartTime().toString(), + editedTask.getEndTime().toString()); + } else { + duration = ""; + } + + return new CommandResult(String.format(MESSAGE_EDIT_TASK_SUCCESS, taskStore.getName(), editedTask.getName()) + + "\n" + duration); + } + +``` +###### /java/seedu/flexitrack/logic/commands/EditCommand.java +``` java + @Override + public String getUndoMessage(){ + return String.format(MESSAGE_UNDO_SUCCESS, taskStore.getName(), editedTask.getName()); + } +} +``` +###### /java/seedu/flexitrack/logic/commands/MarkCommand.java +``` java + @Override + public String getUndoMessage(){ + return String.format(MESSAGE_UNDO_SUCCESS, markedTask); + } +} +``` +###### /java/seedu/flexitrack/logic/commands/RedoCommand.java +``` java +package seedu.flexitrack.logic.commands; + +import java.util.Stack; + +/** + * Clears the FlexiTrack. + */ +public class RedoCommand extends Command { + + public static final String COMMAND_WORD = "redo"; + public static final String COMMAND_SHORTCUT = "rd"; // TODO: impiment ctrl + Y + public static final String MESSAGE_USAGE = COMMAND_WORD + ", Shortcut [" + COMMAND_SHORTCUT + "]" + ": Clear the to do lists in FlexiTrack.\n" + "Example: " + + COMMAND_WORD; + public static final String MESSAGE_SUCCESS = "Your last command has been redone!"; + public static final String MESSAGE_NOT_SUCCESS = "You have no command to redo!"; + + //Stores the undone commands + static Stack undoneCommandStack = new Stack(); + + public RedoCommand() { + } + + @Override + public CommandResult execute() { + Command redo = null; + if (undoneCommandStack.size() == 0 ){ + return new CommandResult(String.format(MESSAGE_NOT_SUCCESS)); + } + + redo = undoneCommandStack.pop(); + if (redo instanceof AddCommand && redo.getNumOfOccurrrence() !=0 ){ + int numOfOccurrrence = redo.getNumOfOccurrrence(); + redo.setNumOfOccurrrence(0); + for (int i = 1; i < numOfOccurrrence; i++) { + redo.execute(); + redo = undoneCommandStack.pop(); + } + redo.setNumOfOccurrrence(numOfOccurrrence); + } + + CommandResult redoMessage = redo.execute(); + model.indicateFlexiTrackerChanged(); + return new CommandResult(MESSAGE_SUCCESS + "\n" + redoMessage.getString()); + } + + +} +``` +###### /java/seedu/flexitrack/logic/commands/UnmarkCommand.java +``` java + @Override + public String getUndoMessage(){ + return String.format(MESSAGE_UNDO_SUCCESS, unMarkedTask); + } +} +``` +###### /java/seedu/flexitrack/logic/parser/Parser.java +``` java + private static final HashMap SHORTCUT_MAP = new HashMap(); // more + static { + SHORTCUT_MAP.put(AddCommand.COMMAND_SHORTCUT, AddCommand.COMMAND_WORD); + SHORTCUT_MAP.put(ClearCommand.COMMAND_SHORTCUT, ClearCommand.COMMAND_WORD); + SHORTCUT_MAP.put(DeleteCommand.COMMAND_SHORTCUT, DeleteCommand.COMMAND_WORD); + SHORTCUT_MAP.put(EditCommand.COMMAND_SHORTCUT, EditCommand.COMMAND_WORD); + SHORTCUT_MAP.put(ExitCommand.COMMAND_SHORTCUT, ExitCommand.COMMAND_WORD); + SHORTCUT_MAP.put(HelpCommand.COMMAND_SHORTCUT, HelpCommand.COMMAND_WORD); + SHORTCUT_MAP.put(FindCommand.COMMAND_SHORTCUT, FindCommand.COMMAND_WORD); + SHORTCUT_MAP.put(ListCommand.COMMAND_SHORTCUT, ListCommand.COMMAND_WORD); + SHORTCUT_MAP.put(MarkCommand.COMMAND_SHORTCUT, MarkCommand.COMMAND_WORD); + SHORTCUT_MAP.put(UnmarkCommand.COMMAND_SHORTCUT, UnmarkCommand.COMMAND_WORD); + SHORTCUT_MAP.put(BlockCommand.COMMAND_SHORTCUT, BlockCommand.COMMAND_WORD); + SHORTCUT_MAP.put(UndoCommand.COMMAND_SHORTCUT, UndoCommand.COMMAND_WORD); + SHORTCUT_MAP.put(RedoCommand.COMMAND_SHORTCUT, RedoCommand.COMMAND_WORD); + SHORTCUT_MAP.put(GapCommand.COMMAND_SHORTCUT, GapCommand.COMMAND_WORD); + } + +``` +###### /java/seedu/flexitrack/logic/parser/Parser.java +``` java + + private static final Pattern EDIT_COMMAND_FORMAT = Pattern.compile("(?[0-9]+)(?.*)"); + private static final Pattern EDIT_ARGS_NAME = Pattern.compile("[Nn]/\\s*(?.+)"); + private static final Pattern EDIT_ARGS_DUEDATE = Pattern.compile("[Bb][Yy]/\\s*(?[^/]+)"); + private static final Pattern EDIT_ARGS_STARTTIME = Pattern.compile("[Ff][Rr][oO][mM]/\\s*(?[^/]+)"); + private static final Pattern EDIT_ARGS_ENDTIME = Pattern.compile("[tT][Oo]/\\s*(?[^/]+)"); +``` +###### /java/seedu/flexitrack/logic/parser/Parser.java +``` java + /** + * parseCommandWord + * ------------------------------------------- + * Parses the given command word string, converting shortcut commands into their full versions + * @param commandWord + * @return String: Full command word + */ + private String parseCommandWord(String commandWord) { + assert commandWord != null; + return SHORTCUT_MAP.getOrDefault(commandWord, commandWord); + } + +``` +###### /java/seedu/flexitrack/logic/parser/Parser.java +``` java + /** + * prepareEdit + * ------------------------------------------ + * Parses the edit command arguments and outputs the correct EditCommand object for execution + * @param arguments + * @return Command: The correct EditCommand object + */ + private Command prepareEdit(String arguments) { + assert arguments != null; + + int index; + String editParameters; + String[] passing = new String[EditCommand.EDIT_PARAMETER_PASSING_MASK.size()]; + + final Matcher matcherEdit = EDIT_COMMAND_FORMAT.matcher(arguments.trim()); + + if (!matcherEdit.matches()) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE)); + } else { + index = Integer.parseInt(matcherEdit.group("index")); + editParameters = matcherEdit.group("arguments").trim(); + } + + final Matcher matcherName = EDIT_ARGS_NAME.matcher(editParameters); + final Matcher matcherDueDate = EDIT_ARGS_DUEDATE.matcher(editParameters); + final Matcher matcherStartTime = EDIT_ARGS_STARTTIME.matcher(editParameters); + final Matcher matcherEndTime = EDIT_ARGS_ENDTIME.matcher(editParameters); + + boolean isNamePresent = matcherName.find(); + boolean isDueDatePresent = matcherDueDate.find(); + boolean isStartTimePresent = matcherStartTime.find(); + boolean isEndTimePresent = matcherEndTime.find(); + + //Check that at least one edit parameter exists + if (!isNamePresent && !isDueDatePresent && !isStartTimePresent && !isEndTimePresent) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE)); + } + + prepareEditParameter(passing, matcherName, isNamePresent, "name"); + prepareEditParameter(passing, matcherDueDate, isDueDatePresent, "dueDate"); + prepareEditParameter(passing, matcherStartTime, isStartTimePresent, "startTime"); + prepareEditParameter(passing, matcherEndTime, isEndTimePresent, "endTime"); + + return new EditCommand(index, passing); + } + + /** + * prepareEditParameter + * --------------------------------------------------------- + * prepares the passing array for each parameter to be passed into the EditCommand constructor + * @param passing + * @param matcherType + * @param typePresent + * @param typeGroupID + */ + private void prepareEditParameter(String[] passing, final Matcher matcherType, boolean typePresent, String typeGroupID) { + if (typePresent) { + passing[EditCommand.EDIT_PARAMETER_PASSING_MASK.get(typeGroupID)] = matcherType.group(typeGroupID); + } else { + passing[EditCommand.EDIT_PARAMETER_PASSING_MASK.get(typeGroupID)] = null; + } + } + +``` +###### /java/seedu/flexitrack/model/FlexiTrack.java +``` java + /** + * Edits a Task in the tasks tracker. + * + * @throws UniqueTaskList.DuplicateTaskException if an equivalent task already exists. + * @throws TaskNotFoundException if specified task is not found. + */ + public Task editTask(ReadOnlyTask taskToEdit, String[] args) + throws IllegalEditException, IllegalValueException { + return task.edit(taskToEdit, args); + } +``` +###### /java/seedu/flexitrack/model/FlexiTrack.java +``` java + /** + * Sorts the flexitrack according to the ReadOnlyTask comparator + */ + public void sort(){ + task.sort(); + } + +``` +###### /java/seedu/flexitrack/model/Model.java +``` java + /** + * Edits the given task + * + * @throws TaskNotFoundException + */ + Task editTask(ReadOnlyTask taskToEdit, String[] args) + throws UniqueTaskList.IllegalEditException, IllegalValueException; + +``` +###### /java/seedu/flexitrack/model/ModelManager.java +``` java + @Override + /** + * Edits a Task in the tasks tracker. + * + * @throws UniqueTaskList.DuplicateTaskException if an equivalent task already exists. + * @throws TaskNotFoundException if specified task is not found. + */ + public Task editTask(ReadOnlyTask taskToEdit, String[] args) + throws IllegalEditException, IllegalValueException { + Task editedTask = flexiTracker.editTask(taskToEdit, args); + indicateFlexiTrackerChanged(); + jumpToATask(editedTask); + return editedTask; + } + + +``` +###### /java/seedu/flexitrack/model/task/ReadOnlyTask.java +``` java + /** + * Comparator for ReadOnlyTask and its children classes + * Sorts by whether the task is a floating task, then by whether the task is done, then by start time/due date, then by name + */ + default public int compareTo(ReadOnlyTask task) { + int compareResult = compareByDone(task); + if (compareResult != 0){ + return compareResult; + } + compareResult = compareByType(task); + if (compareResult != 0){ + return compareResult; + } + if (this.getIsNotFloatingTask() && task.getIsNotFloatingTask()){ + compareResult = compareByDate(task); + if (compareResult != 0){ + return compareResult; + } + } + compareResult = compareByName(task); + return compareResult; + } + + default int compareByDone(ReadOnlyTask task) { + if (this.getIsDone() && !task.getIsDone()){ + return 1; + }else if (!this.getIsDone() && task.getIsDone()){ + return -1; + }else{ + return 0; + } + } + + default int compareByType(ReadOnlyTask task) { + if (this.getIsNotFloatingTask() && !task.getIsNotFloatingTask()){ + return 1; + }else if (!this.getIsNotFloatingTask() && task.getIsNotFloatingTask()){ + return -1; + }else{ + return 0; + } + } + default int compareByDate(ReadOnlyTask task) { + Date date1 = this.getStartingTimeOrDueDate().getTimeInfo().getTimingInfo().getDates().get(0); + Date date2 = task.getStartingTimeOrDueDate().getTimeInfo().getTimingInfo().getDates().get(0); + return date1.compareTo(date2); + } + default int compareByName(ReadOnlyTask task) { + String name1 = this.getName().getNameOnly(); + String name2 = task.getName().getNameOnly(); + return name1.compareTo(name2); + } + + default Task copy(){ + Task clonedTask = null; + try{ + clonedTask = new Task(new Name (this.getName().toString()), + new DateTimeInfo (this.getDueDate().toString()), + new DateTimeInfo (this.getStartTime().toString()), + new DateTimeInfo (this.getEndTime().toString())); + }catch(IllegalValueException ive){ + } + return clonedTask; + } + +``` +###### /java/seedu/flexitrack/model/task/UniqueTaskList.java +``` java + /** + * Sorts the observable list according to the ReadOnlyTask comparator + */ + public void sort(){ + Collections.sort(internalList);; + } + + /** + * edit + * ----------------------------------------------------- + * finds the target task to be edited by the specified index and edits the task using the given argument array + * @param taskToEdit + * @param args: Array of edit parameters + * @return The new duration if the item being edited is an event, or "" if it is a floating task or task + * @throws IllegalEditException + * @throws TaskNotFoundException + * @throws IllegalValueException + */ + public Task edit(ReadOnlyTask taskToEdit, String[] args) + throws IllegalEditException, IllegalValueException { + assert taskToEdit != null; + int targetIndex = internalList.indexOf(taskToEdit); + Task editTask = internalList.get(targetIndex); + checkForIllegalFloatingTaskEdit(args, editTask); + editTaskParameters(editTask, args); + internalList.set(targetIndex, editTask); + return editTask; + + } + + /** + * checkForIllegalFloatingTaskEdit + * ------------------------------------------------- + * checks that the appropriate edit parameters to a floating task; user should not add both task and event parameters to a floating task, + * he must also make the floating task a complete event (with start and end time) if he were to edit it into an event. + * @param args + * @param editTask + * @throws IllegalEditException + */ + private void checkForIllegalFloatingTaskEdit(String[] args, Task editTask) throws IllegalEditException { + assert args != null; + assert editTask != null; + if (!editTask.getIsTask() && !editTask.getIsEvent()) { + if ((args[1] != null) && (args[2] != null || args[3] != null)) { + throw new IllegalEditException(); + } + if ((args[2] != null && args[3] == null) || (args[3] != null && args[2] == null)) { + throw new IllegalEditException(); + } + } + } + + /** + * editTaskParameters + * --------------------------------------------------- + * edits the actual task with given parameters, checking that the wrong parameters are not given to the wrong type of task; + * i.e. user should not add start date to a task, nor should he add a due date to an event + * @param editTask + * @param args + * @throws IllegalValueException + * @throws IllegalEditException + */ + private void editTaskParameters(Task editTask, String[] args) throws IllegalValueException, IllegalEditException { + assert args != null; + assert editTask != null; + for (int i = 0; i < args.length; i++) { + if (!(args[i] == null)) { + switch (i) { + case 0: + editTask.setName(args[i]); + break; + case 1: + if (!editTask.getIsEvent()) { + editTask.setDueDate(args[i]); + editTask.setIsTask(true); + } else { + throw new IllegalEditException(); + } + break; + case 2: + if (!editTask.getIsTask()) { + editTask.setStartTime(args[i]); + editTask.setIsEvent(true); + } else { + throw new IllegalEditException(); + } + break; + case 3: + if (!editTask.getIsTask()) { + editTask.setEndTime(args[i]); + editTask.setIsEvent(true); + } else { + throw new IllegalEditException(); + } + break; + default: + break; + } + } + } + } + + //@@ A0127686R + /** + * Find the index of a task of interest + * + * @param task The task of interest + * @return index of the task in int + */ + public int findIndexOfTask(Task task) { + int numberOfTask = internalList.size(); + for (int i = 0; i < numberOfTask; i++){ + if (internalList.get(i).equals(task)){ + return i; + } + } + return -1; + } +} +``` diff --git a/collated/main/A0138455Y.md b/collated/main/A0138455Y.md new file mode 100644 index 000000000000..5cb569ca17cb --- /dev/null +++ b/collated/main/A0138455Y.md @@ -0,0 +1,692 @@ +# A0138455Y +###### /java/seedu/flexitrack/commons/events/ui/StoragePathChangeEvent.java +``` java +public class StoragePathChangeEvent extends BaseEvent { + private final String newPath; + + public StoragePathChangeEvent(String newPath) { + this.newPath = newPath; + } + + @Override + public String toString() { + return this.newPath; + } +} +``` +###### /java/seedu/flexitrack/logic/commands/BlockCommand.java +``` java +/** + * Adds a block event period to the FlexiTrack. + */ +public class BlockCommand extends Command { +public static final String COMMAND_WORD = "block"; + + public static final String COMMAND_SHORTCUT = "b"; + + + public static final String MESSAGE_USAGE = COMMAND_WORD + ", Shortcut [" + COMMAND_SHORTCUT + "]" + ": Block a period of time in the FlexiTrack.\n" + + "Parameters to block a period of time: [Description] from/ [starting time] to/ [ending time]\n" + + "\tExample: " + COMMAND_WORD + " reserve for study from/ 1st June 5pm to/ 1st July 7pm\n"; + + public static final String MESSAGE_SUCCESS = "Block the date for %1$s"; + public static final String MESSAGE_DUPLICATE_TIME = "This period of time has already taken by other event, Please choose another time."; + public static final String MESSAGE_OVERLAPPING_EVENT_WARNING = "\nWarning: this event is overlaping a existing event!"; + private static final String MESSAGE_UNDO_SUCCESS = "Undid block: %1$s"; + + private Task toBlock; + private boolean isOverlapping = false; + + /** + * Convenience constructor using raw values. + * + * @throws IllegalValueException + * if any of the raw values are invalid + */ + public BlockCommand(String name, String dueDate, String startTime, String endTime) + throws IllegalValueException { + this.toBlock = new Task(new Name(name), new DateTimeInfo(dueDate), new DateTimeInfo(startTime), + new DateTimeInfo(endTime)); + } + + public BlockCommand() { + this.toBlock = null; + } + + @Override + public CommandResult execute() { + assert model != null; + try { + if(model.checkBlock(toBlock)) { + return new CommandResult(BlockCommand.MESSAGE_DUPLICATE_TIME); + } + this.isOverlapping = model.checkOverlapEvent(toBlock); + + model.addTask(toBlock); + toBlock = toBlock.copy(); + recordCommand(this); + if (toBlock.getIsEvent()) { + return new CommandResult((String.format(MESSAGE_SUCCESS, toBlock)) + "\n" + DateTimeInfo + .durationOfTheEvent(toBlock.getStartTime().toString(), toBlock.getEndTime().toString()) + + (isOverlapping? MESSAGE_OVERLAPPING_EVENT_WARNING : "")); + } else { + return new CommandResult(String.format(MESSAGE_SUCCESS, toBlock)); + } + } catch (DuplicateTaskException e) { + return new CommandResult(MESSAGE_DUPLICATE_TIME); + } + + } + @Override + public void executeUndo() { + Task toDelete = toBlock; + try { + model.deleteTask(toDelete); + } catch (TaskNotFoundException pnfe) { + assert false : "The target task cannot be missing"; + } + } + +``` +###### /java/seedu/flexitrack/logic/commands/ChangeStoragePathCommand.java +``` java +/** + * + * Change current storage to other place + * Limitation : only allow user to change storage within FlexiTrack folder + */ +public class ChangeStoragePathCommand extends Command{ + + public static final String COMMAND_WORD = "cs"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Change the storage directory of FlexiTrack.\n" + + "Parameters: DIRECTORY (input can only contains alphanumric, forward slash '/' and underscore '_')\n" + + "Example: " + COMMAND_WORD + + " /Documents/newFolder/tasktracker"; + + public static final String MESSAGE_SUCCESS = "Storage location changed: %1$s"; + public static final String INVALID_VALUE = "Invalid path input! Please enter a valid path!"; + + final String storagePath; + + /** + * taking new storage path + * @throws IllegalValueException if any of the raw values are invalid + */ + public ChangeStoragePathCommand(String newPath) { + this.storagePath = newPath; + } + + @Override + public CommandResult execute() { + model.changeStorage(storagePath); + return new CommandResult(String.format(MESSAGE_SUCCESS, this.storagePath)); + } +} +``` +###### /java/seedu/flexitrack/logic/commands/HelpCommand.java +``` java +package seedu.flexitrack.logic.commands; + +import seedu.flexitrack.commons.core.EventsCenter; +import seedu.flexitrack.commons.events.ui.ShowHelpRequestEvent; + +/** + * Format full help instructions for every command for display. + */ +public class HelpCommand extends Command { + + public static final String COMMAND_WORD = "help"; + public static final String COMMAND_SHORTCUT = "h"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ", Shortcut [" + COMMAND_SHORTCUT + "]" + ": Shows program usage instructions.\n" + "Example: " + + COMMAND_WORD; + + public static final String HELP_MESSAGE_USAGE = COMMAND_WORD + ": Shows program usage instructions.\n" + + "List of Commands: " + AddCommand.COMMAND_WORD + ", " + ClearCommand.COMMAND_WORD + ", " + + DeleteCommand.COMMAND_WORD + ", " + EditCommand.COMMAND_WORD + ", " + ExitCommand.COMMAND_WORD + ", " + + FindCommand.COMMAND_WORD + ", " + ListCommand.COMMAND_WORD + ", " + MarkCommand.COMMAND_WORD + ", " + + UnmarkCommand.COMMAND_WORD + ", " + BlockCommand.COMMAND_WORD + ", " + + ChangeStoragePathCommand.COMMAND_WORD + "(Change Storage Path), " + UndoCommand.COMMAND_WORD + + ", " + RedoCommand.COMMAND_WORD + ", " + GapCommand.COMMAND_WORD + "\n" + "Example: " + COMMAND_WORD + " " + + ClearCommand.COMMAND_WORD; + + public static final String SHOWING_HELP_MESSAGE = "Opened help window."; + private String userInput; + + public HelpCommand(String args) { + this.userInput = args; + } + + @Override + public CommandResult execute() { + switch (userInput) { + + case AddCommand.COMMAND_WORD: + return new CommandResult(AddCommand.MESSAGE_USAGE); + + case EditCommand.COMMAND_WORD: + return new CommandResult(EditCommand.MESSAGE_USAGE); + + case DeleteCommand.COMMAND_WORD: + return new CommandResult(DeleteCommand.MESSAGE_USAGE); + + case ClearCommand.COMMAND_WORD: + return new CommandResult(ClearCommand.MESSAGE_USAGE); + + case FindCommand.COMMAND_WORD: + return new CommandResult(FindCommand.MESSAGE_USAGE); + + case MarkCommand.COMMAND_WORD: + return new CommandResult(MarkCommand.MESSAGE_USAGE); + + case UnmarkCommand.COMMAND_WORD: + return new CommandResult(UnmarkCommand.MESSAGE_USAGE); + + case ListCommand.COMMAND_WORD: + return new CommandResult(ListCommand.MESSAGE_USAGE); + + case ExitCommand.COMMAND_WORD: + return new CommandResult(ExitCommand.MESSAGE_USAGE); + + case BlockCommand.COMMAND_WORD: + return new CommandResult(BlockCommand.MESSAGE_USAGE); + + case UndoCommand.COMMAND_WORD: + return new CommandResult(UndoCommand.MESSAGE_USAGE); + + case RedoCommand.COMMAND_WORD: + return new CommandResult(RedoCommand.MESSAGE_USAGE); + + case GapCommand.COMMAND_WORD: + return new CommandResult(GapCommand.MESSAGE_USAGE); + + case ChangeStoragePathCommand.COMMAND_WORD: + return new CommandResult(ChangeStoragePathCommand.MESSAGE_USAGE); + + default: + return new CommandResult(HELP_MESSAGE_USAGE); + } + /* + * EventsCenter.getInstance().post(new ShowHelpRequestEvent()); return + * new CommandResult(SHOWING_HELP_MESSAGE); + */ + } + +} +``` +###### /java/seedu/flexitrack/logic/commands/MarkCommand.java +``` java +package seedu.flexitrack.logic.commands; + +import seedu.flexitrack.commons.core.Messages; +import seedu.flexitrack.commons.core.UnmodifiableObservableList; +import seedu.flexitrack.commons.exceptions.IllegalValueException; +import seedu.flexitrack.model.task.ReadOnlyTask; +import seedu.flexitrack.model.task.Task; +import seedu.flexitrack.model.task.UniqueTaskList.DuplicateTaskException; +import seedu.flexitrack.model.task.UniqueTaskList.TaskNotFoundException; + +/** + * Selects a task identified using it's last displayed index from the + * FlexiTrack. + */ +public class MarkCommand extends Command { + + public final int targetIndex; + + public static final String COMMAND_WORD = "mark"; + public static final String COMMAND_SHORTCUT = "m"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ", Shortcut [" + COMMAND_SHORTCUT + "]" + + ": Marks the task identified by the index number used in the task listing.\n" + + "Parameters: INDEX (must be a positive integer)\n" + "Example: " + COMMAND_WORD + " 1"; + + public static final String MESSAGE_MARK_TASK_SUCCESS = "Marked Task: %1$s"; + private static final String MESSAGE_UNDO_SUCCESS = "Undid mark task: %1$s"; + + private Task taskStore; + private Task markedTask; + + public MarkCommand(int targetIndex) { + this.targetIndex = targetIndex; + } + + /** + * Constructor for undo command + */ + public MarkCommand() { + this.targetIndex = 0; + } + + @Override + public CommandResult execute(){ + + UnmodifiableObservableList lastShownList = model.getFilteredTaskList(); + + if (lastShownList.size() < targetIndex) { + indicateAttemptToExecuteIncorrectCommand(); + return new CommandResult(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + + try { + taskStore = lastShownList.get(targetIndex - 1).copy(); + markedTask = model.markTask(lastShownList.get(targetIndex-1)); + markedTask = markedTask.copy(); + recordCommand(this); + return new CommandResult(String.format(MESSAGE_MARK_TASK_SUCCESS, targetIndex)); + } catch (IndexOutOfBoundsException ioobe) { + indicateAttemptToExecuteIncorrectCommand(); + return new CommandResult(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } catch (IllegalValueException e) { + return new CommandResult(e.getMessage()); + } + } + +``` +###### /java/seedu/flexitrack/logic/commands/UnmarkCommand.java +``` java +package seedu.flexitrack.logic.commands; + +import seedu.flexitrack.commons.core.Messages; +import seedu.flexitrack.commons.core.UnmodifiableObservableList; +import seedu.flexitrack.commons.exceptions.IllegalValueException; +import seedu.flexitrack.model.task.ReadOnlyTask; +import seedu.flexitrack.model.task.Task; +import seedu.flexitrack.model.task.UniqueTaskList.DuplicateTaskException; +import seedu.flexitrack.model.task.UniqueTaskList.TaskNotFoundException; + +/** + * Selects a task identified using it's last displayed index from the + * FlexiTrack. + */ +public class UnmarkCommand extends Command { + public final int targetIndex; + + public static final String COMMAND_WORD = "unmark"; + public static final String COMMAND_SHORTCUT = "u"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ", Shortcut [" + COMMAND_SHORTCUT + "]" + + ": Unmarks the task identified by the index number used in the task listing.\n" + + "Parameters: INDEX (must be a positive integer)\n" + "Example: " + COMMAND_WORD + " 1"; + + public static final String MESSAGE_UNMARK_TASK_SUCCESS = "Unmark Task: %1$s"; + private static final String MESSAGE_UNDO_SUCCESS = "Undid unmark task: %1$s"; + + private Task taskStore; + private Task unMarkedTask; + + public UnmarkCommand(int targetIndex) { + this.targetIndex = targetIndex; + } + + /** + * Constructor for undo command + */ + public UnmarkCommand() { + this.targetIndex = 0; + } + + @Override + public CommandResult execute() { + + UnmodifiableObservableList lastShownList = model.getFilteredTaskList(); + + if (lastShownList.size() < targetIndex) { + indicateAttemptToExecuteIncorrectCommand(); + return new CommandResult(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + + try { + taskStore = lastShownList.get(targetIndex - 1).copy(); + unMarkedTask = model.unmarkTask(lastShownList.get(targetIndex-1)); + unMarkedTask = unMarkedTask.copy(); + recordCommand(this); + return new CommandResult(String.format(MESSAGE_UNMARK_TASK_SUCCESS, targetIndex)); + } catch (IllegalValueException e) { + return new CommandResult(e.getMessage()); + } + + } + +``` +###### /java/seedu/flexitrack/logic/parser/Parser.java +``` java + private static final Pattern STORAGE_PATH_FORMAT = Pattern.compile("(?^[^\\?~`!@#$^&-+=%*|\"<>\\|]+)"); +``` +###### /java/seedu/flexitrack/logic/parser/Parser.java +``` java + /** + * + * @param args + * @return + */ + private Command prepareBlock(String args) { + final Matcher matcherEvent = TASK_EVENT_TYPE_DATA_ARGS_FORMAT.matcher(args.trim()); + + // Validate arg string format + try { + if (matcherEvent.matches()) { + return new BlockCommand("(Blocked) " + matcherEvent.group("name"), EMPTY_TIME_INFO, matcherEvent.group("startTime"), + matcherEvent.group("endTime")); + } else { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, BlockCommand.MESSAGE_USAGE)); + } + } catch (IllegalValueException ive) { + return new IncorrectCommand(ive.getMessage()); + } + } + +``` +###### /java/seedu/flexitrack/logic/parser/Parser.java +``` java + /** + * Parses arguments in the context of the Unmarkcommand. + * + * @param args + * full command args string + * @return the prepared command + */ + private Command prepareUnmark(String args) { + Optional index = parseIndex(args); + if (!index.isPresent()) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, UnmarkCommand.MESSAGE_USAGE)); + } + + return new UnmarkCommand(index.get()); + } + + /** + * Parses arguments in the context of the Markcommand. + * + * @param args full command args string + * @return the prepared command + */ + private Command prepareMark(String args) { + + Optional index = parseIndex(args); + if (!index.isPresent()) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, MarkCommand.MESSAGE_USAGE)); + } + + return new MarkCommand(index.get()); + } + + /** + * Parses arguments in the context of the ChangeStoragecommand. + * @param arguments + * @return + */ + private Command prepareChangePathCommand(String args) { + args = args.trim(); + Matcher matcher = STORAGE_PATH_FORMAT.matcher(args); + //Validate args string format + if(!matcher.matches()) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, ChangeStoragePathCommand.MESSAGE_USAGE)); + } else { + String newPath = matcher.group("path").trim() + ".xml"; + return new ChangeStoragePathCommand(newPath); + } + } +``` +###### /java/seedu/flexitrack/MainApp.java +``` java + @Subscribe + public void changeStoragePathRequestEvent(StoragePathChangeEvent event) { + logger.info(LogsCenter.getEventHandlingLogMessage(event)); + config.setFlexiTrackFilePath(event.toString()); + try { + ConfigUtil.saveConfig(config, Config.DEFAULT_CONFIG_FILE); + } catch (IOException e) { + logger.warning("Failed to save config file : " + StringUtil.getDetails(e)); + } + + storage.setStoragePath(event.toString()); + model.indicateFlexiTrackerChanged(); + } + +} +``` +###### /java/seedu/flexitrack/model/FlexiTrack.java +``` java + /** + * + * @param targetIndex is non-negative integer + * @return + * @throws IllegalValueException + */ + public Task markTask(ReadOnlyTask targetIndex) throws IllegalValueException { + return task.mark(targetIndex, Boolean.TRUE); + } + + /** + * + * @param targetIndex is non-negative integer + * @return + * @throws IllegalValueException + */ + public Task unmarkTask(ReadOnlyTask targetIndex) throws IllegalValueException { + return task.mark(targetIndex, Boolean.FALSE); + } + + /** + * + * @param toCheck is a Task Object and not null + * @return return ture if this task is not happen in any Block list slot + * @throws DuplicateTaskException + */ + public boolean checkBlock(Task toCheck) throws DuplicateTaskException { + setBlockList(); + //System.out.println("Checking block"); + if(blockList.getInternalList().size()==0) { + //System.out.println("no block found!"); + return false; + } + for(Task forCheck: blockList) { + if(compareDate(toCheck,forCheck)) { + //System.out.println("222 "+forCheck.toString()); + return true; + } + } + //System.out.println("compareed then return false"); + return false; + } + + /** + * + * @param toCheck is a Task Object and not null + * @return true if there is an event happen in the same timing + */ + public boolean checkOverlapEvent(Task toCheck) { + if(task.getInternalList().size()==0) { + return false; + } + for(Task forCheck: task) { + if(compareDate(toCheck,forCheck)) { + return true; + } + } + return false; + } + + /** + * + * @param toCheck + * @param blockDate + * @return ture if 2 event overlapping each other + */ + private boolean compareDate(Task toCheck, Task blockDate) { + Date start1 = toCheck.getStartTime().getTimeInfo().getTimingInfo().getDates().get(0); + Date start2 = blockDate.getStartTime().getTimeInfo().getTimingInfo().getDates().get(0); + Date end1 = toCheck.getEndTime().getTimeInfo().getTimingInfo().getDates().get(0); + Date end2 = blockDate.getEndTime().getTimeInfo().getTimingInfo().getDates().get(0); + // a KKKKKKK from/ 25 dec 3pm to/ 25 dec 6pm + // a LLLLLLL from/ 25 dec 3pm to/ 6pm + //System.out.println("\n"+toCheck.getName().toString()+" --start time : " + start1.toString() +"and end time : " + end1.toString() + // +"\n" +blockDate.getName().toString() +" start time : " + start2.toString() + "and end time : " + end2.toString()); + if((start1.compareTo(start2)>=0 && start1.compareTo(end2)<0) || + (end1.compareTo(start2)>=0 && end1.compareTo(end2)<0)) { + return true; + } + return false; + } + + /** + * return a list of Block task + * @throws DuplicateTaskException + */ + private void setBlockList() throws DuplicateTaskException { + blockList = new UniqueTaskList(); + for(Task toAdd: task) { + if(toAdd.getName().toString().contains("(Blocked) ")) { + blockList.add(toAdd); + } + } + } +``` +###### /java/seedu/flexitrack/model/Model.java +``` java + /** + * Marks the given task as done + * + * @throws TaskNotFoundException + */ + Task markTask(ReadOnlyTask taskToMark) throws IllegalValueException; + + /** + * Unmarks the given task + * + * @throws TaskNotFoundException + */ + Task unmarkTask(ReadOnlyTask taskToMark) throws IllegalValueException; +``` +###### /java/seedu/flexitrack/model/Model.java +``` java + /** + * + * @param Task toAdd + * @return true if new event want to place at a period that reserve for other event + */ + boolean checkBlock(Task toAdd) throws DuplicateTaskException; + + /** + * Limitation: only allow user change path within Flexitrack folder + * @param storagePath : only accept alpha or forward slash '/' (example: data/newStorage) + * storagePath should not include ".xml" + */ + void changeStorage(String storagePath); + + /** + * + * @param Task toAdd + * @return true if new event overLapping other event + */ + boolean checkOverlapEvent(Task toAdd); +``` +###### /java/seedu/flexitrack/model/ModelManager.java +``` java + @Override + public Task markTask(ReadOnlyTask targetIndex) throws IllegalValueException { + Task markedTask = flexiTracker.markTask(targetIndex); + indicateFlexiTrackerChanged(); + jumpToATask(markedTask); + return markedTask; + } + + @Override + public Task unmarkTask(ReadOnlyTask targetIndex) throws IllegalValueException { + Task unMarkedTask = flexiTracker.unmarkTask(targetIndex); + indicateFlexiTrackerChanged(); + jumpToATask(unMarkedTask); + return unMarkedTask; + } + + @Override + /* + * raise the storage path change event + * + */ + public void changeStorage(String storagePath) { + raise(new StoragePathChangeEvent(storagePath)); + } + +``` +###### /java/seedu/flexitrack/model/ModelManager.java +``` java + @Override + public boolean checkBlock(Task toCheck) throws DuplicateTaskException { + return flexiTracker.checkBlock(toCheck); + } + + @Override + public boolean checkOverlapEvent(Task toCheck) { + return flexiTracker.checkOverlapEvent(toCheck); + } +``` +###### /java/seedu/flexitrack/model/task/DateTimeInfoParser.java +``` java + public DateGroup getTimingInfo() { + return timingInfo; + } + +} +``` +###### /java/seedu/flexitrack/model/task/Name.java +``` java + public boolean getIsBlock() { + return isBlockPrefix.equals(BLOCK_PREFIX); + } +``` +###### /java/seedu/flexitrack/model/task/Task.java +``` java + private void setIsDone(boolean isDone) { + if (isDone && !this.isDone) { + name.setAsMark(); + } else if (!isDone && this.isDone) { + name.setAsUnmark(); + } + } + + public void markTask(boolean isDone) throws IllegalValueException { + this.isDone = this.name.getIsDone(); + if (this.isDone && isDone) { + throw new IllegalValueException("Task already marked!"); + } else if (!this.isDone && !isDone) { + throw new IllegalValueException("Task already unmarked!"); + } else { + setIsDone(isDone); + } + } +``` +###### /java/seedu/flexitrack/model/task/UniqueTaskList.java +``` java + public Task mark(ReadOnlyTask targetTask, boolean isDone) throws IllegalValueException { + assert targetTask != null; + int targetIndex = internalList.indexOf(targetTask); + Task markTask = internalList.get(targetIndex); + markTask.markTask(isDone); + internalList.set(targetIndex, markTask); + return markTask; + + } +``` +###### /java/seedu/flexitrack/storage/Storage.java +``` java + void setStoragePath(String newPath); +``` +###### /java/seedu/flexitrack/storage/StorageManager.java +``` java + @Override + public void setStoragePath(String newPath) { + this.flexiTrackStorage = new XmlFlexiTrackStorage(newPath); + } +``` +###### /java/seedu/flexitrack/ui/StatusBarFooter.java +``` java + @Subscribe + public void changeStoragePathRequestEvent(StoragePathChangeEvent event) { + logger.info(LogsCenter.getEventHandlingLogMessage(event, "Setting new data location at: " + event.toString())); + setSaveLocation("./"+event.toString()); + } +} +``` diff --git a/collated/main/A0147092E.md b/collated/main/A0147092E.md new file mode 100644 index 000000000000..f70078829c0e --- /dev/null +++ b/collated/main/A0147092E.md @@ -0,0 +1,613 @@ +# A0147092E +###### /java/seedu/flexitrack/commons/util/StringUtil.java +``` java + public static boolean equalsIgnoreCase(String source, String query) { + String parsedQuery = query.replace("f/", "").trim(); + List strings = Arrays.asList(source.toLowerCase()); + return strings.stream().filter(s -> s.contains(parsedQuery.toLowerCase())).count() > 0; + } +``` +###### /java/seedu/flexitrack/logic/commands/FindCommand.java +``` java + public FindCommand(String keywords) { + Set keyword2 = new HashSet(); + keyword2.add(keywords); + this.keywords = keyword2; + } +``` +###### /java/seedu/flexitrack/logic/parser/Parser.java +``` java + private static final Pattern TASK_RECURRING_EVENT_TYPE_DATA_ARGS_FORMAT = Pattern + .compile("(?.+)" + "[Ff][Rr]/\\s*(?\\d{1,2})\\s*" + + "[tT][Yy]/(?[^/\\d]{3,7})" + "from/(?[^/]+)" + "to/(?[^/]+)"); + private static final Pattern TASK_RECURRING_TASK_TYPE_DATA_ARGS_FORMAT = Pattern + .compile("(?.+)" + "[Ff][Rr]/\\s*(?\\d{1,2})\\s*" + + "[Tt][Yy]/\\s*(?[^/\\d]{3,7})" + "by/(?[^/]+)"); + private static final Pattern TASK_RECURRING_FLOATING_TASK_TYPE_DATA_ARGS_FORMAT = Pattern + .compile("(?.+)" + "[Ff][Rr]/\\s*(?\\d{1,2})"); +``` +###### /java/seedu/flexitrack/logic/parser/Parser.java +``` java + final Matcher matcherRecurringFloatingTask = TASK_RECURRING_FLOATING_TASK_TYPE_DATA_ARGS_FORMAT.matcher(args); + final Matcher matcherRecurringEvent = TASK_RECURRING_EVENT_TYPE_DATA_ARGS_FORMAT.matcher(args); + final Matcher matcherRecurringTask = TASK_RECURRING_TASK_TYPE_DATA_ARGS_FORMAT.matcher(args); + // Validate arg string format + try { +``` +###### /java/seedu/flexitrack/logic/parser/Parser.java +``` java + if (args.trim().contains("fr/") || args.trim().contains("ty/")) { + if (matcherRecurringFloatingTask.matches()) { + return addRecurring(matcherRecurringFloatingTask, "floating"); + } else if (matcherRecurringEvent.matches()) { + return addRecurring(matcherRecurringEvent, "event"); + } else if (matcherRecurringTask.matches()) { + return addRecurring(matcherRecurringTask, "task"); + } else { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); + } +``` +###### /java/seedu/flexitrack/logic/parser/Parser.java +``` java + /* Add a recurring task / event for day(s)/week(s)/month(s) + * + * @param matcher - commands that matches the regular expression + * @param recurringType - day, week, month + */ + private Command addRecurring(Matcher matcher, String recurringType) throws IllegalValueException { + final int DAY_INCREMENT = 1; + final int WEEK_INCREMENT = 2; + final int MONTH_INCREMENT = 3; + + String formattedStartTime; + String formattedEndTime; + String formattedDueDate; + + int numOfOccurrence = Integer.parseInt(matcher.group("numOfOccurrence").trim()); + + if (recurringType.equalsIgnoreCase("floating")) { + for (int i = 1; i < numOfOccurrence; i++) { + Command command = new AddCommand(matcher.group("name") + "(" + (i+1) + ")", EMPTY_TIME_INFO, EMPTY_TIME_INFO, EMPTY_TIME_INFO); + command.setData(model); + command.execute(); + } + return new AddCommand(matcher.group("name") + "(1)", EMPTY_TIME_INFO, EMPTY_TIME_INFO, EMPTY_TIME_INFO, numOfOccurrence); + + } else if (recurringType.equalsIgnoreCase("task")) { + String occurrenceType = matcher.group("occurrenceType").trim().toLowerCase(); + DateTimeInfo dueDateTimeInfo = new DateTimeInfo (matcher.group("dueDate")); + dueDateTimeInfo.checkDueDateOrStartTime(); + Date initialDueDate = new DateTimeInfoParser(dueDateTimeInfo.toString()).getParsedDateTime(); + + switch (occurrenceType.toLowerCase()) { + case "week": case "weekly": + for (int i=1; i < numOfOccurrence; i++) { + formattedDueDate = dateIncrement(initialDueDate, WEEK_INCREMENT, i); + + Command command = new AddCommand(matcher.group("name"), formattedDueDate, EMPTY_TIME_INFO, EMPTY_TIME_INFO); + command.setData(model); + command.execute(); + } + return new AddCommand(matcher.group("name"), matcher.group("dueDate"), EMPTY_TIME_INFO, EMPTY_TIME_INFO, numOfOccurrence); + + case"mth": case"month": case"monthly": + for (int i=1; i < numOfOccurrence; i++) { + formattedDueDate = dateIncrement(initialDueDate, MONTH_INCREMENT, i); + + Command command = new AddCommand(matcher.group("name"), formattedDueDate, EMPTY_TIME_INFO, EMPTY_TIME_INFO); + command.setData(model); + command.execute(); + } + return new AddCommand(matcher.group("name"), matcher.group("dueDate"), EMPTY_TIME_INFO, EMPTY_TIME_INFO, numOfOccurrence); + + case "day": case "daily": + for (int i=1; i < numOfOccurrence; i++) { + formattedDueDate = dateIncrement(initialDueDate, DAY_INCREMENT, i); + + Command command = new AddCommand(matcher.group("name"), formattedDueDate, EMPTY_TIME_INFO, EMPTY_TIME_INFO); + command.setData(model); + command.execute(); + } + return new AddCommand(matcher.group("name"), matcher.group("dueDate"), EMPTY_TIME_INFO, EMPTY_TIME_INFO, numOfOccurrence); + + default: + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); + } + + } else { // Recurring Event + String occurrenceType = matcher.group("occurrenceType").trim().toLowerCase(); + DateTimeInfo startDateTimeInfo = new DateTimeInfo (matcher.group("startTime")); + startDateTimeInfo.checkDueDateOrStartTime(); + DateTimeInfo endDateTimeInfo = new DateTimeInfo (matcher.group("endTime")); + endDateTimeInfo.checkEndTime(startDateTimeInfo); + + Date initialStartTime = new DateTimeInfoParser(startDateTimeInfo.toString()).getParsedDateTime(); + Date initialEndTime = new DateTimeInfoParser(endDateTimeInfo.toString()).getParsedDateTime(); + + switch (occurrenceType.toLowerCase()) { + case "week": case "weekly": + for (int i=1; i < numOfOccurrence; i++) { + + formattedStartTime = dateIncrement(initialStartTime, WEEK_INCREMENT, i); + formattedEndTime = dateIncrement(initialEndTime, WEEK_INCREMENT, i); + + Command command = new AddCommand(matcher.group("name"), EMPTY_TIME_INFO, formattedStartTime, formattedEndTime); + command.setData(model); + command.execute(); + } + return new AddCommand(matcher.group("name"), EMPTY_TIME_INFO, matcher.group("startTime"), matcher.group("endTime"), numOfOccurrence); + + case "mth": case "month": case "monthly": + for (int i=1; i < numOfOccurrence; i++) { + formattedStartTime = dateIncrement(initialStartTime, MONTH_INCREMENT, i); + formattedEndTime = dateIncrement(initialEndTime, MONTH_INCREMENT, i); + + Command command = new AddCommand(matcher.group("name"), EMPTY_TIME_INFO, formattedStartTime, formattedEndTime); + command.setData(model); + command.execute(); + } + return new AddCommand(matcher.group("name"), EMPTY_TIME_INFO, matcher.group("startTime"), matcher.group("endTime"), numOfOccurrence); + + case "day": case "daily": + for (int i=1; i < numOfOccurrence; i++){ + + formattedStartTime = dateIncrement(initialStartTime, DAY_INCREMENT, i); + formattedEndTime = dateIncrement(initialEndTime, DAY_INCREMENT, i); + + Command command = new AddCommand(matcher.group("name"), EMPTY_TIME_INFO, formattedStartTime, formattedEndTime); + command.setData(model); + command.execute(); + } + return new AddCommand(matcher.group("name"), EMPTY_TIME_INFO, matcher.group("startTime"), matcher.group("endTime"), numOfOccurrence); + + default: + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); + } + } + } +``` +###### /java/seedu/flexitrack/logic/parser/Parser.java +``` java + /** + * Increment a given date by number of day(s)/month(s)/week(s) + * + * @param initialDate + * @param incrementType + * @param incrementAmt + * @return formatted date in String Format + */ + public String dateIncrement(Date initialDate, int incrementType, int incrementAmt) { + final int DAYS_PER_WEEK = 7; + Date newDate; + + Calendar calendar = Calendar.getInstance(); + Date defaultDate = calendar.getTime(); + calendar.setTime(initialDate); + + switch (incrementType) { + case 1: // increment by day + calendar.add(Calendar.DATE, incrementAmt); + newDate = calendar.getTime(); + return new SimpleDateFormat("MM-dd-yyyy HHmmss").format(newDate); + + case 2: // increment by week + calendar.add(Calendar.DATE, DAYS_PER_WEEK * incrementAmt); + newDate = calendar.getTime(); + return new SimpleDateFormat("MM-dd-yyyy HHmmss").format(newDate); + + case 3: // increment by month + calendar.add(Calendar.MONTH, incrementAmt); + newDate = calendar.getTime(); + return new SimpleDateFormat("MM-dd-yyyy HHmmss").format(newDate); + + default: + return new SimpleDateFormat("MM-dd-yyyy HHmmss").format(defaultDate); + } + } +``` +###### /java/seedu/flexitrack/model/task/DateTimeInfoParser.java +``` java + public Date getParsedDateTime() { + return timingInfo.getDates().get(0); + } + +``` +###### /java/seedu/flexitrack/ui/CommandBox.java +``` java + public static CommandBox load(Stage primaryStage, AnchorPane commandBoxPlaceholder, ResultDisplay resultDisplay, + Logic logic) { + CommandBox commandBox = UiPartLoader.loadUiPart(primaryStage, commandBoxPlaceholder, new CommandBox()); +// TextFields.bindAutoCompletion(commandBox.commandTextField, "add ", "add from/ to/ ", "add by/", +// "delete 1", "delete 2", "delete 3", "delete 4", "delete 5", "delete 6", "delete 7", "delete 8", "delete 9", +// "edit 1 ", "edit 2 ", "edit 3 ", "edit 4 ", "edit 5 ", "edit 6 ", "edit 7 ", "edit 8 ", "edit 9 ", +// "find ", +// "list", "list mark", "list unmark", "list future", "list past"); + commandBox.configure(resultDisplay, logic); + commandBox.addToPlaceholder(); + return commandBox; + } +``` +###### /java/seedu/flexitrack/ui/CommandBox.java +``` java + @FXML + private void handleCommandInputChanged() { + // Take a copy of the command text + previousCommandTest = commandTextField.getText(); + + commandTextField.textProperty().addListener((observable, oldValue, newValue) -> { + if(newValue != null){ + if(!newValue.equals(previousCommandTest)) + commandTextField.getStyleClass().remove("error"); + } + }); + + + /* + * We assume the command is correct. If it is incorrect, the command box + * will be changed accordingly in the event handling code {@link + * #handleIncorrectCommandAttempted} + */ + setStyleToIndicateCorrectCommand(); + mostRecentResult = logic.execute(previousCommandTest); + resultDisplay.postMessage(mostRecentResult.feedbackToUser); + logger.info("Result: " + mostRecentResult.feedbackToUser); + } +``` +###### /java/seedu/flexitrack/ui/CommandBox.java +``` java + + /** + * Sets the command box style to indicate a correct command. + */ + private void 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() { + commandTextField.getStyleClass().add("error"); + } + +} +``` +###### /java/seedu/flexitrack/ui/TaskCard.java +``` java + @FXML + public void initialize() { + String dateInfo; + if (task.getIsTask()) { + dateInfo = " by " + task.getDueDate(); + if (task.getIsDone()){ + cardPane.setStyle("-fx-background-color: #78AB46"); + } else { + cardPane.setStyle("-fx-background-color: #ffa54f"); + } + } else if (task.getIsEvent()) { + dateInfo = " from " + task.getStartTime() + " to " + task.getEndTime(); + if (task.getIsDone()) { + cardPane.setStyle("-fx-background-color: #78AB46"); + } else { + cardPane.setStyle("-fx-background-color: #cd8500"); + } + } else { + dateInfo = ""; + if (task.getIsDone()) { + cardPane.setStyle("-fx-background-color: #78AB46"); + } else { + cardPane.setStyle("-fx-background-color: #FFFFFF"); + } + } + dateTime.setText(dateInfo); + title.setText(task.getName().toString()); + id.setText(displayedIndex + ". "); + } +``` +###### /resources/view/DarkTheme.css +``` css +.background { + -fx-background-color: derive(#4d2600, 20%); +} + +.label { + -fx-font-size: 11pt; + -fx-font-family: "Segoe UI Semibold"; + -fx-text-fill: #555555; + -fx-opacity: 0.9; +} + +.label-bright { + -fx-font-size: 11pt; + -fx-font-family: "Segoe UI Semibold"; + -fx-text-fill: white; + -fx-opacity: 1; +} + +.label-header { + -fx-font-size: 32pt; + -fx-font-family: "Segoe UI Light"; + -fx-text-fill: white; + -fx-opacity: 1; +} + +.text-field { + -fx-font-size: 12pt; + -fx-font-family: "Segoe UI Semibold"; +} + +.tab-pane { + -fx-padding: 0 0 0 1; +} + +.tab-pane .tab-header-area { + -fx-padding: 0 0 0 0; + -fx-min-height: 0; + -fx-max-height: 0; +} + +.table-view { + -fx-base: #4d2600; + -fx-control-inner-background: #4d2600; + -fx-background-color: #4d2600; + -fx-table-cell-border-color: transparent; + -fx-table-header-border-color: transparent; + -fx-padding: 5; +} + +.table-view .column-header-background { + -fx-background-color: transparent; +} + +.table-view .column-header, .table-view .filler { + -fx-size: 35; + -fx-border-width: 0 0 1 0; + -fx-background-color: transparent; + -fx-border-color: + transparent + transparent + derive(-fx-base, 80%) + transparent; + -fx-border-insets: 0 10 1 0; +} + +.table-view .column-header .label { + -fx-font-size: 20pt; + -fx-font-family: "Segoe UI Light"; + -fx-text-fill: white; + -fx-alignment: center-left; + -fx-opacity: 1; +} + +.table-view:focused .table-row-cell:filled:focused:selected { + -fx-background-color: -fx-focus-color; +} + +.split-pane:horizontal .split-pane-divider { + -fx-border-color: transparent #4d2600 transparent #4d2600; + -fx-background-color: transparent, derive(#4d2600, 10%); +} + +.split-pane { + -fx-border-radius: 1; + -fx-border-width: 1; + -fx-background-color: derive(#4d2600, 20%); +} + +.list-cell { + -fx-label-padding: 0 0 0 0; + -fx-graphic-text-gap : 0; + -fx-padding: 0 0 0 0; +} + +.list-cell .label { + -fx-text-fill: #010504; +} + +.cell_big_label { + -fx-font-size: 18px; + -fx-text-overrun : leading-word-ellipsis; + -fx-text-fill: #010504; +} + +.cell_small_label { + -fx-font-size: 12px; + -fx-text-fill: #010504; +} + +.anchor-pane { + -fx-background-color: derive(#4d2600, 20%); +} + +.anchor-pane-with-border { + -fx-background-color: derive(#4d2600, 20%); + -fx-border-color: derive(#4d2600, 10%); + -fx-border-top-width: 1px; +} + +.status-bar { + -fx-background-color: derive(#4d2600, 20%); + -fx-text-fill: white; +} + +.result-display { + -fx-background-color: #4d2600; +} + +.result-display .label { + -fx-text-fill: black !important; +} + +.status-bar .label { + -fx-text-fill: white; +} + +.status-bar-with-border { + -fx-background-color: derive(#4d2600, 30%); + -fx-border-color: derive(#4d2600, 25%); + -fx-border-width: 1px; +} + +.status-bar-with-border .label { + -fx-text-fill: white; +} + +.grid-pane { + -fx-background-color: derive(#4d2600, 30%); + -fx-border-color: derive(#4d2600, 30%); + -fx-border-width: 1px; +} + +.grid-pane .anchor-pane { + -fx-background-color: derive(#4d2600, 30%); +} + +.context-menu { + -fx-background-color: derive(#4d2600, 50%); +} + +.context-menu .label { + -fx-text-fill: white; +} + +.menu-bar { + -fx-background-color: derive(#4d2600, 10%); +} + +.menu-bar .label { + -fx-font-size: 14pt; + -fx-font-family: "Segoe UI Light"; + -fx-text-fill: white; + -fx-opacity: 0.9; +} + +.menu .left-container { + -fx-background-color: black; +} + +/* + * Metro style Push Button + * Author: Pedro Duque Vieira + * http://pixelduke.wordpress.com/2012/10/23/jmetro-windows-8-controls-on-java/ + */ +.button { + -fx-padding: 5 22 5 22; + -fx-border-color: #4d2600; + -fx-border-width: 2; + -fx-background-radius: 0; + -fx-background-color: #4d2600; + -fx-font-family: "Segoe UI", Helvetica, Arial, sans-serif; + -fx-font-size: 11pt; + -fx-text-fill: #4d2600; + -fx-background-insets: 0 0 0 0, 0, 1, 2; +} + +.button:hover { + -fx-background-color: #3a3a3a; +} + +.button:pressed, .button:default:hover:pressed { + -fx-background-color: white; + -fx-text-fill: #4d2600; +} + +.button:focused { + -fx-border-color: white, white; + -fx-border-width: 1, 1; + -fx-border-style: solid, segments(1, 1); + -fx-border-radius: 0, 0; + -fx-border-insets: 1 1 1 1, 0; +} + +.button:disabled, .button:default:disabled { + -fx-opacity: 0.4; + -fx-background-color: #4d2600; + -fx-text-fill: white; +} + +.button:default { + -fx-background-color: -fx-focus-color; + -fx-text-fill: #ffffff; +} + +.button:default:hover { + -fx-background-color: derive(-fx-focus-color, 30%); +} + +.dialog-pane { + -fx-background-color: #4d2600; +} + +.dialog-pane > *.button-bar > *.container { + -fx-background-color: #4d2600; +} + +.dialog-pane > *.label.content { + -fx-font-size: 14px; + -fx-font-weight: bold; + -fx-text-fill: white; +} + +.dialog-pane:header *.header-panel { + -fx-background-color: derive(#4d2600, 25%); +} + +.dialog-pane:header *.header-panel *.label { + -fx-font-size: 18px; + -fx-font-style: italic; + -fx-fill: white; + -fx-text-fill: white; +} + +.scroll-bar .thumb { + -fx-background-color: derive(#4d2600, 50%); + -fx-background-insets: 3; +} + +.scroll-bar .increment-button, .scroll-bar .decrement-button { + -fx-background-color: transparent; + -fx-padding: 0 0 0 0; +} + +.scroll-bar .increment-arrow, .scroll-bar .decrement-arrow { + -fx-shape: " "; +} + +.scroll-bar:vertical .increment-arrow, .scroll-bar:vertical .decrement-arrow { + -fx-padding: 1 8 1 8; +} + +.scroll-bar:horizontal .increment-arrow, .scroll-bar:horizontal .decrement-arrow { + -fx-padding: 8 1 8 1; +} + +#cardPane { + -fx-background-color: transparent; + -fx-border-color: #d6d6d6; + -fx-border-width: 1 1 1 1; +} + +#commandTypeLabel { + -fx-font-size: 11px; + -fx-text-fill: #F70D1A; +} + +#filterField, #personListPanel, #personWebpage { + -fx-effect: innershadow(gaussian, black, 10, 0, 0, 0); +} +``` diff --git a/collated/test/A0127686R.md b/collated/test/A0127686R.md new file mode 100644 index 000000000000..871bbb43c87d --- /dev/null +++ b/collated/test/A0127686R.md @@ -0,0 +1,380 @@ +# A0127686R +###### /java/guitests/AddCommandTest.java +``` java +public class AddCommandTest extends FlexiTrackGuiTest { + + TestTask[] currentList = td.getTypicalSortedTasks(); + TestTask taskToAdd; + + @Test + public void add_AnEvent_success() { + TestTask[] currentList = td.getTypicalSortedTasks(); + taskToAdd = TypicalTestTasks.basketball; + assertAddSuccess(taskToAdd, currentList); + currentList = TestUtil.addTasksToList(currentList, taskToAdd); + } + + @Test + public void add_ADeadLineTask_success() { + taskToAdd = TypicalTestTasks.lecture; + assertAddSuccess(taskToAdd, currentList); + currentList = TestUtil.addTasksToList(currentList, taskToAdd); + } + + @Test + public void add_AFloatingTask_success() { + taskToAdd = TypicalTestTasks.job; + assertAddSuccess(taskToAdd, currentList); + currentList = TestUtil.addTasksToList(currentList, taskToAdd); + + } + + @Test + public void add_ADuplicateTask_fail() { + commandBox.runCommand(TypicalTestTasks.soccer.getAddCommand()); + assertResultMessage(AddCommand.MESSAGE_DUPLICATE_TASK); + assertTrue(taskListPanel.isListMatching(currentList)); + } + + @Test + public void add_ToAnEmptyList_success() { + commandBox.runCommand("clear"); + assertAddSuccess(TypicalTestTasks.homework1); + + } + + @Test + public void add_invalidAddCommand_fail() { + commandBox.runCommand("adds cs tutorial"); + assertResultMessage(Messages.MESSAGE_UNKNOWN_COMMAND); + } + +``` +###### /java/guitests/AddCommandTest.java +``` java + private void assertAddSuccess(TestTask taskToAdd, TestTask... currentList) { + commandBox.runCommand(taskToAdd.getAddCommand()); + + // confirm the new card contains the right data + TaskCardHandle addedCard = taskListPanel.navigateToTask(taskToAdd.getName().toString()); + assertMatching(taskToAdd, addedCard); + + // confirm the list now contains all previous tasks plus the new task + TestTask[] expectedList = TestUtil.addTasksToList(currentList, taskToAdd); + assertTrue(taskListPanel.isListMatching(expectedList)); + } + +} +``` +###### /java/guitests/ListCommandTest.java +``` java +public class ListCommandTest extends FlexiTrackGuiTest { + + @Test + public void testListBasic() { + TestTask[] currentList = td.getTypicalSortedTasks(); + + // list all future tasks + String listCommand = "list future"; + assertFindSuccess(listCommand, currentList); + currentList = TestUtil.listTasksAccordingToCommand(currentList, listCommand); + + // list all past tasks + listCommand = "list past"; + assertFindSuccess(listCommand, currentList); + currentList = TestUtil.listTasksAccordingToCommand(currentList, listCommand); + + // list all tasks + listCommand = "list"; + assertFindSuccess(listCommand, currentList); + currentList = TestUtil.listTasksAccordingToCommand(currentList, listCommand); + + currentList = TestUtil.markTasksToList(currentList, 6); + currentList = TestUtil.markTasksToList(currentList, 4); + currentList = TestUtil.markTasksToList(currentList, 3); + currentList = TestUtil.markTasksToList(currentList, 1); + + commandBox.runCommand("mark 6"); + commandBox.runCommand("mark 4"); + commandBox.runCommand("mark 3"); + commandBox.runCommand("mark 1"); + + // list all marked tasks + listCommand = "list mark"; + assertFindSuccess(listCommand, currentList); + currentList = TestUtil.listTasksAccordingToCommand(currentList, listCommand); + + // list all unmarked tasks + listCommand = "list unmark"; + assertFindSuccess(listCommand, currentList); + currentList = TestUtil.listTasksAccordingToCommand(currentList, listCommand); + + // list future tasks that are marked + listCommand = "list future mark"; + assertFindSuccess(listCommand, currentList); + currentList = TestUtil.listTasksAccordingToCommand(currentList, listCommand); + + } + + public void testListRelative() { + commandBox.runCommand("add lecture 1 from/ Nov 08 2016 09:00 to/Nov 08 2016 11:00"); + commandBox.runCommand("add exam 1 from/Nov 20 2016 09:00 to/Nov 20 2016 10:30 "); + commandBox.runCommand("add past 1 from/Nov 01 2016 09:00 to/ Nov 01 2016 11:00"); + commandBox.runCommand("add past 2 from/Oct 20 2016 15:00 to/Oct 20 2016 16:00"); + TestTask[] currentList = td.getTypicalSortedTasks(); + + // list last week task + String listCommand = "list last week"; + assertFindSuccess(listCommand, currentList); + currentList = TestUtil.listTasksAccordingToCommand(currentList, listCommand); + + // list last month task + listCommand = "list last month"; + assertFindSuccess(listCommand, currentList); + currentList = TestUtil.listTasksAccordingToCommand(currentList, listCommand); + + // list next week + listCommand = "list next week"; + assertFindSuccess(listCommand, currentList); + currentList = TestUtil.listTasksAccordingToCommand(currentList, listCommand); + + //list next month + listCommand = "list next month"; + assertFindSuccess(listCommand, currentList); + currentList = TestUtil.listTasksAccordingToCommand(currentList, listCommand); + + } + + private void assertFindSuccess(String listCommand, TestTask... currentList) { + commandBox.runCommand(listCommand); + + // confirm the list now contains all previous tasks plus the new task + TestTask[] expectedList = TestUtil.listTasksAccordingToCommand(currentList, listCommand); + assertTrue(taskListPanel.isListMatching(expectedList)); + } + +} +``` +###### /java/guitests/UndoCommandTest.java +``` java +public class UndoCommandTest extends FlexiTrackGuiTest { + + @Test + public void undo() { + + // undo fail + commandBox.runCommand("undo"); + assertResultMessage(UndoCommand.MESSAGE_NOT_SUCCESS); + + // undo unmark command + commandBox.runCommand("mark 2"); + commandBox.runCommand("unmark 2"); + commandBox.runCommand("undo"); + commandBox.runCommand("unmark 8"); + assertUndoSuccess(); + + // undo add command + commandBox.runCommand("add a task"); + commandBox.runCommand("undo"); + assertUndoSuccess(); + + // undo delete command + commandBox.runCommand("delete 4"); + commandBox.runCommand("undo"); + assertUndoSuccess(); + + // undo mark command + commandBox.runCommand("mark 3"); + commandBox.runCommand("undo"); + assertUndoSuccess(); + + + // undo edit command + commandBox.runCommand("edit 5 n/ play bridge with friends"); + commandBox.runCommand("undo"); + assertUndoSuccess(); + + // undo clear command + commandBox.runCommand("clear"); + commandBox.runCommand("undo"); + assertUndoSuccess(); + + // undo add command + commandBox.runCommand("list future"); + commandBox.runCommand("mark 2"); + commandBox.runCommand("list"); + commandBox.runCommand("undo"); + assertUndoSuccess(); + + // undo add command + commandBox.runCommand("list past"); + commandBox.runCommand("mark 2"); + commandBox.runCommand("mark 1"); + commandBox.runCommand("undo"); + commandBox.runCommand("list"); + commandBox.runCommand("undo"); + + assertUndoSuccess(); + } + + private void assertUndoSuccess() { + + // confirm the list now contains all previous tasks plus the new task + TestTask[] expectedList = td.getTypicalSortedTasks(); + assertTrue(taskListPanel.isListMatching(expectedList)); + + } + +} +``` +###### /java/seedu/flexitrack/testutil/TestTask.java +``` java + public String getAddCommand() { + StringBuilder sb = new StringBuilder(); + sb.append("add " + this.getName().toString() + " "); + if (getIsTask()) { + sb.append("by/" + this.getDueDate().toString() + " "); + } else if (getIsEvent()) { + sb.append("from/" + this.getStartTime().toString() + " "); + sb.append("to/" + this.getEndTime().toString() + " "); + } + return sb.toString(); + } + + public static String getMarkCommand(int mark) { + StringBuilder sb = new StringBuilder(); + sb.append("mark " + mark); + return sb.toString(); + } + +``` +###### /java/seedu/flexitrack/testutil/TypicalTestTasks.java +``` java + homework1 = new TaskBuilder().withName("Homework cs 2103").withDueDate("Jan 11 2017 17:00") + .withEndTime("Feb 29 2000 00:00").withStartTime("Feb 29 2000 00:00").build(); + homework2 = new TaskBuilder().withName("Homework cs 2101").withDueDate("Sep 01 2016 13:00") + .withEndTime("Feb 29 2000 00:00").withStartTime("Feb 29 2000 00:00").build(); + homework3 = new TaskBuilder().withName("Homework ma 1505").withStartTime("Feb 29 2000 00:00") + .withEndTime("Feb 29 2000 00:00").withDueDate("Feb 29 2000 00:00").build(); + soccer = new TaskBuilder().withName("Soccer training").withStartTime("Jun 30 2016 21:00") + .withEndTime("Jun 30 2016 23:00").withDueDate("Feb 29 2000 00:00").build(); + dinner = new TaskBuilder().withName("Dinner with parents").withStartTime("Nov 16 2017 19:00") + .withEndTime("Nov 16 2017 20:15").withDueDate("Feb 29 2000 00:00").build(); + exam = new TaskBuilder().withName("MA 1505 Exams").withStartTime("May 12 2016 10:00") + .withEndTime("May 12 2016 11:30").withDueDate("Feb 29 2000 00:00").build(); + midterm = new TaskBuilder().withName("Midter cs 2101").withStartTime("Feb 29 2000 00:00") + .withEndTime("Feb 29 2000 00:00").withDueDate("Mar 23 2017 09:00").build(); + event = new TaskBuilder().withName("Event lol").withStartTime("Feb 29 2000 00:00") + .withEndTime("Feb 29 2000 00:00").withDueDate("Feb 29 2000 00:00").build(); + tutorial2 = new TaskBuilder().withName("(Blocked) for CS 2103 tutorial 2").withStartTime("Nov 21 2017 19:00") + .withEndTime("Nov 21 2017 20:15").withDueDate("Feb 29 2000 00:00").build(); + // Manually added + basketball = new TaskBuilder().withName("Basketball training").withStartTime("Dec 25 2016 18:00") + .withEndTime("Dec 25 2016 21:00").withDueDate("Feb 29 2000 00:00").build(); + lecture = new TaskBuilder().withName("Lecture CS2103").withStartTime("Oct 10 2016 14:00") + .withEndTime("Oct 10 2016 16:00").withDueDate("Feb 29 2000 00:00").build(); + job = new TaskBuilder().withName("Apply Job in Starbucks").withDueDate("Feb 29 2000 00:00") + .withEndTime("Feb 29 2000 00:00").withStartTime("Feb 29 2000 00:00").build(); + tutorial1 = new TaskBuilder().withName("CS2103 tutorial 1").withStartTime("Feb 29 2000 00:00") + .withEndTime("Feb 29 2000 00:00").withDueDate("11 Nov 23:59").build(); + tutorial3 = new TaskBuilder().withName("(Blocked) for CS 2103 tutorial 3").withStartTime("Nov 21 2017 19:00") + .withEndTime("Nov 21 2017 20:15").withDueDate("Feb 29 2000 00:00").build(); + +``` +###### /java/seedu/flexitrack/testutil/TypicalTestTasks.java +``` java + // Additional Event Task + lecture1 = new TaskBuilder().withName("lecture 1").withStartTime("Nov 08 2017 09:00") + .withEndTime("Nov 08 2017 11:00").withDueDate("Feb 29 2000 00:00").build(); + lecture2 = new TaskBuilder().withName("lecture 2").withStartTime("Nov 08 2017 15:00") + .withEndTime("Nov 08 2017 16:00").withDueDate("Feb 29 2000 00:00").build(); + lecture3 = new TaskBuilder().withName("lecture 3").withStartTime("Nov 09 2017 14:00") + .withEndTime("Nov 09 2017 16:00").withDueDate("Feb 29 2000 00:00").build(); + lecture4 = new TaskBuilder().withName("lecture 4").withStartTime("Nov 12 2017 10:00") + .withEndTime("Nov 12 2017 12:00").withDueDate("Feb 29 2000 00:00").build(); + lecture5 = new TaskBuilder().withName("lecture 5").withStartTime("Nov 12 2017 13:00") + .withEndTime("Nov 12 2017 14:00").withDueDate("Feb 29 2000 00:00").build(); + exam1 = new TaskBuilder().withName("exam 1").withStartTime("Nov 20 2017 09:00") + .withEndTime("Nov 20 2017 10:30").withDueDate("Feb 29 2000 00:00").build(); + exam2 = new TaskBuilder().withName("exam 2").withStartTime("Nov 20 2017 12:00") + .withEndTime("Nov 20 2017 14:00").withDueDate("Feb 29 2000 00:00").build(); + exam3 = new TaskBuilder().withName("exam 3").withStartTime("Nov 22 2017 19:00") + .withEndTime("Nov 22 2017 21:00").withDueDate("Feb 29 2000 00:00").build(); + study1 = new TaskBuilder().withName("study 1").withStartTime("Nov 18 2017 09:00") + .withEndTime("Nov 18 2017 23:00").withDueDate("Feb 29 2000 00:00").build(); + study2 = new TaskBuilder().withName("study 2").withStartTime("Nov 19 2017 06:00") + .withEndTime("Nov 19 2017 10:00").withDueDate("Feb 29 2000 00:00").build(); + past1 = new TaskBuilder().withName("past 1").withStartTime("Nov 01 2017 09:00") + .withEndTime("Nov 01 2017 11:00").withDueDate("Feb 29 2000 00:00").build(); + past2 = new TaskBuilder().withName("past 2").withStartTime("Oct 20 2017 15:00") + .withEndTime("Oct 20 2017 16:00").withDueDate("Feb 29 2000 00:00").build(); + + } catch (IllegalValueException e) { + e.printStackTrace(); + assert false : "not possible"; + } + } + +``` +###### /java/seedu/flexitrack/testutil/TypicalTestTasks.java +``` java + public TestTask[] getTypicalSortedTasks() { + TestTask[] testTask = new TestTask[] { event, homework3, exam, soccer, homework2, homework1, midterm, dinner}; + return testTask; + } + + public TestTask[] getTypicalUnsortedTasks() { + TestTask[] testTask = new TestTask[] { homework1, homework2, homework3, soccer, dinner, exam, midterm, event }; + return testTask; + } + + public TestTask[] getExpectedTypicalFutureTasks() { + return new TestTask[] { homework1, homework3, dinner, midterm, event }; + } + + public TestTask[] getExpectedTypicalPastTasks() { + return new TestTask[] { homework2, homework3, soccer, exam, event }; + } + + public TestTask[] getTypicalEventTasks() { + TestTask[] testTask = new TestTask[] { lecture1, lecture2, lecture3, lecture4, lecture5, exam1, exam2, exam3, study1, study2}; + return testTask; + } + + public TestTask[] getExpectedTypicalMarkTasks() { + try { + homework1.setName(new Name(homework1.getName().toString())); + soccer.setName(new Name(soccer.getName().toString())); + exam.setName(new Name(exam.getName().toString())); + event.setName(new Name(event.getName().toString())); + } catch (IllegalValueException e) { + assert false; + } + homework1.getName().setAsMark(); + soccer.getName().setAsMark(); + exam.getName().setAsMark(); + event.getName().setAsMark(); + return new TestTask[] { homework1, soccer, exam, event }; + } + + public TestTask[] getExpectedTypicalUnMarkTasks() { + return new TestTask[] { homework2, homework3, dinner, midterm }; + } + + public TestTask[] getExpectedTypicalFutureMarkTasks() { + try { + homework1.setName(new Name (homework1.getName().toString())); + soccer.setName(new Name (soccer.getName().toString())); + exam.setName(new Name (exam.getName().toString())); + event.setName(new Name (event.getName().toString())); + } catch (IllegalValueException e) { + assert false; + } + homework1.getName().setAsMark(); + soccer.getName().setAsMark(); + exam.getName().setAsMark(); + event.getName().setAsMark(); + return new TestTask[] { homework1 }; + } + +``` diff --git a/collated/test/A0127855W.md b/collated/test/A0127855W.md new file mode 100644 index 000000000000..0d5cf1e74d9e --- /dev/null +++ b/collated/test/A0127855W.md @@ -0,0 +1,248 @@ +# A0127855W +###### /java/guitests/EditCommandTest.java +``` java +package guitests; + +import static org.junit.Assert.assertTrue; +import static seedu.flexitrack.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import org.junit.Test; + +import guitests.guihandles.TaskCardHandle; +import seedu.flexitrack.commons.core.Messages; +import seedu.flexitrack.logic.commands.EditCommand; +import seedu.flexitrack.testutil.TestTask; +import seedu.flexitrack.testutil.TestUtil; +import seedu.flexitrack.testutil.TypicalTestTasks; + +public class EditCommandTest extends FlexiTrackGuiTest { + + TestTask[] currentList = td.getTypicalSortedTasks(); + TestTask editedTask; + int index; + String command; + + @Test + public void edit_taskName_pass() { + editedTask = TypicalTestTasks.homework1EditName; + index = 6; + command = " n/ Name Edited"; + assertEditSuccess(editedTask, currentList, index, command); + } + + @Test + public void edit_taskDueDate_pass() { + editedTask = TypicalTestTasks.homework1EditDueDate; + index = 6; + command = " by/ Jan 14 2016 10am"; + assertEditSuccess(editedTask, currentList, index, command); + } + + @Test + public void edit_eventName_pass() { + editedTask = TypicalTestTasks.soccerEditName; + index = 4; + command = " n/ Name Edited 2"; + assertEditSuccess(editedTask, currentList, index, command); + } + + @Test + public void edit_eventStartTime_pass() { + editedTask = TypicalTestTasks.soccerEditStartTime; + index = 4; + command = " from/ June 10 2016 9pm"; + assertEditSuccess(editedTask, currentList, index, command); + } + + @Test + public void edit_eventEndTime_pass() { + editedTask = TypicalTestTasks.soccerEditEndTime; + index = 4; + command = " to/ June 30 2020 6am"; + assertEditSuccess(editedTask, currentList, index, command); + } + + @Test + public void edit_floatingTaskName_pass() { + editedTask = TypicalTestTasks.homework3EditName; + index = 2; + command = " n/ Name Edited 3"; + assertEditSuccess(editedTask, currentList, index, command); + } + + @Test + public void edit_floatingTaskIntoTask_pass() { + editedTask = TypicalTestTasks.homework3EditToTask; + index = 2; + command = " by/ Jun 10 2016 9pm"; + assertEditSuccess(editedTask, currentList, index, command); + } + + @Test + public void edit_floatingTaskIntoEvent_pass() { + editedTask = TypicalTestTasks.eventEditToEvent; + index = 1; + command = " from/ Jun 10 2016 21:00 to/ Jun 30 2016 23:00"; + assertEditSuccess(editedTask, currentList, index, command); + } + + @Test + public void edit_wrongIndex_fail() { + + commandBox.runCommand("edit " + (currentList.length + 1) + " n/ hello"); + assertResultMessage(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + @Test + public void edit_taskStartTime_fail() { + commandBox.runCommand("edit " + 5 + " from/ today"); + assertResultMessage(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE)); + } + @Test + public void edit_taskEndTime_fail() { + commandBox.runCommand("edit " + 5 + " to/ tomorrow"); + assertResultMessage(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE)); + } + @Test + public void edit_eventDueDate_fail() { + commandBox.runCommand("edit " + 3 + " by/ tomorrow"); + assertResultMessage(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE)); + } + @Test + public void edit_floatingTaskwithStartTime_fail() { + commandBox.runCommand("edit " + 1 + " from/ today"); + assertResultMessage(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE)); + } + @Test + public void edit_floatingTaskWithEndTime_fail() { + commandBox.runCommand("edit " + 1 + " to/ tomorrow"); + assertResultMessage(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE)); + } + @Test + public void edit_floatingTaskWithDueDateAndStartTime_fail() { + commandBox.runCommand("edit " + 1 + " by/ tomorrow from/ tomorrow"); + assertResultMessage(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE)); + } + @Test + public void edit_floatingTaskWithDueDateAndEndTIme_fail() { + commandBox.runCommand("edit " + 1 + " by/ tomorrow to/ tomorrow"); + assertResultMessage(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE)); + } + @Test + public void edit_invalidCommandFormat_fail() { + commandBox.runCommand("edit what is this"); + assertResultMessage(String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE)); + + } + + private void assertEditSuccess(TestTask editedTask, final TestTask[] currentList, int indexOneIndexed, + String command) { + int index = indexOneIndexed - 1; + + commandBox.runCommand("edit " + indexOneIndexed + command); + TaskCardHandle editedCard = taskListPanel.navigateToTask(editedTask.getName().toString()); + + assertMatching(editedTask, editedCard); + + // confirm the list now contains all previous tasks plus the new task + TestTask[] expectedList = TestUtil.editTasksToList(currentList, index, editedTask); + assertTrue(taskListPanel.isListMatching(expectedList)); + } + +} +``` +###### /java/guitests/RedoCommandTest.java +``` java +package guitests; + +import static org.junit.Assert.assertTrue; + +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import seedu.flexitrack.logic.Logic; +import seedu.flexitrack.logic.LogicManager; +import seedu.flexitrack.logic.commands.RedoCommand; +import seedu.flexitrack.model.Model; +import seedu.flexitrack.model.ModelManager; +import seedu.flexitrack.storage.StorageManager; +import seedu.flexitrack.testutil.TestTask; +import seedu.flexitrack.testutil.TestUtil; +import seedu.flexitrack.testutil.TypicalTestTasks; + +public class RedoCommandTest extends FlexiTrackGuiTest { + + @ClassRule + public static TemporaryFolder saveFolder = new TemporaryFolder(); + + private static Model model; + private static Logic logic; + + + @BeforeClass + public static void setupMoreEvents(){ + model = new ModelManager(); + String tempFlexiTrackerFile = saveFolder.getRoot().getPath() + "TempFlexiTracker.xml"; + String tempPreferencesFile = saveFolder.getRoot().getPath() + "TempPreferences.json"; + logic = new LogicManager(model, new StorageManager(tempFlexiTrackerFile, tempPreferencesFile)); + } + + @Test + public void redo() { + TestTask[] expectedList = td.getTypicalSortedTasks(); + + // redo fail + //logic.execute("redo"); + commandBox.runCommand("redo"); + assertResultMessage(RedoCommand.MESSAGE_NOT_SUCCESS); + + // redo add command + TestTask taskToAdd = TypicalTestTasks.basketball; + commandBox.runCommand(taskToAdd.getAddCommand()); + expectedList = TestUtil.addTasksToList(expectedList, taskToAdd); + assertRedoSuccess(expectedList); + + // redo delete command + commandBox.runCommand("delete 6"); + expectedList = TestUtil.removeTasksFromList(expectedList, expectedList[5]); + assertRedoSuccess(expectedList); + + // redo edit command + commandBox.runCommand("edit 6 n/Name Edited"); + expectedList = TestUtil.editTasksToList(expectedList, 5, TypicalTestTasks.homework1EditName); + assertRedoSuccess(expectedList); + } + + private void assertRedoSuccess(TestTask[] expectedList){ + commandBox.runCommand("undo"); + commandBox.runCommand("redo"); + assertTrue(taskListPanel.isListMatching(expectedList)); + } + +} +``` +###### /java/seedu/flexitrack/testutil/TypicalTestTasks.java +``` java + // After edit + homework1EditName = new TaskBuilder().withName("Name Edited").withDueDate("Jan 11 2017 17:00") + .withEndTime("Feb 29 2000 00:00").withStartTime("Feb 29 2000 00:00").build(); + homework1EditDueDate = new TaskBuilder().withName("Homework cs 2103").withDueDate("Jan 14 2016 10:00") + .withEndTime("Feb 29 2000 00:00").withStartTime("Feb 29 2000 00:00").build(); + + soccerEditName = new TaskBuilder().withName("Name Edited 2").withStartTime("Jun 30 2016 21:00") + .withEndTime("Jun 30 2016 23:00").withDueDate("Feb 29 2000 00:00").build(); + soccerEditStartTime = new TaskBuilder().withName("Soccer training").withStartTime("Jun 10 2016 21:00") + .withEndTime("Jun 30 2016 23:00").withDueDate("Feb 29 2000 00:00").build(); + soccerEditEndTime = new TaskBuilder().withName("Soccer training").withStartTime("Jun 30 2016 21:00") + .withEndTime("Jun 30 2020 6:00").withDueDate("Feb 29 2000 00:00").build(); + + homework3EditName = new TaskBuilder().withName("Name Edited 3").withStartTime("Feb 29 2000 00:00") + .withEndTime("Feb 29 2000 00:00").withDueDate("Feb 29 2000 00:00").build(); + homework3EditToTask = new TaskBuilder().withName("Homework ma 1505").withStartTime("Feb 29 2000 00:00") + .withEndTime("Feb 29 2000 00:00").withDueDate("Jun 10 2016 21:00").build(); + + eventEditToEvent = new TaskBuilder().withName("Event lol").withStartTime("Jun 10 2016 21:00") + .withEndTime("Jun 30 2016 23:00").withDueDate("Feb 29 2000 00:00").build(); + +``` diff --git a/collated/test/A0138455Y.md b/collated/test/A0138455Y.md new file mode 100644 index 000000000000..c60c914571fe --- /dev/null +++ b/collated/test/A0138455Y.md @@ -0,0 +1,343 @@ +# A0138455Y +###### /java/guitests/BlockCommandTest.java +``` java +package guitests; + +import org.junit.Test; + +import seedu.flexitrack.logic.commands.BlockCommand; +import seedu.flexitrack.model.task.DateTimeInfo; +import seedu.flexitrack.testutil.TestTask; +import seedu.flexitrack.testutil.TestUtil; +import seedu.flexitrack.testutil.TypicalTestTasks; + +import static seedu.flexitrack.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.flexitrack.logic.commands.BlockCommand.MESSAGE_SUCCESS; +import static seedu.flexitrack.logic.commands.BlockCommand.MESSAGE_DUPLICATE_TIME; + +public class BlockCommandTest extends FlexiTrackGuiTest { + + TestTask[] currentList = td.getTypicalSortedTasks(); + TestTask taskToBlock; + + @Test + public void addBlockSuccess() { + TestTask[] currentList = td.getTypicalSortedTasks(); + taskToBlock = TypicalTestTasks.basketball; + assertBlockSuccess(taskToBlock, currentList); + currentList = TestUtil.addTasksToList(currentList, taskToBlock); + } + + @Test + public void addBlockFail() { + TestTask[] currentList = td.getTypicalSortedTasks(); + taskToBlock = TypicalTestTasks.tutorial1; + assertBlockFail(taskToBlock, currentList); + currentList = TestUtil.addTasksToList(currentList, taskToBlock); + } + + @Test + public void addBlockOverlappingFail() { + TestTask[] currentList = td.getTypicalSortedTasks(); + taskToBlock = TypicalTestTasks.tutorial3; + assertBlockOverlappingFail(taskToBlock, currentList); + currentList = TestUtil.addTasksToList(currentList, taskToBlock); + } + + private void assertBlockSuccess(TestTask taskToBlock, TestTask[] currentList2) { + commandBox.runCommand(taskToBlock.getBlockCommand()); + taskToBlock.setIsBlock(true);; + assertResultMessage((String.format(MESSAGE_SUCCESS, taskToBlock)) + "\n" + DateTimeInfo + .durationOfTheEvent(taskToBlock.getStartTime().toString(), taskToBlock.getEndTime().toString())); + } + + private void assertBlockFail(TestTask taskToBlock, TestTask[] currentList2) { + commandBox.runCommand("block " + taskToBlock.getName().toString() + " by/ " + taskToBlock.getDueDate()); + //taskToBlock.setIsBlock(true);; + assertResultMessage(String.format(MESSAGE_INVALID_COMMAND_FORMAT, BlockCommand.MESSAGE_USAGE)); + } + + private void assertBlockOverlappingFail(TestTask taskToBlock, TestTask[] currentList2) { + commandBox.runCommand(TypicalTestTasks.tutorial2.getAddCommand()); + commandBox.runCommand(taskToBlock.getBlockCommand()); + assertResultMessage(MESSAGE_DUPLICATE_TIME); + } +} +``` +###### /java/guitests/HelpCommandTest.java +``` java +package guitests; + +import static org.junit.Assert.assertEquals; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import seedu.flexitrack.commons.core.EventsCenter; +import seedu.flexitrack.logic.Logic; +import seedu.flexitrack.logic.LogicManager; +import seedu.flexitrack.logic.commands.AddCommand; +import seedu.flexitrack.logic.commands.ClearCommand; +import seedu.flexitrack.logic.commands.CommandResult; +import seedu.flexitrack.logic.commands.DeleteCommand; +import seedu.flexitrack.logic.commands.EditCommand; +import seedu.flexitrack.logic.commands.ExitCommand; +import seedu.flexitrack.logic.commands.FindCommand; +import seedu.flexitrack.logic.commands.HelpCommand; +import seedu.flexitrack.logic.commands.ListCommand; +import seedu.flexitrack.logic.commands.MarkCommand; +import seedu.flexitrack.logic.commands.UnmarkCommand; +import seedu.flexitrack.model.Model; +import seedu.flexitrack.model.ModelManager; +import seedu.flexitrack.storage.StorageManager; + +public class HelpCommandTest extends FlexiTrackGuiTest { + + /** + * See https://github.com/junit-team/junit4/wiki/rules#temporaryfolder-rule + */ + @Rule + public TemporaryFolder saveFolder = new TemporaryFolder(); + + private Model model; + private Logic logic; + + @Before + public void setup() { + model = new ModelManager(); + String tempFlexiTrackerFile = saveFolder.getRoot().getPath() + "TempFlexiTracker.xml"; + String tempPreferencesFile = saveFolder.getRoot().getPath() + "TempPreferences.json"; + logic = new LogicManager(model, new StorageManager(tempFlexiTrackerFile, tempPreferencesFile)); + EventsCenter.getInstance().registerHandler(this); + } + + @After + public void teardown() { + EventsCenter.clearSubscribers(); + } + + @Test + public void execute_help_add() throws Exception { + // help for add command + String help_Add = "help add"; + assertValidHelpSuccess(help_Add, AddCommand.MESSAGE_USAGE); + } + + @Test + public void execute_help_clear() throws Exception { + // help for clear command + String help_clear = "help clear"; + assertValidHelpSuccess(help_clear, ClearCommand.MESSAGE_USAGE); + } + + @Test + public void execute_help_delete() throws Exception { + // help for delete command + String help_Delete = "help delete"; + assertValidHelpSuccess(help_Delete, DeleteCommand.MESSAGE_USAGE); + } + + @Test + public void execute_help_edit() throws Exception { + // help for edit command + String help_Edit = "help edit"; + assertValidHelpSuccess(help_Edit, EditCommand.MESSAGE_USAGE); + } + + @Test + public void execute_help_exit() throws Exception { + // help for Exit command + String help_Exit = "help exit"; + assertValidHelpSuccess(help_Exit, ExitCommand.MESSAGE_USAGE); + } + + @Test + public void execute_help_find() throws Exception { + // help for find command + String help_Find = "help find"; + assertValidHelpSuccess(help_Find, FindCommand.MESSAGE_USAGE); + } + + @Test + public void execute_help_list() throws Exception { + // help for list command + String help_List = "help list"; + assertValidHelpSuccess(help_List, ListCommand.MESSAGE_USAGE); + } + + @Test + public void execute_help_mark() throws Exception { + // help for mark command + String help_Mark = "help mark"; + assertValidHelpSuccess(help_Mark, MarkCommand.MESSAGE_USAGE); + } + + @Test + public void execute_help_select() throws Exception { + // help for select command + String help_Select = "help select"; + assertValidHelpSuccess(help_Select, HelpCommand.HELP_MESSAGE_USAGE); + } + + @Test + public void execute_help_unmark() throws Exception { + // help for unmark command + String help_Unmark = "help unmark"; + assertValidHelpSuccess(help_Unmark, UnmarkCommand.MESSAGE_USAGE); + } + + @Test + public void execute_invalid_help() throws Exception { + // help for invalid command + String help_Invalid = "help addasd"; + assertInvalidHelpCommandSuccess(help_Invalid, HelpCommand.HELP_MESSAGE_USAGE); + + // help for second invalid command + String help_Invalid2 = "help :<14afa"; + assertInvalidHelpCommandSuccess(help_Invalid2, HelpCommand.HELP_MESSAGE_USAGE); + + } + + private void assertValidHelpSuccess(String inputCommand, String expectedMessage) { + // Execute the command + CommandResult result = logic.execute(inputCommand); + + // Confirm the ui display elements should contain the right data; + assertEquals(expectedMessage, result.feedbackToUser); + } + + private void assertInvalidHelpCommandSuccess(String inputCommand, String expectedMessage) { + // Execute the command + CommandResult result = logic.execute(inputCommand); + + // Confirm the ui display elements should contain the right data; + assertEquals(expectedMessage, result.feedbackToUser); + } + +} +``` +###### /java/guitests/MarkCommandTest.java +``` java +package guitests; + +import org.junit.Test; + +import seedu.flexitrack.commons.core.Messages; +import seedu.flexitrack.logic.commands.MarkCommand; +import seedu.flexitrack.logic.commands.UnmarkCommand; +import seedu.flexitrack.testutil.TestTask; +import seedu.flexitrack.testutil.TestUtil; + +public class MarkCommandTest extends FlexiTrackGuiTest { + TestTask[] currentList = td.getTypicalSortedTasks(); + + @Test + public void mark() { + // mark a task + //TestTask[] currentList = td.getTypicalSortedTasks(); + assertMarkSuccess(4, currentList); + currentList = TestUtil.markTasksToList(currentList, 4); + } + + @Test + public void markFloatingTask() { + // mark a floating task + //TestTask[] currentList = td.getTypicalSortedTasks(); + assertMarkSuccess(1, currentList); + currentList = TestUtil.markTasksToList(currentList, 1); + } + + @Test + public void markInvailidIndex() { + // mark a task with invalid number + commandBox.runCommand(TestTask.getMarkCommand(100)); + assertResultMessage(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + //assertTrue(taskListPanel.isListMatching(currentList)); + } + + @Test + public void markAnMarkedTask() { + // mark an already marked task + commandBox.runCommand(TestTask.getMarkCommand(8)); + assertMarkFail(8, currentList); + //currentList = TestUtil.markTasksToList(currentList, 4); + } + + @Test + public void unmark() { + // un-mark a marked task + commandBox.runCommand(TestTask.getMarkCommand(8)); + assertUnMarkSuccess(8, currentList); + currentList = TestUtil.unMarkTasksToList(currentList, 8); + //assertTrue(taskListPanel.isListMatching(currentList)); + } + + @Test + public void unmarkUndoneTask() { + // un-mark an unmarked task + assertUnMarkFail(3, currentList); + currentList = TestUtil.unMarkTasksToList(currentList, 3); + //assertTrue(taskListPanel.isListMatching(currentList)); + } + + @Test + public void unmarkInvalidIndex() { + // unmark a task with invalid number + commandBox.runCommand(TestTask.getUnMarkCommand(100)); + assertResultMessage(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + //assertTrue(taskListPanel.isListMatching(currentList)); + } + + private void assertMarkFail(int taskToMark, TestTask... currentList) { + commandBox.runCommand(TestTask.getMarkCommand(taskToMark)); + assertResultMessage("Task already marked!"); + + // confirm the list now contains all previous tasks plus the new task + //TestTask[] expectedList = TestUtil.markTasksToList(currentList, taskToMark); + //assertTrue(taskListPanel.isListMatching(expectedList)); + + } + +// private void assertUnMarkSuccess(int taskToUnMark, TestTask... currentList) { + private void assertUnMarkSuccess(int taskToUnMark, TestTask... currentList) { + commandBox.runCommand(TestTask.getUnMarkCommand(taskToUnMark)); + assertResultMessage(String.format(UnmarkCommand.MESSAGE_UNMARK_TASK_SUCCESS, taskToUnMark)); + + // confirm the list now contains all previous tasks plus the new task + //TestTask[] expectedList = TestUtil.unMarkTasksToList(currentList, taskToUnMark); + //assertTrue(taskListPanel.isListMatching(expectedList)); + } + + private void assertUnMarkFail(int taskToUnMark , TestTask... currentList) { + commandBox.runCommand(TestTask.getUnMarkCommand(taskToUnMark)); + assertResultMessage("Task already unmarked!"); + + // confirm the list now contains all previous tasks plus the new task + //TestTask[] expectedList = TestUtil.unMarkTasksToList(currentList, taskToUnMark); + //assertTrue(taskListPanel.isListMatching(expectedList)); + } + + + private void assertMarkSuccess(int taskToMark, TestTask... currentList) { + commandBox.runCommand(TestTask.getMarkCommand(taskToMark)); + assertResultMessage(String.format(MarkCommand.MESSAGE_MARK_TASK_SUCCESS, taskToMark)); + // confirm the list now contains all previous tasks plus the new task + //TestTask[] expectedList = TestUtil.markTasksToList(currentList, taskToMark); + //assertTrue(taskListPanel.isListMatching(expectedList)); + } + +} +``` +###### /java/seedu/flexitrack/testutil/TestTask.java +``` java + public String getBlockCommand() { + StringBuilder sb = new StringBuilder(); + sb.append("block " + this.getName().toString() + " "); + sb.append("from/" + this.getStartTime().toString() + " "); + sb.append("to/" + this.getEndTime().toString() + " "); + return sb.toString(); + } +``` diff --git a/collated/test/A0147092E.md b/collated/test/A0147092E.md new file mode 100644 index 000000000000..6ce760aaa900 --- /dev/null +++ b/collated/test/A0147092E.md @@ -0,0 +1,33 @@ +# A0147092E +###### /java/guitests/AddCommandTest.java +``` java + @Test + public void assertAddRecursiveEventSuccess() throws IllegalValueException { + commandBox.runCommand("add Attend PC1222 lecture fr/3 ty/weekly from/4 Nov 3pm to/4 Nov 5pm"); + + for (int i = 0; i < 3; i++) { + TestTask recursiveEvent = new TaskBuilder().withName("Attend PC1222 lecture") + .withStartTime("Nov " + (4 + (i * 7)) + " 2016 15:00") + .withEndTime("Nov " + (4 + (i * 7)) + " 2016 17:00").withDueDate("Feb 29 2000 00:00").build(); + + currentList = TestUtil.addTasksToList(currentList, recursiveEvent); + + } + assertTrue(taskListPanel.isListMatching(0, currentList)); + } + + @Test + public void assertAddRecursiveTaskSuccess() throws IllegalValueException { + commandBox.runCommand("add Submit PC1222 Lab Assignment fr/3 ty/weekly by/Nov 1 2016 17:00"); + + for (int i = 0; i < 3; i++) { + TestTask recursiveTask = new TaskBuilder().withName("Submit PC1222 Lab Assignment") + .withStartTime("Feb 29 2000 00:00").withEndTime("Feb 29 2000 00:00") + .withDueDate("Nov " + (1 + (i * 7)) + " 2016 17:00").build(); + + currentList = TestUtil.addTasksToList(currentList, recursiveTask); + + } + assertTrue(taskListPanel.isListMatching(0, currentList)); + } +``` diff --git a/config.json b/config.json new file mode 100644 index 000000000000..d4500c474403 --- /dev/null +++ b/config.json @@ -0,0 +1,7 @@ +{ + "appTitle" : "FlexiTrack", + "logLevel" : "INFO", + "userPrefsFilePath" : "preferences.json", + "flexiTrackFilePath" : "data/tasktracker.xml", + "flexiTrackName" : "MyTaskTracker" +} \ No newline at end of file diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 33df65bea583..ff72e29d8c00 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -1,52 +1,87 @@ # About Us -We are a team based in the [School of Computing, National University of Singapore](http://www.comp.nus.edu.sg). +We are a team (Friday 09 [C3]) taking based in the [School of Computing, National University of Singapore](http://www.comp.nus.edu.sg). ## Project Team -#### [Damith C. Rajapakse](http://www.comp.nus.edu.sg/~damithch)
-
-**Role**: Project Advisor +#### [Chan Jun Wei]() +
+Role: Tutor
----- -#### [Joshua Lee](http://github.com/lejolly) -
-Role: Developer
-Responsibilities: UI +#### [Guo Shimin](https://github.com/shimin2016) +
+* Role: Developer
+* Responsibilities: Logic, Command +* Features implemented: + * Help Command + * Mark Command + * Unmark Command + * Block Command + * Change Storage Path Command +* Code written: [[functional code](https://github.com/CS2103AUG2016-F09-C3/main/blob/master/collated/main/A0138455Y.md)][[test code](https://github.com/CS2103AUG2016-F09-C3/main/blob/master/collated/test/A0138455Y.md)][[docs] +(https://github.com/CS2103AUG2016-F09-C3/main/blob/master/collated/docs/A0138455Y.md)] +* Other major contributions: + * Did the initial refactoring from AddressBook to FlexiTrack(Partially) [[#19 ](https://github.com/CS2103AUG2016-F09-C3/main/pull/19)] + * Set up Travis ----- -#### [Leow Yijin](http://github.com/yijinl) -
-Role: Developer
-Responsibilities: Data +#### [(Eddie) Tan Yong Sheng](https://github.com/e-tys) +
+* Role: Developer
+* Responsibilities: Documentation, Command, UI +* Features implemented: + * Find Command + * Add Command(Recurring task and event) +* Code written: [[functional code](https://github.com/CS2103AUG2016-F09-C3/main/blob/master/collated/main/A0147092E.md)][[test code](https://github.com/CS2103AUG2016-F09-C3/main/blob/master/collated/test/A0147092E.md)][[docs](https://github.com/CS2103AUG2016-F09-C3/main/blob/master/collated/docs/A0147092E.md)] +* Other major contributions: + * Change the UI design (colors, sizing) + * Bug fixing ----- -#### [Martin Choo](http://github.com/m133225) -
-Role: Developer
-Responsibilities: Dev Ops +#### [Antonia Devina](https://github.com/antoniadevina) +
+* Role: Developer
+* Responsibilities: Integration, Scheduling and Testing +* Features implemented: + * Mark Command + * Undo Command + * List Command + * Find time (Gap) Command +* Code written: [[functional code](https://github.com/CS2103AUG2016-F09-C3/main/blob/master/collated/main/A0127686R.md)][[test code](https://github.com/CS2103AUG2016-F09-C3/main/blob/master/collated/test/A0127686R.md)][[docs](https://github.com/CS2103AUG2016-F09-C3/main/blob/master/collated/docs/A0127686R.md)] +* Other major contributions: + * Did the initial refactoring from AddressBook to FlexiTrack + * Fix the basic function to work for flexi track + * Fix the initial test cases to fit FlexiTrack + * Implement natty and the DateTimeInfo class and DateTimeInfoParser class + * Remove Tag and all the classes associated with tag ----- -#### [Thien Nguyen](https://github.com/ndt93) +#### [Jing Min](https://github.com/teojm37) +
Role: Developer
- Responsibilities: Threading - - ----- + Responsibilities: Threading, Testing + * Features implemented: + * Edit Command + * Sort the Task + * Shortcut commands + * Changed undo command to make it easier for redo + * Redo Command + +* Code written: [[functional code](https://github.com/CS2103AUG2016-F09-C3/main/blob/master/collated/main/A0127855W.md)][[test code](https://github.com/CS2103AUG2016-F09-C3/main/blob/master/collated/test/A0127855W.md)][[docs](https://github.com/CS2103AUG2016-F09-C3/main/blob/master/collated/docs/A0127855W.md)] -#### [You Liang](http://github.com/yl-coder) -
- Role: Developer
- Responsibilities: UI - +* Other major contributions: + * Cleaninging up code + * Bug fixing + * Design of undo and redo + ----- + # Contributors We welcome contributions. See [Contact Us](ContactUs.md) page for more info. - -* [Akshay Narayan](https://github.com/se-edu/addressbook-level4/pulls?q=is%3Apr+author%3Aokkhoy) -* [Sam Yong](https://github.com/se-edu/addressbook-level4/pulls?q=is%3Apr+author%3Amauris) \ No newline at end of file diff --git a/docs/ContactUs.md b/docs/ContactUs.md index 866d0de3fddc..8578bc4196d5 100644 --- a/docs/ContactUs.md +++ b/docs/ContactUs.md @@ -1,8 +1,8 @@ # Contact Us -* **Bug reports, Suggestions** : Post in our [issue tracker](https://github.com/se-edu/addressbook-level4/issues) +* **Bug reports, Suggestions** : Post in our [issue tracker](https://github.com/CS2103AUG2016-F09-C3/main/issues) if you noticed bugs or have suggestions on how to improve. * **Contributing** : We welcome pull requests. Follow the process described [here](https://github.com/oss-generic/process) -* **Email us** : You can also reach us at `damith [at] comp.nus.edu.sg` \ No newline at end of file +* **Email us** : You can also reach us at `FlexiTrack2016 [at] gmail.com` \ No newline at end of file diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index bc710ed45eb9..e53ddd5bbe37 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -44,13 +44,34 @@ ## Design +//@@author A0127855W + +### Undo/Redo Implementation + +Each undoable command has an execute() method and an executeUndo() method. + +2 static Command stacks are maintained. One in the UndoCommand class (doneCommandStack) and the other in the RedoCommand class (undoneCommandStack). From application startup, whenever an undoable command is entered, the created command object is first populated with the data of whatever the command changes, before the execute() method is called. The changes are then applied and the entire command object is pushed into the doneCommandStack. + +Subsequently, when the UndoCommand is entered, the last command entered is popped out of the doneCommandStack and the executeUndo() method is called. The command is then pushed into the undoneCommandStack. + +When the RedoCommand is entered, the last command undone is popped out of the undoneCommand Stack and the execute() method is called, effectively reexecuting the undone command. The command is then pushed into the doneCommandStack to ensure that the we can undo and redo the same command over and over. + +This method of implemeting the undo/redo functionality was chosen due to its simplicity of implementation, as well as the relatively light memory usage, especially when compared to the 'save entire state' method of implementation. + +//@@author + ### Architecture
The **_Architecture Diagram_** given above explains the high-level design of the App. Given below is a quick overview of each component. -`Main` has only one class called [`MainApp`](../src/main/java/seedu/address/MainApp.java). It is responsible for, +`Main` has only one class called [`MainApp`](../src/main/java/seedu/ + + + + +/MainApp.java). It is responsible for, * At app launch: Initializes the components in the correct sequence, and connect them up with each other. * At shut down: Shuts down the components and invoke cleanup method where necessary. @@ -77,14 +98,14 @@ interface and exposes its functionality using the `LogicManager.java` class.
The _Sequence Diagram_ below shows how the components interact for the scenario where the user issues the command `delete 3`. - + ->Note how the `Model` simply raises a `AddressBookChangedEvent` when the Address Book data are changed, +>Note how the `Model` simply raises a `FlexiTrackChangedEvent` when the FlexiTrack data are changed, instead of asking the `Storage` to save the updates to the hard disk. The diagram below shows how the `EventsCenter` reacts to that event, which eventually results in the updates being saved to the hard disk and the status bar of the UI being updated to reflect the 'Last Updated' time.
- + > Note how the event is propagated through the `EventsCenter` to the `Storage` and `UI` without `Model` having to be coupled to either of them. This is an example of how this Event Driven approach helps us reduce direct @@ -93,18 +114,19 @@ being saved to the hard disk and the status bar of the UI being updated to refle The sections below give more details of each component. ### UI component +//@@ author A0127686R +
-
- -**API** : [`Ui.java`](../src/main/java/seedu/address/ui/Ui.java) +//@@ author +**API** : [`Ui.java`](../src/main/java/seedu/flexitrack/ui/Ui.java) -The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `PersonListPanel`, -`StatusBarFooter`, `BrowserPanel` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class +The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `TaskListPanel`, +`StatusBarFooter` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class and they can be loaded using the `UiPartLoader`. The `UI` component uses JavaFx UI framework. The layout of these UI parts are defined in matching `.fxml` files that are in the `src/main/resources/view` folder.
- For example, the layout of the [`MainWindow`](../src/main/java/seedu/address/ui/MainWindow.java) is specified in + For example, the layout of the [`MainWindow`](../src/main/java/seedu/flexitrack/ui/MainWindow.java) is specified in [`MainWindow.fxml`](../src/main/resources/view/MainWindow.fxml) The `UI` component, @@ -116,43 +138,45 @@ The `UI` component,
-**API** : [`Logic.java`](../src/main/java/seedu/address/logic/Logic.java) +**API** : [`Logic.java`](../src/main/java/seedu/flexitrack/logic/Logic.java) 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`. Given below is the Sequence Diagram for interactions within the `Logic` component for the `execute("delete 1")` API call.
-
+
### Model component +//@@ author A0127686R
-**API** : [`Model.java`](../src/main/java/seedu/address/model/Model.java) +**API** : [`Model.java`](../src/main/java/seedu/flexitrack/model/Model.java) +//@@ author The `Model`, * stores a `UserPref` object that represents the user's preferences. -* stores the Address Book data. -* exposes a `UnmodifiableObservableList` that can be 'observed' e.g. the UI can be bound to this list +* stores the FlexiTrack data. +* exposes a `UnmodifiableObservableList` that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change. * does not depend on any of the other three components. ### Storage component -
+
-**API** : [`Storage.java`](../src/main/java/seedu/address/storage/Storage.java) +**API** : [`Storage.java`](../src/main/java/seedu/flexitrack/storage/Storage.java) The `Storage` component, * can save `UserPref` objects in json format and read it back. -* can save the Address Book data in xml format and read it back. +* can save the Flexi Track data in xml format and read it back. ### Common classes -Classes used by multiple components are in the `seedu.addressbook.commons` package. +Classes used by multiple components are in the `seedu.flexitrack.commons` package. ## Implementation @@ -204,13 +228,13 @@ We have two types of tests: 2. **Non-GUI Tests** - These are tests not involving the GUI. They include, 1. _Unit tests_ targeting the lowest level methods/classes.
- e.g. `seedu.address.commons.UrlUtilTest` + e.g. `seedu.flexitrack.commons.UrlUtilTest` 2. _Integration tests_ that are checking the integration of multiple code units (those code units are assumed to be working).
- e.g. `seedu.address.storage.StorageManagerTest` + e.g. `seedu.flexitrack.storage.StorageManagerTest` 3. Hybrids of unit and integration tests. These test are checking multiple code units as well as how the are connected together.
- e.g. `seedu.address.logic.LogicManagerTest` + e.g. `seedu.flexitrack.logic.LogicManagerTest` **Headless GUI Testing** : Thanks to the [TestFX](https://github.com/TestFX/TestFX) library we use, @@ -241,13 +265,14 @@ Here are the steps to create a new release. ### Managing Dependencies -A project often depends on third-party libraries. For example, Address Book depends on the +A project often depends on third-party libraries. For example, FlexiTrack depends on the [Jackson library](http://wiki.fasterxml.com/JacksonHome) for XML parsing. Managing these _dependencies_ can be automated using Gradle. For example, Gradle can download the dependencies automatically, which is better than these alternatives.
a. Include those libraries in the repo (this bloats the repo size)
b. Require developers to download those libraries manually (this creates extra work for developers)
+//@@ author A0127686R ## Appendix A : User Stories Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unlikely to have) - `*` @@ -255,62 +280,280 @@ 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 +`* * *` | As a user | I want add a task by specifying a task description only, | So that I can record tasks that need to be done ‘some day’. +`* * *` | As a user | I want to add an event, | So that I can track the events that I have. +`* * *` | As a user | I want to add a deadline to a task, | So that I can organised my time better. +`* * *` | As a user | I want to find upcoming tasks, | So that I can decide what needs to be done soon. +`* * *` | As a user | I want to edit a task, | So that I can change the details of a task. +`* * *` | As a user | I want to delete a task, | So that I can get rid of tasks that I no longer care to track. +`* * *` | As a user | I want to undo an operation, | So that I do not need to be afraid to make a mistake. +`* * *` | As a new user | I want to view more information about a particular command, | So that I can learn how to use various commands. +`* * *` | As a new user | I want to mark a task as done, | So that I will be reminded that I have completed the task and not to worry about it. +`* * *` | As an advanced user | I want to use shorter versions of a command, | So that type a command faster. +`* * *` | As a user | I want to specify a specific location to store my data, | So that I can choose to store the data file in a local folder controlled by a cloud syncing service, which allowing me to access data from other computers. +`* * *` | As a user | I want to block a multiple time slot for uncertain time of an event, | So that I will not plan something on the uncertain timing. +`* * *` | As a user | I want to find a free time slot in my schedule, | So that can plan my time better. +`* *` | As a user | I want to add a recurring task, | So that I do not have to keep adding the same task every week/day. +`* *` | As a user | I want to find a task that is similar to what I’m searching, | So that I can see similar tasks. +`* *` | As a user | I want to find a task with the exact name, | So that I can locate the exact task I'm looking for. +`* *` | As a user | I want to launch the program with key combination (ALT+SPACE), | So that I can minimized time spent on clicking. +`* *` | As a user | I want to do everything by typing, | So that I can do everything much faster. +`* *` | As a user | I want to do multiple undo, | So that If I have a mistake i do not have to trace back and correct it manually. +`* *` | As a user | I want to do multiple redo, | So that if I decided not to take the changes from undo, I can go back to use my original file. +`* *` | As a user | I want to have a high flexibility of command format, | So that I do not need to memorize or worry about the format of the input. +`* *` | As a new user | I want to have a guided tour for the app, | So that I will know how to use the app. +`* *` | As a user | I want to receive feedback while typing, | So that I know that i have successfully key in what I typed. +`*` | As a user | I want to add a tag to a task, | So that I know at a glance what the task is for. +`*` | As a user | I want to sort the task by the tag, | So that I can see related tasks together. +`*` | As a user | I want to add a sub-task under a task, | So that I can break down my task into smaller tasks. +`*` | As a user | I want to add a note to a task, | So that I will not forget the details about the task, if there is any. +`*` | As a user | I want to mark a sub-task as done, | So that I will not worry about it anymore. +`*` | As a user | I want to set reminder for a task, | So that I will not forget to do the task. +`*` | As a user | I want to set/pick the reminder time, | So that I can assign reminder according to how long the task need to be completed. -{More to be added} ## Appendix B : Use Cases -(For all use cases below, the **System** is the `AddressBook` and the **Actor** is the `user`, unless specified otherwise) +(For all use cases below, the **System** is the `FlexiTrack` and the **Actor** is the `user`, unless specified otherwise) -#### Use case: Delete person +//@@ author A0127686R +####Use case: UC01 – add new task or event **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
-Use case ends. +1.User request to add a new task or an event +2.System add the data +Use case ends + +**Extensions** + +1a. The user input is invalid + + > 1a1. System output an invalid input message + > Use case ends + +1b. The user input existing active task or event title + + > 1b1. System output warning message that there is existing active task or event title + > Use case ends + +1c. The user wish to add an event on top of another event + + > 1b1. System shows warning that the event clash + > continue 2 + + +####Use case: UC02 – Edit a task + +**MSS** +1.User request to edit an existing task giving the index number and the new task +2.System edit the task +Use case ends + **Extensions** -2a. The list is empty +1a. The user input is invalid + + > 1a1. System output an invalid input message + > Use case ends -> Use case ends -3a. The given index is invalid +####Use case: UC03 – delete + +**MSS** + +1.User request to delete an existing task/event +2.System delete the task/event +Use case ends + +**Extensions** + +1a. The user input an invalid list number + + > 1a1. System output an invalid input message + > Use case ends + +####Use case: UC04 – mark + +**MSS** + +1.User request to mark an event as done +2.System mark the event +Use case ends + +**Extensions** + +1a. The user input an invalid list number + + > 1a1. System output an invalid list number + > Use case ends + + +####Use case: UC05 – unmark + +**MSS** + +1.User request to unmark an event as not done +2.System unmark the event +Use case ends + +**Extensions** + +1a. The user input an invalid list number + + > 1a1. System output an invalid list number + > Use case ends + + +####Use case: UC06 – specify storage + +**MSS** + +1.User request to specify storage +2.System move the storage to the specify directory +Use case ends + +**Extensions** + +1a. The user input an invalid directory + + > 1a1. System output an invalid directory message + > Use case ends + + +####Use case: UC07 – block multiple time slot + +**MSS** + +1.User request to block an extra time slot for an event +2.System block the extra time slot +Use case ends + +**Extensions** + +1a. The user input an invalid list number + + > 1a1. System output an invalid list number message + > Use case ends + +1b. The user select a busy timing (unavailable timing) + + > 1b1. System output an unavailable timing message + > Use case ends + + +//@@ author A0127686R +####Use case: UC09 – Find a free time with a specified length and number of occurrence + +**MSS** + +1.User request to list the task in specific order +2.System shows the list of task at the particular timing +Use case ends + +**Extensions** + +1a. The user input a command word not in the list of choice + + > 1a1. System output an invalid input and how to use list command + > Use case ends + + +####Use case: UC09 – Find a free time with a specified length and number of occurrence + +**MSS** + +1.User request to find a specified length of free time (optional: and number of occurrence) +2.System shows the available timing +Use case ends + +**Extensions** + +1a. The user spell out the length of the free time + + > 1a1. System output an message to repeat command with digit + > Use case ends + +1b. The user spell out the number of occurrence of the free time + + > 1b1. System output an message to repeat command with digit + > Use case ends + +1c. The user put in an invalid argument + + > 1c1. System output a message on how to use the gap command + > Use case ends + +The implementation of finding a free time is similar to the delete command shown earlier section. +Below is the sequence diagram of finding a free time: + + +The two main implementation is parsing and preparing the argument to be passed to the GapCommand class and to find the +free time itself. Below are the activities diagram for the two implementations: + + + -> 3a1. AddressBook shows an error message
- Use case resumes at step 2 -{More to be added} ## 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. -3. Should come with automated unit tests and open source code. -4. Should favor DOS style commands over Unix-style commands. +2. Should come with automated unit tests and open source code. +3. User must get the command done within three mouse clicks +4. The total file size must be less than 15MB +5. Search result must be returned in 0.3ms +6. Buttons and text field must be clearly labeled +7. Make the more important/urgent tasks stand out +8. The longer events will look different from shorter events +9. An event will look differently from a task thus easy to differentiate +10.Easy to see free slots +11.Intuitive vocabulary and design {More to be added} ## Appendix D : Glossary +#####Active task +> A task that have not passed the deadline and have not been marked as done + +#####Active event +> An event that has not passed yet + +#####Event +> An occasion that goes on for a period of time. It has a starting time and an ending time. + +#####Task +> An occasion that need to be completed by a certain date called deadline + ##### Mainstream OS > Windows, Linux, Unix, OS-X -##### Private contact detail +//@@author A0127686R +## Appendix E : Product Survey -> A contact detail that is not meant to be shared with others +####Google Calendar -## Appendix E : Product Survey +#####Pros: +> Help to organise schedules and pre-planned them in advance +> Able to show schedules in multiple view (3 days, day, month or week) format +> Easy to synchronise planned schedules across multiple devices + + +#####Cons: +> Unable to use single line command to add new events +> Each event creation take up around 2 minutes of the user time to fill up the details +> Unable to do partial search for a particular event name +> Confusing timing display on each event. (E.g 11p for night event and 11 for morning event) + + +####Any.Do -{TODO: Add a summary of competing products} +#####Pros: +> Help to add a quick task to the list +> Able to invite friends in the same event or task +> Able to send notification to the user before the event start +> Able to add new task(s) to personalised folder diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 0cf4b84f7470..0e26d54e7ba8 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -2,6 +2,7 @@ * [Quick Start](#quick-start) * [Features](#features) +* [Time Format](#time-format) * [FAQ](#faq) * [Command Summary](#command-summary) @@ -11,7 +12,7 @@ > Having any Java 8 version is not enough.
This app will not work with earlier versions of Java 8. -1. Download the latest `addressbook.jar` from the [releases](../../../releases) tab. +1. Download the latest `FlexiTrack.jar` from the [releases](../../../releases) tab. 2. Copy the file to the folder you want to use as the home folder for your Address Book. 3. Double-click the file to start the app. The GUI should appear in a few seconds. > @@ -19,116 +20,330 @@ 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. + * **`add`**` add CS2103 tutorial 3 by/ Saturday` : + adds a task with the title of `CS2103 tutorial 3` to the FlexiTrack. * **`delete`**` 3` : deletes the 3rd contact shown in the current list * **`exit`** : exits the app 6. Refer to the [Features](#features) section below for details of each command.
- +//@@author A0127686R ## Features > **Command Format** -> * Words in `UPPER_CASE` are the parameters. -> * Items in `SQUARE_BRACKETS` are optional. +> * Words in `square brackets ([])` are the parameters. +> * Items within `arrow signs (<>)` are optional. > * Items with `...` after them can have multiple instances. -> * The order of parameters is fixed. +> * The order of parameters is fixed except for edit command. + +#### Adding a task: `add` +#### Shortcut : `a` +Adds a task to the FlexiTrack.
+Format: `add [task title] < by/ [deadline] >` -#### Viewing help : `help` -Format: `help` +Examples: +* `add CS2103 tutorial 3 ` +* `add CS2103 tutorial 3 by/ Saturday` +* `a CS2103 tutorial 3 by/ tmr 9am` -> 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]...` +#### Adding an event: `add` +#### Shortcut : `a` +Adds a event to the FlexiTrack.
+Format: `add [event title] from/ [starting time] to/ [ending time]` + +Examples: +* `add Bintan trip from/ Saturday to/ Sunday` +* `a CS2103 Lecture from/ Friday 2pm to/ Friday 4pm ` + +//@@author -> Persons can have any number of tags (including 0) +//@@author A0147092E +#### Adding an recurring task: `add` +#### Shortcut : `a` +Adds a task(recursive) to the FlexiTrack.
+Format: `add [task title] fr/ [number of occurrence]` 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` +* `add Plan meet-up for assignment fr/ 5` +* `add Watch DareDevil Season 1 EP fr/ 10` -#### Listing all persons : `list` -Shows a list of all persons in the address book.
-Format: `list` +#### Adding an recurring task (with deadline): `add` +#### Shortcut : `a` +Adds a task(recursive) to the FlexiTrack.
+Format: `add [task title] fr/ [number of occurrence] ty/ ["daily" | "weekly" | "monthly"] from/ [starting time] to/ [ending time]` -#### 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]` +Examples: +* `add Submit PC1222 Labsheet fr/ 5 ty/ week by/ Tuesday 5pm` +* `add complete CS2103 post-lecture quiz fr/ 10 ty/ week by/ Sunday 10pm` + +#### Adding an recurring event: `add` +#### Shortcut : `a` +Adds a event(recursive) to the FlexiTrack.
+Format: `add [event title] fr/ [number of occurrence] ty/ ["daily" | "weekly" | "monthly"] from/ [starting time] to/ [ending time]` + +Examples: +* `add attend PC1222 tutorial fr/ 5 ty/ weekly from/ Tuesday 5pm to/ Tuesday 6pm` +* `add attend CS2103 lecture fr/ 10 ty/ weekly from/ Fri 2pm to/ Fri 4pm` +//@@author + +//@@author A0138455Y +#### Block multiple time slot for an event : `block` +#### Shortcut : `b` +Block another time slot for an unconfirmed existing event.
+Format: `block [Description] from/ [starting time] to/ [ending time]` -> * The search is case sensitive. e.g `hans` will not match `Hans` -> * The order of the keywords does not matter. e.g. `Hans Bo` will match `Bo Hans` -> * Only the name is searched. -> * Only full words will be matched e.g. `Han` will not match `Hans` -> * Persons matching at least one keyword will be returned (i.e. `OR` search). - e.g. `Hans` will match `Hans Bo` +> The new block period must not overlapping current block task. +> New event will not be allow to add in if the period of the new event overlapping any blocked task from block list. + +Examples: +* `block for cs2103 project from/ 5pm to/ 7pm`
+//@@author + +//@@author A0127855W +#### Find free time slots: `gap` +#### Shortcut: `g` +Find and list free time slots in the schedule that is equal to or longer than the specified timing (in hours).
+Format: `find time [number of hours] < [number of slots to find] >` + +> If there is there is a time slot longer than the required free time slot, + then the free time period will be return to you +> By default, find time will only give a single free slot when the number of slots required is not keyed in. + +Examples: +* `gap 3 `
+ You have a minimum of 3 hours free time slot between: today 5pm - 9pm. +* `g 5 3 `
+ You have a minimum of 5 hours free time slot between: Monday 2pm - 9pm, Tuesday 1pm - 6pm and Saturday 9am - 5pm. + +#### Deleting a task or event : `delete` +#### Shortcut : `d` +Deletes the specified task/event from the FlexiTrack.
+Format: `delete [index]` + +> Deletes the task/event 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: +* `delete 2`
+ Deletes the 2nd task/event in the address book. +* `d 1`
+ Deletes the 1st task/event in the results of the `find` command. + +//@@author A0127855W +#### Clear the FlexiTrack : `clear` +#### Shortcut : `c` +Clears the FlexiTrack, resetting it to a blank slate.
+Format: `clear` + +> The command can be undone as long as the user does not exit FlexiTrack after clearing. + +#### Edit a task or event: `edit` +#### Shortcut : `e` +Edits the specified task/event from the FlexiTrack.
+Format: `edit [index] ` + + +> * Edits the task/event 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, ... +> * Edit parameters must fit the type of task / event being edited. e.g. duedate should only be edited on a task. +> * Floating tasks can be converted into tasks or events by editing the appropriate parameter. +> * User cannot edit a floating task into an event with only a starting time but no ending time or vice versa. Examples: -* `find John`
- Returns `John Doe` but not `john` -* `find Betsy Tim John`
- Returns Any person having names `Betsy`, `Tim`, or `John` +* `edit 2 n/ Name Edited`
+ Edits the title of the task/event. +* `e 1 from/ today to/ tomorrow`
+ Edits the start and end times of the specified event. +//@@author -#### Deleting a person : `delete` -Deletes the specified person from the address book. Irreversible.
-Format: `delete INDEX` +//@@author A0138455Y +#### Mark a task as complete : `mark` +#### Shortcut : `m` +Mark an existing task to complete and move it to the bottom of the list.
+Format: `mark [index]` -> Deletes the person at the specified `INDEX`. +> Mark the task/event 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`. +* `mark 5`
+ +#### Mark a task as complete : `unmark` +#### Shortcut : `u` +Mark an existing task to complete and move it to the bottom of the list.
+Format: `unmark [index]` + +> Unmark the taks/event at the specified `index`. The index refers to the index number shown in the most recent listing.
The index **must be a positive integer** 1, 2, 3, ... Examples: -* `list`
- `select 2`
- Selects the 2nd person in the address book. -* `find Betsy`
- `select 1`
- Selects the 1st person in the results of the `find` command. +* `unmark 5`
+//@@author + +//@@author A0127686R +#### Finding a task or an event containing any keyword in their title: `find` +#### Shortcut : `f` +Finds a task ot an event whose title contain any of the given keywords.
+Format: `find KEYWORD [MORE_KEYWORDS]` + +> * The search is non case sensitive. e.g `soccer` will match `Soccer` +> * The order of the keywords does not matter. e.g. `soccer dinner` will match `dinner soccer` +> * Only the task/event title is searched. +> * Only full words will be matched e.g. `socc` will not match `soccer` (unless 'f/' keyword is used) +> * Task or event matching at least one keyword will be returned (i.e. `OR` search). + e.g. `soccer` will match `soccer training` +> * Search by exact task name can be activated with the shortcut 'f/' before the task name. + +Examples: +* `find Soccer`
+ Returns `Soccer training` but not `soccer training` +* `find assignment dinner mid-term`
+ Returns Any task/event having `assignment`, `dinner`, or `mid-term` in the title +* `f attend CS2103 lecture`
+ Returns Any task/event having `attend`, `CS2103`, or `lecture` + +#### Finding a specific task or an event containing an exact phrase in their title: `find f/` +#### Shortcut : `f f/` +Finds a task of an event whose title contain any of the given keywords.
+Format: `find f/ EXACT PHRASE` + +//@@author A0127855W +#### List: `list` +#### Shortcut : `l` +Lists tasks and events that match the specified filter.
+Format: `list ` + +> Accepted filters include: +> * future (shortcut : f) +> * past (p) +> * next week (nw) +> * next month (nm) +> * last week (lw) +> * last month (lm) +> * mark (m) +> * unmark (um) +> * block (b) +Unmarked floating tasks will be listed in all the filter except in block and mark filters. -#### Clearing all entries : `clear` -Clears all entries from the address book.
-Format: `clear` +Examples: +* `list next month`
+ Returns a list of next month's tasks and events +* `l b`
+ Returns a list of all the blocked events + +#### Undo operations : `undo` +#### Shortcut : `ud` +Undo the previous operation.
+Format: `undo` + +> The command will only undo commands entered during the current session of FlexiTrack +> Undo works for: add, delete, clear, mark, unmark, block. + +#### Redo operations : `redo` +#### Shortcut : `rd` +Redo the previously undone operation.
+Format: `redo` + +> The command will only redo commands undone during the current session of FlexiTrack + +//@@author A0138455Y +#### Specify storage location: `cs` +Specify the storage location where the program save the data.
+Format: `cs [path]` + +Examples: +* `cs data/newStorage `
+* `cs newDataStorage `
+Limitation: This feature Only allow user to change storage path within the FlexiTrack folder. + +> [path] can only contains alphanumeric or following special character '\', '/', '-', ':', '.', '_'. + +//@@author A0127855W #### Exiting the program : `exit` +#### Shortcut : `q` Exits the program.
Format: `exit` +//@@author A0138455Y +#### Viewing help : `help` +#### Shortcut : `h` +Format: `help` or `help [command word]` + +Examples: +* `help add` +* `help edit` +* `h delete` + +> Help is also shown if you enter an incorrect command e.g. `abcd` +> default help message will show a list of all command word, e.g. enter `help` + #### Saving the data Address book data are saved in the hard disk automatically after any command that changes the data.
There is no need to save manually. +//@@author A0127686R +## Time Format +FlexiTrack support various timing input. Here are some examples! + +#### Exact timing +User Input | Timing information read by FlexiTrack +---------- | :--------------- +21 June 2018 4pm | Jun 21 2016 16:00 +1st January 7.20 | Jan 01 2016 07:20 +April 22nd 5am | Apr 22 2016 05:00 + +#### Relative timing +If today is 1st of February a relative timing input is also possible with FlexiTrack + +User Input | Timing information read by FlexiTrack +---------- | :--------------- +Tomorrow 4pm | Feb 02 2016 16:00 +Next week 720am | Feb 08 2016 07:20 +3 weeks 2 pm | Feb 22 2016 14:00 +next month 8am | Mar 01 2016 08:00 + +#### World-wide event + +User Input | Timing information read by FlexiTrack +---------- | :--------------- +Christmas morning 9am | Dec 25 2016 16:00 +Easter dinner 7pm | Apr 16 2016 19:00 + +#### Notes on FlexiTrack timing +1. FlexiTrack does support year. However, make sure that you also specify the hour of the timing + as FlexiTrack will choose timing over year when it is uncertain. +2. When you do not specify the exact timing, FlexiTrack will assign your task to be 8:00 for due + date and starting time, and 17:00 for ending time. + ## 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 Address Book folder. - + the file that contains the data of your previous FlexiTrack folder. + +//@@author A0127855W ## 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` +Command | Shortcut | Format +-------- | ---- | :-------- +Add task | a | `add [task title] ` +Add event | a | `add [event title] from/ [starting time] to/ [ending time]` +Block | b | `block [description] from/ [starting time] to/ [ending time]` +Find time | g | `gap [number of hours] < [number of slots to find] >` +Delete | d | `delete [index]` +Clear | c | `clear` +Edit | e | `edit [index] ` +Mark | m | `mark [index]` +Unmark | u | `unmark [index]` +Find | f | `find [key words] < [key words] >` +List | l | `list ` +Select | s | `select [index]` +Undo | ud | `undo` +Redo | rd | `redo` +Change Storage Path | cs | `cs [path]` +Exit | q | `exit` +Help | h | `help ` diff --git a/docs/[F09-C3][FlexiTrack].doc b/docs/[F09-C3][FlexiTrack].doc new file mode 100644 index 000000000000..a5a856f983e6 Binary files /dev/null and b/docs/[F09-C3][FlexiTrack].doc differ diff --git a/docs/[F09-C3][FlexiTrack].pdf b/docs/[F09-C3][FlexiTrack].pdf new file mode 100644 index 000000000000..1993753d2f50 Binary files /dev/null and b/docs/[F09-C3][FlexiTrack].pdf differ diff --git a/docs/images/AntoniaDevina.jpeg b/docs/images/AntoniaDevina.jpeg new file mode 100644 index 000000000000..7c84b113124f Binary files /dev/null and b/docs/images/AntoniaDevina.jpeg differ diff --git a/docs/images/ChanJunWei.jpg b/docs/images/ChanJunWei.jpg new file mode 100644 index 000000000000..9006d5e190e3 Binary files /dev/null and b/docs/images/ChanJunWei.jpg differ diff --git a/docs/images/DeletePersonSdForLogic.png b/docs/images/DeletePersonSdForLogic.png deleted file mode 100644 index 6c272fb17af6..000000000000 Binary files a/docs/images/DeletePersonSdForLogic.png and /dev/null differ diff --git a/docs/images/DeleteTaskSdForLogic.png b/docs/images/DeleteTaskSdForLogic.png new file mode 100644 index 000000000000..c59d99f37bc0 Binary files /dev/null and b/docs/images/DeleteTaskSdForLogic.png differ diff --git a/docs/images/EddieTan.jpg b/docs/images/EddieTan.jpg new file mode 100644 index 000000000000..7e4492f743ae Binary files /dev/null and b/docs/images/EddieTan.jpg differ diff --git a/docs/images/GapCommandExecute.png b/docs/images/GapCommandExecute.png new file mode 100644 index 000000000000..a4667dc9be22 Binary files /dev/null and b/docs/images/GapCommandExecute.png differ diff --git a/docs/images/GapCommandPrepareGap.png b/docs/images/GapCommandPrepareGap.png new file mode 100644 index 000000000000..e42eefae1b95 Binary files /dev/null and b/docs/images/GapCommandPrepareGap.png differ diff --git a/docs/images/GapCommandSequenceDiagram.png b/docs/images/GapCommandSequenceDiagram.png new file mode 100644 index 000000000000..b5d91361014c Binary files /dev/null and b/docs/images/GapCommandSequenceDiagram.png differ diff --git a/docs/images/GuoShiMin.jpg b/docs/images/GuoShiMin.jpg new file mode 100644 index 000000000000..7c03a5e11848 Binary files /dev/null and b/docs/images/GuoShiMin.jpg differ diff --git a/docs/images/JingMin.jpg b/docs/images/JingMin.jpg new file mode 100644 index 000000000000..7b52c922a7cf Binary files /dev/null and b/docs/images/JingMin.jpg differ diff --git a/docs/images/ModelClassDiagram.png b/docs/images/ModelClassDiagram.png index 8cdf11ec93a1..38dad2fded5e 100644 Binary files a/docs/images/ModelClassDiagram.png and b/docs/images/ModelClassDiagram.png differ diff --git a/docs/images/SDforDeletePerson.png b/docs/images/SDforDeletePerson.png deleted file mode 100644 index 1e836f10dcd8..000000000000 Binary files a/docs/images/SDforDeletePerson.png and /dev/null differ diff --git a/docs/images/SDforDeletePersonEventHandling.png b/docs/images/SDforDeletePersonEventHandling.png deleted file mode 100644 index ecec0805d32c..000000000000 Binary files a/docs/images/SDforDeletePersonEventHandling.png and /dev/null differ diff --git a/docs/images/SDforDeleteTask.png b/docs/images/SDforDeleteTask.png new file mode 100644 index 000000000000..6fafd9165386 Binary files /dev/null and b/docs/images/SDforDeleteTask.png differ diff --git a/docs/images/SDforDeleteTaskEventHandling.png b/docs/images/SDforDeleteTaskEventHandling.png new file mode 100644 index 000000000000..34e30f4ebca0 Binary files /dev/null and b/docs/images/SDforDeleteTaskEventHandling.png differ diff --git a/docs/images/StorageClassDiagram.png b/docs/images/StorageClassDiagram.png deleted file mode 100644 index 7a4cd2700cbf..000000000000 Binary files a/docs/images/StorageClassDiagram.png and /dev/null differ diff --git a/docs/images/StorageClassDiagram2.png b/docs/images/StorageClassDiagram2.png new file mode 100644 index 000000000000..0aff58dea099 Binary files /dev/null and b/docs/images/StorageClassDiagram2.png differ diff --git a/docs/images/Ui.png b/docs/images/Ui.png index 7121a50a442a..6765305f8097 100644 Binary files a/docs/images/Ui.png and b/docs/images/Ui.png differ diff --git a/docs/images/UiClassDiagram.png b/docs/images/UiClassDiagram.png deleted file mode 100644 index 459245e267af..000000000000 Binary files a/docs/images/UiClassDiagram.png and /dev/null differ diff --git a/docs/images/UiClassDiagram2.png b/docs/images/UiClassDiagram2.png new file mode 100644 index 000000000000..8d6a8f8dcab7 Binary files /dev/null and b/docs/images/UiClassDiagram2.png differ diff --git a/src/main/java/seedu/address/commons/core/Messages.java b/src/main/java/seedu/address/commons/core/Messages.java deleted file mode 100644 index 1deb3a1e4695..000000000000 --- a/src/main/java/seedu/address/commons/core/Messages.java +++ /dev/null @@ -1,13 +0,0 @@ -package seedu.address.commons.core; - -/** - * Container for user visible messages. - */ -public class Messages { - - public static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command"; - public static final String MESSAGE_INVALID_COMMAND_FORMAT = "Invalid command format! \n%1$s"; - public static final String MESSAGE_INVALID_PERSON_DISPLAYED_INDEX = "The person index provided is invalid"; - public static final String MESSAGE_PERSONS_LISTED_OVERVIEW = "%1$d persons listed!"; - -} diff --git a/src/main/java/seedu/address/commons/events/model/AddressBookChangedEvent.java b/src/main/java/seedu/address/commons/events/model/AddressBookChangedEvent.java deleted file mode 100644 index 347a8359e0d5..000000000000 --- a/src/main/java/seedu/address/commons/events/model/AddressBookChangedEvent.java +++ /dev/null @@ -1,19 +0,0 @@ -package seedu.address.commons.events.model; - -import seedu.address.commons.events.BaseEvent; -import seedu.address.model.ReadOnlyAddressBook; - -/** Indicates the AddressBook in the model has changed*/ -public class AddressBookChangedEvent extends BaseEvent { - - public final ReadOnlyAddressBook data; - - public AddressBookChangedEvent(ReadOnlyAddressBook data){ - this.data = data; - } - - @Override - public String toString() { - return "number of persons " + data.getPersonList().size() + ", number of tags " + data.getTagList().size(); - } -} diff --git a/src/main/java/seedu/address/commons/events/ui/PersonPanelSelectionChangedEvent.java b/src/main/java/seedu/address/commons/events/ui/PersonPanelSelectionChangedEvent.java deleted file mode 100644 index 95377b326fa6..000000000000 --- a/src/main/java/seedu/address/commons/events/ui/PersonPanelSelectionChangedEvent.java +++ /dev/null @@ -1,26 +0,0 @@ -package seedu.address.commons.events.ui; - -import seedu.address.commons.events.BaseEvent; -import seedu.address.model.person.ReadOnlyPerson; - -/** - * Represents a selection change in the Person List Panel - */ -public class PersonPanelSelectionChangedEvent extends BaseEvent { - - - private final ReadOnlyPerson newSelection; - - public PersonPanelSelectionChangedEvent(ReadOnlyPerson newSelection){ - this.newSelection = newSelection; - } - - @Override - public String toString() { - return this.getClass().getSimpleName(); - } - - public ReadOnlyPerson getNewSelection() { - return newSelection; - } -} diff --git a/src/main/java/seedu/address/logic/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/ExitCommand.java b/src/main/java/seedu/address/logic/commands/ExitCommand.java deleted file mode 100644 index d98233ce2a0b..000000000000 --- a/src/main/java/seedu/address/logic/commands/ExitCommand.java +++ /dev/null @@ -1,23 +0,0 @@ -package seedu.address.logic.commands; - -import seedu.address.commons.core.EventsCenter; -import seedu.address.commons.events.ui.ExitAppRequestEvent; - -/** - * Terminates the program. - */ -public class ExitCommand extends Command { - - public static final String COMMAND_WORD = "exit"; - - public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting Address Book as requested ..."; - - public ExitCommand() {} - - @Override - public CommandResult execute() { - EventsCenter.getInstance().post(new ExitAppRequestEvent()); - return new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT); - } - -} 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/HelpCommand.java b/src/main/java/seedu/address/logic/commands/HelpCommand.java deleted file mode 100644 index 65af96940242..000000000000 --- a/src/main/java/seedu/address/logic/commands/HelpCommand.java +++ /dev/null @@ -1,26 +0,0 @@ -package seedu.address.logic.commands; - - -import seedu.address.commons.core.EventsCenter; -import seedu.address.commons.events.ui.ShowHelpRequestEvent; - -/** - * Format full help instructions for every command for display. - */ -public class HelpCommand extends Command { - - public static final String COMMAND_WORD = "help"; - - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Shows program usage instructions.\n" - + "Example: " + COMMAND_WORD; - - public static final String SHOWING_HELP_MESSAGE = "Opened help window."; - - public HelpCommand() {} - - @Override - public CommandResult execute() { - EventsCenter.getInstance().post(new ShowHelpRequestEvent()); - return new CommandResult(SHOWING_HELP_MESSAGE); - } -} 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/Name.java b/src/main/java/seedu/address/model/person/Name.java deleted file mode 100644 index 4f30033e70fe..000000000000 --- a/src/main/java/seedu/address/model/person/Name.java +++ /dev/null @@ -1,55 +0,0 @@ -package seedu.address.model.person; - -import seedu.address.commons.exceptions.IllegalValueException; - -/** - * Represents a Person'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 final String fullName; - - /** - * Validates given name. - * - * @throws IllegalValueException if given name string is invalid. - */ - public Name(String name) throws IllegalValueException { - assert name != null; - name = name.trim(); - if (!isValidName(name)) { - throw new IllegalValueException(MESSAGE_NAME_CONSTRAINTS); - } - this.fullName = name; - } - - /** - * Returns true if a given string is a valid person name. - */ - public static boolean isValidName(String test) { - return test.matches(NAME_VALIDATION_REGEX); - } - - - @Override - public String toString() { - return fullName; - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof Name // instanceof handles nulls - && this.fullName.equals(((Name) other).fullName)); // state check - } - - @Override - public int hashCode() { - return fullName.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 76fb7ff3dc5d..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); - } - } - } - - /** - * Returns true 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 80033086985b..000000000000 --- a/src/main/java/seedu/address/storage/AddressBookStorage.java +++ /dev/null @@ -1,44 +0,0 @@ -package seedu.address.storage; - -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.ReadOnlyAddressBook; - -import java.io.IOException; -import java.util.Optional; - -/** - * Represents a storage for {@link seedu.address.model.AddressBook}. - */ -public interface AddressBookStorage { - - /** - * Returns the file path of the data file. - */ - String getAddressBookFilePath(); - - /** - * Returns AddressBook data as a {@link ReadOnlyAddressBook}. - * Returns {@code Optional.empty()} if storage file is not found. - * @throws DataConversionException if the data in storage is not in the expected format. - * @throws IOException if there was any problem when reading from the storage. - */ - Optional readAddressBook() throws DataConversionException, IOException; - - /** - * @see #getAddressBookFilePath() - */ - Optional readAddressBook(String filePath) throws DataConversionException, IOException; - - /** - * Saves the given {@link ReadOnlyAddressBook} to the storage. - * @param addressBook cannot be null. - * @throws IOException if there was any problem writing to the file. - */ - void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException; - - /** - * @see #saveAddressBook(ReadOnlyAddressBook) - */ - void saveAddressBook(ReadOnlyAddressBook addressBook, String filePath) throws IOException; - -} diff --git a/src/main/java/seedu/address/storage/Storage.java b/src/main/java/seedu/address/storage/Storage.java deleted file mode 100644 index 91002a8a821a..000000000000 --- a/src/main/java/seedu/address/storage/Storage.java +++ /dev/null @@ -1,39 +0,0 @@ -package seedu.address.storage; - -import seedu.address.commons.events.model.AddressBookChangedEvent; -import seedu.address.commons.events.storage.DataSavingExceptionEvent; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.UserPrefs; - -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.Optional; - -/** - * API of the Storage component - */ -public interface Storage extends AddressBookStorage, UserPrefsStorage { - - @Override - Optional readUserPrefs() throws DataConversionException, IOException; - - @Override - void saveUserPrefs(UserPrefs userPrefs) throws IOException; - - @Override - String getAddressBookFilePath(); - - @Override - Optional readAddressBook() throws DataConversionException, IOException; - - @Override - void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException; - - /** - * Saves the current version of the Address Book to the hard disk. - * Creates the data file if it is missing. - * Raises {@link DataSavingExceptionEvent} if there was an error during saving. - */ - void handleAddressBookChangedEvent(AddressBookChangedEvent abce); -} diff --git a/src/main/java/seedu/address/storage/StorageManager.java b/src/main/java/seedu/address/storage/StorageManager.java deleted file mode 100644 index ba1f72f15c27..000000000000 --- a/src/main/java/seedu/address/storage/StorageManager.java +++ /dev/null @@ -1,91 +0,0 @@ -package seedu.address.storage; - -import com.google.common.eventbus.Subscribe; -import seedu.address.commons.core.ComponentManager; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.events.model.AddressBookChangedEvent; -import seedu.address.commons.events.storage.DataSavingExceptionEvent; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.UserPrefs; - -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.Optional; -import java.util.logging.Logger; - -/** - * Manages storage of AddressBook data in local storage. - */ -public class StorageManager extends ComponentManager implements Storage { - - private static final Logger logger = LogsCenter.getLogger(StorageManager.class); - private AddressBookStorage addressBookStorage; - private UserPrefsStorage userPrefsStorage; - - - public StorageManager(AddressBookStorage addressBookStorage, UserPrefsStorage userPrefsStorage) { - super(); - this.addressBookStorage = addressBookStorage; - this.userPrefsStorage = userPrefsStorage; - } - - public StorageManager(String addressBookFilePath, String userPrefsFilePath) { - this(new XmlAddressBookStorage(addressBookFilePath), new JsonUserPrefsStorage(userPrefsFilePath)); - } - - // ================ UserPrefs methods ============================== - - @Override - public Optional readUserPrefs() throws DataConversionException, IOException { - return userPrefsStorage.readUserPrefs(); - } - - @Override - public void saveUserPrefs(UserPrefs userPrefs) throws IOException { - userPrefsStorage.saveUserPrefs(userPrefs); - } - - - // ================ AddressBook methods ============================== - - @Override - public String getAddressBookFilePath() { - return addressBookStorage.getAddressBookFilePath(); - } - - @Override - public Optional readAddressBook() throws DataConversionException, IOException { - return readAddressBook(addressBookStorage.getAddressBookFilePath()); - } - - @Override - public Optional readAddressBook(String filePath) throws DataConversionException, IOException { - logger.fine("Attempting to read data from file: " + filePath); - return addressBookStorage.readAddressBook(filePath); - } - - @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException { - saveAddressBook(addressBook, addressBookStorage.getAddressBookFilePath()); - } - - @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook, String filePath) throws IOException { - logger.fine("Attempting to write to data file: " + filePath); - addressBookStorage.saveAddressBook(addressBook, filePath); - } - - - @Override - @Subscribe - public void handleAddressBookChangedEvent(AddressBookChangedEvent event) { - logger.info(LogsCenter.getEventHandlingLogMessage(event, "Local data changed, saving to file")); - try { - saveAddressBook(event.data); - } catch (IOException e) { - raise(new DataSavingExceptionEvent(e)); - } - } - -} diff --git a/src/main/java/seedu/address/storage/UserPrefsStorage.java b/src/main/java/seedu/address/storage/UserPrefsStorage.java deleted file mode 100644 index ad2dc935187c..000000000000 --- a/src/main/java/seedu/address/storage/UserPrefsStorage.java +++ /dev/null @@ -1,29 +0,0 @@ -package seedu.address.storage; - -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.UserPrefs; - -import java.io.IOException; -import java.util.Optional; - -/** - * Represents a storage for {@link seedu.address.model.UserPrefs}. - */ -public interface UserPrefsStorage { - - /** - * Returns UserPrefs data from storage. - * 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 readUserPrefs() throws DataConversionException, IOException; - - /** - * Saves the given {@link seedu.address.model.UserPrefs} to the storage. - * @param userPrefs cannot be null. - * @throws IOException if there was any problem writing to the file. - */ - void saveUserPrefs(UserPrefs userPrefs) throws IOException; - -} 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/XmlFileStorage.java b/src/main/java/seedu/address/storage/XmlFileStorage.java deleted file mode 100644 index 27a5210cadaf..000000000000 --- a/src/main/java/seedu/address/storage/XmlFileStorage.java +++ /dev/null @@ -1,38 +0,0 @@ -package seedu.address.storage; - -import seedu.address.commons.util.XmlUtil; -import seedu.address.commons.exceptions.DataConversionException; - -import javax.xml.bind.JAXBException; -import java.io.File; -import java.io.FileNotFoundException; - -/** - * Stores addressbook data in an XML file - */ -public class XmlFileStorage { - /** - * Saves the given addressbook data to the specified file. - */ - public static void saveDataToFile(File file, XmlSerializableAddressBook addressBook) - throws FileNotFoundException { - try { - XmlUtil.saveDataToFile(file, addressBook); - } catch (JAXBException e) { - assert false : "Unexpected exception " + e.getMessage(); - } - } - - /** - * Returns address book in the file or an empty address book - */ - public static XmlSerializableAddressBook loadDataFromSaveFile(File file) throws DataConversionException, - FileNotFoundException { - try { - return XmlUtil.getDataFromFile(file, XmlSerializableAddressBook.class); - } catch (JAXBException e) { - throw new DataConversionException(e); - } - } - -} 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 b7ec533a3a1e..000000000000 --- a/src/main/java/seedu/address/storage/XmlSerializableAddressBook.java +++ /dev/null @@ -1,88 +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) { - //TODO: better error handling - e.printStackTrace(); - return null; - } - } - - @Override - public UniquePersonList getUniquePersonList() { - UniquePersonList lists = new UniquePersonList(); - for (XmlAdaptedPerson p : persons) { - try { - lists.add(p.toModelType()); - } catch (IllegalValueException e) { - //TODO: better error handling - } - } - return lists; - } - - @Override - public List getPersonList() { - return persons.stream().map(p -> { - try { - return p.toModelType(); - } catch (IllegalValueException e) { - e.printStackTrace(); - //TODO: better error handling - 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/flexitrack/MainApp.java similarity index 61% rename from src/main/java/seedu/address/MainApp.java rename to src/main/java/seedu/flexitrack/MainApp.java index 36dc72a74b7a..0d69a0dc3996 100644 --- a/src/main/java/seedu/address/MainApp.java +++ b/src/main/java/seedu/flexitrack/MainApp.java @@ -1,31 +1,37 @@ -package seedu.address; +package seedu.flexitrack; -import com.google.common.eventbus.Subscribe; -import javafx.application.Application; -import javafx.application.Platform; -import javafx.stage.Stage; -import seedu.address.commons.core.Config; -import seedu.address.commons.core.EventsCenter; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.core.Version; -import seedu.address.commons.events.ui.ExitAppRequestEvent; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.commons.util.StringUtil; -import seedu.address.logic.Logic; -import seedu.address.logic.LogicManager; -import seedu.address.model.*; -import seedu.address.commons.util.ConfigUtil; -import seedu.address.storage.Storage; -import seedu.address.storage.StorageManager; -import seedu.address.ui.Ui; -import seedu.address.ui.UiManager; - -import java.io.FileNotFoundException; import java.io.IOException; import java.util.Map; import java.util.Optional; import java.util.logging.Logger; +import com.google.common.eventbus.Subscribe; + +import javafx.application.Application; +import javafx.application.Platform; +import javafx.stage.Stage; +import seedu.flexitrack.commons.core.Config; +import seedu.flexitrack.commons.core.EventsCenter; +import seedu.flexitrack.commons.core.LogsCenter; +import seedu.flexitrack.commons.core.Version; +import seedu.flexitrack.commons.events.ui.ExitAppRequestEvent; +import seedu.flexitrack.commons.events.ui.JumpToListRequestEvent; +import seedu.flexitrack.commons.events.ui.StoragePathChangeEvent; +import seedu.flexitrack.commons.exceptions.DataConversionException; +import seedu.flexitrack.commons.util.ConfigUtil; +import seedu.flexitrack.commons.util.StringUtil; +import seedu.flexitrack.logic.Logic; +import seedu.flexitrack.logic.LogicManager; +import seedu.flexitrack.model.FlexiTrack; +import seedu.flexitrack.model.Model; +import seedu.flexitrack.model.ModelManager; +import seedu.flexitrack.model.ReadOnlyFlexiTrack; +import seedu.flexitrack.model.UserPrefs; +import seedu.flexitrack.storage.Storage; +import seedu.flexitrack.storage.StorageManager; +import seedu.flexitrack.ui.Ui; +import seedu.flexitrack.ui.UiManager; + /** * The main entry point to the application. */ @@ -41,15 +47,16 @@ public class MainApp extends Application { protected Config config; protected UserPrefs userPrefs; - public MainApp() {} + public MainApp() { + } @Override public void init() throws Exception { - logger.info("=============================[ Initializing AddressBook ]==========================="); + logger.info("=============================[ Initializing FlexiTrack ]==========================="); super.init(); config = initConfig(getApplicationParameter("config")); - storage = new StorageManager(config.getAddressBookFilePath(), config.getUserPrefsFilePath()); + storage = new StorageManager(config.getFlexiTrackFilePath(), config.getUserPrefsFilePath()); userPrefs = initPrefs(config); @@ -64,26 +71,26 @@ public void init() throws Exception { initEventsCenter(); } - private String getApplicationParameter(String parameterName){ + private String getApplicationParameter(String parameterName) { Map applicationParameters = getParameters().getNamed(); return applicationParameters.get(parameterName); } private Model initModelManager(Storage storage, UserPrefs userPrefs) { - Optional addressBookOptional; - ReadOnlyAddressBook initialData; + Optional flexiTrackOptional; + ReadOnlyFlexiTrack initialData; try { - addressBookOptional = storage.readAddressBook(); - if(!addressBookOptional.isPresent()){ - logger.info("Data file not found. Will be starting with an empty AddressBook"); + flexiTrackOptional = storage.readFlexiTrack(); + if (!flexiTrackOptional.isPresent()) { + logger.info("Data file not found. Will be starting with an empty FlexiTrack"); } - initialData = addressBookOptional.orElse(new AddressBook()); + initialData = flexiTrackOptional.orElse(new FlexiTrack()); } 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 FlexiTrack"); + initialData = new FlexiTrack(); } catch (IOException e) { - logger.warning("Problem while reading from the file. . Will be starting with an empty AddressBook"); - initialData = new AddressBook(); + logger.warning("Problem while reading from the file. . Will be starting with an empty FlexiTrack"); + initialData = new FlexiTrack(); } return new ModelManager(initialData, userPrefs); @@ -99,7 +106,7 @@ protected Config initConfig(String configFilePath) { configFilePathUsed = Config.DEFAULT_CONFIG_FILE; - if(configFilePath != null) { + if (configFilePath != null) { logger.info("Custom Config file specified " + configFilePath); configFilePathUsed = configFilePath; } @@ -110,12 +117,13 @@ protected Config initConfig(String configFilePath) { Optional configOptional = ConfigUtil.readConfig(configFilePathUsed); initializedConfig = configOptional.orElse(new Config()); } catch (DataConversionException e) { - logger.warning("Config file at " + configFilePathUsed + " is not in the correct format. " + - "Using default config properties"); + logger.warning("Config file at " + configFilePathUsed + " is not in the correct format. " + + "Using default config properties"); initializedConfig = new Config(); } - //Update config file in case it was missing to begin with or there are new/unused fields + // Update config file in case it was missing to begin with or there are + // new/unused fields try { ConfigUtil.saveConfig(initializedConfig, configFilePathUsed); } catch (IOException e) { @@ -135,15 +143,16 @@ protected UserPrefs initPrefs(Config config) { Optional prefsOptional = storage.readUserPrefs(); initializedPrefs = prefsOptional.orElse(new UserPrefs()); } catch (DataConversionException e) { - logger.warning("UserPrefs file at " + prefsFilePath + " is not in the correct format. " + - "Using default user prefs"); + logger.warning("UserPrefs file at " + prefsFilePath + " is not in the correct format. " + + "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 FlexiTrack"); initializedPrefs = new UserPrefs(); } - //Update prefs file in case it was missing to begin with or there are new/unused fields + // Update prefs file in case it was missing to begin with or there are + // new/unused fields try { storage.saveUserPrefs(initializedPrefs); } catch (IOException e) { @@ -159,13 +168,13 @@ private void initEventsCenter() { @Override public void start(Stage primaryStage) { - logger.info("Starting AddressBook " + MainApp.VERSION); + logger.info("Starting FlexiTrack " + MainApp.VERSION); ui.start(primaryStage); } @Override public void stop() { - logger.info("============================ [ Stopping Address Book ] ============================="); + logger.info("============================ [ Stopping FlexiTrack ] ============================="); ui.stop(); try { storage.saveUserPrefs(userPrefs); @@ -185,4 +194,20 @@ public void handleExitAppRequestEvent(ExitAppRequestEvent event) { public static void main(String[] args) { launch(args); } + + //@@author A0138455Y + @Subscribe + public void changeStoragePathRequestEvent(StoragePathChangeEvent event) { + logger.info(LogsCenter.getEventHandlingLogMessage(event)); + config.setFlexiTrackFilePath(event.toString()); + try { + ConfigUtil.saveConfig(config, Config.DEFAULT_CONFIG_FILE); + } catch (IOException e) { + logger.warning("Failed to save config file : " + StringUtil.getDetails(e)); + } + + storage.setStoragePath(event.toString()); + model.indicateFlexiTrackerChanged(); + } + } diff --git a/src/main/java/seedu/address/commons/core/ComponentManager.java b/src/main/java/seedu/flexitrack/commons/core/ComponentManager.java similarity index 74% rename from src/main/java/seedu/address/commons/core/ComponentManager.java rename to src/main/java/seedu/flexitrack/commons/core/ComponentManager.java index 4bc8564e5824..26ce120e783a 100644 --- a/src/main/java/seedu/address/commons/core/ComponentManager.java +++ b/src/main/java/seedu/flexitrack/commons/core/ComponentManager.java @@ -1,6 +1,6 @@ -package seedu.address.commons.core; +package seedu.flexitrack.commons.core; -import seedu.address.commons.events.BaseEvent; +import seedu.flexitrack.commons.events.BaseEvent; /** * Base class for *Manager classes @@ -13,7 +13,7 @@ public abstract class ComponentManager { /** * Uses default {@link EventsCenter} */ - public ComponentManager(){ + public ComponentManager() { this(EventsCenter.getInstance()); } @@ -22,7 +22,7 @@ public ComponentManager(EventsCenter eventsCenter) { eventsCenter.registerHandler(this); } - protected void raise(BaseEvent event){ + protected void raise(BaseEvent event) { eventsCenter.post(event); } } diff --git a/src/main/java/seedu/address/commons/core/Config.java b/src/main/java/seedu/flexitrack/commons/core/Config.java similarity index 56% rename from src/main/java/seedu/address/commons/core/Config.java rename to src/main/java/seedu/flexitrack/commons/core/Config.java index 6441c9ef20f4..920ee7f04fe3 100644 --- a/src/main/java/seedu/address/commons/core/Config.java +++ b/src/main/java/seedu/flexitrack/commons/core/Config.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package seedu.flexitrack.commons.core; import java.util.Objects; import java.util.logging.Level; @@ -11,12 +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 = "FlexiTrack"; private Level logLevel = Level.INFO; private String userPrefsFilePath = "preferences.json"; - private String addressBookFilePath = "data/addressbook.xml"; - private String addressBookName = "MyAddressBook"; - + private String flexiTrackFilePath = "tasktracker.xml"; + private String flexiTrackName = "MyTaskTracker"; public Config() { } @@ -45,54 +44,52 @@ public void setUserPrefsFilePath(String userPrefsFilePath) { this.userPrefsFilePath = userPrefsFilePath; } - public String getAddressBookFilePath() { - return addressBookFilePath; + public String getFlexiTrackFilePath() { + return flexiTrackFilePath; } - public void setAddressBookFilePath(String addressBookFilePath) { - this.addressBookFilePath = addressBookFilePath; + public void setFlexiTrackFilePath(String flexiTrackFilePath) { + this.flexiTrackFilePath = flexiTrackFilePath; } - public String getAddressBookName() { - return addressBookName; + public String getFlexiTrackName() { + return flexiTrackName; } - public void setAddressBookName(String addressBookName) { - this.addressBookName = addressBookName; + public void setFlexiTrackName(String flexiTrackName) { + this.flexiTrackName = flexiTrackName; } - @Override public boolean equals(Object other) { - if (other == this){ + if (other == this) { return true; } - if (!(other instanceof Config)){ //this handles null as well. + if (!(other instanceof Config)) { // this handles null as well. return false; } - Config o = (Config)other; + Config o = (Config) other; - return Objects.equals(appTitle, o.appTitle) - && Objects.equals(logLevel, o.logLevel) + 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(flexiTrackFilePath, o.flexiTrackFilePath) + && Objects.equals(flexiTrackName, o.flexiTrackName); } @Override public int hashCode() { - return Objects.hash(appTitle, logLevel, userPrefsFilePath, addressBookFilePath, addressBookName); + return Objects.hash(appTitle, logLevel, userPrefsFilePath, flexiTrackFilePath, flexiTrackName); } @Override - public String toString(){ + public String toString() { StringBuilder sb = new StringBuilder(); 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 : " + flexiTrackFilePath); + sb.append("\nFlexiTrack name : " + flexiTrackName); return sb.toString(); } diff --git a/src/main/java/seedu/address/commons/core/EventsCenter.java b/src/main/java/seedu/flexitrack/commons/core/EventsCenter.java similarity index 92% rename from src/main/java/seedu/address/commons/core/EventsCenter.java rename to src/main/java/seedu/flexitrack/commons/core/EventsCenter.java index 9652cd5c227b..38c45b6bffde 100644 --- a/src/main/java/seedu/address/commons/core/EventsCenter.java +++ b/src/main/java/seedu/flexitrack/commons/core/EventsCenter.java @@ -1,9 +1,10 @@ -package seedu.address.commons.core; +package seedu.flexitrack.commons.core; + +import java.util.logging.Logger; import com.google.common.eventbus.EventBus; -import seedu.address.commons.events.BaseEvent; -import java.util.logging.Logger; +import seedu.flexitrack.commons.events.BaseEvent; /** * Manages the event dispatching of the app. diff --git a/src/main/java/seedu/address/commons/core/GuiSettings.java b/src/main/java/seedu/flexitrack/commons/core/GuiSettings.java similarity index 83% rename from src/main/java/seedu/address/commons/core/GuiSettings.java rename to src/main/java/seedu/flexitrack/commons/core/GuiSettings.java index e157ac8b8679..ab83b6a30b15 100644 --- a/src/main/java/seedu/address/commons/core/GuiSettings.java +++ b/src/main/java/seedu/flexitrack/commons/core/GuiSettings.java @@ -1,6 +1,6 @@ -package seedu.address.commons.core; +package seedu.flexitrack.commons.core; -import java.awt.*; +import java.awt.Point; import java.io.Serializable; import java.util.Objects; @@ -42,17 +42,16 @@ public Point getWindowCoordinates() { @Override public boolean equals(Object other) { - if (other == this){ + if (other == this) { return true; } - if (!(other instanceof GuiSettings)){ //this handles null as well. + if (!(other instanceof GuiSettings)) { // this handles null as well. return false; } - GuiSettings o = (GuiSettings)other; + GuiSettings o = (GuiSettings) other; - return Objects.equals(windowWidth, o.windowWidth) - && Objects.equals(windowHeight, o.windowHeight) + return Objects.equals(windowWidth, o.windowWidth) && Objects.equals(windowHeight, o.windowHeight) && Objects.equals(windowCoordinates.x, o.windowCoordinates.x) && Objects.equals(windowCoordinates.y, o.windowCoordinates.y); } @@ -63,7 +62,7 @@ public int hashCode() { } @Override - public String toString(){ + public String toString() { StringBuilder sb = new StringBuilder(); sb.append("Width : " + windowWidth + "\n"); sb.append("Height : " + windowHeight + "\n"); diff --git a/src/main/java/seedu/address/commons/core/LogsCenter.java b/src/main/java/seedu/flexitrack/commons/core/LogsCenter.java similarity index 72% rename from src/main/java/seedu/address/commons/core/LogsCenter.java rename to src/main/java/seedu/flexitrack/commons/core/LogsCenter.java index 17939bab4975..1fc35a2bfa4a 100644 --- a/src/main/java/seedu/address/commons/core/LogsCenter.java +++ b/src/main/java/seedu/flexitrack/commons/core/LogsCenter.java @@ -1,31 +1,38 @@ -package seedu.address.commons.core; - -import seedu.address.commons.events.BaseEvent; +package seedu.flexitrack.commons.core; import java.io.IOException; -import java.util.logging.*; +import java.util.logging.ConsoleHandler; +import java.util.logging.FileHandler; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.logging.SimpleFormatter; + +import seedu.flexitrack.commons.events.BaseEvent; /** * Configures and manages loggers and handlers, including their logging level * Named {@link Logger}s can be obtained from this class
- * These loggers have been configured to output messages to the console and a {@code .log} file by default, - * at the {@code INFO} level. A new {@code .log} file with a new numbering will be created after the log - * file reaches 5MB big, up to a maximum of 5 files.
+ * These loggers have been configured to output messages to the console and a + * {@code .log} file by default, at the {@code INFO} level. A new {@code .log} + * file with a new numbering will be created after the log file reaches 5MB big, + * up to a maximum of 5 files.
*/ 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 = "flexitrack.log"; private static Level currentLogLevel = Level.INFO; private static final Logger logger = LogsCenter.getLogger(LogsCenter.class); private static FileHandler fileHandler; private static ConsoleHandler consoleHandler; /** - * Initializes with a custom log level (specified in the {@code config} object) - * Loggers obtained *AFTER* this initialization will have their logging level changed
- * Logging levels for existing loggers will only be updated if the logger with the same name is requested again - * from the LogsCenter. + * Initializes with a custom log level (specified in the {@code config} + * object) Loggers obtained *AFTER* this initialization will have their + * logging level changed
+ * Logging levels for existing loggers will only be updated if the logger + * with the same name is requested again from the LogsCenter. */ public static void init(Config config) { currentLogLevel = config.getLogLevel(); @@ -47,7 +54,8 @@ public static Logger getLogger(String name) { } private static void addConsoleHandler(Logger logger) { - if (consoleHandler == null) consoleHandler = createConsoleHandler(); + if (consoleHandler == null) + consoleHandler = createConsoleHandler(); logger.addHandler(consoleHandler); } @@ -60,7 +68,8 @@ private static void removeHandlers(Logger logger) { private static void addFileHandler(Logger logger) { try { - if (fileHandler == null) fileHandler = createFileHandler(); + if (fileHandler == null) + fileHandler = createFileHandler(); logger.addHandler(fileHandler); } catch (IOException e) { logger.warning("Error adding file handler for logger."); @@ -84,12 +93,14 @@ private static ConsoleHandler createConsoleHandler() { * Creates a Logger for the given class name. */ public static Logger getLogger(Class clazz) { - if (clazz == null) return Logger.getLogger(""); + if (clazz == null) + return Logger.getLogger(""); return getLogger(clazz.getSimpleName()); } /** - * Decorates the given string to create a log message suitable for logging event handling methods. + * Decorates the given string to create a log message suitable for logging + * event handling methods. */ public static String getEventHandlingLogMessage(BaseEvent e, String message) { return "---[Event handled][" + e + "]" + message; @@ -99,6 +110,6 @@ public static String getEventHandlingLogMessage(BaseEvent e, String message) { * @see #getEventHandlingLogMessage(BaseEvent, String) */ public static String getEventHandlingLogMessage(BaseEvent e) { - return getEventHandlingLogMessage(e,""); + return getEventHandlingLogMessage(e, ""); } } diff --git a/src/main/java/seedu/flexitrack/commons/core/Messages.java b/src/main/java/seedu/flexitrack/commons/core/Messages.java new file mode 100644 index 000000000000..279d2c12f36e --- /dev/null +++ b/src/main/java/seedu/flexitrack/commons/core/Messages.java @@ -0,0 +1,14 @@ +package seedu.flexitrack.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_TASK_LISTED_OVERVIEW = "%1$d tasks listed!"; + public static final String MESSAGE_NUMBER_NEED_TO_BE_IN_DIGIT = "Please enter number in digit."; + +} diff --git a/src/main/java/seedu/address/commons/core/UnmodifiableObservableList.java b/src/main/java/seedu/flexitrack/commons/core/UnmodifiableObservableList.java similarity index 97% rename from src/main/java/seedu/address/commons/core/UnmodifiableObservableList.java rename to src/main/java/seedu/flexitrack/commons/core/UnmodifiableObservableList.java index 5c25d8647a8d..9f0ac260ab6b 100644 --- a/src/main/java/seedu/address/commons/core/UnmodifiableObservableList.java +++ b/src/main/java/seedu/flexitrack/commons/core/UnmodifiableObservableList.java @@ -1,18 +1,23 @@ -package seedu.address.commons.core; - -import javafx.beans.InvalidationListener; -import javafx.collections.ListChangeListener; -import javafx.collections.ObservableList; -import javafx.collections.transformation.FilteredList; -import javafx.collections.transformation.SortedList; +package seedu.flexitrack.commons.core; import java.text.Collator; -import java.util.*; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; import java.util.function.Consumer; import java.util.function.Predicate; import java.util.function.UnaryOperator; import java.util.stream.Stream; +import javafx.beans.InvalidationListener; +import javafx.collections.ListChangeListener; +import javafx.collections.ObservableList; +import javafx.collections.transformation.FilteredList; +import javafx.collections.transformation.SortedList; + /** * Unmodifiable view of an observable list */ @@ -28,7 +33,7 @@ public UnmodifiableObservableList(ObservableList backingList) { } this.backingList = backingList; } - + @Override public final void addListener(ListChangeListener listener) { backingList.addListener(listener); @@ -63,12 +68,12 @@ public final boolean setAll(Object... elements) { public final boolean setAll(Collection col) { throw new UnsupportedOperationException(MUTATION_OP_EXCEPTION_MESSAGE); } - + @Override public final boolean removeAll(Object... elements) { throw new UnsupportedOperationException(MUTATION_OP_EXCEPTION_MESSAGE); } - + @Override public final boolean retainAll(Object... elements) { throw new UnsupportedOperationException(MUTATION_OP_EXCEPTION_MESSAGE); @@ -79,7 +84,6 @@ public final void remove(int from, int to) { throw new UnsupportedOperationException(MUTATION_OP_EXCEPTION_MESSAGE); } - @Override public final FilteredList filtered(Predicate predicate) { return new FilteredList<>(this, predicate); @@ -110,12 +114,12 @@ public final int size() { public final boolean isEmpty() { return backingList.isEmpty(); } - + @Override public final boolean contains(Object o) { return backingList.contains(o); } - + @Override public final Iterator iterator() { return new Iterator() { @@ -124,12 +128,15 @@ public final Iterator iterator() { public final boolean hasNext() { return i.hasNext(); } + public final E next() { return i.next(); } + public final void remove() { throw new UnsupportedOperationException(); } + @Override public final void forEachRemaining(Consumer action) { // Use backing collection version @@ -152,7 +159,7 @@ public final T[] toArray(T[] a) { public final boolean add(E o) { throw new UnsupportedOperationException(MUTATION_OP_EXCEPTION_MESSAGE); } - + @Override public final boolean remove(Object o) { throw new UnsupportedOperationException(MUTATION_OP_EXCEPTION_MESSAGE); @@ -172,12 +179,12 @@ public final boolean addAll(Collection c) { public final boolean addAll(int index, Collection c) { throw new UnsupportedOperationException(MUTATION_OP_EXCEPTION_MESSAGE); } - + @Override public final boolean removeAll(Collection c) { throw new UnsupportedOperationException(MUTATION_OP_EXCEPTION_MESSAGE); } - + @Override public final boolean retainAll(Collection c) { throw new UnsupportedOperationException(MUTATION_OP_EXCEPTION_MESSAGE); @@ -192,13 +199,12 @@ public final void replaceAll(UnaryOperator operator) { public final void sort(Comparator c) { throw new UnsupportedOperationException(MUTATION_OP_EXCEPTION_MESSAGE); } - + @Override public final void clear() { throw new UnsupportedOperationException(MUTATION_OP_EXCEPTION_MESSAGE); } - @Override public final boolean equals(Object o) { return o == this || backingList.equals(o); @@ -209,7 +215,6 @@ public final int hashCode() { return backingList.hashCode(); } - @Override public final E get(int index) { return backingList.get(index); @@ -230,12 +235,12 @@ public final void add(int index, Object element) { public final E remove(int index) { throw new UnsupportedOperationException(MUTATION_OP_EXCEPTION_MESSAGE); } - + @Override public final int indexOf(Object o) { return backingList.indexOf(o); } - + @Override public final int lastIndexOf(Object o) { return backingList.lastIndexOf(o); @@ -245,7 +250,7 @@ public final int lastIndexOf(Object o) { public final ListIterator listIterator() { return listIterator(0); } - + @Override public final ListIterator listIterator(int index) { return new ListIterator() { @@ -254,18 +259,23 @@ public final ListIterator listIterator(int index) { public final boolean hasNext() { return i.hasNext(); } + public final E next() { return i.next(); } + public final boolean hasPrevious() { return i.hasPrevious(); } + public final E previous() { return i.previous(); } + public final int nextIndex() { return i.nextIndex(); } + public final int previousIndex() { return i.previousIndex(); } @@ -273,9 +283,11 @@ public final int previousIndex() { public final void remove() { throw new UnsupportedOperationException(MUTATION_OP_EXCEPTION_MESSAGE); } + public final void set(E e) { throw new UnsupportedOperationException(MUTATION_OP_EXCEPTION_MESSAGE); } + public final void add(E e) { throw new UnsupportedOperationException(MUTATION_OP_EXCEPTION_MESSAGE); } @@ -302,7 +314,7 @@ public final boolean removeIf(Predicate filter) { public final Stream stream() { return (Stream) backingList.stream(); } - + @Override public final void forEach(Consumer action) { backingList.forEach(action); diff --git a/src/main/java/seedu/address/commons/core/Version.java b/src/main/java/seedu/flexitrack/commons/core/Version.java similarity index 82% rename from src/main/java/seedu/address/commons/core/Version.java rename to src/main/java/seedu/flexitrack/commons/core/Version.java index 7ecb85b18f82..06dd491cbcfb 100644 --- a/src/main/java/seedu/address/commons/core/Version.java +++ b/src/main/java/seedu/flexitrack/commons/core/Version.java @@ -1,11 +1,11 @@ -package seedu.address.commons.core; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonValue; +package seedu.flexitrack.commons.core; import java.util.regex.Matcher; import java.util.regex.Pattern; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + /** * Represents a version with major, minor and patch number */ @@ -47,7 +47,9 @@ public boolean isEarlyAccess() { /** * Parses a version number string in the format V1.2.3. - * @param versionString version number string + * + * @param versionString + * version number string * @return a Version object */ @JsonCreator @@ -58,10 +60,8 @@ public static Version fromString(String versionString) throws IllegalArgumentExc throw new IllegalArgumentException(String.format(EXCEPTION_STRING_NOT_VERSION, versionString)); } - return new Version(Integer.parseInt(versionMatcher.group(1)), - Integer.parseInt(versionMatcher.group(2)), - Integer.parseInt(versionMatcher.group(3)), - versionMatcher.group(4) == null ? false : true); + return new Version(Integer.parseInt(versionMatcher.group(1)), Integer.parseInt(versionMatcher.group(2)), + Integer.parseInt(versionMatcher.group(3)), versionMatcher.group(4) == null ? false : true); } @JsonValue @@ -71,11 +71,10 @@ public String toString() { @Override public int compareTo(Version other) { - return this.major != other.major ? this.major - other.major : - this.minor != other.minor ? this.minor - other.minor : - this.patch != other.patch ? this.patch - other.patch : - this.isEarlyAccess == other.isEarlyAccess() ? 0 : - this.isEarlyAccess ? -1 : 1; + return this.major != other.major ? this.major - other.major + : this.minor != other.minor ? this.minor - other.minor + : this.patch != other.patch ? this.patch - other.patch + : this.isEarlyAccess == other.isEarlyAccess() ? 0 : this.isEarlyAccess ? -1 : 1; } @Override diff --git a/src/main/java/seedu/address/commons/events/BaseEvent.java b/src/main/java/seedu/flexitrack/commons/events/BaseEvent.java similarity index 55% rename from src/main/java/seedu/address/commons/events/BaseEvent.java rename to src/main/java/seedu/flexitrack/commons/events/BaseEvent.java index 723a9c69fbd5..2eb8d80720e5 100644 --- a/src/main/java/seedu/address/commons/events/BaseEvent.java +++ b/src/main/java/seedu/flexitrack/commons/events/BaseEvent.java @@ -1,12 +1,13 @@ -package seedu.address.commons.events; +package seedu.flexitrack.commons.events; public abstract class BaseEvent { /** - * All Events should have a clear unambiguous custom toString message so that feedback message creation - * stays consistent and reusable. + * All Events should have a clear unambiguous custom toString message so + * that feedback message creation stays consistent and reusable. * - * For example the event manager post method will call any posted event's toString and print it in the console. + * For example the event manager post method will call any posted event's + * toString and print it in the console. */ public abstract String toString(); diff --git a/src/main/java/seedu/flexitrack/commons/events/model/FlexiTrackChangedEvent.java b/src/main/java/seedu/flexitrack/commons/events/model/FlexiTrackChangedEvent.java new file mode 100644 index 000000000000..74b67bfb4543 --- /dev/null +++ b/src/main/java/seedu/flexitrack/commons/events/model/FlexiTrackChangedEvent.java @@ -0,0 +1,19 @@ +package seedu.flexitrack.commons.events.model; + +import seedu.flexitrack.commons.events.BaseEvent; +import seedu.flexitrack.model.ReadOnlyFlexiTrack; + +/** Indicates the FlexiTrack in the model has changed */ +public class FlexiTrackChangedEvent extends BaseEvent { + + public final ReadOnlyFlexiTrack data; + + public FlexiTrackChangedEvent(ReadOnlyFlexiTrack 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/flexitrack/commons/events/storage/DataSavingExceptionEvent.java similarity index 70% rename from src/main/java/seedu/address/commons/events/storage/DataSavingExceptionEvent.java rename to src/main/java/seedu/flexitrack/commons/events/storage/DataSavingExceptionEvent.java index f0a0640ee523..f91c6dfb09a9 100644 --- a/src/main/java/seedu/address/commons/events/storage/DataSavingExceptionEvent.java +++ b/src/main/java/seedu/flexitrack/commons/events/storage/DataSavingExceptionEvent.java @@ -1,6 +1,6 @@ -package seedu.address.commons.events.storage; +package seedu.flexitrack.commons.events.storage; -import seedu.address.commons.events.BaseEvent; +import seedu.flexitrack.commons.events.BaseEvent; /** * Indicates an exception during a file saving @@ -14,7 +14,7 @@ public DataSavingExceptionEvent(Exception exception) { } @Override - public String toString(){ + public String toString() { return exception.toString(); } diff --git a/src/main/java/seedu/address/commons/events/ui/ExitAppRequestEvent.java b/src/main/java/seedu/flexitrack/commons/events/ui/ExitAppRequestEvent.java similarity index 68% rename from src/main/java/seedu/address/commons/events/ui/ExitAppRequestEvent.java rename to src/main/java/seedu/flexitrack/commons/events/ui/ExitAppRequestEvent.java index 9af6194543a3..2b12c4e787e5 100644 --- a/src/main/java/seedu/address/commons/events/ui/ExitAppRequestEvent.java +++ b/src/main/java/seedu/flexitrack/commons/events/ui/ExitAppRequestEvent.java @@ -1,6 +1,6 @@ -package seedu.address.commons.events.ui; +package seedu.flexitrack.commons.events.ui; -import seedu.address.commons.events.BaseEvent; +import seedu.flexitrack.commons.events.BaseEvent; /** * Indicates a request for App termination diff --git a/src/main/java/seedu/address/commons/events/ui/IncorrectCommandAttemptedEvent.java b/src/main/java/seedu/flexitrack/commons/events/ui/IncorrectCommandAttemptedEvent.java similarity index 52% rename from src/main/java/seedu/address/commons/events/ui/IncorrectCommandAttemptedEvent.java rename to src/main/java/seedu/flexitrack/commons/events/ui/IncorrectCommandAttemptedEvent.java index 991f7ae9fa25..bdc3a7207924 100644 --- a/src/main/java/seedu/address/commons/events/ui/IncorrectCommandAttemptedEvent.java +++ b/src/main/java/seedu/flexitrack/commons/events/ui/IncorrectCommandAttemptedEvent.java @@ -1,14 +1,15 @@ -package seedu.address.commons.events.ui; +package seedu.flexitrack.commons.events.ui; -import seedu.address.commons.events.BaseEvent; -import seedu.address.logic.commands.Command; +import seedu.flexitrack.commons.events.BaseEvent; +import seedu.flexitrack.logic.commands.Command; /** * Indicates an attempt to execute an incorrect command */ public class IncorrectCommandAttemptedEvent extends BaseEvent { - public IncorrectCommandAttemptedEvent(Command command) {} + public IncorrectCommandAttemptedEvent(Command command) { + } @Override public String toString() { diff --git a/src/main/java/seedu/address/commons/events/ui/JumpToListRequestEvent.java b/src/main/java/seedu/flexitrack/commons/events/ui/JumpToListRequestEvent.java similarity index 67% rename from src/main/java/seedu/address/commons/events/ui/JumpToListRequestEvent.java rename to src/main/java/seedu/flexitrack/commons/events/ui/JumpToListRequestEvent.java index 0580d27aecf5..76ed0cba4d72 100644 --- a/src/main/java/seedu/address/commons/events/ui/JumpToListRequestEvent.java +++ b/src/main/java/seedu/flexitrack/commons/events/ui/JumpToListRequestEvent.java @@ -1,9 +1,9 @@ -package seedu.address.commons.events.ui; +package seedu.flexitrack.commons.events.ui; -import seedu.address.commons.events.BaseEvent; +import seedu.flexitrack.commons.events.BaseEvent; /** - * Indicates a request to jump to the list of persons + * Indicates a request to jump to the list of tasks */ public class JumpToListRequestEvent extends BaseEvent { diff --git a/src/main/java/seedu/address/commons/events/ui/ShowHelpRequestEvent.java b/src/main/java/seedu/flexitrack/commons/events/ui/ShowHelpRequestEvent.java similarity index 69% rename from src/main/java/seedu/address/commons/events/ui/ShowHelpRequestEvent.java rename to src/main/java/seedu/flexitrack/commons/events/ui/ShowHelpRequestEvent.java index a7e40940b2c7..dce72677527c 100644 --- a/src/main/java/seedu/address/commons/events/ui/ShowHelpRequestEvent.java +++ b/src/main/java/seedu/flexitrack/commons/events/ui/ShowHelpRequestEvent.java @@ -1,6 +1,6 @@ -package seedu.address.commons.events.ui; +package seedu.flexitrack.commons.events.ui; -import seedu.address.commons.events.BaseEvent; +import seedu.flexitrack.commons.events.BaseEvent; /** * An event requesting to view the help page. diff --git a/src/main/java/seedu/flexitrack/commons/events/ui/StoragePathChangeEvent.java b/src/main/java/seedu/flexitrack/commons/events/ui/StoragePathChangeEvent.java new file mode 100644 index 000000000000..681d969da086 --- /dev/null +++ b/src/main/java/seedu/flexitrack/commons/events/ui/StoragePathChangeEvent.java @@ -0,0 +1,17 @@ +package seedu.flexitrack.commons.events.ui; + +import seedu.flexitrack.commons.events.BaseEvent; + +//@@author A0138455Y +public class StoragePathChangeEvent extends BaseEvent { + private final String newPath; + + public StoragePathChangeEvent(String newPath) { + this.newPath = newPath; + } + + @Override + public String toString() { + return this.newPath; + } +} diff --git a/src/main/java/seedu/address/commons/exceptions/DataConversionException.java b/src/main/java/seedu/flexitrack/commons/exceptions/DataConversionException.java similarity index 83% rename from src/main/java/seedu/address/commons/exceptions/DataConversionException.java rename to src/main/java/seedu/flexitrack/commons/exceptions/DataConversionException.java index 1f689bd8e3f9..33c34f2f7907 100644 --- a/src/main/java/seedu/address/commons/exceptions/DataConversionException.java +++ b/src/main/java/seedu/flexitrack/commons/exceptions/DataConversionException.java @@ -1,9 +1,10 @@ -package seedu.address.commons.exceptions; +package seedu.flexitrack.commons.exceptions; /** * Represents an error during conversion of data from one format to another */ public class DataConversionException extends Exception { + public DataConversionException(Exception cause) { super(cause); } diff --git a/src/main/java/seedu/address/commons/exceptions/DuplicateDataException.java b/src/main/java/seedu/flexitrack/commons/exceptions/DuplicateDataException.java similarity index 84% rename from src/main/java/seedu/address/commons/exceptions/DuplicateDataException.java rename to src/main/java/seedu/flexitrack/commons/exceptions/DuplicateDataException.java index 17aa63d5020c..c49229790adf 100644 --- a/src/main/java/seedu/address/commons/exceptions/DuplicateDataException.java +++ b/src/main/java/seedu/flexitrack/commons/exceptions/DuplicateDataException.java @@ -1,4 +1,4 @@ -package seedu.address.commons.exceptions; +package seedu.flexitrack.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/flexitrack/commons/exceptions/IllegalValueException.java similarity index 57% rename from src/main/java/seedu/address/commons/exceptions/IllegalValueException.java rename to src/main/java/seedu/flexitrack/commons/exceptions/IllegalValueException.java index a473b43bd86f..cc3de4b7f6ff 100644 --- a/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java +++ b/src/main/java/seedu/flexitrack/commons/exceptions/IllegalValueException.java @@ -1,11 +1,13 @@ -package seedu.address.commons.exceptions; +package seedu.flexitrack.commons.exceptions; /** * Signals that some given data does not fulfill some constraints. */ public class IllegalValueException extends Exception { /** - * @param message should contain relevant information on the failed constraint(s) + * @param message + * should contain relevant information on the failed + * constraint(s) */ public IllegalValueException(String message) { super(message); diff --git a/src/main/java/seedu/address/commons/util/AppUtil.java b/src/main/java/seedu/flexitrack/commons/util/AppUtil.java similarity index 79% rename from src/main/java/seedu/address/commons/util/AppUtil.java rename to src/main/java/seedu/flexitrack/commons/util/AppUtil.java index 649cc19aaeda..c33c59c7310c 100644 --- a/src/main/java/seedu/address/commons/util/AppUtil.java +++ b/src/main/java/seedu/flexitrack/commons/util/AppUtil.java @@ -1,7 +1,7 @@ -package seedu.address.commons.util; +package seedu.flexitrack.commons.util; import javafx.scene.image.Image; -import seedu.address.MainApp; +import seedu.flexitrack.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/flexitrack/commons/util/CollectionUtil.java similarity index 86% rename from src/main/java/seedu/address/commons/util/CollectionUtil.java rename to src/main/java/seedu/flexitrack/commons/util/CollectionUtil.java index fde8394f31e5..e18892b7c627 100644 --- a/src/main/java/seedu/address/commons/util/CollectionUtil.java +++ b/src/main/java/seedu/flexitrack/commons/util/CollectionUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.flexitrack.commons.util; import java.util.Collection; import java.util.HashSet; @@ -21,8 +21,6 @@ public static boolean isAnyNull(Object... items) { return false; } - - /** * Throws an assertion error if the collection or any item in it is null. */ @@ -32,12 +30,14 @@ public static void assertNoNullElements(Collection items) { } /** - * Returns true if every element in a collection are unique by {@link Object#equals(Object)}. + * Returns true if every element in a collection are unique by + * {@link Object#equals(Object)}. */ public static boolean elementsAreUnique(Collection items) { final Set testSet = new HashSet<>(); for (Object item : items) { - final boolean itemAlreadyExists = !testSet.add(item); // see Set documentation + final boolean itemAlreadyExists = !testSet.add(item); // see Set + // documentation if (itemAlreadyExists) { return false; } diff --git a/src/main/java/seedu/address/commons/util/ConfigUtil.java b/src/main/java/seedu/flexitrack/commons/util/ConfigUtil.java similarity index 56% rename from src/main/java/seedu/address/commons/util/ConfigUtil.java rename to src/main/java/seedu/flexitrack/commons/util/ConfigUtil.java index af42e03df06c..fd43cd320c81 100644 --- a/src/main/java/seedu/address/commons/util/ConfigUtil.java +++ b/src/main/java/seedu/flexitrack/commons/util/ConfigUtil.java @@ -1,14 +1,14 @@ -package seedu.address.commons.util; - -import seedu.address.commons.core.Config; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.exceptions.DataConversionException; +package seedu.flexitrack.commons.util; import java.io.File; import java.io.IOException; import java.util.Optional; import java.util.logging.Logger; +import seedu.flexitrack.commons.core.Config; +import seedu.flexitrack.commons.core.LogsCenter; +import seedu.flexitrack.commons.exceptions.DataConversionException; + /** * A class for accessing the Config File. */ @@ -17,10 +17,14 @@ public class ConfigUtil { private static final Logger logger = LogsCenter.getLogger(ConfigUtil.class); /** - * Returns the Config object from the given file or {@code Optional.empty()} object if the file is not found. - * If any values are missing from the file, default values will be used, as long as the file is a valid json file. - * @param configFilePath cannot be null. - * @throws DataConversionException if the file format is not as expected. + * Returns the Config object from the given file or {@code Optional.empty()} + * object if the file is not found. If any values are missing from the file, + * default values will be used, as long as the file is a valid json file. + * + * @param configFilePath + * cannot be null. + * @throws DataConversionException + * if the file format is not as expected. */ public static Optional readConfig(String configFilePath) throws DataConversionException { @@ -29,7 +33,7 @@ public static Optional readConfig(String configFilePath) throws DataConv File configFile = new File(configFilePath); if (!configFile.exists()) { - logger.info("Config file " + configFile + " not found"); + logger.info("Config file " + configFile + " not found"); return Optional.empty(); } @@ -46,11 +50,15 @@ public static Optional readConfig(String configFilePath) throws DataConv } /** - * Saves the Config object to the specified file. - * Overwrites existing file if it exists, creates a new file if it doesn't. - * @param config cannot be null - * @param configFilePath cannot be null - * @throws IOException if there was an error during writing to the file + * Saves the Config object to the specified file. Overwrites existing file + * if it exists, creates a new file if it doesn't. + * + * @param config + * cannot be null + * @param configFilePath + * cannot be null + * @throws IOException + * if there was an error during writing to the file */ public static void saveConfig(Config config, String configFilePath) throws IOException { assert config != null; diff --git a/src/main/java/seedu/address/commons/util/FileUtil.java b/src/main/java/seedu/flexitrack/commons/util/FileUtil.java similarity index 83% rename from src/main/java/seedu/address/commons/util/FileUtil.java rename to src/main/java/seedu/flexitrack/commons/util/FileUtil.java index ca8221250de4..e8123412fa93 100644 --- a/src/main/java/seedu/address/commons/util/FileUtil.java +++ b/src/main/java/seedu/flexitrack/commons/util/FileUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.flexitrack.commons.util; import java.io.File; import java.io.IOException; @@ -21,7 +21,8 @@ public static void createIfMissing(File file) throws IOException { } /** - * Creates a file if it does not exist along with its missing parent directories + * Creates a file if it does not exist along with its missing parent + * directories * * @return true if file is created, false if file already exists */ @@ -38,8 +39,10 @@ public static boolean createFile(File file) throws IOException { /** * Creates the given directory along with its parent directories * - * @param dir the directory to be created; assumed not null - * @throws IOException if the directory or a parent directory cannot be created + * @param dir + * the directory to be created; assumed not null + * @throws IOException + * if the directory or a parent directory cannot be created */ public static void createDirs(File dir) throws IOException { if (!dir.exists() && !dir.mkdirs()) { @@ -66,8 +69,8 @@ public static String readFromFile(File file) throws IOException { } /** - * Writes given string to a file. - * Will create the file if it does not exist yet. + * Writes given string to a file. Will create the file if it does not exist + * yet. */ public static void writeToFile(File file, String content) throws IOException { Files.write(file.toPath(), content.getBytes(CHARSET)); @@ -75,8 +78,12 @@ public static void writeToFile(File file, String content) throws IOException { /** * Converts a string to a platform-specific file path - * @param pathWithForwardSlash A String representing a file path but using '/' as the separator - * @return {@code pathWithForwardSlash} but '/' replaced with {@code File.separator} + * + * @param pathWithForwardSlash + * A String representing a file path but using '/' as the + * separator + * @return {@code pathWithForwardSlash} but '/' replaced with + * {@code File.separator} */ public static String getPath(String pathWithForwardSlash) { assert pathWithForwardSlash != null; diff --git a/src/main/java/seedu/address/commons/util/FxViewUtil.java b/src/main/java/seedu/flexitrack/commons/util/FxViewUtil.java similarity index 92% rename from src/main/java/seedu/address/commons/util/FxViewUtil.java rename to src/main/java/seedu/flexitrack/commons/util/FxViewUtil.java index 900efa6bf5c3..8007bafa7c12 100644 --- a/src/main/java/seedu/address/commons/util/FxViewUtil.java +++ b/src/main/java/seedu/flexitrack/commons/util/FxViewUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.flexitrack.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/flexitrack/commons/util/JsonUtil.java similarity index 85% rename from src/main/java/seedu/address/commons/util/JsonUtil.java rename to src/main/java/seedu/flexitrack/commons/util/JsonUtil.java index 80b67de5b7e8..c88d9821acc8 100644 --- a/src/main/java/seedu/address/commons/util/JsonUtil.java +++ b/src/main/java/seedu/flexitrack/commons/util/JsonUtil.java @@ -1,4 +1,7 @@ -package seedu.address.commons.util; +package seedu.flexitrack.commons.util; + +import java.io.IOException; +import java.util.logging.Level; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; @@ -11,9 +14,6 @@ import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; -import java.io.IOException; -import java.util.logging.Level; - /** * Converts a Java object instance to JSON and vice versa */ @@ -52,13 +52,15 @@ public Class handledType() { .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()) + .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 + * Converts a given string representation of a JSON data to instance of a + * class + * + * @param + * The generic type to create an instance of * @return The instance of T with the specified values in the JSON string */ public static T fromJsonString(String json, Class instanceClass) throws IOException { @@ -66,9 +68,13 @@ public static T fromJsonString(String json, Class instanceClass) throws I } /** - * Converts a given instance of a class into its JSON data string representation - * @param instance The T object to be converted into the JSON string - * @param The generic type to create an instance of + * Converts a given instance of a class into its JSON data string + * representation + * + * @param instance + * The T object to be converted into the JSON string + * @param + * The generic type to create an instance of * @return JSON data representation of the given class instance, in string */ public static String toJsonString(T instance) throws JsonProcessingException { diff --git a/src/main/java/seedu/address/commons/util/StringUtil.java b/src/main/java/seedu/flexitrack/commons/util/StringUtil.java similarity index 56% rename from src/main/java/seedu/address/commons/util/StringUtil.java rename to src/main/java/seedu/flexitrack/commons/util/StringUtil.java index 2e94740456a6..f09e43dbb1e0 100644 --- a/src/main/java/seedu/address/commons/util/StringUtil.java +++ b/src/main/java/seedu/flexitrack/commons/util/StringUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.flexitrack.commons.util; import java.io.PrintWriter; import java.io.StringWriter; @@ -9,16 +9,25 @@ * Helper functions for handling strings. */ public class StringUtil { + //@@author A0147092E + public static boolean equalsIgnoreCase(String source, String query) { + String parsedQuery = query.replace("f/", "").trim(); + List strings = Arrays.asList(source.toLowerCase()); + return strings.stream().filter(s -> s.contains(parsedQuery.toLowerCase())).count() > 0; + } + //@@author + public static boolean containsIgnoreCase(String source, String query) { String[] split = source.toLowerCase().split("\\s+"); List strings = Arrays.asList(split); return strings.stream().filter(s -> s.equals(query.toLowerCase())).count() > 0; } - + + /** * Returns a detailed message of the t, including the stack trace. */ - public static String getDetails(Throwable t){ + public static String getDetails(Throwable t) { assert t != null; StringWriter sw = new StringWriter(); t.printStackTrace(new PrintWriter(sw)); @@ -27,10 +36,13 @@ public static String getDetails(Throwable t){ /** * Returns true if s represents an unsigned integer e.g. 1, 2, 3, ...
- * Will return false for null, empty string, "-1", "0", "+1", and " 2 " (untrimmed) "3 0" (contains whitespace). - * @param s Should be trimmed. + * Will return false for null, empty string, "-1", "0", "+1", and " 2 " + * (untrimmed) "3 0" (contains whitespace). + * + * @param s + * Should be trimmed. */ - public static boolean isUnsignedInteger(String s){ + public static boolean isUnsignedInteger(String s) { return s != null && s.matches("^0*[1-9]\\d*$"); } } diff --git a/src/main/java/seedu/address/commons/util/UrlUtil.java b/src/main/java/seedu/flexitrack/commons/util/UrlUtil.java similarity index 81% rename from src/main/java/seedu/address/commons/util/UrlUtil.java rename to src/main/java/seedu/flexitrack/commons/util/UrlUtil.java index 6bbab52b9840..b7f58d3eed6c 100644 --- a/src/main/java/seedu/address/commons/util/UrlUtil.java +++ b/src/main/java/seedu/flexitrack/commons/util/UrlUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.flexitrack.commons.util; import java.net.URL; @@ -18,7 +18,7 @@ public static boolean compareBaseUrls(URL url1, URL url2) { return url1.getHost().toLowerCase().replaceFirst("www.", "") .equals(url2.getHost().replaceFirst("www.", "").toLowerCase()) && url1.getPath().replaceAll("/", "").toLowerCase() - .equals(url2.getPath().replaceAll("/", "").toLowerCase()); + .equals(url2.getPath().replaceAll("/", "").toLowerCase()); } } diff --git a/src/main/java/seedu/address/commons/util/XmlUtil.java b/src/main/java/seedu/flexitrack/commons/util/XmlUtil.java similarity index 62% rename from src/main/java/seedu/address/commons/util/XmlUtil.java rename to src/main/java/seedu/flexitrack/commons/util/XmlUtil.java index 2087e7628a1d..55393b064653 100644 --- a/src/main/java/seedu/address/commons/util/XmlUtil.java +++ b/src/main/java/seedu/flexitrack/commons/util/XmlUtil.java @@ -1,11 +1,12 @@ -package seedu.address.commons.util; +package seedu.flexitrack.commons.util; + +import java.io.File; +import java.io.FileNotFoundException; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; -import java.io.File; -import java.io.FileNotFoundException; /** * Helps with reading from and writing to XML files. @@ -15,12 +16,16 @@ public class XmlUtil { /** * Returns the xml data in the file as an object of the specified type. * - * @param file Points to a valid xml file containing data that match the {@code classToConvert}. - * Cannot be null. - * @param classToConvert The class corresponding to the xml data. - * Cannot be null. - * @throws FileNotFoundException Thrown if the file is missing. - * @throws JAXBException Thrown if the file is empty or does not have the correct format. + * @param file + * Points to a valid xml file containing data that match the + * {@code classToConvert}. Cannot be null. + * @param classToConvert + * The class corresponding to the xml data. Cannot be null. + * @throws FileNotFoundException + * Thrown if the file is missing. + * @throws JAXBException + * Thrown if the file is empty or does not have the correct + * format. */ @SuppressWarnings("unchecked") public static T getDataFromFile(File file, Class classToConvert) @@ -42,11 +47,14 @@ public static T getDataFromFile(File file, Class classToConvert) /** * Saves the data in the file in xml format. * - * @param file Points to a valid xml file containing data that match the {@code classToConvert}. - * Cannot be null. - * @throws FileNotFoundException Thrown if the file is missing. - * @throws JAXBException Thrown if there is an error during converting the data - * into xml and writing to the file. + * @param file + * Points to a valid xml file containing data that match the + * {@code classToConvert}. Cannot be null. + * @throws FileNotFoundException + * Thrown if the file is missing. + * @throws JAXBException + * Thrown if there is an error during converting the data into + * xml and writing to the file. */ public static void saveDataToFile(File file, T data) throws FileNotFoundException, JAXBException { diff --git a/src/main/java/seedu/address/logic/Logic.java b/src/main/java/seedu/flexitrack/logic/Logic.java similarity index 53% rename from src/main/java/seedu/address/logic/Logic.java rename to src/main/java/seedu/flexitrack/logic/Logic.java index 4df1bc65cabb..4e8d85f07ae9 100644 --- a/src/main/java/seedu/address/logic/Logic.java +++ b/src/main/java/seedu/flexitrack/logic/Logic.java @@ -1,8 +1,8 @@ -package seedu.address.logic; +package seedu.flexitrack.logic; import javafx.collections.ObservableList; -import seedu.address.logic.commands.CommandResult; -import seedu.address.model.person.ReadOnlyPerson; +import seedu.flexitrack.logic.commands.CommandResult; +import seedu.flexitrack.model.task.ReadOnlyTask; /** * API of the Logic component @@ -10,12 +10,14 @@ public interface Logic { /** * Executes the command and returns the result. - * @param commandText The command as entered by the user. + * + * @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(); + ObservableList getFilteredTaskList(); } diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/seedu/flexitrack/logic/LogicManager.java similarity index 56% rename from src/main/java/seedu/address/logic/LogicManager.java rename to src/main/java/seedu/flexitrack/logic/LogicManager.java index ce4dc1903cff..1a106d03191a 100644 --- a/src/main/java/seedu/address/logic/LogicManager.java +++ b/src/main/java/seedu/flexitrack/logic/LogicManager.java @@ -1,17 +1,17 @@ -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; +package seedu.flexitrack.logic; import java.util.logging.Logger; +import javafx.collections.ObservableList; +import seedu.flexitrack.commons.core.ComponentManager; +import seedu.flexitrack.commons.core.LogsCenter; +import seedu.flexitrack.logic.commands.Command; +import seedu.flexitrack.logic.commands.CommandResult; +import seedu.flexitrack.logic.parser.Parser; +import seedu.flexitrack.model.Model; +import seedu.flexitrack.model.task.ReadOnlyTask; +import seedu.flexitrack.storage.Storage; + /** * The main LogicManager of the app. */ @@ -23,7 +23,7 @@ public class LogicManager extends ComponentManager implements Logic { public LogicManager(Model model, Storage storage) { this.model = model; - this.parser = new Parser(); + this.parser = new Parser(model); } @Override @@ -35,7 +35,8 @@ public CommandResult execute(String commandText) { } @Override - public ObservableList getFilteredPersonList() { - return model.getFilteredPersonList(); + public ObservableList getFilteredTaskList() { + return model.getFilteredTaskList(); } + } diff --git a/src/main/java/seedu/flexitrack/logic/commands/AddCommand.java b/src/main/java/seedu/flexitrack/logic/commands/AddCommand.java new file mode 100644 index 000000000000..60a11a43b7f2 --- /dev/null +++ b/src/main/java/seedu/flexitrack/logic/commands/AddCommand.java @@ -0,0 +1,116 @@ +package seedu.flexitrack.logic.commands; + +import seedu.flexitrack.commons.exceptions.IllegalValueException; +import seedu.flexitrack.model.task.DateTimeInfo; +import seedu.flexitrack.model.task.Name; +import seedu.flexitrack.model.task.Task; +import seedu.flexitrack.model.task.UniqueTaskList.DuplicateTaskException; +import seedu.flexitrack.model.task.UniqueTaskList.TaskNotFoundException; + +/** + * Adds a task to the FlexiTrack. + */ +public class AddCommand extends Command { + + public static final String COMMAND_WORD = "add"; + public static final String COMMAND_SHORTCUT = "a"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ", Shortcut [" + COMMAND_SHORTCUT + "]" + + ": Adds a task to the FlexiTrack.\n" + + "1. Add Floating Task - Parameters to add an Floating Task: [task title]\n" + "\tExample: " + COMMAND_WORD + + " Do CS homework\n" + + "2. Add Event - Parameters to add an event: [task title] from/ [starting time] to/ [ending time]\n" + + "\tExample: " + COMMAND_WORD + " Summer school from/ 1st June to/ 31st July \n" + + "3. Add Task - Parameters to add a task: [task title] by/ [due date]\n" + "\tExample: " + COMMAND_WORD + + " CS tutorial by/ Saturday 10am \n" + + "4. Add Recursive Task - Parameters: [task title] fr/[no. of times to recurse] ty/[daily or weekly or monthly] by/[due date]\n" + + "\tExample: " + COMMAND_WORD + " Complete PC1222 homework fr/5 ty/weekly by/ Friday 2359\n" + + "5. Add Recursive Event - Parameters: [event title] fr/[no. of times to recurse] ty/[daily or weekly or monthly] from/[start time] to/[ending Time] \n" + + "\tExample: " + COMMAND_WORD + " Attend PC1222 Lecture fr/10 ty/weekly from/Tue 12pm to/Tue 2pm \n"; + + public static final String MESSAGE_SUCCESS = "New task added: %1$s"; + public static final String MESSAGE_DUPLICATE_TASK = "This task already exists in the FlexiTrack"; + public static final String MESSAGE_OVERLAPPING_EVENT_WARNING = "\nWarning: this event is overlaping a existing event!"; + private static final String MESSAGE_UNDO_SUCCESS = "Undid add: %1$s"; + + private Task toAdd; + private boolean isOverlapping = false; + int numOfOccurrrence = 0; + + /** + * Convenience constructor using raw values. + * + * @throws IllegalValueException + * if any of the raw values are invalid + */ + public AddCommand(String name, String dueDate, String startTime, String endTime) throws IllegalValueException { + this.toAdd = new Task(new Name(name), new DateTimeInfo(dueDate), new DateTimeInfo(startTime), + new DateTimeInfo(endTime)); + } + + public AddCommand() { + this.toAdd = null; + } + + public AddCommand(String name, String dueDate, String startTime, String endTime, int numOfOccurrrence) throws IllegalValueException { + this.toAdd = new Task(new Name(name), new DateTimeInfo(dueDate), new DateTimeInfo(startTime), + new DateTimeInfo(endTime)); + this.numOfOccurrrence = numOfOccurrrence; + } + + @Override + public CommandResult execute() { + assert model != null; + try { + + if (model.checkBlock(toAdd)) { + return new CommandResult(BlockCommand.MESSAGE_DUPLICATE_TIME); + } + this.isOverlapping = model.checkOverlapEvent(toAdd); + + model.addTask(toAdd); + toAdd = toAdd.copy(); + recordCommand(this); + + if (toAdd.getIsEvent()) { + return new CommandResult((String.format(MESSAGE_SUCCESS, toAdd)) + "\n" + + DateTimeInfo.durationOfTheEvent(toAdd.getStartTime().toString(), + toAdd.getEndTime().toString()) + + (isOverlapping ? MESSAGE_OVERLAPPING_EVENT_WARNING : "")); + } else { + return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); + } + } catch (DuplicateTaskException e) { + return new CommandResult(MESSAGE_DUPLICATE_TASK); + } + + } + + // @@author A0127686R + @Override + public void executeUndo() { + Task toDelete = toAdd; + + try { + model.deleteTask(toDelete); + } catch (TaskNotFoundException pnfe) { + assert false : "The target task cannot be missing"; + } + } + + //@@author A0127855W + @Override + public String getUndoMessage(){ + return String.format(MESSAGE_UNDO_SUCCESS, toAdd); + } + + @Override + int getNumOfOccurrrence() { + return numOfOccurrrence; + } + + @Override + void setNumOfOccurrrence(int numOfOccurrrence) { + this.numOfOccurrrence = numOfOccurrrence; + } +} diff --git a/src/main/java/seedu/flexitrack/logic/commands/BlockCommand.java b/src/main/java/seedu/flexitrack/logic/commands/BlockCommand.java new file mode 100644 index 000000000000..af49e64e854e --- /dev/null +++ b/src/main/java/seedu/flexitrack/logic/commands/BlockCommand.java @@ -0,0 +1,89 @@ +package seedu.flexitrack.logic.commands; +import java.util.Set; + +import seedu.flexitrack.commons.exceptions.IllegalValueException; +import seedu.flexitrack.model.task.DateTimeInfo; +import seedu.flexitrack.model.task.Name; +import seedu.flexitrack.model.task.Task; +import seedu.flexitrack.model.task.UniqueTaskList.DuplicateTaskException; +import seedu.flexitrack.model.task.UniqueTaskList.TaskNotFoundException; + +//@@author A0138455Y +/** + * Adds a block event period to the FlexiTrack. + */ +public class BlockCommand extends Command { +public static final String COMMAND_WORD = "block"; + + public static final String COMMAND_SHORTCUT = "b"; + + + public static final String MESSAGE_USAGE = COMMAND_WORD + ", Shortcut [" + COMMAND_SHORTCUT + "]" + ": Block a period of time in the FlexiTrack.\n" + + "Parameters to block a period of time: [Description] from/ [starting time] to/ [ending time]\n" + + "\tExample: " + COMMAND_WORD + " reserve for study from/ 1st June 5pm to/ 1st July 7pm\n"; + + public static final String MESSAGE_SUCCESS = "Block the date for %1$s"; + public static final String MESSAGE_DUPLICATE_TIME = "This period of time has already taken by other event, Please choose another time."; + public static final String MESSAGE_OVERLAPPING_EVENT_WARNING = "\nWarning: this event is overlaping a existing event!"; + private static final String MESSAGE_UNDO_SUCCESS = "Undid block: %1$s"; + + private Task toBlock; + private boolean isOverlapping = false; + + /** + * Convenience constructor using raw values. + * + * @throws IllegalValueException + * if any of the raw values are invalid + */ + public BlockCommand(String name, String dueDate, String startTime, String endTime) + throws IllegalValueException { + this.toBlock = new Task(new Name(name), new DateTimeInfo(dueDate), new DateTimeInfo(startTime), + new DateTimeInfo(endTime)); + } + + public BlockCommand() { + this.toBlock = null; + } + + @Override + public CommandResult execute() { + assert model != null; + try { + if(model.checkBlock(toBlock)) { + return new CommandResult(BlockCommand.MESSAGE_DUPLICATE_TIME); + } + this.isOverlapping = model.checkOverlapEvent(toBlock); + + model.addTask(toBlock); + toBlock = toBlock.copy(); + recordCommand(this); + if (toBlock.getIsEvent()) { + return new CommandResult((String.format(MESSAGE_SUCCESS, toBlock)) + "\n" + DateTimeInfo + .durationOfTheEvent(toBlock.getStartTime().toString(), toBlock.getEndTime().toString()) + + (isOverlapping? MESSAGE_OVERLAPPING_EVENT_WARNING : "")); + } else { + return new CommandResult(String.format(MESSAGE_SUCCESS, toBlock)); + } + } catch (DuplicateTaskException e) { + return new CommandResult(MESSAGE_DUPLICATE_TIME); + } + + } + @Override + public void executeUndo() { + Task toDelete = toBlock; + try { + model.deleteTask(toDelete); + } catch (TaskNotFoundException pnfe) { + assert false : "The target task cannot be missing"; + } + } + + //@@author A0127855W + @Override + public String getUndoMessage(){ + return String.format(MESSAGE_UNDO_SUCCESS, toBlock); + } + +} diff --git a/src/main/java/seedu/flexitrack/logic/commands/ChangeStoragePathCommand.java b/src/main/java/seedu/flexitrack/logic/commands/ChangeStoragePathCommand.java new file mode 100644 index 000000000000..3d50e2a0bf26 --- /dev/null +++ b/src/main/java/seedu/flexitrack/logic/commands/ChangeStoragePathCommand.java @@ -0,0 +1,38 @@ +package seedu.flexitrack.logic.commands; + +import seedu.flexitrack.commons.exceptions.IllegalValueException; + +//@@author A0138455Y +/** + * + * Change current storage to other place + * Limitation : only allow user to change storage within FlexiTrack folder + */ +public class ChangeStoragePathCommand extends Command{ + + public static final String COMMAND_WORD = "cs"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Change the storage directory of FlexiTrack.\n" + + "Parameters: DIRECTORY (input can only contains alphanumric, forward slash '/' and underscore '_')\n" + + "Example: " + COMMAND_WORD + + " /Documents/newFolder/tasktracker"; + + public static final String MESSAGE_SUCCESS = "Storage location changed: %1$s"; + public static final String INVALID_VALUE = "Invalid path input! Please enter a valid path!"; + + final String storagePath; + + /** + * taking new storage path + * @throws IllegalValueException if any of the raw values are invalid + */ + public ChangeStoragePathCommand(String newPath) { + this.storagePath = newPath; + } + + @Override + public CommandResult execute() { + model.changeStorage(storagePath); + return new CommandResult(String.format(MESSAGE_SUCCESS, this.storagePath)); + } +} diff --git a/src/main/java/seedu/flexitrack/logic/commands/ClearCommand.java b/src/main/java/seedu/flexitrack/logic/commands/ClearCommand.java new file mode 100644 index 000000000000..34b02c63d107 --- /dev/null +++ b/src/main/java/seedu/flexitrack/logic/commands/ClearCommand.java @@ -0,0 +1,42 @@ +package seedu.flexitrack.logic.commands; + +import seedu.flexitrack.model.FlexiTrack; +import seedu.flexitrack.model.ReadOnlyFlexiTrack; + +/** + * Clears the FlexiTrack. + */ +public class ClearCommand extends Command { + + public static final String COMMAND_WORD = "clear"; + public static final String COMMAND_SHORTCUT = "c"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ", Shortcut [" + COMMAND_SHORTCUT + "]" + ": Clear the to do lists in FlexiTrack.\n" + "Example: " + + COMMAND_WORD; + public static final String MESSAGE_SUCCESS = "FlexiTrack has been cleared!"; + private static final String MESSAGE_UNDO_SUCCESS = "Undid clear"; + + //TODO: i think only allowed one MODEL + private ReadOnlyFlexiTrack storeDataChange; + + public ClearCommand() { + } + + @Override + public CommandResult execute() { + assert model != null; + storeDataChange = new FlexiTrack(model.getFlexiTrack()); + model.resetData(FlexiTrack.getEmptyFlexiTrack()); + recordCommand(this); + return new CommandResult(MESSAGE_SUCCESS); + } + + public void executeUndo() { + model.resetData(storeDataChange); + } + + //@@author A0127855W + @Override + public String getUndoMessage(){ + return String.format(MESSAGE_UNDO_SUCCESS); + } +} diff --git a/src/main/java/seedu/flexitrack/logic/commands/Command.java b/src/main/java/seedu/flexitrack/logic/commands/Command.java new file mode 100644 index 000000000000..a19992725c49 --- /dev/null +++ b/src/main/java/seedu/flexitrack/logic/commands/Command.java @@ -0,0 +1,79 @@ +package seedu.flexitrack.logic.commands; + +import seedu.flexitrack.commons.core.EventsCenter; +import seedu.flexitrack.commons.core.Messages; +import seedu.flexitrack.commons.events.ui.IncorrectCommandAttemptedEvent; +import seedu.flexitrack.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 tasks. + * + * @param displaySize + * used to generate summary + * @return summary message for tasks displayed + */ + public static String getMessageForTaskListShownSummary(int displaySize) { + return String.format(Messages.MESSAGE_TASK_LISTED_OVERVIEW, displaySize); + } + + /** + * Executes the command and returns the result message. + * + * @return feedback message of the operation result for display + */ + public abstract CommandResult execute(); + + /** + * Executes the reverse command to undo and returns the result message. + * + * @return feedback message of the operation result for display + */ + public void executeUndo(){ + } + + public String getUndoMessage(){ + return ""; + } + + /** + * 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)); + } + + //@@author A0127686R + /** + * Record the command to perform undo + * + * @param command + */ + public void recordCommand(Command command){ + UndoCommand.doneCommandStack.add(command); + } + + int getNumOfOccurrrence(){ + return 0; + } + + void setNumOfOccurrrence(int numOfOccurrrence) { + assert numOfOccurrrence != 0; + } +} diff --git a/src/main/java/seedu/address/logic/commands/CommandResult.java b/src/main/java/seedu/flexitrack/logic/commands/CommandResult.java similarity index 70% rename from src/main/java/seedu/address/logic/commands/CommandResult.java rename to src/main/java/seedu/flexitrack/logic/commands/CommandResult.java index f46f2f31353e..4e3ffb8580d7 100644 --- a/src/main/java/seedu/address/logic/commands/CommandResult.java +++ b/src/main/java/seedu/flexitrack/logic/commands/CommandResult.java @@ -1,4 +1,4 @@ -package seedu.address.logic.commands; +package seedu.flexitrack.logic.commands; /** * Represents the result of a command execution. @@ -11,5 +11,9 @@ public CommandResult(String feedbackToUser) { assert feedbackToUser != null; this.feedbackToUser = feedbackToUser; } + + public String getString(){ + return feedbackToUser; + } } diff --git a/src/main/java/seedu/flexitrack/logic/commands/DeleteCommand.java b/src/main/java/seedu/flexitrack/logic/commands/DeleteCommand.java new file mode 100644 index 000000000000..deb2a8594cc0 --- /dev/null +++ b/src/main/java/seedu/flexitrack/logic/commands/DeleteCommand.java @@ -0,0 +1,78 @@ +package seedu.flexitrack.logic.commands; + +import seedu.flexitrack.commons.core.Messages; +import seedu.flexitrack.commons.core.UnmodifiableObservableList; +import seedu.flexitrack.model.task.ReadOnlyTask; +import seedu.flexitrack.model.task.Task; +import seedu.flexitrack.model.task.UniqueTaskList.DuplicateTaskException; +import seedu.flexitrack.model.task.UniqueTaskList.TaskNotFoundException; + +/** + * Deletes a task identified using it's last displayed index from the + * FlexiTrack. + */ +public class DeleteCommand extends Command { + + public static final String COMMAND_WORD = "delete"; + public static final String COMMAND_SHORTCUT = "d"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ", Shortcut [" + COMMAND_SHORTCUT + "]" + + ": Deletes the task identified by the index number used in the last task listing.\n" + + "Parameters: INDEX (must be a positive integer)\n" + "Example: " + COMMAND_WORD + " 1"; + + public static final String MESSAGE_DELETE_TASK_SUCCESS = "Deleted task: %1$s"; + private static final String MESSAGE_UNDO_SUCCESS = "Undid delete: %1$s"; + + public final int targetIndex; + private ReadOnlyTask taskStore; + + public DeleteCommand(int targetIndex) { + this.targetIndex = targetIndex; + } + + /** + * Constructor for undo command + */ + public DeleteCommand() { + this.targetIndex = 0; + } + + @Override + public CommandResult execute() { + + UnmodifiableObservableList lastShownList = model.getFilteredTaskList(); + + if (lastShownList.size() < targetIndex) { + indicateAttemptToExecuteIncorrectCommand(); + return new CommandResult(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + + try { + taskStore = lastShownList.get(targetIndex - 1); + model.deleteTask(taskStore); + recordCommand(this); + } catch (IndexOutOfBoundsException ioobe) { + return new CommandResult(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } catch (TaskNotFoundException tnfe) { + assert false : "The target task cannot be missing"; + } + return new CommandResult(String.format(MESSAGE_DELETE_TASK_SUCCESS, taskStore)); + } + + @Override + public void executeUndo() { + assert model != null; + Task toAdd = (Task) taskStore; + try { + model.addTask(toAdd); + } catch (DuplicateTaskException e) { + e.printStackTrace(); + } + } + + //@@author A0127855W + @Override + public String getUndoMessage(){ + return String.format(MESSAGE_UNDO_SUCCESS, taskStore); + } +} diff --git a/src/main/java/seedu/flexitrack/logic/commands/EditCommand.java b/src/main/java/seedu/flexitrack/logic/commands/EditCommand.java new file mode 100644 index 000000000000..7831b8070535 --- /dev/null +++ b/src/main/java/seedu/flexitrack/logic/commands/EditCommand.java @@ -0,0 +1,127 @@ +//@@author A0127855W +package seedu.flexitrack.logic.commands; + +import static seedu.flexitrack.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import java.util.HashMap; +import java.util.logging.Logger; + +import seedu.flexitrack.commons.core.EventsCenter; +import seedu.flexitrack.commons.core.LogsCenter; +import seedu.flexitrack.commons.core.Messages; +import seedu.flexitrack.commons.core.UnmodifiableObservableList; +import seedu.flexitrack.commons.exceptions.IllegalValueException; +import seedu.flexitrack.model.task.DateTimeInfo; +import seedu.flexitrack.model.task.ReadOnlyTask; +import seedu.flexitrack.model.task.Task; +import seedu.flexitrack.model.task.UniqueTaskList.DuplicateTaskException; +import seedu.flexitrack.model.task.UniqueTaskList.IllegalEditException; +import seedu.flexitrack.model.task.UniqueTaskList.TaskNotFoundException; + +/** + * Edits a task identified using it's last displayed index from the FlexiTrack. + */ +public class EditCommand extends Command { + + private static final Logger logger = LogsCenter.getLogger(EditCommand.class); + public static final String COMMAND_WORD = "edit"; + public static final String COMMAND_SHORTCUT = "e"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ", Shortcut [" + COMMAND_SHORTCUT + "]" + + ": Edits the specified task attributes of the task identified by the index number used in the last task listing.\n" + + "Parameters to edit an event: [index] (must be a positive integer) from/ [starting time] to/ [ending time]\n" + + "Example: " + COMMAND_WORD + " 1 " + "from/ 01062016 to/ 01/072016\n" + + "Parameters to edit a task: [index] (must be a positive integer) by/ [due date]\n" + "Example: " + + COMMAND_WORD + " 1 " + "by/ 01062016"; + + public static final String MESSAGE_EDIT_TASK_SUCCESS = "Edited: %1$s into %2$s"; + private static final String MESSAGE_UNDO_SUCCESS = "Undid edit: %1$s into %2$s"; + + public static final HashMap EDIT_PARAMETER_PASSING_MASK = new HashMap(); + static { + EDIT_PARAMETER_PASSING_MASK.put("name", 0); + EDIT_PARAMETER_PASSING_MASK.put("dueDate", 1); + EDIT_PARAMETER_PASSING_MASK.put("startTime", 2); + EDIT_PARAMETER_PASSING_MASK.put("endTime", 3); + } + + public final int targetIndex; + public final String[] arguments; + + private Task taskStore; + private Task editedTask; + + public EditCommand(int targetIndex, String[] arguments) { + this.targetIndex = targetIndex; + this.arguments = arguments; + } + + /** + * Constructor for the undo method + */ + public EditCommand() { + this.targetIndex = 0 ; + this.arguments = null; + } + + @Override + public CommandResult execute() { + + UnmodifiableObservableList lastShownList = model.getFilteredTaskList(); + + String duration = null; + + try { + taskStore = lastShownList.get(targetIndex - 1).copy(); + editedTask = model.editTask(lastShownList.get(targetIndex - 1), arguments); + editedTask = editedTask.copy(); + recordCommand(this); + } catch (IndexOutOfBoundsException ioobe) { + logger.info("Exception: Index Out of Bounds\n"); + indicateAttemptToExecuteIncorrectCommand(); + return new CommandResult(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } catch (IllegalEditException iee) { + logger.info("Exception: Illegal Edit"); + indicateAttemptToExecuteIncorrectCommand(); + return new CommandResult(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE)); + } catch (IllegalValueException ive) { + logger.info("Exception: Illegal Value"); + assert false : "Illegal value entered"; + } + + if (editedTask.getIsEvent()) { + duration = DateTimeInfo.durationOfTheEvent(editedTask.getStartTime().toString(), + editedTask.getEndTime().toString()); + } else { + duration = ""; + } + + return new CommandResult(String.format(MESSAGE_EDIT_TASK_SUCCESS, taskStore.getName(), editedTask.getName()) + + "\n" + duration); + } + + //@@author A0127686R + @Override + public void executeUndo() { + Task toDelete = editedTask; + Task toAddBack = taskStore; + + try { + model.deleteTask(toDelete); + } catch (TaskNotFoundException pnfe) { + assert false : "The target task cannot be missing"; + } + + try { + model.addTask(toAddBack); + } catch (DuplicateTaskException e) { + indicateAttemptToExecuteIncorrectCommand(); + } + } + + //@@author A0127855W + @Override + public String getUndoMessage(){ + return String.format(MESSAGE_UNDO_SUCCESS, taskStore.getName(), editedTask.getName()); + } +} diff --git a/src/main/java/seedu/flexitrack/logic/commands/ExitCommand.java b/src/main/java/seedu/flexitrack/logic/commands/ExitCommand.java new file mode 100644 index 000000000000..f4047e32a00b --- /dev/null +++ b/src/main/java/seedu/flexitrack/logic/commands/ExitCommand.java @@ -0,0 +1,29 @@ +package seedu.flexitrack.logic.commands; + +import seedu.flexitrack.commons.core.EventsCenter; +import seedu.flexitrack.commons.events.ui.ExitAppRequestEvent; + +/** + * Terminates the program. + */ +public class ExitCommand extends Command { + + public static final String COMMAND_WORD = "exit"; + public static final String COMMAND_SHORTCUT = "q"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ", Shortcut [" + COMMAND_SHORTCUT + "]" + ": Exit the program.\n" + "Example: " + COMMAND_WORD; + + public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting FlexiTrack as requested ..."; + + public ExitCommand() { + } + + @Override + public CommandResult execute() { + UndoCommand.doneCommandStack.clear(); + RedoCommand.undoneCommandStack.clear(); + EventsCenter.getInstance().post(new ExitAppRequestEvent()); + return new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT); + } + +} diff --git a/src/main/java/seedu/flexitrack/logic/commands/FindCommand.java b/src/main/java/seedu/flexitrack/logic/commands/FindCommand.java new file mode 100644 index 000000000000..4cc2be630fa6 --- /dev/null +++ b/src/main/java/seedu/flexitrack/logic/commands/FindCommand.java @@ -0,0 +1,42 @@ +package seedu.flexitrack.logic.commands; + +import java.util.HashSet; +import java.util.Set; + +/** + * Finds and lists all tasks in FlexiTrack 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 COMMAND_SHORTCUT = "f"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ", Shortcut [" + COMMAND_SHORTCUT + "]" + ": Finds all tasks whose names contain any of " + + "the specified keywords (case-sensitive) and displays them as a list with index numbers.\n" + + "1. Default Search - " + "Parameters: KEYWORD [MORE_KEYWORDS]...\n\t" + "Example: " + COMMAND_WORD + + " CS2103\n" + "2. Search by exact task name - " + "Parameters: f/ KEYWORD [MORE KEYWORDS]...\n\t" + + "Example: " + COMMAND_WORD + "f/ attend CS2103 lecture"; + + private final Set keywords; + + public FindCommand(Set keywords) { + this.keywords = keywords; + } + + //@@author A0147092E + public FindCommand(String keywords) { + Set keyword2 = new HashSet(); + keyword2.add(keywords); + this.keywords = keyword2; + } + //@@author + + @Override + public CommandResult execute() { + model.updateFilteredTaskList(keywords); + return new CommandResult(getMessageForTaskListShownSummary(model.getFilteredTaskList().size())); + } + + +} diff --git a/src/main/java/seedu/flexitrack/logic/commands/GapCommand.java b/src/main/java/seedu/flexitrack/logic/commands/GapCommand.java new file mode 100644 index 000000000000..98b0a6c07795 --- /dev/null +++ b/src/main/java/seedu/flexitrack/logic/commands/GapCommand.java @@ -0,0 +1,107 @@ +//@@author A0127686R +package seedu.flexitrack.logic.commands; + +import java.util.List; + +import seedu.flexitrack.model.task.DateTimeInfo; + +/** + * Find a gap with minimum time specified by the user. + */ +public class GapCommand extends Command { + + public static final String COMMAND_WORD = "gap"; + public static final String COMMAND_SHORTCUT = "g"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ", shortcut [" + COMMAND_SHORTCUT + "]" + + ": Find the earliest specified timing available.\n" + + "1. To find a gap with default number of gap searched (3) - Parameters: [specified timing] \n" + + " Example: " + COMMAND_WORD + " 3 hours \n" + + "2. To find a specific number of gap - Parameters: [specified timing] n/[number of gap to find] \n" + + " Example: " + COMMAND_WORD + " 30 minutes n/4\n" + + "Note that :\n" + + " 1. The specified timing and number of gap need to be in digit.\n" + + " 2. Timing for minutes can't be longer than 59 minutes, for hour it can't be longer than 23 hours" + + "and for days it can't be more than 29 days\n" + + "Example of accepted specified timing: 20m, 30 minutes, 2 hours, 12 h, 2 days.\n" + + "Example of rejected specified timing: 70 minutes, 2 hours 30 minutes"; + + public static final String MESSAGE_SUCCESS = "The earliest %1$s free time are found... "; + public static final String WORD_DAY = "day"; + public static final String WORD_HOUR = "hour"; + public static final String WORD_MINUTE = "minute"; + public static final String INITIAL_DAY = "d"; + public static final String INITIAL_HOUR = "h"; + public static final String INITIAL_MINUTE = "m"; + public static final int DEFAULT_NUMBER_OF_SLOT = 3; + public static final int REF_NO_DAY = 2; + public static final int REF_NO_HOUR = 1; + public static final int REF_NO_MINUTE = 0; + + private final int keyword; + private final int length; + private final int numberOfSlot; + + public GapCommand(int keyword, int length, int numberOfSlot) { + this.keyword = keyword; + this.length = length; + this.numberOfSlot = numberOfSlot; + } + + @Override + public CommandResult execute() { + List listOfTiming = model.findSpecifiedGapTiming(keyword, length, numberOfSlot); + String theListOfDates = putResultIntoString(listOfTiming); + String keywordString = putKeywordIntoString(); + return new CommandResult((String.format(MESSAGE_SUCCESS, keywordString) + theListOfDates)); + } + + /** + * Put the user specified timing back into string + * + * @return the specified timing by the users + */ + private String putKeywordIntoString() { + String keywordString = ""; + switch (keyword) { + case 0: + keywordString = "minute"; + break; + case 1: + keywordString = "hour"; + break; + case 2: + keywordString = "day"; + break; + } + keywordString = length + " " + keywordString; + if (length > 1) { + keywordString = keywordString + "s"; + } + return keywordString; + } + + /** + * Rearrange and put into String the starting and ending time of the gap + * @param listOfTiming + * @return The timing of the gap in string, ready to be shown to the user + */ + private String putResultIntoString(List listOfTiming) { + String theListOfDates = ""; + int iter = 0; + for (; iter < listOfTiming.size() - 1; iter++) { + if (iter == 0 && listOfTiming.get(iter).toString().equals(DateTimeInfo.getCurrentTime().toString())) { + theListOfDates = theListOfDates + "\nBetween: now "; + } else { + theListOfDates = theListOfDates + "\nBetween: " + listOfTiming.get(iter).toString(); + } + iter = iter + 1; + theListOfDates = theListOfDates + " to: " + listOfTiming.get(iter).toString(); + + } + if ((iter + 1) / 2 < numberOfSlot) { + theListOfDates = theListOfDates + "\nFree from: " + listOfTiming.get(iter).toString() + " onwards. "; + } + return theListOfDates; + } + +} diff --git a/src/main/java/seedu/flexitrack/logic/commands/HelpCommand.java b/src/main/java/seedu/flexitrack/logic/commands/HelpCommand.java new file mode 100644 index 000000000000..2bdd2d8f9bd1 --- /dev/null +++ b/src/main/java/seedu/flexitrack/logic/commands/HelpCommand.java @@ -0,0 +1,89 @@ +//@@author A0138455Y +package seedu.flexitrack.logic.commands; + +import seedu.flexitrack.commons.core.EventsCenter; +import seedu.flexitrack.commons.events.ui.ShowHelpRequestEvent; + +/** + * Format full help instructions for every command for display. + */ +public class HelpCommand extends Command { + + public static final String COMMAND_WORD = "help"; + public static final String COMMAND_SHORTCUT = "h"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ", Shortcut [" + COMMAND_SHORTCUT + "]" + ": Shows program usage instructions.\n" + "Example: " + + COMMAND_WORD; + + public static final String HELP_MESSAGE_USAGE = COMMAND_WORD + ": Shows program usage instructions.\n" + + "List of Commands: " + AddCommand.COMMAND_WORD + ", " + ClearCommand.COMMAND_WORD + ", " + + DeleteCommand.COMMAND_WORD + ", " + EditCommand.COMMAND_WORD + ", " + ExitCommand.COMMAND_WORD + ", " + + FindCommand.COMMAND_WORD + ", " + ListCommand.COMMAND_WORD + ", " + MarkCommand.COMMAND_WORD + ", " + + UnmarkCommand.COMMAND_WORD + ", " + BlockCommand.COMMAND_WORD + ", " + + ChangeStoragePathCommand.COMMAND_WORD + "(Change Storage Path), " + UndoCommand.COMMAND_WORD + + ", " + RedoCommand.COMMAND_WORD + ", " + GapCommand.COMMAND_WORD + "\n" + "Example: " + COMMAND_WORD + " " + + ClearCommand.COMMAND_WORD; + + public static final String SHOWING_HELP_MESSAGE = "Opened help window."; + private String userInput; + + public HelpCommand(String args) { + this.userInput = args; + } + + @Override + public CommandResult execute() { + switch (userInput) { + + case AddCommand.COMMAND_WORD: + return new CommandResult(AddCommand.MESSAGE_USAGE); + + case EditCommand.COMMAND_WORD: + return new CommandResult(EditCommand.MESSAGE_USAGE); + + case DeleteCommand.COMMAND_WORD: + return new CommandResult(DeleteCommand.MESSAGE_USAGE); + + case ClearCommand.COMMAND_WORD: + return new CommandResult(ClearCommand.MESSAGE_USAGE); + + case FindCommand.COMMAND_WORD: + return new CommandResult(FindCommand.MESSAGE_USAGE); + + case MarkCommand.COMMAND_WORD: + return new CommandResult(MarkCommand.MESSAGE_USAGE); + + case UnmarkCommand.COMMAND_WORD: + return new CommandResult(UnmarkCommand.MESSAGE_USAGE); + + case ListCommand.COMMAND_WORD: + return new CommandResult(ListCommand.MESSAGE_USAGE); + + case ExitCommand.COMMAND_WORD: + return new CommandResult(ExitCommand.MESSAGE_USAGE); + + case BlockCommand.COMMAND_WORD: + return new CommandResult(BlockCommand.MESSAGE_USAGE); + + case UndoCommand.COMMAND_WORD: + return new CommandResult(UndoCommand.MESSAGE_USAGE); + + case RedoCommand.COMMAND_WORD: + return new CommandResult(RedoCommand.MESSAGE_USAGE); + + case GapCommand.COMMAND_WORD: + return new CommandResult(GapCommand.MESSAGE_USAGE); + + case ChangeStoragePathCommand.COMMAND_WORD: + return new CommandResult(ChangeStoragePathCommand.MESSAGE_USAGE); + + default: + return new CommandResult(HELP_MESSAGE_USAGE); + } + /* + * EventsCenter.getInstance().post(new ShowHelpRequestEvent()); return + * new CommandResult(SHOWING_HELP_MESSAGE); + */ + } + +} diff --git a/src/main/java/seedu/address/logic/commands/IncorrectCommand.java b/src/main/java/seedu/flexitrack/logic/commands/IncorrectCommand.java similarity index 75% rename from src/main/java/seedu/address/logic/commands/IncorrectCommand.java rename to src/main/java/seedu/flexitrack/logic/commands/IncorrectCommand.java index 491d9cb9da35..9099281e097b 100644 --- a/src/main/java/seedu/address/logic/commands/IncorrectCommand.java +++ b/src/main/java/seedu/flexitrack/logic/commands/IncorrectCommand.java @@ -1,14 +1,14 @@ -package seedu.address.logic.commands; - +package seedu.flexitrack.logic.commands; /** - * Represents an incorrect command. Upon execution, produces some feedback to the user. + * Represents an incorrect command. Upon execution, produces some feedback to + * the user. */ public class IncorrectCommand extends Command { public final String feedbackToUser; - public IncorrectCommand(String feedbackToUser){ + public IncorrectCommand(String feedbackToUser) { this.feedbackToUser = feedbackToUser; } @@ -19,4 +19,3 @@ public CommandResult execute() { } } - diff --git a/src/main/java/seedu/flexitrack/logic/commands/ListCommand.java b/src/main/java/seedu/flexitrack/logic/commands/ListCommand.java new file mode 100644 index 000000000000..f08e35030e73 --- /dev/null +++ b/src/main/java/seedu/flexitrack/logic/commands/ListCommand.java @@ -0,0 +1,56 @@ +//@@author A0127686R +package seedu.flexitrack.logic.commands; + +import java.util.logging.Logger; + +import seedu.flexitrack.commons.core.LogsCenter; +import seedu.flexitrack.model.ModelManager; + +/** + * Lists all task in the FlexiTrack to the user. + */ +public class ListCommand extends Command { + + public static final String COMMAND_WORD = "list"; + public static final String COMMAND_SHORTCUT = "l"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ", Shortcut [" + COMMAND_SHORTCUT + "]" + + ": List all the to do lists in FlexiTrack.\n" + + COMMAND_WORD + " future : List all the to do lists that is due or start in the future.\n" + + COMMAND_WORD + " past : List all the to do lists in the past.\n" + + COMMAND_WORD + " mark: List all the to do lists that has been marked.\n" + + COMMAND_WORD + " unmark: List all the to do lists in that has not been marked.\n" + + "Example: " + COMMAND_WORD + " past \n"; + public static final String MESSAGE_SUCCESS = "Listed all tasks"; + public static final String LIST_FUTURE_COMMAND = "future"; + public static final String LIST_PAST_COMMAND = "past"; + public static final String LIST_MARK_COMMAND = "mark"; + public static final String LIST_UNMARK_COMMAND = "unmark"; + public static final String LIST_BLOCK_COMMAND = "block"; + public static final String LIST_LAST_COMMAND = "last"; + public static final String LIST_NEXT_COMMAND = "next"; + public static final String LIST_LAST_WEEK_COMMAND = "last week"; + public static final String LIST_LAST_MONTH_COMMAND = "last month"; + public static final String LIST_NEXT_WEEK_COMMAND = "next week"; + public static final String LIST_NEXT_MONTH_COMMAND = "next month"; + public static final String LIST_UNSPECIFIED_COMMAND = ""; + + public final String arguments; + + private static final Logger logger = LogsCenter.getLogger(ModelManager.class); + + public ListCommand(String args) { + this.arguments = args; + logger.info("----------------[LIST COMMAND STORE ARGS][" + args.toUpperCase() + "]"); + } + + @Override + public CommandResult execute() { + if (arguments.equals(LIST_UNSPECIFIED_COMMAND)) { + model.updateFilteredListToShowAll(); + } else { + model.updateFilteredListToFitUserInput(arguments); + } + return new CommandResult(MESSAGE_SUCCESS); + } + +} diff --git a/src/main/java/seedu/flexitrack/logic/commands/MarkCommand.java b/src/main/java/seedu/flexitrack/logic/commands/MarkCommand.java new file mode 100644 index 000000000000..c917eacb62b6 --- /dev/null +++ b/src/main/java/seedu/flexitrack/logic/commands/MarkCommand.java @@ -0,0 +1,92 @@ +//@@author A0138455Y +package seedu.flexitrack.logic.commands; + +import seedu.flexitrack.commons.core.Messages; +import seedu.flexitrack.commons.core.UnmodifiableObservableList; +import seedu.flexitrack.commons.exceptions.IllegalValueException; +import seedu.flexitrack.model.task.ReadOnlyTask; +import seedu.flexitrack.model.task.Task; +import seedu.flexitrack.model.task.UniqueTaskList.DuplicateTaskException; +import seedu.flexitrack.model.task.UniqueTaskList.TaskNotFoundException; + +/** + * Selects a task identified using it's last displayed index from the + * FlexiTrack. + */ +public class MarkCommand extends Command { + + public final int targetIndex; + + public static final String COMMAND_WORD = "mark"; + public static final String COMMAND_SHORTCUT = "m"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ", Shortcut [" + COMMAND_SHORTCUT + "]" + + ": Marks the task identified by the index number used in the task listing.\n" + + "Parameters: INDEX (must be a positive integer)\n" + "Example: " + COMMAND_WORD + " 1"; + + public static final String MESSAGE_MARK_TASK_SUCCESS = "Marked Task: %1$s"; + private static final String MESSAGE_UNDO_SUCCESS = "Undid mark task: %1$s"; + + private Task taskStore; + private Task markedTask; + + public MarkCommand(int targetIndex) { + this.targetIndex = targetIndex; + } + + /** + * Constructor for undo command + */ + public MarkCommand() { + this.targetIndex = 0; + } + + @Override + public CommandResult execute(){ + + UnmodifiableObservableList lastShownList = model.getFilteredTaskList(); + + if (lastShownList.size() < targetIndex) { + indicateAttemptToExecuteIncorrectCommand(); + return new CommandResult(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + + try { + taskStore = lastShownList.get(targetIndex - 1).copy(); + markedTask = model.markTask(lastShownList.get(targetIndex-1)); + markedTask = markedTask.copy(); + recordCommand(this); + return new CommandResult(String.format(MESSAGE_MARK_TASK_SUCCESS, targetIndex)); + } catch (IndexOutOfBoundsException ioobe) { + indicateAttemptToExecuteIncorrectCommand(); + return new CommandResult(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } catch (IllegalValueException e) { + return new CommandResult(e.getMessage()); + } + } + + //@@author A0127686R + @Override + public void executeUndo() { + Task toDelete = markedTask; + Task toAddBack = taskStore; + + try { + model.deleteTask(toDelete); + } catch (TaskNotFoundException pnfe) { + assert false : "The target task cannot be missing"; + } + + try { + model.addTask(toAddBack); + } catch (DuplicateTaskException e) { + indicateAttemptToExecuteIncorrectCommand(); + } + } + + //@@author A0127855W + @Override + public String getUndoMessage(){ + return String.format(MESSAGE_UNDO_SUCCESS, markedTask); + } +} diff --git a/src/main/java/seedu/flexitrack/logic/commands/RedoCommand.java b/src/main/java/seedu/flexitrack/logic/commands/RedoCommand.java new file mode 100644 index 000000000000..337855a28c19 --- /dev/null +++ b/src/main/java/seedu/flexitrack/logic/commands/RedoCommand.java @@ -0,0 +1,48 @@ +//@@author A0127855W +package seedu.flexitrack.logic.commands; + +import java.util.Stack; + +/** + * Clears the FlexiTrack. + */ +public class RedoCommand extends Command { + + public static final String COMMAND_WORD = "redo"; + public static final String COMMAND_SHORTCUT = "rd"; // TODO: impiment ctrl + Y + public static final String MESSAGE_USAGE = COMMAND_WORD + ", Shortcut [" + COMMAND_SHORTCUT + "]" + ": Clear the to do lists in FlexiTrack.\n" + "Example: " + + COMMAND_WORD; + public static final String MESSAGE_SUCCESS = "Your last command has been redone!"; + public static final String MESSAGE_NOT_SUCCESS = "You have no command to redo!"; + + //Stores the undone commands + static Stack undoneCommandStack = new Stack(); + + public RedoCommand() { + } + + @Override + public CommandResult execute() { + Command redo = null; + if (undoneCommandStack.size() == 0 ){ + return new CommandResult(String.format(MESSAGE_NOT_SUCCESS)); + } + + redo = undoneCommandStack.pop(); + if (redo instanceof AddCommand && redo.getNumOfOccurrrence() !=0 ){ + int numOfOccurrrence = redo.getNumOfOccurrrence(); + redo.setNumOfOccurrrence(0); + for (int i = 1; i < numOfOccurrrence; i++) { + redo.execute(); + redo = undoneCommandStack.pop(); + } + redo.setNumOfOccurrrence(numOfOccurrrence); + } + + CommandResult redoMessage = redo.execute(); + model.indicateFlexiTrackerChanged(); + return new CommandResult(MESSAGE_SUCCESS + "\n" + redoMessage.getString()); + } + + +} \ No newline at end of file diff --git a/src/main/java/seedu/flexitrack/logic/commands/UndoCommand.java b/src/main/java/seedu/flexitrack/logic/commands/UndoCommand.java new file mode 100644 index 000000000000..3f7ff9b7dc98 --- /dev/null +++ b/src/main/java/seedu/flexitrack/logic/commands/UndoCommand.java @@ -0,0 +1,60 @@ +//@@author A0127686R +package seedu.flexitrack.logic.commands; + +import java.util.Stack; +import java.util.logging.Logger; + +import seedu.flexitrack.commons.core.LogsCenter; +import seedu.flexitrack.model.ModelManager; +import seedu.flexitrack.model.task.ReadOnlyTask; + +/** + * Clears the FlexiTrack. + */ +public class UndoCommand extends Command { + + public static final String COMMAND_WORD = "undo"; + public static final String COMMAND_SHORTCUT = "ud"; // TODO: impiment ctrl + Z + public static final String MESSAGE_USAGE = COMMAND_WORD + ", Shortcut [" + COMMAND_SHORTCUT + "]" + + ": Clear the to do lists in FlexiTrack.\n" + "Example: " + COMMAND_WORD; + public static final String MESSAGE_SUCCESS = "Your last command has been undone!"; + public static final String MESSAGE_NOT_SUCCESS = "You have no command to undo!"; + + private static final Logger logger = LogsCenter.getLogger(ModelManager.class); + + // Stores all the commands done from app launch + static Stack doneCommandStack = new Stack(); + + public UndoCommand() { + } + + @Override + public CommandResult execute() { + Command undo = null; + + logger.info("----------------[UNDO COMMAND][ executing undo ]"); + + if (doneCommandStack.size() == 0) { + return new CommandResult(String.format(MESSAGE_NOT_SUCCESS)); + } + + + undo = doneCommandStack.pop(); + if (undo instanceof AddCommand && undo.getNumOfOccurrrence() !=0 ){ + int numOfOccurrrence = undo.getNumOfOccurrrence(); + undo.setNumOfOccurrrence(0); + for (int i = 1; i < numOfOccurrrence; i++) { + undo.executeUndo(); + RedoCommand.undoneCommandStack.push(undo); + undo = doneCommandStack.pop(); + } + undo.setNumOfOccurrrence(numOfOccurrrence); + } + + undo.executeUndo(); + RedoCommand.undoneCommandStack.push(undo); + model.indicateFlexiTrackerChanged(); + return new CommandResult(MESSAGE_SUCCESS + "\n" + undo.getUndoMessage()); + } + +} diff --git a/src/main/java/seedu/flexitrack/logic/commands/UnmarkCommand.java b/src/main/java/seedu/flexitrack/logic/commands/UnmarkCommand.java new file mode 100644 index 000000000000..49c09d117e18 --- /dev/null +++ b/src/main/java/seedu/flexitrack/logic/commands/UnmarkCommand.java @@ -0,0 +1,89 @@ +//@@author A0138455Y +package seedu.flexitrack.logic.commands; + +import seedu.flexitrack.commons.core.Messages; +import seedu.flexitrack.commons.core.UnmodifiableObservableList; +import seedu.flexitrack.commons.exceptions.IllegalValueException; +import seedu.flexitrack.model.task.ReadOnlyTask; +import seedu.flexitrack.model.task.Task; +import seedu.flexitrack.model.task.UniqueTaskList.DuplicateTaskException; +import seedu.flexitrack.model.task.UniqueTaskList.TaskNotFoundException; + +/** + * Selects a task identified using it's last displayed index from the + * FlexiTrack. + */ +public class UnmarkCommand extends Command { + public final int targetIndex; + + public static final String COMMAND_WORD = "unmark"; + public static final String COMMAND_SHORTCUT = "u"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ", Shortcut [" + COMMAND_SHORTCUT + "]" + + ": Unmarks the task identified by the index number used in the task listing.\n" + + "Parameters: INDEX (must be a positive integer)\n" + "Example: " + COMMAND_WORD + " 1"; + + public static final String MESSAGE_UNMARK_TASK_SUCCESS = "Unmark Task: %1$s"; + private static final String MESSAGE_UNDO_SUCCESS = "Undid unmark task: %1$s"; + + private Task taskStore; + private Task unMarkedTask; + + public UnmarkCommand(int targetIndex) { + this.targetIndex = targetIndex; + } + + /** + * Constructor for undo command + */ + public UnmarkCommand() { + this.targetIndex = 0; + } + + @Override + public CommandResult execute() { + + UnmodifiableObservableList lastShownList = model.getFilteredTaskList(); + + if (lastShownList.size() < targetIndex) { + indicateAttemptToExecuteIncorrectCommand(); + return new CommandResult(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + + try { + taskStore = lastShownList.get(targetIndex - 1).copy(); + unMarkedTask = model.unmarkTask(lastShownList.get(targetIndex-1)); + unMarkedTask = unMarkedTask.copy(); + recordCommand(this); + return new CommandResult(String.format(MESSAGE_UNMARK_TASK_SUCCESS, targetIndex)); + } catch (IllegalValueException e) { + return new CommandResult(e.getMessage()); + } + + } + + //@@author A0127686R + @Override + public void executeUndo() { + Task toDelete = unMarkedTask; + Task toAddBack = taskStore; + + try { + model.deleteTask(toDelete); + } catch (TaskNotFoundException pnfe) { + assert false : "The target task cannot be missing"; + } + + try { + model.addTask(toAddBack); + } catch (DuplicateTaskException e) { + indicateAttemptToExecuteIncorrectCommand(); + } + } + + //@@author A0127855W + @Override + public String getUndoMessage(){ + return String.format(MESSAGE_UNDO_SUCCESS, unMarkedTask); + } +} diff --git a/src/main/java/seedu/flexitrack/logic/parser/Parser.java b/src/main/java/seedu/flexitrack/logic/parser/Parser.java new file mode 100644 index 000000000000..39c3ab439c87 --- /dev/null +++ b/src/main/java/seedu/flexitrack/logic/parser/Parser.java @@ -0,0 +1,835 @@ +package seedu.flexitrack.logic.parser; + +import static seedu.flexitrack.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.flexitrack.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; +import static seedu.flexitrack.commons.core.Messages.MESSAGE_NUMBER_NEED_TO_BE_IN_DIGIT; + +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import seedu.flexitrack.commons.core.LogsCenter; +import seedu.flexitrack.commons.exceptions.IllegalValueException; +import seedu.flexitrack.commons.util.StringUtil; +import seedu.flexitrack.logic.commands.AddCommand; +import seedu.flexitrack.logic.commands.BlockCommand; +import seedu.flexitrack.logic.commands.ChangeStoragePathCommand; +import seedu.flexitrack.logic.commands.ClearCommand; +import seedu.flexitrack.logic.commands.Command; +import seedu.flexitrack.logic.commands.DeleteCommand; +import seedu.flexitrack.logic.commands.EditCommand; +import seedu.flexitrack.logic.commands.ExitCommand; +import seedu.flexitrack.logic.commands.FindCommand; +import seedu.flexitrack.logic.commands.GapCommand; +import seedu.flexitrack.logic.commands.HelpCommand; +import seedu.flexitrack.logic.commands.IncorrectCommand; +import seedu.flexitrack.logic.commands.ListCommand; +import seedu.flexitrack.logic.commands.MarkCommand; +import seedu.flexitrack.logic.commands.RedoCommand; +import seedu.flexitrack.logic.commands.UndoCommand; +import seedu.flexitrack.logic.commands.UnmarkCommand; +import seedu.flexitrack.model.Model; +import seedu.flexitrack.model.task.DateTimeInfo; +import seedu.flexitrack.model.task.DateTimeInfoParser; + + +/** + * 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 TASK_INDEX_ARGS_FORMAT = Pattern.compile("(?.+)"); + private static final Pattern KEYWORDS_ARGS_FORMAT = Pattern.compile("(?\\S+(?:\\s+\\S+)*)"); + //@@author A0127855W + private static final HashMap SHORTCUT_MAP = new HashMap(); // more + static { + SHORTCUT_MAP.put(AddCommand.COMMAND_SHORTCUT, AddCommand.COMMAND_WORD); + SHORTCUT_MAP.put(ClearCommand.COMMAND_SHORTCUT, ClearCommand.COMMAND_WORD); + SHORTCUT_MAP.put(DeleteCommand.COMMAND_SHORTCUT, DeleteCommand.COMMAND_WORD); + SHORTCUT_MAP.put(EditCommand.COMMAND_SHORTCUT, EditCommand.COMMAND_WORD); + SHORTCUT_MAP.put(ExitCommand.COMMAND_SHORTCUT, ExitCommand.COMMAND_WORD); + SHORTCUT_MAP.put(HelpCommand.COMMAND_SHORTCUT, HelpCommand.COMMAND_WORD); + SHORTCUT_MAP.put(FindCommand.COMMAND_SHORTCUT, FindCommand.COMMAND_WORD); + SHORTCUT_MAP.put(ListCommand.COMMAND_SHORTCUT, ListCommand.COMMAND_WORD); + SHORTCUT_MAP.put(MarkCommand.COMMAND_SHORTCUT, MarkCommand.COMMAND_WORD); + SHORTCUT_MAP.put(UnmarkCommand.COMMAND_SHORTCUT, UnmarkCommand.COMMAND_WORD); + SHORTCUT_MAP.put(BlockCommand.COMMAND_SHORTCUT, BlockCommand.COMMAND_WORD); + SHORTCUT_MAP.put(UndoCommand.COMMAND_SHORTCUT, UndoCommand.COMMAND_WORD); + SHORTCUT_MAP.put(RedoCommand.COMMAND_SHORTCUT, RedoCommand.COMMAND_WORD); + SHORTCUT_MAP.put(GapCommand.COMMAND_SHORTCUT, GapCommand.COMMAND_WORD); + } + + //@@author A0127686R + private final Logger logger = LogsCenter.getLogger(Parser.class); + + private static final HashMap SHORTCUT_LIST_MAP = new HashMap(); // more + static { + SHORTCUT_LIST_MAP.put("f", ListCommand.LIST_FUTURE_COMMAND); + SHORTCUT_LIST_MAP.put("p", ListCommand.LIST_PAST_COMMAND); + SHORTCUT_LIST_MAP.put("m", ListCommand.LIST_MARK_COMMAND); + SHORTCUT_LIST_MAP.put("u", ListCommand.LIST_UNMARK_COMMAND); + SHORTCUT_LIST_MAP.put("b", ListCommand.LIST_BLOCK_COMMAND); + SHORTCUT_LIST_MAP.put("l", ListCommand.LIST_LAST_COMMAND); + SHORTCUT_LIST_MAP.put("n", ListCommand.LIST_NEXT_COMMAND); + SHORTCUT_LIST_MAP.put("lw", ListCommand.LIST_LAST_WEEK_COMMAND); + SHORTCUT_LIST_MAP.put("lm", ListCommand.LIST_LAST_MONTH_COMMAND); + SHORTCUT_LIST_MAP.put("nw", ListCommand.LIST_NEXT_WEEK_COMMAND); + SHORTCUT_LIST_MAP.put("nm", ListCommand.LIST_NEXT_MONTH_COMMAND); + } + // '/' forward slashes are reserved for delimiter prefixes + private static final Pattern TASK_FIND_GAP_WITH_NUMBER_ARGS_FORMAT = Pattern + .compile("(?[^/]+)" + "[Nn]/(?[^/]+)"); + private static final Pattern TASK_FIND_GAP_ARGS_FORMAT = Pattern.compile("(?[^/]+)"); + + private static final Pattern TASK_EVENT_TYPE_DATA_ARGS_FORMAT = Pattern + .compile("(?.+)" + "[fF][rR][oO][mM]/(?[^/]+)" + "[Tt][Oo]/(?[^/]+)"); + private static final Pattern TASK_DEADLINE_TYPE_DATA_ARGS_FORMAT = Pattern + .compile("(?.+)" + "[Bb][Yy]/(?[^/]+)"); + private static final Pattern TASK_FLOATING_TYPE_DATA_ARGS_FORMAT = Pattern.compile("(?.+)"); + + // @@author A0147092E + private static final Pattern TASK_RECURRING_EVENT_TYPE_DATA_ARGS_FORMAT = Pattern + .compile("(?.+)" + "[Ff][Rr]/\\s*(?\\d{1,2})\\s*" + + "[tT][Yy]/(?[^/\\d]{3,7})" + "from/(?[^/]+)" + "to/(?[^/]+)"); + private static final Pattern TASK_RECURRING_TASK_TYPE_DATA_ARGS_FORMAT = Pattern + .compile("(?.+)" + "[Ff][Rr]/\\s*(?\\d{1,2})\\s*" + + "[Tt][Yy]/\\s*(?[^/\\d]{3,7})" + "by/(?[^/]+)"); + private static final Pattern TASK_RECURRING_FLOATING_TASK_TYPE_DATA_ARGS_FORMAT = Pattern + .compile("(?.+)" + "[Ff][Rr]/\\s*(?\\d{1,2})"); + //@@author A0127855W + + private static final Pattern EDIT_COMMAND_FORMAT = Pattern.compile("(?[0-9]+)(?.*)"); + private static final Pattern EDIT_ARGS_NAME = Pattern.compile("[Nn]/\\s*(?.+)"); + private static final Pattern EDIT_ARGS_DUEDATE = Pattern.compile("[Bb][Yy]/\\s*(?[^/]+)"); + private static final Pattern EDIT_ARGS_STARTTIME = Pattern.compile("[Ff][Rr][oO][mM]/\\s*(?[^/]+)"); + private static final Pattern EDIT_ARGS_ENDTIME = Pattern.compile("[tT][Oo]/\\s*(?[^/]+)"); + //@@author A0138455Y + private static final Pattern STORAGE_PATH_FORMAT = Pattern.compile("(?^[^\\?~`!@#$^&-+=%*|\"<>\\|]+)"); + //@@author + + private Model model; + + public final static String EMPTY_TIME_INFO = "Feb 29 2000 00:00:00"; + + + public Parser() { + } + + public Parser(Model model) { + this.model = model; + } + + /** + * 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 parsedCommandWord = parseCommandWord(commandWord.toLowerCase()); + + final String arguments = matcher.group("arguments"); + switch (parsedCommandWord) { + + case AddCommand.COMMAND_WORD: + return prepareAdd(arguments); + + case BlockCommand.COMMAND_WORD: + return prepareBlock(arguments); + + case EditCommand.COMMAND_WORD: + return prepareEdit(arguments); + + case DeleteCommand.COMMAND_WORD: + return prepareDelete(arguments); + + case ClearCommand.COMMAND_WORD: + return new ClearCommand(); + + case UndoCommand.COMMAND_WORD: + return new UndoCommand(); + + case RedoCommand.COMMAND_WORD: + return new RedoCommand(); + + case FindCommand.COMMAND_WORD: + return prepareFind(arguments); + + case MarkCommand.COMMAND_WORD: + return prepareMark(arguments); + + case UnmarkCommand.COMMAND_WORD: + return prepareUnmark(arguments); + + case ListCommand.COMMAND_WORD: + return prepareList(arguments.toLowerCase()); + + case GapCommand.COMMAND_WORD: + return prepareGap(arguments.toLowerCase()); + + case ExitCommand.COMMAND_WORD: + return new ExitCommand(); + + case ChangeStoragePathCommand.COMMAND_WORD: + return prepareChangePathCommand(arguments); + + case HelpCommand.COMMAND_WORD: + return new HelpCommand(arguments.trim()); + + default: + return new IncorrectCommand(MESSAGE_UNKNOWN_COMMAND); + } + } + + // @@author A0127686R + /** + * Prepare the user input arguments to be passed GapCommand class + * + * @param args + * @return a new Gap Command class + */ + private Command prepareGap(String args) { + args.toLowerCase(); + + Matcher matcher = TASK_FIND_GAP_WITH_NUMBER_ARGS_FORMAT.matcher(args.trim()); + int numberOfSlot = GapCommand.DEFAULT_NUMBER_OF_SLOT; + + if (matcher.matches()) { + args = matcher.group("info").trim(); + try { + numberOfSlot = Integer.parseInt(matcher.group("numberOfGaps").trim()); + } catch (NumberFormatException nfe) { + logger.info("----------------[GAP COMMAND PARSER][" + " user input length without a digit " + "]"); + return new IncorrectCommand( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, MESSAGE_NUMBER_NEED_TO_BE_IN_DIGIT)); + } + } else { + if (args.trim().equals("") || args.trim().contains("n/")) { + logger.info("----------------[GAP COMMAND PARSER][" + " user used n/ without specifying the number " + "]"); + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, GapCommand.MESSAGE_USAGE)); + } + matcher = TASK_FIND_GAP_ARGS_FORMAT.matcher(args.trim()); + matcher.matches(); + args = matcher.group("info").trim(); + } + + return issueGapCommandIfArgsIsValid(args, numberOfSlot); + } + + /** + * Test if the args is valid, if it is convert the length and key keywords into integer representing them + * and issue a new Gap Command, else log and return invalid command + * + * @param args Arguments of the length and keyword + * @param numberOfSlot The number of time slot to be find + * @return GapCommand or IncorrectCommand + */ + private Command issueGapCommandIfArgsIsValid(String args, int numberOfSlot) { + if (isGapArgumentValid(args)) { + try { + int keyword = extractKeywordFromArgs(args); + int length = extractLength(args); + return new GapCommand(keyword, length, numberOfSlot); + + } catch (NumberFormatException nfe) { + logger.info("----------------[GAP COMMAND PARSER][" + " Invalid user input " + "]"); + return new IncorrectCommand( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, MESSAGE_NUMBER_NEED_TO_BE_IN_DIGIT)); + } + } else { + logger.info("----------------[GAP COMMAND PARSER][" + " Invalid user input " + "]"); + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, GapCommand.MESSAGE_USAGE)); + } + } + + /** + * Extract the length of the duration from string in keywords to number + * + * @param args + * @return length of duration in integer + * @throws NumberFormatException + */ + private int extractLength(String args) throws NumberFormatException { + String length = args.replace(GapCommand.WORD_DAY + "s", "").replace(GapCommand.WORD_HOUR + "s", "") + .replace(GapCommand.WORD_MINUTE + "s", "").replace(GapCommand.WORD_DAY, "") + .replace(GapCommand.WORD_HOUR, "").replace(GapCommand.WORD_MINUTE, "") + .replace(GapCommand.INITIAL_DAY, "").replace(GapCommand.INITIAL_MINUTE, "") + .replace(GapCommand.INITIAL_HOUR, ""); + if (length.trim().equals("")) { + return 1; + } else { + return Integer.parseInt(length.trim()); + } + } + + /** + * Extract the keyword from the arguments and return it in integer reference + * number + * + * @param args + * @return number representing each key word. + */ + private int extractKeywordFromArgs(String args) { + if (args.contains(GapCommand.WORD_DAY) || args.contains(GapCommand.WORD_DAY + "s") + || args.contains(GapCommand.INITIAL_DAY)) { + return GapCommand.REF_NO_DAY; + } + if (args.contains(GapCommand.WORD_HOUR) || args.contains(GapCommand.WORD_HOUR + "s") + || args.contains(GapCommand.INITIAL_HOUR)) { + return GapCommand.REF_NO_HOUR; + } else { + return GapCommand.REF_NO_MINUTE; + } + } + + /** + * Find out if the argument is a valid argument for a GapCommand. The + * argument could not have more than one timing key words ( minute, hour or + * day ) + * + * @param args + * @return true if the argument is a valid argument + */ + private boolean isGapArgumentValid(String args) { + int numberOfMatch = 0; + + if (args.contains(GapCommand.WORD_DAY) || args.contains(GapCommand.WORD_DAY + "s") + || args.contains(GapCommand.INITIAL_DAY)) { + numberOfMatch = numberOfMatch + 1; + } + if (args.contains(GapCommand.WORD_HOUR) || args.contains(GapCommand.WORD_HOUR + "s") + || args.contains(GapCommand.INITIAL_HOUR)) { + numberOfMatch = numberOfMatch + 1; + } + if (args.contains(GapCommand.WORD_MINUTE) || args.contains(GapCommand.WORD_MINUTE + "s") + || args.contains(GapCommand.INITIAL_MINUTE)) { + numberOfMatch = numberOfMatch + 1; + } + return numberOfMatch == 1; + } + + //@@author A0127855W + /** + * parseCommandWord + * ------------------------------------------- + * Parses the given command word string, converting shortcut commands into their full versions + * @param commandWord + * @return String: Full command word + */ + private String parseCommandWord(String commandWord) { + assert commandWord != null; + return SHORTCUT_MAP.getOrDefault(commandWord, commandWord); + } + + //@@author A0127686R + /** + * Check if the arguments are valid for list Command + * + * @param arguments The user inputed argument + * @return New ListCommand containing arguments + */ + private Command prepareList(String arguments) { + String parsedArguments = parseListWord(arguments.trim()); + parsedArguments = parsedArguments.trim(); + try { + if (isValideListFormat(parsedArguments)) { + return new ListCommand(parsedArguments); + } + } catch (IllegalValueException e) { + logger.info("----------------[LIST COMMAND PARSER][" + " Invalid user input " + "]"); + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, ListCommand.MESSAGE_USAGE)); + } + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, ListCommand.MESSAGE_USAGE)); + } + + /** + * if the argument is a shortcut, return the parsed proper word + * + * @param listWord The list argument key word inputed by the user + * @return The pair proper word, if not found return default + */ + private String parseListWord(String listWord) { + assert listWord != null; + return SHORTCUT_LIST_MAP.getOrDefault(listWord, listWord); + } + + //@@author A0138455Y + /** + * + * @param args + * @return + */ + private Command prepareBlock(String args) { + final Matcher matcherEvent = TASK_EVENT_TYPE_DATA_ARGS_FORMAT.matcher(args.trim()); + + // Validate arg string format + try { + if (matcherEvent.matches()) { + return new BlockCommand("(Blocked) " + matcherEvent.group("name"), EMPTY_TIME_INFO, matcherEvent.group("startTime"), + matcherEvent.group("endTime")); + } else { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, BlockCommand.MESSAGE_USAGE)); + } + } catch (IllegalValueException ive) { + return new IncorrectCommand(ive.getMessage()); + } + } + + //@@author A0127686R + /** + * @param arguments + * @return + * @throws IllegalValueException + */ + private boolean isValideListFormat(String arguments) throws IllegalValueException { + String dateInfo = (arguments.replace(ListCommand.LIST_FUTURE_COMMAND, "").replace(ListCommand.LIST_PAST_COMMAND, ""). + replace(ListCommand.LIST_UNMARK_COMMAND, "").replace(ListCommand.LIST_MARK_COMMAND, ""). + replace(ListCommand.LIST_LAST_MONTH_COMMAND, "").replace(ListCommand.LIST_LAST_WEEK_COMMAND, ""). + replace(ListCommand.LIST_NEXT_MONTH_COMMAND, "").replace(ListCommand.LIST_NEXT_WEEK_COMMAND, ""). + replace(ListCommand.LIST_BLOCK_COMMAND, "").trim()); + if ( !dateInfo.equals("") ){ + DateTimeInfoParser timeArgs = new DateTimeInfoParser(dateInfo); + assert timeArgs != null; + } + return (arguments.contains(ListCommand.LIST_FUTURE_COMMAND) || arguments.contains(ListCommand.LIST_UNMARK_COMMAND) + || arguments.contains(ListCommand.LIST_PAST_COMMAND) || arguments.contains(ListCommand.LIST_MARK_COMMAND) + || arguments.contains(ListCommand.LIST_UNSPECIFIED_COMMAND) || arguments.contains(ListCommand.LIST_LAST_WEEK_COMMAND) + || arguments.contains(ListCommand.LIST_LAST_MONTH_COMMAND) || arguments.contains(ListCommand.LIST_NEXT_MONTH_COMMAND) + || arguments.contains(ListCommand.LIST_NEXT_WEEK_COMMAND) || arguments.contains(ListCommand.LIST_BLOCK_COMMAND)); + } + + //@@author A0127855W + /** + * prepareEdit + * ------------------------------------------ + * Parses the edit command arguments and outputs the correct EditCommand object for execution + * @param arguments + * @return Command: The correct EditCommand object + */ + private Command prepareEdit(String arguments) { + assert arguments != null; + + int index; + String editParameters; + String[] passing = new String[EditCommand.EDIT_PARAMETER_PASSING_MASK.size()]; + + final Matcher matcherEdit = EDIT_COMMAND_FORMAT.matcher(arguments.trim()); + + if (!matcherEdit.matches()) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE)); + } else { + index = Integer.parseInt(matcherEdit.group("index")); + editParameters = matcherEdit.group("arguments").trim(); + } + + final Matcher matcherName = EDIT_ARGS_NAME.matcher(editParameters); + final Matcher matcherDueDate = EDIT_ARGS_DUEDATE.matcher(editParameters); + final Matcher matcherStartTime = EDIT_ARGS_STARTTIME.matcher(editParameters); + final Matcher matcherEndTime = EDIT_ARGS_ENDTIME.matcher(editParameters); + + boolean isNamePresent = matcherName.find(); + boolean isDueDatePresent = matcherDueDate.find(); + boolean isStartTimePresent = matcherStartTime.find(); + boolean isEndTimePresent = matcherEndTime.find(); + + //Check that at least one edit parameter exists + if (!isNamePresent && !isDueDatePresent && !isStartTimePresent && !isEndTimePresent) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE)); + } + + prepareEditParameter(passing, matcherName, isNamePresent, "name"); + prepareEditParameter(passing, matcherDueDate, isDueDatePresent, "dueDate"); + prepareEditParameter(passing, matcherStartTime, isStartTimePresent, "startTime"); + prepareEditParameter(passing, matcherEndTime, isEndTimePresent, "endTime"); + + return new EditCommand(index, passing); + } + + /** + * prepareEditParameter + * --------------------------------------------------------- + * prepares the passing array for each parameter to be passed into the EditCommand constructor + * @param passing + * @param matcherType + * @param typePresent + * @param typeGroupID + */ + private void prepareEditParameter(String[] passing, final Matcher matcherType, boolean typePresent, String typeGroupID) { + if (typePresent) { + passing[EditCommand.EDIT_PARAMETER_PASSING_MASK.get(typeGroupID)] = matcherType.group(typeGroupID); + } else { + passing[EditCommand.EDIT_PARAMETER_PASSING_MASK.get(typeGroupID)] = null; + } + } + + //@@author A0138455Y + /** + * Parses arguments in the context of the Unmarkcommand. + * + * @param args + * full command args string + * @return the prepared command + */ + private Command prepareUnmark(String args) { + Optional index = parseIndex(args); + if (!index.isPresent()) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, UnmarkCommand.MESSAGE_USAGE)); + } + + return new UnmarkCommand(index.get()); + } + + /** + * Parses arguments in the context of the Markcommand. + * + * @param args full command args string + * @return the prepared command + */ + private Command prepareMark(String args) { + + Optional index = parseIndex(args); + if (!index.isPresent()) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, MarkCommand.MESSAGE_USAGE)); + } + + return new MarkCommand(index.get()); + } + + /** + * Parses arguments in the context of the ChangeStoragecommand. + * @param arguments + * @return + */ + private Command prepareChangePathCommand(String args) { + args = args.trim(); + Matcher matcher = STORAGE_PATH_FORMAT.matcher(args); + //Validate args string format + if(!matcher.matches()) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, ChangeStoragePathCommand.MESSAGE_USAGE)); + } else { + String newPath = matcher.group("path").trim() + ".xml"; + return new ChangeStoragePathCommand(newPath); + } + } + //@@author + + //@@author A0127686R + /** + * 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) { + //@@author A0127686R + args = args.trim(); + final Matcher matcherEvent = TASK_EVENT_TYPE_DATA_ARGS_FORMAT.matcher(args); + final Matcher matcherDeadline = TASK_DEADLINE_TYPE_DATA_ARGS_FORMAT.matcher(args); + final Matcher matcherFloating = TASK_FLOATING_TYPE_DATA_ARGS_FORMAT.matcher(args); + //@@author A0147092E + final Matcher matcherRecurringFloatingTask = TASK_RECURRING_FLOATING_TASK_TYPE_DATA_ARGS_FORMAT.matcher(args); + final Matcher matcherRecurringEvent = TASK_RECURRING_EVENT_TYPE_DATA_ARGS_FORMAT.matcher(args); + final Matcher matcherRecurringTask = TASK_RECURRING_TASK_TYPE_DATA_ARGS_FORMAT.matcher(args); + // Validate arg string format + try { + //@@author A0147092E + if (args.trim().contains("fr/") || args.trim().contains("ty/")) { + if (matcherRecurringFloatingTask.matches()) { + return addRecurring(matcherRecurringFloatingTask, "floating"); + } else if (matcherRecurringEvent.matches()) { + return addRecurring(matcherRecurringEvent, "event"); + } else if (matcherRecurringTask.matches()) { + return addRecurring(matcherRecurringTask, "task"); + } else { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); + } + //@@author + } else { + //@@author A0127686R + if (matcherEvent.matches()) { + return addEventTask(matcherEvent); + } else if (matcherDeadline.matches()) { + return addDeadlineTask(matcherDeadline); + } else if (matcherFloating.matches()) { + return addFloatingTask(matcherFloating); + } else { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); + } + } + //@@author + } catch (IllegalValueException ive) { + return new IncorrectCommand(ive.getMessage()); + } + } + + //@@author A0127686R + /** + * Add a floating task, null date for dudate, starting time and ending time. + * + * @return new AddCommand for Floating task + * @throws IllegalValueException + */ + private AddCommand addFloatingTask(Matcher matcher) throws IllegalValueException { + return new AddCommand(matcher.group("name"), EMPTY_TIME_INFO, EMPTY_TIME_INFO, EMPTY_TIME_INFO); + } + + /** + * Add a deadline task, null date for starting time and ending time. + * + * @return new AddCommand for deadline task + * @throws IllegalValueException + */ + private AddCommand addDeadlineTask(Matcher matcher) throws IllegalValueException { + return new AddCommand(matcher.group("name"), matcher.group("dueDate"), EMPTY_TIME_INFO, EMPTY_TIME_INFO); + } + + /** + * Add an event task, null date for dudate. + * + * @return new AddCommand for event task + * @return + * @throws IllegalValueException + */ + private AddCommand addEventTask(Matcher matcher) throws IllegalValueException { + return new AddCommand(matcher.group("name"), EMPTY_TIME_INFO, matcher.group("startTime"), + matcher.group("endTime")); + } + + //@@author A0147092E + /* Add a recurring task / event for day(s)/week(s)/month(s) + * + * @param matcher - commands that matches the regular expression + * @param recurringType - day, week, month + */ + private Command addRecurring(Matcher matcher, String recurringType) throws IllegalValueException { + final int DAY_INCREMENT = 1; + final int WEEK_INCREMENT = 2; + final int MONTH_INCREMENT = 3; + + String formattedStartTime; + String formattedEndTime; + String formattedDueDate; + + int numOfOccurrence = Integer.parseInt(matcher.group("numOfOccurrence").trim()); + + if (recurringType.equalsIgnoreCase("floating")) { + for (int i = 1; i < numOfOccurrence; i++) { + Command command = new AddCommand(matcher.group("name") + "(" + (i+1) + ")", EMPTY_TIME_INFO, EMPTY_TIME_INFO, EMPTY_TIME_INFO); + command.setData(model); + command.execute(); + } + return new AddCommand(matcher.group("name") + "(1)", EMPTY_TIME_INFO, EMPTY_TIME_INFO, EMPTY_TIME_INFO, numOfOccurrence); + + } else if (recurringType.equalsIgnoreCase("task")) { + String occurrenceType = matcher.group("occurrenceType").trim().toLowerCase(); + DateTimeInfo dueDateTimeInfo = new DateTimeInfo (matcher.group("dueDate")); + dueDateTimeInfo.checkDueDateOrStartTime(); + Date initialDueDate = new DateTimeInfoParser(dueDateTimeInfo.toString()).getParsedDateTime(); + + switch (occurrenceType.toLowerCase()) { + case "week": case "weekly": + for (int i=1; i < numOfOccurrence; i++) { + formattedDueDate = dateIncrement(initialDueDate, WEEK_INCREMENT, i); + + Command command = new AddCommand(matcher.group("name"), formattedDueDate, EMPTY_TIME_INFO, EMPTY_TIME_INFO); + command.setData(model); + command.execute(); + } + return new AddCommand(matcher.group("name"), matcher.group("dueDate"), EMPTY_TIME_INFO, EMPTY_TIME_INFO, numOfOccurrence); + + case"mth": case"month": case"monthly": + for (int i=1; i < numOfOccurrence; i++) { + formattedDueDate = dateIncrement(initialDueDate, MONTH_INCREMENT, i); + + Command command = new AddCommand(matcher.group("name"), formattedDueDate, EMPTY_TIME_INFO, EMPTY_TIME_INFO); + command.setData(model); + command.execute(); + } + return new AddCommand(matcher.group("name"), matcher.group("dueDate"), EMPTY_TIME_INFO, EMPTY_TIME_INFO, numOfOccurrence); + + case "day": case "daily": + for (int i=1; i < numOfOccurrence; i++) { + formattedDueDate = dateIncrement(initialDueDate, DAY_INCREMENT, i); + + Command command = new AddCommand(matcher.group("name"), formattedDueDate, EMPTY_TIME_INFO, EMPTY_TIME_INFO); + command.setData(model); + command.execute(); + } + return new AddCommand(matcher.group("name"), matcher.group("dueDate"), EMPTY_TIME_INFO, EMPTY_TIME_INFO, numOfOccurrence); + + default: + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); + } + + } else { // Recurring Event + String occurrenceType = matcher.group("occurrenceType").trim().toLowerCase(); + DateTimeInfo startDateTimeInfo = new DateTimeInfo (matcher.group("startTime")); + startDateTimeInfo.checkDueDateOrStartTime(); + DateTimeInfo endDateTimeInfo = new DateTimeInfo (matcher.group("endTime")); + endDateTimeInfo.checkEndTime(startDateTimeInfo); + + Date initialStartTime = new DateTimeInfoParser(startDateTimeInfo.toString()).getParsedDateTime(); + Date initialEndTime = new DateTimeInfoParser(endDateTimeInfo.toString()).getParsedDateTime(); + + switch (occurrenceType.toLowerCase()) { + case "week": case "weekly": + for (int i=1; i < numOfOccurrence; i++) { + + formattedStartTime = dateIncrement(initialStartTime, WEEK_INCREMENT, i); + formattedEndTime = dateIncrement(initialEndTime, WEEK_INCREMENT, i); + + Command command = new AddCommand(matcher.group("name"), EMPTY_TIME_INFO, formattedStartTime, formattedEndTime); + command.setData(model); + command.execute(); + } + return new AddCommand(matcher.group("name"), EMPTY_TIME_INFO, matcher.group("startTime"), matcher.group("endTime"), numOfOccurrence); + + case "mth": case "month": case "monthly": + for (int i=1; i < numOfOccurrence; i++) { + formattedStartTime = dateIncrement(initialStartTime, MONTH_INCREMENT, i); + formattedEndTime = dateIncrement(initialEndTime, MONTH_INCREMENT, i); + + Command command = new AddCommand(matcher.group("name"), EMPTY_TIME_INFO, formattedStartTime, formattedEndTime); + command.setData(model); + command.execute(); + } + return new AddCommand(matcher.group("name"), EMPTY_TIME_INFO, matcher.group("startTime"), matcher.group("endTime"), numOfOccurrence); + + case "day": case "daily": + for (int i=1; i < numOfOccurrence; i++){ + + formattedStartTime = dateIncrement(initialStartTime, DAY_INCREMENT, i); + formattedEndTime = dateIncrement(initialEndTime, DAY_INCREMENT, i); + + Command command = new AddCommand(matcher.group("name"), EMPTY_TIME_INFO, formattedStartTime, formattedEndTime); + command.setData(model); + command.execute(); + } + return new AddCommand(matcher.group("name"), EMPTY_TIME_INFO, matcher.group("startTime"), matcher.group("endTime"), numOfOccurrence); + + default: + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); + } + } + } + //@@author + + //@@author A0147092E + /** + * Increment a given date by number of day(s)/month(s)/week(s) + * + * @param initialDate + * @param incrementType + * @param incrementAmt + * @return formatted date in String Format + */ + public String dateIncrement(Date initialDate, int incrementType, int incrementAmt) { + final int DAYS_PER_WEEK = 7; + Date newDate; + + Calendar calendar = Calendar.getInstance(); + Date defaultDate = calendar.getTime(); + calendar.setTime(initialDate); + + switch (incrementType) { + case 1: // increment by day + calendar.add(Calendar.DATE, incrementAmt); + newDate = calendar.getTime(); + return new SimpleDateFormat("MM-dd-yyyy HHmmss").format(newDate); + + case 2: // increment by week + calendar.add(Calendar.DATE, DAYS_PER_WEEK * incrementAmt); + newDate = calendar.getTime(); + return new SimpleDateFormat("MM-dd-yyyy HHmmss").format(newDate); + + case 3: // increment by month + calendar.add(Calendar.MONTH, incrementAmt); + newDate = calendar.getTime(); + return new SimpleDateFormat("MM-dd-yyyy HHmmss").format(newDate); + + default: + return new SimpleDateFormat("MM-dd-yyyy HHmmss").format(defaultDate); + } + } + //@@author + + /** + * 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) { + + Optional index = parseIndex(args); + if (!index.isPresent()) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE)); + } + + return new DeleteCommand(index.get()); + } + + /** + * Returns the specified index in the {@code command} IF a positive unsigned + * integer is given as the index. Returns an {@code Optional.empty()} + * otherwise. + */ + private Optional parseIndex(String command) { + final Matcher matcher = TASK_INDEX_ARGS_FORMAT.matcher(command.trim()); + if (!matcher.matches()) { + return Optional.empty(); + } + + String index = matcher.group("targetIndex"); + if (!StringUtil.isUnsignedInteger(index)) { + return Optional.empty(); + } + return Optional.of(Integer.parseInt(index)); + + } + + /** + * Parses arguments in the context of the find task command. + * + * @param args (full command args string) + * @return the prepared command + */ + public static 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)); + } + + if (matcher.group("keywords").contains("f/") || matcher.group("keywords").contains("F/")) { + return new FindCommand(matcher.group("keywords").trim()); + } + + 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/flexitrack/model/FlexiTrack.java b/src/main/java/seedu/flexitrack/model/FlexiTrack.java new file mode 100644 index 000000000000..d11184868501 --- /dev/null +++ b/src/main/java/seedu/flexitrack/model/FlexiTrack.java @@ -0,0 +1,345 @@ +package seedu.flexitrack.model; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +import javafx.collections.ObservableList; +import seedu.flexitrack.commons.exceptions.IllegalValueException; +import seedu.flexitrack.model.task.DateTimeInfo; +import seedu.flexitrack.model.task.ReadOnlyTask; +import seedu.flexitrack.model.task.Task; +import seedu.flexitrack.model.task.UniqueTaskList; +import seedu.flexitrack.model.task.UniqueTaskList.DuplicateTaskException; +import seedu.flexitrack.model.task.UniqueTaskList.IllegalEditException; +import seedu.flexitrack.model.task.UniqueTaskList.TaskNotFoundException; + +/** + * Wraps all data at the task-tracker level Duplicates are not allowed (by + * .equals comparison) + */ +public class FlexiTrack implements ReadOnlyFlexiTrack { + + private final UniqueTaskList task; + private UniqueTaskList blockList; + + { + task = new UniqueTaskList(); + } + + public FlexiTrack() { + } + + /** + * Tasks are copied into this taskstracker + */ + public FlexiTrack(ReadOnlyFlexiTrack toBeCopied) { + this(toBeCopied.getUniqueTaskList()); + } + + /** + * Tasks are copied into this taskstracker + */ + public FlexiTrack(UniqueTaskList tasks) { + resetData(tasks.getInternalList()); + } + + public static ReadOnlyFlexiTrack getEmptyFlexiTrack() { + return new FlexiTrack(); + } + + //// list overwrite operations + + public ObservableList getTasks() { + return task.getInternalList(); + } + + public void setTasks(List tasks) { + this.task.getInternalList().setAll(tasks); + } + + public void resetData(Collection newTasks) { + setTasks(newTasks.stream().map(Task::new).collect(Collectors.toList())); + } + + public void resetData(ReadOnlyFlexiTrack newData) { + resetData(newData.getTaskList()); + } + + //// task-level operations + + /** + * Adds a task to the tasks tracker. + * + * @throws UniqueTaskList.DuplicateTaskException + * if an equivalent task already exists. + */ + public void addTask(Task p) throws DuplicateTaskException { + task.add(p); + } + + //@@author A0127855W + /** + * Edits a Task in the tasks tracker. + * + * @throws UniqueTaskList.DuplicateTaskException if an equivalent task already exists. + * @throws TaskNotFoundException if specified task is not found. + */ + public Task editTask(ReadOnlyTask taskToEdit, String[] args) + throws IllegalEditException, IllegalValueException { + return task.edit(taskToEdit, args); + } + //@@author + + public boolean removeTask(ReadOnlyTask key) throws UniqueTaskList.TaskNotFoundException { + if (task.remove(key)) { + return true; + } else { + throw new UniqueTaskList.TaskNotFoundException(); + } + } + + //// util methods + + @Override + public String toString() { + return task.getInternalList().size() + " tasks."; + // TODO: refine later + } + + @Override + public List getTaskList() { + return Collections.unmodifiableList(task.getInternalList()); + } + + @Override + public UniqueTaskList getUniqueTaskList() { + return this.task; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof FlexiTrack // instanceof handles nulls + && this.task.equals(((FlexiTrack) other).task)); + } + + //@@author A0127855W + /** + * Sorts the flexitrack according to the ReadOnlyTask comparator + */ + public void sort(){ + task.sort(); + } + + //@@author + @Override + public int hashCode() { + // use this method for custom fields hashing instead of implementing + // your own + return Objects.hash(task); + } + + //@@author A0138455Y + /** + * + * @param targetIndex is non-negative integer + * @return + * @throws IllegalValueException + */ + public Task markTask(ReadOnlyTask targetIndex) throws IllegalValueException { + return task.mark(targetIndex, Boolean.TRUE); + } + + /** + * + * @param targetIndex is non-negative integer + * @return + * @throws IllegalValueException + */ + public Task unmarkTask(ReadOnlyTask targetIndex) throws IllegalValueException { + return task.mark(targetIndex, Boolean.FALSE); + } + + /** + * + * @param toCheck is a Task Object and not null + * @return return ture if this task is not happen in any Block list slot + * @throws DuplicateTaskException + */ + public boolean checkBlock(Task toCheck) throws DuplicateTaskException { + setBlockList(); + //System.out.println("Checking block"); + if(blockList.getInternalList().size()==0) { + //System.out.println("no block found!"); + return false; + } + for(Task forCheck: blockList) { + if(compareDate(toCheck,forCheck)) { + //System.out.println("222 "+forCheck.toString()); + return true; + } + } + //System.out.println("compareed then return false"); + return false; + } + + /** + * + * @param toCheck is a Task Object and not null + * @return true if there is an event happen in the same timing + */ + public boolean checkOverlapEvent(Task toCheck) { + if(task.getInternalList().size()==0) { + return false; + } + for(Task forCheck: task) { + if(compareDate(toCheck,forCheck)) { + return true; + } + } + return false; + } + + /** + * + * @param toCheck + * @param blockDate + * @return ture if 2 event overlapping each other + */ + private boolean compareDate(Task toCheck, Task blockDate) { + Date start1 = toCheck.getStartTime().getTimeInfo().getTimingInfo().getDates().get(0); + Date start2 = blockDate.getStartTime().getTimeInfo().getTimingInfo().getDates().get(0); + Date end1 = toCheck.getEndTime().getTimeInfo().getTimingInfo().getDates().get(0); + Date end2 = blockDate.getEndTime().getTimeInfo().getTimingInfo().getDates().get(0); + // a KKKKKKK from/ 25 dec 3pm to/ 25 dec 6pm + // a LLLLLLL from/ 25 dec 3pm to/ 6pm + //System.out.println("\n"+toCheck.getName().toString()+" --start time : " + start1.toString() +"and end time : " + end1.toString() + // +"\n" +blockDate.getName().toString() +" start time : " + start2.toString() + "and end time : " + end2.toString()); + if((start1.compareTo(start2)>=0 && start1.compareTo(end2)<0) || + (end1.compareTo(start2)>=0 && end1.compareTo(end2)<0)) { + return true; + } + return false; + } + + /** + * return a list of Block task + * @throws DuplicateTaskException + */ + private void setBlockList() throws DuplicateTaskException { + blockList = new UniqueTaskList(); + for(Task toAdd: task) { + if(toAdd.getName().toString().contains("(Blocked) ")) { + blockList.add(toAdd); + } + } + } + //@@author + + // @@author A0127686R + /** + * Find the next available time slots that has minimum gap as specified by + * the users. If there are less gap then specified, return the starting time + * where there are no more events + * + * @param keyword Represent the keyword in number. 0 represent minute and 4 represent years + * @param length The length of the duration + * @param numberOfSlot The number of slot to be found + * @return The list of dates where gap are available + */ + public List findNextAvailableSlots(int keyword, int length, int numberOfSlot) { + DateTimeInfo dateNow = DateTimeInfo.getCurrentTime(); + List listOfPossibleTiming = new ArrayList(); + + for (Task task : task.getInternalList()) { + + if (listOfPossibleTiming.size() > (numberOfSlot * 2 - 1)) { + return listOfPossibleTiming; + } + + if (canTheGapBeFound(task, dateNow, keyword, length)) { + listOfPossibleTiming.add(dateNow); + listOfPossibleTiming.add(task.getStartTime()); + dateNow = task.getEndTime(); + } + + if (dateNow.isInTheFuture(task.getEndTime())) { + dateNow = task.getEndTime(); + } + } + listOfPossibleTiming.add(dateNow); + return listOfPossibleTiming; + } + + /** + * Process the data to see if there is any gap available + * + * @param task The current task of interest + * @param dateNow The current Date + * @param keyword Represent the keyword in number. 0 represent minute and 4 represent years + * @param length The length of the duration + * @return true when there is an available gap + */ + private boolean canTheGapBeFound(Task task, DateTimeInfo dateNow, int keyword, int length) { + if (task.getIsEvent() && dateNow.isInTheFuture(task.getStartTime())) { + return isTheLengthOfTheGapSatisfied(task, dateNow, keyword, length); + } + return false; + } + + /** + * Process the task data to calculate if there is enough gap between the events + * + * @param task The current task of interest + * @param dateNow The current date + * @param keyword Represent the keyword in number. 0 represent minute and 4 represent years + * @param length The length of the duration + * @return True if the event starts after the current timing. + */ + private boolean isTheLengthOfTheGapSatisfied(Task task, DateTimeInfo dateNow, int keyword, int length) { + int[] differenceInTime = new int[5]; + differenceInTime = DateTimeInfo.durationBetweenTwoTiming(dateNow.toString(), task.getStartingTimeInString()); + if (differenceInTime[0] >= 0) { + differenceInTime[keyword] = differenceInTime[keyword] - length; + return doesTheGapAtLeastAsLongAsTimingSpecified(keyword, differenceInTime); + } + return false; + } + + /** + * Decide if the gap between two timing is long enough to satisfied the user + * + * @param keyword Represent the keyword in number. 0 represent minute and 4 represent years + * @param differenceInTime The difference in time + * @return True if the gap is longer than what user specified. + */ + private boolean doesTheGapAtLeastAsLongAsTimingSpecified(int keyword, int[] differenceInTime) { + int countNumberOfZero = 0; + for (int i = keyword; i < 5; i++) { + if (differenceInTime[i] > 0) { + return true; + } else if (differenceInTime[i] == 0) { + countNumberOfZero = countNumberOfZero + 1; + } + } + return (countNumberOfZero == (5 - keyword)) ? true : false; + } + + //@@ A0127686R + /** + * Find the index of a task given + * + * @param task The task of interest + * @return The index of the task in int + */ + public int findIndexOfTask(Task task) { + return this.task.findIndexOfTask(task); + + } + +} diff --git a/src/main/java/seedu/flexitrack/model/Model.java b/src/main/java/seedu/flexitrack/model/Model.java new file mode 100644 index 000000000000..2203438b58c6 --- /dev/null +++ b/src/main/java/seedu/flexitrack/model/Model.java @@ -0,0 +1,118 @@ +package seedu.flexitrack.model; + +import java.util.List; +import java.util.Set; + +import seedu.flexitrack.commons.core.UnmodifiableObservableList; +import seedu.flexitrack.commons.exceptions.IllegalValueException; +import seedu.flexitrack.model.task.DateTimeInfo; +import seedu.flexitrack.model.task.ReadOnlyTask; +import seedu.flexitrack.model.task.Task; +import seedu.flexitrack.model.task.UniqueTaskList; +import seedu.flexitrack.model.task.UniqueTaskList.DuplicateTaskException; +import seedu.flexitrack.model.task.UniqueTaskList.TaskNotFoundException; + +/** + * The API of the Model component. + */ +public interface Model { + /** + * Clears existing backing model and replaces with the provided new data. + */ + void resetData(ReadOnlyFlexiTrack newData); + + /** Returns the FLexiTrack */ + ReadOnlyFlexiTrack getFlexiTrack(); + + /** Deletes the given task. */ + void deleteTask(ReadOnlyTask target) throws UniqueTaskList.TaskNotFoundException; + + /** Adds the given task */ + void addTask(Task task) throws DuplicateTaskException; + + /** + * Returns the filtered task list as an + * {@code UnmodifiableObservableList} + */ + UnmodifiableObservableList getFilteredTaskList(); + + /** Updates the filter of the filtered task list to show all tasks */ + void updateFilteredListToShowAll(); + + //@@author A0127686R + /** Updates the filter of the filtered task list to show future tasks */ + void updateFilteredListToFitUserInput( String args ); + + //@@author + /** + * Updates the filter of the filtered task list to filter by the given + * keywords + */ + void updateFilteredTaskList(Set keywords); + + //@@author A0138455Y + /** + * Marks the given task as done + * + * @throws TaskNotFoundException + */ + Task markTask(ReadOnlyTask taskToMark) throws IllegalValueException; + + /** + * Unmarks the given task + * + * @throws TaskNotFoundException + */ + Task unmarkTask(ReadOnlyTask taskToMark) throws IllegalValueException; + //@@author + + //@@author A0127855W + /** + * Edits the given task + * + * @throws TaskNotFoundException + */ + Task editTask(ReadOnlyTask taskToEdit, String[] args) + throws UniqueTaskList.IllegalEditException, IllegalValueException; + + //@@author + + //@@author A0138455Y + /** + * + * @param Task toAdd + * @return true if new event want to place at a period that reserve for other event + */ + boolean checkBlock(Task toAdd) throws DuplicateTaskException; + + /** + * Limitation: only allow user change path within Flexitrack folder + * @param storagePath : only accept alpha or forward slash '/' (example: data/newStorage) + * storagePath should not include ".xml" + */ + void changeStorage(String storagePath); + + /** + * + * @param Task toAdd + * @return true if new event overLapping other event + */ + boolean checkOverlapEvent(Task toAdd); + //@@author + + // @@author A0127686R + /** + * Refresh the TaskList Panel to reflect the changes made + */ + void indicateFlexiTrackerChanged(); + + /** + * Find gap with length and number of slots specified by the user + * @param keyword Represent the keyword in number. 0 represent minute and 4 represent years + * @param length The length of the duration + * @param numberOfSlot The number of slot to be found + * @return The list of the Starting time and ending time for each gap found + */ + List findSpecifiedGapTiming(int keyword, int length, int numberOfSlot); + +} diff --git a/src/main/java/seedu/flexitrack/model/ModelManager.java b/src/main/java/seedu/flexitrack/model/ModelManager.java new file mode 100644 index 000000000000..cd8e0a94fcef --- /dev/null +++ b/src/main/java/seedu/flexitrack/model/ModelManager.java @@ -0,0 +1,382 @@ +package seedu.flexitrack.model; + +import java.util.List; +import java.util.Set; +import java.util.logging.Logger; + +import javafx.collections.transformation.FilteredList; +import seedu.flexitrack.MainApp; +import seedu.flexitrack.commons.core.ComponentManager; +import seedu.flexitrack.commons.core.LogsCenter; +import seedu.flexitrack.commons.core.UnmodifiableObservableList; +import seedu.flexitrack.commons.events.model.FlexiTrackChangedEvent; +import seedu.flexitrack.commons.events.ui.StoragePathChangeEvent; +import seedu.flexitrack.commons.exceptions.IllegalValueException; +import seedu.flexitrack.commons.events.ui.JumpToListRequestEvent; +import seedu.flexitrack.commons.util.StringUtil; +import seedu.flexitrack.logic.commands.ListCommand; +import seedu.flexitrack.model.task.DateTimeInfo; +import seedu.flexitrack.model.task.ReadOnlyTask; +import seedu.flexitrack.model.task.Task; +import seedu.flexitrack.model.task.UniqueTaskList.DuplicateTaskException; +import seedu.flexitrack.model.task.UniqueTaskList.IllegalEditException; +import seedu.flexitrack.model.task.UniqueTaskList.TaskNotFoundException; +import seedu.flexitrack.ui.UiManager; + +/** + * Represents the in-memory model of the tasktracker 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 FlexiTrack flexiTracker; + private final FilteredList filteredTasks; + + /** + * Initializes a ModelManager with the given FlexiTracker FlexiTracker and + * its variables should not be null + */ + public ModelManager(FlexiTrack src, UserPrefs userPrefs) { + super(); + assert src != null; + assert userPrefs != null; + + logger.fine("Initializing with tasktracker: " + src + " and user prefs " + userPrefs); + + flexiTracker = new FlexiTrack(src); + filteredTasks = new FilteredList<>(flexiTracker.getTasks()); + } + + public ModelManager() { + this(new FlexiTrack(), new UserPrefs()); + } + + public ModelManager(ReadOnlyFlexiTrack initialData, UserPrefs userPrefs) { + flexiTracker = new FlexiTrack(initialData); + filteredTasks = new FilteredList<>(flexiTracker.getTasks()); + indicateFlexiTrackerChanged(); + } + + @Override + public void resetData(ReadOnlyFlexiTrack newData) { + flexiTracker.resetData(newData); + updateFilteredListToShowAll(); + indicateFlexiTrackerChanged(); + } + + @Override + public ReadOnlyFlexiTrack getFlexiTrack() { + return flexiTracker; + } + + /** Raises an event to indicate the model has changed */ + public void indicateFlexiTrackerChanged() { + flexiTracker.sort(); + raise(new FlexiTrackChangedEvent(flexiTracker)); + } + + @Override + public synchronized void deleteTask(ReadOnlyTask target) throws TaskNotFoundException { + flexiTracker.removeTask(target); + indicateFlexiTrackerChanged(); + } + + @Override + public synchronized void addTask(Task task) throws DuplicateTaskException { + flexiTracker.addTask(task); + updateFilteredListToShowAll(); + indicateFlexiTrackerChanged(); + jumpToATask(task); + } + + //@@ author A0127686R + /** + * Move the Panel list to the task that was just changed + * + * @param task To be Shown in the Panel List + */ + private void jumpToATask(Task task) { + assert task != null; + int index = flexiTracker.findIndexOfTask(task); + if (index == -1) { + logger.warning("----------------[ModelManager][" + " TASK CANT BE FOUND " + "]"); + } + JumpToListRequestEvent jump = new JumpToListRequestEvent(index); + raise(jump); + } + + //@@author A0138455Y + @Override + public Task markTask(ReadOnlyTask targetIndex) throws IllegalValueException { + Task markedTask = flexiTracker.markTask(targetIndex); + indicateFlexiTrackerChanged(); + jumpToATask(markedTask); + return markedTask; + } + + @Override + public Task unmarkTask(ReadOnlyTask targetIndex) throws IllegalValueException { + Task unMarkedTask = flexiTracker.unmarkTask(targetIndex); + indicateFlexiTrackerChanged(); + jumpToATask(unMarkedTask); + return unMarkedTask; + } + + @Override + /* + * raise the storage path change event + * + */ + public void changeStorage(String storagePath) { + raise(new StoragePathChangeEvent(storagePath)); + } + + //@@author + + //@@author A0127855W + @Override + /** + * Edits a Task in the tasks tracker. + * + * @throws UniqueTaskList.DuplicateTaskException if an equivalent task already exists. + * @throws TaskNotFoundException if specified task is not found. + */ + public Task editTask(ReadOnlyTask taskToEdit, String[] args) + throws IllegalEditException, IllegalValueException { + Task editedTask = flexiTracker.editTask(taskToEdit, args); + indicateFlexiTrackerChanged(); + jumpToATask(editedTask); + return editedTask; + } + + + //@@author A0138455Y + @Override + public boolean checkBlock(Task toCheck) throws DuplicateTaskException { + return flexiTracker.checkBlock(toCheck); + } + + @Override + public boolean checkOverlapEvent(Task toCheck) { + return flexiTracker.checkOverlapEvent(toCheck); + } + //@@author + + @Override + public List findSpecifiedGapTiming(int keyword, int length, int numberOfSlot) { + return flexiTracker.findNextAvailableSlots(keyword, length, numberOfSlot); + } + + // =========== Filtered Tasks List Accessors + // =============================================================== + + @Override + public UnmodifiableObservableList getFilteredTaskList() { + return new UnmodifiableObservableList<>(filteredTasks); + } + + @Override + public void updateFilteredListToShowAll() { + filteredTasks.setPredicate(null); + } + + //@@author A0127686R + @Override + public void updateFilteredListToFitUserInput(String args){ + updateFilteredTaskList(new PredicateExpression(new DateQualifier(args))); + } + + //@@author + @Override + public void updateFilteredTaskList(Set keywords) { + updateFilteredTaskList(new PredicateExpression(new NameQualifier(keywords))); + } + + private void updateFilteredTaskList(Expression expression) { + filteredTasks.setPredicate(expression::satisfies); + } + + // ========== 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(); + } + + private class NameQualifier implements Qualifier { + private Set nameKeyWords; + + NameQualifier(Set nameKeyWords) { + this.nameKeyWords = nameKeyWords; + } + + @Override + public boolean run(ReadOnlyTask task) { + if (nameKeyWords.toString().contains("f/")) { + return nameKeyWords.stream() + .filter(keyword -> StringUtil.equalsIgnoreCase(task.getName().getNameOnly(), keyword)).findAny() + .isPresent(); + } + + return nameKeyWords.stream() + .filter(keyword -> StringUtil.containsIgnoreCase(task.getName().getNameOnly(), keyword)).findAny() + .isPresent(); + } + + @Override + public String toString() { + return "name=" + String.join(", ", nameKeyWords); + } + } + + // @@author A0127686R + /** + * Helper class to process if a particular task should be shown on the list + */ + private class DateQualifier implements Qualifier { + private String keyWords; + private String dateInfo; + + DateQualifier(String keyWord) { + this.keyWords = keyWord; + this.dateInfo = trimKeyWords(keyWord); + } + + /** + * Delete all the list command that is not date from the keywords + * + * @param keyrordArgs A string containing the keywords and other input + * @return The trimmed keyword in a string + */ + private String trimKeyWords(String keyrordArgs) { + return keyrordArgs.replace(ListCommand.LIST_FUTURE_COMMAND, "").replace(ListCommand.LIST_PAST_COMMAND, "") + .replace(ListCommand.LIST_UNMARK_COMMAND, "").replace(ListCommand.LIST_MARK_COMMAND, "") + .replace(ListCommand.LIST_LAST_WEEK_COMMAND, "").replace(ListCommand.LIST_LAST_MONTH_COMMAND, "") + .replace(ListCommand.LIST_NEXT_WEEK_COMMAND, "").replace(ListCommand.LIST_NEXT_MONTH_COMMAND, "") + .replace(ListCommand.LIST_BLOCK_COMMAND, "").trim(); + } + + @Override + public boolean run(ReadOnlyTask task) { + + if (!isTaskGoingToBeShown(task)) { + return false; + } + + if (keyWords.contains(ListCommand.LIST_UNMARK_COMMAND)) { + return !task.getIsDone(); + } else if (keyWords.contains(ListCommand.LIST_MARK_COMMAND)) { + return task.getIsDone(); + } else if (keyWords.contains(ListCommand.LIST_BLOCK_COMMAND)) { + return task.getIsBlock(); + } + + + return isTaskGoingToBeShown(task); + + } + + /** + * Check of the task will be shown to the user in the panel list + * + * @param task The current task of interest + * @return True if the task met all the requirement and will be shown + */ + private boolean isTaskGoingToBeShown(ReadOnlyTask task) { + if (keyWords.contains(ListCommand.LIST_FUTURE_COMMAND)) { + return isTaskInTheFuture(task); + } else if (keyWords.contains(ListCommand.LIST_PAST_COMMAND)) { + return isTaskInThePast(task); + } else if (keyWords.contains(ListCommand.LIST_LAST_COMMAND) + || keyWords.contains(ListCommand.LIST_NEXT_COMMAND)) { + return isTaskWithinTheSpecifiedTiming(task); + } else if (!dateInfo.equals("")) { + return doesTaskCrossTheParticularStatedDate(task); + } else { + return true; + } + } + + /** + * Process If the task happens on a particular date + * + * @param task The current task of interest + * @return True if task contain or cross the date + */ + private boolean doesTaskCrossTheParticularStatedDate(ReadOnlyTask task) { + DateTimeInfo dateTimeInfo=null; + try { + dateTimeInfo = new DateTimeInfo (dateInfo); + } catch (IllegalValueException e) { + logger.warning("----------------[ModelManager][" + " THIS ERROR SHOULD NOT OCCUR. METHOD USING A VALID INPUT " + "]"); + } + return dateTimeInfo.isOnTheDate(task); + } + + /** + * Process if the task happens between now and the time stated + * + * @param task + * The current task of interest + * @return True if task is within the stated time + */ + private boolean isTaskWithinTheSpecifiedTiming(ReadOnlyTask task) { + return DateTimeInfo.withInTheDuration(keyWords, task, DateTimeInfo.getCurrentTime().toString()); + } + + /** + * Process if a particular task has passed + * + * @param task The current task of interest + * @return True if it has passed + */ + private boolean isTaskInThePast(ReadOnlyTask task) { + return DateTimeInfo.getCurrentTime().isInThePast(task.getEndingTimeOrDueDate()); + } + + /** + * Process if a particular task has not passed yet + * + * @param task The current task of interest + * @return True if it has not passed yet + */ + private boolean isTaskInTheFuture(ReadOnlyTask task) { + if (task.getIsNotFloatingTask()) { + return DateTimeInfo.getCurrentTime().isInTheFuture(task.getEndingTimeOrDueDate()); + } else { + return !task.getIsDone(); + } + } + + } + + +} diff --git a/src/main/java/seedu/flexitrack/model/ReadOnlyFlexiTrack.java b/src/main/java/seedu/flexitrack/model/ReadOnlyFlexiTrack.java new file mode 100644 index 000000000000..a3d0e6ab76eb --- /dev/null +++ b/src/main/java/seedu/flexitrack/model/ReadOnlyFlexiTrack.java @@ -0,0 +1,21 @@ +package seedu.flexitrack.model; + +import java.util.List; + +import seedu.flexitrack.model.task.ReadOnlyTask; +import seedu.flexitrack.model.task.UniqueTaskList; + +/** + * Unmodifiable view of an address book + */ +public interface ReadOnlyFlexiTrack{ + + + UniqueTaskList getUniqueTaskList(); + + /** + * Returns an unmodifiable view of persons list + */ + List getTaskList(); + +} diff --git a/src/main/java/seedu/address/model/UserPrefs.java b/src/main/java/seedu/flexitrack/model/UserPrefs.java similarity index 76% rename from src/main/java/seedu/address/model/UserPrefs.java rename to src/main/java/seedu/flexitrack/model/UserPrefs.java index da9c8037f495..21482df34c7b 100644 --- a/src/main/java/seedu/address/model/UserPrefs.java +++ b/src/main/java/seedu/flexitrack/model/UserPrefs.java @@ -1,9 +1,9 @@ -package seedu.address.model; - -import seedu.address.commons.core.GuiSettings; +package seedu.flexitrack.model; import java.util.Objects; +import seedu.flexitrack.commons.core.GuiSettings; + /** * Represents User's preferences. */ @@ -19,7 +19,7 @@ public void updateLastUsedGuiSetting(GuiSettings guiSettings) { this.guiSettings = guiSettings; } - public UserPrefs(){ + public UserPrefs() { this.setGuiSettings(500, 500, 0, 0); } @@ -29,14 +29,14 @@ public void setGuiSettings(double width, double height, int x, int y) { @Override public boolean equals(Object other) { - if (other == this){ + if (other == this) { return true; } - if (!(other instanceof UserPrefs)){ //this handles null as well. + if (!(other instanceof UserPrefs)) { // this handles null as well. return false; } - UserPrefs o = (UserPrefs)other; + UserPrefs o = (UserPrefs) other; return Objects.equals(guiSettings, o.guiSettings); } @@ -47,7 +47,7 @@ public int hashCode() { } @Override - public String toString(){ + public String toString() { return guiSettings.toString(); } diff --git a/src/main/java/seedu/flexitrack/model/task/DateTimeInfo.java b/src/main/java/seedu/flexitrack/model/task/DateTimeInfo.java new file mode 100644 index 000000000000..e209e107b83e --- /dev/null +++ b/src/main/java/seedu/flexitrack/model/task/DateTimeInfo.java @@ -0,0 +1,557 @@ +//@@author A0127686R +package seedu.flexitrack.model.task; + +import java.util.Date; +import java.util.List; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.joestelmach.natty.DateGroup; + +import seedu.flexitrack.commons.core.LogsCenter; +import seedu.flexitrack.commons.exceptions.IllegalValueException; +import seedu.flexitrack.logic.commands.ListCommand; +import seedu.flexitrack.logic.parser.Parser; + +/** + * Represents a DateTimeInfo class in FlexiTrack + */ +public class DateTimeInfo implements Comparable { + public static final String MESSAGE_DATETIMEINFO_CONSTRAINTS = "Invalid time inputed. Please check your spelling!"; + public static final String MESSAGE_FROM_IS_AFTER_TO = "Please check the timing inputed! The given starting time is after the ending time."; + + private static final Pattern TIME_TYPE_DATA_ARGS_FORMAT = Pattern.compile("(?.+)"); + private static final int AVERAGE_DAYS_IN_A_MONTH = 30; + private static final int DAYS_IN_A_WEEK = 7; + private static final boolean FAIL_DUE_TO_EXCEPTION = false; + + private final Logger logger = LogsCenter.getLogger(Parser.class); + + private String setTime; + private DateTimeInfoParser timeInfo; + + public DateTimeInfo(String givenTime) throws IllegalValueException { + setDateGroupTime(givenTime); + } + + /** + * Set the setTime (DateGroup object) as the date inputed by the user + * + * @param givenTime + * @throws IllegalValueException + */ + public void setDateGroupTime(String givenTime) throws IllegalValueException { + assert givenTime != null; + final Matcher matcher = TIME_TYPE_DATA_ARGS_FORMAT.matcher(givenTime.trim()); + matcher.matches(); + DateTimeInfoParser parsedTiming; + try { + parsedTiming = new DateTimeInfoParser(matcher.group("info")); + timeInfo = parsedTiming; + this.setTime = parsedTiming.getParsedTimingInfo(); + formatTimingIntoString(); + } catch (IllegalValueException e) { + throw new IllegalValueException(MESSAGE_DATETIMEINFO_CONSTRAINTS); + } + } + + /** + * A getter method for DateTimeInfoParser + * + * @return DateTimeInfoParser of the timing of interest + */ + public DateTimeInfoParser getTimeInfo() { + return timeInfo; + } + + private void formatTimingIntoString() { + this.setTime = getDateMonthYear() + " " + setTime.substring(12, 17); + } + + /** + * Change the format of the timing saved in setTime + * + */ + void formatStartOrDueDateTime() { + if (timeInfo.isTimeInferred()) { + setTime = setTime.substring(0, 12) + "08:00"; + changeTimeInfo(); + } + } + + /** + * If the time is inferred, replace "08:00" with "17:00" + */ + void formatEndTime(DateTimeInfo startTime) { + if (timeInfo.isDateInferred()) { + setTime = startTime.setTime.substring(0, 12) + setTime.substring(12); + } + if (timeInfo.isTimeInferred()) { + setTime = setTime.substring(0, 12) + "17:00"; + } + changeTimeInfo(); + } + + /** + * Update timeInfo after changing the setTime to the Default value + * + */ + private void changeTimeInfo() { + try { + timeInfo = new DateTimeInfoParser(setTime); + } catch (IllegalValueException e) { + logger.warning("----------------[DateTimeInfo][" + " THIS ERROR SHOULD NOT OCCUR. METHOD USING A VALID INPUT " + "]"); + } + } + + /** + * Extract the month, date and year of a particular date + * + * @return timing in MMM DD YYYY format + */ + private String getDateMonthYear() { + return setTime.substring(5, 12) + setTime.substring(25, 29); + } + + /** + * Validate the timing inputed + * + * @param test A date to be tested + * @return True if it is a valid timing + */ + public static boolean isValidDateTimeInfo(List test) { + return (!test.isEmpty()) ? true : false; + } + + /** + * Change the months which is specified in string to integer + * + * @param month The month in written 3 letters string + * @return Month in integer + */ + public static int whatMonth(String month) { + switch (month) { + case "Jan": + return 1; + case "Feb": + return 2; + case "Mar": + return 3; + case "Apr": + return 4; + case "May": + return 5; + case "Jun": + return 6; + case "Jul": + return 7; + case "Aug": + return 8; + case "Sep": + return 9; + case "Oct": + return 10; + case "Nov": + return 11; + case "Dec": + return 12; + default: + return 0; + } + } + + /** + * To check if the minute inputed in 'from' is before the minute inputed in + * 'to' + * + * @param starting + * Time + * @param ending + * Time + * @return the duration of the event + */ + public static String durationOfTheEvent(String startingTime, String endingTime) { + return timeDifferenceInString(durationBetweenTwoTiming(startingTime, endingTime)); + } + + /** + * prepare variables needed to calculate the duration between two timing + * + * @param startingTime + * @param endingTime + * @return the duration of the event in an array. 0 represents minutes and 4 + * represents years + */ + public static int[] durationBetweenTwoTiming(String startingTime, String endingTime) { + int years = differenceInYears(startingTime, endingTime); + int months = differenceInMonths(startingTime, endingTime); + int days = differenceInDays(startingTime, endingTime); + int hours = differenceInHours(startingTime, endingTime); + int minutes = differenceInMinutes(startingTime, endingTime); + + return combineDuratingOfEvent(years, months, days, hours, minutes); + } + + /** + * Put together the time difference that is represented in array into a String to be shown to the user + * + * @param timeDifference The Time difference between two timing + * @return String of the message to be shown to the users + */ + private static String timeDifferenceInString(int[] timeDifference) { + String duration = new String(""); + if (timeDifference[0] == -1) { + return MESSAGE_FROM_IS_AFTER_TO; + } else if (timeDifference[0] > 0) { + duration = " " + timeDifference[0] + " minute" + ((timeDifference[0] == 1) ? "" + duration : "s"); + } + if (timeDifference[1] > 0) { + duration = " " + timeDifference[1] + " hour" + ((timeDifference[1] == 1) ? "" + duration : "s" + duration); + } + if (timeDifference[2] > 0) { + duration = " " + timeDifference[2] + " day" + ((timeDifference[2] == 1) ? "" + duration : "s" + duration); + } + if (timeDifference[3] > 0) { + duration = " " + timeDifference[3] + " month" + ((timeDifference[3] == 1) ? "" + duration : "s" + duration); + } + if (timeDifference[4] > 0) { + duration = " " + timeDifference[4] + " year" + ((timeDifference[4] == 1) ? "" + duration : "s" + duration); + } + if (duration.equals("")) { + return "Event starts and end at the same time."; + } else { + return "Duration of the event is: " + duration.trim() + "."; + } + } + + /** + * Calculate the duration of the event + * + * @param years + * @param months + * @param days + * @param hours + * @param minutes + * @return the duration of the event in a string + */ + private static int[] combineDuratingOfEvent(int years, int months, int days, int hours, int minutes) { + int[] timeDifference = new int[5]; + + if (minutes < 0) { + minutes = Math.floorMod(minutes, 60); + hours = hours - 1; + } + timeDifference[0] = minutes; + if (hours < 0) { + hours = Math.floorMod(hours, 24); + days = days - 1; + } + timeDifference[1] = hours; + if (days < 0) { + days = Math.floorMod(days, 31); + months = months - 1; + } + timeDifference[2] = days; + if (months < 0) { + months = Math.floorMod(months, 12); + years = years - 1; + } + timeDifference[3] = months; + if (years < 0) { + timeDifference[0] = -1; + return timeDifference; + } else if (years > 0) { + timeDifference[4] = years; + } + return timeDifference; + + } + + /** + * Calculate the minute difference between the end and the start + * + * @param startingTime The starting time + * @param endingTime The ending time + * @return The minutes difference + */ + private static int differenceInMinutes(String startingTime, String endingTime) { + int startMinute = Integer.parseInt(startingTime.substring(15, 17)); + int endMinute = Integer.parseInt(endingTime.substring(15, 17)); + return endMinute - startMinute; + } + + /** + * Calculate the hour difference between the end and the start + * + * @param startingTime The starting time + * @param endingTime The ending time + * @return The hours difference + */ + private static int differenceInHours(String startingTime, String endingTime) { + int startHours = Integer.parseInt(startingTime.substring(12, 14)); + int endHours = Integer.parseInt(endingTime.substring(12, 14)); + return endHours - startHours; + } + + /** + * Calculate the day difference between the end and the start + * + * @param startingTime The starting time + * @param endingTime The ending time + * @return The days difference + */ + private static int differenceInDays(String startingTime, String endingTime) { + int startDate = Integer.parseInt(startingTime.substring(4, 6)); + int endDate = Integer.parseInt(endingTime.substring(4, 6)); + return endDate - startDate; + } + + /** + * Calculate the year difference between the end and the start + * + * @param startingTime The starting time + * @param endingTime The ending time + * @return The years difference + */ + private static int differenceInYears(String startingTime, String endingTime) { + int startYear = Integer.parseInt(startingTime.substring(7, 11)); + int endYear = Integer.parseInt(endingTime.substring(7, 11)); + return endYear - startYear; + } + + /** + * Calculate the month difference between the end and the start + * + * @param startingTime The starting time + * @param endingTime The ending time + * @return The months difference + */ + private static int differenceInMonths(String startingTime, String endingTime) { + String startMonth = startingTime.substring(0, 3); + String endMonth = endingTime.substring(0, 3); + int monthDifference = whatMonth(endMonth) - whatMonth(startMonth); + return monthDifference; + } + + @Override + public String toString() { + return setTime; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof DateTimeInfo // instanceof handles nulls + && this.setTime.equals(((DateTimeInfo) other).setTime)); // state check + } + + @Override + public int hashCode() { + return setTime.hashCode(); + } + + /** + * @return true if the date not specified; + */ + public boolean isDateNull() { + return this.setTime.equals("Feb 29 2000 00:00"); + } + + /** + * Process the task if the task is in the future + * + * @param Date + * Timing to be compared to the current timing + * @return True if the timing timeNow is after the timing Date + */ + public boolean isInTheFuture(DateTimeInfo Date) { + int[] duration = durationBetweenTwoTiming(this.toString(), Date.toString()); + return duration[0] != -1; + } + + /** + * Process the task if the task is in the past + * + * @param Date Timing to be compared to the current timing + * @return True if the timing timeNow is before the timing Date + */ + public boolean isInThePast(DateTimeInfo Date) { + return !this.isInTheFuture(Date); + } + + /** + * Prepare the keyword and process if the task is within the specified date. + * For a task the due date is the dateInfo date. For an event, the specified + * DateInfo date is within the event duration (inclusive the starting and + * the ending date) + * + * @param dateInfo The date of interest + * @param task The task of interest + * @return True if the task is within the specified date + */ + public boolean isOnTheDate(ReadOnlyTask task) { + return task.getDueDate().toString().contains(toString().substring(0, 12)) + || task.getEndTime().toString().contains(toString().substring(0, 12)) + || task.getStartTime().toString().contains(toString().substring(0, 12)) + || isTaskAnEventPassingThisDate(task, toString().substring(0, 12)); + } + + /** + * Process the data if it the task is a event and it is passing through the + * date specified. + * + * @param task The event of interest + * @param dateInfo The date of interest + * @return True if a task is an event and the day interest is within the + * starting date and the ending date + */ + static boolean isTaskAnEventPassingThisDate(ReadOnlyTask task, String dateInfo) { + if (!task.getIsEvent()) { + return false; + } + DateTimeInfo dateSpecified; + try { + dateSpecified = new DateTimeInfo(dateInfo); + return task.getStartTime().isInTheFuture(dateSpecified) && dateSpecified.isInTheFuture(task.getEndTime()); + } catch (IllegalValueException e) { + new IllegalValueException(MESSAGE_DATETIMEINFO_CONSTRAINTS); + } + return FAIL_DUE_TO_EXCEPTION; + } + + /** + * Process if the a task specified is with in the duration stated. + * + * @param keyWords The keywords of the list command enter by the user + * @param task the task of interest + * @return True if the date is within the duration + */ + public static boolean withInTheDuration(String keyWords, ReadOnlyTask task, String dateNow) { + boolean isWithInTime = false; + if (keyWords.contains(ListCommand.LIST_LAST_WEEK_COMMAND)) { + return isNotFloatingTaskAndWithinTheTime(task, dateNow, -DAYS_IN_A_WEEK); + } else if (keyWords.contains(ListCommand.LIST_LAST_MONTH_COMMAND)) { + return isNotFloatingTaskAndWithinTheTime(task, dateNow, -AVERAGE_DAYS_IN_A_MONTH); + } else if (keyWords.contains(ListCommand.LIST_NEXT_MONTH_COMMAND)) { + return isNotFloatingTaskAndWithinTheTime(task, dateNow, AVERAGE_DAYS_IN_A_MONTH); + } else if (keyWords.contains(ListCommand.LIST_NEXT_WEEK_COMMAND)) { + return isNotFloatingTaskAndWithinTheTime(task, dateNow, DAYS_IN_A_WEEK); + } + return isWithInTime; + } + + /** + * Process if the task given is either a deadline task or an event within + * the specified timing + * + * @param task The task of interest + * @param dateNow The current date + * @param expectedDays The longest timing accepted + * @return True if the task is not a floating task and it is within the + * specified timing + */ + private static boolean isNotFloatingTaskAndWithinTheTime(ReadOnlyTask task, String dateNow, int expectedDays) { + return (task.getIsNotFloatingTask()) + ? isTimeDifferenceLessThanSpecified(dateNow, task.getStartingTimeOrDueDate().toString(), expectedDays) + : false; + } + + /** + * Provide an easy access to the current timing in String + * + * @return String of the current time MMM DD YYYY HH:MM format. + */ + public static DateTimeInfo getCurrentTime() { + DateTimeInfo dateNow = null; + try { + dateNow = new DateTimeInfo("now"); + } catch (IllegalValueException e) { + assert false; + } + return dateNow; + } + + /** + * Process if the two given time is less than the time duration. + * + * @param startTime The starting time of the duration + * @param endTime The ending time of the duration + * @param limitTimeDuration The maximum duration allowed + * @return True if the time Difference is less than specified + */ + private static boolean isTimeDifferenceLessThanSpecified(String startTime, String endTime, int limitTimeDuration) { + if (limitTimeDuration < 0) { + limitTimeDuration = limitTimeDuration * (-1); + return isTimeDifferenceLessThanSpecified(endTime, startTime, limitTimeDuration); + } + + int years = differenceInYears(startTime, endTime); + int months = differenceInMonths(startTime, endTime); + int days = differenceInDays(startTime, endTime); + int hours = differenceInHours(startTime, endTime); + + return isTimeLessThanSpecified(limitTimeDuration, years, months, days, hours); + } + + /** + * Helper method to check the duration of a specified timing is with in the + * time duration + * + * @param limitTimeDuration The maximum time duration + * @param years The difference in years between the two timing + * @param months The difference in months between the two timing + * @param days The difference in days between the two timing + * @param hours The difference in hours between the two timing + * @return True if the time is within the specified timing + */ + private static boolean isTimeLessThanSpecified(int limitTimeDuration, int years, int months, int days, int hours) { + if (hours < 0) { + hours = Math.floorMod(hours, 24); + days = days - 1; + } + if (days < 0) { + days = Math.floorMod(days, 30); + months = months - 1; + } + if (months < 0 || months > 1) { + return false; + } + if (years < 0 || years > 0) { + return false; + } + if ((days <= limitTimeDuration && months == 0) + || (limitTimeDuration == AVERAGE_DAYS_IN_A_MONTH && months == 1)) { + return true; + } else { + return false; + } + } + + // @@ author + @Override + public int compareTo(DateTimeInfo dateTimeInfo2) { + Date dateObject1 = convertToDateObject(this.setTime); + Date dateObject2 = convertToDateObject(dateTimeInfo2.setTime); + return dateObject1.compareTo(dateObject2); + } + + private Date convertToDateObject(String dateString) { + try { + DateTimeInfoParser parsedTiming = new DateTimeInfoParser(dateString); + return parsedTiming.getParsedDateTime(); + } catch (IllegalValueException ive) { + return null; + } + + } + + public void checkDueDateOrStartTime() { + this.formatStartOrDueDateTime(); + } + + public void checkEndTime(DateTimeInfo startTime) { + this.formatEndTime(startTime); + } + +} diff --git a/src/main/java/seedu/flexitrack/model/task/DateTimeInfoParser.java b/src/main/java/seedu/flexitrack/model/task/DateTimeInfoParser.java new file mode 100644 index 000000000000..6cd143b1021e --- /dev/null +++ b/src/main/java/seedu/flexitrack/model/task/DateTimeInfoParser.java @@ -0,0 +1,62 @@ +package seedu.flexitrack.model.task; + +import java.util.Date; +import java.util.List; + +import com.joestelmach.natty.DateGroup; +import com.joestelmach.natty.Parser; + +import seedu.flexitrack.commons.exceptions.IllegalValueException; + +//@@author A0127686R +/** + * Represents the Parser and Time related DateTimeInfo. + */ +public class DateTimeInfoParser { + + public static final String MESSAGE_DATETIMEINFO_CONSTRAINTS = "Invalid time inputed. Please check your spelling!"; + + private DateGroup timingInfo; + + public DateTimeInfoParser(String givenTime) throws IllegalValueException { + try { + Parser parser = new Parser(); + List dateParser = parser.parse(givenTime); + setTimingInfo(dateParser); + } catch (IndexOutOfBoundsException e) { + throw new IllegalValueException(MESSAGE_DATETIMEINFO_CONSTRAINTS); + } + } + + /** + * Set the timingInfo variable with the time specified + * + * @param dateParser + */ + private void setTimingInfo(List dateParser) { + this.timingInfo = dateParser.get(0); + } + + public String getParsedTimingInfo() { + return timingInfo.getDates().toString(); + } + + public boolean isTimeInferred() { + return timingInfo.isTimeInferred(); + } + + public boolean isDateInferred() { + return timingInfo.isDateInferred(); + } + + //@@author A0147092E + public Date getParsedDateTime() { + return timingInfo.getDates().get(0); + } + + //@@author A0138455Y + public DateGroup getTimingInfo() { + return timingInfo; + } + +} \ No newline at end of file diff --git a/src/main/java/seedu/flexitrack/model/task/Name.java b/src/main/java/seedu/flexitrack/model/task/Name.java new file mode 100644 index 000000000000..3d0a7e50752e --- /dev/null +++ b/src/main/java/seedu/flexitrack/model/task/Name.java @@ -0,0 +1,104 @@ +package seedu.flexitrack.model.task; + +import seedu.flexitrack.commons.exceptions.IllegalValueException; + +/** + * Represents a Person'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 = "Task should be spaces or alphanumeric characters"; + public static final String NAME_VALIDATION_REGEX = ".+"; + public static final String DONE_PREFIX = "(Done) "; + public static final String BLOCK_PREFIX = "(Blocked) "; + + private String isDonePrefix; + private String isBlockPrefix; + private String name; + + /** + * Validates given name. + * + * @throws IllegalValueException if given name string is invalid. + */ + public Name(String name) throws IllegalValueException { + assert name != null; + name = name.trim(); + if (!isValidName(name)) { + throw new IllegalValueException(MESSAGE_NAME_CONSTRAINTS); + } + if (name.contains(DONE_PREFIX)){ + this.isDonePrefix = DONE_PREFIX; + this.name = name.substring(7); + name = name.substring(7); + } else { + this.name = name; + this.isDonePrefix = ""; + } + if (name.contains(BLOCK_PREFIX)){ + this.isBlockPrefix = BLOCK_PREFIX; + this.name = name.substring(10); + } else { + this.name = name; + this.isBlockPrefix = ""; + } + } + + /** + * Returns true if a given string is a valid person name. + */ + public static boolean isValidName(String test) { + return test.matches(NAME_VALIDATION_REGEX); + } + + public void setName(String name) { + this.name = name; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Name // instanceof handles nulls + && this.name.equals(((Name) other).name)); // state + // check + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + public boolean getIsDone(){ + return isDonePrefix.equals(DONE_PREFIX); + } + + //@@author A0138455Y + public boolean getIsBlock() { + return isBlockPrefix.equals(BLOCK_PREFIX); + } + //@@author + + public String setBlock(){ + return isBlockPrefix = BLOCK_PREFIX; + } + + //@@author A0127686R + @Override + public String toString(){ + return isDonePrefix + isBlockPrefix + name; + } + + public String getNameOnly(){ + return name; + } + + public String setAsMark(){ + return isDonePrefix = DONE_PREFIX; + } + + public String setAsUnmark(){ + name = name.replace(DONE_PREFIX, ""); + return isDonePrefix = ""; + } +} diff --git a/src/main/java/seedu/flexitrack/model/task/ReadOnlyTask.java b/src/main/java/seedu/flexitrack/model/task/ReadOnlyTask.java new file mode 100644 index 000000000000..254852aa6dbd --- /dev/null +++ b/src/main/java/seedu/flexitrack/model/task/ReadOnlyTask.java @@ -0,0 +1,137 @@ +package seedu.flexitrack.model.task; + +import java.util.Date; + +import seedu.flexitrack.commons.exceptions.IllegalValueException; + +/** + * A read-only immutable interface for a Task in FlexiTrack. Implementations + * should guarantee: details are present and not null, field values are + * validated. + */ +public interface ReadOnlyTask extends Comparable{ + + Name getName(); + + DateTimeInfo getDueDate(); + DateTimeInfo getStartTime(); + DateTimeInfo getEndTime(); + + boolean getIsTask(); + boolean getIsEvent(); + boolean getIsDone(); + boolean getIsBlock(); + + + /** + * 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().getNameOnly().equals(this.getName().getNameOnly()) // state checks here onwards + && other.getDueDate().equals(this.getDueDate()) + && other.getStartTime().equals(this.getStartTime()) + && other.getEndTime().equals(this.getEndTime())); + } + + /** + * Formats the person as text, showing all contact details. + */ + default String getAsText() { + final StringBuilder builder = new StringBuilder(); + String text = (getIsTask() || getIsEvent()) + ? getIsTask() ? " by/" + getDueDate() : " from/" + getStartTime() + " to/" + getEndTime() : ""; + builder.append(getIsDone() ? "(Done)" : "" + getName()).append(text); + return builder.toString(); + } + + //@@author A0127855W + /** + * Comparator for ReadOnlyTask and its children classes + * Sorts by whether the task is a floating task, then by whether the task is done, then by start time/due date, then by name + */ + default public int compareTo(ReadOnlyTask task) { + int compareResult = compareByDone(task); + if (compareResult != 0){ + return compareResult; + } + compareResult = compareByType(task); + if (compareResult != 0){ + return compareResult; + } + if (this.getIsNotFloatingTask() && task.getIsNotFloatingTask()){ + compareResult = compareByDate(task); + if (compareResult != 0){ + return compareResult; + } + } + compareResult = compareByName(task); + return compareResult; + } + + default int compareByDone(ReadOnlyTask task) { + if (this.getIsDone() && !task.getIsDone()){ + return 1; + }else if (!this.getIsDone() && task.getIsDone()){ + return -1; + }else{ + return 0; + } + } + + default int compareByType(ReadOnlyTask task) { + if (this.getIsNotFloatingTask() && !task.getIsNotFloatingTask()){ + return 1; + }else if (!this.getIsNotFloatingTask() && task.getIsNotFloatingTask()){ + return -1; + }else{ + return 0; + } + } + default int compareByDate(ReadOnlyTask task) { + Date date1 = this.getStartingTimeOrDueDate().getTimeInfo().getTimingInfo().getDates().get(0); + Date date2 = task.getStartingTimeOrDueDate().getTimeInfo().getTimingInfo().getDates().get(0); + return date1.compareTo(date2); + } + default int compareByName(ReadOnlyTask task) { + String name1 = this.getName().getNameOnly(); + String name2 = task.getName().getNameOnly(); + return name1.compareTo(name2); + } + + default Task copy(){ + Task clonedTask = null; + try{ + clonedTask = new Task(new Name (this.getName().toString()), + new DateTimeInfo (this.getDueDate().toString()), + new DateTimeInfo (this.getStartTime().toString()), + new DateTimeInfo (this.getEndTime().toString())); + }catch(IllegalValueException ive){ + } + return clonedTask; + } + + //@@author A0127686R + default public boolean getIsNotFloatingTask(){ + return (this.getIsEvent() || this.getIsTask()); + } + + default public DateTimeInfo getStartingTimeOrDueDate(){ + if (this.getIsTask()){ + return this.getDueDate(); + } else { + return this.getStartTime(); + } + } + + default public DateTimeInfo getEndingTimeOrDueDate(){ + if (this.getIsTask()){ + return this.getDueDate(); + } else { + return this.getEndTime(); + } + } + +} diff --git a/src/main/java/seedu/flexitrack/model/task/Task.java b/src/main/java/seedu/flexitrack/model/task/Task.java new file mode 100644 index 000000000000..4f72a8ade2c5 --- /dev/null +++ b/src/main/java/seedu/flexitrack/model/task/Task.java @@ -0,0 +1,182 @@ +package seedu.flexitrack.model.task; + +import java.util.Objects; + +import seedu.flexitrack.commons.exceptions.IllegalValueException; +import seedu.flexitrack.commons.util.CollectionUtil; + +/** + * Represents a Person in the address book. Guarantees: details are present and + * not null, field values are validated. + */ +public class Task implements ReadOnlyTask { + + private Name name; + private DateTimeInfo dueDate; + private DateTimeInfo startTime; + private DateTimeInfo endTime; + private boolean isEvent; + private boolean isTask; + private boolean isDone = false; + private boolean isBlock = false; + + /** + * Every field must be present and not null. + */ + public Task(Name name, DateTimeInfo dueDate, DateTimeInfo startTime, DateTimeInfo endTime) { + assert !CollectionUtil.isAnyNull(name); + this.name = name; + this.dueDate = dueDate; + this.startTime = startTime; + this.endTime = endTime; + this.isTask = dueDate.isDateNull() ? false : true; + this.isEvent = startTime.isDateNull() ? false : true; + this.isDone = name.getIsDone(); + this.isBlock = name.getIsDone(); + updateTimeFormat(); + } + + // @@ author A0127686R + /** + * To set the time if they are inferred. Starting and DueDate will start at + * 8 and ending time will end at 17:00 + * + */ + private void updateTimeFormat() { + if (isTask) { + this.dueDate.formatStartOrDueDateTime(); + } else if (isEvent) { + this.startTime.formatStartOrDueDateTime(); + this.endTime.formatEndTime(this.startTime); + } + } + // @@ author + + /** + * Copy constructor. + */ + public Task(ReadOnlyTask source) { + this(source.getName(), source.getDueDate(), source.getStartTime(), source.getEndTime()); + } + + @Override + public Name getName() { + return name; + } + + @Override + public boolean getIsTask() { + return isTask; + } + + @Override + public boolean getIsEvent() { + return isEvent; + } + + @Override + public boolean getIsDone() { + return name.getIsDone(); + } + + @Override + public boolean getIsBlock() { + return name.getIsBlock(); + } + + public void setIsBlock() { + name.setBlock(); + } + + @Override + public DateTimeInfo getDueDate() { + return dueDate; + } + + @Override + public DateTimeInfo getStartTime() { + return startTime; + } + + @Override + public DateTimeInfo getEndTime() { + return endTime; + } + + @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, dueDate, startTime, endTime, isTask, isEvent); + } + + @Override + public String toString() { + return getAsText(); + } + + // @@author A0138455Y + private void setIsDone(boolean isDone) { + if (isDone && !this.isDone) { + name.setAsMark(); + } else if (!isDone && this.isDone) { + name.setAsUnmark(); + } + } + + public void markTask(boolean isDone) throws IllegalValueException { + this.isDone = this.name.getIsDone(); + if (this.isDone && isDone) { + throw new IllegalValueException("Task already marked!"); + } else if (!this.isDone && !isDone) { + throw new IllegalValueException("Task already unmarked!"); + } else { + setIsDone(isDone); + } + } + // @@author + + public void setName(String name) { + this.name.setName(name); + } + + public void setDueDate(String dueDate) throws IllegalValueException { + this.dueDate = new DateTimeInfo(dueDate); + } + + public void setStartTime(String startTime) throws IllegalValueException { + this.startTime = new DateTimeInfo(startTime); + } + + public void setEndTime(String endTime) throws IllegalValueException { + this.endTime = new DateTimeInfo(endTime); + } + + public void setIsTask(Boolean bool) { + this.isTask = bool; + } + + public void setIsEvent(Boolean bool) { + this.isEvent = bool; + } + + public String getStartingTimeInString() { + return this.startTime.toString(); + } + + public String getEndingTimeInString() { + return this.endTime.toString(); + } + + public String getDueDateInString() { + return this.dueDate.toString(); + } + +} diff --git a/src/main/java/seedu/flexitrack/model/task/UniqueTaskList.java b/src/main/java/seedu/flexitrack/model/task/UniqueTaskList.java new file mode 100644 index 000000000000..7de58a3319db --- /dev/null +++ b/src/main/java/seedu/flexitrack/model/task/UniqueTaskList.java @@ -0,0 +1,250 @@ +package seedu.flexitrack.model.task; + +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import seedu.flexitrack.commons.exceptions.DuplicateDataException; +import seedu.flexitrack.commons.exceptions.IllegalValueException; +import seedu.flexitrack.commons.util.CollectionUtil; + +/** + * 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 { + + /** + * 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 person in the list would + * fail because there is no such matching person in the list. + */ + public static class TaskNotFoundException extends Exception { + } + + public static class IllegalEditException extends Exception { + } + + private final ObservableList internalList = FXCollections.observableArrayList(); + + /** + * Constructs empty PersonList. + */ + public UniqueTaskList() { + } + + /** + * Returns true if the list contains an equivalent person as the given + * argument. + */ + public boolean contains(ReadOnlyTask 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(Task toAdd) throws DuplicateTaskException { + assert toAdd != null; + if (contains(toAdd)) { + throw new DuplicateTaskException(); + } + 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(ReadOnlyTask toRemove) throws TaskNotFoundException { + assert toRemove != null; + boolean taskFoundAndDeleted = false; + for(Task task : internalList){ + if(task.equals(toRemove)){ + internalList.remove(task); + taskFoundAndDeleted = true; + break; + } + } + if (!taskFoundAndDeleted) { + throw new TaskNotFoundException(); + } + return taskFoundAndDeleted; + } + + 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 UniqueTaskList // instanceof handles nulls + && this.internalList.equals(((UniqueTaskList) other).internalList)); + } + + @Override + public int hashCode() { + return internalList.hashCode(); + } + + //@@author A0138455Y + public Task mark(ReadOnlyTask targetTask, boolean isDone) throws IllegalValueException { + assert targetTask != null; + int targetIndex = internalList.indexOf(targetTask); + Task markTask = internalList.get(targetIndex); + markTask.markTask(isDone); + internalList.set(targetIndex, markTask); + return markTask; + + } + //@@author + + //@@author A0127855W + /** + * Sorts the observable list according to the ReadOnlyTask comparator + */ + public void sort(){ + Collections.sort(internalList);; + } + + /** + * edit + * ----------------------------------------------------- + * finds the target task to be edited by the specified index and edits the task using the given argument array + * @param taskToEdit + * @param args: Array of edit parameters + * @return The new duration if the item being edited is an event, or "" if it is a floating task or task + * @throws IllegalEditException + * @throws TaskNotFoundException + * @throws IllegalValueException + */ + public Task edit(ReadOnlyTask taskToEdit, String[] args) + throws IllegalEditException, IllegalValueException { + assert taskToEdit != null; + int targetIndex = internalList.indexOf(taskToEdit); + Task editTask = internalList.get(targetIndex); + checkForIllegalFloatingTaskEdit(args, editTask); + editTaskParameters(editTask, args); + internalList.set(targetIndex, editTask); + return editTask; + + } + + /** + * checkForIllegalFloatingTaskEdit + * ------------------------------------------------- + * checks that the appropriate edit parameters to a floating task; user should not add both task and event parameters to a floating task, + * he must also make the floating task a complete event (with start and end time) if he were to edit it into an event. + * @param args + * @param editTask + * @throws IllegalEditException + */ + private void checkForIllegalFloatingTaskEdit(String[] args, Task editTask) throws IllegalEditException { + assert args != null; + assert editTask != null; + if (!editTask.getIsTask() && !editTask.getIsEvent()) { + if ((args[1] != null) && (args[2] != null || args[3] != null)) { + throw new IllegalEditException(); + } + if ((args[2] != null && args[3] == null) || (args[3] != null && args[2] == null)) { + throw new IllegalEditException(); + } + } + } + + /** + * editTaskParameters + * --------------------------------------------------- + * edits the actual task with given parameters, checking that the wrong parameters are not given to the wrong type of task; + * i.e. user should not add start date to a task, nor should he add a due date to an event + * @param editTask + * @param args + * @throws IllegalValueException + * @throws IllegalEditException + */ + private void editTaskParameters(Task editTask, String[] args) throws IllegalValueException, IllegalEditException { + assert args != null; + assert editTask != null; + for (int i = 0; i < args.length; i++) { + if (!(args[i] == null)) { + switch (i) { + case 0: + editTask.setName(args[i]); + break; + case 1: + if (!editTask.getIsEvent()) { + editTask.setDueDate(args[i]); + editTask.setIsTask(true); + } else { + throw new IllegalEditException(); + } + break; + case 2: + if (!editTask.getIsTask()) { + editTask.setStartTime(args[i]); + editTask.setIsEvent(true); + } else { + throw new IllegalEditException(); + } + break; + case 3: + if (!editTask.getIsTask()) { + editTask.setEndTime(args[i]); + editTask.setIsEvent(true); + } else { + throw new IllegalEditException(); + } + break; + default: + break; + } + } + } + } + + //@@ A0127686R + /** + * Find the index of a task of interest + * + * @param task The task of interest + * @return index of the task in int + */ + public int findIndexOfTask(Task task) { + int numberOfTask = internalList.size(); + for (int i = 0; i < numberOfTask; i++){ + if (internalList.get(i).equals(task)){ + return i; + } + } + return -1; + } +} diff --git a/src/main/java/seedu/flexitrack/storage/FlexiTrackStorage.java b/src/main/java/seedu/flexitrack/storage/FlexiTrackStorage.java new file mode 100644 index 000000000000..687e457b8cd8 --- /dev/null +++ b/src/main/java/seedu/flexitrack/storage/FlexiTrackStorage.java @@ -0,0 +1,50 @@ +package seedu.flexitrack.storage; + +import java.io.IOException; +import java.util.Optional; + +import seedu.flexitrack.commons.exceptions.DataConversionException; +import seedu.flexitrack.model.ReadOnlyFlexiTrack; + +/** + * Represents a storage for {@link seedu.flexitrack.model.FlexiTrack}. + */ +public interface FlexiTrackStorage { + + /** + * Returns the file path of the data file. + */ + String getFlexiTrackFilePath(); + + /** + * Returns FlexiTrack data as a {@link ReadOnlyFlexiTrack}. 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 readFlexiTrack() throws DataConversionException, IOException; + + /** + * @see #getFlexiTrackFilePath() + */ + Optional readFlexiTrack(String filePath) throws DataConversionException, IOException; + + /** + * Saves the given {@link ReadOnlyFlexiTrack} to the storage. + * + * @param FlexiTrack + * cannot be null. + * @throws IOException + * if there was any problem writing to the file. + */ + void saveFlexiTrack(ReadOnlyFlexiTrack flexiTrack) throws IOException; + + /** + * @see #saveFlexiTrack(ReadOnlyFlexiTrack) + */ + void saveFlexiTrack(ReadOnlyFlexiTrack flexiTrack, String filePath) throws IOException; + +} diff --git a/src/main/java/seedu/address/storage/JsonUserPrefsStorage.java b/src/main/java/seedu/flexitrack/storage/JsonUserPrefsStorage.java similarity index 70% rename from src/main/java/seedu/address/storage/JsonUserPrefsStorage.java rename to src/main/java/seedu/flexitrack/storage/JsonUserPrefsStorage.java index 1efa8288e4f6..956e829453c5 100644 --- a/src/main/java/seedu/address/storage/JsonUserPrefsStorage.java +++ b/src/main/java/seedu/flexitrack/storage/JsonUserPrefsStorage.java @@ -1,25 +1,25 @@ -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.UserPrefs; +package seedu.flexitrack.storage; import java.io.File; import java.io.IOException; import java.util.Optional; import java.util.logging.Logger; +import seedu.flexitrack.commons.core.LogsCenter; +import seedu.flexitrack.commons.exceptions.DataConversionException; +import seedu.flexitrack.commons.util.FileUtil; +import seedu.flexitrack.model.UserPrefs; + /** * A class to access UserPrefs stored in the hard disk as a json file */ -public class JsonUserPrefsStorage implements UserPrefsStorage{ +public class JsonUserPrefsStorage implements UserPrefsStorage { private static final Logger logger = LogsCenter.getLogger(JsonUserPrefsStorage.class); private String filePath; - public JsonUserPrefsStorage(String filePath){ + public JsonUserPrefsStorage(String filePath) { this.filePath = filePath; } @@ -35,8 +35,11 @@ public void saveUserPrefs(UserPrefs userPrefs) throws IOException { /** * Similar to {@link #readUserPrefs()} - * @param prefsFilePath location of the data. Cannot be null. - * @throws DataConversionException if the file format is not as expected. + * + * @param prefsFilePath + * location of the data. Cannot be null. + * @throws DataConversionException + * if the file format is not as expected. */ public Optional readUserPrefs(String prefsFilePath) throws DataConversionException { assert prefsFilePath != null; @@ -44,7 +47,7 @@ public Optional readUserPrefs(String prefsFilePath) throws DataConver File prefsFile = new File(prefsFilePath); if (!prefsFile.exists()) { - logger.info("Prefs file " + prefsFile + " not found"); + logger.info("Prefs file " + prefsFile + " not found"); return Optional.empty(); } @@ -62,7 +65,9 @@ public Optional readUserPrefs(String prefsFilePath) throws DataConver /** * Similar to {@link #saveUserPrefs(UserPrefs)} - * @param prefsFilePath location of the data. Cannot be null. + * + * @param prefsFilePath + * location of the data. Cannot be null. */ public void saveUserPrefs(UserPrefs userPrefs, String prefsFilePath) throws IOException { assert userPrefs != null; diff --git a/src/main/java/seedu/flexitrack/storage/Storage.java b/src/main/java/seedu/flexitrack/storage/Storage.java new file mode 100644 index 000000000000..3bc5b04354aa --- /dev/null +++ b/src/main/java/seedu/flexitrack/storage/Storage.java @@ -0,0 +1,42 @@ +package seedu.flexitrack.storage; + +import java.io.IOException; +import java.util.Optional; + +import seedu.flexitrack.commons.events.model.FlexiTrackChangedEvent; +import seedu.flexitrack.commons.events.storage.DataSavingExceptionEvent; +import seedu.flexitrack.commons.exceptions.DataConversionException; +import seedu.flexitrack.model.ReadOnlyFlexiTrack; +import seedu.flexitrack.model.UserPrefs; + +/** + * API of the Storage component + */ +public interface Storage extends FlexiTrackStorage, UserPrefsStorage { + + @Override + Optional readUserPrefs() throws DataConversionException, IOException; + + @Override + void saveUserPrefs(UserPrefs userPrefs) throws IOException; + + @Override + String getFlexiTrackFilePath(); + + @Override + Optional readFlexiTrack() throws DataConversionException, IOException; + + @Override + void saveFlexiTrack(ReadOnlyFlexiTrack flexiTrack) throws IOException; + + /** + * Saves the current version of the FlexiTrack to the hard disk. Creates the + * data file if it is missing. Raises {@link DataSavingExceptionEvent} if + * there was an error during saving. + */ + void handleFlexiTrackChangedEvent(FlexiTrackChangedEvent abce); + + //@@author A0138455Y + void setStoragePath(String newPath); + //@@author +} diff --git a/src/main/java/seedu/flexitrack/storage/StorageManager.java b/src/main/java/seedu/flexitrack/storage/StorageManager.java new file mode 100644 index 000000000000..ade971af5027 --- /dev/null +++ b/src/main/java/seedu/flexitrack/storage/StorageManager.java @@ -0,0 +1,94 @@ +package seedu.flexitrack.storage; + +import java.io.IOException; +import java.util.Optional; +import java.util.logging.Logger; + +import com.google.common.eventbus.Subscribe; + +import seedu.flexitrack.commons.core.ComponentManager; +import seedu.flexitrack.commons.core.LogsCenter; +import seedu.flexitrack.commons.events.model.FlexiTrackChangedEvent; +import seedu.flexitrack.commons.events.storage.DataSavingExceptionEvent; +import seedu.flexitrack.commons.exceptions.DataConversionException; +import seedu.flexitrack.model.ReadOnlyFlexiTrack; +import seedu.flexitrack.model.UserPrefs; + +/** + * Manages storage of FlexiTrack data in local storage. + */ +public class StorageManager extends ComponentManager implements Storage { + + private static final Logger logger = LogsCenter.getLogger(StorageManager.class); + private FlexiTrackStorage flexiTrackStorage; + private UserPrefsStorage userPrefsStorage; + + public StorageManager(FlexiTrackStorage flexiTrackStorage, UserPrefsStorage userPrefsStorage) { + super(); + this.flexiTrackStorage = flexiTrackStorage; + this.userPrefsStorage = userPrefsStorage; + } + + public StorageManager(String flexiTrackFilePath, String userPrefsFilePath) { + this(new XmlFlexiTrackStorage(flexiTrackFilePath), new JsonUserPrefsStorage(userPrefsFilePath)); + } + + //@@author A0138455Y + @Override + public void setStoragePath(String newPath) { + this.flexiTrackStorage = new XmlFlexiTrackStorage(newPath); + } + //@@author + // ================ UserPrefs methods ============================== + + @Override + public Optional readUserPrefs() throws DataConversionException, IOException { + return userPrefsStorage.readUserPrefs(); + } + + @Override + public void saveUserPrefs(UserPrefs userPrefs) throws IOException { + userPrefsStorage.saveUserPrefs(userPrefs); + } + + // ================ FlexiTrack methods ============================== + + @Override + public String getFlexiTrackFilePath() { + return flexiTrackStorage.getFlexiTrackFilePath(); + } + + @Override + public Optional readFlexiTrack() throws DataConversionException, IOException { + return readFlexiTrack(flexiTrackStorage.getFlexiTrackFilePath()); + } + + @Override + public Optional readFlexiTrack(String filePath) throws DataConversionException, IOException { + logger.fine("Attempting to read data from file: " + filePath); + return flexiTrackStorage.readFlexiTrack(filePath); + } + + @Override + public void saveFlexiTrack(ReadOnlyFlexiTrack flexiTrack) throws IOException { + saveFlexiTrack(flexiTrack, flexiTrackStorage.getFlexiTrackFilePath()); + } + + @Override + public void saveFlexiTrack(ReadOnlyFlexiTrack flexiTrack, String filePath) throws IOException { + logger.fine("Attempting to write to data file: " + filePath); + flexiTrackStorage.saveFlexiTrack(flexiTrack, filePath); + } + + @Override + @Subscribe + public void handleFlexiTrackChangedEvent(FlexiTrackChangedEvent event) { + logger.info(LogsCenter.getEventHandlingLogMessage(event, "Local data changed, saving to file")); + try { + saveFlexiTrack(event.data); + } catch (IOException e) { + raise(new DataSavingExceptionEvent(e)); + } + } + +} diff --git a/src/main/java/seedu/flexitrack/storage/UserPrefsStorage.java b/src/main/java/seedu/flexitrack/storage/UserPrefsStorage.java new file mode 100644 index 000000000000..4d886cb77d5f --- /dev/null +++ b/src/main/java/seedu/flexitrack/storage/UserPrefsStorage.java @@ -0,0 +1,35 @@ +package seedu.flexitrack.storage; + +import java.io.IOException; +import java.util.Optional; + +import seedu.flexitrack.commons.exceptions.DataConversionException; +import seedu.flexitrack.model.UserPrefs; + +/** + * Represents a storage for {@link seedu.flexitrack.model.UserPrefs}. + */ +public interface UserPrefsStorage { + + /** + * Returns UserPrefs data from storage. 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 readUserPrefs() throws DataConversionException, IOException; + + /** + * Saves the given {@link seedu.flexitrack.model.UserPrefs} to the storage. + * + * @param userPrefs + * cannot be null. + * @throws IOException + * if there was any problem writing to the file. + */ + void saveUserPrefs(UserPrefs userPrefs) throws IOException; + +} diff --git a/src/main/java/seedu/flexitrack/storage/XmlAdaptedTask.java b/src/main/java/seedu/flexitrack/storage/XmlAdaptedTask.java new file mode 100644 index 000000000000..a122c9f0f4c7 --- /dev/null +++ b/src/main/java/seedu/flexitrack/storage/XmlAdaptedTask.java @@ -0,0 +1,64 @@ +package seedu.flexitrack.storage; + +import javax.xml.bind.annotation.XmlElement; + +import seedu.flexitrack.commons.exceptions.IllegalValueException; +import seedu.flexitrack.model.task.DateTimeInfo; +import seedu.flexitrack.model.task.Name; +import seedu.flexitrack.model.task.ReadOnlyTask; +import seedu.flexitrack.model.task.Task; + +/** + * JAXB-friendly version of the Task. + */ +public class XmlAdaptedTask { + + @XmlElement(required = true) + private String name; + // @XmlElement(required = true) + // private Boolean isEvent; + // @XmlElement(required = true) + // private Boolean isTask; + @XmlElement + private String dueDate; + @XmlElement + private String startTime; + @XmlElement + private String endTime; + + /** + * 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().toString(); + dueDate = source.getDueDate().toString(); + startTime = source.getStartTime().toString(); + endTime = source.getEndTime().toString(); + } + + /** + * 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 DateTimeInfo dueDate = new DateTimeInfo(this.dueDate); + final DateTimeInfo startTime = new DateTimeInfo(this.startTime); + final DateTimeInfo endTime = new DateTimeInfo(this.endTime); + return new Task(name, dueDate, startTime, endTime); + } +} diff --git a/src/main/java/seedu/flexitrack/storage/XmlFileStorage.java b/src/main/java/seedu/flexitrack/storage/XmlFileStorage.java new file mode 100644 index 000000000000..9fda41df319b --- /dev/null +++ b/src/main/java/seedu/flexitrack/storage/XmlFileStorage.java @@ -0,0 +1,38 @@ +package seedu.flexitrack.storage; + +import java.io.File; +import java.io.FileNotFoundException; + +import javax.xml.bind.JAXBException; + +import seedu.flexitrack.commons.exceptions.DataConversionException; +import seedu.flexitrack.commons.util.XmlUtil; + +/** + * Stores FlexiTrack data in an XML file + */ +public class XmlFileStorage { + /** + * Saves the given FlexiTrack data to the specified file. + */ + public static void saveDataToFile(File file, XmlSerializableFlexiTrack flexiTrack) throws FileNotFoundException { + try { + XmlUtil.saveDataToFile(file, flexiTrack); + } catch (JAXBException e) { + assert false : "Unexpected exception " + e.getMessage(); + } + } + + /** + * Returns FlexiTrack in the file or an empty FlexiTrack + */ + public static XmlSerializableFlexiTrack loadDataFromSaveFile(File file) + throws DataConversionException, FileNotFoundException { + try { + return XmlUtil.getDataFromFile(file, XmlSerializableFlexiTrack.class); + } catch (JAXBException e) { + throw new DataConversionException(e); + } + } + +} diff --git a/src/main/java/seedu/flexitrack/storage/XmlFlexiTrackStorage.java b/src/main/java/seedu/flexitrack/storage/XmlFlexiTrackStorage.java new file mode 100644 index 000000000000..b23412d13a80 --- /dev/null +++ b/src/main/java/seedu/flexitrack/storage/XmlFlexiTrackStorage.java @@ -0,0 +1,79 @@ +package seedu.flexitrack.storage; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Optional; +import java.util.logging.Logger; + +import seedu.flexitrack.commons.core.LogsCenter; +import seedu.flexitrack.commons.exceptions.DataConversionException; +import seedu.flexitrack.commons.util.FileUtil; +import seedu.flexitrack.model.ReadOnlyFlexiTrack; + +/** + * A class to access FlexiTrack data stored as an xml file on the hard disk. + */ +public class XmlFlexiTrackStorage implements FlexiTrackStorage { + + private static final Logger logger = LogsCenter.getLogger(XmlFlexiTrackStorage.class); + + private String filePath; + + public XmlFlexiTrackStorage(String filePath) { + this.filePath = filePath; + } + + public String getFlexiTrackFilePath() { + return filePath; + } + + /** + * Similar to {@link #readFlexiTrack()} + * + * @param filePath + * location of the data. Cannot be null + * @throws DataConversionException + * if the file is not in the correct format. + */ + public Optional readFlexiTrack(String filePath) + throws DataConversionException, FileNotFoundException { + assert filePath != null; + + File flexiTrackFile = new File(filePath); + + if (!flexiTrackFile.exists()) { + logger.info("FlexiTrack file " + flexiTrackFile + " not found"); + return Optional.empty(); + } + + ReadOnlyFlexiTrack flexiTrackOptional = XmlFileStorage.loadDataFromSaveFile(new File(filePath)); + + return Optional.of(flexiTrackOptional); + } + + /** + * Similar to {@link #saveFlexiTrack(ReadOnlyFlexiTrack)} + * + * @param filePath + * location of the data. Cannot be null + */ + public void saveFlexiTrack(ReadOnlyFlexiTrack flexiTrack, String filePath) throws IOException { + assert flexiTrack != null; + assert filePath != null; + + File file = new File(filePath); + FileUtil.createIfMissing(file); + XmlFileStorage.saveDataToFile(file, new XmlSerializableFlexiTrack(flexiTrack)); + } + + @Override + public Optional readFlexiTrack() throws DataConversionException, IOException { + return readFlexiTrack(filePath); + } + + @Override + public void saveFlexiTrack(ReadOnlyFlexiTrack flexiTrack) throws IOException { + saveFlexiTrack(flexiTrack, filePath); + } +} diff --git a/src/main/java/seedu/flexitrack/storage/XmlSerializableFlexiTrack.java b/src/main/java/seedu/flexitrack/storage/XmlSerializableFlexiTrack.java new file mode 100644 index 000000000000..e05f0d7776b3 --- /dev/null +++ b/src/main/java/seedu/flexitrack/storage/XmlSerializableFlexiTrack.java @@ -0,0 +1,66 @@ +package seedu.flexitrack.storage; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +import seedu.flexitrack.commons.exceptions.IllegalValueException; +import seedu.flexitrack.model.ReadOnlyFlexiTrack; +import seedu.flexitrack.model.task.ReadOnlyTask; +import seedu.flexitrack.model.task.UniqueTaskList; + +/** + * An Immutable FlexiTrack that is serializable to XML format + */ +@XmlRootElement(name = "FlexiTrack") +public class XmlSerializableFlexiTrack implements ReadOnlyFlexiTrack { + + @XmlElement + private List tasks; + + { + tasks = new ArrayList<>(); + } + + /** + * Empty constructor required for marshalling + */ + public XmlSerializableFlexiTrack() { + } + + /** + * Conversion + */ + public XmlSerializableFlexiTrack(ReadOnlyFlexiTrack src) { + 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) { + // TODO: better error handling + } + } + return lists; + } + + @Override + public List getTaskList() { + return tasks.stream().map(p -> { + try { + return p.toModelType(); + } catch (IllegalValueException e) { + e.printStackTrace(); + // TODO: better error handling + return null; + } + }).collect(Collectors.toCollection(ArrayList::new)); + } +} diff --git a/src/main/java/seedu/address/ui/CommandBox.java b/src/main/java/seedu/flexitrack/ui/CommandBox.java similarity index 65% rename from src/main/java/seedu/address/ui/CommandBox.java rename to src/main/java/seedu/flexitrack/ui/CommandBox.java index 2e1409a3016c..8d913448e29b 100644 --- a/src/main/java/seedu/address/ui/CommandBox.java +++ b/src/main/java/seedu/flexitrack/ui/CommandBox.java @@ -1,19 +1,22 @@ -package seedu.address.ui; +package seedu.flexitrack.ui; + +import java.util.logging.Logger; + +import org.controlsfx.control.textfield.TextFields; import com.google.common.eventbus.Subscribe; + import javafx.fxml.FXML; import javafx.scene.Node; import javafx.scene.control.SplitPane; 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 java.util.logging.Logger; +import seedu.flexitrack.commons.core.LogsCenter; +import seedu.flexitrack.commons.events.ui.IncorrectCommandAttemptedEvent; +import seedu.flexitrack.commons.util.FxViewUtil; +import seedu.flexitrack.logic.Logic; +import seedu.flexitrack.logic.commands.CommandResult; public class CommandBox extends UiPart { private final Logger logger = LogsCenter.getLogger(CommandBox.class); @@ -29,15 +32,22 @@ public class CommandBox extends UiPart { @FXML private TextField commandTextField; private CommandResult mostRecentResult; - - public static CommandBox load(Stage primaryStage, AnchorPane commandBoxPlaceholder, - ResultDisplay resultDisplay, Logic logic) { + + //@@author A0147092E + public static CommandBox load(Stage primaryStage, AnchorPane commandBoxPlaceholder, ResultDisplay resultDisplay, + Logic logic) { CommandBox commandBox = UiPartLoader.loadUiPart(primaryStage, commandBoxPlaceholder, new CommandBox()); +// TextFields.bindAutoCompletion(commandBox.commandTextField, "add ", "add from/ to/ ", "add by/", +// "delete 1", "delete 2", "delete 3", "delete 4", "delete 5", "delete 6", "delete 7", "delete 8", "delete 9", +// "edit 1 ", "edit 2 ", "edit 3 ", "edit 4 ", "edit 5 ", "edit 6 ", "edit 7 ", "edit 8 ", "edit 9 ", +// "find ", +// "list", "list mark", "list unmark", "list future", "list past"); commandBox.configure(resultDisplay, logic); commandBox.addToPlaceholder(); return commandBox; } - + //@@author + public void configure(ResultDisplay resultDisplay, Logic logic) { this.resultDisplay = resultDisplay; this.logic = logic; @@ -65,23 +75,33 @@ public String getFxmlPath() { public void setPlaceholder(AnchorPane pane) { this.placeHolderPane = pane; } - - + + //@@author A0147092E @FXML private void handleCommandInputChanged() { - //Take a copy of the command text + // Take a copy of the command text previousCommandTest = commandTextField.getText(); - - /* We assume the command is correct. If it is incorrect, the command box will be changed accordingly - * in the event handling code {@link #handleIncorrectCommandAttempted} + + commandTextField.textProperty().addListener((observable, oldValue, newValue) -> { + if(newValue != null){ + if(!newValue.equals(previousCommandTest)) + commandTextField.getStyleClass().remove("error"); + } + }); + + + /* + * We assume the command is correct. If it is incorrect, the command box + * will be changed accordingly in the event handling code {@link + * #handleIncorrectCommandAttempted} */ setStyleToIndicateCorrectCommand(); mostRecentResult = logic.execute(previousCommandTest); resultDisplay.postMessage(mostRecentResult.feedbackToUser); logger.info("Result: " + mostRecentResult.feedbackToUser); } - - + //@@author A0147092E + /** * Sets the command box style to indicate a correct command. */ @@ -91,8 +111,8 @@ private void setStyleToIndicateCorrectCommand() { } @Subscribe - private void handleIncorrectCommandAttempted(IncorrectCommandAttemptedEvent event){ - logger.info(LogsCenter.getEventHandlingLogMessage(event,"Invalid command: " + previousCommandTest)); + private void handleIncorrectCommandAttempted(IncorrectCommandAttemptedEvent event) { + logger.info(LogsCenter.getEventHandlingLogMessage(event, "Invalid command: " + previousCommandTest)); setStyleToIndicateIncorrectCommand(); restoreCommandText(); } diff --git a/src/main/java/seedu/address/ui/HelpWindow.java b/src/main/java/seedu/flexitrack/ui/HelpWindow.java similarity index 72% rename from src/main/java/seedu/address/ui/HelpWindow.java rename to src/main/java/seedu/flexitrack/ui/HelpWindow.java index 45b765ab6a0c..1f7ead90d954 100644 --- a/src/main/java/seedu/address/ui/HelpWindow.java +++ b/src/main/java/seedu/flexitrack/ui/HelpWindow.java @@ -1,14 +1,14 @@ -package seedu.address.ui; +package seedu.flexitrack.ui; + +import java.util.logging.Logger; 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 java.util.logging.Logger; +import seedu.flexitrack.commons.core.LogsCenter; +import seedu.flexitrack.commons.util.FxViewUtil; /** * Controller for a help page @@ -16,11 +16,10 @@ public class HelpWindow extends UiPart { private static final Logger logger = LogsCenter.getLogger(HelpWindow.class); - private static final String ICON = "/images/help_icon.png"; + private static final String ICON = "/images/task_tracker.png"; 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"; + private static final String USERGUIDE_URL = "https://github.com/CS2103AUG2016-F09-C3/main/blob/master/docs/UserGuide.md"; private AnchorPane mainPane; @@ -43,11 +42,12 @@ public String getFxmlPath() { return FXML; } - private void configure(){ + private void configure() { Scene scene = new Scene(mainPane); - //Null passed as the parent stage to make it non-modal. + // Null passed as the parent stage to make it non-modal. dialogStage = createDialogStage(TITLE, null, scene); - dialogStage.setMaximized(true); //TODO: set a more appropriate initial size + dialogStage.setMaximized(true); // TODO: set a more appropriate initial + // size setIcon(dialogStage, ICON); WebView browser = new WebView(); diff --git a/src/main/java/seedu/address/ui/MainWindow.java b/src/main/java/seedu/flexitrack/ui/MainWindow.java similarity index 69% rename from src/main/java/seedu/address/ui/MainWindow.java rename to src/main/java/seedu/flexitrack/ui/MainWindow.java index 2c76aced3b04..b3b97ec52960 100644 --- a/src/main/java/seedu/address/ui/MainWindow.java +++ b/src/main/java/seedu/flexitrack/ui/MainWindow.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.flexitrack.ui; import javafx.fxml.FXML; import javafx.scene.Node; @@ -8,20 +8,20 @@ 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.flexitrack.commons.core.Config; +import seedu.flexitrack.commons.core.GuiSettings; +import seedu.flexitrack.commons.events.ui.ExitAppRequestEvent; +import seedu.flexitrack.logic.Logic; +import seedu.flexitrack.model.UserPrefs; +import seedu.flexitrack.model.task.ReadOnlyTask; /** - * The Main Window. Provides the basic application layout containing - * a menu bar and space where other JavaFX elements can be placed. + * The Main Window. Provides the basic application layout containing a menu bar + * and space where other JavaFX elements can be placed. */ public class MainWindow extends UiPart { - private static final String ICON = "/images/address_book_32.png"; + private static final String ICON = "/images/task_tracker.png"; private static final String FXML = "MainWindow.fxml"; public static final int MIN_HEIGHT = 600; public static final int MIN_WIDTH = 450; @@ -29,8 +29,7 @@ public class MainWindow extends UiPart { private Logic logic; // Independent Ui parts residing in this Ui container - private BrowserPanel browserPanel; - private PersonListPanel personListPanel; + private TaskListPanel taskListPanel; private ResultDisplay resultDisplay; private StatusBarFooter statusBarFooter; private CommandBox commandBox; @@ -41,10 +40,7 @@ public class MainWindow extends UiPart { private VBox rootLayout; private Scene scene; - private String addressBookName; - - @FXML - private AnchorPane browserPlaceholder; + private String taskTrackerName; @FXML private AnchorPane commandBoxPlaceholder; @@ -53,7 +49,7 @@ public class MainWindow extends UiPart { private MenuItem helpMenuItem; @FXML - private AnchorPane personListPanelPlaceholder; + private AnchorPane taskListPanelPlaceholder; @FXML private AnchorPane resultDisplayPlaceholder; @@ -61,7 +57,6 @@ public class MainWindow extends UiPart { @FXML private AnchorPane statusbarPlaceholder; - public MainWindow() { super(); } @@ -79,20 +74,19 @@ public String getFxmlPath() { public static MainWindow load(Stage primaryStage, Config config, UserPrefs prefs, Logic logic) { MainWindow mainWindow = UiPartLoader.loadUiPart(primaryStage, new MainWindow()); - mainWindow.configure(config.getAppTitle(), config.getAddressBookName(), config, prefs, logic); + mainWindow.configure(config.getAppTitle(), config.getFlexiTrackName(), config, prefs, logic); return mainWindow; } - private void configure(String appTitle, String addressBookName, Config config, UserPrefs prefs, - Logic logic) { + private void configure(String appTitle, String taskTrackerName, Config config, UserPrefs prefs, Logic logic) { - //Set dependencies + // Set dependencies this.logic = logic; - this.addressBookName = addressBookName; + this.taskTrackerName = taskTrackerName; this.config = config; this.userPrefs = prefs; - //Configure the UI + // Configure the UI setTitle(appTitle); setIcon(ICON); setWindowMinSize(); @@ -108,10 +102,9 @@ private void setAccelerators() { } void fillInnerParts() { - browserPanel = BrowserPanel.load(browserPlaceholder); - personListPanel = PersonListPanel.load(primaryStage, getPersonListPlaceholder(), logic.getFilteredPersonList()); + taskListPanel = TaskListPanel.load(primaryStage, getTaskListPlaceholder(), logic.getFilteredTaskList()); resultDisplay = ResultDisplay.load(primaryStage, getResultDisplayPlaceholder()); - statusBarFooter = StatusBarFooter.load(primaryStage, getStatusbarPlaceholder(), config.getAddressBookFilePath()); + statusBarFooter = StatusBarFooter.load(primaryStage, getStatusbarPlaceholder(), config.getFlexiTrackFilePath()); commandBox = CommandBox.load(primaryStage, getCommandBoxPlaceholder(), resultDisplay, logic); } @@ -127,8 +120,8 @@ private AnchorPane getResultDisplayPlaceholder() { return resultDisplayPlaceholder; } - public AnchorPane getPersonListPlaceholder() { - return personListPanelPlaceholder; + public AnchorPane getTaskListPlaceholder() { + return taskListPanelPlaceholder; } public void hide() { @@ -160,8 +153,8 @@ private void setWindowMinSize() { * Returns the current size and the position of the main Window. */ public GuiSettings getCurrentGuiSetting() { - return new GuiSettings(primaryStage.getWidth(), primaryStage.getHeight(), - (int) primaryStage.getX(), (int) primaryStage.getY()); + return new GuiSettings(primaryStage.getWidth(), primaryStage.getHeight(), (int) primaryStage.getX(), + (int) primaryStage.getY()); } @FXML @@ -173,7 +166,7 @@ public void handleHelp() { public void show() { primaryStage.show(); } - + /** * Closes the application. */ @@ -182,15 +175,7 @@ private void handleExit() { raise(new ExitAppRequestEvent()); } - public PersonListPanel getPersonListPanel() { - return this.personListPanel; - } - - public void loadPersonPage(ReadOnlyPerson person) { - browserPanel.loadPersonPage(person); - } - - public void releaseResources() { - browserPanel.freeResources(); + public TaskListPanel getTaskListPanel() { + return this.taskListPanel; } } diff --git a/src/main/java/seedu/address/ui/ResultDisplay.java b/src/main/java/seedu/flexitrack/ui/ResultDisplay.java similarity index 96% rename from src/main/java/seedu/address/ui/ResultDisplay.java rename to src/main/java/seedu/flexitrack/ui/ResultDisplay.java index 37284ee6c696..5ccb7e900aea 100644 --- a/src/main/java/seedu/address/ui/ResultDisplay.java +++ b/src/main/java/seedu/flexitrack/ui/ResultDisplay.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.flexitrack.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.flexitrack.commons.util.FxViewUtil; /** * A ui for the status bar that is displayed at the header of the application. diff --git a/src/main/java/seedu/address/ui/StatusBarFooter.java b/src/main/java/seedu/flexitrack/ui/StatusBarFooter.java similarity index 81% rename from src/main/java/seedu/address/ui/StatusBarFooter.java rename to src/main/java/seedu/flexitrack/ui/StatusBarFooter.java index f74f66be6fc9..9d4ab6e70e78 100644 --- a/src/main/java/seedu/address/ui/StatusBarFooter.java +++ b/src/main/java/seedu/flexitrack/ui/StatusBarFooter.java @@ -1,18 +1,21 @@ -package seedu.address.ui; +package seedu.flexitrack.ui; + +import java.util.Date; +import java.util.logging.Logger; + +import org.controlsfx.control.StatusBar; import com.google.common.eventbus.Subscribe; + import javafx.fxml.FXML; import javafx.scene.Node; import javafx.scene.layout.AnchorPane; 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 java.util.Date; -import java.util.logging.Logger; +import seedu.flexitrack.commons.core.LogsCenter; +import seedu.flexitrack.commons.events.model.FlexiTrackChangedEvent; +import seedu.flexitrack.commons.events.ui.StoragePathChangeEvent; +import seedu.flexitrack.commons.util.FxViewUtil; /** * A ui for the status bar that is displayed at the footer of the application. @@ -90,9 +93,16 @@ public String getFxmlPath() { } @Subscribe - public void handleAddressBookChangedEvent(AddressBookChangedEvent abce) { + public void handleFlexiTrackChangedEvent(FlexiTrackChangedEvent abce) { String lastUpdated = (new Date()).toString(); logger.info(LogsCenter.getEventHandlingLogMessage(abce, "Setting last updated status to " + lastUpdated)); setSyncStatus("Last Updated: " + lastUpdated); } + + //@@author A0138455Y + @Subscribe + public void changeStoragePathRequestEvent(StoragePathChangeEvent event) { + logger.info(LogsCenter.getEventHandlingLogMessage(event, "Setting new data location at: " + event.toString())); + setSaveLocation("./"+event.toString()); + } } diff --git a/src/main/java/seedu/flexitrack/ui/TaskCard.java b/src/main/java/seedu/flexitrack/ui/TaskCard.java new file mode 100644 index 000000000000..0e1b1a4ac6f3 --- /dev/null +++ b/src/main/java/seedu/flexitrack/ui/TaskCard.java @@ -0,0 +1,81 @@ +package seedu.flexitrack.ui; + +import javafx.fxml.FXML; +import javafx.scene.Node; +import javafx.scene.control.Label; +import javafx.scene.layout.HBox; +import seedu.flexitrack.model.task.ReadOnlyTask; + +public class TaskCard extends UiPart { + + private static final String FXML = "TaskListCard.fxml"; + + @FXML + private HBox cardPane; + @FXML + private Label title; + @FXML + private Label id; + @FXML + private Label dateTime; + + private ReadOnlyTask task; + private int displayedIndex; + + public TaskCard() { + + } + + public static TaskCard load(ReadOnlyTask task, int displayedIndex) { + TaskCard card = new TaskCard(); + card.task = task; + card.displayedIndex = displayedIndex; + return UiPartLoader.loadUiPart(card); + } + + //@@author A0147092E + @FXML + public void initialize() { + String dateInfo; + if (task.getIsTask()) { + dateInfo = " by " + task.getDueDate(); + if (task.getIsDone()){ + cardPane.setStyle("-fx-background-color: #78AB46"); + } else { + cardPane.setStyle("-fx-background-color: #ffa54f"); + } + } else if (task.getIsEvent()) { + dateInfo = " from " + task.getStartTime() + " to " + task.getEndTime(); + if (task.getIsDone()) { + cardPane.setStyle("-fx-background-color: #78AB46"); + } else { + cardPane.setStyle("-fx-background-color: #cd8500"); + } + } else { + dateInfo = ""; + if (task.getIsDone()) { + cardPane.setStyle("-fx-background-color: #78AB46"); + } else { + cardPane.setStyle("-fx-background-color: #FFFFFF"); + } + } + dateTime.setText(dateInfo); + title.setText(task.getName().toString()); + id.setText(displayedIndex + ". "); + } + //@@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/flexitrack/ui/TaskListPanel.java b/src/main/java/seedu/flexitrack/ui/TaskListPanel.java new file mode 100644 index 000000000000..53895ac705c6 --- /dev/null +++ b/src/main/java/seedu/flexitrack/ui/TaskListPanel.java @@ -0,0 +1,96 @@ +package seedu.flexitrack.ui; + +import java.util.logging.Logger; + +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.flexitrack.commons.core.LogsCenter; +import seedu.flexitrack.model.task.ReadOnlyTask; + +/** + * Panel containing the list of task. + */ +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; + + public TaskListPanel() { + 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 TaskListPanel load(Stage primaryStage, AnchorPane taskListPlaceholder, + ObservableList taskList) { + TaskListPanel taskListPanel = UiPartLoader.loadUiPart(primaryStage, taskListPlaceholder, new TaskListPanel()); + taskListPanel.configure(taskList); + return taskListPanel; + } + + private void configure(ObservableList taskList) { + setConnections(taskList); + addToPlaceholder(); + } + + private void setConnections(ObservableList taskList) { + taskListView.setItems(taskList); + taskListView.setCellFactory(listView -> new TaskListViewCell()); + } + + private void addToPlaceholder() { + SplitPane.setResizableWithParent(placeHolderPane, false); + placeHolderPane.getChildren().add(panel); + } + + public void scrollTo(int index) { + Platform.runLater(() -> { + taskListView.scrollTo(index); + taskListView.getSelectionModel().clearAndSelect(index); + }); + } + + class TaskListViewCell extends ListCell { + + public TaskListViewCell() { + } + + @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/flexitrack/ui/Ui.java similarity index 70% rename from src/main/java/seedu/address/ui/Ui.java rename to src/main/java/seedu/flexitrack/ui/Ui.java index e6a67fe8c027..3e08451494af 100644 --- a/src/main/java/seedu/address/ui/Ui.java +++ b/src/main/java/seedu/flexitrack/ui/Ui.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.flexitrack.ui; import javafx.stage.Stage; @@ -7,7 +7,7 @@ */ public interface Ui { - /** Starts the UI (and the App). */ + /** Starts the UI (and the App). */ void start(Stage primaryStage); /** Stops the UI. */ diff --git a/src/main/java/seedu/address/ui/UiManager.java b/src/main/java/seedu/flexitrack/ui/UiManager.java similarity index 72% rename from src/main/java/seedu/address/ui/UiManager.java rename to src/main/java/seedu/flexitrack/ui/UiManager.java index 4a4dba3a2f6e..0293473dc2c4 100644 --- a/src/main/java/seedu/address/ui/UiManager.java +++ b/src/main/java/seedu/flexitrack/ui/UiManager.java @@ -1,31 +1,31 @@ -package seedu.address.ui; +package seedu.flexitrack.ui; + +import java.util.logging.Logger; import com.google.common.eventbus.Subscribe; + import javafx.application.Platform; import javafx.scene.control.Alert; 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.ShowHelpRequestEvent; -import seedu.address.commons.util.StringUtil; -import seedu.address.logic.Logic; -import seedu.address.model.UserPrefs; - -import java.util.logging.Logger; +import seedu.flexitrack.MainApp; +import seedu.flexitrack.commons.core.ComponentManager; +import seedu.flexitrack.commons.core.Config; +import seedu.flexitrack.commons.core.LogsCenter; +import seedu.flexitrack.commons.events.storage.DataSavingExceptionEvent; +import seedu.flexitrack.commons.events.ui.JumpToListRequestEvent; +import seedu.flexitrack.commons.events.ui.ShowHelpRequestEvent; +import seedu.flexitrack.commons.util.StringUtil; +import seedu.flexitrack.logic.Logic; +import seedu.flexitrack.model.UserPrefs; /** * The manager of the UI component. */ 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/task_tracker.png"; private Logic logic; private Config config; @@ -44,12 +44,13 @@ public void start(Stage primaryStage) { logger.info("Starting UI..."); primaryStage.setTitle(config.getAppTitle()); - //Set the application icon. + // Set the application icon. primaryStage.getIcons().add(getImage(ICON_APPLICATION)); try { mainWindow = MainWindow.load(primaryStage, config, prefs, logic); - mainWindow.show(); //This should be called before creating other UI parts + mainWindow.show(); // This should be called before creating other UI + // parts mainWindow.fillInnerParts(); } catch (Throwable e) { @@ -62,7 +63,6 @@ public void start(Stage primaryStage) { public void stop() { prefs.updateLastUsedGuiSetting(mainWindow.getCurrentGuiSetting()); mainWindow.hide(); - mainWindow.releaseResources(); } private void showFileOperationAlertAndWait(String description, String details, Throwable cause) { @@ -79,7 +79,7 @@ void showAlertDialogAndWait(Alert.AlertType type, String title, String headerTex } private static void showAlertDialogAndWait(Stage owner, AlertType type, String title, String headerText, - String contentText) { + String contentText) { final Alert alert = new Alert(type); alert.getDialogPane().getStylesheets().add("view/DarkTheme.css"); alert.initOwner(owner); @@ -97,7 +97,8 @@ private void showFatalErrorDialogAndShutdown(String title, Throwable e) { System.exit(1); } - //==================== Event Handling Code ================================================================= + // ==================== Event Handling Code + // ================================================================= @Subscribe private void handleDataSavingExceptionEvent(DataSavingExceptionEvent event) { @@ -114,13 +115,6 @@ private void handleShowHelpEvent(ShowHelpRequestEvent event) { @Subscribe private void handleJumpToListRequestEvent(JumpToListRequestEvent event) { logger.info(LogsCenter.getEventHandlingLogMessage(event)); - mainWindow.getPersonListPanel().scrollTo(event.targetIndex); + mainWindow.getTaskListPanel().scrollTo(event.targetIndex); } - - @Subscribe - private void handlePersonPanelSelectionChangedEvent(PersonPanelSelectionChangedEvent event){ - logger.info(LogsCenter.getEventHandlingLogMessage(event)); - mainWindow.loadPersonPage(event.getNewSelection()); - } - } diff --git a/src/main/java/seedu/address/ui/UiPart.java b/src/main/java/seedu/flexitrack/ui/UiPart.java similarity index 68% rename from src/main/java/seedu/address/ui/UiPart.java rename to src/main/java/seedu/flexitrack/ui/UiPart.java index 0a4ceb33e9b7..03b75f8113ba 100644 --- a/src/main/java/seedu/address/ui/UiPart.java +++ b/src/main/java/seedu/flexitrack/ui/UiPart.java @@ -1,17 +1,17 @@ -package seedu.address.ui; +package seedu.flexitrack.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.flexitrack.commons.core.EventsCenter; +import seedu.flexitrack.commons.events.BaseEvent; +import seedu.flexitrack.commons.util.AppUtil; /** - * Base class for UI parts. - * A 'UI part' represents a distinct part of the UI. e.g. Windows, dialogs, panels, status bars, etc. + * Base class for UI parts. A 'UI part' represents a distinct part of the UI. + * e.g. Windows, dialogs, panels, status bars, etc. */ public abstract class UiPart { @@ -20,34 +20,41 @@ public abstract class UiPart { */ Stage primaryStage; - public UiPart(){ + public UiPart() { } /** * Raises the event via {@link EventsCenter#post(BaseEvent)} + * * @param event */ - protected void raise(BaseEvent event){ + protected void raise(BaseEvent event) { EventsCenter.getInstance().post(event); } /** * Registers the object as an event handler at the {@link EventsCenter} - * @param handler usually {@code this} + * + * @param handler + * usually {@code this} */ protected void registerAsAnEventHandler(Object handler) { EventsCenter.getInstance().registerHandler(handler); } /** - * Override this method to receive the main Node generated while loading the view from the .fxml file. + * Override this method to receive the main Node generated while loading the + * view from the .fxml file. + * * @param node */ public abstract void setNode(Node node); /** - * Override this method to return the name of the fxml file. e.g. {@code "MainWindow.fxml"} + * Override this method to return the name of the fxml file. e.g. + * {@code "MainWindow.fxml"} + * * @return */ public abstract String getFxmlPath(); @@ -56,12 +63,15 @@ public void setStage(Stage primaryStage) { this.primaryStage = primaryStage; } - /** * Creates a modal dialog. - * @param title Title of the dialog. - * @param parentStage The owner stage of the dialog. - * @param scene The scene that will contain the dialog. + * + * @param title + * Title of the dialog. + * @param parentStage + * The owner stage of the dialog. + * @param scene + * The scene that will contain the dialog. * @return the created dialog, not yet made visible. */ protected Stage createDialogStage(String title, Stage parentStage, Scene scene) { @@ -75,7 +85,9 @@ protected Stage createDialogStage(String title, Stage parentStage, Scene scene) /** * Sets the given image as the icon for the primary stage of this UI Part. - * @param iconSource e.g. {@code "/images/help_icon.png"} + * + * @param iconSource + * e.g. {@code "/images/help_icon.png"} */ protected void setIcon(String iconSource) { primaryStage.getIcons().add(AppUtil.getImage(iconSource)); @@ -83,8 +95,10 @@ protected void setIcon(String iconSource) { /** * Sets the given image as the icon for the given stage. + * * @param stage - * @param iconSource e.g. {@code "/images/help_icon.png"} + * @param iconSource + * e.g. {@code "/images/help_icon.png"} */ protected void setIcon(Stage stage, String iconSource) { stage.getIcons().add(AppUtil.getImage(iconSource)); @@ -92,10 +106,11 @@ protected void setIcon(Stage stage, String iconSource) { /** * Sets the placeholder for UI parts that reside inside another UI part. + * * @param placeholder */ public void setPlaceholder(AnchorPane placeholder) { - //Do nothing by default. + // Do nothing by default. } public Stage getPrimaryStage() { diff --git a/src/main/java/seedu/address/ui/UiPartLoader.java b/src/main/java/seedu/flexitrack/ui/UiPartLoader.java similarity index 76% rename from src/main/java/seedu/address/ui/UiPartLoader.java rename to src/main/java/seedu/flexitrack/ui/UiPartLoader.java index f880685a5b15..9903b6b572ba 100644 --- a/src/main/java/seedu/address/ui/UiPartLoader.java +++ b/src/main/java/seedu/flexitrack/ui/UiPartLoader.java @@ -1,10 +1,10 @@ -package seedu.address.ui; +package seedu.flexitrack.ui; import javafx.fxml.FXMLLoader; import javafx.scene.Node; import javafx.scene.layout.AnchorPane; import javafx.stage.Stage; -import seedu.address.MainApp; +import seedu.flexitrack.MainApp; /** * A utility class to load UiParts from FXML files. @@ -19,10 +19,14 @@ public static T loadUiPart(Stage primaryStage, T controllerSe /** * Returns the ui class for a specific UI Part. * - * @param primaryStage The primary stage for the view. - * @param placeholder The placeholder where the loaded Ui Part is added. - * @param sampleUiPart The sample of the expected UiPart class. - * @param The type of the UiPart + * @param primaryStage + * The primary stage for the view. + * @param placeholder + * The placeholder where the loaded Ui Part is added. + * @param sampleUiPart + * The sample of the expected UiPart class. + * @param + * The type of the UiPart */ public static T loadUiPart(Stage primaryStage, AnchorPane placeholder, T sampleUiPart) { FXMLLoader loader = new FXMLLoader(); @@ -32,14 +36,16 @@ public static T loadUiPart(Stage primaryStage, AnchorPane pla controller.setStage(primaryStage); controller.setPlaceholder(placeholder); controller.setNode(mainNode); - return (T)controller; + return (T) controller; } /** * Returns the ui class for a specific UI Part. * - * @param seedUiPart The UiPart object to be used as the ui. - * @param The type of the UiPart + * @param seedUiPart + * The UiPart object to be used as the ui. + * @param + * The type of the UiPart */ public static T loadUiPart(T seedUiPart) { @@ -50,7 +56,6 @@ public static T loadUiPart(T seedUiPart) { return seedUiPart; } - private static Node loadLoader(FXMLLoader loader, String fxmlFileName) { try { return loader.load(); diff --git a/src/main/resources/images/task_tracker.png b/src/main/resources/images/task_tracker.png new file mode 100644 index 000000000000..f2feed7649e1 Binary files /dev/null and b/src/main/resources/images/task_tracker.png differ diff --git a/src/main/resources/view/CommandBox.fxml b/src/main/resources/view/CommandBox.fxml index 575de420b994..aa643dd67471 100644 --- a/src/main/resources/view/CommandBox.fxml +++ b/src/main/resources/view/CommandBox.fxml @@ -3,7 +3,7 @@ + fx:controller="seedu.flexitrack.ui.CommandBox" stylesheets="@DarkTheme.css"> diff --git a/src/main/resources/view/DarkTheme.css b/src/main/resources/view/DarkTheme.css index 8043b344253a..471769abeaba 100644 --- a/src/main/resources/view/DarkTheme.css +++ b/src/main/resources/view/DarkTheme.css @@ -1,5 +1,6 @@ +/*@@author A0147092E*/ .background { - -fx-background-color: derive(#1d1d1d, 20%); + -fx-background-color: derive(#4d2600, 20%); } .label { @@ -39,9 +40,9 @@ } .table-view { - -fx-base: #1d1d1d; - -fx-control-inner-background: #1d1d1d; - -fx-background-color: #1d1d1d; + -fx-base: #4d2600; + -fx-control-inner-background: #4d2600; + -fx-background-color: #4d2600; -fx-table-cell-border-color: transparent; -fx-table-header-border-color: transparent; -fx-padding: 5; @@ -76,14 +77,14 @@ } .split-pane:horizontal .split-pane-divider { - -fx-border-color: transparent #1d1d1d transparent #1d1d1d; - -fx-background-color: transparent, derive(#1d1d1d, 10%); + -fx-border-color: transparent #4d2600 transparent #4d2600; + -fx-background-color: transparent, derive(#4d2600, 10%); } .split-pane { -fx-border-radius: 1; -fx-border-width: 1; - -fx-background-color: derive(#1d1d1d, 20%); + -fx-background-color: derive(#4d2600, 20%); } .list-cell { @@ -97,32 +98,33 @@ } .cell_big_label { - -fx-font-size: 16px; + -fx-font-size: 18px; + -fx-text-overrun : leading-word-ellipsis; -fx-text-fill: #010504; } .cell_small_label { - -fx-font-size: 11px; + -fx-font-size: 12px; -fx-text-fill: #010504; } .anchor-pane { - -fx-background-color: derive(#1d1d1d, 20%); + -fx-background-color: derive(#4d2600, 20%); } .anchor-pane-with-border { - -fx-background-color: derive(#1d1d1d, 20%); - -fx-border-color: derive(#1d1d1d, 10%); + -fx-background-color: derive(#4d2600, 20%); + -fx-border-color: derive(#4d2600, 10%); -fx-border-top-width: 1px; } .status-bar { - -fx-background-color: derive(#1d1d1d, 20%); - -fx-text-fill: black; + -fx-background-color: derive(#4d2600, 20%); + -fx-text-fill: white; } .result-display { - -fx-background-color: #ffffff; + -fx-background-color: #4d2600; } .result-display .label { @@ -134,8 +136,8 @@ } .status-bar-with-border { - -fx-background-color: derive(#1d1d1d, 30%); - -fx-border-color: derive(#1d1d1d, 25%); + -fx-background-color: derive(#4d2600, 30%); + -fx-border-color: derive(#4d2600, 25%); -fx-border-width: 1px; } @@ -144,17 +146,17 @@ } .grid-pane { - -fx-background-color: derive(#1d1d1d, 30%); - -fx-border-color: derive(#1d1d1d, 30%); + -fx-background-color: derive(#4d2600, 30%); + -fx-border-color: derive(#4d2600, 30%); -fx-border-width: 1px; } .grid-pane .anchor-pane { - -fx-background-color: derive(#1d1d1d, 30%); + -fx-background-color: derive(#4d2600, 30%); } .context-menu { - -fx-background-color: derive(#1d1d1d, 50%); + -fx-background-color: derive(#4d2600, 50%); } .context-menu .label { @@ -162,7 +164,7 @@ } .menu-bar { - -fx-background-color: derive(#1d1d1d, 20%); + -fx-background-color: derive(#4d2600, 10%); } .menu-bar .label { @@ -183,13 +185,13 @@ */ .button { -fx-padding: 5 22 5 22; - -fx-border-color: #e2e2e2; + -fx-border-color: #4d2600; -fx-border-width: 2; -fx-background-radius: 0; - -fx-background-color: #1d1d1d; + -fx-background-color: #4d2600; -fx-font-family: "Segoe UI", Helvetica, Arial, sans-serif; -fx-font-size: 11pt; - -fx-text-fill: #d8d8d8; + -fx-text-fill: #4d2600; -fx-background-insets: 0 0 0 0, 0, 1, 2; } @@ -199,7 +201,7 @@ .button:pressed, .button:default:hover:pressed { -fx-background-color: white; - -fx-text-fill: #1d1d1d; + -fx-text-fill: #4d2600; } .button:focused { @@ -212,7 +214,7 @@ .button:disabled, .button:default:disabled { -fx-opacity: 0.4; - -fx-background-color: #1d1d1d; + -fx-background-color: #4d2600; -fx-text-fill: white; } @@ -226,11 +228,11 @@ } .dialog-pane { - -fx-background-color: #1d1d1d; + -fx-background-color: #4d2600; } .dialog-pane > *.button-bar > *.container { - -fx-background-color: #1d1d1d; + -fx-background-color: #4d2600; } .dialog-pane > *.label.content { @@ -240,7 +242,7 @@ } .dialog-pane:header *.header-panel { - -fx-background-color: derive(#1d1d1d, 25%); + -fx-background-color: derive(#4d2600, 25%); } .dialog-pane:header *.header-panel *.label { @@ -251,7 +253,7 @@ } .scroll-bar .thumb { - -fx-background-color: derive(#1d1d1d, 50%); + -fx-background-color: derive(#4d2600, 50%); -fx-background-insets: 3; } diff --git a/src/main/resources/view/HelpWindow.fxml b/src/main/resources/view/HelpWindow.fxml index c4cbd84cac28..3cf38caac979 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..03699090d74e 100644 --- a/src/main/resources/view/MainWindow.fxml +++ b/src/main/resources/view/MainWindow.fxml @@ -4,7 +4,7 @@ - + @@ -29,26 +29,21 @@ - + - + - + - + - - - - - diff --git a/src/main/resources/view/ResultDisplay.fxml b/src/main/resources/view/ResultDisplay.fxml index cc650d739e22..476bce682444 100644 --- a/src/main/resources/view/ResultDisplay.fxml +++ b/src/main/resources/view/ResultDisplay.fxml @@ -3,7 +3,7 @@ + fx:controller="seedu.flexitrack.ui.ResultDisplay"> diff --git a/src/main/resources/view/StatusBarFooter.fxml b/src/main/resources/view/StatusBarFooter.fxml index 2656558b6eb7..f86dd80b21b4 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/PersonListCard.fxml b/src/main/resources/view/TaskListCard.fxml similarity index 73% rename from src/main/resources/view/PersonListCard.fxml rename to src/main/resources/view/TaskListCard.fxml index 13d4b149651b..ac8c1e53ff42 100644 --- a/src/main/resources/view/PersonListCard.fxml +++ b/src/main/resources/view/TaskListCard.fxml @@ -12,7 +12,7 @@ - + @@ -26,14 +26,13 @@ - - - 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..b965b8cd3fc3 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..73af99a08a02 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", + "flexiTrackFilePath" : "tasktracker.xml", + "flexiTrackName" : "TypicalTaskTracker", "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..ef786bafa8a3 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" + "flexiTrackFilePath" : "tasktracker.xml", + "flexiTrackName" : "TypicalTaskTracker" } \ 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..5833d4fe3009 --- /dev/null +++ b/src/test/data/ManualTesting/SampleData.xml @@ -0,0 +1,309 @@ + + + + Cook dinner for family + Feb 29 2000 00:00 + Feb 29 2000 00:00 + Feb 29 2000 00:00 + + + Cut hair + Feb 29 2000 00:00 + Feb 29 2000 00:00 + Feb 29 2000 00:00 + + + Run 5km + Feb 29 2000 00:00 + Feb 29 2000 00:00 + Feb 29 2000 00:00 + + + Watch Jack Reacher Never Go Back + Feb 29 2000 00:00 + Feb 29 2000 00:00 + Feb 29 2000 00:00 + + + Watch Trolls + Feb 29 2000 00:00 + Feb 29 2000 00:00 + Feb 29 2000 00:00 + + + (Blocked) Mum's bday dinner + Feb 29 2000 00:00 + Oct 30 2016 19:00 + Oct 30 2016 21:00 + + + Tuition @Serangoon + Feb 29 2000 00:00 + Nov 06 2016 10:00 + Nov 06 2016 12:00 + + + chemistry tuition + Feb 29 2000 00:00 + Nov 07 2016 19:00 + Nov 07 2016 21:00 + + + CN4122 Project + Nov 07 2016 23:59 + Feb 29 2000 00:00 + Feb 29 2000 00:00 + + + CS2103 IVLE submission + Nov 08 2016 08:00 + Feb 29 2000 00:00 + Feb 29 2000 00:00 + + + PC1222 Mid-term 2 + Feb 29 2000 00:00 + Nov 08 2016 09:00 + Nov 08 2016 11:00 + + + Watch CN3132 Webcast + Nov 08 2016 17:00 + Feb 29 2000 00:00 + Feb 29 2000 00:00 + + + CN 3135 Tutorial + Feb 29 2000 00:00 + Nov 09 2016 09:00 + Nov 14 2016 10:00 + + + Physics Tuition + Feb 29 2000 00:00 + Nov 09 2016 19:00 + Nov 09 2016 21:00 + + + CN 3132 exam + Feb 29 2000 00:00 + Nov 10 2016 09:00 + Nov 10 2016 22:00 + + + CN3421 Homework + Nov 10 2016 17:00 + Feb 29 2000 00:00 + Feb 29 2000 00:00 + + + Group study + Feb 29 2000 00:00 + Nov 12 2016 08:00 + Nov 12 2016 17:00 + + + Tuition @Serangoon + Feb 29 2000 00:00 + Nov 13 2016 10:00 + Nov 13 2016 12:00 + + + Church + Feb 29 2000 00:00 + Nov 13 2016 11:15 + Nov 13 2016 14:00 + + + Buy grocery for the week + Nov 13 2016 17:00 + Feb 29 2000 00:00 + Feb 29 2000 00:00 + + + Floorball match against albatros + Feb 29 2000 00:00 + Nov 13 2016 18:00 + Nov 13 2016 21:00 + + + chemistry tuition + Feb 29 2000 00:00 + Nov 14 2016 19:00 + Nov 14 2016 21:00 + + + Physics Tuition + Feb 29 2000 00:00 + Nov 16 2016 19:00 + Nov 16 2016 21:00 + + + Tuition @Serangoon + Feb 29 2000 00:00 + Nov 20 2016 10:00 + Nov 20 2016 12:00 + + + Church + Feb 29 2000 00:00 + Nov 20 2016 11:15 + Nov 20 2016 14:00 + + + Buy grocery for the week + Nov 20 2016 17:00 + Feb 29 2000 00:00 + Feb 29 2000 00:00 + + + chemistry tuition + Feb 29 2000 00:00 + Nov 21 2016 19:00 + Nov 21 2016 21:00 + + + Dentist appointment + Feb 29 2000 00:00 + Nov 22 2016 15:00 + Nov 22 2016 17:00 + + + Lunch with JC friends + Feb 29 2000 00:00 + Nov 23 2016 12:00 + Nov 23 2016 14:00 + + + Physics Tuition + Feb 29 2000 00:00 + Nov 23 2016 19:00 + Nov 23 2016 21:00 + + + CS1231 exam + Feb 29 2000 00:00 + Nov 26 2016 13:00 + Nov 26 2016 15:00 + + + Tuition @Serangoon + Feb 29 2000 00:00 + Nov 27 2016 10:00 + Nov 27 2016 12:00 + + + Church + Feb 29 2000 00:00 + Nov 27 2016 11:15 + Nov 27 2016 14:00 + + + Buy grocery for the week + Nov 27 2016 17:00 + Feb 29 2000 00:00 + Feb 29 2000 00:00 + + + (Blocked) Cooking class + Feb 29 2000 00:00 + Dec 02 2016 09:00 + Dec 02 2016 15:00 + + + (Blocked) HuiLi's Bday Party + Feb 29 2000 00:00 + Dec 03 2016 16:00 + Dec 03 2016 23:00 + + + Tuition @Serangoon + Feb 29 2000 00:00 + Dec 04 2016 10:00 + Dec 04 2016 12:00 + + + Buy grocery for the week + Dec 04 2016 17:00 + Feb 29 2000 00:00 + Feb 29 2000 00:00 + + + (Blocked) Cooking class + Feb 29 2000 00:00 + Dec 09 2016 09:00 + Dec 09 2016 15:00 + + + Buy grocery for the week + Dec 11 2016 17:00 + Feb 29 2000 00:00 + Feb 29 2000 00:00 + + + Buy grocery for the week + Dec 18 2016 17:00 + Feb 29 2000 00:00 + Feb 29 2000 00:00 + + + Attend Christmas Mass + Feb 29 2000 00:00 + Dec 24 2016 21:00 + Dec 25 2016 00:00 + + + Holiday!!!!! + Feb 29 2000 00:00 + Dec 26 2016 08:00 + Jan 02 2017 17:00 + + + (Done) CS IVLE QUIZ + Oct 04 2016 22:00 + Feb 29 2000 00:00 + Feb 29 2000 00:00 + + + (Done) CS IVLE QUIZ + Oct 11 2016 22:00 + Feb 29 2000 00:00 + Feb 29 2000 00:00 + + + (Done) CS IVLE QUIZ + Oct 18 2016 22:00 + Feb 29 2000 00:00 + Feb 29 2000 00:00 + + + (Done) Tuition @Serangoon + Feb 29 2000 00:00 + Oct 30 2016 10:00 + Oct 30 2016 12:00 + + + (Done) Floorball match against litto + Feb 29 2000 00:00 + Oct 30 2016 18:00 + Oct 30 2016 20:00 + + + (Done) CN 3135 Tutorial + Feb 29 2000 00:00 + Nov 02 2016 09:00 + Nov 02 2016 10:00 + + + (Done) CS1231 Lecture + Feb 29 2000 00:00 + Nov 04 2016 14:00 + Nov 04 2016 16:00 + + + (Done) CN4122 Viva + Feb 29 2000 00:00 + Nov 07 2016 16:00 + Nov 07 2016 18:00 + + diff --git a/src/test/data/ManualTesting/TestScript.md b/src/test/data/ManualTesting/TestScript.md new file mode 100644 index 000000000000..1f045053c06a --- /dev/null +++ b/src/test/data/ManualTesting/TestScript.md @@ -0,0 +1,382 @@ +//@@author A0127855W +------ +# Test Script +------ +> **Note** +- Dates and times depend on current system time and will differ depending on when the testing is being carried out.
+ +------ +## Load Sample Data +------ +> **Steps:** +- 1. Copy `SampleData.xml` into the same folder as `FlexiTrack.jar` +- 2. Rename `SampleData.xml` into `tasktracker.xml` +- 2. Run `FlexiTrack.jar` + +------ +## 0. Help Command +------ +### 0.1 Overall help +> **Command:** `help`
+> **Result:** +- Result display panel posts message:
+`help: Shows program usage instructions.`
+`List of Commands: add, clear, delete, edit, exit, find, list, mark, unmark, block, cs(Change Storage Path), undo, redo, gap `
+`Example: help clear` + +### 0.2 Specific command help +> **Command:** `h edit`
+> **Result:** +- Result display panel posts message:
+`edit, Shortcut [e]: Edits the specified task attributes of the task identified by the index number used in the last task listing.`
+`Parameters to edit an event: [index] (must be a posi
tive integer) from/ [starting time] to/ [ending time]` +`Example: edit 1 from/ 01062016 to/ 01/072016`
+`Parameters to edit a task: [index] (must be a positive integer) by/ [due date]`
+`Example: edit 1 by/ 01062016` + +------ +## 1. Add Command +------ +### 1.1 Add a floating task +> **Command:** `add Floating Task`
+> **Result:**
+- Result display panel posts message:
+`New task added: Floating Task`
+- TaskList panel displays newly added card which is white in color. + +### 1.2 Add a task +> **Command:** `add Task by/Sunday 9am`
+> **Result:**
+- Result display panel posts message:
+`New task added: Task by/Nov 13 2016 09:00`
+- TaskList panel displays newly added card which is orange in color. + +### 1.3 Add an event +> **Command:** `a Event from/ Sunday to/ Monday 10am`
+> **Result:**
+- Result display panel posts message:
+`New task added: Event from/Nov 13 2016 08:00 to/Nov 14 2016 10:00`
+`Duration of the event is: 1 day 2 hours.`
+`Warning: this event is overlaping a existing event!`
+- TaskList panel displays newly added card which is brown in color. + +### 1.4 Add a recurring task +> **Command:** `add Recurring Task fr/3 ty/week by/tuesday`
+> **Result:**
+- Result display panel posts message:
+`New task added: Recurring Task by/Nov 08 2016 08:00`. (Deadline of first added task)
+- TaskList panel displays 3 newly added cards which are orange in color. + +### 1.5 Add a recurring event +> **Command:** `a Recurring Event fr/2 ty/day from/tomorrow 8am to/ tomorrow 9am`
+> **Result:**
+- Result display panel posts message:
+`New task added: Recurring Event from/Nov 08 2016 08:00 to/Nov 08 2016 09:00`
+`Duration of the event is: 1 hour.` (Info of the first added event)
+`Warning: this event is overlaping a existing event!`
+- TaskList panel displays 2 newly added cards which are brown in color. + +------ +## 2. Block Command +------ +### 2.1 Block Time +> **Command:** `block Visit Gramdma?? from/christmas to/christmas`
+> **Result:**
+- Result display panel posts message:
+`Block the date for (Blocked) Visit Grandma?? from/Dec 25 2016 08:00 to/Dec 25 2016 17:00`
+`Duration of the event is: 9 hours.`
+- TaskList panel displays the newly added card which is brown in color and contains the prefix '(Blocked)'. + +### 2.2 Add event over blocked time +> **Command:** `add Shopping from/christmas 10am to/christmas 12pm`
+> **Result:**
+- Result display panel posts message:
+`This period of time has already taken by other event, Please choose another time.` + +------ +## 3. Gap Command +------ +### 3.1 Find next available slot +> **Command:** `gap 2 hours n/1`
+> **Result:**
+- Result display panel posts message:
+`The earliest 2 hours free time are found... `
+`Between: Nov 07 2016 21:00 to: Nov 08 2016 08:00` + +### 3.2 Find next few available slots +> **Command:** `g 4 hours`
+> **Result:**
+- Result display panel posts message:
+`The earliest 4 hours free time are found... `
+`Between: Nov 07 2016 21:00 to: Nov 08 2016 08:00`
+`Between: Nov 08 2016 11:00 to: Nov 09 2016 08:00`
+`Between: Nov 14 2016 10:00 to: Nov 14 2016 19:00` + +------ +## 4. Delete Command +------ +### 4.1 Delete by index +> **Command:** `delete 1`
+> **Result:**
+- Result display panel posts message:
+`Deleted task: Cook dinner for family`
+- TaskList panel updates without the deleted task. + +### 4.2 Delete index out of bounds +> **Command:** `delete 100`
+> **Result:**
+- Result display panel posts message:
+`The task index provided is invalid `
+ +------ +## 5. Edit Command +------ +### 5.1 Edit name +> **Command:** `edit 1 n/Name Edited`
+> **Result:**
+- Result display panel posts message:
+`Edited: Cut hair into Cut hair @ beauty salon`
+- TaskList panel updates with the edited task card. + +### 5.2 Edit due date +> **Command:** `edit 9 by/ nov 9 11:59pm`
+> **Result:**
+- Result display panel posts message:
+`Edited: CN4122 Project into CN4122 Project`
+- TaskList panel updates with the edited task card. + +### 5.3 Edit start time +> **Command:** `edit 7 from/nov 6 11am`
+> **Result:**
+- Result display panel posts message:
+`Edited: Tuition @Serangoon into Tuition @Serangoon`
+`Duration of the event is: 1 hour.`
+- TaskList panel updates with the edited task card. + +### 5.4 Edit end time +> **Command:** `edit 7 to/nov 6 1pm`
+> **Result:**
+- Result display panel posts message:
+`Edited: Tuition @Serangoon into Tuition @Serangoon`
+`Duration of the event is: 2 hours.`
+- TaskList panel updates with the edited task card. + +### 5.5 Edit floating task into task +> **Command:** `edit 1 by/ next tuesday 4pm`
+> **Result:**
+- Result display panel posts message:
+`Edited: Cut hair @ beauty salon into Cut hair @ beauty salon`
+- TaskList panel updates with the edited task card which is now orange. + +### 5.6 Edit floating task into event +> **Command:** `edit 2 from/ tomorrow 5pm to/ tomorrow 6pm`
+> **Result:**
+- Result display panel posts message:
+`Edited: Run 5km into Run 5km`
+`Duration of the event is: 1 hour.`
+- TaskList panel updates with the edited task card which is now brown. + +### 5.7 Edit multiple parameters in any order (valid) +> **Command:** `edit 6 to/ nov 7 7pm from/ nov 7 5pm n/Chemistry Tuition @ Cantonment`
+> **Result:**
+- Result display panel posts message:
+`Edited: chemistry tuition into Chemistry Tuition @ Cantonment` +`Duration of the event is: 2 hours.`
+- TaskList panel updates with the edited task card. + +------ +## 6. Undo Command +------ +### 6.1 Undo +> **Command:** `undo`
+> **Result:**
+- Result display panel posts message:
+`Your last command has been undone!`
+`Undid edit: chemistry tuition into Chemistry Tuition @ Cantonment`
+- TaskList panel reverts to the state before part 5.7. + +### 6.2 Undo shortcut +> **Command:** `ud`
+> **Result:**
+- Result display panel posts message:
+`Your last command has been undone!`
+`Undid edit: Run 5km into Run 5km `
+- TaskList panel reverts to the state before part 5.6. + +------ +## 7. Redo Command +------ +### 7.1 Redo +> **Command:** `redo`
+> **Result:**
+- Result display panel posts message:
+`Your last command has been redone!`
+`Edited: Run 5km into Run 5km`
+`Duration of the event is: 1 hour.`
+- TaskList panel reverts to the state after part 5.6. + +### 7.2 Redo shortcut +> **Command:** `rd`
+> **Result:**
+- Result display panel posts message:
+`Your last command has been redone!`
+`Edited: chemistry tuition into Chemistry Tuition @ Cantonment`
+`Duration of the event is: 2 hours. `
+- TaskList panel reverts to the state after part 5.7. + +------ +## Test Storage +------ +> **Steps:** +- 1. Close application +- 2. Run `FlexiTrack.jar` +> **Result:**
+- Application should have loaded with the same data as before exiting. + +------ +## 8. Mark Command +------ +### 8.1 Mark by index +> **Command:** `mark 5`
+> **Result:**
+- Result display panel posts message:
+`Marked Task: 5`
+- TaskList panel updates with the updated card which is now green with the prefix '(Done)'. + +### 8.2 Mark something already marked +> **Command:** `m 59`
+> **Result:**
+- Result display panel posts message:
+`Task already marked! `
+ +------ +## 9. Unmark Command +------ +### 9.1 Unmark by index +> **Command:** `unmark 51`
+> **Result:**
+- Result display panel posts message:
+`Unmark Task: 51`
+- TaskList panel updates with the updated card which is now orange. + +### 9.2 Unmark something not marked +> **Command:** `unmark 1`
+> **Result:**
+- Result display panel posts message:
+`Task already unmarked!`
+ +------ +## 10. Find Command +------ +### 10.1 Find by any keyword +> **Command:** `find chemistry tuition`
+> **Result:**
+- Result display panel posts message:
+`12 tasks listed!`
+- TaskList panel updates to show any card with 'chemistry' or 'tuition' in the name. + +### 10.2 Find by exact phrase +> **Command:** `f f/ Chemistry Tuition`
+> **Result:**
+- Result display panel posts message:
+`3 tasks listed!`
+- TaskList panel updates to show any card with the exact phrase 'Chemistry Tuition' (not case sensitive) in the name. + +------ +## 11. List Command +------ +### 11.1 List future +> **Command:** `list future`
+> **Result:**
+- Result display panel posts message:
+`Listed all tasks`
+- TaskList panel updates to show only future unmarked cards. Floating tasks are also shown. + +### 11.2 List past +> **Command:** `list past`
+> **Result:**
+- Result display panel posts message:
+`Listed all tasks`
+- TaskList panel updates to show only past and marked cards. Floating tasks are also shown. + +### 11.3 List specific date +> **Command:** `list christmas`
+> **Result:**
+- Result display panel posts message:
+`Listed all tasks`
+- TaskList panel updates to show only cards which either are due on the specific date entered, or occur on that day (may start earlier and end later). + +### 11.4 List past time frame +> **Command:** `list last week`
+> **Result:**
+- Result display panel posts message:
+`Listed all tasks`
+- TaskList panel updates to show only cards that fall in that time frame. + +### 11.5 List future time frame +> **Command:** `list next month`
+> **Result:**
+- Result display panel posts message:
+`Listed all tasks`
+- TaskList panel updates to show only cards that fall in that time frame. + +### 11.6 List marked items +> **Command:** `l mark`
+> **Result:**
+- Result display panel posts message:
+`Listed all tasks`
+- TaskList panel updates to show only marked cards. + +### 11.7 List unmarked items +> **Command:** `l unmark`
+> **Result:**
+- Result display panel posts message:
+`Listed all tasks`
+- TaskList panel updates to show only unmarked cards. + +### 11.8 List blocks +> **Command:** `l block`
+> **Result:**
+- Result display panel posts message:
+`Listed all tasks`
+- TaskList panel updates to show only blocks. + +### 11.9 List all +> **Command:** `l`
+> **Result:**
+- Result display panel posts message:
+`Listed all tasks`
+- TaskList panel updates to show all items. + +------ +## 12. Clear Command +------ +### 12.1 Clear +> **Command:** `clear`
+> **Result:**
+- Result display panel posts message:
+`FlexiTrack has been cleared!`
+- TaskList panel updates to become empty. +- All data is deleted. + +------ +## 13. Change Storage Command +------ +### 13.1 Change storage +> **Command:** `cs newfile`
+> **Result:**
+- Result display panel posts message:
+`Storage location changed: newfile.xml`
+- newfile.xml is created in the FlexiTrack.xml directory and will be used as the new storage file. + +------ +## 14. Exit Command +------ +### 14.1 exit +> **Command:** `q`
+> **Result:**
+- FlexiTrack exits. + +------ +## End +------ diff --git a/src/test/data/XmlAddressBookStorageTest/NotXmlFormatAddressBook.xml b/src/test/data/XmlFlexiTrackStorageTest/NotXmlFormatFlexiTrack.xml similarity index 100% rename from src/test/data/XmlAddressBookStorageTest/NotXmlFormatAddressBook.xml rename to src/test/data/XmlFlexiTrackStorageTest/NotXmlFormatFlexiTrack.xml diff --git a/src/test/data/XmlUtilTest/tempAddressBook.xml b/src/test/data/XmlUtilTest/tempFlexiTrack.xml similarity index 100% rename from src/test/data/XmlUtilTest/tempAddressBook.xml rename to src/test/data/XmlUtilTest/tempFlexiTrack.xml 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/validFlexiTrack.xml b/src/test/data/XmlUtilTest/validFlexiTrack.xml new file mode 100644 index 000000000000..b23528b3d182 --- /dev/null +++ b/src/test/data/XmlUtilTest/validFlexiTrack.xml @@ -0,0 +1,57 @@ + + + + Homework cs 2103 + Jan 11 2016 17:00 + Feb 29 2000 00:00 + Feb 29 2000 00:00 + + + Homework cs 2101 + Sep 01 2016 13:00 + Feb 29 2000 00:00 + Feb 29 2000 00:00 + + + Homework ma 1505 + Feb 29 2000 00:00 + Feb 29 2000 00:00 + Feb 29 2000 00:00 + + + Soccer training + Feb 29 2000 00:00 + Jun 30 2016 21:00 + Jun 30 2016 23:00 + + + Dinner with parents + Feb 29 2000 00:00 + Nov 16 2016 19:00 + Nov 16 2016 20:15 + + + MA 1505 Exams + Feb 29 2000 00:00 + May 12 2016 10:00 + May 12 2016 11:30 + + + Midter cs 2101 + Mar 23 2017 09:00 + Feb 29 2000 00:00 + Feb 29 2000 00:00 + + + Basketball training + Feb 29 2000 00:00 + Dec 25 2016 18:00 + Dec 25 2016 21:00 + + + Apply job in starbucks + Feb 29 2000 00:00 + Feb 29 2000 00:00 + Feb 29 2000 00:00 + + diff --git a/src/test/java/guitests/AddCommandTest.java b/src/test/java/guitests/AddCommandTest.java index 3b2e1844bd0d..dd5043737157 100644 --- a/src/test/java/guitests/AddCommandTest.java +++ b/src/test/java/guitests/AddCommandTest.java @@ -1,53 +1,110 @@ package guitests; -import guitests.guihandles.PersonCardHandle; +import static org.junit.Assert.assertTrue; + 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 guitests.guihandles.TaskCardHandle; +import seedu.flexitrack.commons.core.Messages; +import seedu.flexitrack.commons.exceptions.IllegalValueException; +import seedu.flexitrack.logic.commands.AddCommand; +import seedu.flexitrack.testutil.TaskBuilder; +import seedu.flexitrack.testutil.TestTask; +import seedu.flexitrack.testutil.TestUtil; +import seedu.flexitrack.testutil.TypicalTestTasks; + +//@@author A0127686R +public class AddCommandTest extends FlexiTrackGuiTest { -public class AddCommandTest extends AddressBookGuiTest { + TestTask[] currentList = td.getTypicalSortedTasks(); + TestTask taskToAdd; @Test - public void add() { - //add one person - TestPerson[] currentList = td.getTypicalPersons(); - TestPerson personToAdd = td.hoon; - assertAddSuccess(personToAdd, currentList); - currentList = TestUtil.addPersonsToList(currentList, personToAdd); + public void add_AnEvent_success() { + TestTask[] currentList = td.getTypicalSortedTasks(); + taskToAdd = TypicalTestTasks.basketball; + assertAddSuccess(taskToAdd, currentList); + currentList = TestUtil.addTasksToList(currentList, taskToAdd); + } - //add another person - personToAdd = td.ida; - assertAddSuccess(personToAdd, currentList); - currentList = TestUtil.addPersonsToList(currentList, personToAdd); + @Test + public void add_ADeadLineTask_success() { + taskToAdd = TypicalTestTasks.lecture; + assertAddSuccess(taskToAdd, currentList); + currentList = TestUtil.addTasksToList(currentList, taskToAdd); + } - //add duplicate person - commandBox.runCommand(td.hoon.getAddCommand()); - assertResultMessage(AddCommand.MESSAGE_DUPLICATE_PERSON); - assertTrue(personListPanel.isListMatching(currentList)); + @Test + public void add_AFloatingTask_success() { + taskToAdd = TypicalTestTasks.job; + assertAddSuccess(taskToAdd, currentList); + currentList = TestUtil.addTasksToList(currentList, taskToAdd); - //add to empty list + } + + @Test + public void add_ADuplicateTask_fail() { + commandBox.runCommand(TypicalTestTasks.soccer.getAddCommand()); + assertResultMessage(AddCommand.MESSAGE_DUPLICATE_TASK); + assertTrue(taskListPanel.isListMatching(currentList)); + } + + @Test + public void add_ToAnEmptyList_success() { commandBox.runCommand("clear"); - assertAddSuccess(td.alice); + assertAddSuccess(TypicalTestTasks.homework1); - //invalid command - commandBox.runCommand("adds Johnny"); + } + + @Test + public void add_invalidAddCommand_fail() { + commandBox.runCommand("adds cs tutorial"); assertResultMessage(Messages.MESSAGE_UNKNOWN_COMMAND); } - private void assertAddSuccess(TestPerson personToAdd, TestPerson... currentList) { - commandBox.runCommand(personToAdd.getAddCommand()); + //@@author A0147092E + @Test + public void assertAddRecursiveEventSuccess() throws IllegalValueException { + commandBox.runCommand("add Attend PC1222 lecture fr/3 ty/weekly from/4 Nov 3pm to/4 Nov 5pm"); + + for (int i = 0; i < 3; i++) { + TestTask recursiveEvent = new TaskBuilder().withName("Attend PC1222 lecture") + .withStartTime("Nov " + (4 + (i * 7)) + " 2016 15:00") + .withEndTime("Nov " + (4 + (i * 7)) + " 2016 17:00").withDueDate("Feb 29 2000 00:00").build(); + + currentList = TestUtil.addTasksToList(currentList, recursiveEvent); + + } + assertTrue(taskListPanel.isListMatching(0, currentList)); + } + + @Test + public void assertAddRecursiveTaskSuccess() throws IllegalValueException { + commandBox.runCommand("add Submit PC1222 Lab Assignment fr/3 ty/weekly by/Nov 1 2016 17:00"); + + for (int i = 0; i < 3; i++) { + TestTask recursiveTask = new TaskBuilder().withName("Submit PC1222 Lab Assignment") + .withStartTime("Feb 29 2000 00:00").withEndTime("Feb 29 2000 00:00") + .withDueDate("Nov " + (1 + (i * 7)) + " 2016 17:00").build(); + + currentList = TestUtil.addTasksToList(currentList, recursiveTask); + + } + assertTrue(taskListPanel.isListMatching(0, currentList)); + } + //@@author + + // @@author A0127686R + private void assertAddSuccess(TestTask taskToAdd, TestTask... currentList) { + commandBox.runCommand(taskToAdd.getAddCommand()); - //confirm the new card contains the right data - PersonCardHandle addedCard = personListPanel.navigateToPerson(personToAdd.getName().fullName); - assertMatching(personToAdd, addedCard); + // confirm the new card contains the right data + TaskCardHandle addedCard = taskListPanel.navigateToTask(taskToAdd.getName().toString()); + 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 + TestTask[] expectedList = TestUtil.addTasksToList(currentList, taskToAdd); + assertTrue(taskListPanel.isListMatching(expectedList)); } } diff --git a/src/test/java/guitests/BlockCommandTest.java b/src/test/java/guitests/BlockCommandTest.java new file mode 100644 index 000000000000..5371116f98a7 --- /dev/null +++ b/src/test/java/guitests/BlockCommandTest.java @@ -0,0 +1,63 @@ +//@@author A0138455Y +package guitests; + +import org.junit.Test; + +import seedu.flexitrack.logic.commands.BlockCommand; +import seedu.flexitrack.model.task.DateTimeInfo; +import seedu.flexitrack.testutil.TestTask; +import seedu.flexitrack.testutil.TestUtil; +import seedu.flexitrack.testutil.TypicalTestTasks; + +import static seedu.flexitrack.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.flexitrack.logic.commands.BlockCommand.MESSAGE_SUCCESS; +import static seedu.flexitrack.logic.commands.BlockCommand.MESSAGE_DUPLICATE_TIME; + +public class BlockCommandTest extends FlexiTrackGuiTest { + + TestTask[] currentList = td.getTypicalSortedTasks(); + TestTask taskToBlock; + + @Test + public void addBlockSuccess() { + TestTask[] currentList = td.getTypicalSortedTasks(); + taskToBlock = TypicalTestTasks.basketball; + assertBlockSuccess(taskToBlock, currentList); + currentList = TestUtil.addTasksToList(currentList, taskToBlock); + } + + @Test + public void addBlockFail() { + TestTask[] currentList = td.getTypicalSortedTasks(); + taskToBlock = TypicalTestTasks.tutorial1; + assertBlockFail(taskToBlock, currentList); + currentList = TestUtil.addTasksToList(currentList, taskToBlock); + } + + @Test + public void addBlockOverlappingFail() { + TestTask[] currentList = td.getTypicalSortedTasks(); + taskToBlock = TypicalTestTasks.tutorial3; + assertBlockOverlappingFail(taskToBlock, currentList); + currentList = TestUtil.addTasksToList(currentList, taskToBlock); + } + + private void assertBlockSuccess(TestTask taskToBlock, TestTask[] currentList2) { + commandBox.runCommand(taskToBlock.getBlockCommand()); + taskToBlock.setIsBlock(true);; + assertResultMessage((String.format(MESSAGE_SUCCESS, taskToBlock)) + "\n" + DateTimeInfo + .durationOfTheEvent(taskToBlock.getStartTime().toString(), taskToBlock.getEndTime().toString())); + } + + private void assertBlockFail(TestTask taskToBlock, TestTask[] currentList2) { + commandBox.runCommand("block " + taskToBlock.getName().toString() + " by/ " + taskToBlock.getDueDate()); + //taskToBlock.setIsBlock(true);; + assertResultMessage(String.format(MESSAGE_INVALID_COMMAND_FORMAT, BlockCommand.MESSAGE_USAGE)); + } + + private void assertBlockOverlappingFail(TestTask taskToBlock, TestTask[] currentList2) { + commandBox.runCommand(TypicalTestTasks.tutorial2.getAddCommand()); + commandBox.runCommand(taskToBlock.getBlockCommand()); + assertResultMessage(MESSAGE_DUPLICATE_TIME); + } +} diff --git a/src/test/java/guitests/ClearCommandTest.java b/src/test/java/guitests/ClearCommandTest.java index 9d52b427659c..99e12b3f682f 100644 --- a/src/test/java/guitests/ClearCommandTest.java +++ b/src/test/java/guitests/ClearCommandTest.java @@ -1,31 +1,33 @@ package guitests; +import static org.junit.Assert.assertTrue; + import org.junit.Test; -import static org.junit.Assert.assertTrue; +import seedu.flexitrack.testutil.TypicalTestTasks; -public class ClearCommandTest extends AddressBookGuiTest { +public class ClearCommandTest extends FlexiTrackGuiTest { @Test public void clear() { - //verify a non-empty list can be cleared - assertTrue(personListPanel.isListMatching(td.getTypicalPersons())); + // verify a non-empty list can be cleared + assertTrue(taskListPanel.isListMatching(td.getTypicalSortedTasks())); assertClearCommandSuccess(); - //verify other commands can work after a clear command - commandBox.runCommand(td.hoon.getAddCommand()); - assertTrue(personListPanel.isListMatching(td.hoon)); + // verify other commands can work after a clear command + commandBox.runCommand(TypicalTestTasks.basketball.getAddCommand()); + assertTrue(taskListPanel.isListMatching(TypicalTestTasks.basketball)); commandBox.runCommand("delete 1"); assertListSize(0); - //verify clear command works when the list is empty + // verify clear command works when the list is empty assertClearCommandSuccess(); } private void assertClearCommandSuccess() { commandBox.runCommand("clear"); assertListSize(0); - assertResultMessage("Address book has been cleared!"); + assertResultMessage("FlexiTrack has been cleared!"); } } diff --git a/src/test/java/guitests/CommandBoxTest.java b/src/test/java/guitests/CommandBoxTest.java index 1379198bf8b0..501f4a8b1776 100644 --- a/src/test/java/guitests/CommandBoxTest.java +++ b/src/test/java/guitests/CommandBoxTest.java @@ -1,22 +1,24 @@ package guitests; +import static org.junit.Assert.assertEquals; + import org.junit.Test; -import static org.junit.Assert.assertEquals; +import seedu.flexitrack.testutil.TypicalTestTasks; -public class CommandBoxTest extends AddressBookGuiTest { +public class CommandBoxTest extends FlexiTrackGuiTest { @Test public void commandBox_commandSucceeds_textCleared() { - commandBox.runCommand(td.benson.getAddCommand()); + commandBox.runCommand(TypicalTestTasks.homework2.getAddCommand()); assertEquals(commandBox.getCommandInput(), ""); } @Test - public void commandBox_commandFails_textStays(){ + public void commandBox_commandFails_textStays() { commandBox.runCommand("invalid command"); assertEquals(commandBox.getCommandInput(), "invalid command"); - //TODO: confirm the text box color turns to red + // TODO: confirm the text box color turns to red } } diff --git a/src/test/java/guitests/DeleteCommandTest.java b/src/test/java/guitests/DeleteCommandTest.java index 10c7b9e0dbea..6b143e050296 100644 --- a/src/test/java/guitests/DeleteCommandTest.java +++ b/src/test/java/guitests/DeleteCommandTest.java @@ -1,54 +1,66 @@ package guitests; +import static org.junit.Assert.assertTrue; +import static seedu.flexitrack.logic.commands.DeleteCommand.MESSAGE_DELETE_TASK_SUCCESS; + import org.junit.Test; -import seedu.address.testutil.TestPerson; -import seedu.address.testutil.TestUtil; -import static org.junit.Assert.assertTrue; -import static seedu.address.logic.commands.DeleteCommand.MESSAGE_DELETE_PERSON_SUCCESS; +import seedu.flexitrack.testutil.TestTask; +import seedu.flexitrack.testutil.TestUtil; -public class DeleteCommandTest extends AddressBookGuiTest { +public class DeleteCommandTest extends FlexiTrackGuiTest { @Test public void delete() { - //delete the first in the list - TestPerson[] currentList = td.getTypicalPersons(); + // delete the first in the list + TestTask[] currentList = td.getTypicalSortedTasks(); int targetIndex = 1; assertDeleteSuccess(targetIndex, currentList); - //delete the last in the list - currentList = TestUtil.removePersonFromList(currentList, targetIndex); + // delete the last in the list + currentList = TestUtil.removeTaskFromList(currentList, targetIndex); targetIndex = currentList.length; assertDeleteSuccess(targetIndex, currentList); - //delete from the middle of the list - currentList = TestUtil.removePersonFromList(currentList, targetIndex); - targetIndex = currentList.length/2; + // delete from the middle of the list + currentList = TestUtil.removeTaskFromList(currentList, targetIndex); + targetIndex = currentList.length / 2; assertDeleteSuccess(targetIndex, currentList); - //invalid index + // invalid index commandBox.runCommand("delete " + currentList.length + 1); - assertResultMessage("The person index provided is invalid"); + 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 the task at specified index and + * confirms the result is correct. + * + * @param targetIndexOneIndexed + * e.g. to delete 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 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); + private void assertDeleteSuccess(int targetIndexOneIndexed, final TestTask[] currentList) { + TestTask taskToDelete = currentList[targetIndexOneIndexed - 1]; // -1 + // because + // array + // uses + // zero + // indexing + TestTask[] expectedRemainder = TestUtil.removeTaskFromList(currentList, targetIndexOneIndexed); commandBox.runCommand("delete " + targetIndexOneIndexed); - //confirm the list now contains all previous persons except the deleted person - assertTrue(personListPanel.isListMatching(expectedRemainder)); + // confirm the list now contains all previous tasks except the deleted + // task + assertTrue(taskListPanel.isListMatching(expectedRemainder)); - //confirm the result message is correct - assertResultMessage(String.format(MESSAGE_DELETE_PERSON_SUCCESS, personToDelete)); + // confirm the result message is correct + assertResultMessage(String.format(MESSAGE_DELETE_TASK_SUCCESS, taskToDelete)); } } diff --git a/src/test/java/guitests/EditCommandTest.java b/src/test/java/guitests/EditCommandTest.java new file mode 100644 index 000000000000..f10cde3afff9 --- /dev/null +++ b/src/test/java/guitests/EditCommandTest.java @@ -0,0 +1,149 @@ +//@@author A0127855W +package guitests; + +import static org.junit.Assert.assertTrue; +import static seedu.flexitrack.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import org.junit.Test; + +import guitests.guihandles.TaskCardHandle; +import seedu.flexitrack.commons.core.Messages; +import seedu.flexitrack.logic.commands.EditCommand; +import seedu.flexitrack.testutil.TestTask; +import seedu.flexitrack.testutil.TestUtil; +import seedu.flexitrack.testutil.TypicalTestTasks; + +public class EditCommandTest extends FlexiTrackGuiTest { + + TestTask[] currentList = td.getTypicalSortedTasks(); + TestTask editedTask; + int index; + String command; + + @Test + public void edit_taskName_pass() { + editedTask = TypicalTestTasks.homework1EditName; + index = 6; + command = " n/ Name Edited"; + assertEditSuccess(editedTask, currentList, index, command); + } + + @Test + public void edit_taskDueDate_pass() { + editedTask = TypicalTestTasks.homework1EditDueDate; + index = 6; + command = " by/ Jan 14 2016 10am"; + assertEditSuccess(editedTask, currentList, index, command); + } + + @Test + public void edit_eventName_pass() { + editedTask = TypicalTestTasks.soccerEditName; + index = 4; + command = " n/ Name Edited 2"; + assertEditSuccess(editedTask, currentList, index, command); + } + + @Test + public void edit_eventStartTime_pass() { + editedTask = TypicalTestTasks.soccerEditStartTime; + index = 4; + command = " from/ June 10 2016 9pm"; + assertEditSuccess(editedTask, currentList, index, command); + } + + @Test + public void edit_eventEndTime_pass() { + editedTask = TypicalTestTasks.soccerEditEndTime; + index = 4; + command = " to/ June 30 2020 6am"; + assertEditSuccess(editedTask, currentList, index, command); + } + + @Test + public void edit_floatingTaskName_pass() { + editedTask = TypicalTestTasks.homework3EditName; + index = 2; + command = " n/ Name Edited 3"; + assertEditSuccess(editedTask, currentList, index, command); + } + + @Test + public void edit_floatingTaskIntoTask_pass() { + editedTask = TypicalTestTasks.homework3EditToTask; + index = 2; + command = " by/ Jun 10 2016 9pm"; + assertEditSuccess(editedTask, currentList, index, command); + } + + @Test + public void edit_floatingTaskIntoEvent_pass() { + editedTask = TypicalTestTasks.eventEditToEvent; + index = 1; + command = " from/ Jun 10 2016 21:00 to/ Jun 30 2016 23:00"; + assertEditSuccess(editedTask, currentList, index, command); + } + + @Test + public void edit_wrongIndex_fail() { + + commandBox.runCommand("edit " + (currentList.length + 1) + " n/ hello"); + assertResultMessage(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + @Test + public void edit_taskStartTime_fail() { + commandBox.runCommand("edit " + 5 + " from/ today"); + assertResultMessage(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE)); + } + @Test + public void edit_taskEndTime_fail() { + commandBox.runCommand("edit " + 5 + " to/ tomorrow"); + assertResultMessage(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE)); + } + @Test + public void edit_eventDueDate_fail() { + commandBox.runCommand("edit " + 3 + " by/ tomorrow"); + assertResultMessage(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE)); + } + @Test + public void edit_floatingTaskwithStartTime_fail() { + commandBox.runCommand("edit " + 1 + " from/ today"); + assertResultMessage(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE)); + } + @Test + public void edit_floatingTaskWithEndTime_fail() { + commandBox.runCommand("edit " + 1 + " to/ tomorrow"); + assertResultMessage(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE)); + } + @Test + public void edit_floatingTaskWithDueDateAndStartTime_fail() { + commandBox.runCommand("edit " + 1 + " by/ tomorrow from/ tomorrow"); + assertResultMessage(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE)); + } + @Test + public void edit_floatingTaskWithDueDateAndEndTIme_fail() { + commandBox.runCommand("edit " + 1 + " by/ tomorrow to/ tomorrow"); + assertResultMessage(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE)); + } + @Test + public void edit_invalidCommandFormat_fail() { + commandBox.runCommand("edit what is this"); + assertResultMessage(String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE)); + + } + + private void assertEditSuccess(TestTask editedTask, final TestTask[] currentList, int indexOneIndexed, + String command) { + int index = indexOneIndexed - 1; + + commandBox.runCommand("edit " + indexOneIndexed + command); + TaskCardHandle editedCard = taskListPanel.navigateToTask(editedTask.getName().toString()); + + assertMatching(editedTask, editedCard); + + // confirm the list now contains all previous tasks plus the new task + TestTask[] expectedList = TestUtil.editTasksToList(currentList, index, editedTask); + assertTrue(taskListPanel.isListMatching(expectedList)); + } + +} \ No newline at end of file diff --git a/src/test/java/guitests/FindCommandTest.java b/src/test/java/guitests/FindCommandTest.java index 441a6dbed666..1cf9739c63e9 100644 --- a/src/test/java/guitests/FindCommandTest.java +++ b/src/test/java/guitests/FindCommandTest.java @@ -1,27 +1,35 @@ package guitests; +import static org.junit.Assert.assertTrue; + import org.junit.Test; -import seedu.address.commons.core.Messages; -import seedu.address.testutil.TestPerson; -import static org.junit.Assert.assertTrue; +import edu.emory.mathcs.backport.java.util.Arrays; +import seedu.flexitrack.commons.core.Messages; +import seedu.flexitrack.testutil.TestTask; +import seedu.flexitrack.testutil.TypicalTestTasks; -public class FindCommandTest extends AddressBookGuiTest { +public class FindCommandTest extends FlexiTrackGuiTest { @Test public void find_nonEmptyList() { - assertFindResult("find Mark"); //no results - assertFindResult("find Meier", td.benson, td.daniel); //multiple results - - //find after deleting one result + assertFindResult("find Mark"); // no results + //assertFindResult("find homework soccer", td.homework1, td.homework2, td.homework3, td.soccer); // multiple results + commandBox.runCommand("find Mark"); + TestTask[] expectedHits = getExpectedHits(TypicalTestTasks.homework1, TypicalTestTasks.homework2, TypicalTestTasks.homework3, TypicalTestTasks.soccer); + Arrays.sort(expectedHits); + assertTrue(expectedHits.length==4); + assertResultMessage(0 + " tasks listed!"); + + // find after deleting one result commandBox.runCommand("delete 1"); - assertFindResult("find Meier",td.daniel); + assertFindResult("find soccer", TypicalTestTasks.soccer); } @Test - public void find_emptyList(){ + public void find_emptyList() { commandBox.runCommand("clear"); - assertFindResult("find Jean"); //no results + assertFindResult("find Jean"); // no results } @Test @@ -29,11 +37,16 @@ public void find_invalidCommand_fail() { commandBox.runCommand("findgeorge"); assertResultMessage(Messages.MESSAGE_UNKNOWN_COMMAND); } - - private void assertFindResult(String command, TestPerson... expectedHits ) { + + private TestTask[] getExpectedHits(TestTask... expectedHits){ + return expectedHits; + } + + private void assertFindResult(String command, TestTask... expectedHits) { commandBox.runCommand(command); + Arrays.sort(expectedHits); assertListSize(expectedHits.length); - assertResultMessage(expectedHits.length + " persons listed!"); - assertTrue(personListPanel.isListMatching(expectedHits)); + assertResultMessage(expectedHits.length + " tasks listed!"); + assertTrue(taskListPanel.isListMatching(expectedHits)); } } diff --git a/src/test/java/guitests/AddressBookGuiTest.java b/src/test/java/guitests/FlexiTrackGuiTest.java similarity index 55% rename from src/test/java/guitests/AddressBookGuiTest.java rename to src/test/java/guitests/FlexiTrackGuiTest.java index b9cf9ca092eb..d4e40c44b1fc 100644 --- a/src/test/java/guitests/AddressBookGuiTest.java +++ b/src/test/java/guitests/FlexiTrackGuiTest.java @@ -1,45 +1,54 @@ package guitests; -import guitests.guihandles.*; -import javafx.stage.Stage; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.concurrent.TimeoutException; + import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; 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 java.util.concurrent.TimeoutException; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import guitests.guihandles.CommandBoxHandle; +import guitests.guihandles.MainGuiHandle; +import guitests.guihandles.MainMenuHandle; +import guitests.guihandles.ResultDisplayHandle; +import guitests.guihandles.TaskCardHandle; +import guitests.guihandles.TaskListPanelHandle; +import javafx.stage.Stage; +import seedu.flexitrack.TestApp; +import seedu.flexitrack.commons.core.EventsCenter; +import seedu.flexitrack.model.FlexiTrack; +import seedu.flexitrack.model.task.ReadOnlyTask; +import seedu.flexitrack.testutil.TestUtil; +import seedu.flexitrack.testutil.TypicalTestTasks; /** - * A GUI Test class for AddressBook. + * A GUI Test class for FlexiTrack. */ -public abstract class AddressBookGuiTest { +public abstract class FlexiTrackGuiTest { - /* The TestName Rule makes the current test name available inside test methods */ + /* + * The TestName Rule makes the current test name available inside test + * methods + */ @Rule public TestName name = new TestName(); TestApp testApp; - protected TypicalTestPersons td = new TypicalTestPersons(); + protected static TypicalTestTasks td = new TypicalTestTasks(); /* - * Handles to GUI elements present at the start up are created in advance - * for easy access from child classes. + * Handles to GUI elements present at the start up are created in advance + * for easy access from child classes. */ protected MainGuiHandle mainGui; protected MainMenuHandle mainMenu; - protected PersonListPanelHandle personListPanel; + protected TaskListPanelHandle taskListPanel; protected ResultDisplayHandle resultDisplay; protected CommandBoxHandle commandBox; private Stage stage; @@ -59,7 +68,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; @@ -67,22 +76,24 @@ public void setup() throws Exception { EventsCenter.clearSubscribers(); testApp = (TestApp) FxToolkit.setupApplication(() -> new TestApp(this::getInitialData, getDataFileLocation())); FxToolkit.showStage(); - while (!stage.isShowing()); + while (!stage.isShowing()) + ; mainGui.focusOnMainApp(); } /** - * Override this in child classes to set the initial local data. - * Return null to use the data in the file specified in {@link #getDataFileLocation()} + * 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 FlexiTrack getInitialData() { + FlexiTrack ab = TestUtil.generateEmptyFlexiTrack(); + TypicalTestTasks.loadFlexiTrackWithSampleData(ab); return ab; } /** * Override this in child classes to set the data file location. + * * @return */ protected String getDataFileLocation() { @@ -95,22 +106,24 @@ 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)); + public void assertMatching(ReadOnlyTask task, TaskCardHandle card) { + assertTrue(TestUtil.compareCardAndTask(card, task)); } /** - * Asserts the size of the person list is equal to the given number. + * Asserts the size of the task list is equal to the given number. */ protected void assertListSize(int size) { - int numberOfPeople = personListPanel.getNumberOfPeople(); - assertEquals(size, numberOfPeople); + int numberOfTask = taskListPanel.getNumberOfTask(); + assertEquals(size, numberOfTask); } /** - * Asserts the message shown in the Result Display area is same as the given string. + * Asserts the message shown in the Result Display area is same as the given + * string. + * * @param expected */ protected void assertResultMessage(String expected) { diff --git a/src/test/java/guitests/GapCommandTest.java b/src/test/java/guitests/GapCommandTest.java new file mode 100644 index 000000000000..7ece445d2395 --- /dev/null +++ b/src/test/java/guitests/GapCommandTest.java @@ -0,0 +1,185 @@ +//@@ author A0127686R +package guitests; + +import static org.junit.Assert.assertEquals; + +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import seedu.flexitrack.logic.Logic; +import seedu.flexitrack.logic.LogicManager; +import seedu.flexitrack.logic.commands.CommandResult; +import seedu.flexitrack.logic.commands.GapCommand; +import seedu.flexitrack.model.Model; +import seedu.flexitrack.model.ModelManager; +import seedu.flexitrack.model.task.Task; +import seedu.flexitrack.model.task.UniqueTaskList.DuplicateTaskException; +import seedu.flexitrack.storage.StorageManager; +import seedu.flexitrack.testutil.TestTask; + +public class GapCommandTest extends FlexiTrackGuiTest { + + @ClassRule + public static TemporaryFolder saveFolder = new TemporaryFolder(); + + private static Model model; + private static Logic logic; + + + @BeforeClass + public static void setupMoreEvents(){ + model = new ModelManager(); + String tempFlexiTrackerFile = saveFolder.getRoot().getPath() + "TempFlexiTracker.xml"; + String tempPreferencesFile = saveFolder.getRoot().getPath() + "TempPreferences.json"; + logic = new LogicManager(model, new StorageManager(tempFlexiTrackerFile, tempPreferencesFile)); + +// TestTask[] typicalTask = td.getTypicalSortedTasks(); +// for (TestTask task: typicalTask){ +// try { +// model.addTask(new Task(task)); +// } catch (DuplicateTaskException e) { +// assert true; +// } +// } + + TestTask[] typicalEvent = td.getTypicalEventTasks(); + for (TestTask task: typicalEvent){ + try { + model.addTask(new Task(task)); + } catch (DuplicateTaskException e) { + assert true; + } + } + } + + @Test + public void gap_hour_success() throws Exception { + // using the word + String gapUserCommand = "gap hour"; + String messageShown = "The earliest 1 hour free time are found... " + + "\nBetween: now " + " to: " + "Nov 08 2017 09:00" + + "\nBetween: " + "Nov 08 2017 11:00" + " to: " + "Nov 08 2017 15:00" + + "\nBetween: " + "Nov 08 2017 16:00" + " to: " + "Nov 09 2017 14:00"; + assertGapCommandSuccess(gapUserCommand, messageShown); + + // using a single letter to represent the time + gapUserCommand = "gap 3h"; + messageShown = "The earliest 3 hours free time are found... " + + "\nBetween: now " + " to: " + "Nov 08 2017 09:00" + + "\nBetween: " + "Nov 08 2017 11:00" + " to: " + "Nov 08 2017 15:00" + + "\nBetween: " + "Nov 08 2017 16:00" + " to: " + "Nov 09 2017 14:00"; + assertGapCommandSuccess(gapUserCommand, messageShown); + + // using the complete word with s + gapUserCommand = "gap 7 hours"; + messageShown = "The earliest 7 hours free time are found... " + + "\nBetween: now " + " to: " + "Nov 08 2017 09:00" + + "\nBetween: " + "Nov 08 2017 16:00" + " to: " + "Nov 09 2017 14:00" + + "\nBetween: " + "Nov 09 2017 16:00" + " to: " + "Nov 12 2017 10:00"; + assertGapCommandSuccess(gapUserCommand, messageShown); + + // specify the number of time slot found + gapUserCommand = "gap 4h n/2"; + messageShown = "The earliest 4 hours free time are found... " + + "\nBetween: now " + " to: " + "Nov 08 2017 09:00" + + "\nBetween: " + "Nov 08 2017 11:00" + " to: " + "Nov 08 2017 15:00"; + assertGapCommandSuccess(gapUserCommand, messageShown); + } + + @Test + public void gap_minute_success() throws Exception { + // using a single letter to represent minute + String gapUserCommand = "gap 40m"; + String messageShown = "The earliest 40 minutes free time are found... " + + "\nBetween: now " + " to: " + "Nov 08 2017 09:00" + + "\nBetween: " + "Nov 08 2017 11:00" + " to: " + "Nov 08 2017 15:00" + + "\nBetween: " + "Nov 08 2017 16:00" + " to: " + "Nov 09 2017 14:00"; + assertGapCommandSuccess(gapUserCommand, messageShown); + + // using the proper word + s + gapUserCommand = "gap 30minutes"; + messageShown = "The earliest 30 minutes free time are found... " + + "\nBetween: now " + " to: " + "Nov 08 2017 09:00" + + "\nBetween: " + "Nov 08 2017 11:00" + " to: " + "Nov 08 2017 15:00" + + "\nBetween: " + "Nov 08 2017 16:00" + " to: " + "Nov 09 2017 14:00"; + assertGapCommandSuccess(gapUserCommand, messageShown); + + // using the proper word with specified number of time slot + gapUserCommand = "gap minute n/1"; + messageShown = "The earliest 1 minute free time are found... " + + "\nBetween: now " + " to: " + "Nov 08 2017 09:00"; + assertGapCommandSuccess(gapUserCommand, messageShown); + } + + @Test + public void gap_day_success() throws Exception { + // using a single letter to represent day + String gapUserCommand = "gap 2d n/4"; + String messageShown = "The earliest 2 days free time are found... " + + "\nBetween: now " + " to: " + "Nov 08 2017 09:00" + + "\nBetween: " + "Nov 09 2017 16:00" + " to: " + "Nov 12 2017 10:00" + + "\nBetween: " + "Nov 12 2017 14:00" + " to: " + "Nov 18 2017 09:00" + + "\nBetween: " + "Nov 20 2017 14:00" + " to: " + "Nov 22 2017 19:00"; + assertGapCommandSuccess(gapUserCommand, messageShown); + + // using the proper word + s + gapUserCommand = "gap 3 days"; + messageShown = "The earliest 3 days free time are found... " + + "\nBetween: now " + " to: " + "Nov 08 2017 09:00" + + "\nBetween: " + "Nov 12 2017 14:00" + " to: " + "Nov 18 2017 09:00" + + "\nFree from: " + "Nov 22 2017 21:00" + " onwards. "; + assertGapCommandSuccess(gapUserCommand, messageShown); + + // using the proper word with specified number of time slot + gapUserCommand = "gap day n/6"; + messageShown = "The earliest 1 day free time are found... " + + "\nBetween: now " + " to: " + "Nov 08 2017 09:00" + + "\nBetween: " + "Nov 09 2017 16:00" + " to: " + "Nov 12 2017 10:00" + + "\nBetween: " + "Nov 12 2017 14:00" + " to: " + "Nov 18 2017 09:00" + + "\nBetween: " + "Nov 20 2017 14:00" + " to: " + "Nov 22 2017 19:00" + + "\nFree from: " + "Nov 22 2017 21:00" + " onwards. "; + assertGapCommandSuccess(gapUserCommand, messageShown); + } + + @Test + public void execute_invalid_help() throws Exception { + // missing n/ + String invalidUserInput = "gap 5"; + assertGapCommandSuccess(invalidUserInput, ("Invalid command format! \n" + GapCommand.MESSAGE_USAGE)); + + // with no time specified + invalidUserInput = "gap"; + assertGapCommandSuccess(invalidUserInput, ("Invalid command format! \n" + GapCommand.MESSAGE_USAGE)); + + // with no n/ + invalidUserInput = "gap 5 months"; + assertGapCommandSuccess(invalidUserInput, ("Invalid command format! \n" + GapCommand.MESSAGE_USAGE)); + + // with no number of time slot to be found + invalidUserInput = "gap 2 days n/"; + assertGapCommandSuccess(invalidUserInput, ("Invalid command format! \n" + GapCommand.MESSAGE_USAGE)); + + // number of time slot is in letter + invalidUserInput = "gap day n/three"; + assertGapCommandSuccess(invalidUserInput, ("Invalid command format! \n" + "Please enter number in digit.")); + + // duration is in word + invalidUserInput = "gap two day"; + assertGapCommandSuccess(invalidUserInput, ("Invalid command format! \n" + "Please enter number in digit.")); + + } + + /** + * Check if the system output is the same as expected output. + */ + private void assertGapCommandSuccess(String inputCommand, String expectedMessage) { + // Execute the command + CommandResult result = logic.execute(inputCommand); + + // Check if the expectedMessage is the same as the result + assertEquals(expectedMessage, result.feedbackToUser); + } + +} diff --git a/src/test/java/guitests/GuiRobot.java b/src/test/java/guitests/GuiRobot.java index 44aa9edb48aa..1ee54ce5bc6c 100644 --- a/src/test/java/guitests/GuiRobot.java +++ b/src/test/java/guitests/GuiRobot.java @@ -1,16 +1,17 @@ package guitests; -import javafx.scene.input.KeyCodeCombination; import org.testfx.api.FxRobot; -import seedu.address.testutil.TestUtil; + +import javafx.scene.input.KeyCodeCombination; +import seedu.flexitrack.testutil.TestUtil; /** - * Robot used to simulate user actions on the GUI. - * Extends {@link FxRobot} by adding some customized functionality and workarounds. + * Robot used to simulate user actions on the GUI. Extends {@link FxRobot} by + * adding some customized functionality and workarounds. */ public class GuiRobot extends FxRobot { - public GuiRobot push(KeyCodeCombination keyCodeCombination){ + public GuiRobot push(KeyCodeCombination keyCodeCombination) { return (GuiRobot) super.push(TestUtil.scrub(keyCodeCombination)); } diff --git a/src/test/java/guitests/HelpCommandTest.java b/src/test/java/guitests/HelpCommandTest.java new file mode 100644 index 000000000000..63cceca57204 --- /dev/null +++ b/src/test/java/guitests/HelpCommandTest.java @@ -0,0 +1,153 @@ +//@@author A0138455Y +package guitests; + +import static org.junit.Assert.assertEquals; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import seedu.flexitrack.commons.core.EventsCenter; +import seedu.flexitrack.logic.Logic; +import seedu.flexitrack.logic.LogicManager; +import seedu.flexitrack.logic.commands.AddCommand; +import seedu.flexitrack.logic.commands.ClearCommand; +import seedu.flexitrack.logic.commands.CommandResult; +import seedu.flexitrack.logic.commands.DeleteCommand; +import seedu.flexitrack.logic.commands.EditCommand; +import seedu.flexitrack.logic.commands.ExitCommand; +import seedu.flexitrack.logic.commands.FindCommand; +import seedu.flexitrack.logic.commands.HelpCommand; +import seedu.flexitrack.logic.commands.ListCommand; +import seedu.flexitrack.logic.commands.MarkCommand; +import seedu.flexitrack.logic.commands.UnmarkCommand; +import seedu.flexitrack.model.Model; +import seedu.flexitrack.model.ModelManager; +import seedu.flexitrack.storage.StorageManager; + +public class HelpCommandTest extends FlexiTrackGuiTest { + + /** + * See https://github.com/junit-team/junit4/wiki/rules#temporaryfolder-rule + */ + @Rule + public TemporaryFolder saveFolder = new TemporaryFolder(); + + private Model model; + private Logic logic; + + @Before + public void setup() { + model = new ModelManager(); + String tempFlexiTrackerFile = saveFolder.getRoot().getPath() + "TempFlexiTracker.xml"; + String tempPreferencesFile = saveFolder.getRoot().getPath() + "TempPreferences.json"; + logic = new LogicManager(model, new StorageManager(tempFlexiTrackerFile, tempPreferencesFile)); + EventsCenter.getInstance().registerHandler(this); + } + + @After + public void teardown() { + EventsCenter.clearSubscribers(); + } + + @Test + public void execute_help_add() throws Exception { + // help for add command + String help_Add = "help add"; + assertValidHelpSuccess(help_Add, AddCommand.MESSAGE_USAGE); + } + + @Test + public void execute_help_clear() throws Exception { + // help for clear command + String help_clear = "help clear"; + assertValidHelpSuccess(help_clear, ClearCommand.MESSAGE_USAGE); + } + + @Test + public void execute_help_delete() throws Exception { + // help for delete command + String help_Delete = "help delete"; + assertValidHelpSuccess(help_Delete, DeleteCommand.MESSAGE_USAGE); + } + + @Test + public void execute_help_edit() throws Exception { + // help for edit command + String help_Edit = "help edit"; + assertValidHelpSuccess(help_Edit, EditCommand.MESSAGE_USAGE); + } + + @Test + public void execute_help_exit() throws Exception { + // help for Exit command + String help_Exit = "help exit"; + assertValidHelpSuccess(help_Exit, ExitCommand.MESSAGE_USAGE); + } + + @Test + public void execute_help_find() throws Exception { + // help for find command + String help_Find = "help find"; + assertValidHelpSuccess(help_Find, FindCommand.MESSAGE_USAGE); + } + + @Test + public void execute_help_list() throws Exception { + // help for list command + String help_List = "help list"; + assertValidHelpSuccess(help_List, ListCommand.MESSAGE_USAGE); + } + + @Test + public void execute_help_mark() throws Exception { + // help for mark command + String help_Mark = "help mark"; + assertValidHelpSuccess(help_Mark, MarkCommand.MESSAGE_USAGE); + } + + @Test + public void execute_help_select() throws Exception { + // help for select command + String help_Select = "help select"; + assertValidHelpSuccess(help_Select, HelpCommand.HELP_MESSAGE_USAGE); + } + + @Test + public void execute_help_unmark() throws Exception { + // help for unmark command + String help_Unmark = "help unmark"; + assertValidHelpSuccess(help_Unmark, UnmarkCommand.MESSAGE_USAGE); + } + + @Test + public void execute_invalid_help() throws Exception { + // help for invalid command + String help_Invalid = "help addasd"; + assertInvalidHelpCommandSuccess(help_Invalid, HelpCommand.HELP_MESSAGE_USAGE); + + // help for second invalid command + String help_Invalid2 = "help :<14afa"; + assertInvalidHelpCommandSuccess(help_Invalid2, HelpCommand.HELP_MESSAGE_USAGE); + + } + + private void assertValidHelpSuccess(String inputCommand, String expectedMessage) { + // Execute the command + CommandResult result = logic.execute(inputCommand); + + // Confirm the ui display elements should contain the right data; + assertEquals(expectedMessage, result.feedbackToUser); + } + + private void assertInvalidHelpCommandSuccess(String inputCommand, String expectedMessage) { + // Execute the command + CommandResult result = logic.execute(inputCommand); + + // Confirm the ui display elements should contain the right data; + assertEquals(expectedMessage, result.feedbackToUser); + } + +} diff --git a/src/test/java/guitests/HelpWindowTest.java b/src/test/java/guitests/HelpWindowTest.java index 258d9d628d80..602de5cc2042 100644 --- a/src/test/java/guitests/HelpWindowTest.java +++ b/src/test/java/guitests/HelpWindowTest.java @@ -1,22 +1,23 @@ package guitests; -import guitests.guihandles.HelpWindowHandle; +import static org.junit.Assert.assertTrue; + import org.junit.Test; -import static org.junit.Assert.assertTrue; +import guitests.guihandles.HelpWindowHandle; -public class HelpWindowTest extends AddressBookGuiTest { +public class HelpWindowTest extends FlexiTrackGuiTest { @Test public void openHelpWindow() { - personListPanel.clickOnListView(); + taskListPanel.clickOnListView(); assertHelpWindowOpen(mainMenu.openHelpWindowUsingAccelerator()); assertHelpWindowOpen(mainMenu.openHelpWindowUsingMenu()); - assertHelpWindowOpen(commandBox.runHelpCommand()); + // assertHelpWindowOpen(commandBox.runHelpCommand()); } diff --git a/src/test/java/guitests/ListCommandTest.java b/src/test/java/guitests/ListCommandTest.java new file mode 100644 index 000000000000..09cb34d38f3a --- /dev/null +++ b/src/test/java/guitests/ListCommandTest.java @@ -0,0 +1,96 @@ +package guitests; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import seedu.flexitrack.testutil.TestTask; +import seedu.flexitrack.testutil.TestUtil; + +//@@author A0127686R +public class ListCommandTest extends FlexiTrackGuiTest { + + @Test + public void testListBasic() { + TestTask[] currentList = td.getTypicalSortedTasks(); + + // list all future tasks + String listCommand = "list future"; + assertFindSuccess(listCommand, currentList); + currentList = TestUtil.listTasksAccordingToCommand(currentList, listCommand); + + // list all past tasks + listCommand = "list past"; + assertFindSuccess(listCommand, currentList); + currentList = TestUtil.listTasksAccordingToCommand(currentList, listCommand); + + // list all tasks + listCommand = "list"; + assertFindSuccess(listCommand, currentList); + currentList = TestUtil.listTasksAccordingToCommand(currentList, listCommand); + + currentList = TestUtil.markTasksToList(currentList, 6); + currentList = TestUtil.markTasksToList(currentList, 4); + currentList = TestUtil.markTasksToList(currentList, 3); + currentList = TestUtil.markTasksToList(currentList, 1); + + commandBox.runCommand("mark 6"); + commandBox.runCommand("mark 4"); + commandBox.runCommand("mark 3"); + commandBox.runCommand("mark 1"); + + // list all marked tasks + listCommand = "list mark"; + assertFindSuccess(listCommand, currentList); + currentList = TestUtil.listTasksAccordingToCommand(currentList, listCommand); + + // list all unmarked tasks + listCommand = "list unmark"; + assertFindSuccess(listCommand, currentList); + currentList = TestUtil.listTasksAccordingToCommand(currentList, listCommand); + + // list future tasks that are marked + listCommand = "list future mark"; + assertFindSuccess(listCommand, currentList); + currentList = TestUtil.listTasksAccordingToCommand(currentList, listCommand); + + } + + public void testListRelative() { + commandBox.runCommand("add lecture 1 from/ Nov 08 2016 09:00 to/Nov 08 2016 11:00"); + commandBox.runCommand("add exam 1 from/Nov 20 2016 09:00 to/Nov 20 2016 10:30 "); + commandBox.runCommand("add past 1 from/Nov 01 2016 09:00 to/ Nov 01 2016 11:00"); + commandBox.runCommand("add past 2 from/Oct 20 2016 15:00 to/Oct 20 2016 16:00"); + TestTask[] currentList = td.getTypicalSortedTasks(); + + // list last week task + String listCommand = "list last week"; + assertFindSuccess(listCommand, currentList); + currentList = TestUtil.listTasksAccordingToCommand(currentList, listCommand); + + // list last month task + listCommand = "list last month"; + assertFindSuccess(listCommand, currentList); + currentList = TestUtil.listTasksAccordingToCommand(currentList, listCommand); + + // list next week + listCommand = "list next week"; + assertFindSuccess(listCommand, currentList); + currentList = TestUtil.listTasksAccordingToCommand(currentList, listCommand); + + //list next month + listCommand = "list next month"; + assertFindSuccess(listCommand, currentList); + currentList = TestUtil.listTasksAccordingToCommand(currentList, listCommand); + + } + + private void assertFindSuccess(String listCommand, TestTask... currentList) { + commandBox.runCommand(listCommand); + + // confirm the list now contains all previous tasks plus the new task + TestTask[] expectedList = TestUtil.listTasksAccordingToCommand(currentList, listCommand); + assertTrue(taskListPanel.isListMatching(expectedList)); + } + +} diff --git a/src/test/java/guitests/MarkCommandTest.java b/src/test/java/guitests/MarkCommandTest.java new file mode 100644 index 000000000000..38eb3ca45027 --- /dev/null +++ b/src/test/java/guitests/MarkCommandTest.java @@ -0,0 +1,110 @@ +//@@author A0138455Y +package guitests; + +import org.junit.Test; + +import seedu.flexitrack.commons.core.Messages; +import seedu.flexitrack.logic.commands.MarkCommand; +import seedu.flexitrack.logic.commands.UnmarkCommand; +import seedu.flexitrack.testutil.TestTask; +import seedu.flexitrack.testutil.TestUtil; + +public class MarkCommandTest extends FlexiTrackGuiTest { + TestTask[] currentList = td.getTypicalSortedTasks(); + + @Test + public void mark() { + // mark a task + //TestTask[] currentList = td.getTypicalSortedTasks(); + assertMarkSuccess(4, currentList); + currentList = TestUtil.markTasksToList(currentList, 4); + } + + @Test + public void markFloatingTask() { + // mark a floating task + //TestTask[] currentList = td.getTypicalSortedTasks(); + assertMarkSuccess(1, currentList); + currentList = TestUtil.markTasksToList(currentList, 1); + } + + @Test + public void markInvailidIndex() { + // mark a task with invalid number + commandBox.runCommand(TestTask.getMarkCommand(100)); + assertResultMessage(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + //assertTrue(taskListPanel.isListMatching(currentList)); + } + + @Test + public void markAnMarkedTask() { + // mark an already marked task + commandBox.runCommand(TestTask.getMarkCommand(8)); + assertMarkFail(8, currentList); + //currentList = TestUtil.markTasksToList(currentList, 4); + } + + @Test + public void unmark() { + // un-mark a marked task + commandBox.runCommand(TestTask.getMarkCommand(8)); + assertUnMarkSuccess(8, currentList); + currentList = TestUtil.unMarkTasksToList(currentList, 8); + //assertTrue(taskListPanel.isListMatching(currentList)); + } + + @Test + public void unmarkUndoneTask() { + // un-mark an unmarked task + assertUnMarkFail(3, currentList); + currentList = TestUtil.unMarkTasksToList(currentList, 3); + //assertTrue(taskListPanel.isListMatching(currentList)); + } + + @Test + public void unmarkInvalidIndex() { + // unmark a task with invalid number + commandBox.runCommand(TestTask.getUnMarkCommand(100)); + assertResultMessage(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + //assertTrue(taskListPanel.isListMatching(currentList)); + } + + private void assertMarkFail(int taskToMark, TestTask... currentList) { + commandBox.runCommand(TestTask.getMarkCommand(taskToMark)); + assertResultMessage("Task already marked!"); + + // confirm the list now contains all previous tasks plus the new task + //TestTask[] expectedList = TestUtil.markTasksToList(currentList, taskToMark); + //assertTrue(taskListPanel.isListMatching(expectedList)); + + } + +// private void assertUnMarkSuccess(int taskToUnMark, TestTask... currentList) { + private void assertUnMarkSuccess(int taskToUnMark, TestTask... currentList) { + commandBox.runCommand(TestTask.getUnMarkCommand(taskToUnMark)); + assertResultMessage(String.format(UnmarkCommand.MESSAGE_UNMARK_TASK_SUCCESS, taskToUnMark)); + + // confirm the list now contains all previous tasks plus the new task + //TestTask[] expectedList = TestUtil.unMarkTasksToList(currentList, taskToUnMark); + //assertTrue(taskListPanel.isListMatching(expectedList)); + } + + private void assertUnMarkFail(int taskToUnMark , TestTask... currentList) { + commandBox.runCommand(TestTask.getUnMarkCommand(taskToUnMark)); + assertResultMessage("Task already unmarked!"); + + // confirm the list now contains all previous tasks plus the new task + //TestTask[] expectedList = TestUtil.unMarkTasksToList(currentList, taskToUnMark); + //assertTrue(taskListPanel.isListMatching(expectedList)); + } + + + private void assertMarkSuccess(int taskToMark, TestTask... currentList) { + commandBox.runCommand(TestTask.getMarkCommand(taskToMark)); + assertResultMessage(String.format(MarkCommand.MESSAGE_MARK_TASK_SUCCESS, taskToMark)); + // confirm the list now contains all previous tasks plus the new task + //TestTask[] expectedList = TestUtil.markTasksToList(currentList, taskToMark); + //assertTrue(taskListPanel.isListMatching(expectedList)); + } + +} diff --git a/src/test/java/guitests/RedoCommandTest.java b/src/test/java/guitests/RedoCommandTest.java new file mode 100644 index 000000000000..2b5d4311035c --- /dev/null +++ b/src/test/java/guitests/RedoCommandTest.java @@ -0,0 +1,70 @@ +//@@author A0127855W +package guitests; + +import static org.junit.Assert.assertTrue; + +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import seedu.flexitrack.logic.Logic; +import seedu.flexitrack.logic.LogicManager; +import seedu.flexitrack.logic.commands.RedoCommand; +import seedu.flexitrack.model.Model; +import seedu.flexitrack.model.ModelManager; +import seedu.flexitrack.storage.StorageManager; +import seedu.flexitrack.testutil.TestTask; +import seedu.flexitrack.testutil.TestUtil; +import seedu.flexitrack.testutil.TypicalTestTasks; + +public class RedoCommandTest extends FlexiTrackGuiTest { + + @ClassRule + public static TemporaryFolder saveFolder = new TemporaryFolder(); + + private static Model model; + private static Logic logic; + + + @BeforeClass + public static void setupMoreEvents(){ + model = new ModelManager(); + String tempFlexiTrackerFile = saveFolder.getRoot().getPath() + "TempFlexiTracker.xml"; + String tempPreferencesFile = saveFolder.getRoot().getPath() + "TempPreferences.json"; + logic = new LogicManager(model, new StorageManager(tempFlexiTrackerFile, tempPreferencesFile)); + } + + @Test + public void redo() { + TestTask[] expectedList = td.getTypicalSortedTasks(); + + // redo fail + //logic.execute("redo"); + commandBox.runCommand("redo"); + assertResultMessage(RedoCommand.MESSAGE_NOT_SUCCESS); + + // redo add command + TestTask taskToAdd = TypicalTestTasks.basketball; + commandBox.runCommand(taskToAdd.getAddCommand()); + expectedList = TestUtil.addTasksToList(expectedList, taskToAdd); + assertRedoSuccess(expectedList); + + // redo delete command + commandBox.runCommand("delete 6"); + expectedList = TestUtil.removeTasksFromList(expectedList, expectedList[5]); + assertRedoSuccess(expectedList); + + // redo edit command + commandBox.runCommand("edit 6 n/Name Edited"); + expectedList = TestUtil.editTasksToList(expectedList, 5, TypicalTestTasks.homework1EditName); + assertRedoSuccess(expectedList); + } + + private void assertRedoSuccess(TestTask[] expectedList){ + commandBox.runCommand("undo"); + commandBox.runCommand("redo"); + assertTrue(taskListPanel.isListMatching(expectedList)); + } + +} 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/UndoCommandTest.java b/src/test/java/guitests/UndoCommandTest.java new file mode 100644 index 000000000000..857857e13b27 --- /dev/null +++ b/src/test/java/guitests/UndoCommandTest.java @@ -0,0 +1,79 @@ +package guitests; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import seedu.flexitrack.logic.commands.UndoCommand; +import seedu.flexitrack.testutil.TestTask; + +//@@author A0127686R +public class UndoCommandTest extends FlexiTrackGuiTest { + + @Test + public void undo() { + + // undo fail + commandBox.runCommand("undo"); + assertResultMessage(UndoCommand.MESSAGE_NOT_SUCCESS); + + // undo unmark command + commandBox.runCommand("mark 2"); + commandBox.runCommand("unmark 2"); + commandBox.runCommand("undo"); + commandBox.runCommand("unmark 8"); + assertUndoSuccess(); + + // undo add command + commandBox.runCommand("add a task"); + commandBox.runCommand("undo"); + assertUndoSuccess(); + + // undo delete command + commandBox.runCommand("delete 4"); + commandBox.runCommand("undo"); + assertUndoSuccess(); + + // undo mark command + commandBox.runCommand("mark 3"); + commandBox.runCommand("undo"); + assertUndoSuccess(); + + + // undo edit command + commandBox.runCommand("edit 5 n/ play bridge with friends"); + commandBox.runCommand("undo"); + assertUndoSuccess(); + + // undo clear command + commandBox.runCommand("clear"); + commandBox.runCommand("undo"); + assertUndoSuccess(); + + // undo add command + commandBox.runCommand("list future"); + commandBox.runCommand("mark 2"); + commandBox.runCommand("list"); + commandBox.runCommand("undo"); + assertUndoSuccess(); + + // undo add command + commandBox.runCommand("list past"); + commandBox.runCommand("mark 2"); + commandBox.runCommand("mark 1"); + commandBox.runCommand("undo"); + commandBox.runCommand("list"); + commandBox.runCommand("undo"); + + assertUndoSuccess(); + } + + private void assertUndoSuccess() { + + // confirm the list now contains all previous tasks plus the new task + TestTask[] expectedList = td.getTypicalSortedTasks(); + assertTrue(taskListPanel.isListMatching(expectedList)); + + } + +} diff --git a/src/test/java/guitests/guihandles/CommandBoxHandle.java b/src/test/java/guitests/guihandles/CommandBoxHandle.java index dcd3155636cd..ec714ce34c8f 100644 --- a/src/test/java/guitests/guihandles/CommandBoxHandle.java +++ b/src/test/java/guitests/guihandles/CommandBoxHandle.java @@ -6,7 +6,7 @@ /** * A handle to the Command Box in the GUI. */ -public class CommandBoxHandle extends GuiHandle{ +public class CommandBoxHandle extends GuiHandle { private static final String COMMAND_INPUT_FIELD_ID = "#commandTextField"; @@ -28,7 +28,7 @@ public String getCommandInput() { public void runCommand(String command) { enterCommand(command); pressEnter(); - guiRobot.sleep(200); //Give time for the command to take effect + guiRobot.sleep(200); // Give time for the command to take effect } public HelpWindowHandle runHelpCommand() { diff --git a/src/test/java/guitests/guihandles/GuiHandle.java b/src/test/java/guitests/guihandles/GuiHandle.java index 5e7e0f6de911..6c8231c1597a 100644 --- a/src/test/java/guitests/guihandles/GuiHandle.java +++ b/src/test/java/guitests/guihandles/GuiHandle.java @@ -1,5 +1,7 @@ package guitests.guihandles; +import java.util.logging.Logger; + import guitests.GuiRobot; import javafx.scene.Node; import javafx.scene.control.Label; @@ -7,10 +9,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 java.util.logging.Logger; +import seedu.flexitrack.TestApp; +import seedu.flexitrack.commons.core.LogsCenter; /** * Base class for all GUI Handles used in testing. @@ -31,8 +31,7 @@ public GuiHandle(GuiRobot guiRobot, Stage primaryStage, String stageTitle) { public void focusOnWindow(String stageTitle) { logger.info("Focusing " + stageTitle); - java.util.Optional window = guiRobot.listTargetWindows() - .stream() + java.util.Optional window = guiRobot.listTargetWindows().stream() .filter(w -> w instanceof Stage && ((Stage) w).getTitle().equals(stageTitle)).findAny(); if (!window.isPresent()) { @@ -55,8 +54,9 @@ protected String getTextFieldText(String filedName) { protected void setTextField(String textFieldId, String newText) { guiRobot.clickOn(textFieldId); - ((TextField)guiRobot.lookup(textFieldId).tryQuery().get()).setText(newText); - guiRobot.sleep(500); // so that the texts stays visible on the GUI for a short period + ((TextField) guiRobot.lookup(textFieldId).tryQuery().get()).setText(newText); + guiRobot.sleep(500); // so that the texts stays visible on the GUI for a + // short period } public void pressEnter() { @@ -78,8 +78,7 @@ public void focusOnMainApp() { } public void closeWindow() { - java.util.Optional window = guiRobot.listTargetWindows() - .stream() + java.util.Optional window = guiRobot.listTargetWindows().stream() .filter(w -> w instanceof Stage && ((Stage) w).getTitle().equals(stageTitle)).findAny(); if (!window.isPresent()) { @@ -87,7 +86,7 @@ public void closeWindow() { } guiRobot.targetWindow(window.get()); - guiRobot.interact(() -> ((Stage)window.get()).close()); + guiRobot.interact(() -> ((Stage) window.get()).close()); focusOnMainApp(); } } diff --git a/src/test/java/guitests/guihandles/MainGuiHandle.java b/src/test/java/guitests/guihandles/MainGuiHandle.java index 45802c5135c7..34c0f3a69573 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.flexitrack.TestApp; /** * Provides a handle for the main GUI. @@ -13,8 +13,8 @@ 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() { diff --git a/src/test/java/guitests/guihandles/MainMenuHandle.java b/src/test/java/guitests/guihandles/MainMenuHandle.java index 0aeb047a0e1d..1fcdeda88ac8 100644 --- a/src/test/java/guitests/guihandles/MainMenuHandle.java +++ b/src/test/java/guitests/guihandles/MainMenuHandle.java @@ -1,11 +1,11 @@ package guitests.guihandles; +import java.util.Arrays; + import guitests.GuiRobot; import javafx.scene.input.KeyCode; import javafx.stage.Stage; -import seedu.address.TestApp; - -import java.util.Arrays; +import seedu.flexitrack.TestApp; /** * Provides a handle to the main menu of the app. 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..47de61536264 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.flexitrack.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..9b17b02ad38b --- /dev/null +++ b/src/test/java/guitests/guihandles/TaskCardHandle.java @@ -0,0 +1,64 @@ +package guitests.guihandles; + +import guitests.GuiRobot; +import javafx.scene.Node; +import javafx.stage.Stage; +import seedu.flexitrack.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 = "#title"; + private static final String DATETIMEINFO_DATE_ID = "#dateTime"; + + 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 getName() { + return getTextFromLabel(NAME_FIELD_ID); + } + + public String getTime() { + return getTextFromLabel(DATETIMEINFO_DATE_ID); + } + + public boolean isSameTask(ReadOnlyTask task) { + return getName().equals(task.getName().toString()) && getTime().equals(getTimingShown(task)); + } + + private String getTimingShown(ReadOnlyTask task) { + if (task.getIsEvent()) { + return " from " + task.getStartTime().toString() + " to " + task.getEndTime().toString(); + } else if (task.getIsTask()) { + return " by " + task.getDueDate().toString(); + } else { + return ""; + } + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof TaskCardHandle) { + TaskCardHandle handle = (TaskCardHandle) obj; + return getName().equals(handle.getName()) && getTime().equals(handle.getTime()); // TODO: + // compare + // the + // rest + } + return super.equals(obj); + } + + @Override + public String toString() { + return getName() + " " + getTime(); + } +} diff --git a/src/test/java/guitests/guihandles/TaskListPanelHandle.java b/src/test/java/guitests/guihandles/TaskListPanelHandle.java new file mode 100644 index 000000000000..202e4808e2f8 --- /dev/null +++ b/src/test/java/guitests/guihandles/TaskListPanelHandle.java @@ -0,0 +1,178 @@ +package guitests.guihandles; + +import static org.junit.Assert.assertTrue; + +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import guitests.GuiRobot; +import javafx.geometry.Point2D; +import javafx.scene.Node; +import javafx.scene.control.ListView; +import javafx.stage.Stage; +import seedu.flexitrack.TestApp; +import seedu.flexitrack.model.task.ReadOnlyTask; +import seedu.flexitrack.model.task.Task; +import seedu.flexitrack.testutil.TestUtil; + +/** + * 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 TASK_LIST_VIEW_ID = "#taskListView"; + + public TaskListPanelHandle(GuiRobot guiRobot, Stage primaryStage) { + super(guiRobot, primaryStage, TestApp.APP_TITLE); + } + + public List getSelectedTasks() { + ListView taskList = getListView(); + return taskList.getSelectionModel().getSelectedItems(); + } + + public ListView getListView() { + return (ListView) getNode(TASK_LIST_VIEW_ID); + } + + /** + * 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(ReadOnlyTask... tasks) { + return this.isListMatching(0, tasks); + } + + /** + * Clicks on the ListView. + */ + public void clickOnListView() { + Point2D point = TestUtil.getScreenMidPoint(getListView()); + guiRobot.clickOn(point.getX(), point.getY()); + } + + /** + * Returns true if the {@code tasks} appear as the sub list (in that order) + * at position {@code startPosition}. + */ + public boolean containsInOrder(int startPosition, ReadOnlyTask... tasks) { + List tasksInList = getListView().getItems(); + + // Return false if the list in panel is too short to contain the given + // list + if (startPosition + tasks.length > tasksInList.size()) { + return false; + } + + for (int i = 0; i < tasks.length; i++) { + if (!tasksInList.get(startPosition + i).getName().toString().equals(tasks[i].getName().toString())) { + return false; + } + } + + return true; + } + + /** + * Returns true if the list is showing the task details correctly and in + * correct order. + * + * @param startPosition + * The starting position of the sub list. + * @param tasks + * A list of task in the correct order. + */ + public boolean isListMatching(int startPosition, ReadOnlyTask... tasks) throws IllegalArgumentException { + if (tasks.length + startPosition != getListView().getItems().size()) { + throw new IllegalArgumentException( + "List size mismatched\n" + "Expected " + (getListView().getItems().size() - 1) + " tasks"); + } + assertTrue(this.containsInOrder(startPosition, tasks)); + for (int i = 0; i < tasks.length; i++) { + final int scrollTo = i + startPosition; + guiRobot.interact(() -> getListView().scrollTo(scrollTo)); + guiRobot.sleep(200); + if (!TestUtil.compareCardAndTask(getTaskCardHandle(startPosition + i), tasks[i])) { + return false; + } + } + return true; + } + + public TaskCardHandle navigateToTask(String name) { + guiRobot.sleep(500); // Allow a bit of time for the list to be updated + final Optional task = getListView().getItems().stream() + .filter(p -> p.getName().toString().equals(name)).findAny(); + if (!task.isPresent()) { + throw new IllegalStateException("Name not found: " + name); + } + + return navigateToTask(task.get()); + } + + /** + * Navigates the listview to display and select the task. + */ + public TaskCardHandle navigateToTask(ReadOnlyTask task) { + int index = getTaskIndex(task); + + guiRobot.interact(() -> { + getListView().scrollTo(index); + guiRobot.sleep(150); + getListView().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) { + List tasksInList = getListView().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) { + return getListView().getItems().get(index); + } + + public TaskCardHandle getTaskCardHandle(int index) { + return getTaskCardHandle(new Task(getListView().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() { + return getListView().getItems().size(); + } +} diff --git a/src/test/java/seedu/address/commons/util/JsonUtilTest.java b/src/test/java/seedu/address/commons/util/JsonUtilTest.java deleted file mode 100644 index fc3902188807..000000000000 --- a/src/test/java/seedu/address/commons/util/JsonUtilTest.java +++ /dev/null @@ -1,12 +0,0 @@ -package seedu.address.commons.util; - -/** - * Tests JSON Read and Write - */ -public class JsonUtilTest { - - //TODO: @Test jsonUtil_readJsonStringToObjectInstance_correctObject() - - //TODO: @Test jsonUtil_writeThenReadObjectToJson_correctObject() - -} 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 e1ee0cfb4051..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.ShowHelpRequestEvent; -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 handleShowHelpRequestEvent(ShowHelpRequestEvent 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 04b0db1ce1c7..000000000000 --- a/src/test/java/seedu/address/storage/XmlAddressBookStorageTest.java +++ /dev/null @@ -1,105 +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)); - - //Save and read without specifying file path - original.addPerson(new Person(TypicalTestPersons.ida)); - xmlAddressBookStorage.saveAddressBook(original); //file path not specified - readBack = xmlAddressBookStorage.readAddressBook().get(); //file path not specified - 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/TestUtil.java b/src/test/java/seedu/address/testutil/TestUtil.java deleted file mode 100644 index 17c92d66398a..000000000000 --- a/src/test/java/seedu/address/testutil/TestUtil.java +++ /dev/null @@ -1,354 +0,0 @@ -package seedu.address.testutil; - -import com.google.common.io.Files; -import guitests.guihandles.PersonCardHandle; -import javafx.geometry.Bounds; -import javafx.geometry.Point2D; -import javafx.scene.Node; -import javafx.scene.Scene; -import javafx.scene.input.KeyCode; -import javafx.scene.input.KeyCodeCombination; -import javafx.scene.input.KeyCombination; -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 java.io.File; -import java.io.IOException; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.TimeoutException; -import java.util.stream.Collectors; - -/** - * A utility class for test cases. - */ -public class TestUtil { - - public static String LS = System.lineSeparator(); - - public static void assertThrows(Class expected, Runnable executable) { - try { - executable.run(); - } - catch (Throwable actualException) { - if (!actualException.getClass().isAssignableFrom(expected)) { - String message = String.format("Expected thrown: %s, actual: %s", expected.getName(), - actualException.getClass().getName()); - throw new AssertionFailedError(message); - } else return; - } - throw new AssertionFailedError( - 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() { - 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()) - }; - } catch (IllegalValueException e) { - assert false; - //not possible - return null; - } - } - - 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); - } - - /** - * Appends the file name to the sandbox folder path. - * Creates the sandbox folder if it doesn't exist. - * @param fileName - * @return - */ - public static String getFilePathInSandboxFolder(String fileName) { - try { - FileUtil.createDirs(new File(SANDBOX_FOLDER)); - } catch (IOException e) { - throw new RuntimeException(e); - } - return SANDBOX_FOLDER + fileName; - } - - public static void createDataFileWithSampleData(String filePath) { - createDataFileWithData(generateSampleStorageAddressBook(), filePath); - } - - public static void createDataFileWithData(T data, String filePath) { - try { - File saveFileForTesting = new File(filePath); - FileUtil.createIfMissing(saveFileForTesting); - XmlUtil.saveDataToFile(saveFileForTesting, data); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - 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 XmlSerializableAddressBook generateSampleStorageAddressBook() { - return new XmlSerializableAddressBook(generateEmptyAddressBook()); - } - - /** - * Tweaks the {@code keyCodeCombination} to resolve the {@code KeyCode.SHORTCUT} to their - * respective platform-specific keycodes - */ - public static KeyCode[] scrub(KeyCodeCombination keyCodeCombination) { - List keys = new ArrayList<>(); - if (keyCodeCombination.getAlt() == KeyCombination.ModifierValue.DOWN) { - keys.add(KeyCode.ALT); - } - if (keyCodeCombination.getShift() == KeyCombination.ModifierValue.DOWN) { - keys.add(KeyCode.SHIFT); - } - if (keyCodeCombination.getMeta() == KeyCombination.ModifierValue.DOWN) { - keys.add(KeyCode.META); - } - if (keyCodeCombination.getControl() == KeyCombination.ModifierValue.DOWN) { - keys.add(KeyCode.CONTROL); - } - keys.add(keyCodeCombination.getCode()); - return keys.toArray(new KeyCode[]{}); - } - - public static boolean isHeadlessEnvironment() { - String headlessProperty = System.getProperty("testfx.headless"); - return headlessProperty != null && headlessProperty.equals("true"); - } - - public static void captureScreenShot(String fileName) { - File file = GuiTest.captureScreenshot(); - try { - Files.copy(file, new File(fileName + ".png")); - } catch (IOException e) { - e.printStackTrace(); - } - } - - public static String descOnFail(Object... comparedObjects) { - return "Comparison failed \n" - + Arrays.asList(comparedObjects).stream() - .map(Object::toString) - .collect(Collectors.joining("\n")); - } - - public static void setFinalStatic(Field field, Object newValue) throws NoSuchFieldException, IllegalAccessException{ - field.setAccessible(true); - // remove final modifier from field - Field modifiersField = Field.class.getDeclaredField("modifiers"); - modifiersField.setAccessible(true); - // ~Modifier.FINAL is used to remove the final modifier from field so that its value is no longer - // final and can be changed - modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); - field.set(null, newValue); - } - - public static void initRuntime() throws TimeoutException { - FxToolkit.registerPrimaryStage(); - FxToolkit.hideStage(); - } - - public static void tearDownRuntime() throws Exception { - FxToolkit.cleanupStages(); - } - - /** - * Gets private method of a class - * Invoke the method using method.invoke(objectInstance, params...) - * - * Caveat: only find method declared in the current Class, not inherited from supertypes - */ - public static Method getPrivateMethod(Class objectClass, String methodName) throws NoSuchMethodException { - Method method = objectClass.getDeclaredMethod(methodName); - method.setAccessible(true); - return method; - } - - public static void renameFile(File file, String newFileName) { - try { - Files.copy(file, new File(newFileName)); - } catch (IOException e1) { - e1.printStackTrace(); - } - } - - /** - * Gets mid point of a node relative to the screen. - * @param node - * @return - */ - public static Point2D getScreenMidPoint(Node node) { - double x = getScreenPos(node).getMinX() + node.getLayoutBounds().getWidth() / 2; - double y = getScreenPos(node).getMinY() + node.getLayoutBounds().getHeight() / 2; - return new Point2D(x,y); - } - - /** - * Gets mid point of a node relative to its scene. - * @param node - * @return - */ - public static Point2D getSceneMidPoint(Node node) { - double x = getScenePos(node).getMinX() + node.getLayoutBounds().getWidth() / 2; - double y = getScenePos(node).getMinY() + node.getLayoutBounds().getHeight() / 2; - return new Point2D(x,y); - } - - /** - * Gets the bound of the node relative to the parent scene. - * @param node - * @return - */ - public static Bounds getScenePos(Node node) { - return node.localToScene(node.getBoundsInLocal()); - } - - public static Bounds getScreenPos(Node node) { - return node.localToScreen(node.getBoundsInLocal()); - } - - public static double getSceneMaxX(Scene scene) { - return scene.getX() + scene.getWidth(); - } - - public static double getSceneMaxY(Scene scene) { - return scene.getX() + scene.getHeight(); - } - - public static Object getLastElement(List list) { - return list.get(list.size() - 1); - } - - /** - * 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. - * @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()]); - } - - private static List asList(T[] objs) { - List list = new ArrayList<>(); - for(T obj : objs) { - list.add(obj); - } - 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]); - } - -} 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/flexitrack/TestApp.java similarity index 65% rename from src/test/java/seedu/address/TestApp.java rename to src/test/java/seedu/flexitrack/TestApp.java index 756642b6c180..2bc33cc89991 100644 --- a/src/test/java/seedu/address/TestApp.java +++ b/src/test/java/seedu/flexitrack/TestApp.java @@ -1,41 +1,41 @@ -package seedu.address; +package seedu.flexitrack; + +import java.util.function.Supplier; 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 java.util.function.Supplier; +import seedu.flexitrack.commons.core.Config; +import seedu.flexitrack.commons.core.GuiSettings; +import seedu.flexitrack.model.ReadOnlyFlexiTrack; +import seedu.flexitrack.model.UserPrefs; +import seedu.flexitrack.storage.XmlSerializableFlexiTrack; +import seedu.flexitrack.testutil.TestUtil; /** - * This class is meant to override some properties of MainApp so that it will be suited for - * testing + * This class is meant to override some properties of MainApp so that it will be + * suited for testing */ public class TestApp extends MainApp { public static final String SAVE_LOCATION_FOR_TESTING = TestUtil.getFilePathInSandboxFolder("sampleData.xml"); - protected static final String DEFAULT_PREF_FILE_LOCATION_FOR_TESTING = TestUtil.getFilePathInSandboxFolder("pref_testing.json"); + 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 static final String FLEXI_TRACK_NAME = "Test"; + 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; // If some initial local data has been provided, write those to the file if (initialDataSupplier.get() != null) { - TestUtil.createDataFileWithData( - new XmlSerializableAddressBook(this.initialDataSupplier.get()), + TestUtil.createDataFileWithData(new XmlSerializableFlexiTrack(this.initialDataSupplier.get()), this.saveFileLocation); } } @@ -44,9 +44,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.setFlexiTrackFilePath(saveFileLocation); config.setUserPrefsFilePath(DEFAULT_PREF_FILE_LOCATION_FOR_TESTING); - config.setAddressBookName(ADDRESS_BOOK_NAME); + config.setFlexiTrackName(FLEXI_TRACK_NAME); return config; } @@ -59,7 +59,6 @@ protected UserPrefs initPrefs(Config config) { return userPrefs; } - @Override public void start(Stage primaryStage) { ui.start(primaryStage); diff --git a/src/test/java/seedu/address/commons/core/ConfigTest.java b/src/test/java/seedu/flexitrack/commons/core/ConfigTest.java similarity index 63% rename from src/test/java/seedu/address/commons/core/ConfigTest.java rename to src/test/java/seedu/flexitrack/commons/core/ConfigTest.java index 62d58646f736..3685886a5e89 100644 --- a/src/test/java/seedu/address/commons/core/ConfigTest.java +++ b/src/test/java/seedu/flexitrack/commons/core/ConfigTest.java @@ -1,34 +1,31 @@ -package seedu.address.commons.core; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; +package seedu.flexitrack.commons.core; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + public class ConfigTest { @Rule public ExpectedException thrown = ExpectedException.none(); @Test public void toString_defaultObject_stringReturned() { - String defaultConfigAsString = "App title : Address App\n" + - "Current log level : INFO\n" + - "Preference file Location : preferences.json\n" + - "Local data file location : data/addressbook.xml\n" + - "AddressBook name : MyAddressBook"; + String defaultConfigAsString = "App title : FlexiTrack\n" + "Current log level : INFO\n" + + "Preference file Location : preferences.json\n" + "Local data file location : tasktracker.xml\n" + + "FlexiTrack name : MyTaskTracker"; assertEquals(defaultConfigAsString, new Config().toString()); } @Test - public void equalsMethod(){ + public void equalsMethod() { Config defaultConfig = new Config(); assertFalse(defaultConfig.equals(null)); assertTrue(defaultConfig.equals(defaultConfig)); } - } diff --git a/src/test/java/seedu/address/commons/core/VersionTest.java b/src/test/java/seedu/flexitrack/commons/core/VersionTest.java similarity index 82% rename from src/test/java/seedu/address/commons/core/VersionTest.java rename to src/test/java/seedu/flexitrack/commons/core/VersionTest.java index 87ac01f6c92d..9697f22cfe7b 100644 --- a/src/test/java/seedu/address/commons/core/VersionTest.java +++ b/src/test/java/seedu/flexitrack/commons/core/VersionTest.java @@ -1,12 +1,12 @@ -package seedu.address.commons.core; +package seedu.flexitrack.commons.core; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - public class VersionTest { @Rule public ExpectedException thrown = ExpectedException.none(); @@ -55,56 +55,58 @@ public void versionComparable_validVersion_compareToIsCorrect() { // Tests equality one = new Version(0, 0, 0, true); - another = new Version(0, 0, 0, true); + another = new Version(0, 0, 0, true); assertTrue(one.compareTo(another) == 0); one = new Version(11, 12, 13, false); - another = new Version(11, 12, 13, false); + another = new Version(11, 12, 13, false); assertTrue(one.compareTo(another) == 0); // Tests different patch one = new Version(0, 0, 5, false); - another = new Version(0, 0, 0, false); + another = new Version(0, 0, 0, false); assertTrue(one.compareTo(another) > 0); // Tests different minor one = new Version(0, 0, 0, false); - another = new Version(0, 5, 0, false); + another = new Version(0, 5, 0, false); assertTrue(one.compareTo(another) < 0); // Tests different major one = new Version(10, 0, 0, true); - another = new Version(0, 0, 0, true); + another = new Version(0, 0, 0, true); assertTrue(one.compareTo(another) > 0); // Tests high major vs low minor one = new Version(10, 0, 0, true); - another = new Version(0, 1, 0, true); + another = new Version(0, 1, 0, true); assertTrue(one.compareTo(another) > 0); // Tests high patch vs low minor one = new Version(0, 0, 10, false); - another = new Version(0, 1, 0, false); + another = new Version(0, 1, 0, false); assertTrue(one.compareTo(another) < 0); // Tests same major minor different patch one = new Version(2, 15, 0, false); - another = new Version(2, 15, 5, false); + another = new Version(2, 15, 5, false); assertTrue(one.compareTo(another) < 0); // Tests early access vs not early access on same version number one = new Version(2, 15, 0, true); - another = new Version(2, 15, 0, false); + another = new Version(2, 15, 0, false); assertTrue(one.compareTo(another) < 0); - // Tests early access lower version vs not early access higher version compare by version number first + // Tests early access lower version vs not early access higher version + // compare by version number first one = new Version(2, 15, 0, true); - another = new Version(2, 15, 5, false); + another = new Version(2, 15, 5, false); assertTrue(one.compareTo(another) < 0); - // Tests early access higher version vs not early access lower version compare by version number first + // Tests early access higher version vs not early access lower version + // compare by version number first one = new Version(2, 15, 0, false); - another = new Version(2, 15, 5, true); + another = new Version(2, 15, 5, true); assertTrue(one.compareTo(another) < 0); } @@ -122,16 +124,16 @@ public void versionComparable_validVersion_equalIsCorrect() { Version one, another; one = new Version(0, 0, 0, false); - another = new Version(0, 0, 0, false); + another = new Version(0, 0, 0, false); assertTrue(one.equals(another)); one = new Version(100, 191, 275, true); - another = new Version(100, 191, 275, true); + another = new Version(100, 191, 275, true); assertTrue(one.equals(another)); } - private void verifyVersionParsedCorrectly(String versionString, - int major, int minor, int patch, boolean isEarlyAccess) { + private void verifyVersionParsedCorrectly(String versionString, int major, int minor, int patch, + boolean isEarlyAccess) { assertEquals(new Version(major, minor, patch, isEarlyAccess), Version.fromString(versionString)); } } diff --git a/src/test/java/seedu/address/commons/util/AppUtilTest.java b/src/test/java/seedu/flexitrack/commons/util/AppUtilTest.java similarity index 75% rename from src/test/java/seedu/address/commons/util/AppUtilTest.java rename to src/test/java/seedu/flexitrack/commons/util/AppUtilTest.java index fbea1d0c1e8e..04b233f14807 100644 --- a/src/test/java/seedu/address/commons/util/AppUtilTest.java +++ b/src/test/java/seedu/flexitrack/commons/util/AppUtilTest.java @@ -1,29 +1,25 @@ -package seedu.address.commons.util; +package seedu.flexitrack.commons.util; + +import static org.junit.Assert.assertNotNull; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; -import static org.junit.Assert.assertNotNull; - public class AppUtilTest { @Rule public ExpectedException thrown = ExpectedException.none(); - - @Test - public void getImage_exitingImage(){ + public void getImage_exitingImage() { assertNotNull(AppUtil.getImage("/images/address_book_32.png")); } - @Test - public void getImage_nullGiven_assertionError(){ + public void getImage_nullGiven_assertionError() { thrown.expect(AssertionError.class); AppUtil.getImage(null); } - } diff --git a/src/test/java/seedu/address/commons/util/ConfigUtilTest.java b/src/test/java/seedu/flexitrack/commons/util/ConfigUtilTest.java similarity index 84% rename from src/test/java/seedu/address/commons/util/ConfigUtilTest.java rename to src/test/java/seedu/flexitrack/commons/util/ConfigUtilTest.java index 6699343c4a82..d3d5b9a715eb 100644 --- a/src/test/java/seedu/address/commons/util/ConfigUtilTest.java +++ b/src/test/java/seedu/flexitrack/commons/util/ConfigUtilTest.java @@ -1,20 +1,20 @@ -package seedu.address.commons.util; +package seedu.flexitrack.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 static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import java.io.File; import java.io.IOException; import java.util.Optional; import java.util.logging.Level; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; + +import seedu.flexitrack.commons.core.Config; +import seedu.flexitrack.commons.exceptions.DataConversionException; public class ConfigUtilTest { @@ -43,17 +43,18 @@ public void read_notJasonFormat_exceptionThrown() throws DataConversionException thrown.expect(DataConversionException.class); read("NotJasonFormatConfig.json"); - /* 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 + /* + * 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 read_fileInOrder_successfullyRead() throws DataConversionException { - Config expected = getTypicalConfig(); - Config actual = read("TypicalConfig.json").get(); + assertEquals(expected, actual); } @@ -76,8 +77,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.setFlexiTrackFilePath("tasktracker.xml"); + config.setFlexiTrackName("TypicalTaskTracker"); return config; } @@ -105,12 +106,12 @@ public void saveConfig_allInOrder_success() throws DataConversionException, IOEx String configFilePath = testFolder.getRoot() + File.separator + "TempConfig.json"; ConfigUtil configStorage = new ConfigUtil(); - //Try writing when the file doesn't exist + // Try writing when the file doesn't exist configStorage.saveConfig(original, configFilePath); Config readBack = configStorage.readConfig(configFilePath).get(); assertEquals(original, readBack); - //Try saving when the file exists + // Try saving when the file exists original.setAppTitle("Updated Title"); original.setLogLevel(Level.FINE); configStorage.saveConfig(original, configFilePath); @@ -124,10 +125,7 @@ private void save(Config config, String configFileInTestDataFolder) throws IOExc } private String addToTestDataPathIfNotNull(String configFileInTestDataFolder) { - return configFileInTestDataFolder != null - ? TEST_DATA_FOLDER + configFileInTestDataFolder - : null; + return configFileInTestDataFolder != null ? TEST_DATA_FOLDER + configFileInTestDataFolder : null; } - } diff --git a/src/test/java/seedu/address/commons/util/FileUtilTest.java b/src/test/java/seedu/flexitrack/commons/util/FileUtilTest.java similarity index 84% rename from src/test/java/seedu/address/commons/util/FileUtilTest.java rename to src/test/java/seedu/flexitrack/commons/util/FileUtilTest.java index 8de2621799cf..6ab6d180397a 100644 --- a/src/test/java/seedu/address/commons/util/FileUtilTest.java +++ b/src/test/java/seedu/flexitrack/commons/util/FileUtilTest.java @@ -1,26 +1,25 @@ -package seedu.address.commons.util; +package seedu.flexitrack.commons.util; +import static org.junit.Assert.assertEquals; + +import java.io.File; +import java.io.IOException; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; -import seedu.address.testutil.SerializableTestClass; -import seedu.address.testutil.TestUtil; -import java.io.File; -import java.io.IOException; - -import static org.junit.Assert.assertEquals; +import seedu.flexitrack.testutil.SerializableTestClass; +import seedu.flexitrack.testutil.TestUtil; public class FileUtilTest { private static final File SERIALIZATION_FILE = new File(TestUtil.getFilePathInSandboxFolder("serialize.json")); - @Rule public ExpectedException thrown = ExpectedException.none(); @Test - public void getPath(){ + public void getPath() { // valid case assertEquals("folder" + File.separator + "sub-folder", FileUtil.getPath("folder/sub-folder")); @@ -48,8 +47,8 @@ public void serializeObjectToJsonFile_noExceptionThrown() throws IOException { public void deserializeObjectFromJsonFile_noExceptionThrown() throws IOException { FileUtil.writeToFile(SERIALIZATION_FILE, SerializableTestClass.JSON_STRING_REPRESENTATION); - SerializableTestClass serializableTestClass = FileUtil - .deserializeObjectFromJsonFile(SERIALIZATION_FILE, SerializableTestClass.class); + SerializableTestClass serializableTestClass = FileUtil.deserializeObjectFromJsonFile(SERIALIZATION_FILE, + SerializableTestClass.class); assertEquals(serializableTestClass.getName(), SerializableTestClass.getNameTestValue()); assertEquals(serializableTestClass.getListOfLocalDateTimes(), SerializableTestClass.getListTestValues()); diff --git a/src/test/java/seedu/flexitrack/commons/util/JsonUtilTest.java b/src/test/java/seedu/flexitrack/commons/util/JsonUtilTest.java new file mode 100644 index 000000000000..ed46bd4afea1 --- /dev/null +++ b/src/test/java/seedu/flexitrack/commons/util/JsonUtilTest.java @@ -0,0 +1,12 @@ +package seedu.flexitrack.commons.util; + +/** + * Tests JSON Read and Write + */ +public class JsonUtilTest { + + // TODO: @Test jsonUtil_readJsonStringToObjectInstance_correctObject() + + // TODO: @Test jsonUtil_writeThenReadObjectToJson_correctObject() + +} diff --git a/src/test/java/seedu/address/commons/util/StringUtilTest.java b/src/test/java/seedu/flexitrack/commons/util/StringUtilTest.java similarity index 58% rename from src/test/java/seedu/address/commons/util/StringUtilTest.java rename to src/test/java/seedu/flexitrack/commons/util/StringUtilTest.java index 194dd71d2c3f..501e88872535 100644 --- a/src/test/java/seedu/address/commons/util/StringUtilTest.java +++ b/src/test/java/seedu/flexitrack/commons/util/StringUtilTest.java @@ -1,17 +1,16 @@ -package seedu.address.commons.util; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - -import java.io.FileNotFoundException; +package seedu.flexitrack.commons.util; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import java.io.FileNotFoundException; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + public class StringUtilTest { @Rule @@ -26,27 +25,29 @@ public void isUnsignedPositiveInteger() { assertFalse(StringUtil.isUnsignedInteger(" ")); assertFalse(StringUtil.isUnsignedInteger("-1")); assertFalse(StringUtil.isUnsignedInteger("0")); - assertFalse(StringUtil.isUnsignedInteger("+1")); //should be unsigned - assertFalse(StringUtil.isUnsignedInteger("-1")); //should be unsigned - assertFalse(StringUtil.isUnsignedInteger(" 10")); //should not contain whitespaces - assertFalse(StringUtil.isUnsignedInteger("10 ")); //should not contain whitespaces - assertFalse(StringUtil.isUnsignedInteger("1 0")); //should not contain whitespaces + assertFalse(StringUtil.isUnsignedInteger("+1")); // should be unsigned + assertFalse(StringUtil.isUnsignedInteger("-1")); // should be unsigned + assertFalse(StringUtil.isUnsignedInteger(" 10")); // should not contain + // whitespaces + assertFalse(StringUtil.isUnsignedInteger("10 ")); // should not contain + // whitespaces + assertFalse(StringUtil.isUnsignedInteger("1 0")); // should not contain + // whitespaces assertTrue(StringUtil.isUnsignedInteger("1")); assertTrue(StringUtil.isUnsignedInteger("10")); } @Test - public void getDetails_exceptionGiven(){ + public void getDetails_exceptionGiven() { assertThat(StringUtil.getDetails(new FileNotFoundException("file not found")), - containsString("java.io.FileNotFoundException: file not found")); + containsString("java.io.FileNotFoundException: file not found")); } @Test - public void getDetails_nullGiven_assertionError(){ + public void getDetails_nullGiven_assertionError() { thrown.expect(AssertionError.class); StringUtil.getDetails(null); } - } diff --git a/src/test/java/seedu/address/commons/util/UrlUtilTest.java b/src/test/java/seedu/flexitrack/commons/util/UrlUtilTest.java similarity index 97% rename from src/test/java/seedu/address/commons/util/UrlUtilTest.java rename to src/test/java/seedu/flexitrack/commons/util/UrlUtilTest.java index 58efab5fd499..d67609a74bfe 100644 --- a/src/test/java/seedu/address/commons/util/UrlUtilTest.java +++ b/src/test/java/seedu/flexitrack/commons/util/UrlUtilTest.java @@ -1,12 +1,12 @@ -package seedu.address.commons.util; +package seedu.flexitrack.commons.util; -import org.junit.Test; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import java.net.MalformedURLException; import java.net.URL; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import org.junit.Test; /** * Tests the UrlUtil methods. diff --git a/src/test/java/seedu/address/commons/util/XmlUtilTest.java b/src/test/java/seedu/flexitrack/commons/util/XmlUtilTest.java similarity index 58% rename from src/test/java/seedu/address/commons/util/XmlUtilTest.java rename to src/test/java/seedu/flexitrack/commons/util/XmlUtilTest.java index dc4fd886c23e..96955d7c4e35 100644 --- a/src/test/java/seedu/address/commons/util/XmlUtilTest.java +++ b/src/test/java/seedu/flexitrack/commons/util/XmlUtilTest.java @@ -1,26 +1,28 @@ -package seedu.address.commons.util; +package seedu.flexitrack.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 static org.junit.Assert.assertEquals; -import javax.xml.bind.JAXBException; import java.io.File; import java.io.FileNotFoundException; -import static org.junit.Assert.assertEquals; +import javax.xml.bind.JAXBException; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import seedu.flexitrack.model.FlexiTrack; +import seedu.flexitrack.storage.XmlSerializableFlexiTrack; +import seedu.flexitrack.testutil.FlexiTrackBuilder; +import seedu.flexitrack.testutil.TestUtil; 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 + "validFlexiTrack.xml"); + private static final File TEMP_FILE = new File(TestUtil.getFilePathInSandboxFolder("tempFlexiTrack.xml")); @Rule public ExpectedException thrown = ExpectedException.none(); @@ -28,7 +30,7 @@ public class XmlUtilTest { @Test public void getDataFromFile_nullFile_AssertionError() throws Exception { thrown.expect(AssertionError.class); - XmlUtil.getDataFromFile(null, AddressBook.class); + XmlUtil.getDataFromFile(null, FlexiTrack.class); } @Test @@ -40,26 +42,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, FlexiTrack.class); } @Test public void getDataFromFile_emptyFile_DataFormatMismatchException() throws Exception { thrown.expect(JAXBException.class); - XmlUtil.getDataFromFile(EMPTY_FILE, AddressBook.class); + XmlUtil.getDataFromFile(EMPTY_FILE, FlexiTrack.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()); + XmlSerializableFlexiTrack dataFromFile = XmlUtil.getDataFromFile(VALID_FILE, XmlSerializableFlexiTrack.class); + assertEquals(9, dataFromFile.getTaskList().size()); } @Test public void saveDataToFile_nullFile_AssertionError() throws Exception { thrown.expect(AssertionError.class); - XmlUtil.saveDataToFile(null, new AddressBook()); + XmlUtil.saveDataToFile(null, new FlexiTrack()); } @Test @@ -71,23 +72,24 @@ 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 FlexiTrack()); } @Test public void saveDataToFile_validFile_dataSaved() throws Exception { TEMP_FILE.createNewFile(); - XmlSerializableAddressBook dataToWrite = new XmlSerializableAddressBook(new AddressBook()); + XmlSerializableFlexiTrack dataToWrite = new XmlSerializableFlexiTrack(new FlexiTrack()); XmlUtil.saveDataToFile(TEMP_FILE, dataToWrite); - XmlSerializableAddressBook dataFromFile = XmlUtil.getDataFromFile(TEMP_FILE, XmlSerializableAddressBook.class); - assertEquals((new AddressBook(dataToWrite)).toString(),(new AddressBook(dataFromFile)).toString()); - //TODO: use equality instead of string comparisons + XmlSerializableFlexiTrack dataFromFile = XmlUtil.getDataFromFile(TEMP_FILE, XmlSerializableFlexiTrack.class); + assertEquals((new FlexiTrack(dataToWrite)).toString(), (new FlexiTrack(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()); + FlexiTrackBuilder builder = new FlexiTrackBuilder(new FlexiTrack()); + dataToWrite = new XmlSerializableFlexiTrack( + builder.withPerson(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, XmlSerializableFlexiTrack.class); + assertEquals((new FlexiTrack(dataToWrite)).toString(), (new FlexiTrack(dataFromFile)).toString()); } } diff --git a/src/test/java/seedu/flexitrack/logic/LogicManagerTest.java b/src/test/java/seedu/flexitrack/logic/LogicManagerTest.java new file mode 100644 index 000000000000..66b2c5ac8e7f --- /dev/null +++ b/src/test/java/seedu/flexitrack/logic/LogicManagerTest.java @@ -0,0 +1,526 @@ +package seedu.flexitrack.logic; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static seedu.flexitrack.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.flexitrack.commons.core.Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX; +import static seedu.flexitrack.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import com.google.common.eventbus.Subscribe; + +import seedu.flexitrack.commons.core.EventsCenter; +import seedu.flexitrack.commons.events.model.FlexiTrackChangedEvent; +import seedu.flexitrack.commons.events.ui.JumpToListRequestEvent; +import seedu.flexitrack.commons.events.ui.ShowHelpRequestEvent; +import seedu.flexitrack.logic.commands.AddCommand; +import seedu.flexitrack.logic.commands.ClearCommand; +import seedu.flexitrack.logic.commands.Command; +import seedu.flexitrack.logic.commands.CommandResult; +import seedu.flexitrack.logic.commands.DeleteCommand; +import seedu.flexitrack.logic.commands.ExitCommand; +import seedu.flexitrack.logic.commands.FindCommand; +import seedu.flexitrack.logic.commands.HelpCommand; +import seedu.flexitrack.logic.commands.ListCommand; +import seedu.flexitrack.model.FlexiTrack; +import seedu.flexitrack.model.Model; +import seedu.flexitrack.model.ModelManager; +import seedu.flexitrack.model.ReadOnlyFlexiTrack; +import seedu.flexitrack.model.task.DateTimeInfo; +import seedu.flexitrack.model.task.Name; +import seedu.flexitrack.model.task.ReadOnlyTask; +import seedu.flexitrack.model.task.Task; +import seedu.flexitrack.storage.StorageManager; +import seedu.flexitrack.testutil.TestTask; +import seedu.flexitrack.testutil.TypicalTestTasks; + +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 ReadOnlyFlexiTrack latestSavedFlexiTracker; + private boolean helpShown; + private int targetedJumpIndex; + + @Subscribe + private void handleLocalModelrChangedEvent(FlexiTrackChangedEvent abce) { + latestSavedFlexiTracker = new FlexiTrack(abce.data); + } + + @Subscribe + private void handleShowHelpRequestEvent(ShowHelpRequestEvent she) { + helpShown = true; + } + + @Subscribe + private void handleJumpToListRequestEvent(JumpToListRequestEvent je) { + targetedJumpIndex = je.targetIndex; + } + + @Before + public void setup() { + model = new ModelManager(); + String tempFlexiTrackerFile = saveFolder.getRoot().getPath() + "TempFlexiTracker.xml"; + String tempPreferencesFile = saveFolder.getRoot().getPath() + "TempPreferences.json"; + logic = new LogicManager(model, new StorageManager(tempFlexiTrackerFile, tempPreferencesFile)); + EventsCenter.getInstance().registerHandler(this); + + latestSavedFlexiTracker = new FlexiTrack(model.getFlexiTrack()); // 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 'FlexiTracker' and the 'last shown list' are expected to be + * empty. + * + * @see #assertCommandBehavior(String, String, ReadOnlyFlexiTrack, List) + */ + private void assertCommandBehavior(String inputCommand, String expectedMessage) throws Exception { + assertCommandBehavior(inputCommand, expectedMessage, new FlexiTrack(), 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 FlexiTracker data are same as those in the + * {@code expectedFlexiTracker}
+ * - the backing list shown by UI matches the {@code shownList}
+ * - {@code expectedFlexiTracker} was saved to the storage file.
+ */ + private void assertCommandBehavior(String inputCommand, String expectedMessage, + ReadOnlyFlexiTrack expectedFlexiTracker, 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.getFilteredTaskList()); + + // Confirm the state of data (saved and in-memory) is as expected + assertEquals(expectedFlexiTracker, model.getFlexiTrack()); + assertEquals(expectedFlexiTracker, latestSavedFlexiTracker); + } + + @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.HELP_MESSAGE_USAGE); + // 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 FlexiTrack(), Collections.emptyList()); + } + + // TODO: What is the limitation of add???? + // @Test + // public void execute_add_invalidArgsFormat() throws Exception { + // String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, + // AddCommand.MESSAGE_USAGE); + // assertCommandBehavior( + // "adds 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_invalidTimeData() throws Exception { + assertCommandBehavior("add Apply for job by/ mondat", DateTimeInfo.MESSAGE_DATETIMEINFO_CONSTRAINTS); + + } + + @Test + public void execute_add_successful() throws Exception { + // setup expectations + TestDataHelper helper = new TestDataHelper(); + Task toBeAdded = helper.midterm(); + FlexiTrack expectedAB = new FlexiTrack(); + 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_addDuplicate_notAllowed() throws Exception { + // setup expectations + TestDataHelper helper = new TestDataHelper(); + Task toBeAdded = helper.midterm(); + FlexiTrack expectedAB = new FlexiTrack(); + expectedAB.addTask(toBeAdded); + + // setup starting state + model.addTask(toBeAdded); // task already in internal FlexiTracker + + // execute command and verify result + assertCommandBehavior(helper.generateAddCommand(toBeAdded), AddCommand.MESSAGE_DUPLICATE_TASK, expectedAB, + expectedAB.getTaskList()); + + } + + // TODO: need to change all the test casses + @Test + public void execute_list_showsAllPersons() throws Exception { + // prepare expectations + TestDataHelper helper = new TestDataHelper(); + FlexiTrack expectedAB = helper.generateFlexiTracker(2); + List expectedList = expectedAB.getTaskList(); + + // prepare FlexiTracker 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 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 FlexiTrack()); + for (Task p : taskList) { + model.addTask(p); + } + + assertCommandBehavior(commandWord + " 3", expectedMessage, model.getFlexiTrack(), taskList); + } + + @Test + public void sort(){ + TypicalTestTasks td = new TypicalTestTasks(); + TestTask[] currentArray = td.getTypicalUnsortedTasks(); + List currentList = Arrays.asList(currentArray); + + Arrays.sort(currentArray); + Collections.sort(currentList); + + TestTask[] expectedArray = td.getTypicalSortedTasks(); + List expectedList = Arrays.asList(expectedArray); + + // Test Arrays Sort Correctly + assertTrue(containsInOrder(currentList, expectedList)); + + // Test Lists Sort Correctly + currentList = Arrays.asList(currentArray); + assertTrue(containsInOrder(currentList, expectedList)); + } + + private boolean containsInOrder(List currentList, List expectedList) { + + if (currentList.size() != expectedList.size()) { + return false; + } + + // Return false if any of the tasks doesn't match + for (int i = 0; i < currentList.size(); i++) { + if (!currentList.get(i).getName().toString().equals(expectedList.get(i).getName().toString())) { + return false; + } + } + + return true; + } + + + @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.generateTaskList(3); + Collections.sort(threePersons); + FlexiTrack expectedAB = helper.generateFlexiTracker(threePersons); + expectedAB.removeTask(threePersons.get(1)); + helper.addToModel(model, threePersons); + + assertCommandBehavior("delete 2", String.format(DeleteCommand.MESSAGE_DELETE_TASK_SUCCESS, threePersons.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 fourPersons = helper.generateTaskList(p1, pTarget1, p2, pTarget2); + Collections.sort(fourPersons); + FlexiTrack expectedAB = helper.generateFlexiTracker(fourPersons); + List expectedList = helper.generateTaskList(pTarget1, pTarget2); + Collections.sort(expectedList); + helper.addToModel(model, fourPersons); + + assertCommandBehavior("find KEY", Command.getMessageForTaskListShownSummary(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 fourPersons = helper.generateTaskList(p3, p1, p4, p2); + Collections.sort(fourPersons); + FlexiTrack expectedAB = helper.generateFlexiTracker(fourPersons); + List expectedList = fourPersons; + helper.addToModel(model, fourPersons); + + assertCommandBehavior("find KEY", Command.getMessageForTaskListShownSummary(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 fourPersons = helper.generateTaskList(pTarget1, p1, pTarget2, pTarget3); + Collections.sort(fourPersons); + FlexiTrack expectedAB = helper.generateFlexiTracker(fourPersons); + List expectedList = helper.generateTaskList(pTarget1, pTarget2, pTarget3); + helper.addToModel(model, fourPersons); + + assertCommandBehavior("find key rAnDoM", Command.getMessageForTaskListShownSummary(expectedList.size()), + expectedAB, expectedList); + } + + /** + * A utility class to generate test data. + */ + class TestDataHelper { + + Task midterm() throws Exception { + Name name = new Name("Midter cs 2101"); + DateTimeInfo dueDate = new DateTimeInfo("Mar 23 2017 09:00"); + DateTimeInfo startingTime = new DateTimeInfo("Feb 29 2000 00:00"); + DateTimeInfo endingTime = new DateTimeInfo("Feb 29 2000 00:00"); + return new Task(name, dueDate, startingTime, endingTime); + } + + /** + * 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 + */ + Task generateTask(int seed) throws Exception { + return new Task(new Name("Person " + seed), new DateTimeInfo("" + Math.abs(seed)), + new DateTimeInfo(seed + "@email"), new DateTimeInfo("House of " + seed)); + } + + /** Generates the correct add command based on the person given */ + String generateAddCommand(Task p) { + StringBuffer cmd = new StringBuffer(); + + cmd.append("add "); + + cmd.append(p.getName().toString()); + if (p.getIsTask()) { + cmd.append(" by/ ").append(p.getDueDate()); + } else if (p.getIsEvent()) { + cmd.append(" from/ ").append(p.getStartTime()); + cmd.append(" to/ ").append(p.getEndTime()); + } + return cmd.toString(); + } + + /** + * Generates an AddressBook with auto-generated tasks. + */ + FlexiTrack generateFlexiTracker(int numGenerated) throws Exception { + FlexiTrack addressBook = new FlexiTrack(); + addToAddressBook(addressBook, numGenerated); + return addressBook; + } + + /** + * Generates an AddressBook based on the list of tasks given. + */ + FlexiTrack generateFlexiTracker(List tasks) throws Exception { + FlexiTrack flexiTracker = new FlexiTrack(); + addToFlexiTracker(flexiTracker, tasks); + return flexiTracker; + } + + /** + * Adds auto-generated task objects to the given AddressBook + * + * @param addressBook + * The AddressBook to which the tasks will be added + */ + void addToAddressBook(FlexiTrack addressBook, int numGenerated) throws Exception { + addToFlexiTracker(addressBook, generateTaskList(numGenerated)); + } + + /** + * Adds the given list of Tasks to the given AddressBook + */ + void addToFlexiTracker(FlexiTrack flexiTracker, List tasksToAdd) throws Exception { + for (Task p : tasksToAdd) { + flexiTracker.addTask(p); + } + } + + /** + * Adds auto-generated Task objects to the given model + * + * @param model + * The model to which the Tasks will be added + */ + void addToModel(Model model, int numGenerated) throws Exception { + addToModel(model, generateTaskList(numGenerated)); + } + + /** + * Adds the given list of Tasks to the given model + */ + void addToModel(Model model, List tasksToAdd) throws Exception { + for (Task p : tasksToAdd) { + model.addTask(p); + } + } + + /** + * Generates a list of Tasks based on the flags. + */ + List generateTaskList(int numGenerated) throws Exception { + List tasks = new ArrayList<>(); + for (int i = 1; i <= numGenerated; i++) { + tasks.add(generateTask(i)); + } + return tasks; + } + + List generateTaskList(Task... tasks) { + return Arrays.asList(tasks); + } + + /** + * Generates a Task object with given name. Other fields will have some + * dummy values. + */ + Task generateTaskWithName(String name) throws Exception { + return new Task(new Name(name), new DateTimeInfo("1/1/2011"), new DateTimeInfo("2/2/2012"), + new DateTimeInfo("3/3/2013")); + } + } +} diff --git a/src/test/java/seedu/address/model/UnmodifiableObservableListTest.java b/src/test/java/seedu/flexitrack/model/UnmodifiableObservableListTest.java similarity index 88% rename from src/test/java/seedu/address/model/UnmodifiableObservableListTest.java rename to src/test/java/seedu/flexitrack/model/UnmodifiableObservableListTest.java index 0334d7e42073..f8447a1108a6 100644 --- a/src/test/java/seedu/address/model/UnmodifiableObservableListTest.java +++ b/src/test/java/seedu/flexitrack/model/UnmodifiableObservableListTest.java @@ -1,16 +1,21 @@ -package seedu.address.model; +package seedu.flexitrack.model; + +import static org.junit.Assert.assertSame; +import static seedu.flexitrack.testutil.TestUtil.assertThrows; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; -import javafx.collections.FXCollections; 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 javafx.collections.FXCollections; +import seedu.flexitrack.commons.core.UnmodifiableObservableList; public class UnmodifiableObservableListTest { diff --git a/src/test/java/seedu/flexitrack/model/task/DateTimeInfoTest.java b/src/test/java/seedu/flexitrack/model/task/DateTimeInfoTest.java new file mode 100644 index 000000000000..725d0d639a6f --- /dev/null +++ b/src/test/java/seedu/flexitrack/model/task/DateTimeInfoTest.java @@ -0,0 +1,196 @@ +package seedu.flexitrack.model.task; + +import static org.junit.Assert.assertTrue; +import static seedu.flexitrack.model.task.DateTimeInfo.MESSAGE_FROM_IS_AFTER_TO; + +import java.text.SimpleDateFormat; +import java.util.Calendar; + +import org.junit.Before; +import org.junit.Test; + +import seedu.flexitrack.commons.exceptions.IllegalValueException; + +// @@ author A0127686R +public class DateTimeInfoTest { + + private DateTimeInfo testTime1; + private DateTimeInfo testTime2; + + @Before + public void setup() { + testTime1 = null; + testTime2 = null; + } + + @Test + public void DateTimeInfo_ValidInputWithNoSpecificHoursAndMinutes_returnsDate() { + final String validInput = "3 July 2018"; + final String expectedSetTime = "Jul 03 2018 08:00"; + DateTimeInfoAndAssertCorrect(validInput, expectedSetTime); + } + + @Test + public void DateTimeInfo_ValidInputWithSpecificHours_returnsDate() { + final String validInput = "22 january 2017 4pm"; + final String expectedSetTime = "Jan 22 2017 16:00"; + DateTimeInfoAndAssertCorrect(validInput, expectedSetTime); + } + + @Test + public void DateTimeInfo_RelativeValidInputWithNoSpecificHoursAndMinutes_returnsDate() { + Calendar calendar = Calendar.getInstance(); + calendar.add(Calendar.MONTH, 1); + final String validInput = "next month"; + final String expectedSetTime = new SimpleDateFormat("MMM dd yyyy 08:00").format(calendar.getTime()); + DateTimeInfoAndAssertCorrect(validInput, expectedSetTime); + } + + @Test + public void DateTimeInfo_RelativeValidInputSpecificHours_returnsDate() { + Calendar calendar = Calendar.getInstance(); + calendar.add(Calendar.MONTH, -1); + final String validInput = "last month"; + final String expectedSetTime = new SimpleDateFormat("MMM dd yyyy 08:00").format(calendar.getTime()); + DateTimeInfoAndAssertCorrect(validInput, expectedSetTime); + } + + @Test + public void DateTimeInfo_EndTimeFormat_returnsDate() throws IllegalValueException { + testTime1 = new DateTimeInfo("June 12 2017"); + testTime1.formatEndTime(testTime1); + testTime2 = new DateTimeInfo("3pm"); + testTime2.formatEndTime(testTime1); + final String expectedSetTime1 = "Jun 12 2017 17:00"; + final String expectedSetTime2 = "Jun 12 2017 15:00"; + assertTrue(testTime1.toString().equals(expectedSetTime1)); + assertTrue(testTime2.toString().equals(expectedSetTime2)); + } + + @Test + public void IsDateNull_DateIsNull_True() throws IllegalValueException { + testTime1 = new DateTimeInfo("Feb 29 2000 00:00"); + assertTrue(testTime1.isDateNull()); + } + + @Test + public void IsDateNull_DateIsNotNull_False() throws IllegalValueException { + testTime1 = new DateTimeInfo("Feb 29 2001 00:00"); + assertTrue(!testTime1.isDateNull()); + } + + @Test + public void durationOfTheEvent_DurationIsPositive_true() throws IllegalValueException { + testTime1 = new DateTimeInfo("May 12 2017 07:00"); + testTime2 = new DateTimeInfo("July 19 2017 10:00"); + String expected = "Duration of the event is: 2 months 7 days 3 hours."; + assertTrue(DateTimeInfo.durationOfTheEvent(testTime1.toString(), testTime2.toString()).equals(expected)); + + testTime2 = new DateTimeInfo("Aug 02 2019 10:30"); + expected = "Duration of the event is: 2 years 2 months 21 days 3 hours 30 minutes."; + assertTrue(DateTimeInfo.durationOfTheEvent(testTime1.toString(), testTime2.toString()).equals(expected)); + + testTime2 = new DateTimeInfo("Jun 13 2018 08:01"); + expected = "Duration of the event is: 1 year 1 month 1 day 1 hour 1 minute."; + assertTrue(DateTimeInfo.durationOfTheEvent(testTime1.toString(), testTime2.toString()).equals(expected)); + + } + + @Test + public void durationOfTheEvent_DurationIsNegative_Message() throws IllegalValueException { + testTime1 = new DateTimeInfo("Oct 12 2017 07:00"); + testTime2 = new DateTimeInfo("Feb 19 2017 10:00"); + String expected = MESSAGE_FROM_IS_AFTER_TO; + assertTrue(DateTimeInfo.durationOfTheEvent(testTime1.toString(), testTime2.toString()).equals(expected)); + + testTime1 = new DateTimeInfo("Dec 12 2017 07:30"); + testTime2 = new DateTimeInfo("Nov 19 2017 02:10"); + assertTrue(DateTimeInfo.durationOfTheEvent(testTime1.toString(), testTime2.toString()).equals(expected)); + + testTime1 = new DateTimeInfo("Sep 12 2017 07:30"); + testTime2 = new DateTimeInfo("March 19 2017 02:10"); + assertTrue(DateTimeInfo.durationOfTheEvent(testTime1.toString(), testTime2.toString()).equals(expected)); + } + + @Test + public void durationOfTheEvent_ExactSameTiming_Message() throws IllegalValueException { + testTime1 = new DateTimeInfo("Dec 26 2017 07:00"); + testTime2 = new DateTimeInfo("Dec 26 2017 07:00"); + String expected = "Event starts and end at the same time."; + assertTrue(DateTimeInfo.durationOfTheEvent(testTime1.toString(), testTime2.toString()).equals(expected)); + } + + @Test + public void isInTheFuture_validInput_True() throws IllegalValueException { + testTime1 = new DateTimeInfo("Jun 26 2017 07:00"); + testTime2 = new DateTimeInfo("Jun 26 2018 07:00"); + assertTrue(testTime1.isInTheFuture(testTime2)); + } + + @Test + public void isInTheFuture_invalidInput_False() throws IllegalValueException { + testTime1 = new DateTimeInfo("Jun 26 2017 07:00"); + testTime2 = new DateTimeInfo("Jun 26 2018 07:00"); + assertTrue(!testTime2.isInTheFuture(testTime1)); + } + + @Test + public void isInTheFuture_sameInput_True() throws IllegalValueException { + testTime1 = new DateTimeInfo("Jun 26 2017 07:00"); + assertTrue(testTime1.isInTheFuture(testTime1)); + } + + @Test + public void isInThePast_validInput_True() throws IllegalValueException { + testTime1 = new DateTimeInfo("Jun 26 2017 07:00"); + testTime2 = new DateTimeInfo("Jun 26 2018 07:00"); + assertTrue(testTime2.isInThePast(testTime1)); + } + + @Test + public void isInThePast_invalidInput_False() throws IllegalValueException { + testTime1 = new DateTimeInfo("Jun 26 2017 07:00"); + testTime2 = new DateTimeInfo("Jun 26 2018 07:00"); + assertTrue(!testTime1.isInThePast(testTime2)); + } + + @Test + public void isInThePast_sameInput_True() throws IllegalValueException { + testTime1 = new DateTimeInfo("Jun 26 2017 07:00"); + assertTrue(!testTime1.isInThePast(testTime1)); + } + + @Test + public void isTaskAnEventPassingThisDate_DateIsWithinEvent_True() throws IllegalValueException { + Task event = new Task(new Name("event"), new DateTimeInfo("Feb 29 2000 00:00"), + new DateTimeInfo("April 26 2017 07:00"), new DateTimeInfo("Jan 26 2018 07:00")); + testTime1 = new DateTimeInfo("Jun 26 2017 07:00"); + assertTrue(DateTimeInfo.isTaskAnEventPassingThisDate(event, testTime1.toString())); + } + + @Test + public void isTaskAnEventPassingThisDate_DateIsNotWithinEvent_True() throws IllegalValueException { + Task event = new Task(new Name("event"), new DateTimeInfo("Feb 29 2000 00:00"), + new DateTimeInfo("April 26 2017 07:00"), new DateTimeInfo("Jan 26 2018 07:00")); + testTime1 = new DateTimeInfo("Jan 26 2017 07:00"); + assertTrue(!DateTimeInfo.isTaskAnEventPassingThisDate(event, testTime1.toString())); + } + + @Test + public void isOnTheDate_DateIsNotWithinEvent_True() throws IllegalValueException { + Task event = new Task(new Name("event"), new DateTimeInfo("Feb 29 2000 00:00"), + new DateTimeInfo("April 26 2017 07:00"), new DateTimeInfo("Jan 26 2018 07:00")); + testTime1 = new DateTimeInfo("April 26 2017 07:00"); + assertTrue(testTime1.isOnTheDate(event)); + } + + private void DateTimeInfoAndAssertCorrect(String validInput, String expectedSetTime) { + try { + testTime1 = new DateTimeInfo(validInput); + testTime1.formatStartOrDueDateTime(); + } catch (IllegalValueException e) { + } + assertTrue(expectedSetTime.equals(testTime1.toString())); + } + +} diff --git a/src/test/java/seedu/address/storage/JsonUserPrefsStorageTest.java b/src/test/java/seedu/flexitrack/storage/JsonUserPrefsStorageTest.java similarity index 86% rename from src/test/java/seedu/address/storage/JsonUserPrefsStorageTest.java rename to src/test/java/seedu/flexitrack/storage/JsonUserPrefsStorageTest.java index 4e87203611be..edcc81566db6 100644 --- a/src/test/java/seedu/address/storage/JsonUserPrefsStorageTest.java +++ b/src/test/java/seedu/flexitrack/storage/JsonUserPrefsStorageTest.java @@ -1,20 +1,20 @@ -package seedu.address.storage; +package seedu.flexitrack.storage; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +import java.io.File; +import java.io.IOException; +import java.util.Optional; 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 java.io.File; -import java.io.IOException; -import java.util.Optional; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; +import seedu.flexitrack.commons.exceptions.DataConversionException; +import seedu.flexitrack.commons.util.FileUtil; +import seedu.flexitrack.model.UserPrefs; public class JsonUserPrefsStorageTest { @@ -48,15 +48,15 @@ public void readUserPrefs_notJasonFormat_exceptionThrown() throws DataConversion thrown.expect(DataConversionException.class); readUserPrefs("NotJsonFormatUserPrefs.json"); - /* 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 + /* + * 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 */ } private String addToTestDataPathIfNotNull(String userPrefsFileInTestDataFolder) { - return userPrefsFileInTestDataFolder != null - ? TEST_DATA_FOLDER + userPrefsFileInTestDataFolder - : null; + return userPrefsFileInTestDataFolder != null ? TEST_DATA_FOLDER + userPrefsFileInTestDataFolder : null; } @Test @@ -107,12 +107,12 @@ public void saveUserPrefs_allInOrder_success() throws DataConversionException, I String pefsFilePath = testFolder.getRoot() + File.separator + "TempPrefs.json"; JsonUserPrefsStorage jsonUserPrefsStorage = new JsonUserPrefsStorage(pefsFilePath); - //Try writing when the file doesn't exist + // Try writing when the file doesn't exist jsonUserPrefsStorage.saveUserPrefs(original); UserPrefs readBack = jsonUserPrefsStorage.readUserPrefs().get(); assertEquals(original, readBack); - //Try saving when the file exists + // Try saving when the file exists original.setGuiSettings(5, 5, 5, 5); jsonUserPrefsStorage.saveUserPrefs(original); readBack = jsonUserPrefsStorage.readUserPrefs().get(); diff --git a/src/test/java/seedu/address/storage/StorageManagerTest.java b/src/test/java/seedu/flexitrack/storage/StorageManagerTest.java similarity index 55% rename from src/test/java/seedu/address/storage/StorageManagerTest.java rename to src/test/java/seedu/flexitrack/storage/StorageManagerTest.java index 6780feab6afc..a0e2bcd3e2a0 100644 --- a/src/test/java/seedu/address/storage/StorageManagerTest.java +++ b/src/test/java/seedu/flexitrack/storage/StorageManagerTest.java @@ -1,23 +1,23 @@ -package seedu.address.storage; +package seedu.flexitrack.storage; +import static junit.framework.TestCase.assertNotNull; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; -import seedu.address.commons.events.model.AddressBookChangedEvent; -import seedu.address.commons.events.storage.DataSavingExceptionEvent; -import seedu.address.model.AddressBook; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.UserPrefs; -import seedu.address.testutil.TypicalTestPersons; -import seedu.address.testutil.EventsCollector; -import java.io.IOException; - -import static junit.framework.TestCase.assertNotNull; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import seedu.flexitrack.commons.events.model.FlexiTrackChangedEvent; +import seedu.flexitrack.commons.events.storage.DataSavingExceptionEvent; +import seedu.flexitrack.model.FlexiTrack; +import seedu.flexitrack.model.ReadOnlyFlexiTrack; +import seedu.flexitrack.model.UserPrefs; +import seedu.flexitrack.testutil.EventsCollector; +import seedu.flexitrack.testutil.TypicalTestTasks; public class StorageManagerTest { @@ -26,22 +26,20 @@ public class StorageManagerTest { @Rule public TemporaryFolder testFolder = new TemporaryFolder(); - @Before public void setup() { storageManager = new StorageManager(getTempFilePath("ab"), getTempFilePath("prefs")); } - private String getTempFilePath(String fileName) { return testFolder.getRoot().getPath() + fileName; } - /* - * Note: This is an integration test that verifies the StorageManager is properly wired to the - * {@link JsonUserPrefsStorage} class. - * More extensive testing of UserPref saving/reading is done in {@link JsonUserPrefsStorageTest} class. + * Note: This is an integration test that verifies the StorageManager is + * properly wired to the {@link JsonUserPrefsStorage} class. More extensive + * testing of UserPref saving/reading is done in {@link + * JsonUserPrefsStorageTest} class. */ @Test @@ -55,42 +53,43 @@ 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 + FlexiTrack original = new TypicalTestTasks().getTypicalFlexiTrack(); + storageManager.saveFlexiTrack(original); + ReadOnlyFlexiTrack retrieved = storageManager.readFlexiTrack().get(); + assertEquals(original, new FlexiTrack(retrieved)); + // More extensive testing of AddressBook saving/reading is done in + // XmlAddressBookStorageTest } @Test - public void getAddressBookFilePath(){ - assertNotNull(storageManager.getAddressBookFilePath()); + public void getAddressBookFilePath() { + assertNotNull(storageManager.getFlexiTrackFilePath()); } @Test public void handleAddressBookChangedEvent_exceptionThrown_eventRaised() throws IOException { - //Create a StorageManager while injecting a stub that throws an exception when the save method is called - Storage storage = new StorageManager(new XmlAddressBookStorageExceptionThrowingStub("dummy"), new JsonUserPrefsStorage("dummy")); + // Create a StorageManager while injecting a stub that throws an + // exception when the save method is called + Storage storage = new StorageManager(new XmlAddressBookStorageExceptionThrowingStub("dummy"), + new JsonUserPrefsStorage("dummy")); EventsCollector eventCollector = new EventsCollector(); - storage.handleAddressBookChangedEvent(new AddressBookChangedEvent(new AddressBook())); + storage.handleFlexiTrackChangedEvent(new FlexiTrackChangedEvent(new FlexiTrack())); assertTrue(eventCollector.get(0) instanceof DataSavingExceptionEvent); } - /** * A Stub class to throw an exception when the save method is called */ - class XmlAddressBookStorageExceptionThrowingStub extends XmlAddressBookStorage{ + class XmlAddressBookStorageExceptionThrowingStub extends XmlFlexiTrackStorage { public XmlAddressBookStorageExceptionThrowingStub(String filePath) { super(filePath); } @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook, String filePath) throws IOException { + public void saveFlexiTrack(ReadOnlyFlexiTrack addressBook, String filePath) throws IOException { throw new IOException("dummy exception"); } } - } diff --git a/src/test/java/seedu/flexitrack/storage/XmlFlexiTrackTest.java b/src/test/java/seedu/flexitrack/storage/XmlFlexiTrackTest.java new file mode 100644 index 000000000000..aa372bfebbfc --- /dev/null +++ b/src/test/java/seedu/flexitrack/storage/XmlFlexiTrackTest.java @@ -0,0 +1,107 @@ +package seedu.flexitrack.storage; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +import java.io.IOException; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; + +import seedu.flexitrack.commons.exceptions.DataConversionException; +import seedu.flexitrack.commons.util.FileUtil; +import seedu.flexitrack.model.FlexiTrack; +import seedu.flexitrack.model.ReadOnlyFlexiTrack; +import seedu.flexitrack.model.task.Task; +import seedu.flexitrack.testutil.TypicalTestTasks; + +public class XmlFlexiTrackTest { + private static String TEST_DATA_FOLDER = FileUtil.getPath("./src/test/data/XmlFlexiTrackStorageTest/"); + + @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 XmlFlexiTrackStorage(filePath).readFlexiTrack(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("NotXmlFormatFlexiTrack.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() + "TempFlexiTrack.xml"; + TypicalTestTasks td = new TypicalTestTasks(); + FlexiTrack original = td.getTypicalFlexiTrack(); + XmlFlexiTrackStorage xmlAddressBookStorage = new XmlFlexiTrackStorage(filePath); + + // Save in new file and read back + xmlAddressBookStorage.saveFlexiTrack(original, filePath); + ReadOnlyFlexiTrack readBack = xmlAddressBookStorage.readFlexiTrack(filePath).get(); + assertEquals(original, new FlexiTrack(readBack)); + + // Modify data, overwrite exiting file, and read back + original.addTask(new Task(TypicalTestTasks.basketball)); + original.removeTask(new Task(TypicalTestTasks.homework1)); + xmlAddressBookStorage.saveFlexiTrack(original, filePath); + readBack = xmlAddressBookStorage.readFlexiTrack(filePath).get(); + assertEquals(original, new FlexiTrack(readBack)); + + // Save and read without specifying file path + original.addTask(new Task(TypicalTestTasks.lecture)); + xmlAddressBookStorage.saveFlexiTrack(original); // file path not + // specified + readBack = xmlAddressBookStorage.readFlexiTrack().get(); // file path + // not + // specified + assertEquals(original, new FlexiTrack(readBack)); + + } + + @Test + public void saveAddressBook_nullAddressBook_assertionFailure() throws IOException { + thrown.expect(AssertionError.class); + saveAddressBook(null, "SomeFile.xml"); + } + + private void saveAddressBook(ReadOnlyFlexiTrack addressBook, String filePath) throws IOException { + new XmlFlexiTrackStorage(filePath).saveFlexiTrack(addressBook, addToTestDataPathIfNotNull(filePath)); + } + + @Test + public void saveAddressBook_nullFilePath_assertionFailure() throws IOException { + thrown.expect(AssertionError.class); + saveAddressBook(new FlexiTrack(), null); + } + +} diff --git a/src/test/java/seedu/address/testutil/EventsCollector.java b/src/test/java/seedu/flexitrack/testutil/EventsCollector.java similarity index 66% rename from src/test/java/seedu/address/testutil/EventsCollector.java rename to src/test/java/seedu/flexitrack/testutil/EventsCollector.java index c44d6ca6f95a..14426deabe63 100644 --- a/src/test/java/seedu/address/testutil/EventsCollector.java +++ b/src/test/java/seedu/flexitrack/testutil/EventsCollector.java @@ -1,19 +1,20 @@ -package seedu.address.testutil; - -import com.google.common.eventbus.Subscribe; -import seedu.address.commons.core.EventsCenter; -import seedu.address.commons.events.BaseEvent; +package seedu.flexitrack.testutil; import java.util.ArrayList; import java.util.List; +import com.google.common.eventbus.Subscribe; + +import seedu.flexitrack.commons.core.EventsCenter; +import seedu.flexitrack.commons.events.BaseEvent; + /** * A class that collects events raised by other classes. */ -public class EventsCollector{ +public class EventsCollector { List events = new ArrayList(); - public EventsCollector(){ + public EventsCollector() { EventsCenter.getInstance().registerHandler(this); } @@ -21,21 +22,21 @@ public EventsCollector(){ * Collects any event raised by any class */ @Subscribe - public void collectEvent(BaseEvent event){ + public void collectEvent(BaseEvent event) { events.add(event); } /** * Removes collected events from the collected list */ - public void reset(){ + public void reset() { events.clear(); } /** * Returns the event at the specified index */ - public BaseEvent get(int index){ + public BaseEvent get(int index) { return events.get(index); } } diff --git a/src/test/java/seedu/flexitrack/testutil/FlexiTrackBuilder.java b/src/test/java/seedu/flexitrack/testutil/FlexiTrackBuilder.java new file mode 100644 index 000000000000..0a3d9d13cac3 --- /dev/null +++ b/src/test/java/seedu/flexitrack/testutil/FlexiTrackBuilder.java @@ -0,0 +1,28 @@ +package seedu.flexitrack.testutil; + +import seedu.flexitrack.model.FlexiTrack; +import seedu.flexitrack.model.task.Task; +import seedu.flexitrack.model.task.UniqueTaskList; + +/** + * A utility class to help with building FlexiTrack objects. Example usage:
+ * TODO: + * {@code FlexiTrack ab = new AddressBookBuilder().withPerson("John", "Doe").withTag("Friend").build();} + */ +public class FlexiTrackBuilder { + + private FlexiTrack flexiTrack; + + public FlexiTrackBuilder(FlexiTrack flexiTrack) { + this.flexiTrack = flexiTrack; + } + + public FlexiTrackBuilder withPerson(Task person) throws UniqueTaskList.DuplicateTaskException { + flexiTrack.addTask(person); + return this; + } + + public FlexiTrack build() { + return flexiTrack; + } +} diff --git a/src/test/java/seedu/address/testutil/SerializableTestClass.java b/src/test/java/seedu/flexitrack/testutil/SerializableTestClass.java similarity index 76% rename from src/test/java/seedu/address/testutil/SerializableTestClass.java rename to src/test/java/seedu/flexitrack/testutil/SerializableTestClass.java index ef58ef857179..cd20d3b54d76 100644 --- a/src/test/java/seedu/address/testutil/SerializableTestClass.java +++ b/src/test/java/seedu/flexitrack/testutil/SerializableTestClass.java @@ -1,4 +1,4 @@ -package seedu.address.testutil; +package seedu.flexitrack.testutil; import java.time.LocalDateTime; import java.util.ArrayList; @@ -9,16 +9,11 @@ * A class used to test serialization and deserialization */ public class SerializableTestClass { - public static final String JSON_STRING_REPRESENTATION = String.format("{%n" + - " \"name\" : \"This is a test class\",%n" + - " \"listOfLocalDateTimes\" : " + - "[ \"-999999999-01-01T00:00:00\", \"+999999999-12-31T23:59:59.999999999\", \"0001-01-01T01:01:00\" ],%n" + - " \"mapOfIntegerToString\" : {%n" + - " \"1\" : \"One\",%n" + - " \"2\" : \"Two\",%n" + - " \"3\" : \"Three\"%n" + - " }%n" + - "}"); + public static final String JSON_STRING_REPRESENTATION = String + .format("{%n" + " \"name\" : \"This is a test class\",%n" + " \"listOfLocalDateTimes\" : " + + "[ \"-999999999-01-01T00:00:00\", \"+999999999-12-31T23:59:59.999999999\", \"0001-01-01T01:01:00\" ],%n" + + " \"mapOfIntegerToString\" : {%n" + " \"1\" : \"One\",%n" + " \"2\" : \"Two\",%n" + + " \"3\" : \"Three\"%n" + " }%n" + "}"); private static final String NAME_TEST_VALUE = "This is a test class"; @@ -27,7 +22,8 @@ public class SerializableTestClass { private List listOfLocalDateTimes; private HashMap mapOfIntegerToString; - public SerializableTestClass() {} + public SerializableTestClass() { + } public static String getNameTestValue() { return NAME_TEST_VALUE; diff --git a/src/test/java/seedu/flexitrack/testutil/TaskBuilder.java b/src/test/java/seedu/flexitrack/testutil/TaskBuilder.java new file mode 100644 index 000000000000..6e728788c1b7 --- /dev/null +++ b/src/test/java/seedu/flexitrack/testutil/TaskBuilder.java @@ -0,0 +1,63 @@ +package seedu.flexitrack.testutil; + +import seedu.flexitrack.commons.exceptions.IllegalValueException; +import seedu.flexitrack.model.task.DateTimeInfo; +import seedu.flexitrack.model.task.Name; + +//TODO: change the whole class +/** + * + */ +public class TaskBuilder { + + public static final String MESSAGE_DATETIMEINFO_CONSTRAINTS = "Invalid time inputed. Please check your spelling!"; + + private TestTask task; + private Name name; + private DateTimeInfo dueDate; + private DateTimeInfo startTime; + private DateTimeInfo endTime; + + public TaskBuilder() { + this.task = new TestTask(); + } + + public TaskBuilder withName(String name) throws IllegalValueException { + // this.task.setName(new Name(name)); + this.name = new Name(name); + return this; + } + + public TaskBuilder withDueDate(String dueDate) throws IllegalValueException { + // this.task.setDueDate(new DateTimeInfo(dueDate)); + this.dueDate = new DateTimeInfo(dueDate); + return this; + } + + public TaskBuilder withStartTime(String startTime) throws IllegalValueException { + // this.task.setStartTime(new DateTimeInfo(startTime)); + this.startTime = new DateTimeInfo(startTime); + return this; + } + + public TaskBuilder withEndTime(String endTime) throws IllegalValueException { + // this.task.setEndTime(new DateTimeInfo(endTime)); + this.endTime = new DateTimeInfo(endTime); + return this; + } + + public TestTask build() { + return this.task = new TestTask(this.name, this.dueDate, this.startTime, this.endTime); + } + + private DateTimeInfo getNullTime() { + try { + return new DateTimeInfo("Feb 29 2000 00:00"); + } catch (IllegalValueException e) { + new IllegalValueException (MESSAGE_DATETIMEINFO_CONSTRAINTS); + } + return null; + + } + +} diff --git a/src/test/java/seedu/flexitrack/testutil/TestTask.java b/src/test/java/seedu/flexitrack/testutil/TestTask.java new file mode 100644 index 000000000000..02fea2ac585e --- /dev/null +++ b/src/test/java/seedu/flexitrack/testutil/TestTask.java @@ -0,0 +1,170 @@ +package seedu.flexitrack.testutil; + +import java.util.Objects; + +import seedu.flexitrack.commons.util.CollectionUtil; +import seedu.flexitrack.model.task.DateTimeInfo; +import seedu.flexitrack.model.task.Name; +import seedu.flexitrack.model.task.ReadOnlyTask; + +/** + * A mutable task object. For testing only. + */ +public class TestTask implements ReadOnlyTask { + + private Name name; + private DateTimeInfo dueDate; + private DateTimeInfo startTime; + private DateTimeInfo endTime; + private boolean isEvent; + private boolean isTask; + private boolean isDone = false; + private boolean isBlock = false; + + /** + * Every field must be present and not null. + */ + public TestTask(Name name, DateTimeInfo dueDate, DateTimeInfo startTime, DateTimeInfo endTime) { + assert !CollectionUtil.isAnyNull(name); + this.name = name; + this.dueDate = dueDate; + this.startTime = startTime; + this.endTime = endTime; + this.isTask = dueDate.isDateNull() ? false : true; + this.isEvent = startTime.isDateNull() ? false : true; + this.isDone = name.getIsDone(); + this.isBlock = name.getIsBlock(); + } + + public TestTask() { + + } + + /** + * Copy constructor. + */ + public TestTask(ReadOnlyTask source) { + this(source.getName(), source.getDueDate(), source.getStartTime(), source.getEndTime()); + } + + public void setName(Name name) { + this.name = name; + } + + public void setDueDate(DateTimeInfo date) { + this.dueDate = date; + } + + public void setStartTime(DateTimeInfo date) { + this.startTime = date; + } + + public void setEndTime(DateTimeInfo date) { + this.endTime = date; + } + + @Override + public Name getName() { + return name; + } + + @Override + public boolean getIsTask() { + return isTask; + } + + @Override + public boolean getIsEvent() { + return isEvent; + } + + @Override + public boolean getIsDone() { + return isDone; + } + + @Override + public boolean getIsBlock() { + return isBlock; + } + + public void setIsDone(boolean isDone) { + this.isDone = isDone; + } + + public void setIsBlock(boolean isBlock) { + name.setBlock(); + this.isBlock = isBlock; + } + + @Override + public DateTimeInfo getDueDate() { + return dueDate; + } + + @Override + public DateTimeInfo getStartTime() { + return startTime; + } + + @Override + public DateTimeInfo getEndTime() { + return endTime; + } + + @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, dueDate, startTime, endTime, isTask, isEvent); + } + + @Override + public String toString() { + return getAsText(); + } + + //@@author A0138455Y + public String getBlockCommand() { + StringBuilder sb = new StringBuilder(); + sb.append("block " + this.getName().toString() + " "); + sb.append("from/" + this.getStartTime().toString() + " "); + sb.append("to/" + this.getEndTime().toString() + " "); + return sb.toString(); + } + //@@author + + //@@author A0127686R + public String getAddCommand() { + StringBuilder sb = new StringBuilder(); + sb.append("add " + this.getName().toString() + " "); + if (getIsTask()) { + sb.append("by/" + this.getDueDate().toString() + " "); + } else if (getIsEvent()) { + sb.append("from/" + this.getStartTime().toString() + " "); + sb.append("to/" + this.getEndTime().toString() + " "); + } + return sb.toString(); + } + + public static String getMarkCommand(int mark) { + StringBuilder sb = new StringBuilder(); + sb.append("mark " + mark); + return sb.toString(); + } + + //@@author + public static String getUnMarkCommand(int taskToUnMark) { + StringBuilder sb = new StringBuilder(); + sb.append("unmark " + taskToUnMark); + return sb.toString(); + } + +} diff --git a/src/test/java/seedu/flexitrack/testutil/TestUtil.java b/src/test/java/seedu/flexitrack/testutil/TestUtil.java new file mode 100644 index 000000000000..5860a94a00e6 --- /dev/null +++ b/src/test/java/seedu/flexitrack/testutil/TestUtil.java @@ -0,0 +1,459 @@ +package seedu.flexitrack.testutil; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeoutException; +import java.util.stream.Collectors; + +import org.loadui.testfx.GuiTest; +import org.testfx.api.FxToolkit; + +import com.google.common.io.Files; + +import edu.emory.mathcs.backport.java.util.Collections; +import guitests.guihandles.TaskCardHandle; +import javafx.geometry.Bounds; +import javafx.geometry.Point2D; +import javafx.scene.Node; +import javafx.scene.Scene; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyCodeCombination; +import javafx.scene.input.KeyCombination; +import junit.framework.AssertionFailedError; +import seedu.flexitrack.TestApp; +import seedu.flexitrack.commons.exceptions.IllegalValueException; +import seedu.flexitrack.commons.util.FileUtil; +import seedu.flexitrack.commons.util.XmlUtil; +import seedu.flexitrack.model.FlexiTrack; +import seedu.flexitrack.model.task.DateTimeInfo; +import seedu.flexitrack.model.task.Name; +import seedu.flexitrack.model.task.ReadOnlyTask; +import seedu.flexitrack.model.task.Task; +import seedu.flexitrack.model.task.UniqueTaskList; +import seedu.flexitrack.storage.XmlSerializableFlexiTrack; + +/** + * A utility class for test cases. + */ +public class TestUtil { + + public static String LS = System.lineSeparator(); + + public static void assertThrows(Class expected, Runnable executable) { + try { + executable.run(); + } catch (Throwable actualException) { + if (!actualException.getClass().isAssignableFrom(expected)) { + String message = String.format("Expected thrown: %s, actual: %s", expected.getName(), + actualException.getClass().getName()); + throw new AssertionFailedError(message); + } else + return; + } + throw new AssertionFailedError( + 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 Task[] sampleTaskData = getSampleTaskData(); + + private static Task[] getSampleTaskData() { + try { + Task[] sampleTasks = new Task[] { + new Task(new Name("Go shopping"), new DateTimeInfo("29 Feb 23.23"), new DateTimeInfo("13 Jan 4pm"), + new DateTimeInfo("13 Jan 6pm")), + new Task(new Name("Buy books"), new DateTimeInfo("11 May"), new DateTimeInfo("29 Feb 23.23"), + new DateTimeInfo("29 Feb 23.23")), + new Task(new Name("Go for jogging"), new DateTimeInfo("29 Feb 23.23"), + new DateTimeInfo("8 Apr 5.30"), new DateTimeInfo("8 Apr 6.30")), + new Task(new Name("Dinner with friends"), new DateTimeInfo("29 Feb 23.23"), + new DateTimeInfo("10 Dec 8.30am"), new DateTimeInfo("10 Dec 11am")), + new Task(new Name("CS2103 homework"), new DateTimeInfo("12 Jun 7.30"), + new DateTimeInfo("29 Feb 23.23"), new DateTimeInfo("29 Feb 23.23")), + new Task(new Name("Cs2103 exam"), new DateTimeInfo("29 Feb 23.23"), new DateTimeInfo("7 Jun 3pm"), + new DateTimeInfo("7 Jun 5pm")), + new Task(new Name("Ma1505 midterm"), new DateTimeInfo("29 Feb 23.23"), + new DateTimeInfo("9 Aug 7pm"), new DateTimeInfo("9 Aug 11pm")), + new Task(new Name("Cycling"), new DateTimeInfo("29 Feb 23.23"), new DateTimeInfo("14 Nov 10.30"), + new DateTimeInfo("14 Nov 12.30")), + new Task(new Name("Movie time"), new DateTimeInfo("29 Feb 23.23"), new DateTimeInfo("today 2pm"), + new DateTimeInfo("today 6pm")) }; + Arrays.sort(sampleTasks); + return sampleTasks; + } catch (IllegalValueException e) { + assert false; + // not possible + return null; + } + } + + public static List generateSampleTaskData() { + return Arrays.asList(sampleTaskData); + } + + /** + * Appends the file name to the sandbox folder path. Creates the sandbox + * folder if it doesn't exist. + * + * @param fileName + * @return + */ + public static String getFilePathInSandboxFolder(String fileName) { + try { + FileUtil.createDirs(new File(SANDBOX_FOLDER)); + } catch (IOException e) { + throw new RuntimeException(e); + } + return SANDBOX_FOLDER + fileName; + } + + public static void createDataFileWithSampleData(String filePath) { + createDataFileWithData(generateSampleStorageFlexiTrack(), filePath); + } + + public static void createDataFileWithData(T data, String filePath) { + try { + File saveFileForTesting = new File(filePath); + FileUtil.createIfMissing(saveFileForTesting); + XmlUtil.saveDataToFile(saveFileForTesting, data); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static void main(String... s) { + createDataFileWithSampleData(TestApp.SAVE_LOCATION_FOR_TESTING); + } + + public static FlexiTrack generateEmptyFlexiTrack() { + return new FlexiTrack(new UniqueTaskList()); + } + + public static XmlSerializableFlexiTrack generateSampleStorageFlexiTrack() { + return new XmlSerializableFlexiTrack(generateEmptyFlexiTrack()); + } + + /** + * Tweaks the {@code keyCodeCombination} to resolve the + * {@code KeyCode.SHORTCUT} to their respective platform-specific keycodes + */ + public static KeyCode[] scrub(KeyCodeCombination keyCodeCombination) { + List keys = new ArrayList<>(); + if (keyCodeCombination.getAlt() == KeyCombination.ModifierValue.DOWN) { + keys.add(KeyCode.ALT); + } + if (keyCodeCombination.getShift() == KeyCombination.ModifierValue.DOWN) { + keys.add(KeyCode.SHIFT); + } + if (keyCodeCombination.getMeta() == KeyCombination.ModifierValue.DOWN) { + keys.add(KeyCode.META); + } + if (keyCodeCombination.getControl() == KeyCombination.ModifierValue.DOWN) { + keys.add(KeyCode.CONTROL); + } + keys.add(keyCodeCombination.getCode()); + return keys.toArray(new KeyCode[] {}); + } + + public static boolean isHeadlessEnvironment() { + String headlessProperty = System.getProperty("testfx.headless"); + return headlessProperty != null && headlessProperty.equals("true"); + } + + public static void captureScreenShot(String fileName) { + File file = GuiTest.captureScreenshot(); + try { + Files.copy(file, new File(fileName + ".png")); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public static String descOnFail(Object... comparedObjects) { + return "Comparison failed \n" + + Arrays.asList(comparedObjects).stream().map(Object::toString).collect(Collectors.joining("\n")); + } + + public static void setFinalStatic(Field field, Object newValue) + throws NoSuchFieldException, IllegalAccessException { + field.setAccessible(true); + // remove final modifier from field + Field modifiersField = Field.class.getDeclaredField("modifiers"); + modifiersField.setAccessible(true); + // ~Modifier.FINAL is used to remove the final modifier from field so + // that its value is no longer + // final and can be changed + modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); + field.set(null, newValue); + } + + public static void initRuntime() throws TimeoutException { + FxToolkit.registerPrimaryStage(); + FxToolkit.hideStage(); + } + + public static void tearDownRuntime() throws Exception { + FxToolkit.cleanupStages(); + } + + /** + * Gets private method of a class Invoke the method using + * method.invoke(objectInstance, params...) + * + * Caveat: only find method declared in the current Class, not inherited + * from supertypes + */ + public static Method getPrivateMethod(Class objectClass, String methodName) throws NoSuchMethodException { + Method method = objectClass.getDeclaredMethod(methodName); + method.setAccessible(true); + return method; + } + + public static void renameFile(File file, String newFileName) { + try { + Files.copy(file, new File(newFileName)); + } catch (IOException e1) { + e1.printStackTrace(); + } + } + + /** + * Gets mid point of a node relative to the screen. + * + * @param node + * @return + */ + public static Point2D getScreenMidPoint(Node node) { + double x = getScreenPos(node).getMinX() + node.getLayoutBounds().getWidth() / 2; + double y = getScreenPos(node).getMinY() + node.getLayoutBounds().getHeight() / 2; + return new Point2D(x, y); + } + + /** + * Gets mid point of a node relative to its scene. + * + * @param node + * @return + */ + public static Point2D getSceneMidPoint(Node node) { + double x = getScenePos(node).getMinX() + node.getLayoutBounds().getWidth() / 2; + double y = getScenePos(node).getMinY() + node.getLayoutBounds().getHeight() / 2; + return new Point2D(x, y); + } + + /** + * Gets the bound of the node relative to the parent scene. + * + * @param node + * @return + */ + public static Bounds getScenePos(Node node) { + return node.localToScene(node.getBoundsInLocal()); + } + + public static Bounds getScreenPos(Node node) { + return node.localToScreen(node.getBoundsInLocal()); + } + + public static double getSceneMaxX(Scene scene) { + return scene.getX() + scene.getWidth(); + } + + public static double getSceneMaxY(Scene scene) { + return scene.getX() + scene.getHeight(); + } + + public static Object getLastElement(List list) { + return list.get(list.size() - 1); + } + + /** + * Removes a subset from the list of tasks. + * + * @param tasks + * The list of tasks + * @param tasksToRemove + * The subset of tasks. + * @return The modified tasks after removal of the subset from tasks. + */ + public static TestTask[] removeTasksFromList(final TestTask[] tasks, TestTask... tasksToRemove) { + List listOfTasks = asList(tasks); + listOfTasks.removeAll(asList(tasksToRemove)); + Collections.sort(listOfTasks); + return listOfTasks.toArray(new TestTask[listOfTasks.size()]); + } + + /** + * Returns a copy of the list with the task 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 TestTask[] removeTaskFromList(final TestTask[] list, int targetIndexInOneIndexedFormat) { + return removeTasksFromList(list, list[targetIndexInOneIndexedFormat - 1]); + } + + /** + * 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 TestTask[] replaceTaskFromList(TestTask[] tasks, TestTask task, int index) { + tasks[index] = task; + return tasks; + } + + /** + * Appends tasks to the array of tasks. + * + * @param tasks + * A array of tasks. + * @param tasksToAdd + * The tasks that are to be appended behind the original array. + * @return The modified array of tasks. + */ + public static TestTask[] addTasksToList(final TestTask[] tasks, TestTask... tasksToAdd) { + List listOfTasks = asList(tasks); + listOfTasks.addAll(asList(tasksToAdd)); + Collections.sort(listOfTasks); + return listOfTasks.toArray(new TestTask[listOfTasks.size()]); + } + + public static TestTask[] editTasksToList(final TestTask[] tasks, int index, TestTask EditedTask) { + List listOfTasks = asList(tasks); + listOfTasks.set(index, EditedTask); + Collections.sort(listOfTasks); + return listOfTasks.toArray(new TestTask[listOfTasks.size()]); + } + + private static List asList(T[] objs) { + List list = new ArrayList<>(); + for (T obj : objs) { + list.add(obj); + } + return list; + } + + public static boolean compareCardAndTask(TaskCardHandle card, ReadOnlyTask task) { + return card.isSameTask(task); + } + + + /** + * Mark a task as done. + * + * @param tasks + * A array of tasks. + * @param taskToMark + * The tasks that are to be appended behind the original array. + * @return The modified array of tasks. + */ + public static TestTask[] markTasksToList(final TestTask[] tasks, int taskToMark) { + taskToMark = taskToMark - 1; + List listOfTasks = asList(tasks); + if (taskToMark > listOfTasks.size()) { + throw new IllegalArgumentException("The task index provided is invalid"); + } + if (!listOfTasks.get(taskToMark).getIsDone()) { + try { + listOfTasks.get(taskToMark).setName(new Name("(Done)" + listOfTasks.get(taskToMark).getName())); + listOfTasks.get(taskToMark).setIsDone(true); + } catch (IllegalValueException e) { + throw new IllegalArgumentException("The task index provided is invalid"); + } + } + + Collections.sort(listOfTasks); + return listOfTasks.toArray(new TestTask[listOfTasks.size()]); + } + + /** + * Mark a task as undone. + * + * @param tasks + * A array of tasks. + * @param taskToMark + * The tasks that are to be appended behind the original array. + * @return The modified array of tasks. + */ + public static TestTask[] unMarkTasksToList(TestTask[] currentList, int taskToUnMark) { + taskToUnMark = taskToUnMark - 1; + List listOfTasks = asList(currentList); + if (taskToUnMark > listOfTasks.size()) { + throw new IllegalArgumentException("The task index provided is invalid"); + } + if (!listOfTasks.get(taskToUnMark).getIsDone()) { + try { + listOfTasks.get(taskToUnMark) + .setName(new Name(listOfTasks.get(taskToUnMark).getName().toString().replace("(Done)", ""))); + listOfTasks.get(taskToUnMark).setIsDone(false); + } catch (IllegalValueException e) { + throw new IllegalArgumentException("The task index provided is invalid"); + } + } + Collections.sort(listOfTasks); + return listOfTasks.toArray(new TestTask[listOfTasks.size()]); + } + + public static TestTask[] listTasksAccordingToCommand(TestTask[] currentList, String listCommand) { + TypicalTestTasks tt = new TypicalTestTasks(); + TestTask[] testTaskArray; + switch (listCommand) { + case "list": + testTaskArray = tt.getTypicalSortedTasks(); + break; + case "list future": + testTaskArray = tt.getExpectedTypicalFutureTasks(); + break; + case "list past": + testTaskArray = tt.getExpectedTypicalPastTasks(); + break; + case "list mark": + testTaskArray = tt.getExpectedTypicalMarkTasks(); + break; + case "list unmark": + testTaskArray = tt.getExpectedTypicalUnMarkTasks(); + break; + case "list future mark": + testTaskArray = tt.getExpectedTypicalFutureMarkTasks(); + break; + case "list next week": + testTaskArray = tt.getExpectedTypicalNextWeekTasks(); + break; + case "list last week": + testTaskArray = tt.getExpectedTypicalLastWeekTasks(); + break; + case "list next month": + testTaskArray = tt.getExpectedTypicalNextMonthTasks(); + break; + case "list last month": + testTaskArray = tt.getExpectedTypicalLastMonthTasks(); + break; + default: + return null; + } + + Arrays.sort(testTaskArray); + return testTaskArray; + } + +} diff --git a/src/test/java/seedu/flexitrack/testutil/TypicalTestTasks.java b/src/test/java/seedu/flexitrack/testutil/TypicalTestTasks.java new file mode 100644 index 000000000000..ddf28cd77c2a --- /dev/null +++ b/src/test/java/seedu/flexitrack/testutil/TypicalTestTasks.java @@ -0,0 +1,210 @@ +package seedu.flexitrack.testutil; + +import seedu.flexitrack.commons.exceptions.IllegalValueException; +import seedu.flexitrack.model.FlexiTrack; +import seedu.flexitrack.model.task.Name; +import seedu.flexitrack.model.task.Task; +import seedu.flexitrack.model.task.UniqueTaskList; + +/** + * @param + * + */ +public class TypicalTestTasks { + + public static TestTask homework1, homework2, homework3, event, soccer, dinner, exam, midterm, basketball, lecture,job, + homework1EditName, homework1EditDueDate, homework3EditName, homework3EditToTask, soccerEditName, + soccerEditStartTime, soccerEditEndTime, eventEditToEvent, tutorial1,tutorial2,tutorial3, + lecture1, lecture2, lecture3, lecture4, lecture5, exam1, exam2, exam3, study1, study2, past1, past2; + + public TypicalTestTasks() { + try { + // @@author A0127686R + homework1 = new TaskBuilder().withName("Homework cs 2103").withDueDate("Jan 11 2017 17:00") + .withEndTime("Feb 29 2000 00:00").withStartTime("Feb 29 2000 00:00").build(); + homework2 = new TaskBuilder().withName("Homework cs 2101").withDueDate("Sep 01 2016 13:00") + .withEndTime("Feb 29 2000 00:00").withStartTime("Feb 29 2000 00:00").build(); + homework3 = new TaskBuilder().withName("Homework ma 1505").withStartTime("Feb 29 2000 00:00") + .withEndTime("Feb 29 2000 00:00").withDueDate("Feb 29 2000 00:00").build(); + soccer = new TaskBuilder().withName("Soccer training").withStartTime("Jun 30 2016 21:00") + .withEndTime("Jun 30 2016 23:00").withDueDate("Feb 29 2000 00:00").build(); + dinner = new TaskBuilder().withName("Dinner with parents").withStartTime("Nov 16 2017 19:00") + .withEndTime("Nov 16 2017 20:15").withDueDate("Feb 29 2000 00:00").build(); + exam = new TaskBuilder().withName("MA 1505 Exams").withStartTime("May 12 2016 10:00") + .withEndTime("May 12 2016 11:30").withDueDate("Feb 29 2000 00:00").build(); + midterm = new TaskBuilder().withName("Midter cs 2101").withStartTime("Feb 29 2000 00:00") + .withEndTime("Feb 29 2000 00:00").withDueDate("Mar 23 2017 09:00").build(); + event = new TaskBuilder().withName("Event lol").withStartTime("Feb 29 2000 00:00") + .withEndTime("Feb 29 2000 00:00").withDueDate("Feb 29 2000 00:00").build(); + tutorial2 = new TaskBuilder().withName("(Blocked) for CS 2103 tutorial 2").withStartTime("Nov 21 2017 19:00") + .withEndTime("Nov 21 2017 20:15").withDueDate("Feb 29 2000 00:00").build(); + // Manually added + basketball = new TaskBuilder().withName("Basketball training").withStartTime("Dec 25 2016 18:00") + .withEndTime("Dec 25 2016 21:00").withDueDate("Feb 29 2000 00:00").build(); + lecture = new TaskBuilder().withName("Lecture CS2103").withStartTime("Oct 10 2016 14:00") + .withEndTime("Oct 10 2016 16:00").withDueDate("Feb 29 2000 00:00").build(); + job = new TaskBuilder().withName("Apply Job in Starbucks").withDueDate("Feb 29 2000 00:00") + .withEndTime("Feb 29 2000 00:00").withStartTime("Feb 29 2000 00:00").build(); + tutorial1 = new TaskBuilder().withName("CS2103 tutorial 1").withStartTime("Feb 29 2000 00:00") + .withEndTime("Feb 29 2000 00:00").withDueDate("11 Nov 23:59").build(); + tutorial3 = new TaskBuilder().withName("(Blocked) for CS 2103 tutorial 3").withStartTime("Nov 21 2017 19:00") + .withEndTime("Nov 21 2017 20:15").withDueDate("Feb 29 2000 00:00").build(); + + // @@author A0127855W + // After edit + homework1EditName = new TaskBuilder().withName("Name Edited").withDueDate("Jan 11 2017 17:00") + .withEndTime("Feb 29 2000 00:00").withStartTime("Feb 29 2000 00:00").build(); + homework1EditDueDate = new TaskBuilder().withName("Homework cs 2103").withDueDate("Jan 14 2016 10:00") + .withEndTime("Feb 29 2000 00:00").withStartTime("Feb 29 2000 00:00").build(); + + soccerEditName = new TaskBuilder().withName("Name Edited 2").withStartTime("Jun 30 2016 21:00") + .withEndTime("Jun 30 2016 23:00").withDueDate("Feb 29 2000 00:00").build(); + soccerEditStartTime = new TaskBuilder().withName("Soccer training").withStartTime("Jun 10 2016 21:00") + .withEndTime("Jun 30 2016 23:00").withDueDate("Feb 29 2000 00:00").build(); + soccerEditEndTime = new TaskBuilder().withName("Soccer training").withStartTime("Jun 30 2016 21:00") + .withEndTime("Jun 30 2020 6:00").withDueDate("Feb 29 2000 00:00").build(); + + homework3EditName = new TaskBuilder().withName("Name Edited 3").withStartTime("Feb 29 2000 00:00") + .withEndTime("Feb 29 2000 00:00").withDueDate("Feb 29 2000 00:00").build(); + homework3EditToTask = new TaskBuilder().withName("Homework ma 1505").withStartTime("Feb 29 2000 00:00") + .withEndTime("Feb 29 2000 00:00").withDueDate("Jun 10 2016 21:00").build(); + + eventEditToEvent = new TaskBuilder().withName("Event lol").withStartTime("Jun 10 2016 21:00") + .withEndTime("Jun 30 2016 23:00").withDueDate("Feb 29 2000 00:00").build(); + + // @@author A0127686R + // Additional Event Task + lecture1 = new TaskBuilder().withName("lecture 1").withStartTime("Nov 08 2017 09:00") + .withEndTime("Nov 08 2017 11:00").withDueDate("Feb 29 2000 00:00").build(); + lecture2 = new TaskBuilder().withName("lecture 2").withStartTime("Nov 08 2017 15:00") + .withEndTime("Nov 08 2017 16:00").withDueDate("Feb 29 2000 00:00").build(); + lecture3 = new TaskBuilder().withName("lecture 3").withStartTime("Nov 09 2017 14:00") + .withEndTime("Nov 09 2017 16:00").withDueDate("Feb 29 2000 00:00").build(); + lecture4 = new TaskBuilder().withName("lecture 4").withStartTime("Nov 12 2017 10:00") + .withEndTime("Nov 12 2017 12:00").withDueDate("Feb 29 2000 00:00").build(); + lecture5 = new TaskBuilder().withName("lecture 5").withStartTime("Nov 12 2017 13:00") + .withEndTime("Nov 12 2017 14:00").withDueDate("Feb 29 2000 00:00").build(); + exam1 = new TaskBuilder().withName("exam 1").withStartTime("Nov 20 2017 09:00") + .withEndTime("Nov 20 2017 10:30").withDueDate("Feb 29 2000 00:00").build(); + exam2 = new TaskBuilder().withName("exam 2").withStartTime("Nov 20 2017 12:00") + .withEndTime("Nov 20 2017 14:00").withDueDate("Feb 29 2000 00:00").build(); + exam3 = new TaskBuilder().withName("exam 3").withStartTime("Nov 22 2017 19:00") + .withEndTime("Nov 22 2017 21:00").withDueDate("Feb 29 2000 00:00").build(); + study1 = new TaskBuilder().withName("study 1").withStartTime("Nov 18 2017 09:00") + .withEndTime("Nov 18 2017 23:00").withDueDate("Feb 29 2000 00:00").build(); + study2 = new TaskBuilder().withName("study 2").withStartTime("Nov 19 2017 06:00") + .withEndTime("Nov 19 2017 10:00").withDueDate("Feb 29 2000 00:00").build(); + past1 = new TaskBuilder().withName("past 1").withStartTime("Nov 01 2017 09:00") + .withEndTime("Nov 01 2017 11:00").withDueDate("Feb 29 2000 00:00").build(); + past2 = new TaskBuilder().withName("past 2").withStartTime("Oct 20 2017 15:00") + .withEndTime("Oct 20 2017 16:00").withDueDate("Feb 29 2000 00:00").build(); + + } catch (IllegalValueException e) { + e.printStackTrace(); + assert false : "not possible"; + } + } + + // @@author + public static void loadFlexiTrackWithSampleData(FlexiTrack ab) { + // TODO: change the add Task cases + try { + ab.addTask(new Task(homework1)); + ab.addTask(new Task(homework2)); + ab.addTask(new Task(homework3)); + ab.addTask(new Task(soccer)); + ab.addTask(new Task(dinner)); + ab.addTask(new Task(exam)); + ab.addTask(new Task(midterm)); + ab.addTask(new Task(event)); + ab.sort(); + + } catch (UniqueTaskList.DuplicateTaskException e) { + assert false : "not possible"; + } + } + + // @@author A0127686R + public TestTask[] getTypicalSortedTasks() { + TestTask[] testTask = new TestTask[] { event, homework3, exam, soccer, homework2, homework1, midterm, dinner}; + return testTask; + } + + public TestTask[] getTypicalUnsortedTasks() { + TestTask[] testTask = new TestTask[] { homework1, homework2, homework3, soccer, dinner, exam, midterm, event }; + return testTask; + } + + public TestTask[] getExpectedTypicalFutureTasks() { + return new TestTask[] { homework1, homework3, dinner, midterm, event }; + } + + public TestTask[] getExpectedTypicalPastTasks() { + return new TestTask[] { homework2, homework3, soccer, exam, event }; + } + + public TestTask[] getTypicalEventTasks() { + TestTask[] testTask = new TestTask[] { lecture1, lecture2, lecture3, lecture4, lecture5, exam1, exam2, exam3, study1, study2}; + return testTask; + } + + public TestTask[] getExpectedTypicalMarkTasks() { + try { + homework1.setName(new Name(homework1.getName().toString())); + soccer.setName(new Name(soccer.getName().toString())); + exam.setName(new Name(exam.getName().toString())); + event.setName(new Name(event.getName().toString())); + } catch (IllegalValueException e) { + assert false; + } + homework1.getName().setAsMark(); + soccer.getName().setAsMark(); + exam.getName().setAsMark(); + event.getName().setAsMark(); + return new TestTask[] { homework1, soccer, exam, event }; + } + + public TestTask[] getExpectedTypicalUnMarkTasks() { + return new TestTask[] { homework2, homework3, dinner, midterm }; + } + + public TestTask[] getExpectedTypicalFutureMarkTasks() { + try { + homework1.setName(new Name (homework1.getName().toString())); + soccer.setName(new Name (soccer.getName().toString())); + exam.setName(new Name (exam.getName().toString())); + event.setName(new Name (event.getName().toString())); + } catch (IllegalValueException e) { + assert false; + } + homework1.getName().setAsMark(); + soccer.getName().setAsMark(); + exam.getName().setAsMark(); + event.getName().setAsMark(); + return new TestTask[] { homework1 }; + } + + // @@author + public FlexiTrack getTypicalFlexiTrack() { + FlexiTrack ab = new FlexiTrack(); + loadFlexiTrackWithSampleData(ab); + return ab; + } + + public TestTask[] getExpectedTypicalNextWeekTasks() { + return new TestTask[] { lecture1 }; + } + + public TestTask[] getExpectedTypicalLastWeekTasks() { + return new TestTask[] { past1 }; + } + + public TestTask[] getExpectedTypicalNextMonthTasks() { + return new TestTask[] { lecture1, exam1 }; + } + + public TestTask[] getExpectedTypicalLastMonthTasks() { + return new TestTask[] { past2, past1 }; + } + +}