diff --git a/.classpath b/.classpath index 3f05f311a90b..83c7ddd991c7 100644 --- a/.classpath +++ b/.classpath @@ -15,7 +15,11 @@ - + + + + + diff --git a/.gitignore b/.gitignore index 45a20de82e87..65747dbc92c1 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ preferences.json classes/ /data/ /bin/ +src/test/data/SaveLocationCommandTest/newStorageLocation/taskmanager.xml diff --git a/.project b/.project index 1c9339c5f927..1bfe42bf200f 100644 --- a/.project +++ b/.project @@ -1,7 +1,7 @@ - addressbook-level4 - Project addressbook-level4 created by Buildship. + Taskell + Project Taskell created by Team Autumn. diff --git a/.settings/org.eclipse.buildship.core.prefs b/.settings/org.eclipse.buildship.core.prefs index 4e0fc71ac89f..c382d0aa6c33 100644 --- a/.settings/org.eclipse.buildship.core.prefs +++ b/.settings/org.eclipse.buildship.core.prefs @@ -1,8 +1,7 @@ build.commands=org.eclipse.jdt.core.javabuilder connection.arguments= connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER) -connection.gradle.user.home=null -connection.java.home=null +connection.java.home=C\:\\Program Files\\Java\\jdk1.8.0_101 connection.jvm.arguments= connection.project.dir= derived.resources=.gradle,build diff --git a/README.md b/README.md index 249a00b3899c..393ef35eb718 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,17 @@ -[![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-W15-C3/main.svg?branch=master)](https://travis-ci.org/CS2103AUG2016-W15-C3/main) +[![Coverage Status](https://coveralls.io/repos/github/CS2103AUG2016-W15-C3/main/badge.svg?branch=master)](https://coveralls.io/github/CS2103AUG2016-W15-C3/main?branch=master) +[![Codacy Badge](https://api.codacy.com/project/badge/Grade/c82c9523e1474a039d2e30e6d34a2da0)](https://www.codacy.com/app/marcusngwj/main?utm_source=github.com&utm_medium=referral&utm_content=CS2103AUG2016-W15-C3/main&utm_campaign=Badge_Grade) -# Address Book (Level 4) +# Taskell -
+
-* This is a desktop Address Book application. It has a GUI but most of the user interactions happen using +* This is a desktop task manager application. It has a GUI but most of the user interactions happen using a CLI (Command Line Interface). -* It is a Java sample application intended for students learning Software Engineering while using Java as +* It is a Java application built by 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 @@ -25,10 +22,11 @@ * [Contact Us](docs/ContactUs.md) -#### Acknowledgements +#### Acknowledgement -* Some parts of this sample application were inspired by the excellent +* Some parts of this application were inspired by the excellent [Java FX tutorial](http://code.makery.ch/library/javafx-8-tutorial/) by *Marco Jakob*. +* Some parts of this application were adapted from [Address Book 4](https://github.com/nus-cs2103-AY1617S1/addressbook-level4) by *Damith C. Rajapakse* and his team -#### Licence : [MIT](LICENSE) +#### License : [MIT](LICENSE) \ No newline at end of file diff --git a/build.gradle b/build.gradle index 46b06c1e42ec..fc8baf23b09b 100644 --- a/build.gradle +++ b/build.gradle @@ -52,6 +52,7 @@ allprojects { compile "com.fasterxml.jackson.core:jackson-databind:$jacksonVersion" compile "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jacksonDataTypeVersion" compile "com.google.guava:guava:$guavaVersion" + compile "org.jfxtras:jfxtras-agenda:8.0-r1" testCompile "junit:junit:$junitVersion" testCompile "org.testfx:testfx-core:$testFxVersion" @@ -74,10 +75,10 @@ allprojects { } shadowJar { - archiveName = "addressbook.jar" + archiveName = "Taskell.jar" manifest { - attributes "Main-Class": "seedu.address.MainApp" + attributes "Main-Class": "seedu.taskell.MainApp" } destinationDir = file("${buildDir}/jar/") @@ -113,8 +114,8 @@ tasks.coveralls { onlyIf { System.env.'CI' } } -class AddressBookTest extends Test { - public AddressBookTest() { +class TaskManagerTest extends Test { + public TaskManagerTest() { forkEvery = 1 systemProperty 'testfx.setup.timeout', '60000' } @@ -128,7 +129,7 @@ class AddressBookTest extends Test { } } -task guiTests(type: AddressBookTest) { +task guiTests(type: TaskManagerTest) { include 'guitests/**' jacoco { @@ -137,8 +138,8 @@ task guiTests(type: AddressBookTest) { } -task nonGuiTests(type: AddressBookTest) { - include 'seedu/address/**' +task nonGuiTests(type: TaskManagerTest) { + include 'seedu/taskell/**' jacoco { destinationFile = new File("${buildDir}/jacoco/test.exec") @@ -146,7 +147,7 @@ task nonGuiTests(type: AddressBookTest) { } // Test mode depends on whether headless task has been run -task allTests(type: AddressBookTest) { +task allTests(type: TaskManagerTest) { jacoco { destinationFile = new File("${buildDir}/jacoco/test.exec") } diff --git a/collated/main/A0142130A.md b/collated/main/A0142130A.md new file mode 100644 index 000000000000..8f52262e0f5b --- /dev/null +++ b/collated/main/A0142130A.md @@ -0,0 +1,607 @@ +# A0142130A +###### \java\seedu\taskell\commons\events\model\DisplayListChangedEvent.java +``` java +package seedu.taskell.commons.events.model; + +import java.util.ArrayList; + +import seedu.taskell.commons.events.BaseEvent; + +/** Indicates a list needs to be displayed on Display Panel + * */ +public class DisplayListChangedEvent extends BaseEvent { + + private ArrayList list; + + public DisplayListChangedEvent(ArrayList list) { + this.list = list; + } + + @Override + public String toString() { + return this.getClass().getSimpleName(); + } + + public ArrayList getList() { + return list; + } + +} +``` +###### \java\seedu\taskell\commons\events\storage\StorageLocationChangedEvent.java +``` java +package seedu.taskell.commons.events.storage; + +import seedu.taskell.commons.core.Config; +import seedu.taskell.commons.events.BaseEvent; + +public class StorageLocationChangedEvent extends BaseEvent { + + private Config config; + + public StorageLocationChangedEvent(Config config) { + this.config = config; + } + + public Config getConfig() { + return config; + } + + @Override + public String toString() { + return this.getClass().getSimpleName(); + } + +} +``` +###### \java\seedu\taskell\logic\commands\ListUndoCommand.java +``` java +package seedu.taskell.logic.commands; + +import seedu.taskell.commons.core.EventsCenter; +import seedu.taskell.commons.events.model.DisplayListChangedEvent; + +/** Lists a list of previous commands available for Undo operation + * */ +public class ListUndoCommand extends Command { + + public static final String COMMAND_WORD = "list-undo"; + + public static final String MESSAGE_SUCCESS = "Listed all commands available for undo."; + + @Override + public CommandResult execute() { + indicateDisplayListChanged(); + return new CommandResult(MESSAGE_SUCCESS); + } + + public void indicateDisplayListChanged() { + EventsCenter.getInstance().post( + new DisplayListChangedEvent(UndoCommand.getListOfCommandHistoryText())); + } + +} +``` +###### \java\seedu\taskell\logic\commands\SaveStorageLocationCommand.java +``` java +package seedu.taskell.logic.commands; + +import java.io.IOException; +import java.util.logging.Logger; + +import seedu.taskell.commons.core.Config; +import seedu.taskell.commons.core.EventsCenter; +import seedu.taskell.commons.core.LogsCenter; +import seedu.taskell.commons.events.storage.StorageLocationChangedEvent; +import seedu.taskell.model.ReadOnlyTaskManager; +import seedu.taskell.storage.JsonConfigStorage; +import seedu.taskell.storage.Storage; + +/** Saves current data file to new filepath. + * */ + +public class SaveStorageLocationCommand extends Command { + + private Logger logger = LogsCenter.getLogger(SaveStorageLocationCommand.class.getName()); + + public static final String COMMAND_WORD = "save"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Saves data file to new location specified. " + + "New files can be auto-created as long as given directory is valid.\n" + + "Parameters: FILEPATH (must be valid)\n" + + "Example: " + COMMAND_WORD + " C:/Users/chicken/Desktop/cat"; + + private static final String MESSAGE_SUCCESS = "Data successfully saved to new location."; + private static final String MESSAGE_INVALID_PATH = "Filepath given is invalid. Filepath will be reset to old path."; + + private static Config config; + private String newStorageFilePath, oldStorageFilePath; + private ReadOnlyTaskManager taskManager; + private static JsonConfigStorage jsonConfigStorage; + private static Storage storage; + + public SaveStorageLocationCommand(String newStorageFilePath) { + this.oldStorageFilePath = config.getTaskManagerFilePath(); + logger.info("Old file path: " + oldStorageFilePath); + + this.newStorageFilePath = newStorageFilePath.trim().replace("\\", "/") + "/taskmanager.xml"; + logger.info("New file path: " + this.newStorageFilePath); + jsonConfigStorage = new JsonConfigStorage(Config.DEFAULT_CONFIG_FILE); + } + + public static void setConfig(Config c) { + config = c; + } + + public static void setStorage(Storage s) { + storage = s; + } + + @Override + public CommandResult execute() { + assert config != null; + assert jsonConfigStorage != null; + + taskManager = model.getTaskManager(); + + config.setTaskManagerFilePath(newStorageFilePath); + indicateStorageLocationChanged(); + try { + storage.saveTaskManager(taskManager, newStorageFilePath); + } catch (IOException e) { + handleInvalidFilePathException(); + return new CommandResult(MESSAGE_INVALID_PATH); + } + + saveToConfigJson(); + model.updateFilteredListToShowAll(); + return new CommandResult(MESSAGE_SUCCESS); + } + + private void indicateStorageLocationChanged() { + assert config != null; + EventsCenter.getInstance().post(new StorageLocationChangedEvent(config)); + } + + private void handleInvalidFilePathException() { + logger.info("Error writing to filepath. Handling data save exception."); + assert config != null; + + config.setTaskManagerFilePath(oldStorageFilePath); //set back to old filepath + indicateStorageLocationChanged(); + + try { + storage.saveTaskManager(taskManager, newStorageFilePath); + } catch (IOException e) { + logger.severe("Error saving task manager"); + } + + saveToConfigJson(); + } + + private void saveToConfigJson() { + try { + jsonConfigStorage.saveConfigFile(config); + } catch (IOException e) { + logger.severe("save to config json error"); + } + } + +} +``` +###### \java\seedu\taskell\logic\commands\UndoCommand.java +``` java +package seedu.taskell.logic.commands; + +import java.util.ArrayList; +import java.util.logging.Logger; + +import seedu.taskell.commons.core.EventsCenter; +import seedu.taskell.commons.core.LogsCenter; +import seedu.taskell.commons.events.model.DisplayListChangedEvent; +import seedu.taskell.model.CommandHistory; +import seedu.taskell.model.task.Task; +import seedu.taskell.model.task.UniqueTaskList.DuplicateTaskException; +import seedu.taskell.model.task.UniqueTaskList.TaskNotFoundException; + +/** + * Undo previously executed command (add or delete only for now) + * Note: only for current session only (meaning after app is closed, history will be cleared) + */ +public class UndoCommand extends Command { + private static final Logger logger = LogsCenter.getLogger(UndoCommand.class.getName()); + + public static final String COMMAND_WORD = "undo"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Undo a previously executed command.\n" + + "Parameters: INDEX (must be a positive integer)\n" + + "Example: " + COMMAND_WORD + " 3"; + + private static final String MESSAGE_DELETE_TASK_SUCCESS = "Deleted Task: %1$s"; + private static final String MESSAGE_ADD_TASK_SUCCESS = "Task added back: %1$s"; + + private static final String MESSAGE_DUPLICATE_TASK = "This task already exists in the task manager"; + private static final String MESSAGE_NO_TASK_TO_UNDO = "No add or delete commands available to undo."; + private static final String MESSAGE_COMMAND_HISTORY_EMPTY = "No command history available for undo."; + private static final String MESSAGE_INVALID_INDEX = "Index is invalid"; + + private static ArrayList commandHistoryList; + + private int index; + private CommandHistory commandHistory; + + public UndoCommand(int index) { + logger.info("Creating UndoCommand with index: " + index); + this.index = index; + } + + public static ArrayList getListOfCommandHistoryText() { + assert commandHistoryList != null; + assert !commandHistoryList.isEmpty(); + + ArrayList list = new ArrayList<>(); + for (CommandHistory history: commandHistoryList) { + list.add(history.getCommandText()); + } + + return list; + } + + @Override + public CommandResult execute() { + + if (commandHistoryList.isEmpty()) { + return new CommandResult(String.format(MESSAGE_COMMAND_HISTORY_EMPTY)); + } + else if (index > commandHistoryList.size()) { + return new CommandResult(String.format(MESSAGE_INVALID_INDEX)); + } + + commandHistory = commandHistoryList.get(getOffset(index)); + + switch (commandHistory.getCommandType()) { + case AddCommand.COMMAND_WORD: + return undoAdd(); + case DeleteCommand.COMMAND_WORD: + return undoDelete(); + default: + return new CommandResult(String.format(MESSAGE_NO_TASK_TO_UNDO)); + } + } + + private static int getOffset(int index) { + return index - 1; + } + + private CommandResult undoDelete() { + try { + model.addTask(commandHistory.getTask()); + deleteCommandHistory(); + indicateDisplayListChanged(); + return new CommandResult(String.format(MESSAGE_ADD_TASK_SUCCESS, commandHistory.getTask())); + } catch (DuplicateTaskException e) { + return new CommandResult(MESSAGE_DUPLICATE_TASK); + } + } + + private CommandResult undoAdd() { + try { + model.deleteTask(commandHistory.getTask()); + deleteCommandHistory(); + indicateDisplayListChanged(); + } catch (TaskNotFoundException e) { + assert false : "The target task cannot be missing"; + } + return new CommandResult(String.format(MESSAGE_DELETE_TASK_SUCCESS, commandHistory.getTask())); + } + + private void deleteCommandHistory() { + commandHistoryList.remove(commandHistory); + } + + public static void initializeCommandHistory() { + if (commandHistoryList==null) { + commandHistoryList = new ArrayList<>(); + } + } + + public static void addCommandToHistory(String commandText, + String commandType, Task task) { + assert commandHistoryList != null; + commandHistoryList.add(new CommandHistory(commandText, commandType, task)); + } + + public static void addTaskToCommandHistory(Task task) { + logger.info("Adding task to history"); + if (commandHistoryList.isEmpty()) { + logger.warning("No command history to add task to"); + return; + } + + commandHistoryList.get(getOffset(commandHistoryList.size())).setTask(task); + } + + public static void deletePreviousCommand() { + logger.info("Command unsuccessfully executed. Deleting command history."); + if (commandHistoryList.isEmpty()) { + logger.warning("No command history to delete"); + return; + } + commandHistoryList.remove(getOffset(commandHistoryList.size())); + } + + public void indicateDisplayListChanged() { + EventsCenter.getInstance().post( + new DisplayListChangedEvent(getListOfCommandHistoryText())); + } +} +``` +###### \java\seedu\taskell\logic\parser\Parser.java +``` java + + /** + * Parses arguments in the context of undo command. + * + */ + private Command prepareUndo(String args) { + Optional index = parseIndex(args); + if (!index.isPresent()) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + UndoCommand.MESSAGE_USAGE)); + } + return new UndoCommand(index.get()); + } + + /** + * Parses arguments in the context of the find task by tags command. + * + * @param args full command args string + * @return the prepared command + */ + private Command prepareFindByTag(String args) { + final Matcher matcher = KEYWORDS_ARGS_FORMAT.matcher(args.trim()); + if (!matcher.matches()) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindTagCommand.MESSAGE_USAGE)); + } + + // keywords delimited by whitespace + final String[] keywords = matcher.group("keywords").split("\\s+"); + final Set keywordSet = new HashSet<>(Arrays.asList(keywords)); + return new FindTagCommand(keywordSet); + + } + + /** + * Parses arguments in the context of the save storage location command. + * + * @param args full command args string + * @return the prepared command + */ + private Command prepareSaveStorageLocation(String args) { + if (args.isEmpty()) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + SaveStorageLocationCommand.MESSAGE_USAGE)); + } + return new SaveStorageLocationCommand(args); + } + +``` +###### \java\seedu\taskell\MainApp.java +``` java + @Subscribe + private void handleStorageLocationChangedEvent(StorageLocationChangedEvent event) { + config = event.getConfig(); + storage = new StorageManager(config.getTaskManagerFilePath(), config.getUserPrefsFilePath()); + } +``` +###### \java\seedu\taskell\model\CommandHistory.java +``` java +package seedu.taskell.model; + +import seedu.taskell.model.task.Task; + +/** Stores the relevant details of a command so it can be undone via UndoCommand + * each Add/Delete/Edit Command executed should correspond to a CommandHistory + * stored in UndoCommand's list of CommandHistory + * */ +public class CommandHistory { + private final String commandText, commandType; + private Task task; //relevent task to be added, deleted or edited + private boolean toRedo; + + public CommandHistory() { + commandText = "default command text"; + commandType = "default command type"; + task = null; + } + + public CommandHistory(String commandText, String commandType, Task task) { + assert commandText != null; + assert commandType != null; + assert !commandText.trim().isEmpty(); + assert !commandType.trim().isEmpty(); + + this.commandText = commandText.trim(); + this.commandType = commandType.trim(); + this.task = task; + } + + public String getCommandText() { + return commandText; + } + + public String getCommandType() { + return commandType; + } + + public Task getTask() { + return task; + } + + public void setTask(Task task) { + assert task != null; + this.task = task; + } + + public void setToRedoToTrue() { + toRedo = true; + } + +} +``` +###### \java\seedu\taskell\model\ModelManager.java +``` java + @Override + public void updateFilteredTaskListByAnyKeyword(Set keywords) { + updateFilteredTaskList(new PredicateExpression(new TagsQualifier(keywords))); + } +``` +###### \java\seedu\taskell\model\ModelManager.java +``` java + @Override + public boolean run(ReadOnlyTask task) { + String searchString = task.getDescription().description + + " " + task.tagsSimpleString(); + return nameKeyWords.stream() + .allMatch(keyword -> StringUtil.containsIgnoreCase(searchString, keyword)); + } +``` +###### \java\seedu\taskell\model\ModelManager.java +``` java + private class TagsQualifier implements Qualifier { + private Set tagsKeyWords; + + TagsQualifier(Set keyWords) { + this.tagsKeyWords = keyWords; + } + + @Override + public boolean run(ReadOnlyTask task) { + return tagsKeyWords.stream() + .filter(keyword -> StringUtil.containsIgnoreCase(task.tagsSimpleString(), keyword)) + .findAny() + .isPresent(); + } + + @Override + public String toString() { + return "name=" + String.join(", ", tagsKeyWords); + } + } +``` +###### \java\seedu\taskell\storage\JsonConfigStorage.java +``` java +package seedu.taskell.storage; + +import java.io.File; +import java.io.IOException; +import java.util.Optional; +import java.util.logging.Logger; + +import seedu.taskell.commons.core.Config; +import seedu.taskell.commons.core.LogsCenter; +import seedu.taskell.commons.exceptions.DataConversionException; +import seedu.taskell.commons.util.FileUtil; + +/** + * A class to access Config stored in the hard disk as a json file + */ + +public class JsonConfigStorage implements ConfigStorage { + + private static final Logger logger = LogsCenter.getLogger(JsonConfigStorage.class); + + private String filePath; + + public JsonConfigStorage(String filePath) { + this.filePath = filePath; + } + + @Override + public Optional readConfigFile() throws DataConversionException, IOException { + return readConfig(filePath); + } + + @Override + public void saveConfigFile(Config config) throws IOException { + saveConfig(config, filePath); + } + + /** + * Similar to {@link #readConfigFile()} + * @param configFilePath location of the data. Cannot be null. + * @throws DataConversionException if the file format is not as expected. + */ + public Optional readConfig(String configFilePath) throws DataConversionException { + assert configFilePath != null; + + File configFile = new File(configFilePath); + + if (!configFile.exists()) { + logger.info("Config file " + configFile + " not found"); + return Optional.empty(); + } + + Config config; + + try { + config = FileUtil.deserializeObjectFromJsonFile(configFile, Config.class); + } catch (IOException e) { + logger.warning("Error reading from config file " + configFile + ": " + e); + throw new DataConversionException(e); + } + + return Optional.of(config); + } + + /** + * Similar to {@link #saveConfigFile(Config)} + * @param configFilePath location of the data. Cannot be null. + */ + private void saveConfig(Config config, String configFilePath) throws IOException { + assert config != null; + assert configFilePath != null; + assert !configFilePath.isEmpty(); + + FileUtil.serializeObjectToJsonFile(new File(configFilePath), config); + } + +} +``` +###### \java\seedu\taskell\ui\DisplayPanel.java +``` java + + /** + * Factory method for creating a Display Panel. + * This method should be called after the FX runtime is initialized and in FX application thread. + * @param placeholder The AnchorPane where the DisplayPanel must be inserted + */ + public static DisplayPanel load(AnchorPane placeholder){ + logger.info("Initializing display panel"); + DisplayPanel displayPanel = new DisplayPanel(); + displayPanel.display = new TextArea(); + displayPanel.display.setEditable(false); + displayPanel.display.setId(RESULT_DISPLAY_ID); + displayPanel.display.getStyleClass().removeAll(); + displayPanel.display.getStyleClass().add(STATUS_BAR_STYLE_SHEET); + + FxViewUtil.applyAnchorBoundaryParameters(displayPanel.display, 0.0, 0.0, 0.0, 0.0); + placeholder.getChildren().add(displayPanel.display); + + displayPanel.display.setText("Welcome to Taskell!"); + + return displayPanel; + } + + public void loadList(ArrayList list) { + display.setText(""); + if (list.isEmpty()) { + display.setText("No commands available for undo."); + } + else { + for (int i=0; i readConfigFile() throws DataConversionException, IOException; + + /** + * Saves the given {@link seedu.taskell.commons.core.Config} to the storage. + * @param config cannot be null. + * @throws IOException if there was any problem writing to the file. + */ + void saveConfigFile(Config config) throws IOException; +} +``` diff --git a/collated/test/A0142130A.md b/collated/test/A0142130A.md new file mode 100644 index 000000000000..f8b8c28ccd74 --- /dev/null +++ b/collated/test/A0142130A.md @@ -0,0 +1,145 @@ +# A0142130A +###### \java\guitests\FindCommandTest.java +``` java + + @Test + public void find_nonEmptyList_byTag() { + assertFindResult("find chicken"); //no results + assertFindResult("find friends", td.archivePastEmails, td.borrowBooks); //multiple results + + //find after deleting one result + commandBox.runCommand("delete 1"); + assertFindResult("find friends", td.borrowBooks); + } + + @Test + public void find_byMoreThanOneTags() { + assertFindResult("find friends owesMoney", td.borrowBooks); + } + +``` +###### \java\guitests\FindTagCommandTest.java +``` java +package guitests; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import seedu.taskell.commons.core.Messages; +import seedu.taskell.testutil.TestTask; + +public class FindTagCommandTest extends TaskManagerGuiTest { + @Test + public void findTag_nonEmptyList() { + assertFindResult("find-tag chicken"); //no results + assertFindResult("find-tag friends", td.archivePastEmails, td.borrowBooks); //multiple results + + //find after deleting one result + commandBox.runCommand("delete 1"); + assertFindResult("find-tag friends",td.borrowBooks); + } + + @Test + public void findTag_MulitpleKeywords() { + assertFindResult("find-tag friends owesMoney", td.archivePastEmails, td.borrowBooks, td.collectParcel); //multiple results + } + + @Test + public void findTag_emptyList(){ + commandBox.runCommand("clear"); + assertFindResult("find-tag friends"); //no results + } + + @Test + public void findTag_invalidCommand_fail() { + commandBox.runCommand("find-tagfriends"); + assertResultMessage(Messages.MESSAGE_UNKNOWN_COMMAND); + } + + private void assertFindResult(String command, TestTask... expectedHits ) { + commandBox.runCommand(command); + assertListSize(expectedHits.length); + assertResultMessage(expectedHits.length + " tasks listed!"); + assertTrue(taskListPanel.isListMatching(expectedHits)); + } +} +``` +###### \java\guitests\SaveStorageLocationCommandTest.java +``` java +package guitests; + +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.util.List; +import java.util.Optional; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.junit.Test; + +import seedu.taskell.TestApp; +import seedu.taskell.commons.core.Config; +import seedu.taskell.commons.core.LogsCenter; +import seedu.taskell.commons.exceptions.DataConversionException; +import seedu.taskell.logic.commands.SaveStorageLocationCommand; +import seedu.taskell.model.ReadOnlyTaskManager; +import seedu.taskell.model.TaskManager; +import seedu.taskell.model.task.UniqueTaskList.DuplicateTaskException; +import seedu.taskell.storage.JsonConfigStorage; +import seedu.taskell.testutil.TestTask; +import seedu.taskell.testutil.TestUtil; + +public class SaveStorageLocationCommandTest extends TaskManagerGuiTest { + + private static final Logger logger = LogsCenter.getLogger(SaveStorageLocationCommandTest.class); + + private static final String CONFIG_JSON = "config.json"; + private static final String CONFIG_LOCATION = "./src/test/data/SaveLocationCommandTest"; + + private static final String DEFAULT_SAVE_LOCATION = TestApp.SAVE_LOCATION_FOR_TESTING; + + @Test + public void saveToValidFilePath() throws DataConversionException, IOException, DuplicateTaskException { + String testFilePath = "./src/test/data/SaveLocationCommandTest/newStorageLocation/"; + commandBox.runCommand("save " + testFilePath); + assertWriteToJsonSuccess(); + assertWriteToXmlSuccess(); + } + +``` +###### \java\guitests\SaveStorageLocationCommandTest.java +``` java + + /** NOTE: because of the way SaveStorageLocationCommand works, after running this command + * config.json in Taskell saves the test data so this method is necessary to reset + * config.json to default data + * */ + @Test + public void resetConfigFile() throws IOException { + Config config = new Config(); + config.setAppTitle("Taskell"); + config.setLogLevel(Level.INFO); + config.setUserPrefsFilePath("preferences.json"); + config.setTaskManagerFilePath("data/taskmanager.xml"); + config.setTaskManagerName("MyTaskManager"); + SaveStorageLocationCommand.setConfig(config); + + JsonConfigStorage jsonConfigStorage = new JsonConfigStorage(CONFIG_JSON); + jsonConfigStorage.saveConfigFile(config); + } + + private void assertWriteToJsonSuccess() throws DataConversionException { + JsonConfigStorage jsonConfigStorage = new JsonConfigStorage(CONFIG_LOCATION); + Optional config = jsonConfigStorage.readConfig(CONFIG_JSON); + assert(config.isPresent()); + } + + private void assertWriteToXmlSuccess() { + TestTask[] currentList = td.getTypicalTasks(); + assertTrue(taskListPanel.isListMatching(currentList)); + } + +} +``` diff --git a/collated/test/A0142130Aunused.md b/collated/test/A0142130Aunused.md new file mode 100644 index 000000000000..0f9437932af0 --- /dev/null +++ b/collated/test/A0142130Aunused.md @@ -0,0 +1,18 @@ +# A0142130Aunused +###### \java\guitests\SaveStorageLocationCommandTest.java +``` java + //This test is not run because it has assertion error on Travis build + //@Test + public void saveToInvalidFilePath() throws DataConversionException { + JsonConfigStorage jsonConfigStorage = new JsonConfigStorage(CONFIG_LOCATION); + + commandBox.runCommand("save E:"); + + Optional newConfig = jsonConfigStorage.readConfig(CONFIG_JSON); + String newFilePath = newConfig.get().getTaskManagerFilePath(); + logger.info("New path: " + newFilePath); + + assert(newFilePath.equals(DEFAULT_SAVE_LOCATION)); + } + +``` diff --git a/config/findbugs/excludeFilter.xml b/config/findbugs/excludeFilter.xml index 03c15ae4cc81..8955c4af62b0 100644 --- a/config/findbugs/excludeFilter.xml +++ b/config/findbugs/excludeFilter.xml @@ -6,7 +6,7 @@ - + diff --git a/copyright.txt b/copyright.txt index 93aa2a39ce25..fb67df74cf3a 100644 --- a/copyright.txt +++ b/copyright.txt @@ -1,7 +1,7 @@ Some code adapted from http://code.makery.ch/library/javafx-8-tutorial/ by Marco Jakob Copyright by Susumu Yoshida - http://www.mcdodesign.com/ -- address_book_32.png +- task_manager_32.png - AddressApp.ico Copyright by Jan Jan Kovařík - http://glyphicons.com/ diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 33df65bea583..e4d972b147c0 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -4,49 +4,104 @@ We are a team based in the [School of Computing, National University of Singapor ## Project Team -#### [Damith C. Rajapakse](http://www.comp.nus.edu.sg/~damithch)
-
-**Role**: Project Advisor +#### [Marcus Ng Wen Jian](https://github.com/marcusngwj)
+
+**Role**: Team Leader
+**Responsibilities**: Code Quality Control, Logic
+**Features implemented**: +* Add Command +* TaskDate +* TaskTime +* Priority Colour ------ +As a Team Leader, I will +* Be responsible for the overall project coordination +* Be the main point of contact for the group +* Liaise with Professor and Tutorial Assistants +* Ensure that each member of the team performs his/her roles dutifully +* Be responsible in creating a comfortable workflow structure for the team +
-#### [Joshua Lee](http://github.com/lejolly) -
-Role: Developer
-Responsibilities: UI +As a Code Quality Surveyor, I will +* Look after the code quality +* Ensure that the team adhere to the Java coding standards +* Ensure that code follows software design principles ----- -#### [Leow Yijin](http://github.com/yijinl) -
-Role: Developer
-Responsibilities: Data +#### [Jazlyn Ang Chue Ching](https://github.com/turtle96) +
+**Role**: Developer
+**Responsibilities**: UI, Testing and Integrating of codes
+**Features implemented**: +* Save Location +* Undo Command +* Find Tag +* UI + +As a Code Integrator, I will +* Make sure code works well when integrating a new feature to system (by liaising with those in-charge of Documentation, Code Quality and Testing) +* Make sure any new code follows software design principles for easier maintenance +* Look after the Github repository +* Ensure that branches and PRs are well-labeled and specific for easier tracking +
+ +As a Testing Leader, I will +* Ensure tests cover all the bases required for regression testing (e.g. error handling, nullpointers etc) +* Ensure testing is done regularly and on schedule ----- -#### [Martin Choo](http://github.com/m133225) -
-Role: Developer
-Responsibilities: Dev Ops +#### [Madasamy Ravi Nadar Mamtha](https://github.com/Mamtha3005) +
+**Role**: Developer
+**Responsibilities**: Data, Documentation, Scheduling and Tracking of deadlines
+**Features implemented**: +* Edit Command +* List Command + +As a Documentation Leader, I will +* Be well-organised +* Be responsible for the quality of documents submitted by the team. +* Ensure that documents follow the format provided, if applicable. +* Ensure that sentences in the document are grammatically correct, points are clear, concise, well-analysed and explained in a way everyone can understand +
+ +As a Scheduling Deputy Leader, I will +* Provide realistic deadlines for my team mates so that they can finish their part with good quality +* Remind them when deadlines are nearing, giving them enough time to prepare +* Remind them on a regular basis, giving them suggested timeline so that they can gauge their working pace and better manage their time ----- -#### [Thien Nguyen](https://github.com/ndt93) - Role: Developer
- Responsibilities: Threading - - ----- +#### [Zuo ZhuoLin](https://github.com/ZuoZhuolin) +
+**Role**: Developer
+**Responsibilities**: Development and Operations, Testing, Scheduling and Tracking of deadlines
+**Features implemented**: +* Done Command +* List Command + +As a Scheduling Leader, I will +* Set realistic deadline for different milestones of the project +* Remind team mates when deadlines are nearing +
-#### [You Liang](http://github.com/yl-coder) -
- Role: Developer
- Responsibilities: UI - - ----- +As Testing Deputy Leader, I will +* Double check tests cover all the bases required for regression testing +* Ensures the testing of project is done properly and on time + +----- # Contributors We welcome contributions. See [Contact Us](ContactUs.md) page for more info. +* [Damith C. Rajapakse](http://www.comp.nus.edu.sg/~damithch/) +* [Joshua Lee](https://github.com/lejolly) +* [Leow Yijin](https://github.com/yijinl) +* [Martin Choo](https://github.com/m133225) +* [Thien Nguyen](https://github.com/ndt93) +* [You Liang](https://github.com/yl-coder) * [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..d50841f6a789 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-W15-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 `autumncs2103t [at] gmail.com` \ No newline at end of file diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 690b6d386627..8343a8e2f5dd 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -1,5 +1,6 @@ # Developer Guide +* [Introduction](#introduction) * [Setting Up](#setting-up) * [Design](#design) * [Implementation](#implementation) @@ -11,8 +12,13 @@ * [Appendix D: Glossary](#appendix-d--glossary) * [Appendix E : Product Survey](#appendix-e-product-survey) +##Introduction -## Setting up +Taskell is a simple software for users to keep track of their daily tasks and manage their busy schedule. Keyboard lovers will be able to experience the full benefit of Taskell as it implements a command-line interface. + +This developer guide will help you understand the design and implementation of Taskell. It helps you understand how Taskell works and how you can contribute for further development. This guide follows a top-down approach by giving an overview of the essential components first, followed by thorough explanation subsequently. + +## Setting Up #### Prerequisites @@ -22,22 +28,21 @@ This app will not work with earlier versions of Java 8. 2. **Eclipse** IDE -3. **e(fx)clipse** plugin for Eclipse (Do the steps 2 onwards given in +3. **E(fx)clipse** plugin for Eclipse (Do the steps 2 onwards given in [this page](http://www.eclipse.org/efxclipse/install.html#for-the-ambitious)) 4. **Buildship Gradle Integration** plugin from the Eclipse Marketplace -#### Importing the project into Eclipse +#### Importing The Project Into Eclipse -0. Fork this repo, and clone the fork to your computer -1. Open Eclipse (Note: Ensure you have installed the **e(fx)clipse** and **buildship** plugins as given - in the prerequisites above) +0. Fork this repository, and clone the fork to your computer +1. Open Eclipse (Note: Ensure you have installed the **e(fx)clipse** and **buildship** plugins as given in the prerequisites above) 2. Click `File` > `Import` 3. Click `Gradle` > `Gradle Project` > `Next` > `Next` 4. Click `Browse`, then locate the project's directory 5. Click `Finish` - > * If you are asked whether to 'keep' or 'overwrite' config files, choose to 'keep'. + > * If you are asked whether to 'keep' or 'overwrite' configuration files, choose to 'keep'. > * Depending on your connection speed and server load, it can even take up to 30 minutes for the set up to finish (This is because Gradle downloads library files from servers during the project set up process) > * If Eclipse auto-changed any settings files during the import process, you can discard those changes. @@ -58,45 +63,64 @@ ### Architecture -
-The **_Architecture Diagram_** given above explains the high-level design of the App. -Given below is a quick overview of each component. +

+
+ +Diagram 1: Architecture Diagram +

+ +The Architecture Diagram given above explains the high-level design of the Application. +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, -* 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. +`Main` has only one class called [`MainApp`](../src/main/java/seedu/taskell/MainApp.java). It is responsible for, +* At application launch: Initializes the components in the correct sequence, and connects them up with each other. +* At shut down: Shuts down the components and invokes cleanup method where necessary. [**`Commons`**](#common-classes) represents a collection of classes used by multiple other components. Two of those classes play important roles at the architecture level. -* `EventsCentre` : This class (written using [Google's Event Bus library](https://github.com/google/guava/wiki/EventBusExplained)) - is used by components to communicate with other components using events (i.e. a form of _Event Driven_ design) -* `LogsCenter` : Used by many classes to write log messages to the App's log file. +* `EventsCentre` : Used by components to communicate with other components using events (i.e. a form of _Event Driven_ design)(written using [Google's Event Bus library](https://github.com/google/guava/wiki/EventBusExplained)) -The rest of the App consists four components. -* [**`UI`**](#ui-component) : The UI of tha App. -* [**`Logic`**](#logic-component) : The command executor. -* [**`Model`**](#model-component) : Holds the data of the App in-memory. -* [**`Storage`**](#storage-component) : Reads data from, and writes data to, the hard disk. +* `LogsCenter` : Used by many classes to write log messages to the Application's log file. + +The rest of the Application consists four components. +* [**`UI`**](#ui-component) : UI of the Application. +* [**`Logic`**](#logic-component) : Command executor. +* [**`Model`**](#model-component) : Data Holder of the Application in-memory. +* [**`Storage`**](#storage-component) : Data read from, and written to the hard disk. Each of the four components * Defines its _API_ in an `interface` with the same name as the Component. * Exposes its functionality using a `{Component Name}Manager` class. -For example, the `Logic` component (see the class diagram given below) defines it's API in the `Logic.java` -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`. +Diagram 2: Logic Class Diagram +

+ +The `Logic` component above defines it's API in the `Logic.java` +interface and exposes its functionality using the `LogicManager.java` class.
- +

+
->Note how the `Model` simply raises a `AddressBookChangedEvent` when the Address Book data are changed, +Diagram 3: Sequence Diagram for Delete Task +

+ +The Sequence Diagram above shows how the components interact for the scenario where the user issues the +command `delete 1`. + +>Note how the `Model` simply raises a `TaskManagerChangedEvent` when the Task Manager data is 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.
- +

+
+ +Diagram 4: Sequence Diagram for Delete Task Event Handling +

+ +The diagram above shows how the `EventsCenter` reacts to that event, which eventually results in the updates +being saved to the hard disk. The status bar of the UI is 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 @@ -104,67 +128,93 @@ 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 +### UI Component +


-**API** : [`Ui.java`](../src/main/java/seedu/address/ui/Ui.java) +Diagram 5: UI Class Diagram +

-The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `PersonListPanel`, +The diagram above gives an overview of how the `UI`component is implemented.
+ +**API** : [`Ui.java`](../src/main/java/seedu/taskell/ui/Ui.java) + +The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `TaskListPanel`, `StatusBarFooter`, `BrowserPanel` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class and they can be loaded using the `UiPartLoader`. The `UI` component uses JavaFx UI framework. The layout of these UI parts are defined in matching `.fxml` files that are in the `src/main/resources/view` folder.
- For example, the layout of the [`MainWindow`](../src/main/java/seedu/address/ui/MainWindow.java) is specified in + For example, the layout of the [`MainWindow`](../src/main/java/seedu/taskell/ui/MainWindow.java) is specified in [`MainWindow.fxml`](../src/main/resources/view/MainWindow.fxml) The `UI` component, * Executes user commands using the `Logic` component. -* Binds itself to some data in the `Model` so that the UI can auto-update when data in the `Model` change. -* Responds to events raised from various parts of the App and updates the UI accordingly. +* Binds itself to some data in the `Model` so that the UI can auto-update when data in the `Model` changes. +* Responds to events raised from various parts of the Application and updates the UI accordingly. -### Logic component +### Logic Component +


-**API** : [`Logic.java`](../src/main/java/seedu/address/logic/Logic.java) +Diagram 6: Logic Class Diagram +

-1. `Logic` uses the `Parser` class to parse the user command. -2. This results in a `Command` object which is executed by the `LogicManager`. -3. The command execution can affect the `Model` (e.g. adding a person) and/or raise events. -4. The result of the command execution is encapsulated as a `CommandResult` object which is passed back to the `Ui`. +The diagram above gives an overview of how the `Logic`component is implemented.
+
**API** : [`Logic.java`](../src/main/java/seedu/taskell/logic/Logic.java) -Given below is the Sequence Diagram for interactions within the `Logic` component for the `execute("delete 1")` - API call.
-
+The `Logic` component, +* Uses the `Parser` class to parse the user command, resulting in a `Command` object which is executed by the `LogicManager`. +* Affects the `Model` (e.g. adding a task) and/or raise events. +* Executes the necessary command and the result is encapsulated as a `CommandResult` to be passed back to the `UI`. -### Model component +

+
+ +Diagram 7: Add Task Sequence Diagram For Logic +

+ +The diagram above shows the Sequence Diagram for interactions within the `Logic` component for the `execute("delete 1")` + API call.
+ +### Model Component +


-**API** : [`Model.java`](../src/main/java/seedu/address/model/Model.java) +Diagram 7: Model Class Diagram +

-The `Model`, +The diagram above gives an overview of how the `Model` component is implemented.
+
**API** : [`Model.java`](../src/main/java/seedu/taskell/model/Model.java) + +The `Model` component, * 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 Task Manager 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 +### Storage Component +


-**API** : [`Storage.java`](../src/main/java/seedu/address/storage/Storage.java) +Diagram 8: Storage Class Diagram +

+ +The diagram above gives an overview of how the `Storage` component is implemented.
+
**API** : [`Storage.java`](../src/main/java/seedu/taskell/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 Task Manager data in xml format and read it back. -### Common classes +### Common Classes -Classes used by multiple components are in the `seedu.addressbook.commons` package. +Classes used by multiple components are in the `seedu.taskmanager.commons` package. ## Implementation @@ -182,14 +232,14 @@ and logging destinations. **Logging Levels** * `SEVERE` : Critical problem detected which may possibly cause the termination of the application -* `WARNING` : Can continue, but with caution -* `INFO` : Information showing the noteworthy actions by the App +* `WARNING` : Program can continue, but with caution +* `INFO` : Information showing the noteworthy actions by the Application * `FINE` : Details that is not usually noteworthy but may be useful in debugging e.g. print the actual list instead of just its size ### Configuration -Certain properties of the application can be controlled (e.g App name, logging level) through the configuration file +Certain properties of the application can be controlled (e.g Application name, logging level) through the configuration file (default: `config.json`): @@ -200,7 +250,7 @@ Tests can be found in the `./src/test/java` folder. **In Eclipse**: * To run all tests, right-click on the `src/test/java` folder and choose `Run as` > `JUnit Test` -* To run a subset of tests, you can right-click on a test package, test class, or a test and choose +* To run a subset of tests, right-click on a test package, test class, or a test and choose to run as a JUnit test. **Using Gradle**: @@ -208,22 +258,22 @@ Tests can be found in the `./src/test/java` folder. We have two types of tests: -1. **GUI Tests** - These are _System Tests_ that test the entire App by simulating user actions on the GUI. +1. **GUI Tests** - These are _System Tests_ that test the entire Application by simulating user actions on the GUI. These are in the `guitests` package. 2. **Non-GUI Tests** - These are tests not involving the GUI. They include, 1. _Unit tests_ targeting the lowest level methods/classes.
- e.g. `seedu.address.commons.UrlUtilTest` + e.g. `seedu.taskell.model.task.TaskDateTest` 2. _Integration tests_ that are checking the integration of multiple code units (those code units are assumed to be working).
- e.g. `seedu.address.storage.StorageManagerTest` - 3. Hybrids of unit and integration tests. These test are checking multiple code units as well as + e.g. `seedu.taskell.storage.StorageManagerTest` + 3. Hybrids of unit and integration tests. These tests are checking multiple code units as well as how the are connected together.
- e.g. `seedu.address.logic.LogicManagerTest` + e.g. `seedu.taskell.logic.LogicManagerTest` **Headless GUI Testing** : Thanks to the [TestFX](https://github.com/TestFX/TestFX) library we use, - our GUI tests can be run in the _headless_ mode. + our GUI tests can be run in the headless mode. In the headless mode, GUI tests do not show up on the screen. That means the developer can do other things on the Computer while the tests are running.
See [UsingGradle.md](UsingGradle.md#running-tests) to learn how to run tests in headless mode. @@ -252,17 +302,17 @@ See [UsingTravis.md](UsingTravis.md) for more details. Here are the steps to create a new release. 1. Generate a JAR file [using Gradle](UsingGradle.md#creating-the-jar-file). - 2. Tag the repo with the version number. e.g. `v0.1` - 2. [Crete a new release using GitHub](https://help.github.com/articles/creating-releases/) + 2. Tag the repository with the version number. e.g. `v0.1` + 2. [Create a new release using GitHub](https://help.github.com/articles/creating-releases/) and upload the JAR file your created. ### Managing Dependencies -A project often depends on third-party libraries. For example, Address Book depends on the +A project often depends on third-party libraries. For example, Taskell 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)
+a. Include those libraries in the repository (this bloats the repository size)
b. Require developers to download those libraries manually (this creates extra work for developers)
## Appendix A : User Stories @@ -271,51 +321,234 @@ 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 - -{More to be added} +-------- | :---------- | :--------- | :----------- +`* * *` | new user | see user guide | refer to the different commands when I forget how to use the application. +`* * *` | user | add a task | take note of all my tasks. +`* * *` | user | delete a task | remove task that I no longer need. +`* * *` | user | find a task by its description | locate details of tasks without having to go through the entire list. +`* * *` | user | categorize my tasks | group and view tasks of similar type. +`* * *` | user | view all the tasks, sorted by day, month | plan my schedule. +`* * *` | user | edit task | make changes to the task created. +`* * *` | user | have a start and end time for an event | take note of the duration of the event. +`* * *` | user | set deadlines for a task | remember when the task is due. +`* * *` | user | undo my previous action | correct any mistakes made. +`* * *` | user | mark a task as done | focus on the uncompleted tasks. +`* * *` | user | have flexible command format |have various options to execute a command. +`* * *` | user | specify a folder with cloud syncing service as the storage location | I can easily access my task manager from different computers. +`* * *` | user | I want to see a list of completed tasks | view all the tasks I had done. +`* *` | user | delete tasks based on a certain index | delete a few tasks instead of one. +`*` | user | set some of my task recursively | schedule them on a daily/weekly/monthly basis. +`*` | user | be able to block multiple timeslots, and release the timeslots when timing is confirmed| schedule in events which have uncertain timings more efficiently. +`*` | user | sort tasks by priority | view the most important tasks. +`*` | user | edit my notification time period | customise if I wanted to be reminded earlier or later. +`*` | user | use the history command | saves time typing repeated commands. +`*` | user | view the task in either calendar form or list form | switch between the two display format. ## Appendix B : Use Cases -(For all use cases below, the **System** is the `AddressBook` and the **Actor** is the `user`, unless specified otherwise) +#### Use case: Add task + +**MSS** + +1. User requests to add tasks + +2. Taskell adds the task
+Use case ends + +**Extensions** + +2a. The user did not follow the given format to add the task + +> 2a1. Taskell displays invalid command format warning
+ Use case resumes at step 1 -#### Use case: Delete person +#### Use case: Delete task **MSS** -1. User requests to list persons -2. AddressBook shows a list of persons -3. User requests to delete a specific person in the list -4. AddressBook deletes the person
-Use case ends. +1. User requests to list tasks +2. Taskell shows a list of uncompleted tasks +3. User requests to delete a specific task in the list +4. Taskell deletes the task
+Use case ends **Extensions** 2a. The list is empty -> Use case ends +3a. The given index is invalid + +> 3a1. Taskell shows an error message
+ Use case resumes at step 2 + +#### Use case: Done task + +**MSS** + +1. User requests to list tasks +2. Taskell shows a list of uncompleted tasks +3. User requests to mark a specific task in the list as completed +4. Taskell marks the task as completed
+Use case ends + +**Extensions** + +2a. The list is empty + +3a. The given index is invalid + +> 3a1. Taskell shows an error message
+ Use case resumes at step 2 + +4a. User tries to mark a completed task as completed + +#### Use case: Help task + +**MSS** + +1. User requests to view the different command +2. User enters "help" +3. User displays a summary of all the different command. +Use case ends + +**Extensions** + +2a. The user types "help" incorrectly + +> 3a1. Taskell stil displays the help message
+ +#### Use case: Find task + +**MSS** + +1. User requests to find tasks with specific keywords +2. Taskell displays the tasks with all matching keywords
+Use case ends + +**Extensions** + +1a. No keyword is given + +> 1a1. Taskell shows an error message
+ +#### Use case: Edit task + +**MSS** + +1. User requests to list tasks +2. Taskell shows a list of tasks +3. User requests to edit either the description, date, time or priority of a task +4. Taskell edits the respective field +5. Taskell displays the both the old and updated version of the task
+Use case ends + +**Extensions** + +2a. The list is empty 3a. The given index is invalid +3b. The user did not key in the new field of the task +3c. The user did not key in a valid parameter + +> 3a1, 3b1 and 3c1. Taskell shows an error message
+ Use case resumes at step 2 + +#### Use case: Undo task + +**MSS** + +1. User enters a command +2. Taskell executes it +<<<<<<< HEAD +3. User requests to list commands history +4. User requests to undo command at specific index +5. Taskell undoes the previous command
+======= +3. User requests to list undo commands history +4. User requests to undo command at specific index +5. Taskell revert the command
+>>>>>>> 9d3e940bc97dc4a324dca16582e28cdbfc8c5ebc +Use case ends + +**Extensions** + +3a. The user did not enter any previous command + +> 3a1. Taskell shows a message indicating no commands available for undo
+ +4a. The user enters invalid index + +> 4a1. Taskell shows error message indicating index is invalid
+ +#### Use case: List task + +**MSS** + +1. User requests to list either all tasks, incomplete tasks, completed tasks, task with specific start date or task with specific priority +2. Taskell shows a list of tasks accordingly +Use case ends + +**Extensions** + +2a. The list is empty -> 3a1. AddressBook shows an error message
+> 2a1. Taskell shows an error message
Use case resumes at step 2 + +#### Use case: View calendar for the week + +**MSS** + +1. User requests to view calendar +2. Taskell displays calendar + +#### Use case: Save data to specified folder + +**MSS** + +1. User requests to save all tasks +2. Taskell saves all tasks in the requested folder
+Use case ends + +**Extensions** + +2a. Data cannot be written to the requested folder (invalid directory or access prohibited) + +> 2a1. Taskell shows an error message and still saves data in previous old location.
+ +#### Use case: Clear task + +**MSS** -{More to be added} +1. User requests to clear all tasks +2. Taskell deletes all tasks +Use case ends + +**Extensions** + +2a. The list is empty + +> 2a1. Taskell shows an error message
+ +#### Use case: Exit task + +**MSS** + +1. User requests to exit Taskell +2. Taskell saves all the data and stops +Use case ends + +**Extensions** + +NIL ## Appendix C : Non Functional Requirements 1. Should work on any [mainstream OS](#mainstream-os) as long as it has Java `1.8.0_60` or higher installed. -2. Should be able to hold up to 1000 persons. +2. Should be able to hold up to 1000 tasks. 3. Should come with automated unit tests and open source code. 4. Should favor DOS style commands over Unix-style commands. - -{More to be added} +5. Should execute commands under 5 seconds. ## Appendix D : Glossary @@ -323,11 +556,53 @@ Use case ends. > Windows, Linux, Unix, OS-X -##### Private contact detail +##### Floating Tasks -> A contact detail that is not meant to be shared with others +> Tasks with no deadline ## Appendix E : Product Survey - -{TODO: Add a summary of competing products} - +#### WunderList +**Pros:**
+1. Has support for cross-platform operations
+2. Can share tasks with other people and manage them
+3. Can categorize tasks into different categories
+4. Can attach different types of file inside the task, such as photos, PDF and PowerPoint
+ +**Cons:**
+1. Has limited number of priority levels
+2. Has no undo operation
+ +#### Remember the Milk +**Pros:**
+1. Can be used offline and tasks are synced once internet connection is established
+2. Can handle some natural language processing by saving deadlines from task information itself (e.g. Do math homework tomorrow: Saves task with deadline set to tomorrow)
+3. Can undo when marking tasks as done (recover from accidentally marking a task as done)
+4. Can set priority, and list tasks by priority
+5. Can set recursive tasks
+ +**Cons:**
+1. Need to click frequently to enter or edit a task
+2. Need to remember lots of shortcuts, so user is less likely to use them
+3. Has a cluttered interface, which is not intuitive
+4. Does not support calendar view in-house
+ +#### Google Calendar +**Pros:**
+1. Can add in public holidays
+2. Can update in terms of time zone
+3. Can customize background picture
+ +**Cons:**
+1. Does not have flexibility in viewing the calendar
+2. Has too much information in 1 page in application
+ +#### Any.do +**Pros:**
+1. Displays a reminder when the task is almost due (set in advance)
+2. Able to clear all tasks in one go
+3. Do a daily review at the start/end of day
+4. Arranges tasks by today, tomorrow, someday
+ +**Cons:**
+1. Requires internet connection
+2. Has no support for recurring tasks
\ No newline at end of file diff --git a/docs/LearningOutcomes.md b/docs/LearningOutcomes.md index 5ee57072a8d8..553a23732037 100644 --- a/docs/LearningOutcomes.md +++ b/docs/LearningOutcomes.md @@ -29,7 +29,7 @@ facilitate communication between event creators and event consumers. ## Use API Design `[LO-ApiDesign]` -Note how components of AddressBook have well-defined APIs. For example, the API of the `Logic` component +Note how components of TaskManager have well-defined APIs. For example, the API of the `Logic` component is given in the [`Logic.java`](../src/main/java/seedu/address/logic/Logic.java)
@@ -41,7 +41,7 @@ is given in the [`Logic.java`](../src/main/java/seedu/address/logic/Logic.java) ## Use Assertions `[LO-Assertions]` -Note how the AddressBook app uses Java `assert`s to verify assumptions. +Note how the TaskManager app uses Java `assert`s to verify assumptions. **Resources** * [Programming With Assertions](http://docs.oracle.com/javase/6/docs/technotes/guides/language/assert.html) - a @@ -52,13 +52,13 @@ Note how the AddressBook app uses Java `assert`s to verify assumptions. * Make sure assertions are enabled in Eclipse by forcing an assertion failure (e.g. add `assert false;` somewhere in the code and run the code to ensure the runtime reports an assertion failure). - * Add more assertions to AddressBook as you see fit. + * Add more assertions to TaskManager as you see fit. ------------------------------------------------------------------------------------------------------ ## Use Logging `[LO-Logging]` -Note [how the AddressBook app uses Java's `java.util.log` package to do logging](DeveloperGuide.md#logging). +Note [how the TaskManager app uses Java's `java.util.log` package to do logging](DeveloperGuide.md#logging). **Resources** * Tutorials @@ -71,17 +71,17 @@ Note [how the AddressBook app uses Java's `java.util.log` package to do logging] * [Base 22 Java Logging Standards and Guidelines](https://wiki.base22.com/display/btg/Java+Logging+Standards+and+Guidelines) #### Exercise: Add more logging - Add more logging to AddressBook as you see fit. + Add more logging to TaskManager as you see fit. ------------------------------------------------------------------------------------------------------ ## Use Defensive Coding `[LO-DefensiveCoding]` - Note how AddressBook uses the `ReadOnly*` interfaces to prevent objects being modified by clients who are not + Note how TaskManager uses the `ReadOnly*` interfaces to prevent objects being modified by clients who are not supposed to modify them. #### Exercise: identify more places for defensive coding - Analyze the AddressBook code/design to identify, + Analyze the TaskManager code/design to identify, * where defensive coding is used * where the code can be more defensive @@ -89,7 +89,7 @@ Note [how the AddressBook app uses Java's `java.util.log` package to do logging] ## Use Build Automation `[LO-BuildAutomation]` -Note [how the AddressBook app uses Gradle to automate build tasks](UsingGradle.md). +Note [how the TaskManager app uses Gradle to automate build tasks](UsingGradle.md). **Resources** * Tutorials @@ -108,7 +108,7 @@ Note [how the AddressBook app uses Gradle to automate build tasks](UsingGradle.m ## Use Continuous Integration `[LO-ContinuousIntegration]` -Note [how the AddressBook app uses Travis to perform Continuous Integration](UsingTravis.md). +Note [how the TaskManager app uses Travis to perform Continuous Integration](UsingTravis.md). **Resources** * Tutorials diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 0cf4b84f7470..00d312dfdac5 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -1,134 +1,259 @@ # User Guide - +* [Introduction](#introduction) * [Quick Start](#quick-start) * [Features](#features) * [FAQ](#faq) * [Command Summary](#command-summary) +* [Appendix A](#appendix-a) +* [Appendix B](#appendix-b) + +//@@author A0142073R +## Introduction +Are you having a hard time remembering all the work you have to do? Do you have trouble finding a task manager that suits your preference for keyboard input? Well, worry no more, Taskell is here for you!
+Taskell will be your personal secretary. It will keep track of your daily tasks and remind you of any important dates and deadlines. What distinguishes Taskell from other task managers is that Taskell only requires a single line of command for every task input. This means that you can record each one of your tasks with just a single statement. You will no longer have to use a mouse if you do not wish to.
+Ready to begin life anew with a more efficient task manager? Read on to find out more! +//@@author ## Quick Start -0. Ensure you have Java version `1.8.0_60` or later installed in your Computer.
- > Having any Java 8 version is not enough.
- This app will not work with earlier versions of Java 8. - -1. Download the latest `addressbook.jar` from the [releases](../../../releases) tab. -2. Copy the file to the folder you want to use as the home folder for your Address Book. -3. Double-click the file to start the app. The GUI should appear in a few seconds. - > - -4. Type the command in the command box and press Enter to execute it.
- e.g. typing **`help`** and pressing Enter will open the help window. -5. Some example commands you can try: - * **`list`** : lists all contacts - * **`add`**` John Doe p/98765432 e/johnd@gmail.com a/John street, block 123, #01-01` : - adds a contact named `John Doe` to the Address Book. - * **`delete`**` 3` : deletes the 3rd contact shown in the current list - * **`exit`** : exits the app -6. Refer to the [Features](#features) section below for details of each command.
+Step 1: Ensure you have Java version `1.8.0_60` or later installed in your Computer.
+ + > Having any Java 8 version is not enough.
+ This application will not work with earlier versions of Java 8. + +Step 2: Download the latest `taskell.jar` from here.
+

+Step 3: Copy the file to the folder you want to use as the home folder for your Task Manager.
+Step 4: Double-click the file to start the application. The GUI should appear in a few seconds.
+

+Diagram 1: A screenshot of the Graphical User Interface (GUI)
+
Step 5: Type the relevant command in the command box and press Enter to execute it.
+Step 6: Some example commands you can try:
+ * **`list`** : displays all contacts + * **`add`** buy MA1101R textbook today : adds a task called buy MA1101R textbook to be done by today. + * **`delete`** 3 : deletes the 3rd task shown in the current list + * **`exit`** : exits the application
+Refer to the [Features](#features) section below for details of each command.
## Features -> **Command Format** -> * Words in `UPPER_CASE` are the parameters. -> * Items in `SQUARE_BRACKETS` are optional. -> * Items with `...` after them can have multiple instances. -> * The order of parameters is fixed. +This section shows the different commands that you can use in Taskell. Words that are in UPPER_CASE are parameters. These parameters have to be in the order stated below. Words that are in italics are used to identify the parameters while words enclosed in SQUARE_BRACKETS are optional. INDEX refers to the index number shown in the most recent listing. + +#### Viewing list of commands : `help` + +To open the help window
+ Format: `help` + + +//@@ author A0139257X +#### Adding a task: `add` +To add a floating task
+Format: +- `add` TASK
+Example: `add` read Harry Potter Book
+ +To add a deadline task
+> Please refer to Appendix A and B for date and time format respectively that Taskell supports.
-#### Viewing help : `help` -Format: `help` +Formats: +- `add` TASK by [DATE]
+Example: add buy textbook by today
+- `add` TASK by [TIME]
+Example: add visit Sandy at her house by the seaside by 3.35pm
+- `add` TASK by [DATE] by [TIME]
+Example: `add` do lab homework by Friday by 7pm -> Help is also shown if you enter an incorrect command e.g. `abcd` +To have a greater flexibility in the command format, Taskell supports a few natural variation such as on and at.
+ +- `add ` TASK on [DATE]
+Example: `add ` go for meeting on monday
+- `add ` TASK at [TIME]
+Example: `add ` go for meeting at 3pm
+- `add ` TASK on [DATE] at [TIME]
+Example: `add ` go for meeting on Sunday at 3pm
+- `add ` TASK on [DATE] by[TIME]
+Example: `add ` go for meeting on 1-jan by 3pm
+ +To add an event task
+Formats: +- `add ` TASK on [DATE] startat [TIME] endat [TIME]
+Example: `add ` schedule meeting on Thursday startat 1pm endat 9pm
+ +- `add ` TASK startat [TIME]
+Example: `add ` concert by 2am band startat 7pm
+ +- `add ` TASK endat [TIME]
+Example: `add ` netball training endat 7pm
+//@@ author +//@@ author A0148004Rz +#### Listing tasks : `list` +Formats: +- `list`
+Displays a list of uncompleted tasks.
+- `list-all`
+Displays a list of all tasks.
+- `list-date` DATE
+Displays a list of all the tasks due on the specific date.
+- `list-done`
+Displays a list of completed tasks.
+- `list-priority` PRIORITY
+Displays a list of tasks with given priority.
+

+Diagram 2: Displays all the tasks to be done by today after typing list-date. +//@@ author + + +#### Finding tasks: `find` +You can use the find command to view tasks with specific keywords.
+Formats:
+-`find KEYWORD [MORE_KEYWORDS]`
+Displays a list of tasks with description or tags that match all the keywords.
+Example: `find banana milk essay`
+This returns all tasks with description or tags that match all keywords `banana`, `milk`, and `essay`.
+ +-`find-tag TAG [MORE_TAGS]`
+Displays list of tasks with the same tags.
+Example: `find homework essay cs2103`
+This returns any task with either tag `homework`, `essay`, or `cs2103`.
+

+

-#### Adding a person: `add` -Adds a person to the address book
-Format: `add NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]...` - -> Persons can have any number of tags (including 0) - -Examples: -* `add John Doe p/98765432 e/johnd@gmail.com a/John street, block 123, #01-01` -* `add Betsy Crowe p/1234567 e/betsycrowe@gmail.com a/Newgate Prison t/criminal t/friend` - -#### Listing all persons : `list` -Shows a list of all persons in the address book.
-Format: `list` - -#### Finding all persons containing any keyword in their name: `find` -Finds persons whose names contain any of the given keywords.
-Format: `find KEYWORD [MORE_KEYWORDS]` - -> * The search is case sensitive. e.g `hans` will not match `Hans` -> * The order of the keywords does not matter. e.g. `Hans Bo` will match `Bo Hans` -> * Only the name is searched. -> * Only full words will be matched e.g. `Han` will not match `Hans` -> * Persons matching at least one keyword will be returned (i.e. `OR` search). - e.g. `Hans` will match `Hans Bo` - -Examples: -* `find John`
- Returns `John Doe` but not `john` -* `find Betsy Tim John`
- Returns Any person having names `Betsy`, `Tim`, or `John` - -#### Deleting a person : `delete` -Deletes the specified person from the address book. Irreversible.
-Format: `delete INDEX` - -> Deletes the person at the specified `INDEX`. - The index refers to the index number shown in the most recent listing.
- The index **must be a positive integer** 1, 2, 3, ... - -Examples: -* `list`
- `delete 2`
- Deletes the 2nd person in the address book. -* `find Betsy`
- `delete 1`
- Deletes the 1st person in the results of the `find` command. - -#### Select a person : `select` -Selects the person identified by the index number used in the last person listing.
-Format: `select INDEX` - -> Selects the person and loads the Google search page the person at the specified `INDEX`. - The index refers to the index number shown in the most recent listing.
- The index **must be a positive integer** 1, 2, 3, ... - -Examples: -* `list`
- `select 2`
- Selects the 2nd person in the address book. -* `find Betsy`
- `select 1`
- Selects the 1st person in the results of the `find` command. - -#### Clearing all entries : `clear` -Clears all entries from the address book.
-Format: `clear` + + +Diagram 3 and 4: Keying in �find report� displays list of tasks with report as one of the keywords in task description + +> Take Note!
+> * The order of the keywords does not matter. e.g. `chicken egg` will match `egg chicken`. +> * Full words will be matched e.g. `chicken` will match `chickens`. +> * Tasks matching at least one keyword will be returned (i.e. `OR` search). + e.g. `chicken` will match `chicken duck` + + + +#### Reverting previous action : `undo` +If you wish to undo your previous actions, Enter list-undo to see a list of +previous commands that can be undone.
+Then use undo command at the specified INDEX in the list.
+Format: `undo INDEX`
+Example: `list-undo`, then `undo 3`, will undo third command in command history. + + + +#### Deleting a task : `delete` +To delete a task, use the delete command. This command deletes the task at a specified INDEX. The index refers to the index number shown in the most recent listing.
+Format: `delete INDEX`
+

+Picture 5 and 6: Entering `delete 1 will delete "Arrange meeting with XYZ company".`
+ +Example: `find violin, then delete 1
+This deletes the 1st task in the results of the find command. +======= +Format: `delete` INDEX
+

+Diagram 4: Deletes the first task in the list.
+>>>>>>> 9d3e940bc97dc4a324dca16582e28cdbfc8c5ebc + +#### Marking a task as completed: `done` +Format: `done` INDEX
+Example: `done` 1
+This adds the 1st task as completed and moves it to the completed list.
+ +#### Editing a task : `edit` +To edit the description of a task
+Formats: +- `edit-desc` INDEX NEWDESCRIPTION
+- `edit-name` INDEX NEWDESCRIPTION
+

+Diagram 5: Edits the 2nd task on the list
+ +To edit the time of a task
+Formats: +- `edit-startTime` INDEX NEWTIME
+- `edit-endTime` INDEX NEWTIME
+ +To edit the date of a task
+Formats: +- `edit-startDate` INDEX NEWDATE
+- `edit-endDate` INDEX NEWDATE
+ +To edit the priority of a task
+Formats: +- `edit-priority` INDEX NEWDATE
+ + + +#### Showing calendar view : `calendar` or `cal` +You can view the calendar for the week on right panel to refer to the dates when adding tasks and scheduling events.
+To save time, Taskell has a short form command `cal`.
+Format: `calendar`
+ +#### Saving the information in Taskell : `save` +You can specify the path of a folder to store Taskell's data file. Please note that you should have permissions to access the folder.
+If the specified directory is valid but the file is missing, for example if command is `save C:/Users/Jim/Documents/chicken`, and `C:/Users/Jim/Documents/chicken` is valid +but `chicken` file is not created, Taskell will create the file for you.
+Format: `save FOLDERPATH`
+Example: `save C:/Users/Jim/Documents` + + + +#### Clearing all entries : `clear` +Format: `clear`
#### Exiting the program : `exit` -Exits the program.
Format: `exit` -#### Saving the data -Address book data are saved in the hard disk automatically after any command that changes the data.
-There is no need to save manually. - ## FAQ **Q**: How do I transfer my data to another Computer?
-**A**: Install the app in the other computer and overwrite the empty data file it creates with - the file that contains the data of your previous Address Book folder. +**A**: Install the application in the other computer and overwrite the empty data file it creates with the file that contains the data of your previous Taskell folder. ## Command Summary Command | Format -------- | :-------- -Add | `add NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]...` +Add Floating Task | `add` TASK ITEM +Add Event | `add` TASK ITEM by [DATE] +Add Event | `add` TASK ITEM by [TIME] +Add Event With Deadline | `add` TASK ITEM by [DATE][TIME] +Calendar View | `calendar` or `cal` Clear | `clear` -Delete | `delete INDEX` -Find | `find KEYWORD [MORE_KEYWORDS]` -List | `list` +Complete | `done` INDEX +Delete | `delete` INDEX +Edit | `edit` INDEX NEWTASK +Find | `find` KEYWORD [MORE_KEYWORDS] +Find Tag | `find-tag` KEYWORD [MORE_KEYWORDS] Help | `help` -Select | `select INDEX` +List Incomplete Tasks| `list` +List All Tasks | `list-all` +List Given Day | `list-date` [DATE] +List Tasks Done | `list-done` [DONE] +List Undo | `list-undo` +Undo | `undo INDEX` +Calendar View | `calendar` or `cal` + +## Appendix A + +Supported Date Format | Example +-------- | :-------- +DD-MM-YY |1-1-16 +DD-MM-YY | 1-1-2016 +DD-MM-YY | 1-Jan-2016 +DD-MM-YY | 1-January-2016 +DD-MM-YY | 1.Jan.2016 +DD-MM-YY | 1.January.2016 +MM-YY | july-16 +MM | july +day | today +day | tdy +day | tmr +day | tomorrow +day | thursday + +## Appendix B + +Supported Time Format | Example +-------- | :-------- +12hour |1pm +12hour |12am +12hour |111.45pm diff --git a/docs/UsingGradle.md b/docs/UsingGradle.md index 578c5f8634c2..e46bf10ffa2c 100644 --- a/docs/UsingGradle.md +++ b/docs/UsingGradle.md @@ -35,10 +35,10 @@ Gradle commands look like this: ## Creating the JAR file * **`shadowJar`**
- Creates the `addressbook.jar` file in the `build/jar` folder, _if the current file is outdated_.
+ Creates the `taskell.jar` file in the `build/jar` folder, _if the current file is outdated_.
e.g. `./gradlew shadowJar` - > To force Gradle to create the JAR file even if the current one is up-to-date, you can '`clean`' first.
+ > To force Gradle to create the JAR file even if the current one is up-to-startDate, you can '`clean`' first.
e.g. `./gradlew clean shadowJar` **Note: Why do we create a fat JAR?** @@ -56,7 +56,7 @@ If we package only our own class files into the JAR file, it will not work prope Runs all tests in the `guitests` package * **`nonGuiTests`**
- Runs all non-GUI tests in the `seedu.address` package + Runs all non-GUI tests in the `seedu.taskell` package * **`headless`**
Sets the test mode as _headless_. diff --git a/docs/diagrams/Diagrams.pptx b/docs/diagrams/Diagrams.pptx index 3c28abe9c1d3..666532dc77c5 100644 Binary files a/docs/diagrams/Diagrams.pptx and b/docs/diagrams/Diagrams.pptx differ diff --git a/docs/gif/listToday.gif b/docs/gif/listToday.gif new file mode 100644 index 000000000000..45474411e6b9 Binary files /dev/null and b/docs/gif/listToday.gif differ diff --git a/docs/images/AddTaskSdForLogic.png b/docs/images/AddTaskSdForLogic.png new file mode 100644 index 000000000000..a99c36e0cb02 Binary files /dev/null and b/docs/images/AddTaskSdForLogic.png 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..da681d1a5e18 Binary files /dev/null and b/docs/images/DeleteTaskSdForLogic.png differ diff --git a/docs/images/GUI.png b/docs/images/GUI.png new file mode 100644 index 000000000000..33d5f2cf6f84 Binary files /dev/null and b/docs/images/GUI.png differ diff --git a/docs/images/Icon.png b/docs/images/Icon.png new file mode 100644 index 000000000000..0e8fab8aaa1c Binary files /dev/null and b/docs/images/Icon.png differ diff --git a/docs/images/Jazlyn.png b/docs/images/Jazlyn.png new file mode 100644 index 000000000000..068259585db7 Binary files /dev/null and b/docs/images/Jazlyn.png differ diff --git a/docs/images/ListToday.png b/docs/images/ListToday.png new file mode 100644 index 000000000000..72349c3b0291 Binary files /dev/null and b/docs/images/ListToday.png differ diff --git a/docs/images/Mamtha.png b/docs/images/Mamtha.png new file mode 100644 index 000000000000..abdfc2344d1d Binary files /dev/null and b/docs/images/Mamtha.png differ diff --git a/docs/images/Marcus.png b/docs/images/Marcus.png new file mode 100644 index 000000000000..c441a207a046 Binary files /dev/null and b/docs/images/Marcus.png differ diff --git a/docs/images/ModelClassDiagram.png b/docs/images/ModelClassDiagram.png index 8cdf11ec93a1..3652ec0f8b6c 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..b8816441f7bd 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..88c40b585ce4 Binary files /dev/null and b/docs/images/SDforDeleteTaskEventHandling.png differ diff --git a/docs/images/StorageClassDiagram.png b/docs/images/StorageClassDiagram.png index 7a4cd2700cbf..83b2098890aa 100644 Binary files a/docs/images/StorageClassDiagram.png and b/docs/images/StorageClassDiagram.png differ diff --git a/docs/images/Ui.png b/docs/images/Ui.png index 7121a50a442a..d6069e23ca7d 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 index 459245e267af..0ad175d067c2 100644 Binary files a/docs/images/UiClassDiagram.png and b/docs/images/UiClassDiagram.png differ diff --git a/docs/images/ZhuoLin.png b/docs/images/ZhuoLin.png new file mode 100644 index 000000000000..d79bb8f1ca37 Binary files /dev/null and b/docs/images/ZhuoLin.png differ diff --git a/docs/images/delete1.png b/docs/images/delete1.png new file mode 100644 index 000000000000..b72a91bd3ef5 Binary files /dev/null and b/docs/images/delete1.png differ diff --git a/docs/images/editCmd.png b/docs/images/editCmd.png new file mode 100644 index 000000000000..002469c17a3a Binary files /dev/null and b/docs/images/editCmd.png differ diff --git a/docs/images/findReport.png b/docs/images/findReport.png new file mode 100644 index 000000000000..f06ee74a631e Binary files /dev/null and b/docs/images/findReport.png differ diff --git a/docs/images/findReportResult.png b/docs/images/findReportResult.png new file mode 100644 index 000000000000..4e00fd25f875 Binary files /dev/null and b/docs/images/findReportResult.png differ 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/DeleteCommand.java b/src/main/java/seedu/address/logic/commands/DeleteCommand.java deleted file mode 100644 index 1bfebe8912a8..000000000000 --- a/src/main/java/seedu/address/logic/commands/DeleteCommand.java +++ /dev/null @@ -1,50 +0,0 @@ -package seedu.address.logic.commands; - -import seedu.address.commons.core.Messages; -import seedu.address.commons.core.UnmodifiableObservableList; -import seedu.address.model.person.ReadOnlyPerson; -import seedu.address.model.person.UniquePersonList.PersonNotFoundException; - -/** - * Deletes a person identified using it's last displayed index from the address book. - */ -public class DeleteCommand extends Command { - - public static final String COMMAND_WORD = "delete"; - - public static final String MESSAGE_USAGE = COMMAND_WORD - + ": Deletes the person identified by the index number used in the last person listing.\n" - + "Parameters: INDEX (must be a positive integer)\n" - + "Example: " + COMMAND_WORD + " 1"; - - public static final String MESSAGE_DELETE_PERSON_SUCCESS = "Deleted Person: %1$s"; - - public final int targetIndex; - - public DeleteCommand(int targetIndex) { - this.targetIndex = targetIndex; - } - - - @Override - public CommandResult execute() { - - UnmodifiableObservableList lastShownList = model.getFilteredPersonList(); - - if (lastShownList.size() < targetIndex) { - indicateAttemptToExecuteIncorrectCommand(); - return new CommandResult(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); - } - - ReadOnlyPerson personToDelete = lastShownList.get(targetIndex - 1); - - try { - model.deletePerson(personToDelete); - } catch (PersonNotFoundException pnfe) { - assert false : "The target person cannot be missing"; - } - - return new CommandResult(String.format(MESSAGE_DELETE_PERSON_SUCCESS, personToDelete)); - } - -} diff --git a/src/main/java/seedu/address/logic/commands/FindCommand.java b/src/main/java/seedu/address/logic/commands/FindCommand.java deleted file mode 100644 index 1d61bf6cc857..000000000000 --- a/src/main/java/seedu/address/logic/commands/FindCommand.java +++ /dev/null @@ -1,30 +0,0 @@ -package seedu.address.logic.commands; - -import java.util.Set; - -/** - * Finds and lists all persons in address book whose name contains any of the argument keywords. - * Keyword matching is case sensitive. - */ -public class FindCommand extends Command { - - public static final String COMMAND_WORD = "find"; - - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all persons whose names contain any of " - + "the specified keywords (case-sensitive) and displays them as a list with index numbers.\n" - + "Parameters: KEYWORD [MORE_KEYWORDS]...\n" - + "Example: " + COMMAND_WORD + " alice bob charlie"; - - private final Set keywords; - - public FindCommand(Set keywords) { - this.keywords = keywords; - } - - @Override - public CommandResult execute() { - model.updateFilteredPersonList(keywords); - return new CommandResult(getMessageForPersonListShownSummary(model.getFilteredPersonList().size())); - } - -} diff --git a/src/main/java/seedu/address/logic/commands/ListCommand.java b/src/main/java/seedu/address/logic/commands/ListCommand.java deleted file mode 100644 index 9bdd457a1b01..000000000000 --- a/src/main/java/seedu/address/logic/commands/ListCommand.java +++ /dev/null @@ -1,20 +0,0 @@ -package seedu.address.logic.commands; - - -/** - * Lists all persons in the address book to the user. - */ -public class ListCommand extends Command { - - public static final String COMMAND_WORD = "list"; - - public static final String MESSAGE_SUCCESS = "Listed all persons"; - - public ListCommand() {} - - @Override - public CommandResult execute() { - model.updateFilteredListToShowAll(); - return new CommandResult(MESSAGE_SUCCESS); - } -} diff --git a/src/main/java/seedu/address/logic/commands/SelectCommand.java b/src/main/java/seedu/address/logic/commands/SelectCommand.java deleted file mode 100644 index 9ca0551f1951..000000000000 --- a/src/main/java/seedu/address/logic/commands/SelectCommand.java +++ /dev/null @@ -1,44 +0,0 @@ -package seedu.address.logic.commands; - -import seedu.address.commons.core.EventsCenter; -import seedu.address.commons.core.Messages; -import seedu.address.commons.events.ui.JumpToListRequestEvent; -import seedu.address.commons.core.UnmodifiableObservableList; -import seedu.address.model.person.ReadOnlyPerson; - -/** - * Selects a person identified using it's last displayed index from the address book. - */ -public class SelectCommand extends Command { - - public final int targetIndex; - - public static final String COMMAND_WORD = "select"; - - public static final String MESSAGE_USAGE = COMMAND_WORD - + ": Selects the person identified by the index number used in the last person listing.\n" - + "Parameters: INDEX (must be a positive integer)\n" - + "Example: " + COMMAND_WORD + " 1"; - - public static final String MESSAGE_SELECT_PERSON_SUCCESS = "Selected Person: %1$s"; - - public SelectCommand(int targetIndex) { - this.targetIndex = targetIndex; - } - - @Override - public CommandResult execute() { - - UnmodifiableObservableList lastShownList = model.getFilteredPersonList(); - - if (lastShownList.size() < targetIndex) { - indicateAttemptToExecuteIncorrectCommand(); - return new CommandResult(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); - } - - EventsCenter.getInstance().post(new JumpToListRequestEvent(targetIndex - 1)); - return new CommandResult(String.format(MESSAGE_SELECT_PERSON_SUCCESS, targetIndex)); - - } - -} diff --git a/src/main/java/seedu/address/logic/parser/Parser.java b/src/main/java/seedu/address/logic/parser/Parser.java deleted file mode 100644 index 959b2cd0383c..000000000000 --- a/src/main/java/seedu/address/logic/parser/Parser.java +++ /dev/null @@ -1,192 +0,0 @@ -package seedu.address.logic.parser; - -import seedu.address.logic.commands.*; -import seedu.address.commons.util.StringUtil; -import seedu.address.commons.exceptions.IllegalValueException; - -import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; - -/** - * Parses user input. - */ -public class Parser { - - /** - * Used for initial separation of command word and args. - */ - private static final Pattern BASIC_COMMAND_FORMAT = Pattern.compile("(?\\S+)(?.*)"); - - private static final Pattern PERSON_INDEX_ARGS_FORMAT = Pattern.compile("(?.+)"); - - private static final Pattern KEYWORDS_ARGS_FORMAT = - Pattern.compile("(?\\S+(?:\\s+\\S+)*)"); // one or more keywords separated by whitespace - - private static final Pattern PERSON_DATA_ARGS_FORMAT = // '/' forward slashes are reserved for delimiter prefixes - Pattern.compile("(?[^/]+)" - + " (?p?)p/(?[^/]+)" - + " (?p?)e/(?[^/]+)" - + " (?p?)a/(?
[^/]+)" - + "(?(?: t/[^/]+)*)"); // variable number of tags - - public Parser() {} - - /** - * Parses user input into command for execution. - * - * @param userInput full user input string - * @return the command based on the user input - */ - public Command parseCommand(String userInput) { - final Matcher matcher = BASIC_COMMAND_FORMAT.matcher(userInput.trim()); - if (!matcher.matches()) { - return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE)); - } - - final String commandWord = matcher.group("commandWord"); - final String arguments = matcher.group("arguments"); - switch (commandWord) { - - case AddCommand.COMMAND_WORD: - return prepareAdd(arguments); - - case SelectCommand.COMMAND_WORD: - return prepareSelect(arguments); - - case DeleteCommand.COMMAND_WORD: - return prepareDelete(arguments); - - case ClearCommand.COMMAND_WORD: - return new ClearCommand(); - - case FindCommand.COMMAND_WORD: - return prepareFind(arguments); - - case ListCommand.COMMAND_WORD: - return new ListCommand(); - - case ExitCommand.COMMAND_WORD: - return new ExitCommand(); - - case HelpCommand.COMMAND_WORD: - return new HelpCommand(); - - default: - return new IncorrectCommand(MESSAGE_UNKNOWN_COMMAND); - } - } - - /** - * Parses arguments in the context of the add person command. - * - * @param args full command args string - * @return the prepared command - */ - private Command prepareAdd(String args){ - final Matcher matcher = PERSON_DATA_ARGS_FORMAT.matcher(args.trim()); - // Validate arg string format - if (!matcher.matches()) { - return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); - } - try { - return new AddCommand( - matcher.group("name"), - matcher.group("phone"), - matcher.group("email"), - matcher.group("address"), - getTagsFromArgs(matcher.group("tagArguments")) - ); - } catch (IllegalValueException ive) { - return new IncorrectCommand(ive.getMessage()); - } - } - - /** - * Extracts the new person's tags from the add command's tag arguments string. - * Merges duplicate tag strings. - */ - private static Set getTagsFromArgs(String tagArguments) throws IllegalValueException { - // no tags - if (tagArguments.isEmpty()) { - return Collections.emptySet(); - } - // replace first delimiter prefix, then split - final Collection tagStrings = Arrays.asList(tagArguments.replaceFirst(" t/", "").split(" t/")); - return new HashSet<>(tagStrings); - } - - /** - * Parses arguments in the context of the delete person command. - * - * @param args full command args string - * @return the prepared command - */ - private Command prepareDelete(String args) { - - Optional index = parseIndex(args); - if(!index.isPresent()){ - return new IncorrectCommand( - String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE)); - } - - return new DeleteCommand(index.get()); - } - - /** - * Parses arguments in the context of the select person command. - * - * @param args full command args string - * @return the prepared command - */ - private Command prepareSelect(String args) { - Optional index = parseIndex(args); - if(!index.isPresent()){ - return new IncorrectCommand( - String.format(MESSAGE_INVALID_COMMAND_FORMAT, SelectCommand.MESSAGE_USAGE)); - } - - return new SelectCommand(index.get()); - } - - /** - * Returns the specified index in the {@code command} IF a positive unsigned integer is given as the index. - * Returns an {@code Optional.empty()} otherwise. - */ - private Optional parseIndex(String command) { - final Matcher matcher = PERSON_INDEX_ARGS_FORMAT.matcher(command.trim()); - if (!matcher.matches()) { - return Optional.empty(); - } - - String index = matcher.group("targetIndex"); - if(!StringUtil.isUnsignedInteger(index)){ - return Optional.empty(); - } - return Optional.of(Integer.parseInt(index)); - - } - - /** - * Parses arguments in the context of the find person command. - * - * @param args full command args string - * @return the prepared command - */ - private Command prepareFind(String args) { - final Matcher matcher = KEYWORDS_ARGS_FORMAT.matcher(args.trim()); - if (!matcher.matches()) { - return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, - FindCommand.MESSAGE_USAGE)); - } - - // keywords delimited by whitespace - final String[] keywords = matcher.group("keywords").split("\\s+"); - final Set keywordSet = new HashSet<>(Arrays.asList(keywords)); - return new FindCommand(keywordSet); - } - -} \ No newline at end of file diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/seedu/address/model/AddressBook.java deleted file mode 100644 index 298cc1b82ce8..000000000000 --- a/src/main/java/seedu/address/model/AddressBook.java +++ /dev/null @@ -1,163 +0,0 @@ -package seedu.address.model; - -import javafx.collections.ObservableList; -import seedu.address.model.person.Person; -import seedu.address.model.person.ReadOnlyPerson; -import seedu.address.model.person.UniquePersonList; -import seedu.address.model.tag.Tag; -import seedu.address.model.tag.UniqueTagList; - -import java.util.*; -import java.util.stream.Collectors; - -/** - * Wraps all data at the address-book level - * Duplicates are not allowed (by .equals comparison) - */ -public class AddressBook implements ReadOnlyAddressBook { - - private final UniquePersonList persons; - private final UniqueTagList tags; - - { - persons = new UniquePersonList(); - tags = new UniqueTagList(); - } - - public AddressBook() {} - - /** - * Persons and Tags are copied into this addressbook - */ - public AddressBook(ReadOnlyAddressBook toBeCopied) { - this(toBeCopied.getUniquePersonList(), toBeCopied.getUniqueTagList()); - } - - /** - * Persons and Tags are copied into this addressbook - */ - public AddressBook(UniquePersonList persons, UniqueTagList tags) { - resetData(persons.getInternalList(), tags.getInternalList()); - } - - public static ReadOnlyAddressBook getEmptyAddressBook() { - return new AddressBook(); - } - -//// list overwrite operations - - public ObservableList getPersons() { - return persons.getInternalList(); - } - - public void setPersons(List persons) { - this.persons.getInternalList().setAll(persons); - } - - public void setTags(Collection tags) { - this.tags.getInternalList().setAll(tags); - } - - public void resetData(Collection newPersons, Collection newTags) { - setPersons(newPersons.stream().map(Person::new).collect(Collectors.toList())); - setTags(newTags); - } - - public void resetData(ReadOnlyAddressBook newData) { - resetData(newData.getPersonList(), newData.getTagList()); - } - -//// person-level operations - - /** - * Adds a person to the address book. - * Also checks the new person's tags and updates {@link #tags} with any new tags found, - * and updates the Tag objects in the person to point to those in {@link #tags}. - * - * @throws UniquePersonList.DuplicatePersonException if an equivalent person already exists. - */ - public void addPerson(Person p) throws UniquePersonList.DuplicatePersonException { - syncTagsWithMasterList(p); - persons.add(p); - } - - /** - * Ensures that every tag in this person: - * - exists in the master list {@link #tags} - * - points to a Tag object in the master list - */ - private void syncTagsWithMasterList(Person person) { - final UniqueTagList personTags = person.getTags(); - tags.mergeFrom(personTags); - - // Create map with values = tag object references in the master list - final Map masterTagObjects = new HashMap<>(); - for (Tag tag : tags) { - masterTagObjects.put(tag, tag); - } - - // Rebuild the list of person tags using references from the master list - final Set commonTagReferences = new HashSet<>(); - for (Tag tag : personTags) { - commonTagReferences.add(masterTagObjects.get(tag)); - } - person.setTags(new UniqueTagList(commonTagReferences)); - } - - public boolean removePerson(ReadOnlyPerson key) throws UniquePersonList.PersonNotFoundException { - if (persons.remove(key)) { - return true; - } else { - throw new UniquePersonList.PersonNotFoundException(); - } - } - -//// tag-level operations - - public void addTag(Tag t) throws UniqueTagList.DuplicateTagException { - tags.add(t); - } - -//// util methods - - @Override - public String toString() { - return persons.getInternalList().size() + " persons, " + tags.getInternalList().size() + " tags"; - // TODO: refine later - } - - @Override - public List getPersonList() { - return Collections.unmodifiableList(persons.getInternalList()); - } - - @Override - public List getTagList() { - return Collections.unmodifiableList(tags.getInternalList()); - } - - @Override - public UniquePersonList getUniquePersonList() { - return this.persons; - } - - @Override - public UniqueTagList getUniqueTagList() { - return this.tags; - } - - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof AddressBook // instanceof handles nulls - && this.persons.equals(((AddressBook) other).persons) - && this.tags.equals(((AddressBook) other).tags)); - } - - @Override - public int hashCode() { - // use this method for custom fields hashing instead of implementing your own - return Objects.hash(persons, tags); - } -} diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java deleted file mode 100644 index d14a27a93b5e..000000000000 --- a/src/main/java/seedu/address/model/Model.java +++ /dev/null @@ -1,35 +0,0 @@ -package seedu.address.model; - -import seedu.address.commons.core.UnmodifiableObservableList; -import seedu.address.model.person.Person; -import seedu.address.model.person.ReadOnlyPerson; -import seedu.address.model.person.UniquePersonList; - -import java.util.Set; - -/** - * The API of the Model component. - */ -public interface Model { - /** Clears existing backing model and replaces with the provided new data. */ - void resetData(ReadOnlyAddressBook newData); - - /** Returns the AddressBook */ - ReadOnlyAddressBook getAddressBook(); - - /** Deletes the given person. */ - void deletePerson(ReadOnlyPerson target) throws UniquePersonList.PersonNotFoundException; - - /** Adds the given person */ - void addPerson(Person person) throws UniquePersonList.DuplicatePersonException; - - /** Returns the filtered person list as an {@code UnmodifiableObservableList} */ - UnmodifiableObservableList getFilteredPersonList(); - - /** Updates the filter of the filtered person list to show all persons */ - void updateFilteredListToShowAll(); - - /** Updates the filter of the filtered person list to filter by the given keywords*/ - void updateFilteredPersonList(Set keywords); - -} diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java deleted file mode 100644 index 869226d02bf1..000000000000 --- a/src/main/java/seedu/address/model/ModelManager.java +++ /dev/null @@ -1,153 +0,0 @@ -package seedu.address.model; - -import javafx.collections.transformation.FilteredList; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.core.UnmodifiableObservableList; -import seedu.address.commons.util.StringUtil; -import seedu.address.commons.events.model.AddressBookChangedEvent; -import seedu.address.commons.core.ComponentManager; -import seedu.address.model.person.Person; -import seedu.address.model.person.ReadOnlyPerson; -import seedu.address.model.person.UniquePersonList; -import seedu.address.model.person.UniquePersonList.PersonNotFoundException; - -import java.util.Set; -import java.util.logging.Logger; - -/** - * Represents the in-memory model of the address book data. - * All changes to any model should be synchronized. - */ -public class ModelManager extends ComponentManager implements Model { - private static final Logger logger = LogsCenter.getLogger(ModelManager.class); - - private final AddressBook addressBook; - private final FilteredList filteredPersons; - - /** - * Initializes a ModelManager with the given AddressBook - * AddressBook and its variables should not be null - */ - public ModelManager(AddressBook src, UserPrefs userPrefs) { - super(); - assert src != null; - assert userPrefs != null; - - logger.fine("Initializing with address book: " + src + " and user prefs " + userPrefs); - - addressBook = new AddressBook(src); - filteredPersons = new FilteredList<>(addressBook.getPersons()); - } - - public ModelManager() { - this(new AddressBook(), new UserPrefs()); - } - - public ModelManager(ReadOnlyAddressBook initialData, UserPrefs userPrefs) { - addressBook = new AddressBook(initialData); - filteredPersons = new FilteredList<>(addressBook.getPersons()); - } - - @Override - public void resetData(ReadOnlyAddressBook newData) { - addressBook.resetData(newData); - indicateAddressBookChanged(); - } - - @Override - public ReadOnlyAddressBook getAddressBook() { - return addressBook; - } - - /** Raises an event to indicate the model has changed */ - private void indicateAddressBookChanged() { - raise(new AddressBookChangedEvent(addressBook)); - } - - @Override - public synchronized void deletePerson(ReadOnlyPerson target) throws PersonNotFoundException { - addressBook.removePerson(target); - indicateAddressBookChanged(); - } - - @Override - public synchronized void addPerson(Person person) throws UniquePersonList.DuplicatePersonException { - addressBook.addPerson(person); - updateFilteredListToShowAll(); - indicateAddressBookChanged(); - } - - //=========== Filtered Person List Accessors =============================================================== - - @Override - public UnmodifiableObservableList getFilteredPersonList() { - return new UnmodifiableObservableList<>(filteredPersons); - } - - @Override - public void updateFilteredListToShowAll() { - filteredPersons.setPredicate(null); - } - - @Override - public void updateFilteredPersonList(Set keywords){ - updateFilteredPersonList(new PredicateExpression(new NameQualifier(keywords))); - } - - private void updateFilteredPersonList(Expression expression) { - filteredPersons.setPredicate(expression::satisfies); - } - - //========== Inner classes/interfaces used for filtering ================================================== - - interface Expression { - boolean satisfies(ReadOnlyPerson person); - String toString(); - } - - private class PredicateExpression implements Expression { - - private final Qualifier qualifier; - - PredicateExpression(Qualifier qualifier) { - this.qualifier = qualifier; - } - - @Override - public boolean satisfies(ReadOnlyPerson person) { - return qualifier.run(person); - } - - @Override - public String toString() { - return qualifier.toString(); - } - } - - interface Qualifier { - boolean run(ReadOnlyPerson person); - String toString(); - } - - private class NameQualifier implements Qualifier { - private Set nameKeyWords; - - NameQualifier(Set nameKeyWords) { - this.nameKeyWords = nameKeyWords; - } - - @Override - public boolean run(ReadOnlyPerson person) { - return nameKeyWords.stream() - .filter(keyword -> StringUtil.containsIgnoreCase(person.getName().fullName, keyword)) - .findAny() - .isPresent(); - } - - @Override - public String toString() { - return "name=" + String.join(", ", nameKeyWords); - } - } - -} diff --git a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java b/src/main/java/seedu/address/model/ReadOnlyAddressBook.java deleted file mode 100644 index bfca099b1e81..000000000000 --- a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java +++ /dev/null @@ -1,30 +0,0 @@ -package seedu.address.model; - - -import seedu.address.model.person.ReadOnlyPerson; -import seedu.address.model.person.UniquePersonList; -import seedu.address.model.tag.Tag; -import seedu.address.model.tag.UniqueTagList; - -import java.util.List; - -/** - * Unmodifiable view of an address book - */ -public interface ReadOnlyAddressBook { - - UniqueTagList getUniqueTagList(); - - UniquePersonList getUniquePersonList(); - - /** - * Returns an unmodifiable view of persons list - */ - List getPersonList(); - - /** - * Returns an unmodifiable view of tags list - */ - List getTagList(); - -} diff --git a/src/main/java/seedu/address/model/person/Address.java b/src/main/java/seedu/address/model/person/Address.java deleted file mode 100644 index a2bd109c005e..000000000000 --- a/src/main/java/seedu/address/model/person/Address.java +++ /dev/null @@ -1,54 +0,0 @@ -package seedu.address.model.person; - - -import seedu.address.commons.exceptions.IllegalValueException; - -/** - * Represents a Person's address in the address book. - * Guarantees: immutable; is valid as declared in {@link #isValidAddress(String)} - */ -public class Address { - - public static final String MESSAGE_ADDRESS_CONSTRAINTS = "Person addresses can be in any format"; - public static final String ADDRESS_VALIDATION_REGEX = ".+"; - - public final String value; - - /** - * Validates given address. - * - * @throws IllegalValueException if given address string is invalid. - */ - public Address(String address) throws IllegalValueException { - assert address != null; - if (!isValidAddress(address)) { - throw new IllegalValueException(MESSAGE_ADDRESS_CONSTRAINTS); - } - this.value = address; - } - - /** - * Returns true if a given string is a valid person email. - */ - public static boolean isValidAddress(String test) { - return test.matches(ADDRESS_VALIDATION_REGEX); - } - - @Override - public String toString() { - return value; - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof Address // instanceof handles nulls - && this.value.equals(((Address) other).value)); // state check - } - - @Override - public int hashCode() { - return value.hashCode(); - } - -} \ No newline at end of file diff --git a/src/main/java/seedu/address/model/person/Email.java b/src/main/java/seedu/address/model/person/Email.java deleted file mode 100644 index 5da4d1078236..000000000000 --- a/src/main/java/seedu/address/model/person/Email.java +++ /dev/null @@ -1,56 +0,0 @@ -package seedu.address.model.person; - - -import seedu.address.commons.exceptions.IllegalValueException; - -/** - * Represents a Person's phone number in the address book. - * Guarantees: immutable; is valid as declared in {@link #isValidEmail(String)} - */ -public class Email { - - public static final String MESSAGE_EMAIL_CONSTRAINTS = - "Person emails should be 2 alphanumeric/period strings separated by '@'"; - public static final String EMAIL_VALIDATION_REGEX = "[\\w\\.]+@[\\w\\.]+"; - - public final String value; - - /** - * Validates given email. - * - * @throws IllegalValueException if given email address string is invalid. - */ - public Email(String email) throws IllegalValueException { - assert email != null; - email = email.trim(); - if (!isValidEmail(email)) { - throw new IllegalValueException(MESSAGE_EMAIL_CONSTRAINTS); - } - this.value = email; - } - - /** - * Returns if a given string is a valid person email. - */ - public static boolean isValidEmail(String test) { - return test.matches(EMAIL_VALIDATION_REGEX); - } - - @Override - public String toString() { - return value; - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof Email // instanceof handles nulls - && this.value.equals(((Email) other).value)); // state check - } - - @Override - public int hashCode() { - return value.hashCode(); - } - -} diff --git a/src/main/java/seedu/address/model/person/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/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/XmlAdaptedPerson.java b/src/main/java/seedu/address/storage/XmlAdaptedPerson.java deleted file mode 100644 index f2167ec201b4..000000000000 --- a/src/main/java/seedu/address/storage/XmlAdaptedPerson.java +++ /dev/null @@ -1,68 +0,0 @@ -package seedu.address.storage; - -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.person.*; -import seedu.address.model.tag.Tag; -import seedu.address.model.tag.UniqueTagList; - -import javax.xml.bind.annotation.XmlElement; -import java.util.ArrayList; -import java.util.List; - -/** - * JAXB-friendly version of the Person. - */ -public class XmlAdaptedPerson { - - @XmlElement(required = true) - private String name; - @XmlElement(required = true) - private String phone; - @XmlElement(required = true) - private String email; - @XmlElement(required = true) - private String address; - - @XmlElement - private List tagged = new ArrayList<>(); - - /** - * No-arg constructor for JAXB use. - */ - public XmlAdaptedPerson() {} - - - /** - * Converts a given Person into this class for JAXB use. - * - * @param source future changes to this will not affect the created XmlAdaptedPerson - */ - public XmlAdaptedPerson(ReadOnlyPerson source) { - name = source.getName().fullName; - phone = source.getPhone().value; - email = source.getEmail().value; - address = source.getAddress().value; - tagged = new ArrayList<>(); - for (Tag tag : source.getTags()) { - tagged.add(new XmlAdaptedTag(tag)); - } - } - - /** - * Converts this jaxb-friendly adapted person object into the model's Person object. - * - * @throws IllegalValueException if there were any data constraints violated in the adapted person - */ - public Person toModelType() throws IllegalValueException { - final List personTags = new ArrayList<>(); - for (XmlAdaptedTag tag : tagged) { - personTags.add(tag.toModelType()); - } - final Name name = new Name(this.name); - final Phone phone = new Phone(this.phone); - final Email email = new Email(this.email); - final Address address = new Address(this.address); - final UniqueTagList tags = new UniqueTagList(personTags); - return new Person(name, phone, email, address, tags); - } -} diff --git a/src/main/java/seedu/address/storage/XmlAddressBookStorage.java b/src/main/java/seedu/address/storage/XmlAddressBookStorage.java deleted file mode 100644 index 30cb00270cc4..000000000000 --- a/src/main/java/seedu/address/storage/XmlAddressBookStorage.java +++ /dev/null @@ -1,73 +0,0 @@ -package seedu.address.storage; - -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.commons.util.FileUtil; -import seedu.address.model.ReadOnlyAddressBook; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.Optional; -import java.util.logging.Logger; - -/** - * A class to access AddressBook data stored as an xml file on the hard disk. - */ -public class XmlAddressBookStorage implements AddressBookStorage { - - private static final Logger logger = LogsCenter.getLogger(XmlAddressBookStorage.class); - - private String filePath; - - public XmlAddressBookStorage(String filePath){ - this.filePath = filePath; - } - - public String getAddressBookFilePath(){ - return filePath; - } - - /** - * Similar to {@link #readAddressBook()} - * @param filePath location of the data. Cannot be null - * @throws DataConversionException if the file is not in the correct format. - */ - public Optional readAddressBook(String filePath) throws DataConversionException, FileNotFoundException { - assert filePath != null; - - File addressBookFile = new File(filePath); - - if (!addressBookFile.exists()) { - logger.info("AddressBook file " + addressBookFile + " not found"); - return Optional.empty(); - } - - ReadOnlyAddressBook addressBookOptional = XmlFileStorage.loadDataFromSaveFile(new File(filePath)); - - return Optional.of(addressBookOptional); - } - - /** - * Similar to {@link #saveAddressBook(ReadOnlyAddressBook)} - * @param filePath location of the data. Cannot be null - */ - public void saveAddressBook(ReadOnlyAddressBook addressBook, String filePath) throws IOException { - assert addressBook != null; - assert filePath != null; - - File file = new File(filePath); - FileUtil.createIfMissing(file); - XmlFileStorage.saveDataToFile(file, new XmlSerializableAddressBook(addressBook)); - } - - @Override - public Optional readAddressBook() throws DataConversionException, IOException { - return readAddressBook(filePath); - } - - @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException { - saveAddressBook(addressBook, filePath); - } -} diff --git a/src/main/java/seedu/address/ui/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/taskell/MainApp.java similarity index 69% rename from src/main/java/seedu/address/MainApp.java rename to src/main/java/seedu/taskell/MainApp.java index 36dc72a74b7a..2f9ef6b39f87 100644 --- a/src/main/java/seedu/address/MainApp.java +++ b/src/main/java/seedu/taskell/MainApp.java @@ -1,24 +1,27 @@ -package seedu.address; +package seedu.taskell; import com.google.common.eventbus.Subscribe; import javafx.application.Application; import javafx.application.Platform; import javafx.stage.Stage; -import seedu.address.commons.core.Config; -import seedu.address.commons.core.EventsCenter; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.core.Version; -import seedu.address.commons.events.ui.ExitAppRequestEvent; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.commons.util.StringUtil; -import seedu.address.logic.Logic; -import seedu.address.logic.LogicManager; -import seedu.address.model.*; -import seedu.address.commons.util.ConfigUtil; -import seedu.address.storage.Storage; -import seedu.address.storage.StorageManager; -import seedu.address.ui.Ui; -import seedu.address.ui.UiManager; +import seedu.taskell.commons.core.Config; +import seedu.taskell.commons.core.EventsCenter; +import seedu.taskell.commons.core.LogsCenter; +import seedu.taskell.commons.core.Version; +import seedu.taskell.commons.events.storage.StorageLocationChangedEvent; +import seedu.taskell.commons.events.ui.ExitAppRequestEvent; +import seedu.taskell.commons.exceptions.DataConversionException; +import seedu.taskell.commons.util.ConfigUtil; +import seedu.taskell.commons.util.StringUtil; +import seedu.taskell.logic.Logic; +import seedu.taskell.logic.LogicManager; +import seedu.taskell.logic.commands.SaveStorageLocationCommand; +import seedu.taskell.logic.commands.UndoCommand; +import seedu.taskell.model.*; +import seedu.taskell.storage.Storage; +import seedu.taskell.storage.StorageManager; +import seedu.taskell.ui.Ui; +import seedu.taskell.ui.UiManager; import java.io.FileNotFoundException; import java.io.IOException; @@ -45,11 +48,11 @@ public MainApp() {} @Override public void init() throws Exception { - logger.info("=============================[ Initializing AddressBook ]==========================="); + logger.info("=============================[ Initializing Task Manager ]==========================="); super.init(); config = initConfig(getApplicationParameter("config")); - storage = new StorageManager(config.getAddressBookFilePath(), config.getUserPrefsFilePath()); + storage = new StorageManager(config.getTaskManagerFilePath(), config.getUserPrefsFilePath()); userPrefs = initPrefs(config); @@ -62,6 +65,11 @@ public void init() throws Exception { ui = new UiManager(logic, config, userPrefs); initEventsCenter(); + + UndoCommand.initializeCommandHistory(); + UndoCommand.getInstance().setData(model); + SaveStorageLocationCommand.setConfig(config); + SaveStorageLocationCommand.setStorage(storage); } private String getApplicationParameter(String parameterName){ @@ -70,20 +78,20 @@ private String getApplicationParameter(String parameterName){ } private Model initModelManager(Storage storage, UserPrefs userPrefs) { - Optional addressBookOptional; - ReadOnlyAddressBook initialData; + Optional taskManagerOptional; + ReadOnlyTaskManager initialData; try { - addressBookOptional = storage.readAddressBook(); - if(!addressBookOptional.isPresent()){ - logger.info("Data file not found. Will be starting with an empty AddressBook"); + taskManagerOptional = storage.readTaskManager(); + if(!taskManagerOptional.isPresent()){ + logger.info("Data file not found. Will be starting with an empty TaskManager"); } - initialData = addressBookOptional.orElse(new AddressBook()); + initialData = taskManagerOptional.orElse(new TaskManager()); } catch (DataConversionException e) { - logger.warning("Data file not in the correct format. Will be starting with an empty AddressBook"); - initialData = new AddressBook(); + logger.warning("Data file not in the correct format. Will be starting with an empty TaskManager"); + initialData = new TaskManager(); } catch (IOException e) { - logger.warning("Problem while reading from the file. . Will be starting with an empty AddressBook"); - initialData = new AddressBook(); + logger.warning("Problem while reading from the file. . Will be starting with an empty TaskManager"); + initialData = new TaskManager(); } return new ModelManager(initialData, userPrefs); @@ -139,7 +147,7 @@ protected UserPrefs initPrefs(Config config) { "Using default user prefs"); initializedPrefs = new UserPrefs(); } catch (IOException e) { - logger.warning("Problem while reading from the file. . Will be starting with an empty AddressBook"); + logger.warning("Problem while reading from the file. . Will be starting with an empty TaskManager"); initializedPrefs = new UserPrefs(); } @@ -159,13 +167,13 @@ private void initEventsCenter() { @Override public void start(Stage primaryStage) { - logger.info("Starting AddressBook " + MainApp.VERSION); + logger.info("Starting TaskManager " + MainApp.VERSION); ui.start(primaryStage); } @Override public void stop() { - logger.info("============================ [ Stopping Address Book ] ============================="); + logger.info("============================ [ Stopping Task Manager ] ============================="); ui.stop(); try { storage.saveUserPrefs(userPrefs); @@ -181,6 +189,14 @@ public void handleExitAppRequestEvent(ExitAppRequestEvent event) { logger.info(LogsCenter.getEventHandlingLogMessage(event)); this.stop(); } + + /** @@author A0142130A **/ + @Subscribe + private void handleStorageLocationChangedEvent(StorageLocationChangedEvent event) { + config = event.getConfig(); + storage = new StorageManager(config.getTaskManagerFilePath(), config.getUserPrefsFilePath()); + } + /** @@author **/ public static void main(String[] args) { launch(args); diff --git a/src/main/java/seedu/address/commons/core/ComponentManager.java b/src/main/java/seedu/taskell/commons/core/ComponentManager.java similarity index 87% rename from src/main/java/seedu/address/commons/core/ComponentManager.java rename to src/main/java/seedu/taskell/commons/core/ComponentManager.java index 4bc8564e5824..e839ac9e2414 100644 --- a/src/main/java/seedu/address/commons/core/ComponentManager.java +++ b/src/main/java/seedu/taskell/commons/core/ComponentManager.java @@ -1,6 +1,6 @@ -package seedu.address.commons.core; +package seedu.taskell.commons.core; -import seedu.address.commons.events.BaseEvent; +import seedu.taskell.commons.events.BaseEvent; /** * Base class for *Manager classes diff --git a/src/main/java/seedu/address/commons/core/Config.java b/src/main/java/seedu/taskell/commons/core/Config.java similarity index 62% rename from src/main/java/seedu/address/commons/core/Config.java rename to src/main/java/seedu/taskell/commons/core/Config.java index 6441c9ef20f4..e16e49b00fc0 100644 --- a/src/main/java/seedu/address/commons/core/Config.java +++ b/src/main/java/seedu/taskell/commons/core/Config.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package seedu.taskell.commons.core; import java.util.Objects; import java.util.logging.Level; @@ -11,11 +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 = "Taskell"; private Level logLevel = Level.INFO; private String userPrefsFilePath = "preferences.json"; - private String addressBookFilePath = "data/addressbook.xml"; - private String addressBookName = "MyAddressBook"; + private String taskManagerFilePath = "data/taskmanager.xml"; + private String taskManagerName = "MyTaskManager"; public Config() { @@ -45,29 +45,28 @@ public void setUserPrefsFilePath(String userPrefsFilePath) { this.userPrefsFilePath = userPrefsFilePath; } - public String getAddressBookFilePath() { - return addressBookFilePath; + public String getTaskManagerFilePath() { + return taskManagerFilePath; } - public void setAddressBookFilePath(String addressBookFilePath) { - this.addressBookFilePath = addressBookFilePath; + public void setTaskManagerFilePath(String taskManagerFilePath) { + this.taskManagerFilePath = taskManagerFilePath; } - public String getAddressBookName() { - return addressBookName; + public String getTaskManagerName() { + return taskManagerName; } - public void setAddressBookName(String addressBookName) { - this.addressBookName = addressBookName; + public void setTaskManagerName(String taskManagerName) { + this.taskManagerName = taskManagerName; } - @Override public boolean equals(Object other) { - 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; } @@ -76,23 +75,23 @@ public boolean equals(Object other) { return Objects.equals(appTitle, o.appTitle) && Objects.equals(logLevel, o.logLevel) && Objects.equals(userPrefsFilePath, o.userPrefsFilePath) - && Objects.equals(addressBookFilePath, o.addressBookFilePath) - && Objects.equals(addressBookName, o.addressBookName); + && Objects.equals(taskManagerFilePath, o.taskManagerFilePath) + && Objects.equals(taskManagerName, o.taskManagerName); } @Override public int hashCode() { - return Objects.hash(appTitle, logLevel, userPrefsFilePath, addressBookFilePath, addressBookName); + return Objects.hash(appTitle, logLevel, userPrefsFilePath, taskManagerFilePath, taskManagerName); } @Override - 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 : " + taskManagerFilePath); + sb.append("\nTaskManager name : " + taskManagerName); return sb.toString(); } diff --git a/src/main/java/seedu/address/commons/core/EventsCenter.java b/src/main/java/seedu/taskell/commons/core/EventsCenter.java similarity index 92% rename from src/main/java/seedu/address/commons/core/EventsCenter.java rename to src/main/java/seedu/taskell/commons/core/EventsCenter.java index 9652cd5c227b..c546c0b09e1d 100644 --- a/src/main/java/seedu/address/commons/core/EventsCenter.java +++ b/src/main/java/seedu/taskell/commons/core/EventsCenter.java @@ -1,7 +1,8 @@ -package seedu.address.commons.core; +package seedu.taskell.commons.core; import com.google.common.eventbus.EventBus; -import seedu.address.commons.events.BaseEvent; + +import seedu.taskell.commons.events.BaseEvent; import java.util.logging.Logger; diff --git a/src/main/java/seedu/address/commons/core/GuiSettings.java b/src/main/java/seedu/taskell/commons/core/GuiSettings.java similarity index 98% rename from src/main/java/seedu/address/commons/core/GuiSettings.java rename to src/main/java/seedu/taskell/commons/core/GuiSettings.java index e157ac8b8679..b8f551716126 100644 --- a/src/main/java/seedu/address/commons/core/GuiSettings.java +++ b/src/main/java/seedu/taskell/commons/core/GuiSettings.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package seedu.taskell.commons.core; import java.awt.*; import java.io.Serializable; diff --git a/src/main/java/seedu/address/commons/core/LogsCenter.java b/src/main/java/seedu/taskell/commons/core/LogsCenter.java similarity index 96% rename from src/main/java/seedu/address/commons/core/LogsCenter.java rename to src/main/java/seedu/taskell/commons/core/LogsCenter.java index 17939bab4975..ebb8ad5927af 100644 --- a/src/main/java/seedu/address/commons/core/LogsCenter.java +++ b/src/main/java/seedu/taskell/commons/core/LogsCenter.java @@ -1,10 +1,10 @@ -package seedu.address.commons.core; - -import seedu.address.commons.events.BaseEvent; +package seedu.taskell.commons.core; import java.io.IOException; import java.util.logging.*; +import seedu.taskell.commons.events.BaseEvent; + /** * Configures and manages loggers and handlers, including their logging level * Named {@link Logger}s can be obtained from this class
@@ -15,7 +15,7 @@ public class LogsCenter { private static final int MAX_FILE_COUNT = 5; private static final int MAX_FILE_SIZE_IN_BYTES = (int) (Math.pow(2, 20) * 5); // 5MB - private static final String LOG_FILE = "addressbook.log"; + private static final String LOG_FILE = "taskmanager.log"; private static Level currentLogLevel = Level.INFO; private static final Logger logger = LogsCenter.getLogger(LogsCenter.class); private static FileHandler fileHandler; diff --git a/src/main/java/seedu/address/commons/core/Messages.java b/src/main/java/seedu/taskell/commons/core/Messages.java similarity index 51% rename from src/main/java/seedu/address/commons/core/Messages.java rename to src/main/java/seedu/taskell/commons/core/Messages.java index 1deb3a1e4695..5237f37b560f 100644 --- a/src/main/java/seedu/address/commons/core/Messages.java +++ b/src/main/java/seedu/taskell/commons/core/Messages.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package seedu.taskell.commons.core; /** * Container for user visible messages. @@ -7,7 +7,7 @@ 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!"; + public static final String MESSAGE_INVALID_TASK_DISPLAYED_INDEX = "The task index provided is invalid"; + public static final String MESSAGE_TASKS_LISTED_OVERVIEW = "%1$d tasks listed!"; } diff --git a/src/main/java/seedu/address/commons/core/UnmodifiableObservableList.java b/src/main/java/seedu/taskell/commons/core/UnmodifiableObservableList.java similarity index 99% rename from src/main/java/seedu/address/commons/core/UnmodifiableObservableList.java rename to src/main/java/seedu/taskell/commons/core/UnmodifiableObservableList.java index 5c25d8647a8d..803d7ff77329 100644 --- a/src/main/java/seedu/address/commons/core/UnmodifiableObservableList.java +++ b/src/main/java/seedu/taskell/commons/core/UnmodifiableObservableList.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package seedu.taskell.commons.core; import javafx.beans.InvalidationListener; import javafx.collections.ListChangeListener; diff --git a/src/main/java/seedu/address/commons/core/Version.java b/src/main/java/seedu/taskell/commons/core/Version.java similarity index 98% rename from src/main/java/seedu/address/commons/core/Version.java rename to src/main/java/seedu/taskell/commons/core/Version.java index 7ecb85b18f82..61920725c5ed 100644 --- a/src/main/java/seedu/address/commons/core/Version.java +++ b/src/main/java/seedu/taskell/commons/core/Version.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package seedu.taskell.commons.core; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; diff --git a/src/main/java/seedu/address/commons/events/BaseEvent.java b/src/main/java/seedu/taskell/commons/events/BaseEvent.java similarity index 90% rename from src/main/java/seedu/address/commons/events/BaseEvent.java rename to src/main/java/seedu/taskell/commons/events/BaseEvent.java index 723a9c69fbd5..24ff9515243c 100644 --- a/src/main/java/seedu/address/commons/events/BaseEvent.java +++ b/src/main/java/seedu/taskell/commons/events/BaseEvent.java @@ -1,4 +1,4 @@ -package seedu.address.commons.events; +package seedu.taskell.commons.events; public abstract class BaseEvent { diff --git a/src/main/java/seedu/taskell/commons/events/model/TaskManagerChangedEvent.java b/src/main/java/seedu/taskell/commons/events/model/TaskManagerChangedEvent.java new file mode 100644 index 000000000000..30c9db225ef7 --- /dev/null +++ b/src/main/java/seedu/taskell/commons/events/model/TaskManagerChangedEvent.java @@ -0,0 +1,19 @@ +package seedu.taskell.commons.events.model; + +import seedu.taskell.commons.events.BaseEvent; +import seedu.taskell.model.ReadOnlyTaskManager; + +/** Indicates the TaskManager in the model has changed*/ +public class TaskManagerChangedEvent extends BaseEvent { + + public final ReadOnlyTaskManager data; + + public TaskManagerChangedEvent(ReadOnlyTaskManager data){ + this.data = data; + } + + @Override + public String toString() { + return "number of tasks " + data.getTaskList().size() + ", number of tags " + data.getTagList().size(); + } +} diff --git a/src/main/java/seedu/address/commons/events/storage/DataSavingExceptionEvent.java b/src/main/java/seedu/taskell/commons/events/storage/DataSavingExceptionEvent.java similarity index 78% rename from src/main/java/seedu/address/commons/events/storage/DataSavingExceptionEvent.java rename to src/main/java/seedu/taskell/commons/events/storage/DataSavingExceptionEvent.java index f0a0640ee523..dba2a61ebc8d 100644 --- a/src/main/java/seedu/address/commons/events/storage/DataSavingExceptionEvent.java +++ b/src/main/java/seedu/taskell/commons/events/storage/DataSavingExceptionEvent.java @@ -1,6 +1,6 @@ -package seedu.address.commons.events.storage; +package seedu.taskell.commons.events.storage; -import seedu.address.commons.events.BaseEvent; +import seedu.taskell.commons.events.BaseEvent; /** * Indicates an exception during a file saving diff --git a/src/main/java/seedu/taskell/commons/events/storage/StorageLocationChangedEvent.java b/src/main/java/seedu/taskell/commons/events/storage/StorageLocationChangedEvent.java new file mode 100644 index 000000000000..ac1f736672af --- /dev/null +++ b/src/main/java/seedu/taskell/commons/events/storage/StorageLocationChangedEvent.java @@ -0,0 +1,24 @@ +/** @@author A0142130A **/ +package seedu.taskell.commons.events.storage; + +import seedu.taskell.commons.core.Config; +import seedu.taskell.commons.events.BaseEvent; + +public class StorageLocationChangedEvent extends BaseEvent { + + private Config config; + + public StorageLocationChangedEvent(Config config) { + this.config = config; + } + + public Config getConfig() { + return config; + } + + @Override + public String toString() { + return this.getClass().getSimpleName(); + } + +} diff --git a/src/main/java/seedu/taskell/commons/events/ui/DisplayCalendarViewEvent.java b/src/main/java/seedu/taskell/commons/events/ui/DisplayCalendarViewEvent.java new file mode 100644 index 000000000000..09de3dc514ab --- /dev/null +++ b/src/main/java/seedu/taskell/commons/events/ui/DisplayCalendarViewEvent.java @@ -0,0 +1,15 @@ +/** @@author A0142130A **/ +package seedu.taskell.commons.events.ui; + +import seedu.taskell.commons.events.BaseEvent; + +/** Indicates display panel needs to show calendar **/ + +public class DisplayCalendarViewEvent extends BaseEvent { + + @Override + public String toString() { + return this.getClass().getSimpleName(); + } + +} diff --git a/src/main/java/seedu/taskell/commons/events/ui/DisplayListChangedEvent.java b/src/main/java/seedu/taskell/commons/events/ui/DisplayListChangedEvent.java new file mode 100644 index 000000000000..125948cd6635 --- /dev/null +++ b/src/main/java/seedu/taskell/commons/events/ui/DisplayListChangedEvent.java @@ -0,0 +1,27 @@ +/** @@author A0142130A **/ +package seedu.taskell.commons.events.ui; + +import java.util.ArrayList; + +import seedu.taskell.commons.events.BaseEvent; + +/** Indicates a list needs to be displayed on Display Panel + * */ +public class DisplayListChangedEvent extends BaseEvent { + + private ArrayList list; + + public DisplayListChangedEvent(ArrayList list) { + this.list = list; + } + + @Override + public String toString() { + return this.getClass().getSimpleName(); + } + + public ArrayList getList() { + return list; + } + +} diff --git a/src/main/java/seedu/address/commons/events/ui/ExitAppRequestEvent.java b/src/main/java/seedu/taskell/commons/events/ui/ExitAppRequestEvent.java similarity index 70% rename from src/main/java/seedu/address/commons/events/ui/ExitAppRequestEvent.java rename to src/main/java/seedu/taskell/commons/events/ui/ExitAppRequestEvent.java index 9af6194543a3..5400dc303cac 100644 --- a/src/main/java/seedu/address/commons/events/ui/ExitAppRequestEvent.java +++ b/src/main/java/seedu/taskell/commons/events/ui/ExitAppRequestEvent.java @@ -1,6 +1,6 @@ -package seedu.address.commons.events.ui; +package seedu.taskell.commons.events.ui; -import seedu.address.commons.events.BaseEvent; +import seedu.taskell.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/taskell/commons/events/ui/IncorrectCommandAttemptedEvent.java similarity index 68% rename from src/main/java/seedu/address/commons/events/ui/IncorrectCommandAttemptedEvent.java rename to src/main/java/seedu/taskell/commons/events/ui/IncorrectCommandAttemptedEvent.java index 991f7ae9fa25..d07ccc8d5fac 100644 --- a/src/main/java/seedu/address/commons/events/ui/IncorrectCommandAttemptedEvent.java +++ b/src/main/java/seedu/taskell/commons/events/ui/IncorrectCommandAttemptedEvent.java @@ -1,7 +1,7 @@ -package seedu.address.commons.events.ui; +package seedu.taskell.commons.events.ui; -import seedu.address.commons.events.BaseEvent; -import seedu.address.logic.commands.Command; +import seedu.taskell.commons.events.BaseEvent; +import seedu.taskell.logic.commands.Command; /** * Indicates an attempt to execute an incorrect command diff --git a/src/main/java/seedu/address/commons/events/ui/JumpToListRequestEvent.java b/src/main/java/seedu/taskell/commons/events/ui/JumpToListRequestEvent.java similarity index 68% rename from src/main/java/seedu/address/commons/events/ui/JumpToListRequestEvent.java rename to src/main/java/seedu/taskell/commons/events/ui/JumpToListRequestEvent.java index 0580d27aecf5..94ded5eab69e 100644 --- a/src/main/java/seedu/address/commons/events/ui/JumpToListRequestEvent.java +++ b/src/main/java/seedu/taskell/commons/events/ui/JumpToListRequestEvent.java @@ -1,9 +1,9 @@ -package seedu.address.commons.events.ui; +package seedu.taskell.commons.events.ui; -import seedu.address.commons.events.BaseEvent; +import seedu.taskell.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/taskell/commons/events/ui/ShowHelpRequestEvent.java similarity index 70% rename from src/main/java/seedu/address/commons/events/ui/ShowHelpRequestEvent.java rename to src/main/java/seedu/taskell/commons/events/ui/ShowHelpRequestEvent.java index a7e40940b2c7..f35eaa16f4b1 100644 --- a/src/main/java/seedu/address/commons/events/ui/ShowHelpRequestEvent.java +++ b/src/main/java/seedu/taskell/commons/events/ui/ShowHelpRequestEvent.java @@ -1,6 +1,6 @@ -package seedu.address.commons.events.ui; +package seedu.taskell.commons.events.ui; -import seedu.address.commons.events.BaseEvent; +import seedu.taskell.commons.events.BaseEvent; /** * An event requesting to view the help page. diff --git a/src/main/java/seedu/taskell/commons/events/ui/TaskPanelSelectionChangedEvent.java b/src/main/java/seedu/taskell/commons/events/ui/TaskPanelSelectionChangedEvent.java new file mode 100644 index 000000000000..29d70ad6cfb3 --- /dev/null +++ b/src/main/java/seedu/taskell/commons/events/ui/TaskPanelSelectionChangedEvent.java @@ -0,0 +1,26 @@ +package seedu.taskell.commons.events.ui; + +import seedu.taskell.commons.events.BaseEvent; +import seedu.taskell.model.task.ReadOnlyTask; + +/** + * Represents a selection change in the Task List Panel + */ +public class TaskPanelSelectionChangedEvent extends BaseEvent { + + + private final ReadOnlyTask newSelection; + + public TaskPanelSelectionChangedEvent(ReadOnlyTask newSelection){ + this.newSelection = newSelection; + } + + @Override + public String toString() { + return this.getClass().getSimpleName(); + } + + public ReadOnlyTask getNewSelection() { + return newSelection; + } +} diff --git a/src/main/java/seedu/address/commons/exceptions/DataConversionException.java b/src/main/java/seedu/taskell/commons/exceptions/DataConversionException.java similarity index 84% rename from src/main/java/seedu/address/commons/exceptions/DataConversionException.java rename to src/main/java/seedu/taskell/commons/exceptions/DataConversionException.java index 1f689bd8e3f9..04f4f5e2e5ea 100644 --- a/src/main/java/seedu/address/commons/exceptions/DataConversionException.java +++ b/src/main/java/seedu/taskell/commons/exceptions/DataConversionException.java @@ -1,4 +1,4 @@ -package seedu.address.commons.exceptions; +package seedu.taskell.commons.exceptions; /** * Represents an error during conversion of data from one format to another diff --git a/src/main/java/seedu/address/commons/exceptions/DuplicateDataException.java b/src/main/java/seedu/taskell/commons/exceptions/DuplicateDataException.java similarity index 85% rename from src/main/java/seedu/address/commons/exceptions/DuplicateDataException.java rename to src/main/java/seedu/taskell/commons/exceptions/DuplicateDataException.java index 17aa63d5020c..28a22d88f6ae 100644 --- a/src/main/java/seedu/address/commons/exceptions/DuplicateDataException.java +++ b/src/main/java/seedu/taskell/commons/exceptions/DuplicateDataException.java @@ -1,4 +1,4 @@ -package seedu.address.commons.exceptions; +package seedu.taskell.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/taskell/commons/exceptions/IllegalValueException.java similarity index 88% rename from src/main/java/seedu/address/commons/exceptions/IllegalValueException.java rename to src/main/java/seedu/taskell/commons/exceptions/IllegalValueException.java index a473b43bd86f..6ad188df01f8 100644 --- a/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java +++ b/src/main/java/seedu/taskell/commons/exceptions/IllegalValueException.java @@ -1,4 +1,4 @@ -package seedu.address.commons.exceptions; +package seedu.taskell.commons.exceptions; /** * Signals that some given data does not fulfill some constraints. diff --git a/src/main/java/seedu/address/commons/util/AppUtil.java b/src/main/java/seedu/taskell/commons/util/AppUtil.java similarity index 81% rename from src/main/java/seedu/address/commons/util/AppUtil.java rename to src/main/java/seedu/taskell/commons/util/AppUtil.java index 649cc19aaeda..766b57c5b425 100644 --- a/src/main/java/seedu/address/commons/util/AppUtil.java +++ b/src/main/java/seedu/taskell/commons/util/AppUtil.java @@ -1,7 +1,7 @@ -package seedu.address.commons.util; +package seedu.taskell.commons.util; import javafx.scene.image.Image; -import seedu.address.MainApp; +import seedu.taskell.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/taskell/commons/util/CollectionUtil.java similarity index 96% rename from src/main/java/seedu/address/commons/util/CollectionUtil.java rename to src/main/java/seedu/taskell/commons/util/CollectionUtil.java index fde8394f31e5..6196975c934e 100644 --- a/src/main/java/seedu/address/commons/util/CollectionUtil.java +++ b/src/main/java/seedu/taskell/commons/util/CollectionUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.taskell.commons.util; import java.util.Collection; import java.util.HashSet; diff --git a/src/main/java/seedu/address/commons/util/ConfigUtil.java b/src/main/java/seedu/taskell/commons/util/ConfigUtil.java similarity index 91% rename from src/main/java/seedu/address/commons/util/ConfigUtil.java rename to src/main/java/seedu/taskell/commons/util/ConfigUtil.java index af42e03df06c..d91f07efb9da 100644 --- a/src/main/java/seedu/address/commons/util/ConfigUtil.java +++ b/src/main/java/seedu/taskell/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.taskell.commons.util; import java.io.File; import java.io.IOException; import java.util.Optional; import java.util.logging.Logger; +import seedu.taskell.commons.core.Config; +import seedu.taskell.commons.core.LogsCenter; +import seedu.taskell.commons.exceptions.DataConversionException; + /** * A class for accessing the Config File. */ diff --git a/src/main/java/seedu/address/commons/util/FileUtil.java b/src/main/java/seedu/taskell/commons/util/FileUtil.java similarity index 98% rename from src/main/java/seedu/address/commons/util/FileUtil.java rename to src/main/java/seedu/taskell/commons/util/FileUtil.java index ca8221250de4..9ed774632f2a 100644 --- a/src/main/java/seedu/address/commons/util/FileUtil.java +++ b/src/main/java/seedu/taskell/commons/util/FileUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.taskell.commons.util; import java.io.File; import java.io.IOException; diff --git a/src/main/java/seedu/address/commons/util/FxViewUtil.java b/src/main/java/seedu/taskell/commons/util/FxViewUtil.java similarity index 92% rename from src/main/java/seedu/address/commons/util/FxViewUtil.java rename to src/main/java/seedu/taskell/commons/util/FxViewUtil.java index 900efa6bf5c3..b43bda757bb2 100644 --- a/src/main/java/seedu/address/commons/util/FxViewUtil.java +++ b/src/main/java/seedu/taskell/commons/util/FxViewUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.taskell.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/taskell/commons/util/JsonUtil.java similarity index 98% rename from src/main/java/seedu/address/commons/util/JsonUtil.java rename to src/main/java/seedu/taskell/commons/util/JsonUtil.java index 80b67de5b7e8..51223ab439fa 100644 --- a/src/main/java/seedu/address/commons/util/JsonUtil.java +++ b/src/main/java/seedu/taskell/commons/util/JsonUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.taskell.commons.util; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; diff --git a/src/main/java/seedu/address/commons/util/StringUtil.java b/src/main/java/seedu/taskell/commons/util/StringUtil.java similarity index 96% rename from src/main/java/seedu/address/commons/util/StringUtil.java rename to src/main/java/seedu/taskell/commons/util/StringUtil.java index 2e94740456a6..4cb571bb902b 100644 --- a/src/main/java/seedu/address/commons/util/StringUtil.java +++ b/src/main/java/seedu/taskell/commons/util/StringUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.taskell.commons.util; import java.io.PrintWriter; import java.io.StringWriter; diff --git a/src/main/java/seedu/address/commons/util/UrlUtil.java b/src/main/java/seedu/taskell/commons/util/UrlUtil.java similarity index 94% rename from src/main/java/seedu/address/commons/util/UrlUtil.java rename to src/main/java/seedu/taskell/commons/util/UrlUtil.java index 6bbab52b9840..a8e378738d57 100644 --- a/src/main/java/seedu/address/commons/util/UrlUtil.java +++ b/src/main/java/seedu/taskell/commons/util/UrlUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.taskell.commons.util; import java.net.URL; diff --git a/src/main/java/seedu/address/commons/util/XmlUtil.java b/src/main/java/seedu/taskell/commons/util/XmlUtil.java similarity index 98% rename from src/main/java/seedu/address/commons/util/XmlUtil.java rename to src/main/java/seedu/taskell/commons/util/XmlUtil.java index 2087e7628a1d..142dbc266d4f 100644 --- a/src/main/java/seedu/address/commons/util/XmlUtil.java +++ b/src/main/java/seedu/taskell/commons/util/XmlUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.taskell.commons.util; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; diff --git a/src/main/java/seedu/address/logic/Logic.java b/src/main/java/seedu/taskell/logic/Logic.java similarity index 58% rename from src/main/java/seedu/address/logic/Logic.java rename to src/main/java/seedu/taskell/logic/Logic.java index 4df1bc65cabb..c652c8685262 100644 --- a/src/main/java/seedu/address/logic/Logic.java +++ b/src/main/java/seedu/taskell/logic/Logic.java @@ -1,8 +1,8 @@ -package seedu.address.logic; +package seedu.taskell.logic; import javafx.collections.ObservableList; -import seedu.address.logic.commands.CommandResult; -import seedu.address.model.person.ReadOnlyPerson; +import seedu.taskell.logic.commands.CommandResult; +import seedu.taskell.model.task.ReadOnlyTask; /** * API of the Logic component @@ -15,7 +15,7 @@ public interface Logic { */ CommandResult execute(String commandText); - /** Returns the filtered list of persons */ - ObservableList getFilteredPersonList(); + /** Returns the filtered list of tasks */ + ObservableList getFilteredTaskList(); } diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/seedu/taskell/logic/LogicManager.java similarity index 60% rename from src/main/java/seedu/address/logic/LogicManager.java rename to src/main/java/seedu/taskell/logic/LogicManager.java index ce4dc1903cff..76554fca0514 100644 --- a/src/main/java/seedu/address/logic/LogicManager.java +++ b/src/main/java/seedu/taskell/logic/LogicManager.java @@ -1,14 +1,14 @@ -package seedu.address.logic; +package seedu.taskell.logic; import javafx.collections.ObservableList; -import seedu.address.commons.core.ComponentManager; -import seedu.address.commons.core.LogsCenter; -import seedu.address.logic.commands.Command; -import seedu.address.logic.commands.CommandResult; -import seedu.address.logic.parser.Parser; -import seedu.address.model.Model; -import seedu.address.model.person.ReadOnlyPerson; -import seedu.address.storage.Storage; +import seedu.taskell.commons.core.ComponentManager; +import seedu.taskell.commons.core.LogsCenter; +import seedu.taskell.logic.commands.Command; +import seedu.taskell.logic.commands.CommandResult; +import seedu.taskell.logic.parser.Parser; +import seedu.taskell.model.Model; +import seedu.taskell.model.task.ReadOnlyTask; +import seedu.taskell.storage.Storage; import java.util.logging.Logger; @@ -35,7 +35,7 @@ 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/taskell/logic/commands/AddCommand.java b/src/main/java/seedu/taskell/logic/commands/AddCommand.java new file mode 100644 index 000000000000..aba90b74b3ba --- /dev/null +++ b/src/main/java/seedu/taskell/logic/commands/AddCommand.java @@ -0,0 +1,69 @@ +package seedu.taskell.logic.commands; + +import java.util.HashSet; +import java.util.Set; + +import seedu.taskell.commons.exceptions.IllegalValueException; +import seedu.taskell.model.tag.Tag; +import seedu.taskell.model.tag.UniqueTagList; +import seedu.taskell.model.task.*; + +//@@author A0139257X +/** + * Adds a task to the task manager. + */ +public class AddCommand extends Command { + + public static final String COMMAND_WORD = "add"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a task to the task manager. " + + "Parameters: DESCRIPTION by/on[DATE] from[START_TIME] to[END_TIME] [p/PRIORITY] [#TAG]...\n" + + "Example: " + COMMAND_WORD + + " go for meeting on 1-1-2100 from 12.30AM to 12.45AM p/3 #work"; + + public static final String MESSAGE_SUCCESS = "New task added: %1$s"; + public static final String MESSAGE_DUPLICATE_TASK = "This task already exists in the task manager"; + + private final Task toAdd; + + /** + * Convenience constructor using raw values. + * + * @throws IllegalValueException if any of the raw values are invalid + */ + + public AddCommand(String description, String taskType, String startDate, String endDate, String startTime, String endTime, String taskPriority, Set tags) + throws IllegalValueException { + final Set tagSet = new HashSet<>(); + for (String tagName : tags) { + tagSet.add(new Tag(tagName)); + } + + switch (taskType) { + case Task.FLOATING_TASK: + this.toAdd = new FloatingTask(description, taskPriority, TaskStatus.INCOMPLETE, new UniqueTagList(tagSet)); + break; + case Task.EVENT_TASK: + this.toAdd = new EventTask(description, startDate, endDate, startTime, endTime, taskPriority, TaskStatus.INCOMPLETE, new UniqueTagList(tagSet)); + break; + default: + toAdd = null; + } + } + + //@@author A0139257X-reused + @Override + public CommandResult execute() { + assert model != null; + try { + model.addTask(toAdd); + UndoCommand.addTaskToCommandHistory(toAdd); + return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); + } catch (UniqueTaskList.DuplicateTaskException e) { + UndoCommand.deletePreviousCommand(); + return new CommandResult(MESSAGE_DUPLICATE_TASK); + } + + } + +} diff --git a/src/main/java/seedu/address/logic/commands/ClearCommand.java b/src/main/java/seedu/taskell/logic/commands/ClearCommand.java similarity index 56% rename from src/main/java/seedu/address/logic/commands/ClearCommand.java rename to src/main/java/seedu/taskell/logic/commands/ClearCommand.java index 522d57189f51..ad354af80372 100644 --- a/src/main/java/seedu/address/logic/commands/ClearCommand.java +++ b/src/main/java/seedu/taskell/logic/commands/ClearCommand.java @@ -1,14 +1,14 @@ -package seedu.address.logic.commands; +package seedu.taskell.logic.commands; -import seedu.address.model.AddressBook; +import seedu.taskell.model.TaskManager; /** - * Clears the address book. + * Clears the task manager. */ public class ClearCommand extends Command { public static final String COMMAND_WORD = "clear"; - public static final String MESSAGE_SUCCESS = "Address book has been cleared!"; + public static final String MESSAGE_SUCCESS = "Task manager has been cleared!"; public ClearCommand() {} @@ -16,7 +16,7 @@ public ClearCommand() {} @Override public CommandResult execute() { assert model != null; - model.resetData(AddressBook.getEmptyAddressBook()); + model.resetData(TaskManager.getEmptyTaskManager()); return new CommandResult(MESSAGE_SUCCESS); } } diff --git a/src/main/java/seedu/address/logic/commands/Command.java b/src/main/java/seedu/taskell/logic/commands/Command.java similarity index 67% rename from src/main/java/seedu/address/logic/commands/Command.java rename to src/main/java/seedu/taskell/logic/commands/Command.java index 7c0ba2fd0161..87f517c5b4cc 100644 --- a/src/main/java/seedu/address/logic/commands/Command.java +++ b/src/main/java/seedu/taskell/logic/commands/Command.java @@ -1,9 +1,9 @@ -package seedu.address.logic.commands; +package seedu.taskell.logic.commands; -import seedu.address.commons.core.EventsCenter; -import seedu.address.commons.core.Messages; -import seedu.address.commons.events.ui.IncorrectCommandAttemptedEvent; -import seedu.address.model.Model; +import seedu.taskell.commons.core.EventsCenter; +import seedu.taskell.commons.core.Messages; +import seedu.taskell.commons.events.ui.IncorrectCommandAttemptedEvent; +import seedu.taskell.model.Model; /** * Represents a command with hidden internal logic and the ability to be executed. @@ -12,13 +12,13 @@ public abstract class Command { protected Model model; /** - * Constructs a feedback message to summarise an operation that displayed a listing of persons. + * Constructs a feedback message to summarise an operation that displayed a listing of tasks. * * @param displaySize used to generate summary - * @return summary message for persons displayed + * @return summary message for tasks displayed */ - public static String getMessageForPersonListShownSummary(int displaySize) { - return String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, displaySize); + public static String getMessageForTaskListShownSummary(int displaySize) { + return String.format(Messages.MESSAGE_TASKS_LISTED_OVERVIEW, displaySize); } /** diff --git a/src/main/java/seedu/address/logic/commands/CommandResult.java b/src/main/java/seedu/taskell/logic/commands/CommandResult.java similarity index 87% rename from src/main/java/seedu/address/logic/commands/CommandResult.java rename to src/main/java/seedu/taskell/logic/commands/CommandResult.java index f46f2f31353e..fbc2d5e97862 100644 --- a/src/main/java/seedu/address/logic/commands/CommandResult.java +++ b/src/main/java/seedu/taskell/logic/commands/CommandResult.java @@ -1,4 +1,4 @@ -package seedu.address.logic.commands; +package seedu.taskell.logic.commands; /** * Represents the result of a command execution. diff --git a/src/main/java/seedu/taskell/logic/commands/DeleteCommand.java b/src/main/java/seedu/taskell/logic/commands/DeleteCommand.java new file mode 100644 index 000000000000..784eb030d199 --- /dev/null +++ b/src/main/java/seedu/taskell/logic/commands/DeleteCommand.java @@ -0,0 +1,55 @@ +package seedu.taskell.logic.commands; + +import seedu.taskell.commons.core.Messages; +import seedu.taskell.commons.core.UnmodifiableObservableList; +import seedu.taskell.model.task.ReadOnlyTask; +import seedu.taskell.model.task.Task; +import seedu.taskell.model.task.UniqueTaskList.TaskNotFoundException; + +/** + * Deletes a task identified using it's last displayed index from the task manager. + */ +public class DeleteCommand extends Command { + + public static final String COMMAND_WORD = "delete"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Deletes the task identified by the index number used in the last task listing.\n" + + "Parameters: INDEX (must be a positive integer)\n" + + "Example: " + COMMAND_WORD + " 1"; + + public static final String MESSAGE_DELETE_TASK_SUCCESS = "Deleted Task: %1$s"; + + public final int targetIndex; + + public DeleteCommand(int targetIndex) { + this.targetIndex = targetIndex; + } + + + @Override + public CommandResult execute() { + + UnmodifiableObservableList lastShownList = model.getFilteredTaskList(); + + if (lastShownList.size() < targetIndex) { + indicateAttemptToExecuteIncorrectCommand(); + UndoCommand.deletePreviousCommand(); + return new CommandResult(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + + ReadOnlyTask taskToDelete = lastShownList.get(targetIndex - 1); + + try { + model.deleteTask(taskToDelete); + UndoCommand.addTaskToCommandHistory((Task) taskToDelete); + ListUndoCommand.getInstance().setData(model); + ListUndoCommand.getInstance().indicateDisplayListChanged(); + } catch (TaskNotFoundException tnfe) { + assert false : "The target task cannot be missing"; + } + + return new CommandResult(String.format(MESSAGE_DELETE_TASK_SUCCESS, taskToDelete)); + } + +} diff --git a/src/main/java/seedu/taskell/logic/commands/DoneCommand.java b/src/main/java/seedu/taskell/logic/commands/DoneCommand.java new file mode 100644 index 000000000000..2ab71310c84b --- /dev/null +++ b/src/main/java/seedu/taskell/logic/commands/DoneCommand.java @@ -0,0 +1,58 @@ +//@@author A0148004R +package seedu.taskell.logic.commands; + +import seedu.taskell.commons.core.Messages; +import seedu.taskell.commons.core.UnmodifiableObservableList; +import seedu.taskell.model.task.ReadOnlyTask; +import seedu.taskell.model.task.Task; +import seedu.taskell.model.task.TaskStatus; +import seedu.taskell.model.task.UniqueTaskList; +import seedu.taskell.model.task.UniqueTaskList.TaskNotFoundException; + +public class DoneCommand extends Command { + public static final String COMMAND_WORD = "done"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Done 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_DONE_TASK_SUCCESS = "Done Task: %1$s"; + + public final int targetIndex; + + + public DoneCommand(int targetIndex) { + this.targetIndex = targetIndex; + + } + + @Override + public CommandResult execute() { + + UnmodifiableObservableList lastShownList = model.getFilteredTaskList(); + + if (lastShownList.size() < targetIndex) { + indicateAttemptToExecuteIncorrectCommand(); + return new CommandResult(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + + TaskStatus finsihedStatus = new TaskStatus(TaskStatus.FINISHED); + + ReadOnlyTask taskToDone = lastShownList.get(targetIndex - 1); + + Task newTask = new Task(taskToDone.getDescription(), taskToDone.getTaskType(), taskToDone.getStartDate(), taskToDone.getEndDate(), + taskToDone.getStartTime(), taskToDone.getEndTime(), taskToDone.getTaskPriority(), finsihedStatus, taskToDone.getTags()); + + try { + model.editTask(taskToDone, newTask); + } catch (TaskNotFoundException pnfe) { + assert false : "The target task cannot be missing"; + } catch (UniqueTaskList.DuplicateTaskException e) { + return new CommandResult(AddCommand.MESSAGE_DUPLICATE_TASK); + } + + return new CommandResult(String.format(MESSAGE_DONE_TASK_SUCCESS, taskToDone)); + } +} +//@@author \ No newline at end of file diff --git a/src/main/java/seedu/taskell/logic/commands/EditDescriptionCommand.java b/src/main/java/seedu/taskell/logic/commands/EditDescriptionCommand.java new file mode 100644 index 000000000000..9f3345940ecb --- /dev/null +++ b/src/main/java/seedu/taskell/logic/commands/EditDescriptionCommand.java @@ -0,0 +1,71 @@ +//@@author A0142073R +package seedu.taskell.logic.commands; + +import seedu.taskell.commons.core.Messages; +import seedu.taskell.commons.core.UnmodifiableObservableList; +import seedu.taskell.commons.exceptions.IllegalValueException; +import seedu.taskell.model.tag.Tag; +import seedu.taskell.model.tag.UniqueTagList; +import seedu.taskell.model.task.Description; +import seedu.taskell.model.task.ReadOnlyTask; +import seedu.taskell.model.task.Task; +import seedu.taskell.model.task.TaskDate; +import seedu.taskell.model.task.TaskPriority; +import seedu.taskell.model.task.TaskTime; +import seedu.taskell.model.task.UniqueTaskList; +import seedu.taskell.model.task.UniqueTaskList.TaskNotFoundException; + +/** + * Edits a task identified using it's last displayed index from the task manager. + */ +public class EditDescriptionCommand extends Command { + public static final String COMMAND_WORD_1 = "edit-desc"; + public static final String COMMAND_WORD_2 = "edit-name"; + + public static final String MESSAGE_USAGE = COMMAND_WORD_1 + + "/"+ COMMAND_WORD_2 + + ": Edits the description task identified by the index number used in the last task listing.\n" + + "Parameters: INDEX (must be a positive integer) NEW_DESCRIPTION\n" + + "Example: " + COMMAND_WORD_1 + " 1 buy cake\n" + + "Example: " + COMMAND_WORD_2 + " 2 do 2103t\n"; + + public static final String MESSAGE_EDIT_TASK_SUCCESS = "Original Task: %1$s \n\nUpdated Task: %2$s"; + + public final int targetIndex; + public final Description description; + + public EditDescriptionCommand(int targetIndex, String newDescription) throws IllegalValueException { + this.targetIndex = targetIndex; + this.description = new Description(newDescription); + } + + @Override + public CommandResult execute() { + + UnmodifiableObservableList lastShownList = model.getFilteredTaskList(); + + if (lastShownList.size() < targetIndex) { + indicateAttemptToExecuteIncorrectCommand(); + return new CommandResult(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + + ReadOnlyTask taskToEdit = lastShownList.get(targetIndex - 1); + + Task newTask = new Task(description,taskToEdit.getTaskType(),taskToEdit.getStartDate(), taskToEdit.getEndDate(), taskToEdit.getStartTime(),taskToEdit.getEndTime(), + taskToEdit.getTaskPriority(),taskToEdit.getTaskStatus(), taskToEdit.getTags() + ); + try { + model.editTask(taskToEdit,newTask); + UndoCommand.addTaskToCommandHistory(newTask); + UndoCommand.addOldTaskToCommandHistory((Task) taskToEdit); + } catch (TaskNotFoundException pnfe) { + assert false : "The target task cannot be missing"; + } catch (UniqueTaskList.DuplicateTaskException e) { + UndoCommand.deletePreviousCommand(); + return new CommandResult(AddCommand.MESSAGE_DUPLICATE_TASK); + } + + return new CommandResult(String.format(MESSAGE_EDIT_TASK_SUCCESS, taskToEdit, newTask)); + } +} +//@@author diff --git a/src/main/java/seedu/taskell/logic/commands/EditEndDateCommand.java b/src/main/java/seedu/taskell/logic/commands/EditEndDateCommand.java new file mode 100644 index 000000000000..14d27b4d8c06 --- /dev/null +++ b/src/main/java/seedu/taskell/logic/commands/EditEndDateCommand.java @@ -0,0 +1,68 @@ +//@@author A0142073R +package seedu.taskell.logic.commands; + +import seedu.taskell.commons.core.Messages; +import seedu.taskell.commons.core.UnmodifiableObservableList; +import seedu.taskell.commons.exceptions.IllegalValueException; +import seedu.taskell.model.tag.Tag; +import seedu.taskell.model.tag.UniqueTagList; +import seedu.taskell.model.task.ReadOnlyTask; +import seedu.taskell.model.task.Task; +import seedu.taskell.model.task.TaskDate; +import seedu.taskell.model.task.TaskPriority; +import seedu.taskell.model.task.TaskTime; +import seedu.taskell.model.task.UniqueTaskList; +import seedu.taskell.model.task.UniqueTaskList.TaskNotFoundException; + +/** + * Edits a task EndDate identified using it's last displayed index from the task + * manager. + */ +public class EditEndDateCommand extends Command { + public static final String COMMAND_WORD = "edit-endDate"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Edits the EndDate of a task identified by the index number used in the last task listing.\n" + + "Parameters: INDEX (must be a positive integer) NEW_DATE\n" + "Example: " + COMMAND_WORD + " 1 8-8-2016 "; + + public static final String MESSAGE_EDIT_TASK_SUCCESS = "Original Task: %1$s \n\nUpdatedTask: %2$s"; + + public final int targetIndex; + public final TaskDate endDate; + + public EditEndDateCommand(int targetIndex, String EndDate) throws IllegalValueException { + this.targetIndex = targetIndex; + this.endDate = new TaskDate(EndDate); + } + + @Override + public CommandResult execute() { + + UnmodifiableObservableList lastShownList = model.getFilteredTaskList(); + + if (lastShownList.size() < targetIndex) { + indicateAttemptToExecuteIncorrectCommand(); + return new CommandResult(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + + ReadOnlyTask taskToEdit = lastShownList.get(targetIndex - 1); + if (taskToEdit.getTaskType().equals("FLOATING")) { + return new CommandResult("Unable to edit date of floating task"); + } + Task newTask = new Task(taskToEdit.getDescription(), taskToEdit.getTaskType(), taskToEdit.getStartDate(), + + endDate, taskToEdit.getStartTime(), taskToEdit.getEndTime(), taskToEdit.getTaskPriority(), taskToEdit.getTaskStatus(), + + taskToEdit.getTags()); + try { + model.editTask(taskToEdit, newTask); + } catch (TaskNotFoundException pnfe) { + assert false : "The target task cannot be missing"; + } catch (UniqueTaskList.DuplicateTaskException e) { + return new CommandResult(AddCommand.MESSAGE_DUPLICATE_TASK); + } + + return new CommandResult(String.format(MESSAGE_EDIT_TASK_SUCCESS, taskToEdit, newTask)); + } +} +//@@author \ No newline at end of file diff --git a/src/main/java/seedu/taskell/logic/commands/EditEndTimeCommand.java b/src/main/java/seedu/taskell/logic/commands/EditEndTimeCommand.java new file mode 100644 index 000000000000..4c3ea11997f2 --- /dev/null +++ b/src/main/java/seedu/taskell/logic/commands/EditEndTimeCommand.java @@ -0,0 +1,69 @@ +//@@author A0142073R +package seedu.taskell.logic.commands; + +import seedu.taskell.commons.core.Messages; +import seedu.taskell.commons.core.UnmodifiableObservableList; +import seedu.taskell.commons.exceptions.IllegalValueException; +import seedu.taskell.model.tag.Tag; +import seedu.taskell.model.tag.UniqueTagList; +import seedu.taskell.model.task.ReadOnlyTask; +import seedu.taskell.model.task.Task; +import seedu.taskell.model.task.TaskDate; +import seedu.taskell.model.task.TaskPriority; +import seedu.taskell.model.task.TaskTime; +import seedu.taskell.model.task.UniqueTaskList; +import seedu.taskell.model.task.UniqueTaskList.TaskNotFoundException; + +/** + * Edits a task end time identified using it's last displayed index from the task manager. + */ +public class EditEndTimeCommand extends Command { + public static final String COMMAND_WORD = "edit-endTime"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Edits the end time of a task identified by the index number used in the last task listing.\n" + + "Parameters: INDEX (must be a positive integer) NEW_END_TIME\n" + + "Example: " + COMMAND_WORD + " 1 9pm "; + + public static final String MESSAGE_EDIT_TASK_SUCCESS = "Original Task: %1$s \n\nUpdatedTask: %2$s"; + + public final int targetIndex; + public final TaskTime endTime; + + public EditEndTimeCommand(int targetIndex, String newTime) throws IllegalValueException { + this.targetIndex = targetIndex; + this.endTime = new TaskTime(newTime); + } + + @Override + public CommandResult execute() { + + UnmodifiableObservableList lastShownList = model.getFilteredTaskList(); + + if (lastShownList.size() < targetIndex) { + indicateAttemptToExecuteIncorrectCommand(); + return new CommandResult(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + + ReadOnlyTask taskToEdit = lastShownList.get(targetIndex - 1); + if (taskToEdit.getTaskType().equals("FLOATING")) { + return new CommandResult("Unable to edit time of floating task"); + } + Task newTask = new Task(taskToEdit.getDescription(),taskToEdit.getTaskType(),taskToEdit.getStartDate(), taskToEdit.getEndDate(), taskToEdit.getStartTime(),endTime, + taskToEdit.getTaskPriority(),taskToEdit.getTaskStatus(), taskToEdit.getTags() + ); + try { + model.editTask(taskToEdit,newTask); + UndoCommand.addTaskToCommandHistory(newTask); + UndoCommand.addOldTaskToCommandHistory((Task) taskToEdit); + } catch (TaskNotFoundException pnfe) { + assert false : "The target task cannot be missing"; + } catch (UniqueTaskList.DuplicateTaskException e) { + UndoCommand.deletePreviousCommand(); + return new CommandResult(AddCommand.MESSAGE_DUPLICATE_TASK); + } + + return new CommandResult(String.format(MESSAGE_EDIT_TASK_SUCCESS, taskToEdit, newTask)); + } +} +//@@author \ No newline at end of file diff --git a/src/main/java/seedu/taskell/logic/commands/EditPriorityCommand.java b/src/main/java/seedu/taskell/logic/commands/EditPriorityCommand.java new file mode 100644 index 000000000000..f939254ca226 --- /dev/null +++ b/src/main/java/seedu/taskell/logic/commands/EditPriorityCommand.java @@ -0,0 +1,67 @@ +//@@author A0142073R +package seedu.taskell.logic.commands; + +import seedu.taskell.commons.core.Messages; +import seedu.taskell.commons.core.UnmodifiableObservableList; +import seedu.taskell.commons.exceptions.IllegalValueException; +import seedu.taskell.model.tag.Tag; +import seedu.taskell.model.tag.UniqueTagList; +import seedu.taskell.model.task.Description; +import seedu.taskell.model.task.ReadOnlyTask; +import seedu.taskell.model.task.Task; +import seedu.taskell.model.task.TaskDate; +import seedu.taskell.model.task.TaskPriority; +import seedu.taskell.model.task.TaskTime; +import seedu.taskell.model.task.UniqueTaskList; +import seedu.taskell.model.task.UniqueTaskList.TaskNotFoundException; + +/** + * Edits the priority of a task identified using it's last displayed index from the task manager. + */ +public class EditPriorityCommand extends Command { + public static final String COMMAND_WORD = "edit-priority"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Edits the priority of a task identified by the index number used in the last task listing.\n" + + "Parameters: INDEX (must be a positive integer) NEW_PRIORITY(must be between 0 to 3)\n" + + "Example: " + COMMAND_WORD + " 1 3 "; + + public static final String MESSAGE_EDIT_TASK_SUCCESS = "Original Task: %1$s \n\nUpdatedTask: %2$s"; + + public final int targetIndex; + public final TaskPriority taskPriority; + + public EditPriorityCommand(int targetIndex, String taskPriority) throws IllegalValueException { + this.targetIndex = targetIndex; + this.taskPriority = new TaskPriority(taskPriority); + } + + @Override + public CommandResult execute() { + + UnmodifiableObservableList lastShownList = model.getFilteredTaskList(); + + if (lastShownList.size() < targetIndex) { + indicateAttemptToExecuteIncorrectCommand(); + return new CommandResult(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + + ReadOnlyTask taskToEdit = lastShownList.get(targetIndex - 1); + Task newTask = new Task(taskToEdit.getDescription(),taskToEdit.getTaskType(),taskToEdit.getStartDate(), taskToEdit.getEndDate(), taskToEdit.getStartTime(),taskToEdit.getEndTime(), + taskPriority,taskToEdit.getTaskStatus(), taskToEdit.getTags() + ); + try { + model.editTask(taskToEdit,newTask); + UndoCommand.addTaskToCommandHistory(newTask); + UndoCommand.addOldTaskToCommandHistory((Task) taskToEdit); + } catch (TaskNotFoundException pnfe) { + assert false : "The target task cannot be missing"; + } catch (UniqueTaskList.DuplicateTaskException e) { + UndoCommand.deletePreviousCommand(); + return new CommandResult(AddCommand.MESSAGE_DUPLICATE_TASK); + } + + return new CommandResult(String.format(MESSAGE_EDIT_TASK_SUCCESS, taskToEdit, newTask)); + } +} +//@@author \ No newline at end of file diff --git a/src/main/java/seedu/taskell/logic/commands/EditStartDateCommand.java b/src/main/java/seedu/taskell/logic/commands/EditStartDateCommand.java new file mode 100644 index 000000000000..3b0de8d8d7c2 --- /dev/null +++ b/src/main/java/seedu/taskell/logic/commands/EditStartDateCommand.java @@ -0,0 +1,68 @@ +//@@author A0142073R +package seedu.taskell.logic.commands; + +import seedu.taskell.commons.core.Messages; +import seedu.taskell.commons.core.UnmodifiableObservableList; +import seedu.taskell.commons.exceptions.IllegalValueException; +import seedu.taskell.model.tag.Tag; +import seedu.taskell.model.tag.UniqueTagList; +import seedu.taskell.model.task.ReadOnlyTask; +import seedu.taskell.model.task.Task; +import seedu.taskell.model.task.TaskDate; +import seedu.taskell.model.task.TaskPriority; +import seedu.taskell.model.task.TaskTime; +import seedu.taskell.model.task.UniqueTaskList; +import seedu.taskell.model.task.UniqueTaskList.TaskNotFoundException; + +/** + * Edits a task startDate identified using it's last displayed index from the task + * manager. + */ +public class EditStartDateCommand extends Command { + public static final String COMMAND_WORD = "edit-startDate"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Edits the startDate of a task identified by the index number used in the last task listing.\n" + + "Parameters: INDEX (must be a positive integer) NEW_DATE\n" + "Example: " + COMMAND_WORD + " 1 8-8-2016 "; + + public static final String MESSAGE_EDIT_TASK_SUCCESS = "Original Task: %1$s \n\nUpdatedTask: %2$s"; + + public final int targetIndex; + public final TaskDate startDate; + + public EditStartDateCommand(int targetIndex, String startDate) throws IllegalValueException { + this.targetIndex = targetIndex; + this.startDate = new TaskDate(startDate); + } + + @Override + public CommandResult execute() { + + UnmodifiableObservableList lastShownList = model.getFilteredTaskList(); + + if (lastShownList.size() < targetIndex) { + indicateAttemptToExecuteIncorrectCommand(); + return new CommandResult(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + + ReadOnlyTask taskToEdit = lastShownList.get(targetIndex - 1); + if (taskToEdit.getTaskType().equals("FLOATING")) { + return new CommandResult("Unable to edit date of floating task"); + } + Task newTask = new Task(taskToEdit.getDescription(), taskToEdit.getTaskType(), startDate, taskToEdit.getEndDate(), + taskToEdit.getStartTime(), taskToEdit.getEndTime(), taskToEdit.getTaskPriority(), taskToEdit.getTaskStatus(), taskToEdit.getTags()); + try { + model.editTask(taskToEdit, newTask); + UndoCommand.addTaskToCommandHistory(newTask); + UndoCommand.addOldTaskToCommandHistory((Task) taskToEdit); + } catch (TaskNotFoundException pnfe) { + assert false : "The target task cannot be missing"; + } catch (UniqueTaskList.DuplicateTaskException e) { + UndoCommand.deletePreviousCommand(); + return new CommandResult(AddCommand.MESSAGE_DUPLICATE_TASK); + } + + return new CommandResult(String.format(MESSAGE_EDIT_TASK_SUCCESS, taskToEdit, newTask)); + } +} +//@@author \ No newline at end of file diff --git a/src/main/java/seedu/taskell/logic/commands/EditStartTimeCommand.java b/src/main/java/seedu/taskell/logic/commands/EditStartTimeCommand.java new file mode 100644 index 000000000000..42e7a38d7194 --- /dev/null +++ b/src/main/java/seedu/taskell/logic/commands/EditStartTimeCommand.java @@ -0,0 +1,70 @@ +//@@author A0142073R +package seedu.taskell.logic.commands; + +import seedu.taskell.commons.core.Messages; +import seedu.taskell.commons.core.UnmodifiableObservableList; +import seedu.taskell.commons.exceptions.IllegalValueException; +import seedu.taskell.model.tag.Tag; +import seedu.taskell.model.tag.UniqueTagList; +import seedu.taskell.model.task.ReadOnlyTask; +import seedu.taskell.model.task.Task; +import seedu.taskell.model.task.TaskDate; +import seedu.taskell.model.task.TaskPriority; +import seedu.taskell.model.task.TaskTime; +import seedu.taskell.model.task.UniqueTaskList; +import seedu.taskell.model.task.UniqueTaskList.TaskNotFoundException; + +/** + * Edits a task start time identified using it's last displayed index from the task manager. + */ +public class EditStartTimeCommand extends Command { + public static final String COMMAND_WORD = "edit-startTime"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Edits the start time of a task identified by the index number used in the last task listing.\n" + + "Parameters: INDEX (must be a positive integer) NEW_START_TIME\n" + + "Example: " + COMMAND_WORD + " 1 2pm "; + + public static final String MESSAGE_EDIT_TASK_SUCCESS = "Original Task: %1$s \n\nUpdatedTask: %2$s"; + + public final int targetIndex; + public final TaskTime startTime; + + public EditStartTimeCommand(int targetIndex, String newTime) throws IllegalValueException { + this.targetIndex = targetIndex; + this.startTime = new TaskTime(newTime); + } + + @Override + public CommandResult execute() { + + UnmodifiableObservableList lastShownList = model.getFilteredTaskList(); + + if (lastShownList.size() < targetIndex) { + indicateAttemptToExecuteIncorrectCommand(); + return new CommandResult(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + + ReadOnlyTask taskToEdit = lastShownList.get(targetIndex - 1); + + if (taskToEdit.getTaskType().equals("FLOATING")) { + return new CommandResult("Unable to edit time of floating task"); + } + Task newTask = new Task(taskToEdit.getDescription(),taskToEdit.getTaskType(),taskToEdit.getStartDate(), taskToEdit.getEndDate(), startTime,taskToEdit.getEndTime(), + taskToEdit.getTaskPriority(),taskToEdit.getTaskStatus(), taskToEdit.getTags() + ); + try { + model.editTask(taskToEdit,newTask); + UndoCommand.addTaskToCommandHistory(newTask); + UndoCommand.addOldTaskToCommandHistory((Task) taskToEdit); + } catch (TaskNotFoundException pnfe) { + assert false : "The target task cannot be missing"; + } catch (UniqueTaskList.DuplicateTaskException e) { + UndoCommand.deletePreviousCommand(); + return new CommandResult(AddCommand.MESSAGE_DUPLICATE_TASK); + } + + return new CommandResult(String.format(MESSAGE_EDIT_TASK_SUCCESS, taskToEdit, newTask)); + } +} +//@@author \ No newline at end of file diff --git a/src/main/java/seedu/address/logic/commands/ExitCommand.java b/src/main/java/seedu/taskell/logic/commands/ExitCommand.java similarity index 69% rename from src/main/java/seedu/address/logic/commands/ExitCommand.java rename to src/main/java/seedu/taskell/logic/commands/ExitCommand.java index d98233ce2a0b..e246ac2ed236 100644 --- a/src/main/java/seedu/address/logic/commands/ExitCommand.java +++ b/src/main/java/seedu/taskell/logic/commands/ExitCommand.java @@ -1,7 +1,7 @@ -package seedu.address.logic.commands; +package seedu.taskell.logic.commands; -import seedu.address.commons.core.EventsCenter; -import seedu.address.commons.events.ui.ExitAppRequestEvent; +import seedu.taskell.commons.core.EventsCenter; +import seedu.taskell.commons.events.ui.ExitAppRequestEvent; /** * Terminates the program. @@ -10,7 +10,7 @@ public class ExitCommand extends Command { public static final String COMMAND_WORD = "exit"; - public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting Address Book as requested ..."; + public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting Task Manager as requested ..."; public ExitCommand() {} diff --git a/src/main/java/seedu/taskell/logic/commands/FindCommand.java b/src/main/java/seedu/taskell/logic/commands/FindCommand.java new file mode 100644 index 000000000000..22c3e4bda5d5 --- /dev/null +++ b/src/main/java/seedu/taskell/logic/commands/FindCommand.java @@ -0,0 +1,31 @@ +/** @@author A0142130A **/ +package seedu.taskell.logic.commands; + +import java.util.Set; + +/** + * Finds and lists all tasks in task manager whose description and tags contains all of the argument keywords. + * Keyword matching is not case sensitive. + */ +public class FindCommand extends Command { + + public static final String COMMAND_WORD = "find"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all tasks whose description contains all of " + + "the specified keywords (not case-sensitive) and displays them as a list with index numbers.\n" + + "Parameters: KEYWORD [MORE_KEYWORDS]...\n" + + "Example: " + COMMAND_WORD + " milk bananas chicken"; + + private final Set keywords; + + public FindCommand(Set keywords) { + this.keywords = keywords; + } + + @Override + public CommandResult execute() { + model.updateFilteredTaskList(keywords); + return new CommandResult(getMessageForTaskListShownSummary(model.getFilteredTaskList().size())); + } + +} diff --git a/src/main/java/seedu/taskell/logic/commands/FindTagCommand.java b/src/main/java/seedu/taskell/logic/commands/FindTagCommand.java new file mode 100644 index 000000000000..c4d1e97b6c59 --- /dev/null +++ b/src/main/java/seedu/taskell/logic/commands/FindTagCommand.java @@ -0,0 +1,31 @@ +/** @@author A0142130A **/ +package seedu.taskell.logic.commands; + +import java.util.Set; + +/** + * Finds and lists all tasks in task manager whose description contains any of the argument keywords. + * Keyword matching is not case sensitive. + */ + +public class FindTagCommand extends Command { + + public static final String COMMAND_WORD = "find-tag"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all tasks whose description contain any of " + + "the specified keywords (not case-sensitive) and displays them as a list with index numbers.\n" + + "Parameters: KEYWORD [MORE_KEYWORDS]...\n" + + "Example: " + COMMAND_WORD + " help study homework"; + + private final Set keywords; + + public FindTagCommand(Set keywords) { + this.keywords = keywords; + } + + @Override + public CommandResult execute() { + model.updateFilteredTaskListByAnyKeyword(keywords); + return new CommandResult(getMessageForTaskListShownSummary(model.getFilteredTaskList().size())); + } +} diff --git a/src/main/java/seedu/address/logic/commands/HelpCommand.java b/src/main/java/seedu/taskell/logic/commands/HelpCommand.java similarity index 80% rename from src/main/java/seedu/address/logic/commands/HelpCommand.java rename to src/main/java/seedu/taskell/logic/commands/HelpCommand.java index 65af96940242..70181eeff23e 100644 --- a/src/main/java/seedu/address/logic/commands/HelpCommand.java +++ b/src/main/java/seedu/taskell/logic/commands/HelpCommand.java @@ -1,8 +1,8 @@ -package seedu.address.logic.commands; +package seedu.taskell.logic.commands; -import seedu.address.commons.core.EventsCenter; -import seedu.address.commons.events.ui.ShowHelpRequestEvent; +import seedu.taskell.commons.core.EventsCenter; +import seedu.taskell.commons.events.ui.ShowHelpRequestEvent; /** * Format full help instructions for every command for display. diff --git a/src/main/java/seedu/address/logic/commands/IncorrectCommand.java b/src/main/java/seedu/taskell/logic/commands/IncorrectCommand.java similarity index 92% rename from src/main/java/seedu/address/logic/commands/IncorrectCommand.java rename to src/main/java/seedu/taskell/logic/commands/IncorrectCommand.java index 491d9cb9da35..c317d003693e 100644 --- a/src/main/java/seedu/address/logic/commands/IncorrectCommand.java +++ b/src/main/java/seedu/taskell/logic/commands/IncorrectCommand.java @@ -1,4 +1,4 @@ -package seedu.address.logic.commands; +package seedu.taskell.logic.commands; /** diff --git a/src/main/java/seedu/taskell/logic/commands/ListAllCommand.java b/src/main/java/seedu/taskell/logic/commands/ListAllCommand.java new file mode 100644 index 000000000000..aa1422bfe787 --- /dev/null +++ b/src/main/java/seedu/taskell/logic/commands/ListAllCommand.java @@ -0,0 +1,18 @@ +//@@author A0148004R +package seedu.taskell.logic.commands; + +public class ListAllCommand extends Command { + + public static final String COMMAND_WORD = "list-all"; + + public static final String MESSAGE_SUCCESS = "Listed all tasks"; + + public ListAllCommand() {} + + @Override + public CommandResult execute() { + model.updateFilteredListToShowAll(); + return new CommandResult(MESSAGE_SUCCESS); + } +} +//@@author \ No newline at end of file diff --git a/src/main/java/seedu/taskell/logic/commands/ListCommand.java b/src/main/java/seedu/taskell/logic/commands/ListCommand.java new file mode 100644 index 000000000000..088dc66e499c --- /dev/null +++ b/src/main/java/seedu/taskell/logic/commands/ListCommand.java @@ -0,0 +1,32 @@ +//@@author A0148004R +package seedu.taskell.logic.commands; + + +/** + * Lists all tasks in the task manager to the user. + */ +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import seedu.taskell.model.task.TaskStatus; + +public class ListCommand extends Command { + + public static final String COMMAND_WORD = "list"; + + public static final String MESSAGE_SUCCESS = "Listed all tasks need to be done"; + + private Set keywordSet = new HashSet<>(Arrays.asList(TaskStatus.INCOMPLETE)); + + public ListCommand() { + + } + + @Override + public CommandResult execute() { + model.updateFilteredtaskListCompleted(keywordSet); + return new CommandResult(getMessageForTaskListShownSummary(model.getFilteredTaskList().size())); + } +} +//@@author diff --git a/src/main/java/seedu/taskell/logic/commands/ListDateCommand.java b/src/main/java/seedu/taskell/logic/commands/ListDateCommand.java new file mode 100644 index 000000000000..087fdef408cf --- /dev/null +++ b/src/main/java/seedu/taskell/logic/commands/ListDateCommand.java @@ -0,0 +1,32 @@ +//@@author A0142073R +package seedu.taskell.logic.commands; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import seedu.taskell.model.task.Task; + +public class ListDateCommand extends Command { + + public static final String COMMAND_WORD = "list-date"; + + public static final String MESSAGE_SUCCESS = "Listed all tasks on a given date"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Lists tasks on a specific date.\n" + + "Parameters: DATE (must be a positive integer)\n" + "Example: " + COMMAND_WORD + " 8-8-2016 "; + + private Set keywordSet; + + public ListDateCommand(String date) { + keywordSet = new HashSet<>(Arrays.asList(date, Task.EVENT_TASK)); + } + + @Override + public CommandResult execute() { + model.updateFilteredtaskListDate(keywordSet); + return new CommandResult(getMessageForTaskListShownSummary(model.getFilteredTaskList().size())); + } +} +//@@author \ No newline at end of file diff --git a/src/main/java/seedu/taskell/logic/commands/ListDoneCommand.java b/src/main/java/seedu/taskell/logic/commands/ListDoneCommand.java new file mode 100644 index 000000000000..36dd8bd35f75 --- /dev/null +++ b/src/main/java/seedu/taskell/logic/commands/ListDoneCommand.java @@ -0,0 +1,28 @@ +//@@author A0148004R +package seedu.taskell.logic.commands; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import seedu.taskell.model.task.TaskStatus; + +public class ListDoneCommand extends Command { + + public static final String COMMAND_WORD = "list-done"; + + public static final String MESSAGE_SUCCESS = "Listed all completed tasks"; + + private Set keywordSet = new HashSet<>(Arrays.asList(TaskStatus.FINISHED)); + + public ListDoneCommand() { + + } + + @Override + public CommandResult execute() { + model.updateFilteredtaskListCompleted(keywordSet); + return new CommandResult(getMessageForTaskListShownSummary(model.getFilteredTaskList().size())); + } +} +//@@author \ No newline at end of file diff --git a/src/main/java/seedu/taskell/logic/commands/ListPriorityCommand.java b/src/main/java/seedu/taskell/logic/commands/ListPriorityCommand.java new file mode 100644 index 000000000000..d2dfeab93679 --- /dev/null +++ b/src/main/java/seedu/taskell/logic/commands/ListPriorityCommand.java @@ -0,0 +1,44 @@ +//@@author A0142073R +package seedu.taskell.logic.commands; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import seedu.taskell.model.task.TaskPriority; + +public class ListPriorityCommand extends Command { + + public static final String COMMAND_WORD = "list-priority"; + + public static final String MESSAGE_SUCCESS = "Listed all completed tasks in descending priority"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": List the task with the specified priority. " + + "Parameters: INDEX (must be between 0 and 3 inclusive).\n" + + "Example: " + COMMAND_WORD + " 1"; + + private Set mostImportant = new HashSet<>(Arrays.asList(TaskPriority.HIGH_PRIORITY)); + private Set important = new HashSet<>(Arrays.asList(TaskPriority.MEDIUM_PRIORITY)); + private Set lessImportant = new HashSet<>(Arrays.asList(TaskPriority.LOW_PRIORITY)); + private Set leastImportant = new HashSet<>(Arrays.asList(TaskPriority.NO_PRIORITY)); + + private Set keyword; + public ListPriorityCommand(String priority) { + if(priority.equals(TaskPriority.HIGH_PRIORITY)) + keyword = mostImportant; + else if(priority.equals(TaskPriority.MEDIUM_PRIORITY)) + keyword = important; + else if(priority.equals(TaskPriority.LOW_PRIORITY)) + keyword = lessImportant; + else + keyword = leastImportant; + } + + @Override + public CommandResult execute() { + model.updateFilteredTaskListPriority(keyword); + return new CommandResult(getMessageForTaskListShownSummary(model.getFilteredTaskList().size())); + } +} +//@@author \ No newline at end of file diff --git a/src/main/java/seedu/taskell/logic/commands/ListUndoCommand.java b/src/main/java/seedu/taskell/logic/commands/ListUndoCommand.java new file mode 100644 index 000000000000..5f66e64dc0e2 --- /dev/null +++ b/src/main/java/seedu/taskell/logic/commands/ListUndoCommand.java @@ -0,0 +1,36 @@ +/** @@author A0142130A **/ +package seedu.taskell.logic.commands; + +import seedu.taskell.commons.core.EventsCenter; +import seedu.taskell.commons.events.ui.DisplayListChangedEvent; + +/** Lists a list of previous commands available for Undo operation + * */ +public class ListUndoCommand extends Command { + + public static final String COMMAND_WORD = "list-undo"; + + public static final String MESSAGE_SUCCESS = "Listed all commands available for undo."; + + private static ListUndoCommand self; + + public static ListUndoCommand getInstance() { + if (self == null) { + self = new ListUndoCommand(); + } + + return self; + } + + @Override + public CommandResult execute() { + indicateDisplayListChanged(); + return new CommandResult(MESSAGE_SUCCESS); + } + + public void indicateDisplayListChanged() { + EventsCenter.getInstance().post( + new DisplayListChangedEvent(UndoCommand.getListOfCommandHistoryText())); + } + +} diff --git a/src/main/java/seedu/taskell/logic/commands/SaveStorageLocationCommand.java b/src/main/java/seedu/taskell/logic/commands/SaveStorageLocationCommand.java new file mode 100644 index 000000000000..d293c82834a0 --- /dev/null +++ b/src/main/java/seedu/taskell/logic/commands/SaveStorageLocationCommand.java @@ -0,0 +1,105 @@ +/** @@author A0142130A **/ +package seedu.taskell.logic.commands; + +import java.io.IOException; +import java.util.logging.Logger; + +import seedu.taskell.commons.core.Config; +import seedu.taskell.commons.core.EventsCenter; +import seedu.taskell.commons.core.LogsCenter; +import seedu.taskell.commons.events.storage.StorageLocationChangedEvent; +import seedu.taskell.model.ReadOnlyTaskManager; +import seedu.taskell.storage.JsonConfigStorage; +import seedu.taskell.storage.Storage; + +/** Saves current data file to new filepath. + * */ + +public class SaveStorageLocationCommand extends Command { + + private Logger logger = LogsCenter.getLogger(SaveStorageLocationCommand.class.getName()); + + public static final String COMMAND_WORD = "save"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Saves data file to new location specified. " + + "New files can be auto-created as long as given directory is valid.\n" + + "Parameters: FILEPATH (must be valid)\n" + + "Example: " + COMMAND_WORD + " C:/Users/chicken/Desktop/cat"; + + private static final String MESSAGE_SUCCESS = "Data successfully saved to new location."; + private static final String MESSAGE_INVALID_PATH = "Filepath given is invalid. Filepath will be reset to old path."; + + private static Config config; + private String newStorageFilePath, oldStorageFilePath; + private ReadOnlyTaskManager taskManager; + private static JsonConfigStorage jsonConfigStorage; + private static Storage storage; + + public SaveStorageLocationCommand(String newStorageFilePath) { + this.oldStorageFilePath = config.getTaskManagerFilePath(); + logger.info("Old file path: " + oldStorageFilePath); + + this.newStorageFilePath = newStorageFilePath.trim().replace("\\", "/") + "/taskmanager.xml"; + logger.info("New file path: " + this.newStorageFilePath); + jsonConfigStorage = new JsonConfigStorage(Config.DEFAULT_CONFIG_FILE); + } + + public static void setConfig(Config c) { + config = c; + } + + public static void setStorage(Storage s) { + storage = s; + } + + @Override + public CommandResult execute() { + assert config != null; + assert jsonConfigStorage != null; + + taskManager = model.getTaskManager(); + + config.setTaskManagerFilePath(newStorageFilePath); + indicateStorageLocationChanged(); + try { + storage.saveTaskManager(taskManager, newStorageFilePath); + } catch (IOException e) { + handleInvalidFilePathException(); + return new CommandResult(MESSAGE_INVALID_PATH); + } + + saveToConfigJson(); + model.updateFilteredListToShowAll(); + return new CommandResult(MESSAGE_SUCCESS); + } + + private void indicateStorageLocationChanged() { + assert config != null; + EventsCenter.getInstance().post(new StorageLocationChangedEvent(config)); + } + + private void handleInvalidFilePathException() { + logger.info("Error writing to filepath. Handling data save exception."); + assert config != null; + + config.setTaskManagerFilePath(oldStorageFilePath); //set back to old filepath + indicateStorageLocationChanged(); + + try { + storage.saveTaskManager(taskManager, newStorageFilePath); + } catch (IOException e) { + logger.severe("Error saving task manager"); + } + + saveToConfigJson(); + } + + private void saveToConfigJson() { + try { + jsonConfigStorage.saveConfigFile(config); + } catch (IOException e) { + logger.severe("save to config json error"); + } + } + +} diff --git a/src/main/java/seedu/taskell/logic/commands/SelectCommand.java b/src/main/java/seedu/taskell/logic/commands/SelectCommand.java new file mode 100644 index 000000000000..88c591b77775 --- /dev/null +++ b/src/main/java/seedu/taskell/logic/commands/SelectCommand.java @@ -0,0 +1,44 @@ +package seedu.taskell.logic.commands; + +import seedu.taskell.commons.core.EventsCenter; +import seedu.taskell.commons.core.Messages; +import seedu.taskell.commons.core.UnmodifiableObservableList; +import seedu.taskell.commons.events.ui.JumpToListRequestEvent; +import seedu.taskell.model.task.ReadOnlyTask; + +/** + * Selects a task identified using it's last displayed index from the task manager. + */ +public class SelectCommand extends Command { + + public final int targetIndex; + + public static final String COMMAND_WORD = "select"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Selects the task identified by the index number used in the last task listing.\n" + + "Parameters: INDEX (must be a positive integer)\n" + + "Example: " + COMMAND_WORD + " 1"; + + public static final String MESSAGE_SELECT_TASK_SUCCESS = "Selected Task: %1$s"; + + public SelectCommand(int targetIndex) { + this.targetIndex = targetIndex; + } + + @Override + public CommandResult execute() { + + UnmodifiableObservableList lastShownList = model.getFilteredTaskList(); + + if (lastShownList.size() < targetIndex) { + indicateAttemptToExecuteIncorrectCommand(); + return new CommandResult(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + + EventsCenter.getInstance().post(new JumpToListRequestEvent(targetIndex - 1)); + return new CommandResult(String.format(MESSAGE_SELECT_TASK_SUCCESS, targetIndex)); + + } + +} diff --git a/src/main/java/seedu/taskell/logic/commands/UndoCommand.java b/src/main/java/seedu/taskell/logic/commands/UndoCommand.java new file mode 100644 index 000000000000..03e82acb57b2 --- /dev/null +++ b/src/main/java/seedu/taskell/logic/commands/UndoCommand.java @@ -0,0 +1,291 @@ +/** @@author A0142130A **/ +package seedu.taskell.logic.commands; + +import java.util.ArrayList; +import java.util.logging.Logger; + +import seedu.taskell.commons.core.EventsCenter; +import seedu.taskell.commons.core.LogsCenter; +import seedu.taskell.commons.events.ui.DisplayListChangedEvent; +import seedu.taskell.model.CommandHistory; +import seedu.taskell.model.Model; +import seedu.taskell.model.task.Task; +import seedu.taskell.model.task.UniqueTaskList.DuplicateTaskException; +import seedu.taskell.model.task.UniqueTaskList.TaskNotFoundException; + +/** + * Undo previously executed command (add or delete only for now) + * Note: only for current session only (meaning after app is closed, history will be cleared) + */ +public class UndoCommand extends Command { + private static final Logger logger = LogsCenter.getLogger(UndoCommand.class.getName()); + + public static final String COMMAND_WORD = "undo"; + public static final String EDIT = "edit"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Undo a previously executed command.\n" + + "Parameters: INDEX (must be a positive integer)\n" + + "Example: " + COMMAND_WORD + " 3"; + + private static final String MESSAGE_DELETE_TASK_SUCCESS = "Deleted Task: %1$s"; + private static final String MESSAGE_ADD_TASK_SUCCESS = "Task added back: %1$s"; + private static final String MESSAGE_EDIT_TASK_SUCCESS = "Task edited back to old version: %1$s"; + + private static final String MESSAGE_DUPLICATE_TASK = "This task already exists in the task manager"; + private static final String MESSAGE_NO_TASK_TO_UNDO = "No add or delete commands available to undo."; + private static final String MESSAGE_COMMAND_HISTORY_EMPTY = "No command history available for undo."; + private static final String MESSAGE_INVALID_INDEX = "Index is invalid"; + + private static ArrayList commandHistoryList; + private static UndoCommand self; + + private int index; + private CommandHistory commandHistory; + + public UndoCommand(int index) { + logger.info("Creating UndoCommand with index: " + index); + this.index = index; + } + + public static UndoCommand getInstance() { + if (self == null) { + self = new UndoCommand(0); + } + + return self; + } + + public static ArrayList getListOfCommandHistoryText() { + assert commandHistoryList != null; + + UndoCommand.getInstance().updateCommandList(); + + ArrayList list = new ArrayList<>(); + for (CommandHistory history: commandHistoryList) { + list.add(history.getCommandText()); + } + + return list; + } + + //removes commandHistory with tasks not present in system + private void updateCommandList() { + if (model == null) { + logger.severe("model is null"); + return; + } + for (CommandHistory commandHistory: commandHistoryList) { + if (isCommandTypeAddOrEdit(commandHistory) + && !model.isTaskPresent(commandHistory.getTask())) { + commandHistoryList.remove(commandHistory); + } else if (isUndoEditCommand(commandHistory) + && !model.isTaskPresent(commandHistory.getOldTask())) { + commandHistoryList.remove(commandHistory); + } + } + + } + + private boolean isCommandTypeAddOrEdit(CommandHistory commandHistory) { + return (commandHistory.getCommandType().contains(AddCommand.COMMAND_WORD) + || commandHistory.getCommandType().contains(EDIT)) + && !commandHistory.isRedoTrue(); + } + + private boolean isUndoEditCommand(CommandHistory commandHistory) { + return commandHistory.isRedoTrue() + && commandHistory.getCommandType().contains(EDIT); + } + + @Override + public CommandResult execute() { + + if (commandHistoryList.isEmpty()) { + return new CommandResult(String.format(MESSAGE_COMMAND_HISTORY_EMPTY)); + } else if (index > commandHistoryList.size()) { + return new CommandResult(String.format(MESSAGE_INVALID_INDEX)); + } + + commandHistory = commandHistoryList.get(getOffset(index)); + + if (commandHistory.isRedoTrue()) { + return redoUndo(); + } + + switch (commandHistory.getCommandType()) { + case AddCommand.COMMAND_WORD: + return undoAdd(); + case DeleteCommand.COMMAND_WORD: + return undoDelete(); + case EditStartDateCommand.COMMAND_WORD: + return undoEdit(); + case EditEndDateCommand.COMMAND_WORD: + return undoEdit(); + case EditDescriptionCommand.COMMAND_WORD_1: + return undoEdit(); + case EditDescriptionCommand.COMMAND_WORD_2: + return undoEdit(); + case EditStartTimeCommand.COMMAND_WORD: + return undoEdit(); + case EditEndTimeCommand.COMMAND_WORD: + return undoEdit(); + case EditPriorityCommand.COMMAND_WORD: + return undoEdit(); + default: + logger.severe("CommandHistory is invalid"); + return new CommandResult(String.format(MESSAGE_NO_TASK_TO_UNDO)); + } + } + + private CommandResult redoUndo() { + switch (commandHistory.getCommandType()) { + case AddCommand.COMMAND_WORD: + return undoDelete(); + case DeleteCommand.COMMAND_WORD: + return undoAdd(); + case EditStartDateCommand.COMMAND_WORD: + return redoEdit(); + case EditEndDateCommand.COMMAND_WORD: + return redoEdit(); + case EditDescriptionCommand.COMMAND_WORD_1: + return redoEdit(); + case EditDescriptionCommand.COMMAND_WORD_2: + return redoEdit(); + case EditStartTimeCommand.COMMAND_WORD: + return redoEdit(); + case EditEndTimeCommand.COMMAND_WORD: + return redoEdit(); + case EditPriorityCommand.COMMAND_WORD: + return redoEdit(); + default: + logger.severe("CommandHistory is invalid"); + return new CommandResult(String.format(MESSAGE_NO_TASK_TO_UNDO)); + } + } + + private CommandResult undoEdit() { + try { + model.editTask(commandHistory.getTask(), commandHistory.getOldTask()); + deleteCommandHistory(); + addUndoCommand(commandHistory); + indicateDisplayListChanged(); + return new CommandResult(String.format(MESSAGE_EDIT_TASK_SUCCESS, commandHistory.getOldTask())); + } catch (DuplicateTaskException e) { + return new CommandResult(MESSAGE_DUPLICATE_TASK); + } catch (TaskNotFoundException e) { + assert false : "The target task cannot be missing"; + } + + assert false: "Undo edit should return a command result"; + return null; + } + + private CommandResult redoEdit() { + try { + model.editTask(commandHistory.getOldTask(), commandHistory.getTask()); + deleteCommandHistory(); + indicateDisplayListChanged(); + return new CommandResult(String.format(MESSAGE_EDIT_TASK_SUCCESS, commandHistory.getTask())); + } catch (DuplicateTaskException e) { + return new CommandResult(MESSAGE_DUPLICATE_TASK); + } catch (TaskNotFoundException e) { + assert false : "The target task cannot be missing"; + } + + assert false: "Redo edit should return a command result"; + return null; + } + + private CommandResult undoDelete() { + try { + model.addTask(commandHistory.getTask()); + deleteCommandHistory(); + addUndoCommand(commandHistory); + indicateDisplayListChanged(); + return new CommandResult(String.format(MESSAGE_ADD_TASK_SUCCESS, commandHistory.getTask())); + } catch (DuplicateTaskException e) { + return new CommandResult(MESSAGE_DUPLICATE_TASK); + } + } + + private CommandResult undoAdd() { + try { + model.deleteTask(commandHistory.getTask()); + deleteCommandHistory(); + addUndoCommand(commandHistory); + indicateDisplayListChanged(); + } catch (TaskNotFoundException e) { + assert false : "The target task cannot be missing"; + } + return new CommandResult(String.format(MESSAGE_DELETE_TASK_SUCCESS, commandHistory.getTask())); + } + + private void deleteCommandHistory() { + commandHistoryList.remove(commandHistory); + } + + private void addUndoCommand(CommandHistory commandHistory) { + if (commandHistory.isRedoTrue()) { + return; + } + commandHistory.setCommandText("undo " + commandHistory.getCommandText()); + commandHistory.setToRedoToTrue(); + commandHistoryList.add(commandHistory); + } + + /******** static methods *********/ + + public static void initializeCommandHistory() { + if (commandHistoryList == null) { + commandHistoryList = new ArrayList<>(); + } + } + + public static void clearCommandHistory() { + commandHistoryList.clear(); + } + + private static int getOffset(int index) { + return index - 1; + } + + public static void addCommandToHistory(String commandText, + String commandType) { + assert commandHistoryList != null; + commandHistoryList.add(new CommandHistory(commandText, commandType)); + } + + public static void addTaskToCommandHistory(Task task) { + logger.info("Adding task to history"); + if (commandHistoryList.isEmpty()) { + logger.warning("No command history to add task to"); + return; + } + + commandHistoryList.get(getOffset(commandHistoryList.size())).setTask(task); + } + + public static void addOldTaskToCommandHistory(Task task) { + logger.info("Adding old task to history"); + if (commandHistoryList.isEmpty()) { + logger.warning("No command history to add task to"); + return; + } + + commandHistoryList.get(getOffset(commandHistoryList.size())).setOldTask(task); + } + + public static void deletePreviousCommand() { + logger.info("Command unsuccessfully executed. Deleting command history."); + if (commandHistoryList.isEmpty()) { + logger.warning("No command history to delete"); + return; + } + commandHistoryList.remove(getOffset(commandHistoryList.size())); + } + + /****** Event ******/ + public void indicateDisplayListChanged() { + EventsCenter.getInstance().post( + new DisplayListChangedEvent(getListOfCommandHistoryText())); + } +} diff --git a/src/main/java/seedu/taskell/logic/commands/ViewCalendarCommand.java b/src/main/java/seedu/taskell/logic/commands/ViewCalendarCommand.java new file mode 100644 index 000000000000..502cea7b9cd9 --- /dev/null +++ b/src/main/java/seedu/taskell/logic/commands/ViewCalendarCommand.java @@ -0,0 +1,26 @@ +/** @@author A0142130A **/ +package seedu.taskell.logic.commands; + +import seedu.taskell.commons.core.EventsCenter; +import seedu.taskell.commons.events.ui.DisplayCalendarViewEvent; + +/** Shows calendar view on right panel + * */ +public class ViewCalendarCommand extends Command { + + public static final String COMMAND_WORD_1 = "calendar"; + public static final String COMMAND_WORD_2 = "cal"; + + private static final String MESSAGE_SUCCESS = "Displayed calendar."; + + @Override + public CommandResult execute() { + indicateDisplayCalendarView(); + return new CommandResult(String.format(MESSAGE_SUCCESS)); + } + + private void indicateDisplayCalendarView() { + EventsCenter.getInstance().post(new DisplayCalendarViewEvent()); + } + +} diff --git a/src/main/java/seedu/taskell/logic/parser/Parser.java b/src/main/java/seedu/taskell/logic/parser/Parser.java new file mode 100644 index 000000000000..8c6686a3a85e --- /dev/null +++ b/src/main/java/seedu/taskell/logic/parser/Parser.java @@ -0,0 +1,873 @@ +package seedu.taskell.logic.parser; + +import static seedu.taskell.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.taskell.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; +import static seedu.taskell.commons.core.Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX; + +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import seedu.taskell.commons.exceptions.IllegalValueException; +import seedu.taskell.commons.util.StringUtil; +import seedu.taskell.logic.commands.*; +import seedu.taskell.model.tag.Tag; +import seedu.taskell.model.task.Task; +import seedu.taskell.model.task.TaskDate; +import seedu.taskell.model.task.TaskPriority; +import seedu.taskell.model.task.TaskTime; + +/** + * 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+)*)"); // one + + private static final Pattern TASK_DATA_ARGS_FORMAT = // '/' forward slashes + // are reserved for + // delimiter prefixes + Pattern.compile("(?[^/]+)" + " (?p?)p/(?[^/]+)" + + " (?p?)p/(?[^/]+)" + " (?p?)e/(?[^/]+)" + + " (?p?)e/(?[^/]+)" + + " (?p?)a/(?[^/]+)" + + " (?p?)a/(?[^/]+)" + "(?(?: t/[^/]+)*)"); // variable + // number + private static final String BY = "by"; + private static final String ON = "on"; + private static final String AT = "at"; + private static final String FROM = "from"; + private static final String TO = "to"; + + 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: + UndoCommand.addCommandToHistory(userInput, commandWord); + return prepareAdd(arguments); + + case SelectCommand.COMMAND_WORD: + return prepareSelect(arguments); + + case DeleteCommand.COMMAND_WORD: + UndoCommand.addCommandToHistory(userInput, commandWord); + return prepareDelete(arguments); + + case EditStartDateCommand.COMMAND_WORD: + UndoCommand.addCommandToHistory(userInput, commandWord); + return prepareEditStartDate(arguments); + + case EditEndDateCommand.COMMAND_WORD: + UndoCommand.addCommandToHistory(userInput, commandWord); + return prepareEditEndDate(arguments); + + case EditDescriptionCommand.COMMAND_WORD_1: + UndoCommand.addCommandToHistory(userInput, commandWord); + return prepareEditDescription(arguments); + + case EditDescriptionCommand.COMMAND_WORD_2: + UndoCommand.addCommandToHistory(userInput, commandWord); + return prepareEditDescription(arguments); + + case EditStartTimeCommand.COMMAND_WORD: + UndoCommand.addCommandToHistory(userInput, commandWord); + return prepareEditStartTime(arguments); + + case EditEndTimeCommand.COMMAND_WORD: + UndoCommand.addCommandToHistory(userInput, commandWord); + return prepareEditEndTime(arguments); + + case EditPriorityCommand.COMMAND_WORD: + UndoCommand.addCommandToHistory(userInput, commandWord); + return prepareEditPriority(arguments); + + case ClearCommand.COMMAND_WORD: + return new ClearCommand(); + + case FindCommand.COMMAND_WORD: + return prepareFind(arguments); + + case FindTagCommand.COMMAND_WORD: + return prepareFindByTag(arguments); + + case ListCommand.COMMAND_WORD: + return new ListCommand(); + + case ListAllCommand.COMMAND_WORD: + return new ListAllCommand(); + + case ListDoneCommand.COMMAND_WORD: + return new ListDoneCommand(); + + case ListDateCommand.COMMAND_WORD: + return prepareListDate(arguments); + + case ListPriorityCommand.COMMAND_WORD: + return prepareListPriority(arguments); + + case UndoCommand.COMMAND_WORD: + return prepareUndo(arguments); + + case SaveStorageLocationCommand.COMMAND_WORD: + return prepareSaveStorageLocation(arguments); + + case ExitCommand.COMMAND_WORD: + return new ExitCommand(); + + case HelpCommand.COMMAND_WORD: + return new HelpCommand(); + + case DoneCommand.COMMAND_WORD: + return prepareDone(arguments); + + case ListUndoCommand.COMMAND_WORD: + return new ListUndoCommand(); + + case ViewCalendarCommand.COMMAND_WORD_1: + return new ViewCalendarCommand(); + + case ViewCalendarCommand.COMMAND_WORD_2: + return new ViewCalendarCommand(); + + + + default: + return new IncorrectCommand(MESSAGE_UNKNOWN_COMMAND); + } + } + + //@@author A0142073R + private Command prepareListDate(String arguments) { + if (arguments.isEmpty()) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, ListDateCommand.MESSAGE_USAGE)); + } + StringTokenizer st = new StringTokenizer(arguments.trim(), " "); + String date = st.nextToken(); + if (!TaskDate.isValidDate(date)) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, ListDateCommand.MESSAGE_USAGE)); + } + + return new ListDateCommand(date); + } + + + private Command prepareListPriority(String args) { + if (args.isEmpty()) { + return new IncorrectCommand( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, ListPriorityCommand.MESSAGE_USAGE)); + } + StringTokenizer st = new StringTokenizer(args.trim(), " "); + String intValue = st.nextToken(); + if (st.hasMoreTokens()) { + return new IncorrectCommand( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, ListPriorityCommand.MESSAGE_USAGE)); + } + if (!isInt(intValue)) { + return new IncorrectCommand( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditEndDateCommand.MESSAGE_USAGE)); + } + int targetIdx = Integer.valueOf(intValue); + if (targetIdx < 0 || targetIdx > 3) { + return new IncorrectCommand( + String.format(MESSAGE_INVALID_TASK_DISPLAYED_INDEX, ListPriorityCommand.MESSAGE_USAGE)); + } else + return new ListPriorityCommand(intValue); + } + + /** + * Parses arguments in the context of the edit task startDate command. + * + * @param args + * full command args string + * @return the prepared command + */ + private Command prepareEditStartDate(String args) { + String arguments = ""; + if (args.isEmpty()) { + UndoCommand.deletePreviousCommand(); + return new IncorrectCommand( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditStartDateCommand.MESSAGE_USAGE)); + } + StringTokenizer st = new StringTokenizer(args.trim(), " "); + String intValue = st.nextToken(); + if (!isInt(intValue)) { + UndoCommand.deletePreviousCommand(); + return new IncorrectCommand( + String.format(MESSAGE_INVALID_TASK_DISPLAYED_INDEX, EditEndDateCommand.MESSAGE_USAGE)); + } + int targetIdx = Integer.valueOf(intValue); + arguments = st.nextToken(); + if (st.hasMoreTokens()) { + return new IncorrectCommand( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditStartDateCommand.MESSAGE_USAGE)); + } + if (!TaskDate.isValidDate(arguments)) { + UndoCommand.deletePreviousCommand(); + return new IncorrectCommand( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditStartDateCommand.MESSAGE_USAGE)); + } + + try { + return new EditStartDateCommand(targetIdx, arguments); + } catch (IllegalValueException ive) { + UndoCommand.deletePreviousCommand(); + return new IncorrectCommand(ive.getMessage()); + } + } + + + /** + * Parses arguments in the context of the edit task description command. + * + * @param args + * full command args string + * @return the prepared command + */ + private Command prepareEditDescription(String args) { + String arguments = ""; + if (args.isEmpty()) { + UndoCommand.deletePreviousCommand(); + return new IncorrectCommand( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditDescriptionCommand.MESSAGE_USAGE)); + } + StringTokenizer st = new StringTokenizer(args.trim(), " "); + String intValue = st.nextToken(); + if (!isInt(intValue)) { + UndoCommand.deletePreviousCommand(); + return new IncorrectCommand( + String.format(MESSAGE_INVALID_TASK_DISPLAYED_INDEX, EditDescriptionCommand.MESSAGE_USAGE)); + } + int targetIdx = Integer.valueOf(intValue); + while (st.hasMoreTokens()) { + arguments += st.nextToken() + " "; + } + arguments = arguments.trim(); + try { + return new EditDescriptionCommand(targetIdx, arguments); + } catch (IllegalValueException ive) { + UndoCommand.deletePreviousCommand(); + return new IncorrectCommand(ive.getMessage()); + } + } + + + /** + * Parses arguments in the context of the edit start time command. + * + * @param args + * full command args string + * @return the prepared command + */ + private Command prepareEditStartTime(String args) { + String arguments = ""; + if (args.isEmpty()) { + UndoCommand.deletePreviousCommand(); + return new IncorrectCommand( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditStartTimeCommand.MESSAGE_USAGE)); + } + StringTokenizer st = new StringTokenizer(args.trim(), " "); + String intValue = st.nextToken(); + if (!isInt(intValue)) { + UndoCommand.deletePreviousCommand(); + return new IncorrectCommand( + String.format(MESSAGE_INVALID_TASK_DISPLAYED_INDEX, EditStartTimeCommand.MESSAGE_USAGE)); + } + + int targetIdx = Integer.valueOf(intValue); + arguments = st.nextToken(); + if (st.hasMoreTokens()) { + UndoCommand.deletePreviousCommand(); + return new IncorrectCommand( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditStartTimeCommand.MESSAGE_USAGE)); + } + + if (!TaskTime.isValidTime(arguments)) { + UndoCommand.deletePreviousCommand(); + return new IncorrectCommand( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditStartTimeCommand.MESSAGE_USAGE)); + } + + try { + return new EditStartTimeCommand(targetIdx, arguments); + } catch (IllegalValueException ive) { + UndoCommand.deletePreviousCommand(); + return new IncorrectCommand(ive.getMessage()); + } + } + + /** + * Parses arguments in the context of the edit task endDate command. + * + * @param args + * full command args string + * @return the prepared command + */ + private Command prepareEditEndDate(String args) { + String arguments = ""; + if (args.isEmpty()) { + UndoCommand.deletePreviousCommand(); + return new IncorrectCommand( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditEndDateCommand.MESSAGE_USAGE)); + } + StringTokenizer st = new StringTokenizer(args.trim(), " "); + String intValue = st.nextToken(); + if (!isInt(intValue)) { + UndoCommand.deletePreviousCommand(); + return new IncorrectCommand( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditEndDateCommand.MESSAGE_USAGE)); + } + int targetIdx = Integer.valueOf(intValue); + arguments = st.nextToken(); + if (st.hasMoreTokens() || !TaskDate.isValidDate(arguments)) { + UndoCommand.deletePreviousCommand(); + return new IncorrectCommand( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditEndDateCommand.MESSAGE_USAGE)); + } + + try { + return new EditEndDateCommand(targetIdx, arguments); + } catch (IllegalValueException ive) { + UndoCommand.deletePreviousCommand(); + return new IncorrectCommand(ive.getMessage()); + } + } + + + /** + * Parses arguments in the context of the edit task end time command. + * + * @param args + * full command args string + * @return the prepared command + */ + private Command prepareEditEndTime(String args) { + String arguments = ""; + if (args.isEmpty()) { + UndoCommand.deletePreviousCommand(); + return new IncorrectCommand( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditEndTimeCommand.MESSAGE_USAGE)); + } + StringTokenizer st = new StringTokenizer(args.trim(), " "); + String intValue = st.nextToken(); + if (!isInt(intValue)) { + UndoCommand.deletePreviousCommand(); + return new IncorrectCommand( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditEndTimeCommand.MESSAGE_USAGE)); + } + int targetIdx = Integer.valueOf(intValue); + arguments = st.nextToken(); + if (st.hasMoreTokens() || !TaskTime.isValidTime(arguments)) { + UndoCommand.deletePreviousCommand(); + return new IncorrectCommand( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditEndTimeCommand.MESSAGE_USAGE)); + } + + try { + return new EditEndTimeCommand(targetIdx, arguments); + } catch (IllegalValueException ive) { + UndoCommand.deletePreviousCommand(); + return new IncorrectCommand(ive.getMessage()); + } + } + + + /** + * Parses arguments in the context of the edit task priority command. + * + * @param args + * full command args string + * @return the prepared command + */ + private Command prepareEditPriority(String args) { + String arguments = ""; + if (args.isEmpty()) { + UndoCommand.deletePreviousCommand(); + return new IncorrectCommand( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditPriorityCommand.MESSAGE_USAGE)); + } + StringTokenizer st = new StringTokenizer(args.trim(), " "); + int targetIdx = Integer.valueOf(st.nextToken()); + arguments = st.nextToken(); + if (st.hasMoreTokens() || !TaskPriority.isValidPriority(arguments)) { + UndoCommand.deletePreviousCommand(); + return new IncorrectCommand( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditPriorityCommand.MESSAGE_USAGE)); + } + try { + return new EditPriorityCommand(targetIdx, arguments); + } catch (IllegalValueException ive) { + UndoCommand.deletePreviousCommand(); + return new IncorrectCommand(ive.getMessage()); + } + } + + //@@author + + + //@@author A0139257X + + /** + * 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) { + if (args.isEmpty()) { + UndoCommand.deletePreviousCommand(); + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); + } + + ArrayList argsList = tokenizeArguments(args); + Queue initialQueue = initialiseArgQueue(argsList); + Queue descriptionQueue = new LinkedList(); + Queue byQueue = new LinkedList(); + Queue onQueue = new LinkedList(); + Queue atQueue = new LinkedList(); + Queue fromQueue = new LinkedList(); + Queue toQueue = new LinkedList(); + + String description = ""; + String startDate = TaskDate.DEFAULT_DATE; + String endDate = startDate; + String startTime = TaskTime.DEFAULT_START_TIME; + String endTime = TaskTime.DEFAULT_END_TIME; + String token = ""; + String taskPriority = TaskPriority.DEFAULT_PRIORITY; + String tagString = ""; + + int priorityCount = 0; + + boolean hasStartDate = false; + boolean hasEndDate = false; + boolean hasStartTime = false; + boolean hasEndTime = false; + + while (!initialQueue.isEmpty()) { + token = initialQueue.poll().trim(); + String tempToken = ""; + + if (!token.equals(BY) && !token.equals(ON) && !token.equals(AT) && !token.equals(FROM) && !token.equals(TO) + && !TaskDate.isValidDate(token) && !TaskTime.isValidTime(token) && !token.startsWith(Tag.PREFIX) + && !token.startsWith(TaskPriority.PREFIX)) { + tempToken = flushQueue(byQueue, onQueue, atQueue, fromQueue, toQueue); + if (!tempToken.isEmpty()) { + descriptionQueue.offer(tempToken); + } + descriptionQueue.offer(token); + continue; + } else if (token.equals(BY)) { + tempToken = flushQueue(byQueue, onQueue, atQueue, fromQueue, toQueue); + if (!tempToken.isEmpty()) { + descriptionQueue.offer(tempToken); + } + byQueue.offer(token); + continue; + } else if (token.equals(ON)) { + tempToken = flushQueue(byQueue, onQueue, atQueue, fromQueue, toQueue); + if (!tempToken.isEmpty()) { + descriptionQueue.offer(tempToken); + } + onQueue.offer(token); + continue; + } else if (token.equals(AT)) { + tempToken = flushQueue(byQueue, onQueue, atQueue, fromQueue, toQueue); + if (!tempToken.isEmpty()) { + descriptionQueue.offer(tempToken); + } + atQueue.offer(token); + continue; + } else if (token.equals(FROM)) { + tempToken = flushQueue(byQueue, onQueue, atQueue, fromQueue, toQueue); + if (!tempToken.isEmpty()) { + descriptionQueue.offer(tempToken); + } + fromQueue.offer(token); + continue; + } else if (token.equals(TO)) { + tempToken = flushQueue(byQueue, onQueue, atQueue, fromQueue, toQueue); + if (!tempToken.isEmpty()) { + descriptionQueue.offer(tempToken); + } + toQueue.offer(token); + continue; + } else if (token.startsWith(Tag.PREFIX)) { + tempToken = flushQueue(byQueue, onQueue, atQueue, fromQueue, toQueue); + if (!tempToken.isEmpty()) { + descriptionQueue.offer(tempToken); + } + tagString += " " + token; + continue; + } else if (token.startsWith(TaskPriority.PREFIX)) { + tempToken = flushQueue(byQueue, onQueue, atQueue, fromQueue, toQueue); + if (!tempToken.isEmpty()) { + descriptionQueue.offer(tempToken); + } + if (priorityCount > 0) { + return new IncorrectCommand( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); + } else { + taskPriority = token.substring(token.indexOf(TaskPriority.PREFIX) + 2); + priorityCount++; + } + continue; + } else if (TaskDate.isValidDate(token)) { + if (byQueue.isEmpty() && onQueue.isEmpty() && atQueue.isEmpty() && fromQueue.isEmpty() + && toQueue.isEmpty()) { + descriptionQueue.offer(token); + } else if (!onQueue.isEmpty()) { + if (!hasStartDate) { + onQueue.poll(); + startDate = token; + hasStartDate = true; + } else { + descriptionQueue.offer(onQueue.poll()); + descriptionQueue.offer(token); + } + } else if (!byQueue.isEmpty()) { + if (!hasEndDate) { + byQueue.poll(); + endDate = token; + hasEndDate = true; + } else { + descriptionQueue.offer(byQueue.poll()); + descriptionQueue.offer(token); + } + } else if (!atQueue.isEmpty()) { + descriptionQueue.offer(atQueue.poll()); + descriptionQueue.offer(token); + } else if (!fromQueue.isEmpty()) { + if (!hasStartDate) { + fromQueue.poll(); + startDate = token; + hasStartDate = true; + } else { + descriptionQueue.offer(fromQueue.poll()); + descriptionQueue.offer(token); + } + } else if (!toQueue.isEmpty()) { + if (!hasEndDate) { + toQueue.poll(); + endDate = token; + hasEndDate = true; + } else { + descriptionQueue.offer(toQueue.poll()); + descriptionQueue.offer(token); + } + } + } else if (TaskTime.isValidTime(token)) { + if (byQueue.isEmpty() && onQueue.isEmpty() && atQueue.isEmpty() && fromQueue.isEmpty() + && toQueue.isEmpty()) { + descriptionQueue.offer(token); + } else if (!byQueue.isEmpty()) { + if (!hasEndTime) { + byQueue.poll(); + endTime = token; + hasEndTime = true; + } else { + descriptionQueue.offer(byQueue.poll()); + descriptionQueue.offer(token); + } + } else if (!atQueue.isEmpty()) { + if (!hasStartTime) { + atQueue.poll(); + startTime = token; + hasStartTime = true; + } else { + descriptionQueue.offer(atQueue.poll()); + descriptionQueue.offer(token); + } + } else if (!fromQueue.isEmpty()) { + if (!hasStartTime) { + fromQueue.poll(); + startTime = token; + hasStartTime = true; + } else { + descriptionQueue.offer(fromQueue.poll()); + descriptionQueue.offer(token); + } + } else if (!toQueue.isEmpty()) { + if (!hasEndTime) { + toQueue.poll(); + endTime = token; + hasEndTime = true; + } else { + descriptionQueue.offer(toQueue.poll()); + descriptionQueue.offer(token); + } + } else if (!onQueue.isEmpty()) { + descriptionQueue.offer(onQueue.poll()); + descriptionQueue.offer(token); + } + } + } + + String tempToken = flushQueue(byQueue, onQueue, atQueue, fromQueue, toQueue); + if (!tempToken.isEmpty()) { + descriptionQueue.offer(tempToken); + } + + while (!descriptionQueue.isEmpty()) { + description += descriptionQueue.poll() + " "; + } + description.trim(); + + if (!hasEndDate) { + endDate = startDate; + } + + if ((TaskDate.isValidToday(startDate) && !hasStartTime) || startDate.equals(TaskDate.DEFAULT_DATE) && !hasStartTime) { + startTime = TaskTime.getTimeNow().toString(); + } + + if (hasStartDate || hasEndDate || hasStartTime || hasEndTime) { + try { + return new AddCommand(description, Task.EVENT_TASK, startDate, endDate, startTime, endTime, + taskPriority, getTagsFromArgs(tagString)); + } catch (IllegalValueException ive) { + UndoCommand.deletePreviousCommand(); + return new IncorrectCommand(ive.getMessage()); + } + } else { + try { + return new AddCommand(description, Task.FLOATING_TASK, startDate, endDate, startTime, endTime, + taskPriority, getTagsFromArgs(tagString)); + } catch (IllegalValueException ive) { + UndoCommand.deletePreviousCommand(); + return new IncorrectCommand(ive.getMessage()); + } + } + + } + + private String flushQueue(Queue byQueue, Queue onQueue, Queue atQueue, + Queue fromQueue, Queue toQueue) { + String token = ""; + + if (!byQueue.isEmpty()) { + token = byQueue.poll(); + } else if (!onQueue.isEmpty()) { + token = onQueue.poll(); + } else if (!atQueue.isEmpty()) { + token = atQueue.poll(); + } else if (!fromQueue.isEmpty()) { + token = fromQueue.poll(); + } else if (!toQueue.isEmpty()) { + token = toQueue.poll(); + } + + return token; + } + + private Queue initialiseArgQueue(ArrayList argsList) { + Queue argsQueue = new LinkedList(); + for (String arg : argsList) { + argsQueue.offer(arg); + } + return argsQueue; + } + + private ArrayList tokenizeArguments(String args) { + ArrayList argsList = new ArrayList(); + StringTokenizer st = new StringTokenizer(args, " "); + while (st.hasMoreTokens()) { + argsList.add(st.nextToken()); + } + return argsList; + } + //@@author + + /** + * Extracts the new task'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(" " + Tag.PREFIX, "").split(" " + Tag.PREFIX)); + return new HashSet<>(tagStrings); + } + + /** + * 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()) { + UndoCommand.deletePreviousCommand(); + 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 task 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 = 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 + * 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); + } + + /** @@author A0142130A **/ + + /** + * Parses arguments in the context of undo command. + * + */ + private Command prepareUndo(String args) { + Optional index = parseIndex(args); + if (!index.isPresent()) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, UndoCommand.MESSAGE_USAGE)); + } + return new UndoCommand(index.get()); + } + + /** + * Parses arguments in the context of the find task by tags command. + * + * @param args + * full command args string + * @return the prepared command + */ + private Command prepareFindByTag(String args) { + final Matcher matcher = KEYWORDS_ARGS_FORMAT.matcher(args.trim()); + if (!matcher.matches()) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindTagCommand.MESSAGE_USAGE)); + } + + // keywords delimited by whitespace + final String[] keywords = matcher.group("keywords").split("\\s+"); + final Set keywordSet = new HashSet<>(Arrays.asList(keywords)); + return new FindTagCommand(keywordSet); + + } + + /** + * Parses arguments in the context of the save storage location command. + * + * @param args + * full command args string + * @return the prepared command + */ + private Command prepareSaveStorageLocation(String args) { + if (args.isEmpty()) { + return new IncorrectCommand( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, SaveStorageLocationCommand.MESSAGE_USAGE)); + } + return new SaveStorageLocationCommand(args); + } + /** @@author **/ + + //@@author A0148004R + /** + * Parses arguments in the context of the done task command. + * + * @param args + * full command args string + * @return the prepared command + */ + private Command prepareDone(String args) { + Optional index = parseIndex(args); + if (!index.isPresent()) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, DoneCommand.MESSAGE_USAGE)); + } + + return new DoneCommand(index.get()); + } + //@@author + + //@@author A0142073R + private static boolean isInt(String s) { + try { + int i = Integer.parseInt(s); + return true; + } + + catch (NumberFormatException er) { + return false; + } + } + //@@author +} \ No newline at end of file diff --git a/src/main/java/seedu/taskell/model/CommandHistory.java b/src/main/java/seedu/taskell/model/CommandHistory.java new file mode 100644 index 000000000000..c0560dbb3b45 --- /dev/null +++ b/src/main/java/seedu/taskell/model/CommandHistory.java @@ -0,0 +1,77 @@ +/** @@author A0142130A **/ +package seedu.taskell.model; + +import seedu.taskell.model.task.Task; + +/** Stores the relevant details of a command so it can be undone via UndoCommand + * each Add/Delete/Edit Command executed should correspond to a CommandHistory + * stored in UndoCommand's list of CommandHistory + * */ +public class CommandHistory { + private String commandText, commandType; + private Task task; //relevent task to be added, deleted or edited + private Task oldTask; //oldTask needed for EditCommands + private boolean toRedo; + + public CommandHistory() { + commandText = "default command text"; + commandType = "default command type"; + task = null; + toRedo = false; + } + + public CommandHistory(String commandText, String commandType) { + assert commandText != null; + assert commandType != null; + assert !commandText.trim().isEmpty(); + assert !commandType.trim().isEmpty(); + + this.commandText = commandText.trim(); + this.commandType = commandType.trim(); + this.task = null; + toRedo = false; + } + + public String getCommandText() { + return commandText; + } + + public String getCommandType() { + return commandType; + } + + public Task getTask() { + return task; + } + + public Task getOldTask() { + return oldTask; + } + + public boolean isRedoTrue() { + return toRedo; + } + + /**** Setter methods ****/ + + public void setCommandText(String text) { + this.commandText = text; + } + + public void setTask(Task task) { + assert task != null; + this.task = task; + if (commandType.equals("delete")) { + commandText = "delete " + this.task.getAsText(); + } + } + + public void setOldTask(Task task) { + this.oldTask = task; + } + + public void setToRedoToTrue() { + toRedo = true; + } + +} diff --git a/src/main/java/seedu/taskell/model/Model.java b/src/main/java/seedu/taskell/model/Model.java new file mode 100644 index 000000000000..1a004d27597f --- /dev/null +++ b/src/main/java/seedu/taskell/model/Model.java @@ -0,0 +1,54 @@ +package seedu.taskell.model; + +import java.util.Set; + +import seedu.taskell.commons.core.UnmodifiableObservableList; +import seedu.taskell.model.task.Task; +import seedu.taskell.model.task.ReadOnlyTask; +import seedu.taskell.model.task.UniqueTaskList; +import seedu.taskell.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(ReadOnlyTaskManager newData); + + /** Returns the TaskManager */ + ReadOnlyTaskManager getTaskManager(); + + /** Deletes the given task. */ + void deleteTask(ReadOnlyTask target) throws UniqueTaskList.TaskNotFoundException; + + /** Adds the given task */ + void addTask(Task task) throws UniqueTaskList.DuplicateTaskException; + + /** Edits the given task + * @throws TaskNotFoundException */ + void editTask(ReadOnlyTask task,Task newTask) throws UniqueTaskList.DuplicateTaskException, TaskNotFoundException; + + /** 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(); + + /** Updates the filter of the filtered task list to filter by priority*/ + void updateFilteredTaskListPriority(Set keywords); + + /** Updates the filter of the filtered task list to filter by the given keywords (AND operation)*/ + void updateFilteredTaskList(Set keywords); + + /** Updates the filter of the filtered task list to filter by the given date*/ + void updateFilteredtaskListDate(Set keywords); + + /** Updates the filter of the filtered task list to filter by the any of given keywords (OR operation)*/ + void updateFilteredTaskListByAnyKeyword(Set keywords); + + /** Updates the filter of the filtered task list to filter by the given taskStatus keywords*/ + void updateFilteredtaskListCompleted(Set keywords); + + /** Checks if task is present in manager */ + boolean isTaskPresent(Task task); +} diff --git a/src/main/java/seedu/taskell/model/ModelManager.java b/src/main/java/seedu/taskell/model/ModelManager.java new file mode 100644 index 000000000000..fa07d2e2154c --- /dev/null +++ b/src/main/java/seedu/taskell/model/ModelManager.java @@ -0,0 +1,286 @@ +package seedu.taskell.model; + +import javafx.collections.transformation.FilteredList; +import seedu.taskell.commons.core.ComponentManager; +import seedu.taskell.commons.core.LogsCenter; +import seedu.taskell.commons.core.UnmodifiableObservableList; +import seedu.taskell.commons.events.model.TaskManagerChangedEvent; +import seedu.taskell.commons.util.StringUtil; +import seedu.taskell.logic.commands.UndoCommand; +import seedu.taskell.model.task.Task; +import seedu.taskell.model.task.ReadOnlyTask; +import seedu.taskell.model.task.UniqueTaskList; +import seedu.taskell.model.task.UniqueTaskList.DuplicateTaskException; +import seedu.taskell.model.task.UniqueTaskList.TaskNotFoundException; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.logging.Logger; + +/** + * Represents the in-memory model of the task manager data. All changes to any + * model should be synchronized. + */ +public class ModelManager extends ComponentManager implements Model { + private static final Logger logger = LogsCenter.getLogger(ModelManager.class); + + private final TaskManager taskManager; + private final FilteredList filteredTasks; + + /** + * Initializes a ModelManager with the given TaskManager TaskManager and its + * variables should not be null + */ + public ModelManager(TaskManager src, UserPrefs userPrefs) { + super(); + assert src != null; + assert userPrefs != null; + + logger.fine("Initializing with task manager: " + src + " and user prefs " + userPrefs); + + taskManager = new TaskManager(src); + filteredTasks = new FilteredList<>(taskManager.getTasks()); + } + + public ModelManager() { + this(new TaskManager(), new UserPrefs()); + } + + public ModelManager(ReadOnlyTaskManager initialData, UserPrefs userPrefs) { + taskManager = new TaskManager(initialData); + filteredTasks = new FilteredList<>(taskManager.getTasks()); + } + + @Override + public void resetData(ReadOnlyTaskManager newData) { + taskManager.resetData(newData); + indicateTaskManagerChanged(); + } + + @Override + public ReadOnlyTaskManager getTaskManager() { + return taskManager; + } + + /** Raises an event to indicate the model has changed */ + private void indicateTaskManagerChanged() { + raise(new TaskManagerChangedEvent(taskManager)); + } + //@@author A0142073R + @Override + public synchronized void editTask(ReadOnlyTask old, Task toEdit) + throws DuplicateTaskException, TaskNotFoundException { + taskManager.editTask(old, toEdit); + indicateTaskManagerChanged(); + } + //@@author + + @Override + public void deleteTask(ReadOnlyTask target) throws TaskNotFoundException { + taskManager.removeTask(target); + // UndoCommand.updateMostRecentDeletedTask(target); + indicateTaskManagerChanged(); + } + + @Override + public synchronized void addTask(Task task) throws UniqueTaskList.DuplicateTaskException { + taskManager.addTask(task); + // UndoCommand.updateMostRecentAddedTask(task); + updateFilteredListToShowAll(); + indicateTaskManagerChanged(); + } + + @Override + public boolean isTaskPresent(Task task) { + assert task != null; + return taskManager.isTaskPresent(task); + } + + // =========== Filtered Task List Accessors + // =============================================================== + + @Override + public UnmodifiableObservableList getFilteredTaskList() { + return new UnmodifiableObservableList<>(filteredTasks); + } + + @Override + public void updateFilteredListToShowAll() { + filteredTasks.setPredicate(null); + } + + @Override + public void updateFilteredTaskList(Set keywords) { + updateFilteredTaskList(new PredicateExpression(new NameQualifier(keywords))); + } + + //@@author A0142073R + + @Override + public void updateFilteredtaskListDate(Set keywords) { + updateFilteredTaskList(new PredicateExpression(new DateQualifier(keywords))); + } + + public void updateFilteredTaskListPriority(Set keywords) { + updateFilteredTaskList(new PredicateExpression(new PriorityQualifier(keywords))); + + } + //@@author + /** @@author A0142130A **/ + @Override + public void updateFilteredTaskListByAnyKeyword(Set keywords) { + updateFilteredTaskList(new PredicateExpression(new TagsQualifier(keywords))); + } + + /** @@author **/ + @Override + public void updateFilteredtaskListCompleted(Set keywords) { + updateFilteredTaskList(new PredicateExpression(new CompleteQualifier(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; + } + + /** @@author A0142130A **/ + @Override + public boolean run(ReadOnlyTask task) { + String searchString = task.getDescription().description + " " + task.tagsSimpleString(); + return nameKeyWords.stream().allMatch(keyword -> StringUtil.containsIgnoreCase(searchString, keyword)); + } + + /** @@author **/ + + @Override + public String toString() { + return "name=" + String.join(", ", nameKeyWords); + } + } + + /** @@author A0142130A **/ + private class TagsQualifier implements Qualifier { + private Set tagsKeyWords; + + TagsQualifier(Set keyWords) { + this.tagsKeyWords = keyWords; + } + + @Override + public boolean run(ReadOnlyTask task) { + return tagsKeyWords.stream() + .filter(keyword -> StringUtil.containsIgnoreCase(task.tagsSimpleString(), keyword)).findAny() + .isPresent(); + } + + @Override + public String toString() { + return "name=" + String.join(", ", tagsKeyWords); + } + } + + /** @@author **/ + //@@author A0148004R + private class CompleteQualifier implements Qualifier { + private Set CompleteKeyWords; + + CompleteQualifier(Set CompleteKeyWords) { + this.CompleteKeyWords = CompleteKeyWords; + } + + @Override + public boolean run(ReadOnlyTask task) { + String searchString = task.getTaskStatus().taskStatus + " " + task.tagsSimpleString(); + return CompleteKeyWords.stream().allMatch(keyword -> StringUtil.containsIgnoreCase(searchString, keyword)); + } + + @Override + public String toString() { + return "complete=" + String.join(", ", CompleteKeyWords); + } + } + //@@author + //@@author A0142073R + private class DateQualifier implements Qualifier { + private Set DateKeyWords; + + DateQualifier(Set dateKeyWords) { + this.DateKeyWords = dateKeyWords; + } + + @Override + public boolean run(ReadOnlyTask task) { + String searchString = task.getStartDate().taskDate + " " + task.getTaskType(); + return DateKeyWords.stream().allMatch(keyword -> StringUtil.containsIgnoreCase(searchString, keyword)); + } + + @Override + public String toString() { + return "date=" + String.join(", ", DateKeyWords); + } + } + + private class PriorityQualifier implements Qualifier { + private Set PriorityKeyWords; + + PriorityQualifier(Set keyWords) { + this.PriorityKeyWords = keyWords; + } + + @Override + public boolean run(ReadOnlyTask task) { + String searchString = task.getTaskPriority().taskPriority; + return PriorityKeyWords.stream().allMatch(keyword -> StringUtil.containsIgnoreCase(searchString, keyword)); + } + + + @Override + public String toString() { + return "prioritye=" + String.join(", ", PriorityKeyWords); + } + + } + //@@author +} diff --git a/src/main/java/seedu/taskell/model/ReadOnlyTaskManager.java b/src/main/java/seedu/taskell/model/ReadOnlyTaskManager.java new file mode 100644 index 000000000000..a4a369644f99 --- /dev/null +++ b/src/main/java/seedu/taskell/model/ReadOnlyTaskManager.java @@ -0,0 +1,30 @@ +package seedu.taskell.model; + + +import java.util.List; + +import seedu.taskell.model.tag.Tag; +import seedu.taskell.model.tag.UniqueTagList; +import seedu.taskell.model.task.ReadOnlyTask; +import seedu.taskell.model.task.UniqueTaskList; + +/** + * Unmodifiable view of an task manager + */ +public interface ReadOnlyTaskManager { + + UniqueTagList getUniqueTagList(); + + UniqueTaskList getUniqueTaskList(); + + /** + * Returns an unmodifiable view of tasks list + */ + List getTaskList(); + + /** + * Returns an unmodifiable view of tags list + */ + List getTagList(); + +} diff --git a/src/main/java/seedu/taskell/model/TaskManager.java b/src/main/java/seedu/taskell/model/TaskManager.java new file mode 100644 index 000000000000..76d40c2f681a --- /dev/null +++ b/src/main/java/seedu/taskell/model/TaskManager.java @@ -0,0 +1,186 @@ +package seedu.taskell.model; + +import javafx.collections.ObservableList; +import seedu.taskell.model.tag.Tag; +import seedu.taskell.model.tag.UniqueTagList; +import seedu.taskell.model.task.Task; +import seedu.taskell.model.task.ReadOnlyTask; +import seedu.taskell.model.task.UniqueTaskList; +import seedu.taskell.model.task.UniqueTaskList.TaskNotFoundException; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * Wraps all data at the task-manager level Duplicates are not allowed (by + * .equals comparison) + */ +public class TaskManager implements ReadOnlyTaskManager { + + private final UniqueTaskList tasks; + private final UniqueTagList tags; + + { + tasks = new UniqueTaskList(); + tags = new UniqueTagList(); + } + + public TaskManager() { + } + + /** + * Tasks and Tags are copied into this taskmanager + */ + public TaskManager(ReadOnlyTaskManager toBeCopied) { + this(toBeCopied.getUniqueTaskList(), toBeCopied.getUniqueTagList()); + } + + /** + * Tasks and Tags are copied into this taskmanager + */ + public TaskManager(UniqueTaskList tasks, UniqueTagList tags) { + resetData(tasks.getInternalList(), tags.getInternalList()); + } + + public static ReadOnlyTaskManager getEmptyTaskManager() { + return new TaskManager(); + } + + //// list overwrite operations + + public ObservableList getTasks() { + return tasks.getInternalList(); + } + + public void setTasks(List tasks) { + this.tasks.getInternalList().setAll(tasks); + } + + public void setTags(Collection tags) { + this.tags.getInternalList().setAll(tags); + } + + public void resetData(Collection newTasks, Collection newTags) { + setTasks(newTasks.stream().map(Task::new).collect(Collectors.toList())); + setTags(newTags); + } + + public void resetData(ReadOnlyTaskManager newData) { + resetData(newData.getTaskList(), newData.getTagList()); + } + + //// task-level operations + + /** + * Adds a task to the task manager. Also checks the new task's tags and + * updates {@link #tags} with any new tags found, and updates the Tag + * objects in the task to point to those in {@link #tags}. + * + * @throws UniqueTaskList.DuplicateTaskException + * if an equivalent task already exists. + */ + public void addTask(Task p) throws UniqueTaskList.DuplicateTaskException { + syncTagsWithMasterList(p); + tasks.add(p); + } + + //@@author A0142073R + /** + * Edits a task in task manager. + * + * @throws UniqueTaskList.DuplicateTaskException + * if an equivalent task already exists. + * @throws TaskNotFoundException + */ + public boolean editTask(ReadOnlyTask old, Task p) throws UniqueTaskList.DuplicateTaskException, TaskNotFoundException { + if (tasks.edit(old, p)) { + return true; + } else { + throw new UniqueTaskList.TaskNotFoundException(); + } + } + //@@author + + /** + * Ensures that every tag in this task: - exists in the master list + * {@link #tags} - points to a Tag object in the master list + */ + private void syncTagsWithMasterList(Task task) { + final UniqueTagList taskTags = task.getTags(); + tags.mergeFrom(taskTags); + + // Create map with values = tag object references in the master list + final Map masterTagObjects = new HashMap<>(); + for (Tag tag : tags) { + masterTagObjects.put(tag, tag); + } + + // Rebuild the list of task tags using references from the master list + final Set commonTagReferences = new HashSet<>(); + for (Tag tag : taskTags) { + commonTagReferences.add(masterTagObjects.get(tag)); + } + task.setTags(new UniqueTagList(commonTagReferences)); + } + + public boolean removeTask(ReadOnlyTask key) throws UniqueTaskList.TaskNotFoundException { + if (tasks.remove(key)) { + return true; + } else { + throw new UniqueTaskList.TaskNotFoundException(); + } + } + + //// tag-level operations + + public void addTag(Tag t) throws UniqueTagList.DuplicateTagException { + tags.add(t); + } + + //// util methods + + @Override + public String toString() { + return tasks.getInternalList().size() + " tasks, " + tags.getInternalList().size() + " tags"; + // TODO: refine later + } + + @Override + public List getTaskList() { + return Collections.unmodifiableList(tasks.getInternalList()); + } + + @Override + public List getTagList() { + return Collections.unmodifiableList(tags.getInternalList()); + } + + @Override + public UniqueTaskList getUniqueTaskList() { + return this.tasks; + } + + @Override + public UniqueTagList getUniqueTagList() { + return this.tags; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof TaskManager // instanceof handles nulls + && this.tasks.equals(((TaskManager) other).tasks) + && this.tags.equals(((TaskManager) other).tags)); + } + + @Override + public int hashCode() { + // use this method for custom fields hashing instead of implementing + // your own + return Objects.hash(tasks, tags); + } + + public boolean isTaskPresent(Task task) { + return tasks.contains(task); + } +} diff --git a/src/main/java/seedu/address/model/UserPrefs.java b/src/main/java/seedu/taskell/model/UserPrefs.java similarity index 93% rename from src/main/java/seedu/address/model/UserPrefs.java rename to src/main/java/seedu/taskell/model/UserPrefs.java index da9c8037f495..15887a84e149 100644 --- a/src/main/java/seedu/address/model/UserPrefs.java +++ b/src/main/java/seedu/taskell/model/UserPrefs.java @@ -1,9 +1,9 @@ -package seedu.address.model; - -import seedu.address.commons.core.GuiSettings; +package seedu.taskell.model; import java.util.Objects; +import seedu.taskell.commons.core.GuiSettings; + /** * Represents User's preferences. */ diff --git a/src/main/java/seedu/address/model/tag/Tag.java b/src/main/java/seedu/taskell/model/tag/Tag.java similarity index 84% rename from src/main/java/seedu/address/model/tag/Tag.java rename to src/main/java/seedu/taskell/model/tag/Tag.java index 5bcffdb5ddf1..ab17eafbff57 100644 --- a/src/main/java/seedu/address/model/tag/Tag.java +++ b/src/main/java/seedu/taskell/model/tag/Tag.java @@ -1,16 +1,17 @@ -package seedu.address.model.tag; +package seedu.taskell.model.tag; -import seedu.address.commons.exceptions.IllegalValueException; +import seedu.taskell.commons.exceptions.IllegalValueException; /** - * Represents a Tag in the address book. + * Represents a Tag in the task manager. * 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 static final String PREFIX = "#"; public String tagName; @@ -56,5 +57,9 @@ public int hashCode() { public String toString() { return '[' + tagName + ']'; } + + public String toSimpleString() { + return tagName; + } } diff --git a/src/main/java/seedu/address/model/tag/UniqueTagList.java b/src/main/java/seedu/taskell/model/tag/UniqueTagList.java similarity index 96% rename from src/main/java/seedu/address/model/tag/UniqueTagList.java rename to src/main/java/seedu/taskell/model/tag/UniqueTagList.java index 76fb7ff3dc5d..22c310084eb4 100644 --- a/src/main/java/seedu/address/model/tag/UniqueTagList.java +++ b/src/main/java/seedu/taskell/model/tag/UniqueTagList.java @@ -1,9 +1,9 @@ -package seedu.address.model.tag; +package seedu.taskell.model.tag; import javafx.collections.FXCollections; import javafx.collections.ObservableList; -import seedu.address.commons.util.CollectionUtil; -import seedu.address.commons.exceptions.DuplicateDataException; +import seedu.taskell.commons.exceptions.DuplicateDataException; +import seedu.taskell.commons.util.CollectionUtil; import java.util.*; diff --git a/src/main/java/seedu/taskell/model/task/Description.java b/src/main/java/seedu/taskell/model/task/Description.java new file mode 100644 index 000000000000..0855955f2a8b --- /dev/null +++ b/src/main/java/seedu/taskell/model/task/Description.java @@ -0,0 +1,58 @@ +package seedu.taskell.model.task; + +import seedu.taskell.commons.exceptions.IllegalValueException; + +//@@author A0139257X +/** + * Represents a Task's description in the task manager. + * Guarantees: immutable; is valid as declared in {@link #isValidDescription(String)} + */ +public class Description { + + public static final String MESSAGE_DESCRIPTION_CONSTRAINTS = "Task description should be spaces or alphanumeric characters"; + + public String description; + + /** + * Validates given description. + * + * @throws IllegalValueException if given description string is invalid. + */ + public Description(String description) throws IllegalValueException { + assert description != null; + description = description.trim(); + if (!isValidDescription(description)) { + throw new IllegalValueException(MESSAGE_DESCRIPTION_CONSTRAINTS); + } + this.description = description; + } + + /** + * Returns true if a given string is a valid task description. + */ + public static boolean isValidDescription(String test) { + return !test.isEmpty(); + } + + public void setDescription(String description){ + this.description = description; + } + + @Override + public String toString() { + return description; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Description // instanceof handles nulls + && this.description.equals(((Description) other).description)); // state check + } + + @Override + public int hashCode() { + return description.hashCode(); + } + +} diff --git a/src/main/java/seedu/taskell/model/task/EventTask.java b/src/main/java/seedu/taskell/model/task/EventTask.java new file mode 100644 index 000000000000..bd8dba7a35e8 --- /dev/null +++ b/src/main/java/seedu/taskell/model/task/EventTask.java @@ -0,0 +1,87 @@ +package seedu.taskell.model.task; + +import java.util.Objects; + +import seedu.taskell.commons.exceptions.IllegalValueException; +import seedu.taskell.commons.util.CollectionUtil; +import seedu.taskell.model.tag.UniqueTagList; + +//@@author A0139257X +/** + * Represents an Event task in the task manager. + * Guarantees: details are present and not null, field values are validated. + */ +public class EventTask extends Task { + public static final String MESSAGE_EVENT_CONSTRAINTS = "Start date and time must be before end date and time" + + "\nAll date and time should not before current time"; + + public EventTask(String description, String startDate, String endDate, String startTime, String endTime, String taskPriority, String taskStatus, UniqueTagList tags) throws IllegalValueException { + this(new Description(description), + EVENT_TASK, + new TaskDate(startDate), + new TaskDate(endDate), + new TaskTime(startTime), + new TaskTime(endTime), + new TaskPriority(taskPriority), + new TaskStatus(taskStatus), + tags); + } + /** + * Every field must be present and not null. + * @throws IllegalValueException + */ + public EventTask(Description description, String taskType, TaskDate startDate, TaskDate endDate, TaskTime startTime, TaskTime endTime, TaskPriority taskPriority, TaskStatus taskStatus, UniqueTagList tags) throws IllegalValueException { + endDate = autoAdjustEndDate(startDate, endDate, startTime, endTime); + + if (!isValidEventDuration(startDate, endDate, startTime, endTime)) { + throw new IllegalValueException(MESSAGE_EVENT_CONSTRAINTS); + } + + this.description = description; + this.taskType = EVENT_TASK; + this.startDate = startDate; + this.endDate = endDate; + this.startTime = startTime; + this.endTime = endTime; + this.taskPriority = taskPriority; + this.taskStatus = taskStatus; + this.tags = tags; + } + + private boolean isValidEventDuration(TaskDate startDate, TaskDate endDate, TaskTime startTime, TaskTime endTime) { + TaskDate today = TaskDate.getTodayDate(); + TaskTime currentTime = TaskTime.getTimeNow(); + + if (startDate.isBefore(today) || endDate.isBefore(today)) { + return false; + } else if (startDate.isAfter(endDate)) { + return false; + } if (startDate.equals(today) && startTime.isBefore(currentTime)) { + return false; + } else { + return true; + } + } + + /** + * Adjust the endDate such that it fits into the real-world context + * @throws IllegalValueException + */ + private TaskDate autoAdjustEndDate(TaskDate startDate, TaskDate endDate, TaskTime startTime, TaskTime endTime) throws IllegalValueException { + TaskDate today = TaskDate.getTodayDate(); + if (startDate.equals(endDate) && startTime.isAfter(endTime)) { + endDate = endDate.getNextDay(); + } else if (startDate.getDayNameInWeek().equals(today.getDayNameInWeek()) + && !endDate.getDayNameInWeek().equals(today.getDayNameInWeek())) { + endDate = endDate.getNextWeek(); + } + return endDate; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof ReadOnlyTask // instanceof handles nulls + && this.isSameStateAs((ReadOnlyTask) other)); + } +} \ No newline at end of file diff --git a/src/main/java/seedu/taskell/model/task/FloatingTask.java b/src/main/java/seedu/taskell/model/task/FloatingTask.java new file mode 100644 index 000000000000..dfc7dff5426a --- /dev/null +++ b/src/main/java/seedu/taskell/model/task/FloatingTask.java @@ -0,0 +1,40 @@ +package seedu.taskell.model.task; + +import java.util.Objects; + +import seedu.taskell.model.task.Description; +import seedu.taskell.commons.exceptions.IllegalValueException; +import seedu.taskell.commons.util.CollectionUtil; +import seedu.taskell.model.tag.UniqueTagList; + +//@@author A0139257X +/** + * Represents a Floating Task in the task manager. + * Guarantees: details are present and not null, field values are validated. + */ +public class FloatingTask extends Task { + + public FloatingTask(String description, String taskPriority, String taskStatus, UniqueTagList tags) throws IllegalValueException { + this(new Description(description), + FLOATING_TASK, + new TaskDate(TaskDate.DEFAULT_DATE), + new TaskDate(TaskDate.DEFAULT_DATE), + new TaskTime(TaskTime.DEFAULT_START_TIME), + new TaskTime(TaskTime.DEFAULT_END_TIME), + new TaskPriority(taskPriority), + new TaskStatus(taskStatus), + tags); + } + + public FloatingTask(Description description, String taskType, TaskDate startDate, TaskDate endDate, TaskTime startTime, TaskTime endTime, TaskPriority taskPriority, TaskStatus taskStatus, UniqueTagList tags) { + super(description, taskType, startDate, endDate, startTime, endTime, taskPriority, taskStatus, tags); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof ReadOnlyTask // instanceof handles nulls + && this.isSameStateAs((ReadOnlyTask) other)); + } + +} diff --git a/src/main/java/seedu/taskell/model/task/ReadOnlyTask.java b/src/main/java/seedu/taskell/model/task/ReadOnlyTask.java new file mode 100644 index 000000000000..ecfce173fd88 --- /dev/null +++ b/src/main/java/seedu/taskell/model/task/ReadOnlyTask.java @@ -0,0 +1,117 @@ +package seedu.taskell.model.task; + +import seedu.taskell.model.tag.UniqueTagList; + +/** + * A read-only immutable interface for a Task in the taskmanager. + * Implementations should guarantee: details are present and not null, field values are validated. + */ +public interface ReadOnlyTask { + Description getDescription(); + String getTaskType(); + TaskDate getStartDate(); + TaskDate getEndDate(); + TaskTime getStartTime(); + TaskTime getEndTime(); + TaskPriority getTaskPriority(); + TaskStatus getTaskStatus(); + + /** + * The returned TagList is a deep copy of the internal TagList, + * changes on the returned list will not affect the task's internal tags. + */ + UniqueTagList getTags(); + + /** + * 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.getDescription().equals(this.getDescription()) // state checks here onwards + && other.getTaskType().equals(this.getTaskType()) + && other.getStartDate().equals(this.getStartDate()) + && other.getEndDate().equals(this.getEndDate()) + && other.getStartTime().equals(this.getStartTime()) + && other.getEndTime().equals(this.getEndTime()) + && other.getTaskPriority().equals(this.getTaskPriority()) + && other.getTaskStatus().equals(this.getTaskStatus())); + } + + /** + * Formats the task as text, showing all contact details. + */ + default String getAsText() { + if (getTaskType().equals(Task.FLOATING_TASK)) { + return getAsTextFloatingTask(); + } else { + return getAsTextEventTask(); + } + } + + /** + * Formats the floating task as text, showing all contact details. + */ + default String getAsTextFloatingTask() { + final StringBuilder builder = new StringBuilder(); + builder.append(getDescription()) + .append(" TaskPriority: ") + .append(getTaskPriority()) + .append(" Tags: "); + getTags().forEach(builder::append); + return builder.toString(); + } + + /** + * Formats the event task as text, showing all contact details. + */ + default String getAsTextEventTask() { + final StringBuilder builder = new StringBuilder(); + builder.append(getDescription()) + .append(" StartDate: ") + .append(getStartDate()) + .append(" EndDate: ") + .append(getEndDate()) + .append(" StartTime: ") + .append(getStartTime()) + .append(" EndTime: ") + .append(getEndTime()) + .append(" TaskPriority: ") + .append(getTaskPriority()) + .append(" TaskComplete: ") + .append(getTaskStatus()) + .append(" Tags: "); + getTags().forEach(builder::append); + return builder.toString(); + } + + /** + * Returns a string representation of this Task's tags + */ + default String tagsString() { + final StringBuffer buffer = new StringBuffer(); + final String separator = " "; + getTags().forEach(tag -> buffer.append(tag).append(separator)); + if (buffer.length() == 0) { + return ""; + } else { + return buffer.substring(0, buffer.length() - separator.length()); + } + } + + /** + * @return a simple string representation of this Task's tags + * with each tag separated by a whitespace + */ + default String tagsSimpleString() { + final StringBuffer buffer = new StringBuffer(); + final String separator = " "; + getTags().forEach(tag -> buffer.append(tag.toSimpleString()).append(separator)); + if (buffer.length() == 0) { + return ""; + } else { + return buffer.substring(0, buffer.length() - separator.length()); + } + } + +} diff --git a/src/main/java/seedu/taskell/model/task/Task.java b/src/main/java/seedu/taskell/model/task/Task.java new file mode 100644 index 000000000000..f52a4c42fc4e --- /dev/null +++ b/src/main/java/seedu/taskell/model/task/Task.java @@ -0,0 +1,129 @@ +package seedu.taskell.model.task; + +import java.util.Objects; + +import seedu.taskell.commons.exceptions.IllegalValueException; +import seedu.taskell.commons.util.CollectionUtil; +import seedu.taskell.model.tag.UniqueTagList; + +/** + * Represents a Task in the task manager. + * Guarantees: details are present and not null, field values are validated. + */ +public class Task implements ReadOnlyTask { + public static final String FLOATING_TASK = "FLOATING"; + public static final String EVENT_TASK = "EVENT"; + + protected Description description; + protected String taskType; + protected TaskDate startDate; + protected TaskDate endDate; + protected TaskTime startTime; + protected TaskTime endTime; + protected TaskPriority taskPriority; + protected TaskStatus taskStatus; + + protected UniqueTagList tags; + + public Task() { + //Not applicable + } + + /** + * Every field must be present and not null. + */ + public Task(Description description, String taskType, TaskDate startDate, TaskDate endDate, TaskTime startTime, TaskTime endTime, TaskPriority taskPriority, TaskStatus taskStatus, UniqueTagList tags) { + assert !CollectionUtil.isAnyNull(description, taskType, startDate, startTime, endTime, taskPriority, taskStatus, tags); + this.description = description; + this.taskType = taskType; + this.startDate = startDate; + this.endDate = endDate; + this.endDate = endDate; + this.startTime = startTime; + this.endTime = endTime; + this.taskPriority = taskPriority; + this.taskStatus = taskStatus; + this.tags = new UniqueTagList(tags); // protect internal tags from changes in the arg list + } + + public Task(String description) throws IllegalValueException{ + this.description = new Description(description); + } + /** + * Copy constructor. + */ + public Task(ReadOnlyTask source) { + this(source.getDescription(), source.getTaskType(), source.getStartDate(), source.getEndDate(), source.getStartTime(), source.getEndTime(), source.getTaskPriority(), source.getTaskStatus(), source.getTags()); + } + + @Override + public Description getDescription() { + return description; + } + + @Override + public String getTaskType() { + return taskType; + } + + @Override + public TaskDate getStartDate() { + return startDate; + } + + @Override + public TaskDate getEndDate() { + return endDate; + } + + @Override + public TaskTime getStartTime() { + return startTime; + } + + @Override + public TaskTime getEndTime() { + return endTime; + } + + @Override + public TaskPriority getTaskPriority() { + return taskPriority; + } + + @Override + public TaskStatus getTaskStatus() { + return taskStatus; + } + + @Override + public UniqueTagList getTags() { + return new UniqueTagList(tags); + } + + /** + * Replaces this task'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 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(description, startDate, startTime, endTime, taskPriority, taskStatus, tags); + } + + @Override + public String toString() { + return getAsText(); + } + +} diff --git a/src/main/java/seedu/taskell/model/task/TaskDate.java b/src/main/java/seedu/taskell/model/task/TaskDate.java new file mode 100644 index 000000000000..25a962ee936e --- /dev/null +++ b/src/main/java/seedu/taskell/model/task/TaskDate.java @@ -0,0 +1,615 @@ +package seedu.taskell.model.task; + +import java.util.StringTokenizer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import seedu.taskell.commons.exceptions.IllegalValueException; + +import java.util.Locale; +import java.util.Date; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.DateTimeException; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.format.TextStyle; + +//@@author A0139257X +/** + * Represents a Task's taskDate in the task manager. + * Guarantees: is valid as declared in {@link #isValidDate(String)} + */ +public class TaskDate { + public static final int JANUARY = 1; + public static final int FEBRUARY = 2; + public static final int MARCH = 3; + public static final int APRIL = 4; + public static final int MAY = 5; + public static final int JUNE = 6; + public static final int JULY = 7; + public static final int AUGUST = 8; + public static final int SEPTEMBER = 9; + public static final int OCTOBER = 10; + public static final int NOVEMBER = 11; + public static final int DECEMBER = 12; + + public static final int MONDAY = 1; + public static final int TUESDAY = 2; + public static final int WEDNESDAY = 3; + public static final int THURSDAY = 4; + public static final int FRIDAY = 5; + public static final int SATURDAY = 6; + public static final int SUNDAY = 7; + + public static final int NUM_DAYS_IN_A_WEEK = 7; + public static final int NUM_MONTHS_IN_A_YEAR = 12; + + public static final int NOT_A_VALID_MONTH = 0; + public static final int NOT_A_VALID_DAY_OF_THE_WEEK = 0; + + public static final int FIRST_DAY_OF_THE_MONTH = 1; + + public static final String DATE_DELIMITER = " .-/"; + + public static final String DEFAULT_DATE = getDefaultDate(); + + public static final Pattern TASK_DATE_ARGS_FORMAT = Pattern + .compile("(?(3[0-1]|2[0-9]|1[0-9]|[1-9]))" + "(-)(?(1[0-2]|[1-9]))" + "(-)(?([0-9]{4}))"); + private static final DateTimeFormatter dtf = DateTimeFormatter.ofPattern("EEEE, d MMMM yyyy"); + private static final DateTimeFormatter standardFormat = DateTimeFormatter.ofPattern("d-MM-yyyy"); + SimpleDateFormat sdf = new SimpleDateFormat("d M yyyy"); + + public static final String MESSAGE_TASK_DATE_CONSTRAINTS = + "Task dates should be separated by '-' or '.' or '/'" + + "\nSpelling of month should be in full or 3-letters" + + "\nYear should only be 4-digits"; + + public String taskDate; + + /** + * Initialize the different fields given taskDate in the format of + * DAY-MONTH-YEAR, separated by DATE_DELIMITER + * @throws IllegalValueException + */ + public TaskDate(String dateToAdd) throws IllegalValueException { + if (isValidFullDate(dateToAdd)) { + setDateGivenFullDate(dateToAdd); + } else if (isValidDayAndMonth(dateToAdd)) { + setDateGivenDayMonth(dateToAdd); + } else if (isValidMonthAndYear(dateToAdd)) { + setDateGivenMonthYear(dateToAdd); + } else if (isValidMonth(dateToAdd)) { + setDateGivenMonth(dateToAdd); + } else if (isValidDayOfWeek(dateToAdd)) { + setDateGivenDayNameOfWeek(dateToAdd); + } else if (isValidToday(dateToAdd)) { + setDateGivenToday(dateToAdd); + } else if (isValidTomorrow(dateToAdd)) { + setDateGivenTomorrow(dateToAdd); + } else { + throw new IllegalValueException(MESSAGE_TASK_DATE_CONSTRAINTS); + } + } + + /** + * Extract the different fields from taskDate having the format of + * DAY-MONTH-YEAR, separated by DATE_DELIMITER + * @throws DateTimeException + * @throws IllegalValueException + */ + private void setDateGivenFullDate(String dateToConvert) throws DateTimeException, IllegalValueException { + StringTokenizer st = new StringTokenizer(dateToConvert, DATE_DELIMITER); + String[] tokenArr = new String[3]; + int i = 0; + while (st.hasMoreTokens()) { + tokenArr[i] = st.nextToken(); + i++; + } + + int day = Integer.valueOf(tokenArr[0]); + String monthStr = tokenArr[1]; + int month; + try { + month = Integer.valueOf(tokenArr[1]); + } catch (NumberFormatException nfe) { + month = convertMonthIntoInteger(monthStr); + } + int year = Integer.valueOf(tokenArr[2]); + + try { + setDate(day, month, year); + getYear(); + } catch (IllegalValueException ive) { + throw ive; + } + } + + private void setDateGivenDayMonth(String dateToConvert) throws IllegalValueException { + StringTokenizer st = new StringTokenizer(dateToConvert, DATE_DELIMITER); + String[] tokenArr = new String[3]; + int i = 0; + while (st.hasMoreTokens()) { + tokenArr[i] = st.nextToken(); + i++; + } + + int day = Integer.valueOf(tokenArr[0]); + String monthStr = tokenArr[1]; + int month; + try { + month = Integer.valueOf(tokenArr[1]); + } catch (NumberFormatException nfe) { + month = convertMonthIntoInteger(monthStr); + } + int year = Integer.valueOf(getThisYear()); + + try { + setDate(day, month, year); + getYear(); + } catch (IllegalValueException ive) { + throw ive; + } + } + + private void setDateGivenMonthYear(String dateToConvert) throws IllegalValueException { + StringTokenizer st = new StringTokenizer(dateToConvert, DATE_DELIMITER); + String[] tokenArr = new String[3]; + int i = 0; + while (st.hasMoreTokens()) { + tokenArr[i] = st.nextToken(); + i++; + } + + int day = FIRST_DAY_OF_THE_MONTH; + String monthStr = tokenArr[0]; + int month; + try { + month = Integer.valueOf(tokenArr[0]); + } catch (NumberFormatException nfe) { + month = convertMonthIntoInteger(monthStr); + } + int year = Integer.valueOf(tokenArr[1]); + + try { + setDate(day, month, year); + getYear(); + } catch (IllegalValueException ive) { + throw ive; + } + } + + private void setDateGivenMonth(String monthToConvert) { + int day = FIRST_DAY_OF_THE_MONTH; + int month = convertMonthIntoInteger(monthToConvert); + int year = Integer.valueOf(getThisYear()); + + try { + setDate(day, month, year); + } catch (DateTimeException dte) { + throw dte; + } + } + + private void setDateGivenDayNameOfWeek(String dayName) { + int day = convertDayOfWeekIntoInteger(dayName); + LocalDate today = LocalDate.now(); + String todayDayNameInWeek = today.getDayOfWeek().getDisplayName(TextStyle.FULL, Locale.US); + int todayDayInWeek = convertDayOfWeekIntoInteger(todayDayNameInWeek); + int daysToAdd = day - todayDayInWeek; + if (daysToAdd <= 0) { + daysToAdd += NUM_DAYS_IN_A_WEEK; + } + LocalDate finalDate = today.plusDays(daysToAdd); + setDate(finalDate.getDayOfMonth(), finalDate.getMonthValue(), finalDate.getYear()); + } + + private void setDateGivenToday(String taskDate) { + LocalDate today = LocalDate.now(); + setDate(today.getDayOfMonth(), today.getMonthValue(), today.getYear()); + } + + private void setDateGivenTomorrow(String taskDate) { + LocalDate today = LocalDate.now(); + LocalDate tomorrow = today.plusDays(1); + setDate(tomorrow.getDayOfMonth(), tomorrow.getMonthValue(), tomorrow.getYear()); + } + + /** + * Extract the different fields of a given valid taskDate + * @throws DateTimeException + */ + public void setDate(int day, int month, int year) { + this.taskDate = convertToStandardFormat(day, month, year); + } + + /** + * Convert this TaskDate to the format of + * DAY_MONTH-YEAR + */ + public static String convertToStandardFormat(int day, int month, int year) { + return day + "-" + month + "-" + year; + } + + /** + * Returns true if a given string is a valid task taskDate. + */ + public static boolean isValidDate(String dateToValidate) { + if (dateToValidate == null || dateToValidate.isEmpty()) { + return false; + } + + return isValidFullDate(dateToValidate) || isValidMonthAndYear(dateToValidate) + || isValidDayAndMonth(dateToValidate) || isValidMonth(dateToValidate) || isValidToday(dateToValidate) + || isValidTomorrow(dateToValidate) || isValidDayOfWeek(dateToValidate); + } + + public static boolean isValidDayOfWeek(String dateToValidate) { + if (convertDayOfWeekIntoInteger(dateToValidate) == NOT_A_VALID_DAY_OF_THE_WEEK) { + return false; + } + return true; + } + + public static boolean isValidMonthAndYear(String dateToValidate) { + if (isValidFormat(dateToValidate, "MMM yyyy") || isValidFormat(dateToValidate, "MMM-yyyy") + || isValidFormat(dateToValidate, "MMM.yyyy") + || isValidFormat(dateToValidate, "MMM/yyyy")) { + return true; + } + return false; + } + + public static boolean isValidDayAndMonth(String dateToValidate) { + if (isValidFormat(dateToValidate, "d MMM") || isValidFormat(dateToValidate, "d-MMM") + || isValidFormat(dateToValidate, "d.MMM") + || isValidFormat(dateToValidate, "d/MMM")) { + return true; + } + return false; + } + + public static boolean isValidFullDate(String dateToValidate) { + if (isValidFormat(dateToValidate, "d M yyyy") || isValidFormat(dateToValidate, "d MMM yyyy") + || isValidFormat(dateToValidate, "d-M-yyyy") || isValidFormat(dateToValidate, "d-MMM-yyyy") + || isValidFormat(dateToValidate, "d.M.yyyy") || isValidFormat(dateToValidate, "d.MMM.yyyy") + || isValidFormat(dateToValidate, "d.M-yyyy") || isValidFormat(dateToValidate, "d.MMM-yyyy") + || isValidFormat(dateToValidate, "d-M.yyyy") || isValidFormat(dateToValidate, "d-MMM.yyyy") + || isValidFormat(dateToValidate, "d/M/yyyy") || isValidFormat(dateToValidate, "d/MMM/yyyy") + || isValidFormat(dateToValidate, "d-M/yyyy") || isValidFormat(dateToValidate, "d-MMM/yyyy") + || isValidFormat(dateToValidate, "d/M-yyyy") || isValidFormat(dateToValidate, "d/MMM-yyyy") + || isValidFormat(dateToValidate, "d.M/yyyy") || isValidFormat(dateToValidate, "d.MMM/yyyy") + || isValidFormat(dateToValidate, "d/M.yyyy") || isValidFormat(dateToValidate, "d/MMM.yyyy")) { + return true; + } + return false; + } + + /** + * Returns true if a given string has a valid format supported by SimpleDateFormat. + */ + public static boolean isValidFormat(String dateToValidate, String acceptedFormat) { + if (dateToValidate == null) { + return false; + } + + SimpleDateFormat sdf = new SimpleDateFormat(acceptedFormat); + sdf.setLenient(false); + + try { + // if not valid, it will throw ParseException + Date taskDate = sdf.parse(dateToValidate); + } catch (ParseException e) { + return false; + } + + return true; + } + + public static boolean isValidToday(String dateToValidate) { + assert (dateToValidate != null); + dateToValidate = dateToValidate.toLowerCase(); + + switch (dateToValidate) { + case "today": + // Fallthrough + case "tdy": + return true; + default: + return false; + } + } + + public static boolean isValidTomorrow(String dateToValidate) { + assert (dateToValidate != null); + dateToValidate = dateToValidate.toLowerCase(); + + switch (dateToValidate) { + case "tomorrow": + // Fallthrough + case "tmr": + return true; + default: + return false; + } + } + + public static boolean isValidMonth(String month) { + if (convertMonthIntoInteger(month) == NOT_A_VALID_MONTH) { + return false; + } else { + return true; + } + } + + /** + * Returns an integer representing the day in a week + */ + private static int convertDayOfWeekIntoInteger(String day) { + assert (day != null); + day = day.toLowerCase(); + + switch (day) { + case "mon": + // Fallthrough + case "monday": + return MONDAY; + case "tue": + // Fallthrough + case "tues": + // Fallthrough + case "tuesday": + return TUESDAY; + case "wed": + // Fallthrough + case "wednesday": + return WEDNESDAY; + case "thu": + // Fallthrough + case "thur": + // Fallthrough + case "thurs": + // Fallthrough + case "thursday": + return THURSDAY; + case "fri": + // Fallthrough + case "friday": + return FRIDAY; + case "sat": + // Fallthrough + case "saturday": + return SATURDAY; + case "sun": + // Fallthrough + case "sunday": + return SUNDAY; + default: + return NOT_A_VALID_DAY_OF_THE_WEEK; + } + } + + /** + * Returns an integer representing the month of a year. + */ + private static int convertMonthIntoInteger(String month) { + assert (month!= null); + if (Character.isLetter(month.charAt(0))) { + month = month.toLowerCase(); + } + + switch (month) { + case "jan": + // Fallthrough + case "january": + return JANUARY; + case "feb": + // Fallthrough + case "february": + return FEBRUARY; + case "mar": + // Fallthrough + case "march": + return MARCH; + case "apr": + // Fallthrough + case "april": + return APRIL; + case "may": + return MAY; + case "jun": + // Fallthrough + case "june": + return JUNE; + case "jul": + // Fallthrough + case "july": + return JULY; + case "aug": + // Fallthrough + case "august": + return AUGUST; + case "sep": + // Fallthrough + case "sept": + // Fallthrough + case "september": + return SEPTEMBER; + case "oct": + // Fallthrough + case "october": + return OCTOBER; + case "nov": + // Fallthrough + case "november": + return NOVEMBER; + case "dec": + // Fallthrough + case "december": + return DECEMBER; + default: + return NOT_A_VALID_MONTH; + } + } + + /** + * Get today's taskDate in the format of + * DAY-MONTH-YEAR + */ + public static TaskDate getTodayDate() { + try { + return new TaskDate(LocalDate.now().format(standardFormat)); + } catch (IllegalValueException e) { + return null; + } + } + + /** + * Get tomorrow's taskDate in the format of + * DAY-MONTH-YEAR + */ + public static TaskDate getTomorrowDate() { + try { + return new TaskDate(LocalDate.now().plusDays(1).format(standardFormat)); + } catch (IllegalValueException e) { + return null; + } + } + + public TaskDate getNextDay() throws IllegalValueException { + try { + LocalDate localDate = LocalDate.of(Integer.valueOf(getYear()), Integer.valueOf(getMonth()), Integer.valueOf(getDay())); + LocalDate nextDay = localDate.plusDays(1); + return new TaskDate(nextDay.format(standardFormat)); + } catch (IllegalValueException e) { + throw new IllegalValueException(MESSAGE_TASK_DATE_CONSTRAINTS); + } + } + + public TaskDate getNextWeek() throws IllegalValueException { + try { + LocalDate localDate = LocalDate.of(Integer.valueOf(getYear()), Integer.valueOf(getMonth()), Integer.valueOf(getDay())); + LocalDate nextWeek = localDate.plusWeeks(1); + return new TaskDate(nextWeek.format(standardFormat)); + } catch (IllegalValueException e) { + throw new IllegalValueException(MESSAGE_TASK_DATE_CONSTRAINTS); + } + } + + /** + * Returns a string representing the integer value of this year + */ + public static String getThisYear() { + return LocalDate.now().getYear() + ""; + } + + public String getDay() throws IllegalValueException { + assert taskDate != null; + + final Matcher matcherFullArg = TASK_DATE_ARGS_FORMAT.matcher(taskDate.trim()); + if (matcherFullArg.matches()) { + return matcherFullArg.group("day"); + } else { + throw new IllegalValueException(MESSAGE_TASK_DATE_CONSTRAINTS); + } + } + + public String getMonth() throws IllegalValueException { + assert taskDate != null; + + final Matcher matcherFullArg = TASK_DATE_ARGS_FORMAT.matcher(taskDate.trim()); + if (matcherFullArg.matches()) { + return matcherFullArg.group("month"); + } else { + throw new IllegalValueException(MESSAGE_TASK_DATE_CONSTRAINTS); + } + } + + public String getYear() throws IllegalValueException { + assert taskDate != null; + + final Matcher matcherFullArg = TASK_DATE_ARGS_FORMAT.matcher(taskDate.trim()); + if (matcherFullArg.matches()) { + return matcherFullArg.group("year"); + } else { + throw new IllegalValueException(MESSAGE_TASK_DATE_CONSTRAINTS); + } + } + + public String getDayNameInWeek() throws IllegalValueException { + try { + LocalDate localDate = LocalDate.of(Integer.valueOf(getYear()), Integer.valueOf(getMonth()), Integer.valueOf(getDay())); + String dayNameInWeek = localDate.getDayOfWeek().getDisplayName(TextStyle.FULL, Locale.US); + return dayNameInWeek; + } catch (IllegalValueException ive) { + throw ive; + } + } + + public String getMonthName() throws IllegalValueException { + try { + LocalDate localDate = LocalDate.of(Integer.valueOf(getYear()), Integer.valueOf(getMonth()), Integer.valueOf(getDay())); + String month = localDate.getMonth().getDisplayName(TextStyle.FULL, Locale.US); + return month; + } catch (IllegalValueException ive) { + throw ive; + } + } + + public String getDisplayDate() { + try { + return getDayNameInWeek() + ", " + getDay() + " " + getMonthName() + " " + getYear(); + } catch (IllegalValueException e) { + return ""; + } + } + + public static String getDefaultDate() { + int day = LocalDate.now().getDayOfMonth(); + int month = LocalDate.now().getMonthValue(); + int year = LocalDate.now().getYear(); + return convertToStandardFormat(day, month, year); + } + + public boolean isBefore(TaskDate date) { + try { + LocalDate thisDate = LocalDate.of(Integer.valueOf(this.getYear()), Integer.valueOf(this.getMonth()), Integer.valueOf(this.getDay())); + LocalDate dateToComapare = LocalDate.of(Integer.valueOf(date.getYear()), Integer.valueOf(date.getMonth()), Integer.valueOf(date.getDay())); + return thisDate.isBefore(dateToComapare); + } catch (Exception e) { + return false; + } + } + + public boolean isAfter(TaskDate date) { + try { + LocalDate thisDate = LocalDate.of(Integer.valueOf(this.getYear()), Integer.valueOf(this.getMonth()), Integer.valueOf(this.getDay())); + LocalDate dateToComapare = LocalDate.of(Integer.valueOf(date.getYear()), Integer.valueOf(date.getMonth()), Integer.valueOf(date.getDay())); + return thisDate.isAfter(dateToComapare); + } catch (Exception e) { + return false; + } + } + + /** + * Returns a string with the format of + * DAY-MONTH-YEAR + */ + @Override + public String toString() { + return taskDate; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof TaskDate // instanceof handles nulls + && this.taskDate.equals(((TaskDate)other).taskDate)); + } + + @Override + public int hashCode() { + return taskDate.hashCode(); + } + +} diff --git a/src/main/java/seedu/taskell/model/task/TaskPriority.java b/src/main/java/seedu/taskell/model/task/TaskPriority.java new file mode 100644 index 000000000000..6dfccb7eb856 --- /dev/null +++ b/src/main/java/seedu/taskell/model/task/TaskPriority.java @@ -0,0 +1,71 @@ +package seedu.taskell.model.task; + +import seedu.taskell.commons.exceptions.IllegalValueException; + +//@@author A0139257X +/** + * Represents a Task's priority in the task manager. + * Guarantees: immutable; is valid as declared in {@link #isValidPriority(String)} + */ +public class TaskPriority { + + public static final String MESSAGE_TASK_PRIORITY_CONSTRAINTS = "Task priority should range from 0-3" + + "\nThere should not be more than 1 priority level for a task"; + public static final String TASK_PRIORITY_VALIDATION_REGEX = ".+"; + + public static final String PREFIX = "p/"; + + public static final String NO_PRIORITY = "0"; + public static final String LOW_PRIORITY = "1"; + public static final String MEDIUM_PRIORITY = "2"; + public static final String HIGH_PRIORITY = "3"; + + public static final String DEFAULT_PRIORITY = NO_PRIORITY; + + public static final String LOW_PRIORITY_BACKGROUND = "-fx-background-color:#4ca64c"; + public static final String MEDIUM_PRIORITY_BACKGROUND = "-fx-background-color:#ffff4c"; + public static final String HIGH_PRIORITY_BACKGROUND = "-fx-background-color:#ff4c4c"; + + public final String taskPriority; + + /** + * Validates given priority. + * + * @throws IllegalValueException if given priority string is invalid. + */ + public TaskPriority(String priority) throws IllegalValueException { + assert priority != null; + if (!isValidPriority(priority)) { + throw new IllegalValueException(MESSAGE_TASK_PRIORITY_CONSTRAINTS); + } + this.taskPriority = priority; + } + + /** + * Returns true if a given string is a valid task taskTime. + */ + public static boolean isValidPriority(String priority) { + return priority.equals(NO_PRIORITY) + || priority.equals(LOW_PRIORITY) + || priority.equals(MEDIUM_PRIORITY) + || priority.equals(HIGH_PRIORITY); + } + + @Override + public String toString() { + return taskPriority; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof TaskPriority // instanceof handles nulls + && this.taskPriority.equals(((TaskPriority) other).taskPriority)); // state check + } + + @Override + public int hashCode() { + return taskPriority.hashCode(); + } + +} \ No newline at end of file diff --git a/src/main/java/seedu/taskell/model/task/TaskStatus.java b/src/main/java/seedu/taskell/model/task/TaskStatus.java new file mode 100644 index 000000000000..f0110fc2b31b --- /dev/null +++ b/src/main/java/seedu/taskell/model/task/TaskStatus.java @@ -0,0 +1,39 @@ +//@@author A0148004R +package seedu.taskell.model.task; + +import java.util.Objects; + +public class TaskStatus { + public static final String FINISHED = "finished"; + public static final String INCOMPLETE = "incomplete"; + public final String taskStatus; + + public TaskStatus(String taskStatus) { + this.taskStatus = taskStatus; + } + + public String taskStatus() { + return taskStatus; + } + + public static boolean isValidTaskComplete(String taskToValidate) { + return taskToValidate.equals(FINISHED) || taskToValidate.equals(INCOMPLETE); + } + + @Override + public String toString() { + return taskStatus; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof TaskStatus // instanceof handles nulls + && this.taskStatus.equals(((TaskStatus)other).taskStatus)); // state check + } + @Override + public int hashCode() { + return taskStatus.hashCode(); + } +} +//@@author diff --git a/src/main/java/seedu/taskell/model/task/TaskTime.java b/src/main/java/seedu/taskell/model/task/TaskTime.java new file mode 100644 index 000000000000..f06ac8327047 --- /dev/null +++ b/src/main/java/seedu/taskell/model/task/TaskTime.java @@ -0,0 +1,245 @@ +package seedu.taskell.model.task; + +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import seedu.taskell.commons.exceptions.IllegalValueException; + +//@@author A0139257X +/** + * Represents a Task's taskTime in the task manager. + */ +public class TaskTime { + + public static final String ZERO_MINUTE = "00"; + public static final String NOON = "12:00PM"; + public static final String MIDNIGHT = "12:00AM"; + + public static final String AM = "AM"; + public static final String PM = "PM"; + + public static final int TIME_OFFSET = 12; + + public static final DateTimeFormatter dtf = DateTimeFormatter.ofPattern("h:mma"); + + public static final String DEFAULT_START_TIME = MIDNIGHT; + public static final String DEFAULT_END_TIME = "11:59PM"; + + public static final Pattern TASK_TIME_ARGS_FORMAT = Pattern + .compile("(?(1[0-2]|[1-9]))" + "(.|-|:)(?([0-5][0-9]))" + "(?(am|pm|AM|PM|Am|Pm|aM|pM))"); + public static final Pattern TASK_TIME_HOUR_ONLY_FORMAT = Pattern + .compile("(?(1[0-2]|[1-9]))" + "(?(am|pm|AM|PM|Am|Pm|aM|pM))"); + final static String FULL_TIME_REGEX = "^(1[0-2]|[1-9])(.|-|:)([0-5][0-9])(am|pm|AM|PM|Am|Pm|aM|pM)$"; + final static String HOUR_ONLY_TIME_REGEX = "^(1[0-2]|[1-9])(am|pm|AM|PM|Am|Pm|aM|pM)$"; + + public static final String MESSAGE_TASK_TIME_CONSTRAINTS = + "Time should be in 12hour clock format." + + "\nHour and Minute are separated by '.' or ':'" + + "\nEg. 9.30am or 12:50pm"; + + public String taskTime; //Standard format + + public TaskTime(String time) throws IllegalValueException { + if (isValidTime(time)) { + setTime(time); + } else { + throw new IllegalValueException(MESSAGE_TASK_TIME_CONSTRAINTS); + } + } + + public static boolean isValidTime(String time) { + assert time != null; + if (time.isEmpty()) { + return false; + } + + if (time.matches(HOUR_ONLY_TIME_REGEX) || time.matches(FULL_TIME_REGEX)) { + return true; + } else if (isValidNow(time)) { + return true; + } else if (isValidNoon(time)) { + return true; + } else if (isValidMidnight(time)) { + return true; + } else { + return false; + } + } + + private static boolean isValidNow(String time) { + time = time.toLowerCase(); + switch (time) { + case "now": + return true; + default: + return false; + } + } + + private static boolean isValidNoon(String time) { + time = time.toLowerCase(); + switch (time) { + case "afternoon": + //Fallthrough + case "noon": + //Fallthrough + case "12noon": + //Fallthrough + case "12-noon": + return true; + default: + return false; + } + } + + private static boolean isValidMidnight(String time) { + time = time.toLowerCase(); + switch (time) { + case "midnight": + //Fallthrough + case "mid-night": + //Fallthrough + case "12midnight": + //Fallthrough + case "12-midnight": + //Fallthrough + case "12mid-night": + //Fallthrough + case "12-mid-night": + //Fallthrough + return true; + default: + return false; + } + } + + /** + * Checks if this time is before the specified time + */ + public boolean isBefore(TaskTime time) { + int timeHour = Integer.valueOf(time.getHour()); + if (time.getAntePost().equals(PM) && (timeHour != TIME_OFFSET)) { + timeHour += TIME_OFFSET; + } else if (time.getAntePost().equals(AM) && (timeHour == TIME_OFFSET)) { + timeHour -= TIME_OFFSET; + } + LocalTime timeToCompare = LocalTime.of(timeHour, Integer.valueOf(time.getMinute())); + + //TaskTime thisTimeTaskTime = new TaskTime(this.taskTime); + int thisTimeHour = Integer.valueOf(this.getHour()); + if (this.getAntePost().equals(PM) && (thisTimeHour != TIME_OFFSET)) { + thisTimeHour += TIME_OFFSET; + } else if (this.getAntePost().equals(AM) && (thisTimeHour == TIME_OFFSET)) { + thisTimeHour -= TIME_OFFSET; + } + LocalTime thisTimeLocalTime = LocalTime.of(thisTimeHour, Integer.valueOf(this.getMinute())); + return thisTimeLocalTime.isBefore(timeToCompare); + } + + /** + * Checks if this time is after the specified time + */ + public boolean isAfter(TaskTime time) { + int timeHour = Integer.valueOf(time.getHour()); + if (time.getAntePost().equals(PM) && (timeHour != TIME_OFFSET)) { + timeHour += TIME_OFFSET; + } else if (time.getAntePost().equals(AM) && (timeHour == TIME_OFFSET)) { + timeHour -= TIME_OFFSET; + } + LocalTime timeToCompare = LocalTime.of(timeHour, Integer.valueOf(time.getMinute())); + + //TaskTime thisTimeTaskTime = new TaskTime(this.taskTime); + int thisTimeHour = Integer.valueOf(this.getHour()); + if (this.getAntePost().equals(PM) && (thisTimeHour != TIME_OFFSET)) { + thisTimeHour += TIME_OFFSET; + } else if (this.getAntePost().equals(AM) && (thisTimeHour == TIME_OFFSET)) { + thisTimeHour -= TIME_OFFSET; + } + LocalTime thisTimeLocalTime = LocalTime.of(thisTimeHour, Integer.valueOf(this.getMinute())); + return thisTimeLocalTime.isAfter(timeToCompare); + } + + public void setTime(String time) throws IllegalValueException { + final Matcher matcherFullArg = TASK_TIME_ARGS_FORMAT.matcher(time.trim()); + final Matcher matcherHourOnly = TASK_TIME_HOUR_ONLY_FORMAT.matcher(time.trim()); + if (matcherFullArg.matches()) { + this.taskTime = setTime(matcherFullArg.group("hour"), matcherFullArg.group("minute"), matcherFullArg.group("antePost")); + } else if (matcherHourOnly.matches()) { + this.taskTime = setTime(matcherHourOnly.group("hour"), ZERO_MINUTE, matcherHourOnly.group("antePost")); + } else if (isValidNow(time)) { + this.taskTime = getTimeNow().toString(); + } else if (isValidNoon(time)) { + this.taskTime = NOON; + } else if (isValidMidnight(time)) { + this.taskTime = MIDNIGHT; + } else { + throw new IllegalValueException(MESSAGE_TASK_TIME_CONSTRAINTS); + } + } + + public String setTime(String hour, String minute, String antePost) { + this.taskTime = hour + ":" + minute + antePost.toUpperCase(); + return taskTime; + } + + public static TaskTime getTimeNow() { + LocalTime currTime = LocalTime.now(); + try { + return new TaskTime(LocalTime.of(currTime.getHour(), currTime.getMinute()).format(dtf)); + } catch (IllegalValueException e) { + return null; + } + } + + public String getHour() { + assert taskTime != null; + + final Matcher matcherFullArg = TASK_TIME_ARGS_FORMAT.matcher(taskTime.trim()); + if (matcherFullArg.matches()) { + return matcherFullArg.group("hour"); + } + + return ""; + } + + public String getMinute() { + assert taskTime != null; + + final Matcher matcherFullArg = TASK_TIME_ARGS_FORMAT.matcher(taskTime.trim()); + if (matcherFullArg.matches()) { + return matcherFullArg.group("minute"); + } + + return ""; + } + + public String getAntePost() { + assert taskTime != null; + + final Matcher matcherFullArg = TASK_TIME_ARGS_FORMAT.matcher(taskTime.trim()); + if (matcherFullArg.matches()) { + return matcherFullArg.group("antePost"); + } + + return ""; + } + + @Override + public String toString() { + return taskTime; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof TaskTime // instanceof handles nulls + && this.taskTime.equals(((TaskTime) other).taskTime)); // state check + } + + @Override + public int hashCode() { + return (taskTime).hashCode(); + } +} diff --git a/src/main/java/seedu/taskell/model/task/UniqueTaskList.java b/src/main/java/seedu/taskell/model/task/UniqueTaskList.java new file mode 100644 index 000000000000..5d4290cc8710 --- /dev/null +++ b/src/main/java/seedu/taskell/model/task/UniqueTaskList.java @@ -0,0 +1,113 @@ +package seedu.taskell.model.task; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import seedu.taskell.commons.exceptions.DuplicateDataException; +import seedu.taskell.commons.util.CollectionUtil; + +import java.util.*; + +/** + * A list of tasks that enforces uniqueness between its elements and does not allow nulls. + * + * Supports a minimal set of list operations. + * + * @see Task#equals(Object) + * @see CollectionUtil#elementsAreUnique(Collection) + */ +public class UniqueTaskList implements Iterable { + + /** + * Signals that an operation would have violated the 'no duplicates' property of the list. + */ + public static class DuplicateTaskException extends DuplicateDataException { + protected DuplicateTaskException() { + super("Operation would result in duplicate tasks"); + } + } + + /** + * Signals that an operation targeting a specified task in the list would fail because + * there is no such matching task in the list. + */ + public static class TaskNotFoundException extends Exception {} + + private final ObservableList internalList = FXCollections.observableArrayList(); + + /** + * Constructs empty TaskList. + */ + public UniqueTaskList() {} + + /** + * Returns true if the list contains an equivalent task as the given argument. + */ + public boolean contains(ReadOnlyTask toCheck) { + assert toCheck != null; + return internalList.contains(toCheck); + } + + /** + * Adds a task to the list. + * + * @throws DuplicateTaskException if the task to add is a duplicate of an existing task in the list. + */ + public void add(Task toAdd) throws DuplicateTaskException { + assert toAdd != null; + if (contains(toAdd)) { + throw new DuplicateTaskException(); + } + internalList.add(toAdd); + } + + /** + * Removes the equivalent task from the list. + * + * @throws TaskNotFoundException if no such task could be found in the list. + */ + public boolean remove(ReadOnlyTask toRemove) throws TaskNotFoundException { + assert toRemove != null; + final boolean taskFoundAndDeleted = internalList.remove(toRemove); + 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)); + } + //@@author A0142073R + /** + * Edits the equivalent task from the list. + * + * @throws TaskNotFoundException if no such task could be found in the list. + */ + public boolean edit(ReadOnlyTask old, Task edit) throws TaskNotFoundException { + assert old != null; + final boolean taskFoundAndUpdated = internalList.contains(old); + if (!taskFoundAndUpdated) { + throw new TaskNotFoundException(); + } + internalList.set(internalList.indexOf(old), edit); + return taskFoundAndUpdated; + } + //@@author + @Override + public int hashCode() { + return internalList.hashCode(); + } +} diff --git a/src/main/java/seedu/taskell/storage/ConfigStorage.java b/src/main/java/seedu/taskell/storage/ConfigStorage.java new file mode 100644 index 000000000000..81e0ec9fff9a --- /dev/null +++ b/src/main/java/seedu/taskell/storage/ConfigStorage.java @@ -0,0 +1,28 @@ +/** @@author A0142130A-reused**/ +package seedu.taskell.storage; + +import java.io.IOException; +import java.util.Optional; + +import seedu.taskell.commons.core.Config; +import seedu.taskell.commons.exceptions.DataConversionException; + +/** + * Represents a storage for {@link seedu.taskell.commons.core.Config}. + */ +public interface ConfigStorage { + /** + * Returns Config 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 readConfigFile() throws DataConversionException, IOException; + + /** + * Saves the given {@link seedu.taskell.commons.core.Config} to the storage. + * @param config cannot be null. + * @throws IOException if there was any problem writing to the file. + */ + void saveConfigFile(Config config) throws IOException; +} diff --git a/src/main/java/seedu/taskell/storage/JsonConfigStorage.java b/src/main/java/seedu/taskell/storage/JsonConfigStorage.java new file mode 100644 index 000000000000..d63202836593 --- /dev/null +++ b/src/main/java/seedu/taskell/storage/JsonConfigStorage.java @@ -0,0 +1,77 @@ +/** @@author A0142130A **/ +package seedu.taskell.storage; + +import java.io.File; +import java.io.IOException; +import java.util.Optional; +import java.util.logging.Logger; + +import seedu.taskell.commons.core.Config; +import seedu.taskell.commons.core.LogsCenter; +import seedu.taskell.commons.exceptions.DataConversionException; +import seedu.taskell.commons.util.FileUtil; + +/** + * A class to access Config stored in the hard disk as a json file + */ + +public class JsonConfigStorage implements ConfigStorage { + + private static final Logger logger = LogsCenter.getLogger(JsonConfigStorage.class); + + private String filePath; + + public JsonConfigStorage(String filePath) { + this.filePath = filePath; + } + + @Override + public Optional readConfigFile() throws DataConversionException, IOException { + return readConfig(filePath); + } + + @Override + public void saveConfigFile(Config config) throws IOException { + saveConfig(config, filePath); + } + + /** + * Similar to {@link #readConfigFile()} + * @param configFilePath location of the data. Cannot be null. + * @throws DataConversionException if the file format is not as expected. + */ + public Optional readConfig(String configFilePath) throws DataConversionException { + assert configFilePath != null; + + File configFile = new File(configFilePath); + + if (!configFile.exists()) { + logger.info("Config file " + configFile + " not found"); + return Optional.empty(); + } + + Config config; + + try { + config = FileUtil.deserializeObjectFromJsonFile(configFile, Config.class); + } catch (IOException e) { + logger.warning("Error reading from config file " + configFile + ": " + e); + throw new DataConversionException(e); + } + + return Optional.of(config); + } + + /** + * Similar to {@link #saveConfigFile(Config)} + * @param configFilePath location of the data. Cannot be null. + */ + private void saveConfig(Config config, String configFilePath) throws IOException { + assert config != null; + assert configFilePath != null; + assert !configFilePath.isEmpty(); + + FileUtil.serializeObjectToJsonFile(new File(configFilePath), config); + } + +} diff --git a/src/main/java/seedu/address/storage/JsonUserPrefsStorage.java b/src/main/java/seedu/taskell/storage/JsonUserPrefsStorage.java similarity index 90% rename from src/main/java/seedu/address/storage/JsonUserPrefsStorage.java rename to src/main/java/seedu/taskell/storage/JsonUserPrefsStorage.java index 1efa8288e4f6..8fcff2051e36 100644 --- a/src/main/java/seedu/address/storage/JsonUserPrefsStorage.java +++ b/src/main/java/seedu/taskell/storage/JsonUserPrefsStorage.java @@ -1,15 +1,15 @@ -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.taskell.storage; import java.io.File; import java.io.IOException; import java.util.Optional; import java.util.logging.Logger; +import seedu.taskell.commons.core.LogsCenter; +import seedu.taskell.commons.exceptions.DataConversionException; +import seedu.taskell.commons.util.FileUtil; +import seedu.taskell.model.UserPrefs; + /** * A class to access UserPrefs stored in the hard disk as a json file */ diff --git a/src/main/java/seedu/taskell/storage/Storage.java b/src/main/java/seedu/taskell/storage/Storage.java new file mode 100644 index 000000000000..cdbe32dee7df --- /dev/null +++ b/src/main/java/seedu/taskell/storage/Storage.java @@ -0,0 +1,39 @@ +package seedu.taskell.storage; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Optional; + +import seedu.taskell.commons.events.model.TaskManagerChangedEvent; +import seedu.taskell.commons.events.storage.DataSavingExceptionEvent; +import seedu.taskell.commons.exceptions.DataConversionException; +import seedu.taskell.model.ReadOnlyTaskManager; +import seedu.taskell.model.UserPrefs; + +/** + * API of the Storage component + */ +public interface Storage extends TaskManagerStorage, UserPrefsStorage { + + @Override + Optional readUserPrefs() throws DataConversionException, IOException; + + @Override + void saveUserPrefs(UserPrefs userPrefs) throws IOException; + + @Override + String getTaskManagerFilePath(); + + @Override + Optional readTaskManager() throws DataConversionException, IOException; + + @Override + void saveTaskManager(ReadOnlyTaskManager taskManager) throws IOException; + + /** + * Saves the current version of the Task Manager to the hard disk. + * Creates the data file if it is missing. + * Raises {@link DataSavingExceptionEvent} if there was an error during saving. + */ + void handleTaskManagerChangedEvent(TaskManagerChangedEvent abce); +} diff --git a/src/main/java/seedu/address/storage/StorageManager.java b/src/main/java/seedu/taskell/storage/StorageManager.java similarity index 50% rename from src/main/java/seedu/address/storage/StorageManager.java rename to src/main/java/seedu/taskell/storage/StorageManager.java index ba1f72f15c27..765bfb216cf0 100644 --- a/src/main/java/seedu/address/storage/StorageManager.java +++ b/src/main/java/seedu/taskell/storage/StorageManager.java @@ -1,37 +1,37 @@ -package seedu.address.storage; +package seedu.taskell.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 seedu.taskell.commons.core.ComponentManager; +import seedu.taskell.commons.core.LogsCenter; +import seedu.taskell.commons.events.model.TaskManagerChangedEvent; +import seedu.taskell.commons.events.storage.DataSavingExceptionEvent; +import seedu.taskell.commons.exceptions.DataConversionException; +import seedu.taskell.model.ReadOnlyTaskManager; +import seedu.taskell.model.UserPrefs; + import java.io.IOException; import java.util.Optional; import java.util.logging.Logger; /** - * Manages storage of AddressBook data in local storage. + * Manages storage of TaskManager data in local storage. */ public class StorageManager extends ComponentManager implements Storage { private static final Logger logger = LogsCenter.getLogger(StorageManager.class); - private AddressBookStorage addressBookStorage; + private TaskManagerStorage taskManagerStorage; private UserPrefsStorage userPrefsStorage; - public StorageManager(AddressBookStorage addressBookStorage, UserPrefsStorage userPrefsStorage) { + public StorageManager(TaskManagerStorage taskManagerStorage, UserPrefsStorage userPrefsStorage) { super(); - this.addressBookStorage = addressBookStorage; + this.taskManagerStorage = taskManagerStorage; this.userPrefsStorage = userPrefsStorage; } - public StorageManager(String addressBookFilePath, String userPrefsFilePath) { - this(new XmlAddressBookStorage(addressBookFilePath), new JsonUserPrefsStorage(userPrefsFilePath)); + public StorageManager(String taskManagerFilePath, String userPrefsFilePath) { + this(new XmlTaskManagerStorage(taskManagerFilePath), new JsonUserPrefsStorage(userPrefsFilePath)); } // ================ UserPrefs methods ============================== @@ -47,42 +47,42 @@ public void saveUserPrefs(UserPrefs userPrefs) throws IOException { } - // ================ AddressBook methods ============================== + // ================ TaskManager methods ============================== @Override - public String getAddressBookFilePath() { - return addressBookStorage.getAddressBookFilePath(); + public String getTaskManagerFilePath() { + return taskManagerStorage.getTaskManagerFilePath(); } @Override - public Optional readAddressBook() throws DataConversionException, IOException { - return readAddressBook(addressBookStorage.getAddressBookFilePath()); + public Optional readTaskManager() throws DataConversionException, IOException { + return readTaskManager(taskManagerStorage.getTaskManagerFilePath()); } @Override - public Optional readAddressBook(String filePath) throws DataConversionException, IOException { + public Optional readTaskManager(String filePath) throws DataConversionException, IOException { logger.fine("Attempting to read data from file: " + filePath); - return addressBookStorage.readAddressBook(filePath); + return taskManagerStorage.readTaskManager(filePath); } @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException { - saveAddressBook(addressBook, addressBookStorage.getAddressBookFilePath()); + public void saveTaskManager(ReadOnlyTaskManager taskManager) throws IOException { + saveTaskManager(taskManager, taskManagerStorage.getTaskManagerFilePath()); } @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook, String filePath) throws IOException { + public void saveTaskManager(ReadOnlyTaskManager taskManager, String filePath) throws IOException { logger.fine("Attempting to write to data file: " + filePath); - addressBookStorage.saveAddressBook(addressBook, filePath); + taskManagerStorage.saveTaskManager(taskManager, filePath); } @Override @Subscribe - public void handleAddressBookChangedEvent(AddressBookChangedEvent event) { + public void handleTaskManagerChangedEvent(TaskManagerChangedEvent event) { logger.info(LogsCenter.getEventHandlingLogMessage(event, "Local data changed, saving to file")); try { - saveAddressBook(event.data); + saveTaskManager(event.data); } catch (IOException e) { raise(new DataSavingExceptionEvent(e)); } diff --git a/src/main/java/seedu/taskell/storage/TaskManagerStorage.java b/src/main/java/seedu/taskell/storage/TaskManagerStorage.java new file mode 100644 index 000000000000..8eabc19917fe --- /dev/null +++ b/src/main/java/seedu/taskell/storage/TaskManagerStorage.java @@ -0,0 +1,44 @@ +package seedu.taskell.storage; + +import java.io.IOException; +import java.util.Optional; + +import seedu.taskell.commons.exceptions.DataConversionException; +import seedu.taskell.model.ReadOnlyTaskManager; + +/** + * Represents a storage for {@link seedu.taskell.model.TaskManager}. + */ +public interface TaskManagerStorage { + + /** + * Returns the file path of the data file. + */ + String getTaskManagerFilePath(); + + /** + * Returns TaskManager data as a {@link ReadOnlyTaskManager}. + * 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 readTaskManager() throws DataConversionException, IOException; + + /** + * @see #getTaskManagerFilePath() + */ + Optional readTaskManager(String filePath) throws DataConversionException, IOException; + + /** + * Saves the given {@link ReadOnlyTaskManager} to the storage. + * @param taskManager cannot be null. + * @throws IOException if there was any problem writing to the file. + */ + void saveTaskManager(ReadOnlyTaskManager taskManager) throws IOException; + + /** + * @see #saveTaskManager(ReadOnlyTaskManager) + */ + void saveTaskManager(ReadOnlyTaskManager taskManager, String filePath) throws IOException; + +} diff --git a/src/main/java/seedu/address/storage/UserPrefsStorage.java b/src/main/java/seedu/taskell/storage/UserPrefsStorage.java similarity index 73% rename from src/main/java/seedu/address/storage/UserPrefsStorage.java rename to src/main/java/seedu/taskell/storage/UserPrefsStorage.java index ad2dc935187c..913201e1539f 100644 --- a/src/main/java/seedu/address/storage/UserPrefsStorage.java +++ b/src/main/java/seedu/taskell/storage/UserPrefsStorage.java @@ -1,13 +1,13 @@ -package seedu.address.storage; - -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.UserPrefs; +package seedu.taskell.storage; import java.io.IOException; import java.util.Optional; +import seedu.taskell.commons.exceptions.DataConversionException; +import seedu.taskell.model.UserPrefs; + /** - * Represents a storage for {@link seedu.address.model.UserPrefs}. + * Represents a storage for {@link seedu.taskell.model.UserPrefs}. */ public interface UserPrefsStorage { @@ -20,7 +20,7 @@ public interface UserPrefsStorage { Optional readUserPrefs() throws DataConversionException, IOException; /** - * Saves the given {@link seedu.address.model.UserPrefs} to the storage. + * Saves the given {@link seedu.taskell.model.UserPrefs} to the storage. * @param userPrefs cannot be null. * @throws IOException if there was any problem writing to the file. */ diff --git a/src/main/java/seedu/address/storage/XmlAdaptedTag.java b/src/main/java/seedu/taskell/storage/XmlAdaptedTag.java similarity index 77% rename from src/main/java/seedu/address/storage/XmlAdaptedTag.java rename to src/main/java/seedu/taskell/storage/XmlAdaptedTag.java index b9723fafbc67..4e4d3fd22578 100644 --- a/src/main/java/seedu/address/storage/XmlAdaptedTag.java +++ b/src/main/java/seedu/taskell/storage/XmlAdaptedTag.java @@ -1,11 +1,11 @@ -package seedu.address.storage; - -import seedu.address.commons.util.CollectionUtil; -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.tag.Tag; +package seedu.taskell.storage; import javax.xml.bind.annotation.XmlValue; +import seedu.taskell.commons.exceptions.IllegalValueException; +import seedu.taskell.commons.util.CollectionUtil; +import seedu.taskell.model.tag.Tag; + /** * JAXB-friendly adapted version of the Tag. */ @@ -31,7 +31,7 @@ public XmlAdaptedTag(Tag source) { /** * 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 + * @throws IllegalValueException if there were any data constraints violated in the adapted task */ public Tag toModelType() throws IllegalValueException { return new Tag(tagName); diff --git a/src/main/java/seedu/taskell/storage/XmlAdaptedTask.java b/src/main/java/seedu/taskell/storage/XmlAdaptedTask.java new file mode 100644 index 000000000000..9f5da142343d --- /dev/null +++ b/src/main/java/seedu/taskell/storage/XmlAdaptedTask.java @@ -0,0 +1,86 @@ +package seedu.taskell.storage; + +import javax.xml.bind.annotation.XmlElement; + +import seedu.taskell.commons.exceptions.IllegalValueException; +import seedu.taskell.model.tag.Tag; +import seedu.taskell.model.tag.UniqueTagList; +import seedu.taskell.model.task.*; + +import java.util.ArrayList; +import java.util.List; + +/** + * JAXB-friendly version of the Task. + */ +public class XmlAdaptedTask { + + @XmlElement(required = true) + private String description; + @XmlElement(required = true) + private String taskType; + @XmlElement(required = true) + private String startDate; + @XmlElement(required = true) + private String endDate; + @XmlElement(required = true) + private String startTime; + @XmlElement(required = true) + private String endTime; + @XmlElement(required = true) + private String taskPriority; + @XmlElement(required = true) + private String taskStatus; + + @XmlElement + private List tagged = new ArrayList<>(); + + /** + * No-arg constructor for JAXB use. + */ + public XmlAdaptedTask() {} + + + /** + * Converts a given Task into this class for JAXB use. + * + * @param source future changes to this will not affect the created XmlAdaptedTask + */ + public XmlAdaptedTask(ReadOnlyTask source) { + description = source.getDescription().description; + taskType = source.getTaskType(); + startDate = source.getStartDate().taskDate; + endDate = source.getEndDate().taskDate; + startTime = source.getStartTime().taskTime; + endTime = source.getEndTime().taskTime; + taskPriority = source.getTaskPriority().taskPriority; + taskStatus = source.getTaskStatus().taskStatus; + tagged = new ArrayList<>(); + for (Tag tag : source.getTags()) { + tagged.add(new XmlAdaptedTag(tag)); + } + + } + + /** + * Converts this jaxb-friendly adapted task object into the model's Task object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted task + */ + public Task toModelType() throws IllegalValueException { + final List taskTags = new ArrayList<>(); + for (XmlAdaptedTag tag : tagged) { + taskTags.add(tag.toModelType()); + } + final Description description = new Description(this.description); + final String taskType = this.taskType; + final TaskDate startDate = new TaskDate(this.startDate); + final TaskDate endDate = new TaskDate(this.endDate); + final TaskTime startTime= new TaskTime(this.startTime); + final TaskTime endTime = new TaskTime(this.endTime); + final TaskPriority taskPriority = new TaskPriority(this.taskPriority); + final TaskStatus taskStatus = new TaskStatus(this.taskStatus); + final UniqueTagList tags = new UniqueTagList(taskTags); + return new Task(description, taskType, startDate, endDate, startTime, endTime, taskPriority, taskStatus, tags); + } +} diff --git a/src/main/java/seedu/address/storage/XmlFileStorage.java b/src/main/java/seedu/taskell/storage/XmlFileStorage.java similarity index 61% rename from src/main/java/seedu/address/storage/XmlFileStorage.java rename to src/main/java/seedu/taskell/storage/XmlFileStorage.java index 27a5210cadaf..4b910d8e425d 100644 --- a/src/main/java/seedu/address/storage/XmlFileStorage.java +++ b/src/main/java/seedu/taskell/storage/XmlFileStorage.java @@ -1,35 +1,36 @@ -package seedu.address.storage; - -import seedu.address.commons.util.XmlUtil; -import seedu.address.commons.exceptions.DataConversionException; +package seedu.taskell.storage; import javax.xml.bind.JAXBException; + +import seedu.taskell.commons.exceptions.DataConversionException; +import seedu.taskell.commons.util.XmlUtil; + import java.io.File; import java.io.FileNotFoundException; /** - * Stores addressbook data in an XML file + * Stores taskmanager data in an XML file */ public class XmlFileStorage { /** - * Saves the given addressbook data to the specified file. + * Saves the given taskmanager data to the specified file. */ - public static void saveDataToFile(File file, XmlSerializableAddressBook addressBook) + public static void saveDataToFile(File file, XmlSerializableTaskManager taskManager) throws FileNotFoundException { try { - XmlUtil.saveDataToFile(file, addressBook); + XmlUtil.saveDataToFile(file, taskManager); } catch (JAXBException e) { assert false : "Unexpected exception " + e.getMessage(); } } /** - * Returns address book in the file or an empty address book + * Returns task manager in the file or an empty task manager */ - public static XmlSerializableAddressBook loadDataFromSaveFile(File file) throws DataConversionException, + public static XmlSerializableTaskManager loadDataFromSaveFile(File file) throws DataConversionException, FileNotFoundException { try { - return XmlUtil.getDataFromFile(file, XmlSerializableAddressBook.class); + return XmlUtil.getDataFromFile(file, XmlSerializableTaskManager.class); } catch (JAXBException e) { throw new DataConversionException(e); } diff --git a/src/main/java/seedu/address/storage/XmlSerializableAddressBook.java b/src/main/java/seedu/taskell/storage/XmlSerializableTaskManager.java similarity index 57% rename from src/main/java/seedu/address/storage/XmlSerializableAddressBook.java rename to src/main/java/seedu/taskell/storage/XmlSerializableTaskManager.java index b7ec533a3a1e..891ffacfe6ef 100644 --- a/src/main/java/seedu/address/storage/XmlSerializableAddressBook.java +++ b/src/main/java/seedu/taskell/storage/XmlSerializableTaskManager.java @@ -1,45 +1,46 @@ -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; +package seedu.taskell.storage; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; + +import seedu.taskell.commons.exceptions.IllegalValueException; +import seedu.taskell.model.ReadOnlyTaskManager; +import seedu.taskell.model.tag.Tag; +import seedu.taskell.model.tag.UniqueTagList; +import seedu.taskell.model.task.ReadOnlyTask; +import seedu.taskell.model.task.UniqueTaskList; + 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 + * An Immutable TaskManager that is serializable to XML format */ -@XmlRootElement(name = "addressbook") -public class XmlSerializableAddressBook implements ReadOnlyAddressBook { +@XmlRootElement(name = "taskmanager") +public class XmlSerializableTaskManager implements ReadOnlyTaskManager { @XmlElement - private List persons; + private List tasks; @XmlElement private List tags; { - persons = new ArrayList<>(); + tasks = new ArrayList<>(); tags = new ArrayList<>(); } /** * Empty constructor required for marshalling */ - public XmlSerializableAddressBook() {} + public XmlSerializableTaskManager() {} /** * Conversion */ - public XmlSerializableAddressBook(ReadOnlyAddressBook src) { - persons.addAll(src.getPersonList().stream().map(XmlAdaptedPerson::new).collect(Collectors.toList())); + public XmlSerializableTaskManager(ReadOnlyTaskManager src) { + tasks.addAll(src.getTaskList().stream().map(XmlAdaptedTask::new).collect(Collectors.toList())); tags = src.getTagList(); } @@ -55,9 +56,9 @@ public UniqueTagList getUniqueTagList() { } @Override - public UniquePersonList getUniquePersonList() { - UniquePersonList lists = new UniquePersonList(); - for (XmlAdaptedPerson p : persons) { + public UniqueTaskList getUniqueTaskList() { + UniqueTaskList lists = new UniqueTaskList(); + for (XmlAdaptedTask p : tasks) { try { lists.add(p.toModelType()); } catch (IllegalValueException e) { @@ -68,8 +69,8 @@ public UniquePersonList getUniquePersonList() { } @Override - public List getPersonList() { - return persons.stream().map(p -> { + public List getTaskList() { + return tasks.stream().map(p -> { try { return p.toModelType(); } catch (IllegalValueException e) { diff --git a/src/main/java/seedu/taskell/storage/XmlTaskManagerStorage.java b/src/main/java/seedu/taskell/storage/XmlTaskManagerStorage.java new file mode 100644 index 000000000000..f07ef813f1d7 --- /dev/null +++ b/src/main/java/seedu/taskell/storage/XmlTaskManagerStorage.java @@ -0,0 +1,73 @@ +package seedu.taskell.storage; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Optional; +import java.util.logging.Logger; + +import seedu.taskell.commons.core.LogsCenter; +import seedu.taskell.commons.exceptions.DataConversionException; +import seedu.taskell.commons.util.FileUtil; +import seedu.taskell.model.ReadOnlyTaskManager; + +/** + * A class to access TaskManager data stored as an xml file on the hard disk. + */ +public class XmlTaskManagerStorage implements TaskManagerStorage { + + private static final Logger logger = LogsCenter.getLogger(XmlTaskManagerStorage.class); + + private String filePath; + + public XmlTaskManagerStorage(String filePath){ + this.filePath = filePath; + } + + public String getTaskManagerFilePath(){ + return filePath; + } + + /** + * Similar to {@link #readTaskManager()} + * @param filePath location of the data. Cannot be null + * @throws DataConversionException if the file is not in the correct format. + */ + public Optional readTaskManager(String filePath) throws DataConversionException, FileNotFoundException { + assert filePath != null; + + File taskManagerFile = new File(filePath); + + if (!taskManagerFile.exists()) { + logger.info("TaskManager file " + taskManagerFile + " not found"); + return Optional.empty(); + } + + ReadOnlyTaskManager taskManagerOptional = XmlFileStorage.loadDataFromSaveFile(new File(filePath)); + + return Optional.of(taskManagerOptional); + } + + /** + * Similar to {@link #saveTaskManager(ReadOnlyTaskManager)} + * @param filePath location of the data. Cannot be null + */ + public void saveTaskManager(ReadOnlyTaskManager taskManager, String filePath) throws IOException { + assert taskManager != null; + assert filePath != null; + + File file = new File(filePath); + FileUtil.createIfMissing(file); + XmlFileStorage.saveDataToFile(file, new XmlSerializableTaskManager(taskManager)); + } + + @Override + public Optional readTaskManager() throws DataConversionException, IOException { + return readTaskManager(filePath); + } + + @Override + public void saveTaskManager(ReadOnlyTaskManager taskManager) throws IOException { + saveTaskManager(taskManager, filePath); + } +} diff --git a/src/main/java/seedu/taskell/ui/CalendarView.java b/src/main/java/seedu/taskell/ui/CalendarView.java new file mode 100644 index 000000000000..de62e339cd93 --- /dev/null +++ b/src/main/java/seedu/taskell/ui/CalendarView.java @@ -0,0 +1,21 @@ +/** @@author A0142130A **/ +package seedu.taskell.ui; + +import jfxtras.scene.control.agenda.Agenda; + +/** This class holds the necessary elements to display calendar UI via Agenda API from jfxtras + * */ + +public class CalendarView { + + private Agenda agenda; + + public CalendarView() { + agenda = new Agenda(); + } + + public Agenda getAgenda() { + return agenda; + } + +} diff --git a/src/main/java/seedu/address/ui/CommandBox.java b/src/main/java/seedu/taskell/ui/CommandBox.java similarity index 93% rename from src/main/java/seedu/address/ui/CommandBox.java rename to src/main/java/seedu/taskell/ui/CommandBox.java index 2e1409a3016c..56cefcf30c60 100644 --- a/src/main/java/seedu/address/ui/CommandBox.java +++ b/src/main/java/seedu/taskell/ui/CommandBox.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.taskell.ui; import com.google.common.eventbus.Subscribe; import javafx.fxml.FXML; @@ -7,11 +7,11 @@ import javafx.scene.control.TextField; import javafx.scene.layout.AnchorPane; import javafx.stage.Stage; -import seedu.address.commons.events.ui.IncorrectCommandAttemptedEvent; -import seedu.address.logic.Logic; -import seedu.address.logic.commands.*; -import seedu.address.commons.util.FxViewUtil; -import seedu.address.commons.core.LogsCenter; +import seedu.taskell.commons.core.LogsCenter; +import seedu.taskell.commons.events.ui.IncorrectCommandAttemptedEvent; +import seedu.taskell.commons.util.FxViewUtil; +import seedu.taskell.logic.Logic; +import seedu.taskell.logic.commands.*; import java.util.logging.Logger; diff --git a/src/main/java/seedu/taskell/ui/DisplayPanel.java b/src/main/java/seedu/taskell/ui/DisplayPanel.java new file mode 100644 index 000000000000..7df604dc6e38 --- /dev/null +++ b/src/main/java/seedu/taskell/ui/DisplayPanel.java @@ -0,0 +1,99 @@ +/** @@author A0142130A **/ +package seedu.taskell.ui; + +import javafx.scene.Node; +import javafx.scene.control.TextArea; +import javafx.scene.layout.AnchorPane; +import jfxtras.scene.control.agenda.Agenda; +import seedu.taskell.commons.core.LogsCenter; +import seedu.taskell.commons.util.FxViewUtil; + +import java.util.ArrayList; +import java.util.logging.Logger; + +/** + * The Display Panel of the App. + */ +public class DisplayPanel extends UiPart{ + + private static Logger logger = LogsCenter.getLogger(DisplayPanel.class); + + public static final String RESULT_DISPLAY_ID = "resultDisplay"; + private static final String STATUS_BAR_STYLE_SHEET = "result-display"; + + private TextArea display; + private CalendarView calendarView; + + /** + * Constructor is kept private as {@link #load(AnchorPane)} is the only way to create a DisplayPanel. + */ + private DisplayPanel() { + calendarView = new CalendarView(); + display = new TextArea(); + } + + @Override + public void setNode(Node node) { + //not applicable + } + + @Override + public String getFxmlPath() { + return null; //not applicable + } + + /** + * This method should be called after the FX runtime is initialized and in FX application thread. + * @param placeholder The AnchorPane where the DisplayPanel must be inserted + */ + public static DisplayPanel load(AnchorPane placeholder){ + logger.info("Initializing display panel"); + DisplayPanel displayPanel = new DisplayPanel(); + + //displayPanel.display = new TextArea(); + displayPanel.display.setEditable(false); + displayPanel.display.setId(RESULT_DISPLAY_ID); + displayPanel.display.getStyleClass().removeAll(); + displayPanel.display.getStyleClass().add(STATUS_BAR_STYLE_SHEET); + + FxViewUtil.applyAnchorBoundaryParameters(displayPanel.display, 0.0, 0.0, 0.0, 0.0); + placeholder.getChildren().add(displayPanel.display); + + displayPanel.display.setText("Welcome to Taskell!\n" + + "Enter 'add' in command box to add a task.\n" + + "Enter 'list-undo' for list of commands to undo.\n" + + "Enter 'help' for more information about commands.\n" + + "Enter 'calendar' to view calendar."); + + return displayPanel; + } + + public void loadList(AnchorPane placeholder, ArrayList list) { + placeholder.getChildren().clear(); + placeholder.getChildren().add(display); + + display.setText(""); + if (list.isEmpty()) { + display.setText("No commands available for undo."); + } + else { + for (int i=0; i list) { + displayPanel.loadList(displayPanelPlaceholder, list); } - - public void releaseResources() { - browserPanel.freeResources(); + + public void loadCalendarView() { + displayPanel.loadCalendar(displayPanelPlaceholder); } + + /** @@author **/ + } diff --git a/src/main/java/seedu/address/ui/ResultDisplay.java b/src/main/java/seedu/taskell/ui/ResultDisplay.java similarity index 96% rename from src/main/java/seedu/address/ui/ResultDisplay.java rename to src/main/java/seedu/taskell/ui/ResultDisplay.java index 37284ee6c696..56b2dc2cfad7 100644 --- a/src/main/java/seedu/address/ui/ResultDisplay.java +++ b/src/main/java/seedu/taskell/ui/ResultDisplay.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.taskell.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.taskell.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/taskell/ui/StatusBarFooter.java similarity index 84% rename from src/main/java/seedu/address/ui/StatusBarFooter.java rename to src/main/java/seedu/taskell/ui/StatusBarFooter.java index f74f66be6fc9..1dcc0f347caf 100644 --- a/src/main/java/seedu/address/ui/StatusBarFooter.java +++ b/src/main/java/seedu/taskell/ui/StatusBarFooter.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.taskell.ui; import com.google.common.eventbus.Subscribe; import javafx.fxml.FXML; @@ -6,10 +6,12 @@ import javafx.scene.layout.AnchorPane; import javafx.scene.layout.GridPane; import javafx.stage.Stage; +import seedu.taskell.commons.core.LogsCenter; +import seedu.taskell.commons.events.model.TaskManagerChangedEvent; +import seedu.taskell.commons.events.storage.StorageLocationChangedEvent; +import seedu.taskell.commons.util.FxViewUtil; + 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; @@ -90,9 +92,14 @@ public String getFxmlPath() { } @Subscribe - public void handleAddressBookChangedEvent(AddressBookChangedEvent abce) { + public void handleTaskManagerChangedEvent(TaskManagerChangedEvent abce) { String lastUpdated = (new Date()).toString(); logger.info(LogsCenter.getEventHandlingLogMessage(abce, "Setting last updated status to " + lastUpdated)); setSyncStatus("Last Updated: " + lastUpdated); } + + @Subscribe + public void handleStorageLocationChangedEvent(StorageLocationChangedEvent event) { + setSaveLocation(event.getConfig().getTaskManagerFilePath()); + } } diff --git a/src/main/java/seedu/taskell/ui/TaskCard.java b/src/main/java/seedu/taskell/ui/TaskCard.java new file mode 100644 index 000000000000..b1af50d23c09 --- /dev/null +++ b/src/main/java/seedu/taskell/ui/TaskCard.java @@ -0,0 +1,103 @@ +package seedu.taskell.ui; + +import javafx.fxml.FXML; +import javafx.scene.Node; +import javafx.scene.control.Label; +import javafx.scene.layout.HBox; +import seedu.taskell.model.task.ReadOnlyTask; +import seedu.taskell.model.task.Task; +import seedu.taskell.model.task.TaskPriority; + +public class TaskCard extends UiPart{ + + private static final String FXML = "TaskListCard.fxml"; + + @FXML + private HBox cardPane; + @FXML + private Label description; + @FXML + private Label id; + @FXML + private Label startDate; + @FXML + private Label endDate; + @FXML + private Label startTime; + @FXML + private Label endTime; + @FXML + private Label taskStatus; + @FXML + private Label tags; + + 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 A0139257X + @FXML + public void initialize() { + id.setText(displayedIndex + ". "); + description.setText(task.getDescription().description); + tags.setText(task.tagsString()); + taskStatus.setText(task.getTaskStatus().taskStatus); + + setDateTime(); + setDateTimeVisibility(); + setCardPaneBackground(); + } + + private void setDateTime() { + startDate.setText(task.getStartDate().getDisplayDate()); + endDate.setText(task.getEndDate().getDisplayDate()); + startTime.setText(task.getStartTime().taskTime); + endTime.setText(task.getEndTime().taskTime); + + } + + private void setDateTimeVisibility() { + if (task.getTaskType().equals(Task.FLOATING_TASK)) { + startDate.setVisible(false); + endDate.setVisible(false); + startTime.setVisible(false); + endTime.setVisible(false); + } + + } + + private void setCardPaneBackground() { + if (task.getTaskPriority().taskPriority.equals(TaskPriority.HIGH_PRIORITY)) { + cardPane.setStyle(TaskPriority.HIGH_PRIORITY_BACKGROUND); + } else if (task.getTaskPriority().taskPriority.equals(TaskPriority.MEDIUM_PRIORITY)) { + cardPane.setStyle(TaskPriority.MEDIUM_PRIORITY_BACKGROUND); + } else if (task.getTaskPriority().taskPriority.equals(TaskPriority.LOW_PRIORITY)) { + cardPane.setStyle(TaskPriority.LOW_PRIORITY_BACKGROUND); + } + } +//@@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/taskell/ui/TaskListPanel.java b/src/main/java/seedu/taskell/ui/TaskListPanel.java new file mode 100644 index 000000000000..91e484e63dca --- /dev/null +++ b/src/main/java/seedu/taskell/ui/TaskListPanel.java @@ -0,0 +1,108 @@ +package seedu.taskell.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.taskell.commons.core.LogsCenter; +import seedu.taskell.commons.events.ui.TaskPanelSelectionChangedEvent; +import seedu.taskell.model.task.ReadOnlyTask; + +import java.util.logging.Logger; + +/** + * Panel containing the list of tasks. + */ +public class TaskListPanel extends UiPart { + private final Logger logger = LogsCenter.getLogger(TaskListPanel.class); + private static final String FXML = "TaskListPanel.fxml"; + private VBox panel; + private AnchorPane placeHolderPane; + + @FXML + private ListView taskListView; + + 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()); + setEventHandlerForSelectionChangeEvent(); + } + + private void addToPlaceholder() { + SplitPane.setResizableWithParent(placeHolderPane, false); + placeHolderPane.getChildren().add(panel); + } + + private void setEventHandlerForSelectionChangeEvent() { + taskListView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> { + if (newValue != null) { + logger.fine("Selection in task list panel changed to : '" + newValue + "'"); + raise(new TaskPanelSelectionChangedEvent(newValue)); + } + }); + } + + public void scrollTo(int index) { + Platform.runLater(() -> { + taskListView.scrollTo(index); + taskListView.getSelectionModel().clearAndSelect(index); + }); + } + + class TaskListViewCell extends ListCell { + + 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/taskell/ui/Ui.java similarity index 88% rename from src/main/java/seedu/address/ui/Ui.java rename to src/main/java/seedu/taskell/ui/Ui.java index e6a67fe8c027..409f419fa110 100644 --- a/src/main/java/seedu/address/ui/Ui.java +++ b/src/main/java/seedu/taskell/ui/Ui.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.taskell.ui; import javafx.stage.Stage; diff --git a/src/main/java/seedu/address/ui/UiManager.java b/src/main/java/seedu/taskell/ui/UiManager.java similarity index 72% rename from src/main/java/seedu/address/ui/UiManager.java rename to src/main/java/seedu/taskell/ui/UiManager.java index 4a4dba3a2f6e..1e89e8de82fd 100644 --- a/src/main/java/seedu/address/ui/UiManager.java +++ b/src/main/java/seedu/taskell/ui/UiManager.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.taskell.ui; import com.google.common.eventbus.Subscribe; import javafx.application.Platform; @@ -6,17 +6,19 @@ 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 seedu.taskell.MainApp; +import seedu.taskell.commons.core.ComponentManager; +import seedu.taskell.commons.core.Config; +import seedu.taskell.commons.core.LogsCenter; +import seedu.taskell.commons.events.storage.DataSavingExceptionEvent; +import seedu.taskell.commons.events.ui.DisplayCalendarViewEvent; +import seedu.taskell.commons.events.ui.DisplayListChangedEvent; +import seedu.taskell.commons.events.ui.JumpToListRequestEvent; +import seedu.taskell.commons.events.ui.TaskPanelSelectionChangedEvent; +import seedu.taskell.commons.events.ui.ShowHelpRequestEvent; +import seedu.taskell.commons.util.StringUtil; +import seedu.taskell.logic.Logic; +import seedu.taskell.model.UserPrefs; import java.util.logging.Logger; @@ -25,7 +27,7 @@ */ 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_manager_32.png"; private Logic logic; private Config config; @@ -62,7 +64,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) { @@ -81,7 +82,7 @@ void showAlertDialogAndWait(Alert.AlertType type, String title, String headerTex private static void showAlertDialogAndWait(Stage owner, AlertType type, String title, String headerText, String contentText) { final Alert alert = new Alert(type); - alert.getDialogPane().getStylesheets().add("view/DarkTheme.css"); + alert.getDialogPane().getStylesheets().add("view/TaskellCyanTheme.css"); alert.initOwner(owner); alert.setTitle(title); alert.setHeaderText(headerText); @@ -114,13 +115,25 @@ 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); } - + + /** @@author A0142130A **/ + + @Subscribe + private void handleDisplayList(DisplayListChangedEvent event) { + logger.info(LogsCenter.getEventHandlingLogMessage(event)); + logger.info("Displaying list..."); + mainWindow.loadList(event.getList()); + } + @Subscribe - private void handlePersonPanelSelectionChangedEvent(PersonPanelSelectionChangedEvent event){ + private void handleShowCalendarView(DisplayCalendarViewEvent event) { logger.info(LogsCenter.getEventHandlingLogMessage(event)); - mainWindow.loadPersonPage(event.getNewSelection()); + logger.info("Displaying calendar view..."); + mainWindow.loadCalendarView(); } + + /** @@author **/ } diff --git a/src/main/java/seedu/address/ui/UiPart.java b/src/main/java/seedu/taskell/ui/UiPart.java similarity index 94% rename from src/main/java/seedu/address/ui/UiPart.java rename to src/main/java/seedu/taskell/ui/UiPart.java index 0a4ceb33e9b7..f5c7c4343d43 100644 --- a/src/main/java/seedu/address/ui/UiPart.java +++ b/src/main/java/seedu/taskell/ui/UiPart.java @@ -1,13 +1,13 @@ -package seedu.address.ui; +package seedu.taskell.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.taskell.commons.core.EventsCenter; +import seedu.taskell.commons.events.BaseEvent; +import seedu.taskell.commons.util.AppUtil; /** * Base class for UI parts. diff --git a/src/main/java/seedu/address/ui/UiPartLoader.java b/src/main/java/seedu/taskell/ui/UiPartLoader.java similarity index 97% rename from src/main/java/seedu/address/ui/UiPartLoader.java rename to src/main/java/seedu/taskell/ui/UiPartLoader.java index f880685a5b15..471b1e2c1145 100644 --- a/src/main/java/seedu/address/ui/UiPartLoader.java +++ b/src/main/java/seedu/taskell/ui/UiPartLoader.java @@ -1,10 +1,10 @@ -package seedu.address.ui; +package seedu.taskell.ui; import javafx.fxml.FXMLLoader; import javafx.scene.Node; import javafx.scene.layout.AnchorPane; import javafx.stage.Stage; -import seedu.address.MainApp; +import seedu.taskell.MainApp; /** * A utility class to load UiParts from FXML files. diff --git a/src/main/resources/images/address_book_32.png b/src/main/resources/images/task_manager_32.png similarity index 100% rename from src/main/resources/images/address_book_32.png rename to src/main/resources/images/task_manager_32.png diff --git a/src/main/resources/view/CommandBox.fxml b/src/main/resources/view/CommandBox.fxml index 575de420b994..330fca331809 100644 --- a/src/main/resources/view/CommandBox.fxml +++ b/src/main/resources/view/CommandBox.fxml @@ -3,7 +3,7 @@ + fx:controller="seedu.taskell.ui.CommandBox" stylesheets="@TaskellCyanTheme.css"> diff --git a/src/main/resources/view/HelpWindow.fxml b/src/main/resources/view/HelpWindow.fxml index c4cbd84cac28..80da7949614f 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..675fcb826cc1 100644 --- a/src/main/resources/view/MainWindow.fxml +++ b/src/main/resources/view/MainWindow.fxml @@ -4,9 +4,9 @@ - + - + @@ -14,7 +14,7 @@ - + @@ -34,17 +34,17 @@ - + - + - + - + diff --git a/src/main/resources/view/ResultDisplay.fxml b/src/main/resources/view/ResultDisplay.fxml index cc650d739e22..e876cb30e219 100644 --- a/src/main/resources/view/ResultDisplay.fxml +++ b/src/main/resources/view/ResultDisplay.fxml @@ -3,9 +3,9 @@ + fx:controller="seedu.taskell.ui.ResultDisplay"> - + diff --git a/src/main/resources/view/StatusBarFooter.fxml b/src/main/resources/view/StatusBarFooter.fxml index 2656558b6eb7..a3457bdff384 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 70% rename from src/main/resources/view/PersonListCard.fxml rename to src/main/resources/view/TaskListCard.fxml index 13d4b149651b..404487781c27 100644 --- a/src/main/resources/view/PersonListCard.fxml +++ b/src/main/resources/view/TaskListCard.fxml @@ -14,7 +14,7 @@ - + @@ -26,14 +26,16 @@ - - diff --git a/src/main/resources/view/PersonListPanel.fxml b/src/main/resources/view/TaskListPanel.fxml similarity index 52% rename from src/main/resources/view/PersonListPanel.fxml rename to src/main/resources/view/TaskListPanel.fxml index 000c4c999b65..33b583591d03 100644 --- a/src/main/resources/view/PersonListPanel.fxml +++ b/src/main/resources/view/TaskListPanel.fxml @@ -3,12 +3,12 @@ - + - + - + diff --git a/src/main/resources/view/DarkTheme.css b/src/main/resources/view/TaskellCyanTheme.css similarity index 78% rename from src/main/resources/view/DarkTheme.css rename to src/main/resources/view/TaskellCyanTheme.css index 8043b344253a..749be172f31f 100644 --- a/src/main/resources/view/DarkTheme.css +++ b/src/main/resources/view/TaskellCyanTheme.css @@ -1,5 +1,5 @@ .background { - -fx-background-color: derive(#1d1d1d, 20%); + -fx-background-color: derive(#00838F, 20%); } .label { @@ -39,9 +39,9 @@ } .table-view { - -fx-base: #1d1d1d; - -fx-control-inner-background: #1d1d1d; - -fx-background-color: #1d1d1d; + -fx-base: #00838F; + -fx-control-inner-background: #00838F; + -fx-background-color: #00838F; -fx-table-cell-border-color: transparent; -fx-table-header-border-color: transparent; -fx-padding: 5; @@ -76,14 +76,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 #00838F transparent #00838F; + -fx-background-color: transparent, derive(#00838F, 10%); } .split-pane { -fx-border-radius: 1; -fx-border-width: 1; - -fx-background-color: derive(#1d1d1d, 20%); + -fx-background-color: derive(#00838F, 20%); } .list-cell { @@ -102,27 +102,28 @@ } .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(#00838F, 20%); } .anchor-pane-with-border { - -fx-background-color: derive(#1d1d1d, 20%); - -fx-border-color: derive(#1d1d1d, 10%); + -fx-background-color: derive(#00838F, 20%); + -fx-border-color: derive(#00838F, 10%); -fx-border-top-width: 1px; } .status-bar { - -fx-background-color: derive(#1d1d1d, 20%); + -fx-background-color: derive(#00838F, 20%); -fx-text-fill: black; } .result-display { -fx-background-color: #ffffff; + -fx-font-size: 16px; } .result-display .label { @@ -134,8 +135,8 @@ } .status-bar-with-border { - -fx-background-color: derive(#1d1d1d, 30%); - -fx-border-color: derive(#1d1d1d, 25%); + -fx-background-color: derive(#00838F, 30%); + -fx-border-color: derive(#00838F, 25%); -fx-border-width: 1px; } @@ -144,17 +145,17 @@ } .grid-pane { - -fx-background-color: derive(#1d1d1d, 30%); - -fx-border-color: derive(#1d1d1d, 30%); + -fx-background-color: derive(#00838F, 30%); + -fx-border-color: derive(#00838F, 30%); -fx-border-width: 1px; } .grid-pane .anchor-pane { - -fx-background-color: derive(#1d1d1d, 30%); + -fx-background-color: derive(#00838F, 30%); } .context-menu { - -fx-background-color: derive(#1d1d1d, 50%); + -fx-background-color: derive(#00838F, 50%); } .context-menu .label { @@ -162,7 +163,7 @@ } .menu-bar { - -fx-background-color: derive(#1d1d1d, 20%); + -fx-background-color: derive(#00838F, 20%); } .menu-bar .label { @@ -172,6 +173,10 @@ -fx-opacity: 0.9; } +.menu-item:focused { + -fx-background-color: black; +} + .menu .left-container { -fx-background-color: black; } @@ -186,7 +191,7 @@ -fx-border-color: #e2e2e2; -fx-border-width: 2; -fx-background-radius: 0; - -fx-background-color: #1d1d1d; + -fx-background-color: #00838F; -fx-font-family: "Segoe UI", Helvetica, Arial, sans-serif; -fx-font-size: 11pt; -fx-text-fill: #d8d8d8; @@ -199,7 +204,7 @@ .button:pressed, .button:default:hover:pressed { -fx-background-color: white; - -fx-text-fill: #1d1d1d; + -fx-text-fill: #00838F; } .button:focused { @@ -212,7 +217,7 @@ .button:disabled, .button:default:disabled { -fx-opacity: 0.4; - -fx-background-color: #1d1d1d; + -fx-background-color: #00838F; -fx-text-fill: white; } @@ -226,11 +231,11 @@ } .dialog-pane { - -fx-background-color: #1d1d1d; + -fx-background-color: #00838F; } .dialog-pane > *.button-bar > *.container { - -fx-background-color: #1d1d1d; + -fx-background-color: #00838F; } .dialog-pane > *.label.content { @@ -240,7 +245,7 @@ } .dialog-pane:header *.header-panel { - -fx-background-color: derive(#1d1d1d, 25%); + -fx-background-color: derive(#00838F, 25%); } .dialog-pane:header *.header-panel *.label { @@ -251,7 +256,7 @@ } .scroll-bar .thumb { - -fx-background-color: derive(#1d1d1d, 50%); + -fx-background-color: derive(#00838F, 50%); -fx-background-insets: 3; } @@ -273,7 +278,7 @@ } #cardPane { - -fx-background-color: transparent; + -fx-background-color: #E0F7FA; -fx-border-color: #d6d6d6; -fx-border-width: 1 1 1 1; } @@ -283,6 +288,6 @@ -fx-text-fill: #F70D1A; } -#filterField, #personListPanel, #personWebpage { +#filterField, #taskListPanel, #taskWebpage { -fx-effect: innershadow(gaussian, black, 10, 0, 0, 0); } \ No newline at end of file diff --git a/src/test/data/ConfigUtilTest/ExtraValuesConfig.json b/src/test/data/ConfigUtilTest/ExtraValuesConfig.json index 578b4445204b..34df9f0f5894 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", + "taskManagerFilePath" : "taskmanager.xml", + "taskManagerName" : "TypicalTaskManagerName", "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..e9d5c5781a76 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" + "taskManagerFilePath" : "taskmanager.xml", + "taskManagerName" : "TypicalTaskManagerName" } \ No newline at end of file diff --git a/src/test/data/XmlAddressBookStorageTest/NotXmlFormatAddressBook.xml b/src/test/data/XmlTaskManagerStorageTest/NotXmlFormatTaskManager.xml similarity index 100% rename from src/test/data/XmlAddressBookStorageTest/NotXmlFormatAddressBook.xml rename to src/test/data/XmlTaskManagerStorageTest/NotXmlFormatTaskManager.xml diff --git a/src/test/data/XmlUtilTest/tempAddressBook.xml b/src/test/data/XmlUtilTest/tempTaskManager.xml similarity index 84% rename from src/test/data/XmlUtilTest/tempAddressBook.xml rename to src/test/data/XmlUtilTest/tempTaskManager.xml index 41eeb8eb391a..2d1fb996212e 100644 --- a/src/test/data/XmlUtilTest/tempAddressBook.xml +++ b/src/test/data/XmlUtilTest/tempTaskManager.xml @@ -1,6 +1,6 @@ - - + + 1 John Doe @@ -8,8 +8,8 @@ - + Friends - + diff --git a/src/test/data/XmlUtilTest/validAddressBook.xml b/src/test/data/XmlUtilTest/validAddressBook.xml deleted file mode 100644 index eafca730fb1e..000000000000 --- a/src/test/data/XmlUtilTest/validAddressBook.xml +++ /dev/null @@ -1,57 +0,0 @@ - - - - Hans Muster - 9482424 - hans@google.com -
4th street
-
- - Ruth Mueller - 87249245 - ruth@google.com -
81th street
-
- - Heinz Kurz - 95352563 - heinz@yahoo.com -
wall street
-
- - Cornelia Meier - 87652533 - cornelia@google.com -
10th street
-
- - Werner Meyer - 9482224 - werner@gmail.com -
michegan ave
-
- - Lydia Kunz - 9482427 - lydia@gmail.com -
little tokyo
-
- - Anna Best - 9482442 - anna@google.com -
4th street
-
- - Stefan Meier - 8482424 - stefan@mail.com -
little india
-
- - Martin Mueller - 8482131 - hans@google.com -
chicago ave
-
-
diff --git a/src/test/data/XmlUtilTest/validTaskManager.xml b/src/test/data/XmlUtilTest/validTaskManager.xml new file mode 100644 index 000000000000..810edeeacdef --- /dev/null +++ b/src/test/data/XmlUtilTest/validTaskManager.xml @@ -0,0 +1,93 @@ + + + + Hans Muster + EVENT + 1-1-2100 + 1-12-2100 + 12:30AM + 12:45AM + 0 + incomplete + + + Ruth Mueller + EVENT + 1-1-2100 + 1-12-2100 + 12:30AM + 12:45AM + 0 + incomplete + + + Heinz Kurz + EVENT + 1-1-2100 + 1-12-2100 + 12:30AM + 12:45AM + 0 + incomplete + + + Cornelia Meier + EVENT + 1-1-2100 + 1-12-2100 + 12:30AM + 12:45AM + 0 + incomplete + + + Werner Meyer + EVENT + 1-1-2100 + 1-12-2100 + 12:30AM + 12:45AM + 0 + incomplete + + + Lydia Kunz + EVENT + 1-1-2100 + 1-12-2100 + 12:30AM + 12:45AM + 0 + incomplete + + + Anna Best + EVENT + 1-1-2100 + 1-12-2100 + 12:30AM + 12:45AM + 0 + incomplete + + + Stefan Meier + EVENT + 1-1-2100 + 1-12-2100 + 12:30AM + 12:45AM + 0 + incomplete + + + Martin Mueller + EVENT + 1-1-2100 + 1-12-2100 + 12:30AM + 12:45AM + 0 + incomplete + + diff --git a/src/test/java/guitests/AddCommandTest.java b/src/test/java/guitests/AddCommandTest.java index 3b2e1844bd0d..5279639b0912 100644 --- a/src/test/java/guitests/AddCommandTest.java +++ b/src/test/java/guitests/AddCommandTest.java @@ -1,53 +1,77 @@ package guitests; -import guitests.guihandles.PersonCardHandle; +import guitests.guihandles.TaskCardHandle; import org.junit.Test; -import seedu.address.logic.commands.AddCommand; -import seedu.address.commons.core.Messages; -import seedu.address.testutil.TestPerson; -import seedu.address.testutil.TestUtil; + +import seedu.taskell.commons.core.Messages; +import seedu.taskell.logic.commands.AddCommand; +import seedu.taskell.logic.commands.HelpCommand; +import seedu.taskell.testutil.TestTask; +import seedu.taskell.testutil.TestUtil; import static org.junit.Assert.assertTrue; +import static seedu.taskell.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -public class AddCommandTest extends AddressBookGuiTest { +//@@author A0139257X-reused +public class AddCommandTest extends TaskManagerGuiTest { @Test public void add() { - //add one person - TestPerson[] currentList = td.getTypicalPersons(); - TestPerson personToAdd = td.hoon; - assertAddSuccess(personToAdd, currentList); - currentList = TestUtil.addPersonsToList(currentList, personToAdd); - - //add another person - personToAdd = td.ida; - assertAddSuccess(personToAdd, currentList); - currentList = TestUtil.addPersonsToList(currentList, personToAdd); - - //add duplicate person - commandBox.runCommand(td.hoon.getAddCommand()); - assertResultMessage(AddCommand.MESSAGE_DUPLICATE_PERSON); - assertTrue(personListPanel.isListMatching(currentList)); + //add one task + TestTask[] currentList = td.getTypicalTasks(); + TestTask taskToAdd = td.holdMeeting; + assertAddSuccess(taskToAdd, currentList); + currentList = TestUtil.addTasksToList(currentList, taskToAdd); + + //add another task + taskToAdd = td.inspectWarehouse; + assertAddSuccess(taskToAdd, currentList); + currentList = TestUtil.addTasksToList(currentList, taskToAdd); + + //add duplicate task + commandBox.runCommand(td.holdMeeting.getAddCommand()); + assertResultMessage(AddCommand.MESSAGE_DUPLICATE_TASK); + assertTrue(taskListPanel.isListMatching(currentList)); //add to empty list commandBox.runCommand("clear"); - assertAddSuccess(td.alice); + assertAddSuccess(td.archivePastEmails); //invalid command commandBox.runCommand("adds Johnny"); assertResultMessage(Messages.MESSAGE_UNKNOWN_COMMAND); + + //add valid floating task + commandBox.runCommand("clear"); + assertAddFloatingSuccess(td.floatingTask_Valid); + + commandBox.runCommand("clear"); + assertAddFloatingSuccess(td.floatingTask_NonIntuitiveDescription); + } - private void assertAddSuccess(TestPerson personToAdd, TestPerson... currentList) { - commandBox.runCommand(personToAdd.getAddCommand()); + private void assertAddSuccess(TestTask taskToAdd, TestTask... currentList) { + commandBox.runCommand(taskToAdd.getAddCommand()); + + //confirm the new card contains the right data + TaskCardHandle addedCard = taskListPanel.navigateToTask(taskToAdd.getDescription().description); + 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)); + } + + private void assertAddFloatingSuccess(TestTask taskToAdd, TestTask... currentList) { + commandBox.runCommand(taskToAdd.getAddFloatingCommand()); //confirm the new card contains the right data - PersonCardHandle addedCard = personListPanel.navigateToPerson(personToAdd.getName().fullName); - assertMatching(personToAdd, addedCard); + TaskCardHandle addedCard = taskListPanel.navigateToTask(taskToAdd.getDescription().description); + 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/ClearCommandTest.java b/src/test/java/guitests/ClearCommandTest.java index 9d52b427659c..0d467663f736 100644 --- a/src/test/java/guitests/ClearCommandTest.java +++ b/src/test/java/guitests/ClearCommandTest.java @@ -4,18 +4,18 @@ import static org.junit.Assert.assertTrue; -public class ClearCommandTest extends AddressBookGuiTest { +public class ClearCommandTest extends TaskManagerGuiTest { @Test public void clear() { //verify a non-empty list can be cleared - assertTrue(personListPanel.isListMatching(td.getTypicalPersons())); + assertTrue(taskListPanel.isListMatching(td.getTypicalTasks())); assertClearCommandSuccess(); //verify other commands can work after a clear command - commandBox.runCommand(td.hoon.getAddCommand()); - assertTrue(personListPanel.isListMatching(td.hoon)); + commandBox.runCommand(td.holdMeeting.getAddCommand()); + assertTrue(taskListPanel.isListMatching(td.holdMeeting)); commandBox.runCommand("delete 1"); assertListSize(0); @@ -26,6 +26,6 @@ public void clear() { private void assertClearCommandSuccess() { commandBox.runCommand("clear"); assertListSize(0); - assertResultMessage("Address book has been cleared!"); + assertResultMessage("Task manager has been cleared!"); } } diff --git a/src/test/java/guitests/CommandBoxTest.java b/src/test/java/guitests/CommandBoxTest.java index 1379198bf8b0..62d0eea89e0f 100644 --- a/src/test/java/guitests/CommandBoxTest.java +++ b/src/test/java/guitests/CommandBoxTest.java @@ -4,11 +4,11 @@ import static org.junit.Assert.assertEquals; -public class CommandBoxTest extends AddressBookGuiTest { +public class CommandBoxTest extends TaskManagerGuiTest { @Test public void commandBox_commandSucceeds_textCleared() { - commandBox.runCommand(td.benson.getAddCommand()); + commandBox.runCommand(td.borrowBooks.getAddCommand()); assertEquals(commandBox.getCommandInput(), ""); } diff --git a/src/test/java/guitests/DeleteCommandTest.java b/src/test/java/guitests/DeleteCommandTest.java index 10c7b9e0dbea..5378997108eb 100644 --- a/src/test/java/guitests/DeleteCommandTest.java +++ b/src/test/java/guitests/DeleteCommandTest.java @@ -1,54 +1,55 @@ package guitests; import org.junit.Test; -import seedu.address.testutil.TestPerson; -import seedu.address.testutil.TestUtil; + +import seedu.taskell.testutil.TestTask; +import seedu.taskell.testutil.TestUtil; import static org.junit.Assert.assertTrue; -import static seedu.address.logic.commands.DeleteCommand.MESSAGE_DELETE_PERSON_SUCCESS; +import static seedu.taskell.logic.commands.DeleteCommand.MESSAGE_DELETE_TASK_SUCCESS; -public class DeleteCommandTest extends AddressBookGuiTest { +public class DeleteCommandTest extends TaskManagerGuiTest { @Test public void delete() { //delete the first in the list - TestPerson[] currentList = td.getTypicalPersons(); + TestTask[] currentList = td.getTypicalTasks(); int targetIndex = 1; assertDeleteSuccess(targetIndex, currentList); //delete the last in the list - currentList = TestUtil.removePersonFromList(currentList, targetIndex); + currentList = TestUtil.removeTaskFromList(currentList, targetIndex); targetIndex = currentList.length; assertDeleteSuccess(targetIndex, currentList); //delete from the middle of the list - currentList = TestUtil.removePersonFromList(currentList, targetIndex); + currentList = TestUtil.removeTaskFromList(currentList, targetIndex); targetIndex = currentList.length/2; assertDeleteSuccess(targetIndex, currentList); //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)); + assertResultMessage(String.format(MESSAGE_DELETE_TASK_SUCCESS, taskToDelete)); } } diff --git a/src/test/java/guitests/DoneCommandTest.java b/src/test/java/guitests/DoneCommandTest.java new file mode 100644 index 000000000000..7ab85654dbc0 --- /dev/null +++ b/src/test/java/guitests/DoneCommandTest.java @@ -0,0 +1,47 @@ +//@@author A0148004R +package guitests; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertEquals; +import static seedu.taskell.logic.commands.DoneCommand.MESSAGE_DONE_TASK_SUCCESS; + +import org.junit.Test; + +import guitests.guihandles.TaskCardHandle; +import seedu.taskell.model.task.TaskStatus; +import seedu.taskell.testutil.TestTask; + +public class DoneCommandTest extends TaskManagerGuiTest { + + @Test + public void done() { + + //done the first item on the list + TestTask[] currentList = td.getTypicalTasks(); + int targetIndex = 1; + assertDoneSuccess(targetIndex, currentList); + + commandBox.runCommand("done " + currentList.length + 1); + assertResultMessage("The task index provided is invalid"); + } + + /** + * Runs the done command to finish the task at specified index and confirms the result is correct. + * @param targetIndexOneIndexed e.g. to mark the first task in the list as finished, 1 should be given as the target index. + * @param currentList A copy of the current list of tasks (before marking as ). + */ + private void assertDoneSuccess(int targetIndexOneIndexed, final TestTask[] currentList) { + commandBox.runCommand("done " + targetIndexOneIndexed); + TestTask taskToFinish = currentList[targetIndexOneIndexed-1]; //-1 because array uses zero indexing + + //confirm the new card contains the right data + TaskCardHandle updatedCard = taskListPanel.navigateToTask(taskToFinish.getDescription().description); + + assertEquals(TaskStatus.FINISHED, updatedCard.getTaskComplete()); + + //confirm the result message is correct + assertResultMessage(String.format(MESSAGE_DONE_TASK_SUCCESS, taskToFinish)); + } + +} +//@@author diff --git a/src/test/java/guitests/EditDescriptionCommandTest.java b/src/test/java/guitests/EditDescriptionCommandTest.java new file mode 100644 index 000000000000..b55a0861f6f4 --- /dev/null +++ b/src/test/java/guitests/EditDescriptionCommandTest.java @@ -0,0 +1,76 @@ +//@@author A0142073R +package guitests; + +import org.junit.Test; + +import seedu.taskell.commons.exceptions.IllegalValueException; +import seedu.taskell.testutil.TestTask; +import seedu.taskell.testutil.TestUtil; + +import static org.junit.Assert.assertTrue; +import static seedu.taskell.logic.commands.EditDescriptionCommand.MESSAGE_EDIT_TASK_SUCCESS; + +public class EditDescriptionCommandTest extends TaskManagerGuiTest { + + @Test + public void edit() throws IllegalValueException { + + // edit the first in the list + TestTask[] currentList = td.getTypicalTasks(); + int targetIndex = 1; + String newDescription = "make tea"; + asserteditSuccess(targetIndex, currentList, newDescription); + + // edit the last in the list + targetIndex = currentList.length; + newDescription = "get ready for software demo"; + asserteditSuccess(targetIndex, currentList, newDescription); + + // invalid index + commandBox.runCommand("edit-name " + currentList.length + 1 + " go shopping"); + assertResultMessage("The task index provided is invalid"); + + // invalid command + commandBox.runCommand("edit-desc "); + assertResultMessage("Invalid command format! \n" + "edit-desc" + + "/"+ "edit-name" + + ": Edits the description task identified by the index number used in the last task listing.\n" + + "Parameters: INDEX (must be a positive integer) NEW_DESCRIPTION\n" + + "Example: " + "edit-desc" + " 1 buy cake\n" + + "Example: " + "edit-name" + " 2 do 2103t\n"); + + } + + /** + * Runs the edit command to edit the description of a task at specified + * index and confirms the result is correct. + * + * @param targetIndexOneIndexed + * e.g. to edit the description of 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). + * @throws IllegalValueException + */ + private void asserteditSuccess(int targetIndexOneIndexed, final TestTask[] currentList, String newDescription) + throws IllegalValueException { + TestTask taskToedit = currentList[targetIndexOneIndexed - 1]; // -1 + // because + // array + // uses + // zero + // indexing + TestTask newTask = TestUtil.editTaskDescription(taskToedit, newDescription); + + commandBox.runCommand("edit-desc " + targetIndexOneIndexed + " " + newDescription); + TestTask[] current = TestUtil.replaceTaskFromList(currentList, newTask, targetIndexOneIndexed - 1); + // confirm the list now contains all previous tasks except the edited + // task + assertTrue(taskListPanel.isListMatching(current)); + + // confirm the result message is correct + assertResultMessage(String.format(MESSAGE_EDIT_TASK_SUCCESS, taskToedit, newTask)); + } + +} +//@@author \ No newline at end of file diff --git a/src/test/java/guitests/EditEndTimeCommandTest.java b/src/test/java/guitests/EditEndTimeCommandTest.java new file mode 100644 index 000000000000..81bb57f120b6 --- /dev/null +++ b/src/test/java/guitests/EditEndTimeCommandTest.java @@ -0,0 +1,75 @@ +//@@author A0142073R +package guitests; + +import org.junit.Test; + +import seedu.taskell.commons.exceptions.IllegalValueException; +import seedu.taskell.testutil.TestTask; +import seedu.taskell.testutil.TestUtil; + +import static org.junit.Assert.assertTrue; +import static seedu.taskell.logic.commands.EditEndTimeCommand.MESSAGE_EDIT_TASK_SUCCESS; + +public class EditEndTimeCommandTest extends TaskManagerGuiTest { + + @Test + public void edit() throws IllegalValueException { + + // edit the first in the list + TestTask[] currentList = td.getTypicalTasks(); + int targetIndex = 1; + String newDate = "5pm"; + asserteditSuccess(targetIndex, currentList, newDate); + + // edit the last in the list + targetIndex = currentList.length; + newDate = "12.35am"; + asserteditSuccess(targetIndex, currentList, newDate); + + // invalid index + commandBox.runCommand("edit-endTime " + currentList.length + 1 + " 10am"); + assertResultMessage("The task index provided is invalid"); + + //invalid command format + commandBox.runCommand("edit-endTime 1 to 4pm "); + assertResultMessage("Invalid command format! \n" + + "edit-endTime" + + ": Edits the end time of a task identified by the index number used in the last task listing.\n" + + "Parameters: INDEX (must be a positive integer) NEW_END_TIME\n" + + "Example: " + "edit-endTime" + " 1 9pm "); + + } + + /** + * Runs the edit command to edit the end time of a task at specified + * index and confirms the result is correct. + * + * @param targetIndexOneIndexed + * e.g. to edit the end time of 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). + * @throws IllegalValueException + */ + private void asserteditSuccess(int targetIndexOneIndexed, final TestTask[] currentList, String newDate) + throws IllegalValueException { + TestTask taskToedit = currentList[targetIndexOneIndexed - 1]; // -1 + // because + // array + // uses + // zero + // indexing + TestTask newTask = TestUtil.editTaskEndTime(taskToedit, newDate); + + commandBox.runCommand("edit-endTime " + targetIndexOneIndexed + " " + newDate); + TestTask[] current = TestUtil.replaceTaskFromList(currentList, newTask, targetIndexOneIndexed - 1); + // confirm the list now contains all previous tasks except the edited + // task + assertTrue(taskListPanel.isListMatching(current)); + + // confirm the result message is correct + assertResultMessage(String.format(MESSAGE_EDIT_TASK_SUCCESS, taskToedit, newTask)); + } + +} +//@@author \ No newline at end of file diff --git a/src/test/java/guitests/EditPriorityCommandTest.java b/src/test/java/guitests/EditPriorityCommandTest.java new file mode 100644 index 000000000000..347c653d529e --- /dev/null +++ b/src/test/java/guitests/EditPriorityCommandTest.java @@ -0,0 +1,73 @@ +//@@author A0142073R +package guitests; + +import org.junit.Test; + +import seedu.taskell.commons.exceptions.IllegalValueException; +import seedu.taskell.testutil.TestTask; +import seedu.taskell.testutil.TestUtil; + +import static org.junit.Assert.assertTrue; +import static seedu.taskell.logic.commands.EditPriorityCommand.MESSAGE_EDIT_TASK_SUCCESS; + +public class EditPriorityCommandTest extends TaskManagerGuiTest { + + @Test + public void edit() throws IllegalValueException { + + // edit the first in the list + TestTask[] currentList = td.getTypicalTasks(); + int targetIndex = 1; + String newPriority = "3"; + asserteditSuccess(targetIndex, currentList, newPriority); + + // edit the last in the list + targetIndex = currentList.length; + newPriority = "2"; + asserteditSuccess(targetIndex, currentList, newPriority); + + // invalid index + commandBox.runCommand("edit-priority " + currentList.length + 1 + " 2"); + assertResultMessage("The task index provided is invalid"); + + // invalid command + commandBox.runCommand("edit-priority "); + assertResultMessage("Invalid command format! \n" + "edit-priority" + + ": Edits the priority of a task identified by the index number used in the last task listing.\n" + + "Parameters: INDEX (must be a positive integer) NEW_PRIORITY(must be between 0 to 3)\n" + "Example: " + "edit-priority" + " 1 3 "); + + } + + /** + * Runs the edit command to edit the priority of a task at specified + * index and confirms the result is correct. + * + * @param targetIndexOneIndexed + * e.g. to edit the priority of 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). + * @throws IllegalValueException + */ + private void asserteditSuccess(int targetIndexOneIndexed, final TestTask[] currentList, String newPriority) + throws IllegalValueException { + TestTask taskToedit = currentList[targetIndexOneIndexed - 1]; // -1 + // because + // array + // uses + // zero + // indexing + TestTask newTask = TestUtil.editTaskPriority(taskToedit, newPriority); + + commandBox.runCommand("edit-priority " + targetIndexOneIndexed + " " + newPriority); + TestTask[] current = TestUtil.replaceTaskFromList(currentList, newTask, targetIndexOneIndexed - 1); + // confirm the list now contains all previous tasks except the edited + // task + assertTrue(taskListPanel.isListMatching(current)); + + // confirm the result message is correct + assertResultMessage(String.format(MESSAGE_EDIT_TASK_SUCCESS, taskToedit, newTask)); + } + +} +//@@author diff --git a/src/test/java/guitests/EditStartTimeCommandTest.java b/src/test/java/guitests/EditStartTimeCommandTest.java new file mode 100644 index 000000000000..7fa87d34dc0c --- /dev/null +++ b/src/test/java/guitests/EditStartTimeCommandTest.java @@ -0,0 +1,75 @@ +//@@author A0142073R +package guitests; + +import org.junit.Test; + +import seedu.taskell.commons.exceptions.IllegalValueException; +import seedu.taskell.testutil.TestTask; +import seedu.taskell.testutil.TestUtil; + +import static org.junit.Assert.assertTrue; +import static seedu.taskell.logic.commands.EditStartTimeCommand.MESSAGE_EDIT_TASK_SUCCESS; + +public class EditStartTimeCommandTest extends TaskManagerGuiTest { + + @Test + public void edit() throws IllegalValueException { + + // edit the first in the list + TestTask[] currentList = td.getTypicalTasks(); + int targetIndex = 1; + String newTime = "5am"; + asserteditSuccess(targetIndex, currentList, newTime); + + // edit the last in the list + targetIndex = currentList.length; + newTime = "12.35pm"; + asserteditSuccess(targetIndex, currentList, newTime); + + // invalid index + commandBox.runCommand("edit-startTime " + currentList.length + 1 + " 1am"); + assertResultMessage("The task index provided is invalid"); + + //invalid command format + commandBox.runCommand("edit-startTime 3 to 4pm "); + assertResultMessage("Invalid command format! \n" + + "edit-startTime" + + ": Edits the start time of a task identified by the index number used in the last task listing.\n" + + "Parameters: INDEX (must be a positive integer) NEW_START_TIME\n" + + "Example: edit-startTime 1 2pm "); + + } + + /** + * Runs the edit command to edit the start time of a task at specified + * index and confirms the result is correct. + * + * @param targetIndexOneIndexed + * e.g. to edit the start time of 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). + * @throws IllegalValueException + */ + private void asserteditSuccess(int targetIndexOneIndexed, final TestTask[] currentList, String newTime) + throws IllegalValueException { + TestTask taskToedit = currentList[targetIndexOneIndexed - 1]; // -1 + // because + // array + // uses + // zero + // indexing + TestTask newTask = TestUtil.editTaskStartTime(taskToedit, newTime); + + commandBox.runCommand("edit-startTime " + targetIndexOneIndexed + " " + newTime); + TestTask[] current = TestUtil.replaceTaskFromList(currentList, newTask, targetIndexOneIndexed - 1); + // confirm the list now contains all previous tasks except the edited + // task + assertTrue(taskListPanel.isListMatching(current)); + + // confirm the result message is correct + assertResultMessage(String.format(MESSAGE_EDIT_TASK_SUCCESS, taskToedit, newTask)); + } + +} +//@@author \ No newline at end of file diff --git a/src/test/java/guitests/FindCommandTest.java b/src/test/java/guitests/FindCommandTest.java index 441a6dbed666..d39cb01f7e44 100644 --- a/src/test/java/guitests/FindCommandTest.java +++ b/src/test/java/guitests/FindCommandTest.java @@ -1,22 +1,42 @@ package guitests; import org.junit.Test; -import seedu.address.commons.core.Messages; -import seedu.address.testutil.TestPerson; + +import seedu.taskell.commons.core.Messages; +import seedu.taskell.testutil.TestTask; import static org.junit.Assert.assertTrue; -public class FindCommandTest extends AddressBookGuiTest { +public class FindCommandTest extends TaskManagerGuiTest { @Test public void find_nonEmptyList() { assertFindResult("find Mark"); //no results - assertFindResult("find Meier", td.benson, td.daniel); //multiple results + assertFindResult("find Books", td.borrowBooks, td.discardBooks); //multiple results + + //find after deleting one result + commandBox.runCommand("delete 1"); + assertFindResult("find Books",td.discardBooks); + } + + /** @@author A0142130A **/ + + @Test + public void find_nonEmptyList_byTag() { + assertFindResult("find chicken"); //no results + assertFindResult("find friends", td.archivePastEmails, td.borrowBooks); //multiple results //find after deleting one result commandBox.runCommand("delete 1"); - assertFindResult("find Meier",td.daniel); + assertFindResult("find friends", td.borrowBooks); + } + + @Test + public void find_byMoreThanOneTags() { + assertFindResult("find friends owesMoney", td.borrowBooks); } + + /** @@author **/ @Test public void find_emptyList(){ @@ -30,10 +50,10 @@ public void find_invalidCommand_fail() { assertResultMessage(Messages.MESSAGE_UNKNOWN_COMMAND); } - private void assertFindResult(String command, TestPerson... expectedHits ) { + private void assertFindResult(String command, TestTask... expectedHits ) { commandBox.runCommand(command); 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/FindTagCommandTest.java b/src/test/java/guitests/FindTagCommandTest.java new file mode 100644 index 000000000000..de1177fe630a --- /dev/null +++ b/src/test/java/guitests/FindTagCommandTest.java @@ -0,0 +1,45 @@ +/** @@author A0142130A **/ +package guitests; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import seedu.taskell.commons.core.Messages; +import seedu.taskell.testutil.TestTask; + +public class FindTagCommandTest extends TaskManagerGuiTest { + @Test + public void findTag_nonEmptyList() { + assertFindResult("find-tag chicken"); //no results + assertFindResult("find-tag friends", td.archivePastEmails, td.borrowBooks); //multiple results + + //find after deleting one result + commandBox.runCommand("delete 1"); + assertFindResult("find-tag friends",td.borrowBooks); + } + + @Test + public void findTag_MulitpleKeywords() { + assertFindResult("find-tag friends owesMoney", td.archivePastEmails, td.borrowBooks, td.collectParcel); //multiple results + } + + @Test + public void findTag_emptyList(){ + commandBox.runCommand("clear"); + assertFindResult("find-tag friends"); //no results + } + + @Test + public void findTag_invalidCommand_fail() { + commandBox.runCommand("find-tagfriends"); + assertResultMessage(Messages.MESSAGE_UNKNOWN_COMMAND); + } + + private void assertFindResult(String command, TestTask... expectedHits ) { + commandBox.runCommand(command); + assertListSize(expectedHits.length); + assertResultMessage(expectedHits.length + " tasks listed!"); + assertTrue(taskListPanel.isListMatching(expectedHits)); + } +} diff --git a/src/test/java/guitests/GuiRobot.java b/src/test/java/guitests/GuiRobot.java index 44aa9edb48aa..55ba7f4194e6 100644 --- a/src/test/java/guitests/GuiRobot.java +++ b/src/test/java/guitests/GuiRobot.java @@ -1,8 +1,9 @@ package guitests; import javafx.scene.input.KeyCodeCombination; +import seedu.taskell.testutil.TestUtil; + import org.testfx.api.FxRobot; -import seedu.address.testutil.TestUtil; /** * Robot used to simulate user actions on the GUI. diff --git a/src/test/java/guitests/HelpWindowTest.java b/src/test/java/guitests/HelpWindowTest.java index 258d9d628d80..cc36594f111b 100644 --- a/src/test/java/guitests/HelpWindowTest.java +++ b/src/test/java/guitests/HelpWindowTest.java @@ -5,12 +5,12 @@ import static org.junit.Assert.assertTrue; -public class HelpWindowTest extends AddressBookGuiTest { +public class HelpWindowTest extends TaskManagerGuiTest { @Test public void openHelpWindow() { - personListPanel.clickOnListView(); + taskListPanel.clickOnListView(); assertHelpWindowOpen(mainMenu.openHelpWindowUsingAccelerator()); diff --git a/src/test/java/guitests/ListDateCommandTest.java b/src/test/java/guitests/ListDateCommandTest.java new file mode 100644 index 000000000000..5fd77f0a7063 --- /dev/null +++ b/src/test/java/guitests/ListDateCommandTest.java @@ -0,0 +1,59 @@ +//@@author A0142073R +package guitests; + +import org.junit.Test; + +import seedu.taskell.commons.exceptions.IllegalValueException; +import seedu.taskell.testutil.TestTask; + +import static org.junit.Assert.assertTrue; + +public class ListDateCommandTest extends TaskManagerGuiTest { + + @Test + public void listDate() throws IllegalValueException { + + // list 2 tasks in the list + TestTask[] currentList = td.getTypicalTasks(); + String date = "1-1-2100"; + assertlistDateSuccess(currentList, date, td.archivePastEmails, td.borrowBooks, td.collectParcel, + td.discardBooks, td.editPowerpoint, td.fixbugs, td.getNewUniform); + + // no tasks to list + date = "1-1-2016"; + assertlistDateSuccess(currentList, date); + + // invalid command format + commandBox.runCommand("list-date"); + assertResultMessage("Invalid command format! \n" + "list-date" + ": Lists tasks on a specific date.\n" + + "Parameters: DATE (must be a positive integer)\n" + "Example: " + "list-date" + " 8-8-2016 "); + + } + + /** + * Runs the list date command to list tasks on a specified date and confirms + * the result is correct. + * + * @param date + * e.g. to list tasks on date "1-1-2016", "1-1-2016" should be + * given as the date. + * @param currentList + * A copy of the current list of tasks (before listing). + * @param + * @throws IllegalValueException + */ + private void assertlistDateSuccess(final TestTask[] currentList, String date, TestTask... values) + throws IllegalValueException { + + commandBox.runCommand("list-date " + date); + + assertListSize(values.length); + // confirm the result message is correct + assertResultMessage(values.length + " tasks listed!"); + // confirm the list now contains all previous tasks except the edited + // task + assertTrue(taskListPanel.isListMatching(values)); + } + +} +//@@author \ No newline at end of file diff --git a/src/test/java/guitests/ListPriorityCommandTest.java b/src/test/java/guitests/ListPriorityCommandTest.java new file mode 100644 index 000000000000..22645cb0aecf --- /dev/null +++ b/src/test/java/guitests/ListPriorityCommandTest.java @@ -0,0 +1,60 @@ +//@@author A0142073R +package guitests; + +import org.junit.Test; + +import seedu.taskell.commons.exceptions.IllegalValueException; +import seedu.taskell.model.task.TaskPriority; +import seedu.taskell.testutil.TestTask; + +import static org.junit.Assert.assertTrue; + +public class ListPriorityCommandTest extends TaskManagerGuiTest { + + @Test + public void listPriority() throws IllegalValueException { + + // list 2 tasks in the list + TestTask[] currentList = td.getTypicalTasks(); + String priority = TaskPriority.LOW_PRIORITY; + assertlistPrioritySuccess(currentList, priority, td.archivePastEmails, td.borrowBooks, td.collectParcel, td.discardBooks,td.editPowerpoint, td.fixbugs, td.getNewUniform); + + //no tasks to list + priority = TaskPriority.NO_PRIORITY; + assertlistPrioritySuccess(currentList, priority); + + // invalid command format + commandBox.runCommand("list-priority"); + assertResultMessage("Invalid command format! \n" + "list-priority" + ": List the task with the specified priority. " + + "Parameters: INDEX (must be between 0 and 3 inclusive).\n" + + "Example: list-priority 1"); + + } + + /** + * Runs the list priority command to list tasks with a specified + * priority and confirms the result is correct. + * + * @param priority + * e.g. to list tasks with priority 1, "1" + * should be given as the date. + * @param currentList + * A copy of the current list of tasks (before prioritising). + * @param + * @throws IllegalValueException + */ + private void assertlistPrioritySuccess(final TestTask[] currentList, String priority, TestTask... values) + throws IllegalValueException { + + commandBox.runCommand("list-priority " + priority); + + assertListSize(values.length); + // confirm the result message is correct + assertResultMessage(values.length + " tasks listed!"); + // confirm the list now contains all previous tasks except the edited + // task + assertTrue(taskListPanel.isListMatching(values)); + } + +} +// @@author \ No newline at end of file diff --git a/src/test/java/guitests/SaveStorageLocationCommandTest.java b/src/test/java/guitests/SaveStorageLocationCommandTest.java new file mode 100644 index 000000000000..56aa2ced0897 --- /dev/null +++ b/src/test/java/guitests/SaveStorageLocationCommandTest.java @@ -0,0 +1,89 @@ +/** @@author A0142130A **/ +package guitests; + +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.util.List; +import java.util.Optional; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.junit.Test; + +import seedu.taskell.TestApp; +import seedu.taskell.commons.core.Config; +import seedu.taskell.commons.core.LogsCenter; +import seedu.taskell.commons.exceptions.DataConversionException; +import seedu.taskell.logic.commands.SaveStorageLocationCommand; +import seedu.taskell.model.ReadOnlyTaskManager; +import seedu.taskell.model.TaskManager; +import seedu.taskell.model.task.UniqueTaskList.DuplicateTaskException; +import seedu.taskell.storage.JsonConfigStorage; +import seedu.taskell.testutil.TestTask; +import seedu.taskell.testutil.TestUtil; + +public class SaveStorageLocationCommandTest extends TaskManagerGuiTest { + + private static final Logger logger = LogsCenter.getLogger(SaveStorageLocationCommandTest.class); + + private static final String CONFIG_JSON = "config.json"; + private static final String CONFIG_LOCATION = "./src/test/data/SaveLocationCommandTest"; + + private static final String DEFAULT_SAVE_LOCATION = TestApp.SAVE_LOCATION_FOR_TESTING; + + @Test + public void saveToValidFilePath() throws DataConversionException, IOException, DuplicateTaskException { + String testFilePath = "./src/test/data/SaveLocationCommandTest/newStorageLocation/"; + commandBox.runCommand("save " + testFilePath); + assertWriteToJsonSuccess(); + assertWriteToXmlSuccess(); + } + + /** @@author A0142130A-unused **/ + //This test is not run because it has assertion error on Travis build + //@Test + public void saveToInvalidFilePath() throws DataConversionException { + JsonConfigStorage jsonConfigStorage = new JsonConfigStorage(CONFIG_LOCATION); + + commandBox.runCommand("save E:"); + + Optional newConfig = jsonConfigStorage.readConfig(CONFIG_JSON); + String newFilePath = newConfig.get().getTaskManagerFilePath(); + logger.info("New path: " + newFilePath); + + assert(newFilePath.equals(DEFAULT_SAVE_LOCATION)); + } + + /** @@author A0142130A **/ + + /** NOTE: because of the way SaveStorageLocationCommand works, after running this command + * config.json in Taskell saves the test data so this method is necessary to reset + * config.json to default data + * */ + @Test + public void resetConfigFile() throws IOException { + Config config = new Config(); + config.setAppTitle("Taskell"); + config.setLogLevel(Level.INFO); + config.setUserPrefsFilePath("preferences.json"); + config.setTaskManagerFilePath("data/taskmanager.xml"); + config.setTaskManagerName("MyTaskManager"); + SaveStorageLocationCommand.setConfig(config); + + JsonConfigStorage jsonConfigStorage = new JsonConfigStorage(CONFIG_JSON); + jsonConfigStorage.saveConfigFile(config); + } + + private void assertWriteToJsonSuccess() throws DataConversionException { + JsonConfigStorage jsonConfigStorage = new JsonConfigStorage(CONFIG_LOCATION); + Optional config = jsonConfigStorage.readConfig(CONFIG_JSON); + assert(config.isPresent()); + } + + private void assertWriteToXmlSuccess() { + TestTask[] currentList = td.getTypicalTasks(); + assertTrue(taskListPanel.isListMatching(currentList)); + } + +} diff --git a/src/test/java/guitests/SelectCommandTest.java b/src/test/java/guitests/SelectCommandTest.java index 5273552056ce..5aa1be515d2e 100644 --- a/src/test/java/guitests/SelectCommandTest.java +++ b/src/test/java/guitests/SelectCommandTest.java @@ -1,33 +1,34 @@ package guitests; import org.junit.Test; -import seedu.address.model.person.ReadOnlyPerson; + +import seedu.taskell.model.task.ReadOnlyTask; import static org.junit.Assert.assertEquals; -public class SelectCommandTest extends AddressBookGuiTest { +public class SelectCommandTest extends TaskManagerGuiTest { @Test - public void selectPerson_nonEmptyList() { + public void selectTask_nonEmptyList() { assertSelectionInvalid(10); //invalid index - assertNoPersonSelected(); + assertNoTaskSelected(); - 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 + assertSelectionSuccess(1); //first task in the list + int taskCount = td.getTypicalTasks().length; + assertSelectionSuccess(taskCount); //last task in the list + int middleIndex = taskCount / 2; + assertSelectionSuccess(middleIndex); //a task in the middle of the list - assertSelectionInvalid(personCount + 1); //invalid index - assertPersonSelected(middleIndex); //assert previous selection remains + assertSelectionInvalid(taskCount + 1); //invalid index + assertTaskSelected(middleIndex); //assert previous selection remains /* Testing other invalid indexes such as -1 should be done when testing the SelectCommand */ } @Test - public void selectPerson_emptyList(){ + public void selectTask_emptyList(){ commandBox.runCommand("clear"); assertListSize(0); assertSelectionInvalid(1); //invalid index @@ -35,24 +36,24 @@ public void selectPerson_emptyList(){ private void assertSelectionInvalid(int index) { commandBox.runCommand("select " + index); - assertResultMessage("The person index provided is invalid"); + assertResultMessage("The task index provided is invalid"); } private void assertSelectionSuccess(int index) { commandBox.runCommand("select " + index); - assertResultMessage("Selected Person: "+index); - assertPersonSelected(index); + assertResultMessage("Selected Task: "+index); + assertTaskSelected(index); } - private void assertPersonSelected(int index) { - assertEquals(personListPanel.getSelectedPersons().size(), 1); - ReadOnlyPerson selectedPerson = personListPanel.getSelectedPersons().get(0); - assertEquals(personListPanel.getPerson(index-1), selectedPerson); + private void assertTaskSelected(int index) { + assertEquals(taskListPanel.getSelectedTasks().size(), 1); + ReadOnlyTask selectedTask = taskListPanel.getSelectedTasks().get(0); + assertEquals(taskListPanel.getTask(index-1), selectedTask); //TODO: confirm the correct page is loaded in the Browser Panel } - private void assertNoPersonSelected() { - assertEquals(personListPanel.getSelectedPersons().size(), 0); + private void assertNoTaskSelected() { + assertEquals(taskListPanel.getSelectedTasks().size(), 0); } } diff --git a/src/test/java/guitests/AddressBookGuiTest.java b/src/test/java/guitests/TaskManagerGuiTest.java similarity index 71% rename from src/test/java/guitests/AddressBookGuiTest.java rename to src/test/java/guitests/TaskManagerGuiTest.java index 35734932f11c..2d560526942d 100644 --- a/src/test/java/guitests/AddressBookGuiTest.java +++ b/src/test/java/guitests/TaskManagerGuiTest.java @@ -8,12 +8,13 @@ import org.junit.Rule; import org.junit.rules.TestName; import org.testfx.api.FxToolkit; -import seedu.address.TestApp; -import seedu.address.commons.core.EventsCenter; -import seedu.address.model.AddressBook; -import seedu.address.model.person.ReadOnlyPerson; -import seedu.address.testutil.TestUtil; -import seedu.address.testutil.TypicalTestPersons; + +import seedu.taskell.TestApp; +import seedu.taskell.commons.core.EventsCenter; +import seedu.taskell.model.TaskManager; +import seedu.taskell.model.task.ReadOnlyTask; +import seedu.taskell.testutil.TestUtil; +import seedu.taskell.testutil.TypicalTestTasks; import java.util.concurrent.TimeoutException; @@ -21,9 +22,9 @@ import static org.junit.Assert.assertTrue; /** - * A GUI Test class for AddressBook. + * A GUI Test class for TaskManager. */ -public abstract class AddressBookGuiTest { +public abstract class TaskManagerGuiTest { /* The TestName Rule makes the current test name available inside test methods */ @Rule @@ -31,7 +32,7 @@ public abstract class AddressBookGuiTest { TestApp testApp; - protected TypicalTestPersons td = new TypicalTestPersons(); + protected TypicalTestTasks td = new TypicalTestTasks(); /* * Handles to GUI elements present at the start up are created in advance @@ -39,7 +40,7 @@ public abstract class AddressBookGuiTest { */ protected MainGuiHandle mainGui; protected MainMenuHandle mainMenu; - protected PersonListPanelHandle personListPanel; + protected TaskListPanelHandle taskListPanel; protected ResultDisplayHandle resultDisplay; protected CommandBoxHandle commandBox; private Stage stage; @@ -59,7 +60,7 @@ public void setup() throws Exception { FxToolkit.setupStage((stage) -> { mainGui = new MainGuiHandle(new GuiRobot(), stage); mainMenu = mainGui.getMainMenu(); - personListPanel = mainGui.getPersonListPanel(); + taskListPanel = mainGui.getTaskListPanel(); resultDisplay = mainGui.getResultDisplay(); commandBox = mainGui.getCommandBox(); this.stage = stage; @@ -75,9 +76,9 @@ public void setup() throws Exception { * Override this in child classes to set the initial local data. * Return null to use the data in the file specified in {@link #getDataFileLocation()} */ - protected AddressBook getInitialData() { - AddressBook ab = TestUtil.generateEmptyAddressBook(); - TypicalTestPersons.loadAddressBookWithSampleData(ab); + protected TaskManager getInitialData() { + TaskManager ab = TestUtil.generateEmptyTaskManager(); + TypicalTestTasks.loadTaskManagerWithSampleData(ab); return ab; } @@ -94,17 +95,17 @@ 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(); + int numberOfPeople = taskListPanel.getNumberOfPeople(); assertEquals(size, numberOfPeople); } diff --git a/src/test/java/guitests/UndoCommandTest.java b/src/test/java/guitests/UndoCommandTest.java new file mode 100644 index 000000000000..3a46d29ad551 --- /dev/null +++ b/src/test/java/guitests/UndoCommandTest.java @@ -0,0 +1,120 @@ +/** @@author A0142130A **/ +package guitests; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import guitests.guihandles.TaskCardHandle; +import seedu.taskell.logic.commands.UndoCommand; +import seedu.taskell.testutil.TestTask; +import seedu.taskell.testutil.TestUtil; + +public class UndoCommandTest extends TaskManagerGuiTest { + + @Test + public void undoAdd() { + UndoCommand.clearCommandHistory(); + + TestTask[] currentList = td.getTypicalTasks(); + TestTask taskToAdd = td.holdMeeting; + + //add one task + commandBox.runCommand(taskToAdd.getAddCommand()); + assertAddSuccess(taskToAdd, currentList); + currentList = TestUtil.addTasksToList(currentList, taskToAdd); + + commandBox.runCommand("undo 1"); + assertDeleteSuccess(currentList.length, currentList); + + UndoCommand.clearCommandHistory(); + } + + @Test + public void undoDelete() { + UndoCommand.clearCommandHistory(); + + TestTask[] currentList = td.getTypicalTasks(); + TestTask taskToDelete = currentList[0]; + int target = 1; + + //delete a task + commandBox.runCommand("delete " + target); + assertDeleteSuccess(target, currentList); + currentList = TestUtil.removeTaskFromList(currentList, 1); + + commandBox.runCommand("undo 1"); + assertAddSuccess(taskToDelete, currentList); + + UndoCommand.clearCommandHistory(); + } + + @Test + public void undoAndRedoAdd() { + UndoCommand.clearCommandHistory(); + + TestTask[] currentList = td.getTypicalTasks(); + TestTask taskToAdd = td.holdMeeting; + + //add one task + commandBox.runCommand(taskToAdd.getAddCommand()); + assertAddSuccess(taskToAdd, currentList); + currentList = TestUtil.addTasksToList(currentList, taskToAdd); + + commandBox.runCommand("undo 1"); + assertDeleteSuccess(currentList.length, currentList); + currentList = TestUtil.removeTaskFromList(currentList, currentList.length); + + commandBox.runCommand("undo 1"); + assertAddSuccess(taskToAdd, currentList); + + UndoCommand.clearCommandHistory(); + } + + @Test + public void undoAndRedoDelete() { + UndoCommand.clearCommandHistory(); + + TestTask[] currentList = td.getTypicalTasks(); + TestTask taskToDelete = currentList[0]; + int target = 1; + + //delete a task + commandBox.runCommand("delete " + target); + assertDeleteSuccess(target, currentList); + currentList = TestUtil.removeTaskFromList(currentList, 1); + + commandBox.runCommand("undo 1"); + assertAddSuccess(taskToDelete, currentList); + currentList = TestUtil.addTasksToList(currentList, taskToDelete); + + target = currentList.length; + commandBox.runCommand("undo 1"); + assertDeleteSuccess(target, currentList); + + UndoCommand.clearCommandHistory(); + } + + private void assertAddSuccess(TestTask taskToAdd, TestTask... currentList) { + //confirm the new card contains the right data + TaskCardHandle addedCard = taskListPanel.navigateToTask(taskToAdd.getDescription().description); + 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)); + } + + /** + * Confirms the undo add result is correct. Means task has been deleted. + * @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 TestTask[] currentList) { + TestTask taskToDelete = currentList[targetIndexOneIndexed-1]; //-1 because array uses zero indexing + TestTask[] expectedRemainder = TestUtil.removeTaskFromList(currentList, targetIndexOneIndexed); + + //confirm the list now contains all previous tasks except the deleted task + assertTrue(taskListPanel.isListMatching(expectedRemainder)); + } +} diff --git a/src/test/java/guitests/ViewCalendarCommandTest.java b/src/test/java/guitests/ViewCalendarCommandTest.java new file mode 100644 index 000000000000..d7e5b32ecc97 --- /dev/null +++ b/src/test/java/guitests/ViewCalendarCommandTest.java @@ -0,0 +1,18 @@ +/** @@author A0142130A **/ +package guitests; + +import org.junit.Test; + +public class ViewCalendarCommandTest extends TaskManagerGuiTest { + + @Test + public void viewCal() { + String expected = "Displayed calendar."; + + commandBox.runCommand("calendar"); + assertResultMessage(expected); + + commandBox.runCommand("cal"); + assertResultMessage(expected); + } +} diff --git a/src/test/java/guitests/guihandles/GuiHandle.java b/src/test/java/guitests/guihandles/GuiHandle.java index 5e7e0f6de911..fccb0ceb7fde 100644 --- a/src/test/java/guitests/guihandles/GuiHandle.java +++ b/src/test/java/guitests/guihandles/GuiHandle.java @@ -7,8 +7,8 @@ import javafx.scene.input.KeyCode; import javafx.stage.Stage; import javafx.stage.Window; -import seedu.address.TestApp; -import seedu.address.commons.core.LogsCenter; +import seedu.taskell.TestApp; +import seedu.taskell.commons.core.LogsCenter; import java.util.logging.Logger; diff --git a/src/test/java/guitests/guihandles/MainGuiHandle.java b/src/test/java/guitests/guihandles/MainGuiHandle.java index 45802c5135c7..4b809ec5b785 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.taskell.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..d2395c39e06a 100644 --- a/src/test/java/guitests/guihandles/MainMenuHandle.java +++ b/src/test/java/guitests/guihandles/MainMenuHandle.java @@ -3,7 +3,7 @@ import guitests.GuiRobot; import javafx.scene.input.KeyCode; import javafx.stage.Stage; -import seedu.address.TestApp; +import seedu.taskell.TestApp; import java.util.Arrays; diff --git a/src/test/java/guitests/guihandles/PersonCardHandle.java b/src/test/java/guitests/guihandles/PersonCardHandle.java deleted file mode 100644 index fae22a45ae2f..000000000000 --- a/src/test/java/guitests/guihandles/PersonCardHandle.java +++ /dev/null @@ -1,63 +0,0 @@ -package guitests.guihandles; - -import guitests.GuiRobot; -import javafx.scene.Node; -import javafx.stage.Stage; -import seedu.address.model.person.ReadOnlyPerson; - -/** - * Provides a handle to a person card in the person list panel. - */ -public class PersonCardHandle extends GuiHandle { - private static final String NAME_FIELD_ID = "#name"; - private static final String ADDRESS_FIELD_ID = "#address"; - private static final String PHONE_FIELD_ID = "#phone"; - private static final String EMAIL_FIELD_ID = "#email"; - - private Node node; - - public PersonCardHandle(GuiRobot guiRobot, Stage primaryStage, Node node){ - super(guiRobot, primaryStage, null); - this.node = node; - } - - protected String getTextFromLabel(String fieldId) { - return getTextFromLabel(fieldId, node); - } - - public String getFullName() { - return getTextFromLabel(NAME_FIELD_ID); - } - - public String getAddress() { - return getTextFromLabel(ADDRESS_FIELD_ID); - } - - public String getPhone() { - return getTextFromLabel(PHONE_FIELD_ID); - } - - public String getEmail() { - return getTextFromLabel(EMAIL_FIELD_ID); - } - - public boolean isSamePerson(ReadOnlyPerson person){ - return getFullName().equals(person.getName().fullName) && getPhone().equals(person.getPhone().value) - && getEmail().equals(person.getEmail().value) && getAddress().equals(person.getAddress().value); - } - - @Override - public boolean equals(Object obj) { - if(obj instanceof PersonCardHandle) { - PersonCardHandle handle = (PersonCardHandle) obj; - return getFullName().equals(handle.getFullName()) - && getAddress().equals(handle.getAddress()); //TODO: compare the rest - } - return super.equals(obj); - } - - @Override - public String toString() { - return getFullName() + " " + getAddress(); - } -} diff --git a/src/test/java/guitests/guihandles/PersonListPanelHandle.java b/src/test/java/guitests/guihandles/PersonListPanelHandle.java deleted file mode 100644 index 3451992cf735..000000000000 --- a/src/test/java/guitests/guihandles/PersonListPanelHandle.java +++ /dev/null @@ -1,172 +0,0 @@ -package guitests.guihandles; - - -import guitests.GuiRobot; -import javafx.geometry.Point2D; -import javafx.scene.Node; -import javafx.scene.control.ListView; -import javafx.stage.Stage; -import seedu.address.TestApp; -import seedu.address.model.person.Person; -import seedu.address.model.person.ReadOnlyPerson; -import seedu.address.testutil.TestUtil; - -import java.util.List; -import java.util.Optional; -import java.util.Set; - -import static org.junit.Assert.assertTrue; - -/** - * Provides a handle for the panel containing the person list. - */ -public class PersonListPanelHandle extends GuiHandle { - - public static final int NOT_FOUND = -1; - public static final String CARD_PANE_ID = "#cardPane"; - - private static final String PERSON_LIST_VIEW_ID = "#personListView"; - - public PersonListPanelHandle(GuiRobot guiRobot, Stage primaryStage) { - super(guiRobot, primaryStage, TestApp.APP_TITLE); - } - - public List getSelectedPersons() { - ListView personList = getListView(); - return personList.getSelectionModel().getSelectedItems(); - } - - public ListView getListView() { - return (ListView) getNode(PERSON_LIST_VIEW_ID); - } - - /** - * Returns true if the list is showing the person details correctly and in correct order. - * @param persons A list of person in the correct order. - */ - public boolean isListMatching(ReadOnlyPerson... persons) { - return this.isListMatching(0, persons); - } - - /** - * Clicks on the ListView. - */ - public void clickOnListView() { - Point2D point= TestUtil.getScreenMidPoint(getListView()); - guiRobot.clickOn(point.getX(), point.getY()); - } - - /** - * Returns true if the {@code persons} appear as the sub list (in that order) at position {@code startPosition}. - */ - public boolean containsInOrder(int startPosition, ReadOnlyPerson... persons) { - List personsInList = getListView().getItems(); - - // Return false if the list in panel is too short to contain the given list - if (startPosition + persons.length > personsInList.size()){ - return false; - } - - // Return false if any of the persons doesn't match - for (int i = 0; i < persons.length; i++) { - if (!personsInList.get(startPosition + i).getName().fullName.equals(persons[i].getName().fullName)){ - return false; - } - } - - return true; - } - - /** - * Returns true if the list is showing the person details correctly and in correct order. - * @param startPosition The starting position of the sub list. - * @param persons A list of person in the correct order. - */ - public boolean isListMatching(int startPosition, ReadOnlyPerson... persons) throws IllegalArgumentException { - if (persons.length + startPosition != getListView().getItems().size()) { - throw new IllegalArgumentException("List size mismatched\n" + - "Expected " + (getListView().getItems().size() - 1) + " persons"); - } - assertTrue(this.containsInOrder(startPosition, persons)); - for (int i = 0; i < persons.length; i++) { - final int scrollTo = i + startPosition; - guiRobot.interact(() -> getListView().scrollTo(scrollTo)); - guiRobot.sleep(200); - if (!TestUtil.compareCardAndPerson(getPersonCardHandle(startPosition + i), persons[i])) { - return false; - } - } - return true; - } - - - public PersonCardHandle navigateToPerson(String name) { - guiRobot.sleep(500); //Allow a bit of time for the list to be updated - final Optional person = getListView().getItems().stream().filter(p -> p.getName().fullName.equals(name)).findAny(); - if (!person.isPresent()) { - throw new IllegalStateException("Name not found: " + name); - } - - return navigateToPerson(person.get()); - } - - /** - * Navigates the listview to display and select the person. - */ - public PersonCardHandle navigateToPerson(ReadOnlyPerson person) { - int index = getPersonIndex(person); - - guiRobot.interact(() -> { - getListView().scrollTo(index); - guiRobot.sleep(150); - getListView().getSelectionModel().select(index); - }); - guiRobot.sleep(100); - return getPersonCardHandle(person); - } - - - /** - * Returns the position of the person given, {@code NOT_FOUND} if not found in the list. - */ - public int getPersonIndex(ReadOnlyPerson targetPerson) { - List personsInList = getListView().getItems(); - for (int i = 0; i < personsInList.size(); i++) { - if(personsInList.get(i).getName().equals(targetPerson.getName())){ - return i; - } - } - return NOT_FOUND; - } - - /** - * Gets a person from the list by index - */ - public ReadOnlyPerson getPerson(int index) { - return getListView().getItems().get(index); - } - - public PersonCardHandle getPersonCardHandle(int index) { - return getPersonCardHandle(new Person(getListView().getItems().get(index))); - } - - public PersonCardHandle getPersonCardHandle(ReadOnlyPerson person) { - Set nodes = getAllCardNodes(); - Optional personCardNode = nodes.stream() - .filter(n -> new PersonCardHandle(guiRobot, primaryStage, n).isSamePerson(person)) - .findFirst(); - if (personCardNode.isPresent()) { - return new PersonCardHandle(guiRobot, primaryStage, personCardNode.get()); - } else { - return null; - } - } - - protected Set getAllCardNodes() { - return guiRobot.lookup(CARD_PANE_ID).queryAll(); - } - - public int getNumberOfPeople() { - return getListView().getItems().size(); - } -} diff --git a/src/test/java/guitests/guihandles/ResultDisplayHandle.java b/src/test/java/guitests/guihandles/ResultDisplayHandle.java index 110b4682b184..0695f3743a66 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.taskell.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..1210836ba88b --- /dev/null +++ b/src/test/java/guitests/guihandles/TaskCardHandle.java @@ -0,0 +1,78 @@ +package guitests.guihandles; + +import guitests.GuiRobot; +import javafx.scene.Node; +import javafx.stage.Stage; +import seedu.taskell.model.task.ReadOnlyTask; + +public class TaskCardHandle extends GuiHandle { + private static final String DESCRIPTION_FIELD_ID = "#description"; + +// private static final String TASK_PRIORITY_FIELD_ID = "#taskPriority"; + private static final String START_DATE_FIELD_ID = "#startDate"; + private static final String END_DATE_FIELD_ID = "#endDate"; + private static final String TASK_START_TIME_FIELD_ID = "#startTime"; + private static final String TASK_END_TIME_FIELD_ID = "#endTime"; + private static final String TASK_COMPLETE_FIELD_ID = "#taskStatus"; + 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 getDescription() { + return getTextFromLabel(DESCRIPTION_FIELD_ID); + } + +// public String getTaskPriority() { +// return getTextFromLabel(TASK_PRIORITY_FIELD_ID); +// } + + public String getStartDate() { + return getTextFromLabel(START_DATE_FIELD_ID); + } + + public String getEndDate() { + return getTextFromLabel(END_DATE_FIELD_ID); + } + + public String getStartTime() { + return getTextFromLabel(TASK_START_TIME_FIELD_ID); + } + + public String getEndTime() { + return getTextFromLabel(TASK_END_TIME_FIELD_ID); + } + + public String getTaskComplete() { + return getTextFromLabel(TASK_COMPLETE_FIELD_ID); + } + + public boolean isSameTask(ReadOnlyTask task){ + return getDescription().equals(task.getDescription().description) + && getStartDate().equals(task.getStartDate().getDisplayDate()) + && getEndDate().equals(task.getEndDate().getDisplayDate()) + && getStartTime().equals(task.getStartTime().taskTime) + && getEndTime().equals(task.getEndTime().taskTime) + && getTaskComplete().equals(task.getTaskStatus().taskStatus); + } + + @Override + public boolean equals(Object obj) { + if(obj instanceof TaskCardHandle) { + TaskCardHandle handle = (TaskCardHandle) obj; + return getDescription().equals(handle.getDescription()); + } + return super.equals(obj); + } + + @Override + public String toString() { + return getDescription(); + } +} diff --git a/src/test/java/guitests/guihandles/TaskListPanelHandle.java b/src/test/java/guitests/guihandles/TaskListPanelHandle.java new file mode 100644 index 000000000000..f98200ce580c --- /dev/null +++ b/src/test/java/guitests/guihandles/TaskListPanelHandle.java @@ -0,0 +1,172 @@ +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.taskell.TestApp; +import seedu.taskell.model.task.Task; +import seedu.taskell.model.task.ReadOnlyTask; +import seedu.taskell.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 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; + } + + // Return false if any of the tasks doesn't match + for (int i = 0; i < tasks.length; i++) { + if (!tasksInList.get(startPosition + i).getDescription().description.equals(tasks[i].getDescription().description)){ + 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 description) { + guiRobot.sleep(500); //Allow a bit of time for the list to be updated + final Optional task = getListView().getItems().stream().filter(p -> p.getDescription().description.equals(description)).findAny(); + if (!task.isPresent()) { + throw new IllegalStateException("Description not found: " + description); + } + + 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).getDescription().equals(targetTask.getDescription())){ + 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 getNumberOfPeople() { + return getListView().getItems().size(); + } +} diff --git a/src/test/java/seedu/address/logic/LogicManagerTest.java b/src/test/java/seedu/address/logic/LogicManagerTest.java deleted file mode 100644 index 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/taskell/TestApp.java similarity index 73% rename from src/test/java/seedu/address/TestApp.java rename to src/test/java/seedu/taskell/TestApp.java index 756642b6c180..aaaf9b292d80 100644 --- a/src/test/java/seedu/address/TestApp.java +++ b/src/test/java/seedu/taskell/TestApp.java @@ -1,13 +1,14 @@ -package seedu.address; +package seedu.taskell; import javafx.stage.Screen; import javafx.stage.Stage; -import seedu.address.commons.core.Config; -import seedu.address.commons.core.GuiSettings; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.UserPrefs; -import seedu.address.storage.XmlSerializableAddressBook; -import seedu.address.testutil.TestUtil; +import seedu.taskell.MainApp; +import seedu.taskell.commons.core.Config; +import seedu.taskell.commons.core.GuiSettings; +import seedu.taskell.model.ReadOnlyTaskManager; +import seedu.taskell.model.UserPrefs; +import seedu.taskell.storage.XmlSerializableTaskManager; +import seedu.taskell.testutil.TestUtil; import java.util.function.Supplier; @@ -20,14 +21,14 @@ 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"); public static final String APP_TITLE = "Test App"; - protected static final String ADDRESS_BOOK_NAME = "Test"; - protected Supplier initialDataSupplier = () -> null; + protected static final String TASK_MANAGER_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; @@ -35,7 +36,7 @@ public TestApp(Supplier initialDataSupplier, String saveFil // If some initial local data has been provided, write those to the file if (initialDataSupplier.get() != null) { TestUtil.createDataFileWithData( - new XmlSerializableAddressBook(this.initialDataSupplier.get()), + new XmlSerializableTaskManager(this.initialDataSupplier.get()), this.saveFileLocation); } } @@ -44,9 +45,9 @@ public TestApp(Supplier initialDataSupplier, String saveFil protected Config initConfig(String configFilePath) { Config config = super.initConfig(configFilePath); config.setAppTitle(APP_TITLE); - config.setAddressBookFilePath(saveFileLocation); + config.setTaskManagerFilePath(saveFileLocation); config.setUserPrefsFilePath(DEFAULT_PREF_FILE_LOCATION_FOR_TESTING); - config.setAddressBookName(ADDRESS_BOOK_NAME); + config.setTaskManagerName(TASK_MANAGER_NAME); return config; } diff --git a/src/test/java/seedu/address/commons/core/ConfigTest.java b/src/test/java/seedu/taskell/commons/core/ConfigTest.java similarity index 75% rename from src/test/java/seedu/address/commons/core/ConfigTest.java rename to src/test/java/seedu/taskell/commons/core/ConfigTest.java index 62d58646f736..0773baffc98a 100644 --- a/src/test/java/seedu/address/commons/core/ConfigTest.java +++ b/src/test/java/seedu/taskell/commons/core/ConfigTest.java @@ -1,9 +1,11 @@ -package seedu.address.commons.core; +package seedu.taskell.commons.core; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import seedu.taskell.commons.core.Config; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -14,11 +16,11 @@ public class ConfigTest { @Test public void toString_defaultObject_stringReturned() { - String defaultConfigAsString = "App title : Address App\n" + + String defaultConfigAsString = "App title : Taskell\n" + "Current log level : INFO\n" + "Preference file Location : preferences.json\n" + - "Local data file location : data/addressbook.xml\n" + - "AddressBook name : MyAddressBook"; + "Local data file location : data/taskmanager.xml\n" + + "TaskManager name : MyTaskManager"; assertEquals(defaultConfigAsString, new Config().toString()); } diff --git a/src/test/java/seedu/address/commons/core/VersionTest.java b/src/test/java/seedu/taskell/commons/core/VersionTest.java similarity index 98% rename from src/test/java/seedu/address/commons/core/VersionTest.java rename to src/test/java/seedu/taskell/commons/core/VersionTest.java index 87ac01f6c92d..4df438f64d90 100644 --- a/src/test/java/seedu/address/commons/core/VersionTest.java +++ b/src/test/java/seedu/taskell/commons/core/VersionTest.java @@ -1,9 +1,11 @@ -package seedu.address.commons.core; +package seedu.taskell.commons.core; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import seedu.taskell.commons.core.Version; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; diff --git a/src/test/java/seedu/address/commons/util/AppUtilTest.java b/src/test/java/seedu/taskell/commons/util/AppUtilTest.java similarity index 76% rename from src/test/java/seedu/address/commons/util/AppUtilTest.java rename to src/test/java/seedu/taskell/commons/util/AppUtilTest.java index fbea1d0c1e8e..19b221fd676e 100644 --- a/src/test/java/seedu/address/commons/util/AppUtilTest.java +++ b/src/test/java/seedu/taskell/commons/util/AppUtilTest.java @@ -1,9 +1,11 @@ -package seedu.address.commons.util; +package seedu.taskell.commons.util; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import seedu.taskell.commons.util.AppUtil; + import static org.junit.Assert.assertNotNull; public class AppUtilTest { @@ -15,7 +17,7 @@ public class AppUtilTest { @Test public void getImage_exitingImage(){ - assertNotNull(AppUtil.getImage("/images/address_book_32.png")); + assertNotNull(AppUtil.getImage("/images/task_manager_32.png")); } diff --git a/src/test/java/seedu/address/commons/util/ConfigUtilTest.java b/src/test/java/seedu/taskell/commons/util/ConfigUtilTest.java similarity index 92% rename from src/test/java/seedu/address/commons/util/ConfigUtilTest.java rename to src/test/java/seedu/taskell/commons/util/ConfigUtilTest.java index 6699343c4a82..35e7062040ba 100644 --- a/src/test/java/seedu/address/commons/util/ConfigUtilTest.java +++ b/src/test/java/seedu/taskell/commons/util/ConfigUtilTest.java @@ -1,12 +1,15 @@ -package seedu.address.commons.util; +package seedu.taskell.commons.util; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; -import seedu.address.commons.core.Config; -import seedu.address.commons.exceptions.DataConversionException; + +import seedu.taskell.commons.core.Config; +import seedu.taskell.commons.exceptions.DataConversionException; +import seedu.taskell.commons.util.ConfigUtil; +import seedu.taskell.commons.util.FileUtil; import java.io.File; import java.io.IOException; @@ -76,8 +79,8 @@ private Config getTypicalConfig() { config.setAppTitle("Typical App Title"); config.setLogLevel(Level.INFO); config.setUserPrefsFilePath("C:\\preferences.json"); - config.setAddressBookFilePath("addressbook.xml"); - config.setAddressBookName("TypicalAddressBookName"); + config.setTaskManagerFilePath("taskmanager.xml"); + config.setTaskManagerName("TypicalTaskManagerName"); return config; } diff --git a/src/test/java/seedu/address/commons/util/FileUtilTest.java b/src/test/java/seedu/taskell/commons/util/FileUtilTest.java similarity index 91% rename from src/test/java/seedu/address/commons/util/FileUtilTest.java rename to src/test/java/seedu/taskell/commons/util/FileUtilTest.java index 8de2621799cf..543fda5d0b85 100644 --- a/src/test/java/seedu/address/commons/util/FileUtilTest.java +++ b/src/test/java/seedu/taskell/commons/util/FileUtilTest.java @@ -1,11 +1,13 @@ -package seedu.address.commons.util; +package seedu.taskell.commons.util; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; -import seedu.address.testutil.SerializableTestClass; -import seedu.address.testutil.TestUtil; + +import seedu.taskell.commons.util.FileUtil; +import seedu.taskell.testutil.SerializableTestClass; +import seedu.taskell.testutil.TestUtil; import java.io.File; import java.io.IOException; diff --git a/src/test/java/seedu/address/commons/util/JsonUtilTest.java b/src/test/java/seedu/taskell/commons/util/JsonUtilTest.java similarity index 85% rename from src/test/java/seedu/address/commons/util/JsonUtilTest.java rename to src/test/java/seedu/taskell/commons/util/JsonUtilTest.java index fc3902188807..83785fe9483d 100644 --- a/src/test/java/seedu/address/commons/util/JsonUtilTest.java +++ b/src/test/java/seedu/taskell/commons/util/JsonUtilTest.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.taskell.commons.util; /** * Tests JSON Read and Write diff --git a/src/test/java/seedu/address/commons/util/StringUtilTest.java b/src/test/java/seedu/taskell/commons/util/StringUtilTest.java similarity index 95% rename from src/test/java/seedu/address/commons/util/StringUtilTest.java rename to src/test/java/seedu/taskell/commons/util/StringUtilTest.java index 194dd71d2c3f..8353ca6001fc 100644 --- a/src/test/java/seedu/address/commons/util/StringUtilTest.java +++ b/src/test/java/seedu/taskell/commons/util/StringUtilTest.java @@ -1,9 +1,11 @@ -package seedu.address.commons.util; +package seedu.taskell.commons.util; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import seedu.taskell.commons.util.StringUtil; + import java.io.FileNotFoundException; import static org.hamcrest.CoreMatchers.containsString; diff --git a/src/test/java/seedu/address/commons/util/UrlUtilTest.java b/src/test/java/seedu/taskell/commons/util/UrlUtilTest.java similarity index 95% rename from src/test/java/seedu/address/commons/util/UrlUtilTest.java rename to src/test/java/seedu/taskell/commons/util/UrlUtilTest.java index 58efab5fd499..96f348df6743 100644 --- a/src/test/java/seedu/address/commons/util/UrlUtilTest.java +++ b/src/test/java/seedu/taskell/commons/util/UrlUtilTest.java @@ -1,7 +1,9 @@ -package seedu.address.commons.util; +package seedu.taskell.commons.util; import org.junit.Test; +import seedu.taskell.commons.util.UrlUtil; + import java.net.MalformedURLException; import java.net.URL; diff --git a/src/test/java/seedu/address/commons/util/XmlUtilTest.java b/src/test/java/seedu/taskell/commons/util/XmlUtilTest.java similarity index 60% rename from src/test/java/seedu/address/commons/util/XmlUtilTest.java rename to src/test/java/seedu/taskell/commons/util/XmlUtilTest.java index dc4fd886c23e..48c4dcb3dbdf 100644 --- a/src/test/java/seedu/address/commons/util/XmlUtilTest.java +++ b/src/test/java/seedu/taskell/commons/util/XmlUtilTest.java @@ -1,12 +1,15 @@ -package seedu.address.commons.util; +package seedu.taskell.commons.util; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; -import seedu.address.model.AddressBook; -import seedu.address.storage.XmlSerializableAddressBook; -import seedu.address.testutil.AddressBookBuilder; -import seedu.address.testutil.TestUtil; + +import seedu.taskell.commons.util.FileUtil; +import seedu.taskell.commons.util.XmlUtil; +import seedu.taskell.model.TaskManager; +import seedu.taskell.storage.XmlSerializableTaskManager; +import seedu.taskell.testutil.TaskManagerBuilder; +import seedu.taskell.testutil.TestUtil; import javax.xml.bind.JAXBException; import java.io.File; @@ -19,8 +22,8 @@ public class XmlUtilTest { private static final String TEST_DATA_FOLDER = FileUtil.getPath("src/test/data/XmlUtilTest/"); private static final File EMPTY_FILE = new File(TEST_DATA_FOLDER + "empty.xml"); private static final File MISSING_FILE = new File(TEST_DATA_FOLDER + "missing.xml"); - private static final File VALID_FILE = new File(TEST_DATA_FOLDER + "validAddressBook.xml"); - private static final File TEMP_FILE = new File(TestUtil.getFilePathInSandboxFolder("tempAddressBook.xml")); + private static final File VALID_FILE = new File(TEST_DATA_FOLDER + "validTaskManager.xml"); + private static final File TEMP_FILE = new File(TestUtil.getFilePathInSandboxFolder("tempTaskManager.xml")); @Rule public ExpectedException thrown = ExpectedException.none(); @@ -28,7 +31,7 @@ public class XmlUtilTest { @Test public void getDataFromFile_nullFile_AssertionError() throws Exception { thrown.expect(AssertionError.class); - XmlUtil.getDataFromFile(null, AddressBook.class); + XmlUtil.getDataFromFile(null, TaskManager.class); } @Test @@ -40,26 +43,26 @@ 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, TaskManager.class); } @Test public void getDataFromFile_emptyFile_DataFormatMismatchException() throws Exception { thrown.expect(JAXBException.class); - XmlUtil.getDataFromFile(EMPTY_FILE, AddressBook.class); + XmlUtil.getDataFromFile(EMPTY_FILE, TaskManager.class); } @Test public void getDataFromFile_validFile_validResult() throws Exception { - XmlSerializableAddressBook dataFromFile = XmlUtil.getDataFromFile(VALID_FILE, XmlSerializableAddressBook.class); - assertEquals(9, dataFromFile.getPersonList().size()); + XmlSerializableTaskManager dataFromFile = XmlUtil.getDataFromFile(VALID_FILE, XmlSerializableTaskManager.class); + assertEquals(9, dataFromFile.getTaskList().size()); assertEquals(0, dataFromFile.getTagList().size()); } @Test public void saveDataToFile_nullFile_AssertionError() throws Exception { thrown.expect(AssertionError.class); - XmlUtil.saveDataToFile(null, new AddressBook()); + XmlUtil.saveDataToFile(null, new TaskManager()); } @Test @@ -71,23 +74,23 @@ public void saveDataToFile_nullClass_AssertionError() throws Exception { @Test public void saveDataToFile_missingFile_FileNotFoundException() throws Exception { thrown.expect(FileNotFoundException.class); - XmlUtil.saveDataToFile(MISSING_FILE, new AddressBook()); + XmlUtil.saveDataToFile(MISSING_FILE, new TaskManager()); } @Test public void saveDataToFile_validFile_dataSaved() throws Exception { TEMP_FILE.createNewFile(); - XmlSerializableAddressBook dataToWrite = new XmlSerializableAddressBook(new AddressBook()); + XmlSerializableTaskManager dataToWrite = new XmlSerializableTaskManager(new TaskManager()); XmlUtil.saveDataToFile(TEMP_FILE, dataToWrite); - XmlSerializableAddressBook dataFromFile = XmlUtil.getDataFromFile(TEMP_FILE, XmlSerializableAddressBook.class); - assertEquals((new AddressBook(dataToWrite)).toString(),(new AddressBook(dataFromFile)).toString()); + XmlSerializableTaskManager dataFromFile = XmlUtil.getDataFromFile(TEMP_FILE, XmlSerializableTaskManager.class); + assertEquals((new TaskManager(dataToWrite)).toString(),(new TaskManager(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()); + TaskManagerBuilder builder = new TaskManagerBuilder(new TaskManager()); + dataToWrite = new XmlSerializableTaskManager(builder.withTask(TestUtil.generateSampleTaskData().get(0)).withTag("Friends").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, XmlSerializableTaskManager.class); + assertEquals((new TaskManager(dataToWrite)).toString(),(new TaskManager(dataFromFile)).toString()); } } diff --git a/src/test/java/seedu/taskell/logic/LogicManagerTest.java b/src/test/java/seedu/taskell/logic/LogicManagerTest.java new file mode 100644 index 000000000000..286eef4df660 --- /dev/null +++ b/src/test/java/seedu/taskell/logic/LogicManagerTest.java @@ -0,0 +1,885 @@ +package seedu.taskell.logic; + +import com.google.common.eventbus.Subscribe; + +import seedu.taskell.commons.core.EventsCenter; +import seedu.taskell.commons.events.model.TaskManagerChangedEvent; +import seedu.taskell.commons.events.ui.JumpToListRequestEvent; +import seedu.taskell.commons.events.ui.ShowHelpRequestEvent; +import seedu.taskell.commons.exceptions.IllegalValueException; +import seedu.taskell.logic.Logic; +import seedu.taskell.logic.LogicManager; +import seedu.taskell.logic.commands.*; +import seedu.taskell.model.TaskManager; +import seedu.taskell.model.Model; +import seedu.taskell.model.ModelManager; +import seedu.taskell.model.ReadOnlyTaskManager; +import seedu.taskell.model.tag.Tag; +import seedu.taskell.model.tag.UniqueTagList; +import seedu.taskell.model.task.*; +import seedu.taskell.storage.StorageManager; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +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.assertFalse; +import static org.junit.Assert.assertTrue; +import static seedu.taskell.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 ReadOnlyTaskManager latestSavedTaskManager; + private boolean helpShown; + private int targetedJumpIndex; + + @Subscribe + private void handleLocalModelChangedEvent(TaskManagerChangedEvent abce) { + latestSavedTaskManager = new TaskManager(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 tempTaskManagerFile = saveFolder.getRoot().getPath() + "TempTaskManager.xml"; + String tempPreferencesFile = saveFolder.getRoot().getPath() + "TempPreferences.json"; + logic = new LogicManager(model, new StorageManager(tempTaskManagerFile, tempPreferencesFile)); + EventsCenter.getInstance().registerHandler(this); + + UndoCommand.initializeCommandHistory(); + + latestSavedTaskManager = new TaskManager(model.getTaskManager()); // last saved assumed to be up to startDate 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 'task manager' and the 'last shown list' are expected to be empty. + * @see #assertCommandBehavior(String, String, ReadOnlyTaskManager, List) + */ + private void assertCommandBehavior(String inputCommand, String expectedMessage) throws Exception { + assertCommandBehavior(inputCommand, expectedMessage, new TaskManager(), 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 task manager data are same as those in the {@code expectedTaskManager}
+ * - the backing list shown by UI matches the {@code shownList}
+ * - {@code expectedTaskManager} was saved to the storage file.
+ */ + private void assertCommandBehavior(String inputCommand, String expectedMessage, + ReadOnlyTaskManager expectedTaskManager, + 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(expectedTaskManager, model.getTaskManager()); + assertEquals(expectedTaskManager, latestSavedTaskManager); + } + + + @Test + public void execute_unknownCommandWord() throws Exception { + String unknownCommand = "uicfhmowqewca"; + assertCommandBehavior(unknownCommand, MESSAGE_UNKNOWN_COMMAND); + } + + @Test + public void execute_help() throws Exception { + assertCommandBehavior("help", HelpCommand.SHOWING_HELP_MESSAGE); + assertTrue(helpShown); + } + + @Test + public void execute_exit() throws Exception { + assertCommandBehavior("exit", ExitCommand.MESSAGE_EXIT_ACKNOWLEDGEMENT); + } + + @Test + public void execute_clear() throws Exception { + TestDataHelper helper = new TestDataHelper(); + model.addTask(helper.generateTask(1)); + model.addTask(helper.generateTask(2)); + model.addTask(helper.generateTask(3)); + + assertCommandBehavior("clear", ClearCommand.MESSAGE_SUCCESS, new TaskManager(), Collections.emptyList()); + } + + + @Test + public void execute_add_invalidArgsFormat() throws Exception { + String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE); + assertCommandBehavior( + "add", expectedMessage); + } + + //@@author A0139257X-reused + @Test + public void execute_add_invalidTaskData() throws Exception { + assertCommandBehavior( + "add #descriptionIsEmpty", Description.MESSAGE_DESCRIPTION_CONSTRAINTS); + assertCommandBehavior( + "add Valid Description with invalid startDate format by 1-jan-16", TaskDate.MESSAGE_TASK_DATE_CONSTRAINTS); + assertCommandBehavior( + "add Valid Description with start date before today's date on 1-jan-2000", EventTask.MESSAGE_EVENT_CONSTRAINTS); + assertCommandBehavior( + "add Valid Description with end date before today's date by 1-jan-2000", EventTask.MESSAGE_EVENT_CONSTRAINTS); + assertCommandBehavior( + "add Valid Description with startDate after endDate from 1-jan-2200 to 1-jan-2100", EventTask.MESSAGE_EVENT_CONSTRAINTS); + assertCommandBehavior( + "add Valid Description with startTime before Today's current time at 2am", EventTask.MESSAGE_EVENT_CONSTRAINTS); + assertCommandBehavior( + "add Valid Description p/invalidPriority ", TaskPriority.MESSAGE_TASK_PRIORITY_CONSTRAINTS); + assertCommandBehavior( + "add Valid Description p/0 p/1 ", String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); + assertCommandBehavior( + "add Valid Description #invalid_-[.tag", Tag.MESSAGE_TAG_CONSTRAINTS); + } + + @Test + public void execute_add_successful() throws Exception { + // setup expectations + TestDataHelper helper = new TestDataHelper(); + Task toBeAdded = helper.askBoon(); + TaskManager expectedAB = new TaskManager(); + expectedAB.addTask(toBeAdded); + + // execute command and verify result + assertCommandBehavior(helper.generateAddCommand(toBeAdded), + String.format(AddCommand.MESSAGE_SUCCESS, toBeAdded), + expectedAB, + expectedAB.getTaskList()); + } + + //@@author A0139257X + @Test + public void execute_add_ValidFloatingTaskWithKeywords() throws Exception { + // setup expectations + TestDataHelper helper = new TestDataHelper(); + Task toBeAdded = helper.generateFloatingTask("on by on at from to", "0"); + TaskManager expectedAB = new TaskManager(); + expectedAB.addTask(toBeAdded); + + // execute command and verify result + assertCommandBehavior(helper.generateAddFloatingTaskCommand(toBeAdded), + String.format(AddCommand.MESSAGE_SUCCESS, toBeAdded), + expectedAB, + expectedAB.getTaskList()); + } + + @Test + public void execute_add_ValidFloatingTaskWithKeywordsButNoValidDateTime() throws Exception { + // setup expectations + TestDataHelper helper = new TestDataHelper(); + Task toBeAdded = helper.generateFloatingTask("sleep by the seaside", "0"); + TaskManager expectedAB = new TaskManager(); + expectedAB.addTask(toBeAdded); + + // execute command and verify result + assertCommandBehavior(helper.generateAddFloatingTaskCommand(toBeAdded), + String.format(AddCommand.MESSAGE_SUCCESS, toBeAdded), + expectedAB, + expectedAB.getTaskList()); + } + + @Test + public void execute_add_ValidTaskWithImproperUsageOfAt() throws Exception { + // setup expectations + TestDataHelper helper = new TestDataHelper(); + Task toBeAdded = helper.generateFloatingTask("go shopping at monday", "0"); + TaskManager expectedAB = new TaskManager(); + expectedAB.addTask(toBeAdded); + + // execute command and verify result + assertCommandBehavior(helper.generateAddFloatingTaskCommand(toBeAdded), + String.format(AddCommand.MESSAGE_SUCCESS, toBeAdded), + expectedAB, + expectedAB.getTaskList()); + } + + @Test + public void execute_add_ValidTaskWithValidDateButNoPreFix() throws Exception { + // setup expectations + TestDataHelper helper = new TestDataHelper(); + Task toBeAdded = helper.generateFloatingTask("go shopping today", "0"); + TaskManager expectedAB = new TaskManager(); + expectedAB.addTask(toBeAdded); + + // execute command and verify result + assertCommandBehavior(helper.generateAddFloatingTaskCommand(toBeAdded), + String.format(AddCommand.MESSAGE_SUCCESS, toBeAdded), + expectedAB, + expectedAB.getTaskList()); + } + + @Test + public void execute_add_ValidTaskWithValidTimeButNoPreFix() throws Exception { + // setup expectations + TestDataHelper helper = new TestDataHelper(); + Task toBeAdded = helper.generateFloatingTask("go shopping 7pm", "0"); + TaskManager expectedAB = new TaskManager(); + expectedAB.addTask(toBeAdded); + + // execute command and verify result + assertCommandBehavior(helper.generateAddFloatingTaskCommand(toBeAdded), + String.format(AddCommand.MESSAGE_SUCCESS, toBeAdded), + expectedAB, + expectedAB.getTaskList()); + } + + @Test + public void execute_add_ValidTaskWithImproperUsageOfOn() throws Exception { + // setup expectations + TestDataHelper helper = new TestDataHelper(); + Task toBeAdded = helper.generateFloatingTask("go shopping on 7pm", "0"); + TaskManager expectedAB = new TaskManager(); + expectedAB.addTask(toBeAdded); + + // execute command and verify result + assertCommandBehavior(helper.generateAddFloatingTaskCommand(toBeAdded), + String.format(AddCommand.MESSAGE_SUCCESS, toBeAdded), + expectedAB, + expectedAB.getTaskList()); + } + + @Test + public void execute_add_ValidTaskWithMultipleOn() throws Exception { + String description = "add go shopping on 2-2-2222 on 3-3-3333"; + Task toBeAdded = new EventTask("go shopping on 3-3-3333", "2-2-2222", "2-2-2222", TaskTime.DEFAULT_START_TIME, TaskTime.DEFAULT_END_TIME, "0", TaskStatus.INCOMPLETE, new UniqueTagList()); + TaskManager expectedAB = new TaskManager(); + expectedAB.addTask(toBeAdded); + // execute command and verify result + assertCommandBehavior(description, + String.format(AddCommand.MESSAGE_SUCCESS, toBeAdded), + expectedAB, + expectedAB.getTaskList()); + } + + @Test + public void execute_add_ValidTaskWithMultipleAt() throws Exception { + String description = "add go shopping at 11.58pm at 11.59pm"; + Task toBeAdded = new EventTask("go shopping at 11.59pm", TaskDate.DEFAULT_DATE, TaskDate.DEFAULT_DATE, "11.58pm", TaskTime.DEFAULT_END_TIME, "0", TaskStatus.INCOMPLETE, new UniqueTagList()); + TaskManager expectedAB = new TaskManager(); + expectedAB.addTask(toBeAdded); + // execute command and verify result + assertCommandBehavior(description, + String.format(AddCommand.MESSAGE_SUCCESS, toBeAdded), + expectedAB, + expectedAB.getTaskList()); + } + + @Test + public void execute_add_ValidTaskWithMultipleByDate() throws Exception { + String description = "add go shopping by 2-2-2222 by 3-3-3333"; + Task toBeAdded = new EventTask("go shopping by 3-3-3333", TaskDate.DEFAULT_DATE, "2-2-2222", TaskTime.getTimeNow().toString(), TaskTime.DEFAULT_END_TIME, TaskPriority.DEFAULT_PRIORITY, TaskStatus.INCOMPLETE, new UniqueTagList()); + TaskManager expectedAB = new TaskManager(); + expectedAB.addTask(toBeAdded); + // execute command and verify result + assertCommandBehavior(description, + String.format(AddCommand.MESSAGE_SUCCESS, toBeAdded), + expectedAB, + expectedAB.getTaskList()); + } + + @Test + public void execute_add_ValidTaskWithMultipleByTime() throws Exception { + String description = "add go shopping by 11.58pm by 11.59pm"; + Task toBeAdded = new EventTask("go shopping by 11.59pm", TaskDate.DEFAULT_DATE, TaskDate.DEFAULT_DATE, TaskTime.getTimeNow().toString(), "11.58pm", TaskPriority.DEFAULT_PRIORITY, TaskStatus.INCOMPLETE, new UniqueTagList()); + TaskManager expectedAB = new TaskManager(); + expectedAB.addTask(toBeAdded); + // execute command and verify result + assertCommandBehavior(description, + String.format(AddCommand.MESSAGE_SUCCESS, toBeAdded), + expectedAB, + expectedAB.getTaskList()); + } + + @Test + public void execute_add_ValidTaskWithMultipleFromDate() throws Exception { + String description = "add go shopping from 2-2-2222 from 3-3-3333"; + Task toBeAdded = new EventTask("go shopping from 3-3-3333", "2-2-2222", "2-2-2222", TaskTime.DEFAULT_START_TIME, TaskTime.DEFAULT_END_TIME, "0", TaskStatus.INCOMPLETE, new UniqueTagList()); + TaskManager expectedAB = new TaskManager(); + expectedAB.addTask(toBeAdded); + // execute command and verify result + assertCommandBehavior(description, + String.format(AddCommand.MESSAGE_SUCCESS, toBeAdded), + expectedAB, + expectedAB.getTaskList()); + } + + @Test + public void execute_add_ValidTaskWithMultipleFromTime() throws Exception { + String description = "add go shopping from 11.58pm from 11.59pm"; + Task toBeAdded = new EventTask("go shopping from 11.59pm", TaskDate.DEFAULT_DATE, TaskDate.DEFAULT_DATE, "11.58pm", TaskTime.DEFAULT_END_TIME, "0", TaskStatus.INCOMPLETE, new UniqueTagList()); + TaskManager expectedAB = new TaskManager(); + expectedAB.addTask(toBeAdded); + // execute command and verify result + assertCommandBehavior(description, + String.format(AddCommand.MESSAGE_SUCCESS, toBeAdded), + expectedAB, + expectedAB.getTaskList()); + } + + @Test + public void execute_add_ValidTaskWithMultipleToDate() throws Exception { + String description = "add go shopping to 2-2-2222 to 3-3-3333"; + Task toBeAdded = new EventTask("go shopping to 3-3-3333", TaskDate.DEFAULT_DATE, "2-2-2222", TaskTime.getTimeNow().toString(), TaskTime.DEFAULT_END_TIME, TaskPriority.DEFAULT_PRIORITY, TaskStatus.INCOMPLETE, new UniqueTagList()); + TaskManager expectedAB = new TaskManager(); + expectedAB.addTask(toBeAdded); + // execute command and verify result + assertCommandBehavior(description, + String.format(AddCommand.MESSAGE_SUCCESS, toBeAdded), + expectedAB, + expectedAB.getTaskList()); + } + + @Test + public void execute_add_ValidTaskWithMultipleToTime() throws Exception { + String description = "add go shopping to 11.58pm to 11.59pm"; + Task toBeAdded = new EventTask("go shopping to 11.59pm", TaskDate.DEFAULT_DATE, TaskDate.DEFAULT_DATE, TaskTime.getTimeNow().toString(), "11.58pm", TaskPriority.DEFAULT_PRIORITY, TaskStatus.INCOMPLETE, new UniqueTagList()); + TaskManager expectedAB = new TaskManager(); + expectedAB.addTask(toBeAdded); + // execute command and verify result + assertCommandBehavior(description, + String.format(AddCommand.MESSAGE_SUCCESS, toBeAdded), + expectedAB, + expectedAB.getTaskList()); + } + + @Test + public void execute_add_ValidEventWithByTime() throws Exception { + String description = "add go shopping by 11:59pm"; + Task toBeAdded = new EventTask("go shopping", TaskDate.getTodayDate().toString(), TaskDate.getTodayDate().toString(), TaskTime.getTimeNow().toString(), "11:59pm", TaskPriority.DEFAULT_PRIORITY, TaskStatus.INCOMPLETE, new UniqueTagList()); + TaskManager expectedAB = new TaskManager(); + expectedAB.addTask(toBeAdded); + // execute command and verify result + assertCommandBehavior(description, + String.format(AddCommand.MESSAGE_SUCCESS, toBeAdded), + expectedAB, + expectedAB.getTaskList()); + } + + @Test + public void execute_add_ValidEventWithAtTime() throws Exception { + String description = "add go shopping at 11:59pm"; + Task toBeAdded = new EventTask("go shopping", TaskDate.getTodayDate().toString(), TaskDate.getTodayDate().toString(), "11:59pm", TaskTime.DEFAULT_END_TIME, "0", TaskStatus.INCOMPLETE, new UniqueTagList()); + TaskManager expectedAB = new TaskManager(); + expectedAB.addTask(toBeAdded); + // execute command and verify result + assertCommandBehavior(description, + String.format(AddCommand.MESSAGE_SUCCESS, toBeAdded), + expectedAB, + expectedAB.getTaskList()); + } + + @Test + public void execute_add_ValidEventWithTagNotAtTheEnd() throws Exception { + String description = "add go to #girlfriend Mavis's house at 11:58pm"; + Task toBeAdded = new EventTask("go to Mavis's house", TaskDate.getTodayDate().toString(), TaskDate.getTodayDate().toString(), "11:58pm", TaskTime.DEFAULT_END_TIME, TaskPriority.DEFAULT_PRIORITY, TaskStatus.INCOMPLETE, new UniqueTagList(new Tag("girlfriend"))); + TaskManager expectedAB = new TaskManager(); + expectedAB.addTask(toBeAdded); + // execute command and verify result + assertCommandBehavior(description, + String.format(AddCommand.MESSAGE_SUCCESS, toBeAdded), + expectedAB, + expectedAB.getTaskList()); + } + + @Test + public void execute_add_ValidEventWithEndTimeBeforeStartTime() throws Exception { + String description = "add stayover at Juliet's house from 11.58pm to 10am"; + Task toBeAdded = new EventTask("stayover at Juliet's house", TaskDate.getTodayDate().toString(), TaskDate.getTodayDate().toString(), "11:58pm", "10:00am", TaskPriority.DEFAULT_PRIORITY, TaskStatus.INCOMPLETE, new UniqueTagList()); + TaskManager expectedAB = new TaskManager(); + expectedAB.addTask(toBeAdded); + // execute command and verify result + assertCommandBehavior(description, + String.format(AddCommand.MESSAGE_SUCCESS, toBeAdded), + expectedAB, + expectedAB.getTaskList()); + } + + @Test + public void execute_add_ValidEventWithSameDayNameInWeekAsToday() throws Exception { + TaskDate today = new TaskDate(TaskDate.getTodayDate().toString()); + TaskDate tomorrow = new TaskDate(TaskDate.getTomorrowDate().toString()); + String todayNameInWeek = today.getDayNameInWeek(); + String tomorrowNameInWeek = tomorrow.getDayNameInWeek(); + String description = "add go school from " + todayNameInWeek + " to " + tomorrowNameInWeek ; + Task toBeAdded = new EventTask("go school", today.getNextWeek().toString(), tomorrow.toString(), TaskTime.DEFAULT_START_TIME, TaskTime.DEFAULT_END_TIME, "0", TaskStatus.INCOMPLETE, new UniqueTagList()); + TaskManager expectedAB = new TaskManager(); + expectedAB.addTask(toBeAdded); + // execute command and verify result + assertCommandBehavior(description, + String.format(AddCommand.MESSAGE_SUCCESS, toBeAdded), + expectedAB, + expectedAB.getTaskList()); + } + + @Test + public void execute_add_ValidEventDuration_successful() throws Exception { + // setup expectations + TestDataHelper helper = new TestDataHelper(); + Task toBeAdded = helper.validEventDuration(); + TaskManager expectedAB = new TaskManager(); + expectedAB.addTask(toBeAdded); + + // execute command and verify result + assertCommandBehavior(helper.generateAddCommand(toBeAdded), + String.format(AddCommand.MESSAGE_SUCCESS, toBeAdded), + expectedAB, + expectedAB.getTaskList()); + } + + //@@author A0139257X-reused + @Test + public void execute_addDuplicate_notAllowed() throws Exception { + // setup expectations + TestDataHelper helper = new TestDataHelper(); + Task toBeAdded = helper.askBoon(); + TaskManager expectedAB = new TaskManager(); + expectedAB.addTask(toBeAdded); + + // setup starting state + model.addTask(toBeAdded); // task already in internal task manager + + // execute command and verify result + assertCommandBehavior( + helper.generateAddCommand(toBeAdded), + AddCommand.MESSAGE_DUPLICATE_TASK, + expectedAB, + expectedAB.getTaskList()); + + } + //@@author + + @Test + public void execute_list_showsAllTasks() throws Exception { + // prepare expectations + TestDataHelper helper = new TestDataHelper(); + TaskManager expectedAB = helper.generateTaskManager(2); + List expectedList = expectedAB.getTaskList(); + + // prepare task manager state + helper.addToModel(model, 2); + + assertCommandBehavior("list-all", + ListAllCommand.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 TaskManager()); + for (Task p : taskList) { + model.addTask(p); + } + + assertCommandBehavior(commandWord + " 3", expectedMessage, model.getTaskManager(), taskList); + } + + @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_jumpsToCorrectTask() throws Exception { + TestDataHelper helper = new TestDataHelper(); + List threeTasks = helper.generateTaskList(3); + + TaskManager expectedAB = helper.generateTaskManager(threeTasks); + helper.addToModel(model, threeTasks); + + assertCommandBehavior("select 2", + String.format(SelectCommand.MESSAGE_SELECT_TASK_SUCCESS, 2), + expectedAB, + expectedAB.getTaskList()); + assertEquals(1, targetedJumpIndex); + assertEquals(model.getFilteredTaskList().get(1), threeTasks.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_removesCorrectTask() throws Exception { + TestDataHelper helper = new TestDataHelper(); + List threeTasks = helper.generateTaskList(3); + + TaskManager expectedAB = helper.generateTaskManager(threeTasks); + expectedAB.removeTask(threeTasks.get(1)); + helper.addToModel(model, threeTasks); + + assertCommandBehavior("delete 2", + String.format(DeleteCommand.MESSAGE_DELETE_TASK_SUCCESS, threeTasks.get(1)), + expectedAB, + expectedAB.getTaskList()); + } + + + @Test + public void execute_find_invalidArgsFormat() throws Exception { + String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE); + assertCommandBehavior("find ", expectedMessage); + } + + @Test + public void execute_find_onlyMatchesFullWordsInNames() throws Exception { + TestDataHelper helper = new TestDataHelper(); + Task pTarget1 = helper.generateTaskWithName("bla bla KEY bla"); + Task pTarget2 = helper.generateTaskWithName("bla KEY bla bceofeia"); + Task p1 = helper.generateTaskWithName("KE Y"); + Task p2 = helper.generateTaskWithName("KEYKEYKEY sduauo"); + + List fourTasks = helper.generateTaskList(p1, pTarget1, p2, pTarget2); + TaskManager expectedAB = helper.generateTaskManager(fourTasks); + List expectedList = helper.generateTaskList(pTarget1, pTarget2); + helper.addToModel(model, fourTasks); + + 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 fourTasks = helper.generateTaskList(p3, p1, p4, p2); + TaskManager expectedAB = helper.generateTaskManager(fourTasks); + List expectedList = fourTasks; + helper.addToModel(model, fourTasks); + + assertCommandBehavior("find KEY", + Command.getMessageForTaskListShownSummary(expectedList.size()), + expectedAB, + expectedList); + } + + @Test + public void execute_find_matchesIfAllKeywordsPresent() throws Exception { + TestDataHelper helper = new TestDataHelper(); + + Task pTarget = helper.generateTaskWithName("bla KEY rAnDoM bla bceofeia"); + Task p1 = helper.generateTaskWithName("sduauo"); + Task p2 = helper.generateTaskWithName("bla bla KEY bla"); + Task p3 = helper.generateTaskWithName("key key"); + + List oneTask = helper.generateTaskList(pTarget); + TaskManager expectedAB = helper.generateTaskManager(oneTask); + List expectedList = helper.generateTaskList(pTarget); + helper.addToModel(model, oneTask); + + assertCommandBehavior("find key rAnDoM", + Command.getMessageForTaskListShownSummary(expectedList.size()), + expectedAB, + expectedList); + } + + + /** + * A utility class to generate test data. + */ + class TestDataHelper{ + + Task askBoon() throws Exception { + Description description = new Description("Ask boon for tax rebate"); + String taskType = Task.EVENT_TASK; + TaskDate startDate = new TaskDate("1-1-2100"); + TaskDate endDate = new TaskDate("1-12-2100"); + TaskTime startTime = new TaskTime("12:30AM"); + TaskTime endTime = new TaskTime("12:45AM"); + TaskPriority privatetaskPriority = new TaskPriority("0"); + TaskStatus initialStatus = new TaskStatus(TaskStatus.INCOMPLETE); + Tag tag1 = new Tag("tag1"); + Tag tag2 = new Tag("tag2"); + UniqueTagList tags = new UniqueTagList(tag1, tag2); + return new Task(description, taskType, startDate, endDate, startTime, endTime, privatetaskPriority, initialStatus, tags); + } + + Task validEventDuration() throws Exception { + Description description = new Description("StartDate is before EndDate but startTime is afterEndTime"); + String taskType = Task.EVENT_TASK; + TaskDate startDate = new TaskDate("1-1-2100"); + TaskDate endDate = new TaskDate("1-12-2100"); + TaskTime startTime = new TaskTime("2:30pm"); + TaskTime endTime = new TaskTime("3:45AM"); + TaskPriority privatetaskPriority = new TaskPriority("0"); + TaskStatus initialStatus = new TaskStatus(TaskStatus.INCOMPLETE); + Tag tag1 = new Tag("tag1"); + Tag tag2 = new Tag("tag2"); + UniqueTagList tags = new UniqueTagList(tag1, tag2); + return new Task(description, taskType, startDate, endDate, startTime, endTime, privatetaskPriority, initialStatus, tags); + } + + + /** + * Generates a valid task using the given seed. + * Running this function with the same parameter values guarantees the returned task will have the same state. + * Each unique seed will generate a unique Task object. + * + * @param seed used to generate the task data field values + */ + Task generateTask(int seed) throws Exception { + return new Task( + new Description("Task " + seed), + Task.EVENT_TASK, + new TaskDate("1-1-2100"), + new TaskDate("1-12-2100"), + new TaskTime("12:30AM"), + new TaskTime("12:45AM"), + new TaskPriority((seed % 4) + ""), + new TaskStatus(TaskStatus.INCOMPLETE), + new UniqueTagList(new Tag("tag" + Math.abs(seed)), new Tag("tag" + Math.abs(seed + 1))) + ); + } + + /** + * Generate event task with the given parameters + */ + Task generateEventTask(String description, String startDate, String endDate, String startTime, String endTime, String taskPriority, String taskStatus) throws Exception{ + return new Task( + new Description(description), + Task.EVENT_TASK, + new TaskDate(startDate), + new TaskDate(endDate), + new TaskTime(startTime), + new TaskTime(endTime), + new TaskPriority(taskPriority), + new TaskStatus(taskStatus), + new UniqueTagList(new Tag("tag" + Math.abs(1)), new Tag("tag" + Math.abs(2))) + ); + } + + /** + * Generate floating task with the given parameters + */ + Task generateFloatingTask(String description, String taskPriority) throws Exception{ + return new Task( + new Description(description), + Task.FLOATING_TASK, + new TaskDate(TaskDate.DEFAULT_DATE), + new TaskDate(TaskDate.DEFAULT_DATE), + new TaskTime(TaskTime.DEFAULT_START_TIME), + new TaskTime(TaskTime.DEFAULT_END_TIME), + new TaskPriority(taskPriority), + new TaskStatus(TaskStatus.INCOMPLETE), + new UniqueTagList(new Tag("tag" + Math.abs(1)), new Tag("tag" + Math.abs(2))) + ); + } + + /** Generates the correct add command based on the task given */ + String generateAddCommand(Task p) { + StringBuffer cmd = new StringBuffer(); + + cmd.append("add "); + cmd.append(p.getDescription().toString()); + cmd.append(" from ").append(p.getStartDate()); + cmd.append(" to ").append(p.getEndDate()); + cmd.append(" from ").append(p.getStartTime()); + cmd.append(" to ").append(p.getEndTime()); + cmd.append(" " + TaskPriority.PREFIX).append(p.getTaskPriority()); + + UniqueTagList tags = p.getTags(); + for(Tag t: tags){ + cmd.append(" " + Tag.PREFIX).append(t.tagName); + } + + return cmd.toString(); + } + + /** Generates the correct add command based on the floating task given */ + String generateAddFloatingTaskCommand(Task p) { + StringBuffer cmd = new StringBuffer(); + + cmd.append("add "); + cmd.append(p.getDescription().toString()); + cmd.append(" " + TaskPriority.PREFIX).append(p.getTaskPriority()); + + UniqueTagList tags = p.getTags(); + for(Tag t: tags){ + cmd.append(" " + Tag.PREFIX).append(t.tagName); + } + + return cmd.toString(); + } + + /** + * Generates an TaskManager with auto-generated tasks. + */ + TaskManager generateTaskManager(int numGenerated) throws Exception{ + TaskManager taskManager = new TaskManager(); + addToTaskManager(taskManager, numGenerated); + return taskManager; + } + + /** + * Generates an TaskManager based on the list of Tasks given. + */ + TaskManager generateTaskManager(List tasks) throws Exception{ + TaskManager taskManager = new TaskManager(); + addToTaskManager(taskManager, tasks); + return taskManager; + } + + /** + * Adds auto-generated Task objects to the given TaskManager + * @param taskManager The TaskManager to which the Tasks will be added + */ + void addToTaskManager(TaskManager taskManager, int numGenerated) throws Exception{ + addToTaskManager(taskManager, generateTaskList(numGenerated)); + } + + /** + * Adds the given list of Tasks to the given TaskManager + */ + void addToTaskManager(TaskManager taskManager, List tasksToAdd) throws Exception{ + for(Task p: tasksToAdd){ + taskManager.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 description. Other fields will have some dummy values. + */ + + Task generateTaskWithName(String description) throws Exception { + return new Task( + new Description(description), + Task.EVENT_TASK, + new TaskDate("1-1-2100"), + new TaskDate("1-12-2100"), + new TaskTime("12:30AM"), + new TaskTime("12:45AM"), + new TaskPriority(TaskPriority.NO_PRIORITY), + new TaskStatus(TaskStatus.INCOMPLETE), + new UniqueTagList(new Tag("tag")) + ); + } + + } +} diff --git a/src/test/java/seedu/address/model/UnmodifiableObservableListTest.java b/src/test/java/seedu/taskell/model/UnmodifiableObservableListTest.java similarity index 93% rename from src/test/java/seedu/address/model/UnmodifiableObservableListTest.java rename to src/test/java/seedu/taskell/model/UnmodifiableObservableListTest.java index 0334d7e42073..920172773dcb 100644 --- a/src/test/java/seedu/address/model/UnmodifiableObservableListTest.java +++ b/src/test/java/seedu/taskell/model/UnmodifiableObservableListTest.java @@ -1,16 +1,17 @@ -package seedu.address.model; +package seedu.taskell.model; import javafx.collections.FXCollections; +import seedu.taskell.commons.core.UnmodifiableObservableList; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; -import seedu.address.commons.core.UnmodifiableObservableList; import java.util.*; import static org.junit.Assert.assertSame; -import static seedu.address.testutil.TestUtil.assertThrows; +import static seedu.taskell.testutil.TestUtil.assertThrows; public class UnmodifiableObservableListTest { diff --git a/src/test/java/seedu/taskell/model/task/TaskDateTest.java b/src/test/java/seedu/taskell/model/task/TaskDateTest.java new file mode 100644 index 000000000000..2b6f01e67cd4 --- /dev/null +++ b/src/test/java/seedu/taskell/model/task/TaskDateTest.java @@ -0,0 +1,220 @@ +package seedu.taskell.model.task; + +import seedu.taskell.commons.exceptions.IllegalValueException; +import org.junit.Test; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertTrue; + +//@@author A0139257X +public class TaskDateTest { + + @Test + public void assertValidFormatBehaviourForDate() { + //Valid Day of the Week + assertTrue(TaskDate.isValidDate("mon")); + assertTrue(TaskDate.isValidDate("tue")); + assertTrue(TaskDate.isValidDate("WED")); + assertTrue(TaskDate.isValidDate("thurs")); + assertTrue(TaskDate.isValidDate("fRi")); + assertTrue(TaskDate.isValidDate("saturday")); + assertTrue(TaskDate.isValidDate("sun")); + + //Valid Month + assertTrue(TaskDate.isValidDate("jan")); + assertTrue(TaskDate.isValidDate("fEb")); + assertTrue(TaskDate.isValidDate("march")); + assertTrue(TaskDate.isValidDate("APRIL")); + assertTrue(TaskDate.isValidDate("mAy")); + assertTrue(TaskDate.isValidDate("junE")); + assertTrue(TaskDate.isValidDate("jul")); + assertTrue(TaskDate.isValidDate("aug")); + assertTrue(TaskDate.isValidDate("sept")); + assertTrue(TaskDate.isValidDate("oct")); + assertTrue(TaskDate.isValidDate("November")); + assertTrue(TaskDate.isValidDate("December")); + + //Valid Month and Year + assertTrue(TaskDate.isValidDate("may 2016")); + assertTrue(TaskDate.isValidDate("may-2016")); + assertTrue(TaskDate.isValidDate("may.2016")); + assertTrue(TaskDate.isValidDate("may/2016")); + + //Valid Day and Month + assertTrue(TaskDate.isValidDate("1 jan")); + assertTrue(TaskDate.isValidDate("1-jan")); + assertTrue(TaskDate.isValidDate("1.jan")); + assertTrue(TaskDate.isValidDate("1/jan")); + + //Valid full Date + assertTrue(TaskDate.isValidDate(TaskDate.DEFAULT_DATE)); + assertTrue(TaskDate.isValidDate("1 1 2016")); + assertTrue(TaskDate.isValidDate("1 jan 2016")); + assertTrue(TaskDate.isValidDate("1-1-2016")); + assertTrue(TaskDate.isValidDate("1-jan-2016")); + assertTrue(TaskDate.isValidDate("1.1.2016")); + assertTrue(TaskDate.isValidDate("8.DeCeMbEr.2016")); + assertTrue(TaskDate.isValidDate("8/8/2016")); + assertTrue(TaskDate.isValidDate("8/jan/2016")); + assertTrue(TaskDate.isValidDate("1-1/2016")); + assertTrue(TaskDate.isValidDate("1-jan/2016")); + assertTrue(TaskDate.isValidDate("1-1.2016")); + assertTrue(TaskDate.isValidDate("1-jan.2016")); + assertTrue(TaskDate.isValidDate("1/1-2016")); + assertTrue(TaskDate.isValidDate("1/jan-2016")); + assertTrue(TaskDate.isValidDate("1/1.2016")); + assertTrue(TaskDate.isValidDate("1/jan.2016")); + assertTrue(TaskDate.isValidDate("1.1/2016")); + assertTrue(TaskDate.isValidDate("1.jan/2016")); + assertTrue(TaskDate.isValidDate("1.1-2016")); + assertTrue(TaskDate.isValidDate("1.jan-2016")); + + //Valid Today + assertTrue(TaskDate.isValidDate("Today")); + assertTrue(TaskDate.isValidDate("tdy")); + + //Valid Tomorrow + assertTrue(TaskDate.isValidDate("Tomorrow")); + assertTrue(TaskDate.isValidDate("tmr")); + + } + + @Test + public void assertInvalidFormatBehaviourForDate() { + assertFalse(TaskDate.isValidDate("")); + assertFalse(TaskDate.isValidDate(null)); + assertFalse(TaskDate.isValidDate("1st January")); + assertFalse(TaskDate.isValidDate("1/2")); + assertFalse(TaskDate.isValidDate("01022016")); + assertFalse(TaskDate.isValidDate("2016")); + assertFalse(TaskDate.isValidDate("NotAValidDate")); + } + + @Test + public void assertNewTaskDateBehaviour() throws IllegalValueException { + TaskDate today = TaskDate.getTodayDate(); + TaskDate validDayOfWeek = new TaskDate(today.getDayNameInWeek()); + assertEquals(today.getNextWeek(), validDayOfWeek); + + TaskDate validMonth = new TaskDate("september"); + assertEquals("1-9-2016", validMonth.toString()); + + TaskDate validMonthAndYear = new TaskDate("dec-2016"); + assertEquals("1-12-2016", validMonthAndYear.toString()); + + TaskDate validDayAndMonth = new TaskDate("1-jan"); + assertEquals("1-1-2016", validDayAndMonth.toString()); + + TaskDate validFullDate = new TaskDate("1-1-2011"); + assertEquals("1-1-2011", validFullDate.toString()); + + TaskDate validToday = new TaskDate("today"); + DateTimeFormatter standardFormat = DateTimeFormatter.ofPattern("d-MM-yyyy"); + assertEquals(LocalDate.now().format(standardFormat), validToday.toString()); + + TaskDate validTomorrow = new TaskDate("tmr"); + standardFormat = DateTimeFormatter.ofPattern("d-MM-yyyy"); + assertEquals(LocalDate.now().plusDays(1).format(standardFormat), validTomorrow.toString()); + + try { + TaskDate invalidDate = new TaskDate("NOT-A-VALID-DATE"); + } catch (IllegalValueException ive) { + assertEquals(TaskDate.MESSAGE_TASK_DATE_CONSTRAINTS, ive.getMessage()); + } + } + + @Test + public void assertCorrectTodayDate() { + DateTimeFormatter standardFormat = DateTimeFormatter.ofPattern("d-MM-yyyy"); + assertEquals(LocalDate.now().format(standardFormat), TaskDate.getTodayDate().toString()); + } + + @Test + public void assertCorrectTomorrowDate() { + DateTimeFormatter standardFormat = DateTimeFormatter.ofPattern("d-MM-yyyy"); + assertEquals(LocalDate.now().plusDays(1).format(standardFormat), TaskDate.getTomorrowDate().toString()); + } + + @Test + public void assertCorrectThisYear() { + assertEquals(LocalDate.now().getYear() + "", TaskDate.getThisYear()); + } + + @Test + public void assertCorrectGetNextDay() throws IllegalValueException { + TaskDate today = new TaskDate("1-1-2016"); + TaskDate nextDay = new TaskDate("2-1-2016"); + assertEquals(nextDay, today.getNextDay()); + } + + @Test + public void assertCorrectGetNextWeek() throws IllegalValueException { + TaskDate today = new TaskDate("1-1-2016"); + TaskDate nextWeek = new TaskDate("8-1-2016"); + assertEquals(nextWeek, today.getNextWeek()); + } + + @Test + public void assertDateisBeforeBehaviour() throws IllegalValueException { + TaskDate startDate = new TaskDate("1-1-2100"); + TaskDate endDateDiffDaySameMonthSameYear = new TaskDate("10-1-2100"); + TaskDate endDateSameDayDiffMonthSameYear = new TaskDate("1-2-2100"); + TaskDate endDateSameDaySameMonthDiffYear = new TaskDate("1-1-2200"); + + assertTrue(startDate.isBefore(endDateDiffDaySameMonthSameYear)); + assertTrue(startDate.isBefore(endDateSameDayDiffMonthSameYear)); + assertTrue(startDate.isBefore(endDateSameDaySameMonthDiffYear)); + + assertFalse(endDateDiffDaySameMonthSameYear.isBefore(startDate)); + assertFalse(endDateSameDayDiffMonthSameYear.isBefore(startDate)); + assertFalse(endDateSameDaySameMonthDiffYear.isBefore(startDate)); + } + + @Test + public void assertDateisAfterBehaviour() throws IllegalValueException { + TaskDate startDate = new TaskDate("1-1-2100"); + TaskDate endDateDiffDaySameMonthSameYear = new TaskDate("10-1-2100"); + TaskDate endDateSameDayDiffMonthSameYear = new TaskDate("1-2-2100"); + TaskDate endDateSameDaySameMonthDiffYear = new TaskDate("1-1-2200"); + + assertTrue(endDateDiffDaySameMonthSameYear.isAfter(startDate)); + assertTrue(endDateSameDayDiffMonthSameYear.isAfter(startDate)); + assertTrue(endDateSameDaySameMonthDiffYear.isAfter(startDate)); + + assertFalse(startDate.isAfter(endDateDiffDaySameMonthSameYear)); + assertFalse(startDate.isAfter(endDateSameDayDiffMonthSameYear)); + assertFalse(startDate.isAfter(endDateSameDaySameMonthDiffYear)); + } + + @Test + public void assertCorrectDisplayDate() throws IllegalValueException { + TaskDate date = new TaskDate("22-10-2016"); + assertEquals("Saturday, 22 October 2016", date.getDisplayDate()); + } + + @Test + public void assertCorrectToString() throws IllegalValueException { + TaskDate date = new TaskDate("1-1-2015"); + assertEquals("1-1-2015", date.toString()); + } + + @Test + public void assertEqualsBehaviour() throws IllegalValueException { + TaskDate date = new TaskDate("1-1-2015"); + TaskDate sameDate = new TaskDate("1-1-2015"); + TaskDate differentDate = new TaskDate("2-2-2016"); + + assertEquals(date, date); + assertEquals(date, sameDate); + + assertNotSame(date, differentDate); + assertNotSame(date, "1-1-2015"); + assertNotSame(date, "NOT A DATE"); + assertNotSame(date, null); + } +} diff --git a/src/test/java/seedu/taskell/model/task/TaskTimeTest.java b/src/test/java/seedu/taskell/model/task/TaskTimeTest.java new file mode 100644 index 000000000000..92dbaebcbe14 --- /dev/null +++ b/src/test/java/seedu/taskell/model/task/TaskTimeTest.java @@ -0,0 +1,159 @@ +package seedu.taskell.model.task; + +import seedu.taskell.commons.exceptions.IllegalValueException; +import org.junit.Test; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertTrue; + +//@@author A0139257X +public class TaskTimeTest { + @Test + public void assertValidFormatBehaviourForTime() { + assertTrue(TaskTime.isValidTime(TaskTime.DEFAULT_START_TIME)); + assertTrue(TaskTime.isValidTime(TaskTime.DEFAULT_END_TIME)); + assertTrue(TaskTime.isValidTime("12am")); + assertTrue(TaskTime.isValidTime("1.30pm")); + assertTrue(TaskTime.isValidTime("1:40pm")); + assertTrue(TaskTime.isValidTime("1-30am")); + assertTrue(TaskTime.isValidTime("2:30Am")); + assertTrue(TaskTime.isValidTime("noW")); + + //Valid Noon + assertTrue(TaskTime.isValidTime("noon")); + assertTrue(TaskTime.isValidTime("afterNoon")); + assertTrue(TaskTime.isValidTime("12noon")); + assertTrue(TaskTime.isValidTime("12-noon")); + + //Valid Midnight + assertTrue(TaskTime.isValidTime("midnight")); + assertTrue(TaskTime.isValidTime("Mid-Night")); + assertTrue(TaskTime.isValidTime("12MidnIght")); + assertTrue(TaskTime.isValidTime("12-miDnight")); + assertTrue(TaskTime.isValidTime("12mid-night")); + assertTrue(TaskTime.isValidTime("12-mid-nighT")); + } + + @Test + public void assertInvalidFormatBehaviourForTime() { + assertFalse(TaskTime.isValidTime("1.3am")); + assertFalse(TaskTime.isValidTime("2")); + assertFalse(TaskTime.isValidTime("13pm")); + assertFalse(TaskTime.isValidTime("2359")); + assertFalse(TaskTime.isValidTime("NotAValidTime")); + assertFalse(TaskTime.isValidTime("")); + } + + @Test + public void assertValidNewTaskTimeBehaviour() { + try { + TaskTime time = new TaskTime("now"); + TaskTime expected = new TaskTime(TaskTime.getTimeNow().toString()); + assertEquals(expected, time); + + time = new TaskTime("12Noon"); + expected = new TaskTime(TaskTime.NOON); + assertEquals(expected, time); + + time = new TaskTime("midNiGht"); + expected = new TaskTime(TaskTime.MIDNIGHT); + assertEquals(expected, time); + } catch (IllegalValueException ive) { + assert false; + } + } + + @Test + public void assertInvalidNewTaskTimeBehaviour() { + try { + TaskTime time = new TaskTime("NOT A VALID TIME"); + } catch (IllegalValueException ive) { + assertEquals(TaskTime.MESSAGE_TASK_TIME_CONSTRAINTS, ive.getMessage()); + } + } + + @Test + public void assertTimeIsBeforeBehaviour() throws IllegalValueException { + TaskTime timeIs12Am = new TaskTime("12am"); + TaskTime timeIs12Pm = new TaskTime("12pm"); + TaskTime timeNot12Am = new TaskTime("3am"); + TaskTime timeNot12Pm = new TaskTime("3pm"); + TaskTime time = new TaskTime(TaskTime.DEFAULT_END_TIME); + + //Correct Behaviour + assertTrue(timeIs12Am.isBefore(timeIs12Pm)); + assertTrue(timeIs12Am.isBefore(timeNot12Am)); + assertTrue(timeNot12Am.isBefore(timeIs12Pm)); + assertTrue(timeNot12Pm.isBefore(time)); + assertTrue(timeIs12Am.isBefore(time)); + + //Incorrect Behaviour + assertFalse(timeIs12Am.isBefore(timeIs12Am)); + assertFalse(timeIs12Pm.isBefore(timeIs12Am)); + assertFalse(timeIs12Pm.isBefore(timeNot12Am)); + } + + @Test + public void assertTimeIsAfterBehaviour() throws IllegalValueException { + TaskTime timeIs12Am = new TaskTime("12am"); + TaskTime timeIs12Pm = new TaskTime("12pm"); + TaskTime timeNot12Am = new TaskTime("3am"); + TaskTime timeNot12Pm = new TaskTime("3pm"); + TaskTime time = new TaskTime(TaskTime.DEFAULT_END_TIME); + + //Correct Behaviour + assertTrue(timeIs12Pm.isAfter(timeIs12Am)); + assertTrue(timeIs12Pm.isAfter(timeNot12Am)); + + //Incorrect Behaviour + assertFalse(timeIs12Am.isAfter(timeIs12Am)); + assertFalse(timeIs12Am.isAfter(timeIs12Pm)); + assertFalse(timeIs12Am.isAfter(timeNot12Am)); + assertFalse(timeNot12Am.isAfter(timeIs12Pm)); + assertFalse(timeNot12Pm.isAfter(time)); + } + + @Test + public void assertCorrectExtractionOfTime() throws IllegalValueException { + TaskTime validTime = new TaskTime("4.35pm"); + + assertEquals("4", validTime.getHour()); + assertEquals("35", validTime.getMinute()); + assertEquals("PM", validTime.getAntePost()); + } + + @Test + public void assertCorrectCurrentTime() { + DateTimeFormatter dtf = DateTimeFormatter.ofPattern("h:mma"); + LocalTime currTime = LocalTime.now(); + assertEquals(LocalTime.of(currTime.getHour(), currTime.getMinute()).format(dtf), TaskTime.getTimeNow().toString()); + } + + @Test + public void assertCorrectToString() throws IllegalValueException { + TaskTime time = new TaskTime("5pm"); + assertEquals("5:00PM", time.toString()); + } + + @Test + public void assertEqualsBehaviour() throws IllegalValueException { + TaskTime time = new TaskTime("12.30am"); + TaskTime sameTime= new TaskTime("12.30am"); + TaskTime differentTime= new TaskTime("3pm"); + + assertEquals(time, time); + assertEquals(time, sameTime); + + assertNotSame(time, differentTime); + assertNotSame(time, "3am"); + assertNotSame(time, "NOT A TIME"); + assertNotSame(time, null); + } + +} diff --git a/src/test/java/seedu/address/storage/JsonUserPrefsStorageTest.java b/src/test/java/seedu/taskell/storage/JsonUserPrefsStorageTest.java similarity index 94% rename from src/test/java/seedu/address/storage/JsonUserPrefsStorageTest.java rename to src/test/java/seedu/taskell/storage/JsonUserPrefsStorageTest.java index 4e87203611be..60cdf5bd451b 100644 --- a/src/test/java/seedu/address/storage/JsonUserPrefsStorageTest.java +++ b/src/test/java/seedu/taskell/storage/JsonUserPrefsStorageTest.java @@ -1,13 +1,15 @@ -package seedu.address.storage; +package seedu.taskell.storage; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.commons.util.FileUtil; -import seedu.address.model.UserPrefs; + +import seedu.taskell.commons.exceptions.DataConversionException; +import seedu.taskell.commons.util.FileUtil; +import seedu.taskell.model.UserPrefs; +import seedu.taskell.storage.JsonUserPrefsStorage; import java.io.File; import java.io.IOException; diff --git a/src/test/java/seedu/address/storage/StorageManagerTest.java b/src/test/java/seedu/taskell/storage/StorageManagerTest.java similarity index 55% rename from src/test/java/seedu/address/storage/StorageManagerTest.java rename to src/test/java/seedu/taskell/storage/StorageManagerTest.java index 6780feab6afc..1bd6a0750b76 100644 --- a/src/test/java/seedu/address/storage/StorageManagerTest.java +++ b/src/test/java/seedu/taskell/storage/StorageManagerTest.java @@ -1,17 +1,22 @@ -package seedu.address.storage; +package seedu.taskell.storage; 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 seedu.taskell.commons.events.model.TaskManagerChangedEvent; +import seedu.taskell.commons.events.storage.DataSavingExceptionEvent; +import seedu.taskell.model.TaskManager; +import seedu.taskell.model.ReadOnlyTaskManager; +import seedu.taskell.model.UserPrefs; +import seedu.taskell.storage.JsonUserPrefsStorage; +import seedu.taskell.storage.Storage; +import seedu.taskell.storage.StorageManager; +import seedu.taskell.storage.XmlTaskManagerStorage; +import seedu.taskell.testutil.EventsCollector; +import seedu.taskell.testutil.TypicalTestTasks; import java.io.IOException; @@ -54,25 +59,25 @@ public void prefsReadSave() throws Exception { } @Test - public void addressBookReadSave() throws Exception { - AddressBook original = new TypicalTestPersons().getTypicalAddressBook(); - storageManager.saveAddressBook(original); - ReadOnlyAddressBook retrieved = storageManager.readAddressBook().get(); - assertEquals(original, new AddressBook(retrieved)); - //More extensive testing of AddressBook saving/reading is done in XmlAddressBookStorageTest + public void taskManagerReadSave() throws Exception { + TaskManager original = new TypicalTestTasks().getTypicalTaskManager(); + storageManager.saveTaskManager(original); + ReadOnlyTaskManager retrieved = storageManager.readTaskManager().get(); + assertEquals(original, new TaskManager(retrieved)); + //More extensive testing of TaskManager saving/reading is done in XmlTaskManagerStorageTest } @Test - public void getAddressBookFilePath(){ - assertNotNull(storageManager.getAddressBookFilePath()); + public void getTaskManagerFilePath(){ + assertNotNull(storageManager.getTaskManagerFilePath()); } @Test - public void handleAddressBookChangedEvent_exceptionThrown_eventRaised() throws IOException { + public void handleTaskManagerChangedEvent_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")); + Storage storage = new StorageManager(new XmlTaskManagerStorageExceptionThrowingStub("dummy"), new JsonUserPrefsStorage("dummy")); EventsCollector eventCollector = new EventsCollector(); - storage.handleAddressBookChangedEvent(new AddressBookChangedEvent(new AddressBook())); + storage.handleTaskManagerChangedEvent(new TaskManagerChangedEvent(new TaskManager())); assertTrue(eventCollector.get(0) instanceof DataSavingExceptionEvent); } @@ -80,14 +85,14 @@ public void handleAddressBookChangedEvent_exceptionThrown_eventRaised() throws I /** * A Stub class to throw an exception when the save method is called */ - class XmlAddressBookStorageExceptionThrowingStub extends XmlAddressBookStorage{ + class XmlTaskManagerStorageExceptionThrowingStub extends XmlTaskManagerStorage{ - public XmlAddressBookStorageExceptionThrowingStub(String filePath) { + public XmlTaskManagerStorageExceptionThrowingStub(String filePath) { super(filePath); } @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook, String filePath) throws IOException { + public void saveTaskManager(ReadOnlyTaskManager taskManager, String filePath) throws IOException { throw new IOException("dummy exception"); } } diff --git a/src/test/java/seedu/taskell/storage/XmlTaskManagerStorageTest.java b/src/test/java/seedu/taskell/storage/XmlTaskManagerStorageTest.java new file mode 100644 index 000000000000..e745c6be88b4 --- /dev/null +++ b/src/test/java/seedu/taskell/storage/XmlTaskManagerStorageTest.java @@ -0,0 +1,107 @@ +package seedu.taskell.storage; + + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; + +import seedu.taskell.commons.exceptions.DataConversionException; +import seedu.taskell.commons.util.FileUtil; +import seedu.taskell.model.TaskManager; +import seedu.taskell.model.ReadOnlyTaskManager; +import seedu.taskell.model.task.Task; +import seedu.taskell.storage.XmlTaskManagerStorage; +import seedu.taskell.testutil.TypicalTestTasks; + +import java.io.IOException; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +public class XmlTaskManagerStorageTest { + private static String TEST_DATA_FOLDER = FileUtil.getPath("./src/test/data/XmlTaskManagerStorageTest/"); + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Rule + public TemporaryFolder testFolder = new TemporaryFolder(); + + @Test + public void readTaskManager_nullFilePath_assertionFailure() throws Exception { + thrown.expect(AssertionError.class); + readTaskManager(null); + } + + private java.util.Optional readTaskManager(String filePath) throws Exception { + return new XmlTaskManagerStorage(filePath).readTaskManager(addToTestDataPathIfNotNull(filePath)); + } + + private String addToTestDataPathIfNotNull(String prefsFileInTestDataFolder) { + return prefsFileInTestDataFolder != null + ? TEST_DATA_FOLDER + prefsFileInTestDataFolder + : null; + } + + @Test + public void read_missingFile_emptyResult() throws Exception { + assertFalse(readTaskManager("NonExistentFile.xml").isPresent()); + } + + @Test + public void read_notXmlFormat_exceptionThrown() throws Exception { + + thrown.expect(DataConversionException.class); + readTaskManager("NotXmlFormatTaskManager.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 readAndSaveTaskManager_allInOrder_success() throws Exception { + String filePath = testFolder.getRoot().getPath() + "TempTaskManager.xml"; + TypicalTestTasks td = new TypicalTestTasks(); + TaskManager original = td.getTypicalTaskManager(); + XmlTaskManagerStorage xmlTaskManagerStorage = new XmlTaskManagerStorage(filePath); + + //Save in new file and read back + xmlTaskManagerStorage.saveTaskManager(original, filePath); + ReadOnlyTaskManager readBack = xmlTaskManagerStorage.readTaskManager(filePath).get(); + assertEquals(original, new TaskManager(readBack)); + + //Modify data, overwrite exiting file, and read back + original.addTask(new Task(TypicalTestTasks.holdMeeting)); + original.removeTask(new Task(TypicalTestTasks.archivePastEmails)); + xmlTaskManagerStorage.saveTaskManager(original, filePath); + readBack = xmlTaskManagerStorage.readTaskManager(filePath).get(); + assertEquals(original, new TaskManager(readBack)); + + //Save and read without specifying file path + original.addTask(new Task(TypicalTestTasks.inspectWarehouse)); + xmlTaskManagerStorage.saveTaskManager(original); //file path not specified + readBack = xmlTaskManagerStorage.readTaskManager().get(); //file path not specified + assertEquals(original, new TaskManager(readBack)); + + } + + @Test + public void saveTaskManager_nullTaskManager_assertionFailure() throws IOException { + thrown.expect(AssertionError.class); + saveTaskManager(null, "SomeFile.xml"); + } + + private void saveTaskManager(ReadOnlyTaskManager taskManager, String filePath) throws IOException { + new XmlTaskManagerStorage(filePath).saveTaskManager(taskManager, addToTestDataPathIfNotNull(filePath)); + } + + @Test + public void saveTaskManager_nullFilePath_assertionFailure() throws IOException { + thrown.expect(AssertionError.class); + saveTaskManager(new TaskManager(), null); + } + + +} diff --git a/src/test/java/seedu/address/testutil/EventsCollector.java b/src/test/java/seedu/taskell/testutil/EventsCollector.java similarity index 85% rename from src/test/java/seedu/address/testutil/EventsCollector.java rename to src/test/java/seedu/taskell/testutil/EventsCollector.java index c44d6ca6f95a..347d5c32b634 100644 --- a/src/test/java/seedu/address/testutil/EventsCollector.java +++ b/src/test/java/seedu/taskell/testutil/EventsCollector.java @@ -1,8 +1,9 @@ -package seedu.address.testutil; +package seedu.taskell.testutil; import com.google.common.eventbus.Subscribe; -import seedu.address.commons.core.EventsCenter; -import seedu.address.commons.events.BaseEvent; + +import seedu.taskell.commons.core.EventsCenter; +import seedu.taskell.commons.events.BaseEvent; import java.util.ArrayList; import java.util.List; diff --git a/src/test/java/seedu/address/testutil/SerializableTestClass.java b/src/test/java/seedu/taskell/testutil/SerializableTestClass.java similarity index 98% rename from src/test/java/seedu/address/testutil/SerializableTestClass.java rename to src/test/java/seedu/taskell/testutil/SerializableTestClass.java index ef58ef857179..ada0477cfd8e 100644 --- a/src/test/java/seedu/address/testutil/SerializableTestClass.java +++ b/src/test/java/seedu/taskell/testutil/SerializableTestClass.java @@ -1,4 +1,4 @@ -package seedu.address.testutil; +package seedu.taskell.testutil; import java.time.LocalDateTime; import java.util.ArrayList; diff --git a/src/test/java/seedu/taskell/testutil/TaskBuilder.java b/src/test/java/seedu/taskell/testutil/TaskBuilder.java new file mode 100644 index 000000000000..a887fd631fa9 --- /dev/null +++ b/src/test/java/seedu/taskell/testutil/TaskBuilder.java @@ -0,0 +1,69 @@ +package seedu.taskell.testutil; + +import seedu.taskell.commons.exceptions.IllegalValueException; +import seedu.taskell.model.tag.Tag; +import seedu.taskell.model.task.*; + +/** + * + */ +public class TaskBuilder { + + private TestTask task; + + public TaskBuilder() { + this.task = new TestTask(); + } + + public TaskBuilder withDescription(String name) throws IllegalValueException { + this.task.setDescription(new Description(name)); + return this; + } + + public TaskBuilder withTaskType(String taskType) throws IllegalValueException { + this.task.setTaskType(taskType); + return this; + } + + public TaskBuilder withTags(String ... tags) throws IllegalValueException { + for (String tag: tags) { + task.getTags().add(new Tag(tag)); + } + return this; + } + + public TaskBuilder withTaskPriority(String taskPriority) throws IllegalValueException { + this.task.setTaskPriority(new TaskPriority(taskPriority)); + return this; + } + + public TaskBuilder withStartDate(String startDate) throws IllegalValueException { + this.task.setStartDate(new TaskDate(startDate)); + return this; + } + + public TaskBuilder withEndDate(String endDate) throws IllegalValueException { + this.task.setEndDate(new TaskDate(endDate)); + return this; + } + + public TaskBuilder withStartTime(String startTime) throws IllegalValueException { + this.task.setStartTime(new TaskTime(startTime)); + return this; + } + + public TaskBuilder withEndTime(String endTime) throws IllegalValueException { + this.task.setEndTime(new TaskTime(endTime)); + return this; + } + + public TaskBuilder withTaskComplete(String taskStatus) throws IllegalValueException { + this.task.setTaskComplete(new TaskStatus(taskStatus)); + return this; + } + + public TestTask build() { + return this.task; + } + +} diff --git a/src/test/java/seedu/taskell/testutil/TaskManagerBuilder.java b/src/test/java/seedu/taskell/testutil/TaskManagerBuilder.java new file mode 100644 index 000000000000..79db22e1571c --- /dev/null +++ b/src/test/java/seedu/taskell/testutil/TaskManagerBuilder.java @@ -0,0 +1,35 @@ +package seedu.taskell.testutil; + +import seedu.taskell.commons.exceptions.IllegalValueException; +import seedu.taskell.model.TaskManager; +import seedu.taskell.model.tag.Tag; +import seedu.taskell.model.task.Task; +import seedu.taskell.model.task.UniqueTaskList; + +/** + * A utility class to help with building Taskmanager objects. + * Example usage:
+ * {@code TaskManager ab = new TaskManagerBuilder().withTask("John", "Doe").withTag("Friend").build();} + */ +public class TaskManagerBuilder { + + private TaskManager taskManager; + + public TaskManagerBuilder(TaskManager taskManager){ + this.taskManager = taskManager; + } + + public TaskManagerBuilder withTask(Task task) throws UniqueTaskList.DuplicateTaskException { + taskManager.addTask(task); + return this; + } + + public TaskManagerBuilder withTag(String tagName) throws IllegalValueException { + taskManager.addTag(new Tag(tagName)); + return this; + } + + public TaskManager build(){ + return taskManager; + } +} diff --git a/src/test/java/seedu/taskell/testutil/TestTask.java b/src/test/java/seedu/taskell/testutil/TestTask.java new file mode 100644 index 000000000000..bf09b8736d73 --- /dev/null +++ b/src/test/java/seedu/taskell/testutil/TestTask.java @@ -0,0 +1,165 @@ +package seedu.taskell.testutil; + +import seedu.taskell.model.tag.Tag; +import seedu.taskell.model.tag.UniqueTagList; +import seedu.taskell.model.task.*; + +/** + * A mutable task object. For testing only. + */ +public class TestTask implements ReadOnlyTask { + + private Description description; + private String taskType; + private TaskPriority taskPriority; + private TaskTime startTime; + private TaskTime endTime; + private TaskDate startDate; + private TaskDate endDate; + private TaskStatus taskStatus; + private UniqueTagList tags; + + public TestTask() { + tags = new UniqueTagList(); + } + + public TestTask(Description description, String taskType, TaskPriority taskPriority, TaskTime startTime, + TaskTime endTime, TaskDate startDate, TaskDate endDate, UniqueTagList tags){ + this.description = description; + this.taskType = taskType; + this.startDate = startDate; + this.endDate = endDate; + this.endDate = endDate; + this.startTime = startTime; + this.endTime = endTime; + this.taskPriority = taskPriority; + this.tags = new UniqueTagList(tags); + } + + public TestTask(Description description, String taskType, TaskPriority taskPriority, TaskTime startTime, + TaskTime endTime, TaskDate startDate, TaskDate endDate, TaskStatus taskStatus, UniqueTagList tags) { + this.description = description; + this.taskType = taskType; + this.startDate = startDate; + this.endDate = endDate; + this.endDate = endDate; + this.startTime = startTime; + this.endTime = endTime; + this.taskPriority = taskPriority; + this.taskStatus = taskStatus; + this.tags = new UniqueTagList(tags); + } + + public void setDescription(Description description) { + this.description = description; + } + + public void setTaskType(String taskType) { + this.taskType = taskType; + } + + public void setTaskPriority(TaskPriority taskPriority) { + this.taskPriority = taskPriority; + } + + public void setStartTime(TaskTime startTime) { + this.startTime = startTime; + } + + public void setEndTime(TaskTime endTime) { + this.endTime = endTime; + } + + public void setStartDate(TaskDate startDate) { + this.startDate = startDate; + } + + public void setEndDate(TaskDate endDate) { + this.endDate = endDate; + } + + + + + public void setTaskComplete(TaskStatus taskStatus) { + this.taskStatus = taskStatus; + } + + public void setTags(UniqueTagList tags) { + this.tags = tags; + } + + public void setTaskStatus(TaskStatus taskStatus) { + this.taskStatus = taskStatus; + } + + @Override + public Description getDescription() { + return description; + } + + @Override + public String getTaskType() { + return taskType; + } + + @Override + public TaskDate getStartDate() { + return startDate; + } + + @Override + public TaskDate getEndDate() { + return startDate; + } + + @Override + public TaskTime getStartTime() { + return startTime; + } + + @Override + public TaskTime getEndTime() { + return endTime; + } + + @Override + public TaskPriority getTaskPriority() { + return taskPriority; + } + + @Override + public TaskStatus getTaskStatus() { + return taskStatus; + } + + @Override + public UniqueTagList getTags() { + return tags; + } + + @Override + public String toString() { + return getAsText(); + } + + public String getAddCommand() { + StringBuilder sb = new StringBuilder(); + sb.append("add " + this.getDescription().description + " "); + sb.append("from " + this.getStartDate().taskDate + " "); + sb.append("to " + this.getEndDate().taskDate + " "); + sb.append("from " + this.getStartTime().taskTime + " "); + sb.append("to " + this.getEndTime().taskTime + " "); + sb.append(TaskPriority.PREFIX + this.getTaskPriority().taskPriority + " "); + this.getTags().getInternalList().stream().forEach(s -> sb.append(Tag.PREFIX + s.tagName + " ")); + return sb.toString(); + } + + public String getAddFloatingCommand() { + StringBuilder sb = new StringBuilder(); + sb.append("add " + this.getDescription().description + " "); + sb.append(TaskPriority.PREFIX + this.getTaskPriority().taskPriority + " "); + this.getTags().getInternalList().stream().forEach(s -> sb.append(Tag.PREFIX + s.tagName + " ")); + return sb.toString(); + } +} diff --git a/src/test/java/seedu/taskell/testutil/TestUtil.java b/src/test/java/seedu/taskell/testutil/TestUtil.java new file mode 100644 index 000000000000..8d42ddfd5745 --- /dev/null +++ b/src/test/java/seedu/taskell/testutil/TestUtil.java @@ -0,0 +1,420 @@ +package seedu.taskell.testutil; + +import com.google.common.io.Files; +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 org.loadui.testfx.GuiTest; +import org.testfx.api.FxToolkit; + +import seedu.taskell.TestApp; +import seedu.taskell.commons.exceptions.IllegalValueException; +import seedu.taskell.commons.util.FileUtil; +import seedu.taskell.commons.util.XmlUtil; +import seedu.taskell.model.TaskManager; +import seedu.taskell.model.tag.Tag; +import seedu.taskell.model.tag.UniqueTagList; +import seedu.taskell.model.task.*; +import seedu.taskell.storage.XmlSerializableTaskManager; + +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 Task[] sampleTaskData = getSampleTaskData(); + + private static Task[] getSampleTaskData() { + try { + + return new Task[] { + new Task(new Description("Ali Muster"), Task.EVENT_TASK, new TaskDate("1-1-2100"), + new TaskDate("1-12-2100"), new TaskTime("12:30AM"), new TaskTime("12:45AM"), + new TaskPriority("0"), new TaskStatus(TaskStatus.INCOMPLETE), new UniqueTagList()), + new Task(new Description("Boris Mueller"), Task.EVENT_TASK, new TaskDate("1-1-2100"), + new TaskDate("1-12-2100"), new TaskTime("12:30AM"), new TaskTime("12:45AM"), + new TaskPriority("0"), new TaskStatus(TaskStatus.INCOMPLETE), new UniqueTagList()), + new Task(new Description("Carl Kurz"), Task.EVENT_TASK, new TaskDate("1-1-2100"), + new TaskDate("1-12-2100"), new TaskTime("12:30AM"), new TaskTime("12:45AM"), + new TaskPriority("0"), new TaskStatus(TaskStatus.INCOMPLETE), new UniqueTagList()), + new Task(new Description("Daniel Meier"), Task.EVENT_TASK, new TaskDate("1-1-2100"), + new TaskDate("1-12-2100"), new TaskTime("12:30AM"), new TaskTime("12:45AM"), + new TaskPriority("0"), new TaskStatus(TaskStatus.INCOMPLETE), new UniqueTagList()), + new Task(new Description("Elle Meyer"), Task.EVENT_TASK, new TaskDate("1-1-2100"), + new TaskDate("1-12-2100"), new TaskTime("12:30AM"), new TaskTime("12:45AM"), + new TaskPriority("0"), new TaskStatus(TaskStatus.INCOMPLETE), new UniqueTagList()), + new Task(new Description("Fiona Kunz"), Task.EVENT_TASK, new TaskDate("1-1-2100"), + new TaskDate("1-12-2100"), new TaskTime("12:30AM"), new TaskTime("12:45AM"), + new TaskPriority("0"), new TaskStatus(TaskStatus.INCOMPLETE), new UniqueTagList()), + new Task(new Description("George Best"), Task.EVENT_TASK, new TaskDate("1-1-2100"), + new TaskDate("1-12-2100"), new TaskTime("12:30AM"), new TaskTime("12:45AM"), + new TaskPriority("0"), new TaskStatus(TaskStatus.INCOMPLETE), new UniqueTagList()), + new Task(new Description("Hoon Meier"), Task.EVENT_TASK, new TaskDate("1-1-2100"), + new TaskDate("1-12-2100"), new TaskTime("12:30AM"), new TaskTime("12:45AM"), + new TaskPriority("0"), new TaskStatus(TaskStatus.INCOMPLETE), new UniqueTagList()), + new Task(new Description("Ida Mueller"), Task.EVENT_TASK, new TaskDate("1-1-2100"), + new TaskDate("1-12-2100"), new TaskTime("12:30AM"), new TaskTime("12:45AM"), + new TaskPriority("0"), new TaskStatus(TaskStatus.INCOMPLETE), 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 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(generateSampleStorageTaskManager(), 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 TaskManager generateEmptyTaskManager() { + return new TaskManager(new UniqueTaskList(), new UniqueTagList()); + } + + public static XmlSerializableTaskManager generateSampleStorageTaskManager() { + return new XmlSerializableTaskManager(generateEmptyTaskManager()); + } + + /** + * 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")); + } + + // @@author A0142073R + + public static TestTask editTaskDescription(TestTask taskToEdit, String newDescription) + throws IllegalValueException { + TestTask newTask = new TestTask(new Description(newDescription), taskToEdit.getTaskType(), + taskToEdit.getTaskPriority(), taskToEdit.getStartTime(), taskToEdit.getEndTime(), + taskToEdit.getStartDate(), taskToEdit.getEndDate(), taskToEdit.getTaskStatus(), taskToEdit.getTags()); + return newTask; + } + + public static TestTask editTaskStartTime(TestTask taskToEdit, String newStartTime) throws IllegalValueException { + TestTask newTask = new TestTask(taskToEdit.getDescription(), taskToEdit.getTaskType(), + taskToEdit.getTaskPriority(), new TaskTime(newStartTime), taskToEdit.getEndTime(), + taskToEdit.getStartDate(), taskToEdit.getEndDate(), taskToEdit.getTaskStatus(), taskToEdit.getTags()); + return newTask; + } + + public static TestTask editTaskEndTime(TestTask taskToEdit, String newEndTime) throws IllegalValueException { + TestTask newTask = new TestTask(taskToEdit.getDescription(), taskToEdit.getTaskType(), + taskToEdit.getTaskPriority(), taskToEdit.getStartTime(), new TaskTime(newEndTime), + taskToEdit.getStartDate(), taskToEdit.getEndDate(), taskToEdit.getTaskStatus(), taskToEdit.getTags()); + return newTask; + } + + public static TestTask editTaskPriority(TestTask taskToEdit, String newPriority) throws IllegalValueException { + TestTask newTask = new TestTask(taskToEdit.getDescription(), taskToEdit.getTaskType(), + new TaskPriority(newPriority), taskToEdit.getStartTime(), taskToEdit.getEndTime(), + taskToEdit.getStartDate(), taskToEdit.getEndDate(), taskToEdit.getTaskStatus(), taskToEdit.getTags()); + return newTask; + } + // @@author + + 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)); + 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)); + 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); + } + + 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/taskell/testutil/TypicalTestTasks.java b/src/test/java/seedu/taskell/testutil/TypicalTestTasks.java new file mode 100644 index 000000000000..98d0c78dd831 --- /dev/null +++ b/src/test/java/seedu/taskell/testutil/TypicalTestTasks.java @@ -0,0 +1,91 @@ +package seedu.taskell.testutil; + +import seedu.taskell.commons.exceptions.IllegalValueException; +import seedu.taskell.model.TaskManager; +import seedu.taskell.model.task.*; + +//@@author A0139257X +/** + * + */ +public class TypicalTestTasks { + + public static TestTask archivePastEmails, borrowBooks, collectParcel, discardBooks, editPowerpoint, fixbugs, + getNewUniform, holdMeeting, inspectWarehouse, floatingTask_Valid, floatingTask_NonIntuitiveDescription; + + public TypicalTestTasks() { + try { + archivePastEmails = new TaskBuilder().withDescription("Archieve past emails").withTaskType(Task.EVENT_TASK) + .withTaskPriority("1").withStartTime("12:30AM").withEndTime("12:45AM").withStartDate("1-1-2100") + .withEndDate("1-12-2100").withTags("friends").withTaskComplete(TaskStatus.INCOMPLETE).build(); + borrowBooks = new TaskBuilder().withDescription("Borrow books").withTaskType(Task.EVENT_TASK) + .withTaskPriority("1").withStartTime("12:30AM").withEndTime("12:45AM").withStartDate("1-1-2100") + .withEndDate("1-12-2100").withTags("owesMoney", "friends").withTaskComplete(TaskStatus.INCOMPLETE) + .build(); + collectParcel = new TaskBuilder().withDescription("Collect parcel").withTaskType(Task.EVENT_TASK) + .withStartDate("1-1-2100").withEndDate("1-12-2100").withStartTime("12:30AM").withEndTime("12:45AM") + .withTaskPriority("1").withTags("owesMoney").withTaskComplete(TaskStatus.INCOMPLETE).build(); + discardBooks = new TaskBuilder().withDescription("Discard books").withTaskType(Task.EVENT_TASK) + .withStartDate("1-1-2100").withEndDate("1-12-2100").withStartTime("12:30AM").withEndTime("12:45AM") + .withTaskPriority("1").withTaskComplete(TaskStatus.INCOMPLETE).build(); + editPowerpoint = new TaskBuilder().withDescription("Edit powerpoint").withTaskType(Task.EVENT_TASK) + .withStartDate("1-1-2100").withEndDate("1-12-2100").withStartTime("12:30AM").withEndTime("12:45AM") + .withTaskPriority("1").withTaskComplete(TaskStatus.INCOMPLETE).build(); + fixbugs = new TaskBuilder().withDescription("Fix bugs").withTaskType(Task.EVENT_TASK) + .withStartDate("1-1-2100").withEndDate("1-12-2100").withStartTime("12:30AM").withEndTime("12:45AM") + .withTaskPriority("1").withTaskComplete(TaskStatus.INCOMPLETE).build(); + getNewUniform = new TaskBuilder().withDescription("Get new uniform").withTaskType(Task.EVENT_TASK) + .withStartDate("1-1-2100").withEndDate("1-12-2100").withStartTime("12:30AM").withEndTime("12:45AM") + .withTaskPriority("1").withTaskComplete(TaskStatus.INCOMPLETE).build(); + + // Manually added + holdMeeting = new TaskBuilder().withDescription("Hold meeting").withTaskType(Task.EVENT_TASK) + .withStartDate("11-1-2100").withEndDate("1-12-2100").withStartTime("12:30AM").withEndTime("12:45AM") + .withTaskPriority("0").withTaskComplete(TaskStatus.INCOMPLETE).build(); + inspectWarehouse = new TaskBuilder().withDescription("Inspect warehouse").withTaskType(Task.EVENT_TASK) + .withStartDate("11-1-2100").withEndDate("1-12-2100").withStartTime("12:30AM").withEndTime("12:45AM") + .withTaskPriority("0").withTaskComplete(TaskStatus.INCOMPLETE).build(); + + floatingTask_Valid = new TaskBuilder().withDescription("floating task").withTaskType(Task.FLOATING_TASK) + .withStartDate(TaskDate.DEFAULT_DATE).withEndDate(TaskDate.DEFAULT_DATE) + .withStartTime(TaskTime.DEFAULT_START_TIME).withEndTime(TaskTime.DEFAULT_END_TIME) + .withTaskPriority(TaskPriority.DEFAULT_PRIORITY).withTaskComplete(TaskStatus.INCOMPLETE).build(); + floatingTask_NonIntuitiveDescription = new TaskBuilder().withDescription("76@#$5632 on by at on") + .withTaskType(Task.FLOATING_TASK).withStartDate(TaskDate.DEFAULT_DATE) + .withEndDate(TaskDate.DEFAULT_DATE).withStartTime(TaskTime.DEFAULT_START_TIME) + .withEndTime(TaskTime.DEFAULT_END_TIME).withTaskPriority(TaskPriority.DEFAULT_PRIORITY) + .withTaskComplete(TaskStatus.INCOMPLETE).build(); + + } catch (IllegalValueException e) { + e.printStackTrace(); + assert false : "not possible"; + } + } + + public static void loadTaskManagerWithSampleData(TaskManager ab) { + + try { + ab.addTask(new Task(archivePastEmails)); + ab.addTask(new Task(borrowBooks)); + ab.addTask(new Task(collectParcel)); + ab.addTask(new Task(discardBooks)); + ab.addTask(new Task(editPowerpoint)); + ab.addTask(new Task(fixbugs)); + ab.addTask(new Task(getNewUniform)); + } catch (UniqueTaskList.DuplicateTaskException e) { + assert false : "not possible"; + } + } + + public TestTask[] getTypicalTasks() { + return new TestTask[] { archivePastEmails, borrowBooks, collectParcel, discardBooks, editPowerpoint, fixbugs, + getNewUniform }; + } + + // @@author + public TaskManager getTypicalTaskManager() { + TaskManager ab = new TaskManager(); + loadTaskManagerWithSampleData(ab); + return ab; + } +}