diff --git a/doc/UserGuide.md b/doc/UserGuide.md
index 3e4baae95..15276ba5a 100644
--- a/doc/UserGuide.md
+++ b/doc/UserGuide.md
@@ -50,6 +50,19 @@ Examples:
* `find Betsy Tim John`
Returns Any person having names `Betsy`, `Tim`, or `John`
+## Finding all persons containing any keyword in their tag: `find-tag`
+Finds persons whose tags contain any of the given keywords.
+Format: `find-tag KEYWORD [MORE_KEYWORDS]`
+
+> The search is case sensitive, the order of the keywords does not matter, only the tag is searched,
+and persons matching at least one keyword will be returned (i.e. `OR` search).
+
+Examples:
+* `find-tag friend`
+ Returns `friend` but not `Friend`
+* `find-tag friend CCA`
+ Returns Any person having tags `friend` or `CCA`
+
## Deleting a person : `delete`
Deletes the specified person from the address book. Irreversible.
Format: `delete INDEX`
diff --git a/src/seedu/addressbook/commands/EditCommand.java b/src/seedu/addressbook/commands/EditCommand.java
new file mode 100644
index 000000000..aa7623f87
--- /dev/null
+++ b/src/seedu/addressbook/commands/EditCommand.java
@@ -0,0 +1,70 @@
+package seedu.addressbook.commands;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import seedu.addressbook.common.Messages;
+import seedu.addressbook.data.exception.IllegalValueException;
+import seedu.addressbook.data.person.Address;
+import seedu.addressbook.data.person.Email;
+import seedu.addressbook.data.person.Name;
+import seedu.addressbook.data.person.Person;
+import seedu.addressbook.data.person.Phone;
+import seedu.addressbook.data.person.ReadOnlyPerson;
+import seedu.addressbook.data.person.UniquePersonList;
+import seedu.addressbook.data.person.UniquePersonList.PersonNotFoundException;
+import seedu.addressbook.data.tag.Tag;
+import seedu.addressbook.data.tag.UniqueTagList;
+
+public class EditCommand extends Command {
+
+ public static final String COMMAND_WORD = "edit";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + "Edit a person in the address book. "
+ + "Contact details can be marked private by prepending 'p' to the prefix.\n\t"
+ + "Parameters: NAME [p]p/PHONE [p]e/EMAIL [p]a/ADDRESS [t/TAG]...\n\t"
+ + "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 = "This person has been edited: %1$s";
+ public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book";
+
+ private final Person toEdit;
+
+ public EditCommand(String name,
+ String phone, boolean isPhonePrivate,
+ String email, boolean isEmailPrivate,
+ String address, boolean isAddressPrivate,
+ Set tags) throws IllegalValueException {
+ final Set tagSet = new HashSet<>();
+ for (String tagName : tags) {
+ tagSet.add(new Tag(tagName));
+ }
+ this.toEdit= new Person(
+ new Name(name),
+ new Phone(phone, isPhonePrivate),
+ new Email(email, isEmailPrivate),
+ new Address(address, isAddressPrivate),
+ new UniqueTagList(tagSet)
+ );
+ }
+
+ @Override
+ public CommandResult execute() {
+ try {
+ addressBook.addPerson(toEdit);
+ ReadOnlyPerson toRemove = null;
+ for (ReadOnlyPerson person : addressBook.getAllPersons()) {
+ if(person.getName().equals(toEdit.getName()))
+ toRemove = person;
+ }
+ addressBook.removePerson(toRemove);
+ return new CommandResult(String.format(MESSAGE_SUCCESS, toEdit));
+ } catch (UniquePersonList.DuplicatePersonException dpe) {
+ return new CommandResult(MESSAGE_DUPLICATE_PERSON);
+ } catch (PersonNotFoundException pnfe) {
+ return new CommandResult(Messages.MESSAGE_PERSON_NOT_IN_ADDRESSBOOK);
+ }
+ }
+
+}
diff --git a/src/seedu/addressbook/commands/FindTagCommand.java b/src/seedu/addressbook/commands/FindTagCommand.java
new file mode 100644
index 000000000..b8ebd4cac
--- /dev/null
+++ b/src/seedu/addressbook/commands/FindTagCommand.java
@@ -0,0 +1,69 @@
+package seedu.addressbook.commands;
+
+import seedu.addressbook.data.exception.IllegalValueException;
+import seedu.addressbook.data.person.ReadOnlyPerson;
+import seedu.addressbook.data.tag.Tag;
+import seedu.addressbook.data.tag.UniqueTagList;
+
+import java.util.*;
+
+/**
+ * Finds and lists all persons in address book whose tags contains some of the argument keywords.
+ * Keyword matching is case sensitive.
+ */
+public class FindTagCommand extends Command{
+
+ public static final String COMMAND_WORD = "find-tag";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + "Finds all persons whose tags contain some of "
+ + "the specified keywords (case-sensitive) and displays them as a list with index numbers.\n\t"
+ + "Parameters: KEYWORD [MORE_KEYWORDS]...\n\t"
+ + "Example: " + COMMAND_WORD + " friend CCA";
+
+ private final Set keywords;
+
+ public FindTagCommand(Set keywords) {
+ this.keywords = keywords;
+ }
+
+ /**
+ * Returns copy of keywords in this command.
+ */
+ public Set getKeywords() {
+ return new HashSet<>(keywords);
+ }
+
+ @Override
+ public CommandResult execute() {
+ List personsFound;
+ try {
+ personsFound = getPersonsWithTagContainingAllKeyword(keywords);
+ } catch (IllegalValueException e) {
+ return new IncorrectCommand(e.getMessage()).execute();
+ }
+ return new CommandResult(getMessageForPersonListShownSummary(personsFound), personsFound);
+ }
+
+ /**
+ * Retrieve all persons in the address book whose tags contain some of the specified keywords.
+ *
+ * @param keywords for searching
+ * @return list of persons found
+ * @throws IllegalValueException
+ */
+ private List getPersonsWithTagContainingAllKeyword(Set keywords) throws IllegalValueException {
+ final List matchedPersons = new ArrayList<>();
+ for (ReadOnlyPerson person : addressBook.getAllPersons()) {
+ final UniqueTagList tags = person.getTags();
+ for (String k : keywords) {
+ Tag tag = new Tag(k);
+ if (tags.contains(tag)) {
+ matchedPersons.add(person);
+ break;
+ }
+ }
+ }
+ return matchedPersons;
+ }
+
+}
diff --git a/src/seedu/addressbook/commands/HelpCommand.java b/src/seedu/addressbook/commands/HelpCommand.java
index 82530440c..779997cdd 100644
--- a/src/seedu/addressbook/commands/HelpCommand.java
+++ b/src/seedu/addressbook/commands/HelpCommand.java
@@ -14,7 +14,9 @@ public class HelpCommand extends Command {
public static final String MESSAGE_ALL_USAGES = AddCommand.MESSAGE_USAGE
+ "\n" + DeleteCommand.MESSAGE_USAGE
+ "\n" + ClearCommand.MESSAGE_USAGE
+ + "\n" + EditCommand.MESSAGE_USAGE
+ "\n" + FindCommand.MESSAGE_USAGE
+ + "\n" + FindTagCommand.MESSAGE_USAGE
+ "\n" + ListCommand.MESSAGE_USAGE
+ "\n" + ViewCommand.MESSAGE_USAGE
+ "\n" + ViewAllCommand.MESSAGE_USAGE
diff --git a/src/seedu/addressbook/commands/RemoveTag.java b/src/seedu/addressbook/commands/RemoveTag.java
new file mode 100644
index 000000000..3db512680
--- /dev/null
+++ b/src/seedu/addressbook/commands/RemoveTag.java
@@ -0,0 +1,48 @@
+package seedu.addressbook.commands;
+
+import seedu.addressbook.data.exception.IllegalValueException;
+import seedu.addressbook.data.person.ReadOnlyPerson;
+import seedu.addressbook.data.tag.Tag;
+import seedu.addressbook.data.tag.UniqueTagList;
+import seedu.addressbook.data.tag.UniqueTagList.TagNotFoundException;
+
+public class RemoveTag extends Command{
+ public static final String COMMAND_WORD="removetag";
+ public static final String MESSAGE_USAGE=COMMAND_WORD+":\n"+
+ "remove the tag from the address book. "+"Example: "+COMMAND_WORD+
+ "frends";
+ public static final String MESSAGE_SUCCESS="Tag deleted. ";
+ private final Tag toDelete;
+ public RemoveTag(String tagToDelete) throws IllegalValueException{
+ toDelete=new Tag(tagToDelete);
+ }
+ public CommandResult execute(){
+ removePersonWithTag();
+ removeTagFromList();
+ return new CommandResult(MESSAGE_SUCCESS);
+ }
+ private boolean removePersonWithTag(){
+ boolean hasDeleted=false;
+ for(ReadOnlyPerson person: addressBook.getAllPersons()){
+ final UniqueTagList tags=person.getTags();
+ if(tags.contains(toDelete)){
+ try {
+ tags.remove(toDelete);
+ } catch (TagNotFoundException e) {
+ e.printStackTrace();
+ }
+ hasDeleted=true;
+ }
+ }
+ return hasDeleted;
+ }
+ private boolean removeTagFromList(){
+ try {
+ addressBook.removeTag(toDelete);
+ } catch (TagNotFoundException e) {
+ return false;
+ }
+ return true;
+ }
+
+}
diff --git a/src/seedu/addressbook/parser/Parser.java b/src/seedu/addressbook/parser/Parser.java
index 3a8305f28..52c9c7bfe 100644
--- a/src/seedu/addressbook/parser/Parser.java
+++ b/src/seedu/addressbook/parser/Parser.java
@@ -48,8 +48,9 @@ public Parser() {}
*
* @param userInput full user input string
* @return the command based on the user input
+ * @throws IllegalValueException
*/
- public Command parseCommand(String userInput) {
+ 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));
@@ -68,8 +69,14 @@ public Command parseCommand(String userInput) {
case ClearCommand.COMMAND_WORD:
return new ClearCommand();
+ case EditCommand.COMMAND_WORD:
+ return prepareEdit(arguments);
+
case FindCommand.COMMAND_WORD:
return prepareFind(arguments);
+
+ case FindTagCommand.COMMAND_WORD:
+ return prepareFindTag(arguments);
case ListCommand.COMMAND_WORD:
return new ListCommand();
@@ -79,6 +86,9 @@ public Command parseCommand(String userInput) {
case ViewAllCommand.COMMAND_WORD:
return prepareViewAll(arguments);
+
+ case RemoveTag.COMMAND_WORD:
+ return prepareDeleteTag(arguments);
case ExitCommand.COMMAND_WORD:
return new ExitCommand();
@@ -88,6 +98,16 @@ public Command parseCommand(String userInput) {
return new HelpCommand();
}
}
+ private Command prepareDeleteTag(String args){
+ Command deleteTag;
+ try {
+ deleteTag=new RemoveTag(args);
+ return deleteTag;
+ } catch (IllegalValueException e) {
+ return new IncorrectCommand(e.getMessage());
+ }
+
+ }
/**
* Parses arguments in the context of the add person command.
@@ -227,6 +247,56 @@ private Command prepareFind(String args) {
final Set keywordSet = new HashSet<>(Arrays.asList(keywords));
return new FindCommand(keywordSet);
}
+
+ /**
+ * Parses arguments in the context of the find tag command.
+ *
+ * @param args full command args string
+ * @return the prepared command
+ */
+ private Command prepareFindTag(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 FindTagCommand(keywordSet);
+ }
+
+
+ /**
+ * Parses arguments in the context of the add person command.
+ *
+ * @param args full command args string
+ * @return the prepared command
+ */
+ private Command prepareEdit(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, EditCommand.MESSAGE_USAGE));
+ }
+ try {
+ return new EditCommand(
+ matcher.group("name"),
+
+ matcher.group("phone"),
+ isPrivatePrefixPresent(matcher.group("isPhonePrivate")),
+ matcher.group("email"),
+ isPrivatePrefixPresent(matcher.group("isEmailPrivate")),
+
+ matcher.group("address"),
+ isPrivatePrefixPresent(matcher.group("isAddressPrivate")),
+ getTagsFromArgs(matcher.group("tagArguments"))
+ );
+ } catch (IllegalValueException ive) {
+ return new IncorrectCommand(ive.getMessage());
+ }
+ }
}
\ No newline at end of file
diff --git a/test/java/seedu/addressbook/parser/ParserTest.java b/test/java/seedu/addressbook/parser/ParserTest.java
index f01be613c..cd9ab7eb1 100644
--- a/test/java/seedu/addressbook/parser/ParserTest.java
+++ b/test/java/seedu/addressbook/parser/ParserTest.java
@@ -292,6 +292,7 @@ private void parseAndAssertIncorrectWithMessage(String feedbackMessage, String..
* @param input to be parsed
* @param expectedCommandClass expected class of returned command
* @return the parsed command object
+ *
*/
private T parseAndAssertCommandType(String input, Class expectedCommandClass) {
final Command result = parser.parseCommand(input);