Skip to content

Commit

Permalink
Merge pull request nus-cs2103-AY1819S2#24 from yinya998/master
Browse files Browse the repository at this point in the history
update find command and coresponding test file
  • Loading branch information
daDangminh authored Mar 5, 2019
2 parents 1be5676 + 20c5fbf commit bec9734
Show file tree
Hide file tree
Showing 15 changed files with 347 additions and 43 deletions.
20 changes: 12 additions & 8 deletions docs/UserGuide.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -96,23 +96,27 @@ Edits the name of the 2nd person to be `Betsy Crower` and clears all existing ta

=== Locating persons by name: `find`

Finds persons whose names contain any of the given keywords. +
Format: `find KEYWORD [MORE_KEYWORDS]`
Finds persons whose fields contain any of the given keywords. +
Format:
Example1: `find KEYWORD [MORE_KEYWORDS]`
Example2: [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]

****
* The search is case insensitive. e.g `hans` will 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`
* If there is no prefix, all fields will be searched
* Persons matching at least one keyword will be returned (i.e. `OR` search). e.g. `Hans Bo` will return `Hans Gruber`, `Bo Yang`
****

Examples:

* `find John` +
Returns `john` and `John Doe`
* `find Betsy Tim John` +
Returns any person having names `Betsy`, `Tim`, or `John`
* `find John PGP` +
Returns any person having fields with keywords `John` or `PGP`
* `find n/Betsy t/friends` +
Returns the person having name `Betsy` AND tag `friends`
* `find t/friends teammate` +
Returns any person having tag `friends` OR tag `teammate`

=== Deleting a person : `delete`

Expand Down Expand Up @@ -239,7 +243,7 @@ _{explain how the user can enable/disable data encryption}_

Redirect user to external email service on browser to email a specific email address

Format: `email NAME`
Format: `email INDEX`

=== Sorting the name in contact list alphabetically : `sort`

Expand Down
31 changes: 24 additions & 7 deletions src/main/java/seedu/address/logic/commands/FindCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,42 @@

import static java.util.Objects.requireNonNull;

import java.util.function.Predicate;

import seedu.address.commons.core.Messages;
import seedu.address.logic.CommandHistory;
import seedu.address.model.Model;
import seedu.address.model.person.NameContainsKeywordsPredicate;
import seedu.address.model.person.Person;

/**
* Finds and lists all persons in address book whose name contains any of the argument keywords.
* Finds and lists all persons in address book
* whose field(name, address, email, phone) contains any of the argument keywords.
* Keyword matching is case insensitive.
*/
public class FindCommand extends Command {

public static final String COMMAND_WORD = "find";

public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all persons whose names contain any of "
public static final String MESSAGE_USAGE = COMMAND_WORD
+ ": Finds all persons whose field contain any of "
+ "the specified keywords (case-insensitive) and displays them as a list with index numbers.\n"
+ "Parameters: KEYWORD [MORE_KEYWORDS]...\n"
+ "Example: " + COMMAND_WORD + " alice bob charlie";
+ "Parameters: [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]...\n"
+ "Example1: " + COMMAND_WORD + " yinya alex "
+ "\\if there's no prefix, all fields will be searched\n"
+ "Example2: " + COMMAND_WORD + " p/123456 t/teammate\n"
+ "Example3: " + COMMAND_WORD + " t/friends teammate\n";


private final NameContainsKeywordsPredicate predicate;
//private final NameContainsKeywordsPredicate predicate;//todo delete?
public static final String MESSAGE_NO_PARAMETER = "Must provide at least one parameters to find.";
private Predicate<Person> predicate;

public FindCommand(NameContainsKeywordsPredicate predicate) {

public FindCommand(Predicate<Person> predicate) {
this.predicate = predicate;
}


@Override
public CommandResult execute(Model model, CommandHistory history) {
requireNonNull(model);
Expand All @@ -40,4 +52,9 @@ public boolean equals(Object other) {
|| (other instanceof FindCommand // instanceof handles nulls
&& predicate.equals(((FindCommand) other).predicate)); // state check
}

public Predicate<Person> getPredicate() {
return predicate;
}
}

2 changes: 1 addition & 1 deletion src/main/java/seedu/address/logic/parser/CliSyntax.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ public class CliSyntax {
public static final Prefix PREFIX_EMAIL = new Prefix("e/");
public static final Prefix PREFIX_ADDRESS = new Prefix("a/");
public static final Prefix PREFIX_TAG = new Prefix("t/");

public static final Prefix PREFIX_AVATOR = new Prefix("av/");
}
97 changes: 95 additions & 2 deletions src/main/java/seedu/address/logic/parser/FindCommandParser.java
Original file line number Diff line number Diff line change
@@ -1,21 +1,48 @@
package seedu.address.logic.parser;

import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.function.Predicate;
import java.util.stream.Stream;

import seedu.address.logic.commands.FindCommand;
import seedu.address.logic.parser.exceptions.ParseException;
import seedu.address.model.person.AddressContainsKeywordPredicate;
import seedu.address.model.person.EmailContainsKeywordPredicate;
import seedu.address.model.person.NameContainsKeywordsPredicate;
import seedu.address.model.person.Person;
import seedu.address.model.person.PhoneContainsKeywordPredicate;
import seedu.address.model.person.TagsContainsKeywordPredicate;


/**
* Parses input arguments and creates a new FindCommand object
*/
public class FindCommandParser implements Parser<FindCommand> {

/**
* check if there's a prefix in the command
*/
private boolean hasPrefix(String command) {
String[] commands = command.split("\\s+");

return (commands[0].contains(PREFIX_NAME.toString()) || commands[0].contains(PREFIX_EMAIL.toString())
|| commands[0].contains(PREFIX_ADDRESS.toString())
|| commands[0].contains(PREFIX_PHONE.toString())
|| commands[0].contains(PREFIX_TAG.toString()));
}

/**
* Parses the given {@code String} of arguments in the context of the FindCommand
* and returns an FindCommand object for execution.
*
* @throws ParseException if the user input does not conform the expected format
*/
public FindCommand parse(String args) throws ParseException {
Expand All @@ -25,9 +52,75 @@ public FindCommand parse(String args) throws ParseException {
String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE));
}

String[] nameKeywords = trimmedArgs.split("\\s+");
//String[] nameKeywords = trimmedArgs.split("\\s+");
//return new FindCommand(new NameContainsKeywordsPredicate(Arrays.asList(nameKeywords)));

ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(
args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG);
ArrayList<Predicate<Person>> predicates = new ArrayList<>();
Predicate<Person> predicateResult;

// if there's no prefix, find in all fields
if (!hasPrefix(trimmedArgs)) {
String[] splitedKeywords = trimmedArgs.split("\\s+");

predicates.add(new NameContainsKeywordsPredicate(Arrays.asList(splitedKeywords)));
predicates.add(new PhoneContainsKeywordPredicate(Arrays.asList(splitedKeywords)));
predicates.add(new EmailContainsKeywordPredicate(Arrays.asList(splitedKeywords)));
predicates.add(new AddressContainsKeywordPredicate(Arrays.asList(splitedKeywords)));
predicates.add(new TagsContainsKeywordPredicate(Arrays.asList(splitedKeywords)));

Predicate<Person>[] predicatesList = predicates.toArray(new Predicate[predicates.size()]);
predicateResult = Stream.of(predicatesList).reduce(condition -> false, Predicate::or);

return new FindCommand(predicateResult);
}

// create find Command according to the specific prefix
if (argMultimap.getValue(PREFIX_NAME).isPresent()) {
String[] nameList = argMultimap.getValue(PREFIX_NAME).get().split("\\s+");
predicates.add(new NameContainsKeywordsPredicate(Arrays.asList(nameList)));
}

if (argMultimap.getValue(PREFIX_EMAIL).isPresent()) {
String[] emailList = argMultimap.getValue(PREFIX_EMAIL).get().split("\\s+");
predicates.add(new EmailContainsKeywordPredicate(Arrays.asList(emailList)));
}

if (argMultimap.getValue(PREFIX_PHONE).isPresent()) {
String[] phoneList = argMultimap.getValue(PREFIX_PHONE).get().split("\\s+");
predicates.add(new PhoneContainsKeywordPredicate(Arrays.asList(phoneList)));
}

if (argMultimap.getValue(PREFIX_ADDRESS).isPresent()) {
String[] addressList = argMultimap.getValue(PREFIX_ADDRESS).get().split("\\s+");
predicates.add(new AddressContainsKeywordPredicate(Arrays.asList(addressList)));
}

if (argMultimap.getValue(PREFIX_TAG).isPresent()) {
String[] tagList = argMultimap.getValue(PREFIX_TAG).get().split("\\s+");
predicates.add(new TagsContainsKeywordPredicate(Arrays.asList(tagList)));
}


Predicate<Person>[] predicatesList = predicates.toArray(new Predicate[predicates.size()]);
predicateResult = Stream.of(predicatesList).reduce(condition -> true, Predicate::and);

return new FindCommand(predicateResult);


return new FindCommand(new NameContainsKeywordsPredicate(Arrays.asList(nameKeywords)));
}


}











Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package seedu.address.model.person;

import java.util.List;
import java.util.function.Predicate;

import seedu.address.commons.util.StringUtil;

/**
* Tests that a {@code Person}'s {@code Address} matches any of the keywords given.
*/
public class AddressContainsKeywordPredicate implements Predicate<Person> {
private final List<String> keywords;

public AddressContainsKeywordPredicate(List<String> keywords) {
this.keywords = keywords;
}

@Override
public boolean test(Person person) {
return keywords.stream()
.anyMatch(keyword -> StringUtil.containsWordIgnoreCase(person.getAddress().value, keyword));
}

@Override
public boolean equals(Object other) {
return other == this || (other instanceof AddressContainsKeywordPredicate
&& this.keywords.equals(((AddressContainsKeywordPredicate) other).keywords));
}

}

Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package seedu.address.model.person;

import java.util.List;
import java.util.function.Predicate;

import seedu.address.commons.util.StringUtil;

/**
* Tests that a {@code Person}'s {@code Email} matches any of the keywords given.
*/
public class EmailContainsKeywordPredicate implements Predicate<Person> {
private final List<String> keywords;

public EmailContainsKeywordPredicate(List<String> keywords) {
this.keywords = keywords;
}

@Override
public boolean test(Person person) {
return keywords.stream()
.anyMatch(keyword -> StringUtil.containsWordIgnoreCase(person.getEmail().value, keyword));
}

@Override
public boolean equals(Object other) {
return other == this || (other instanceof EmailContainsKeywordPredicate
&& this.keywords.equals(((EmailContainsKeywordPredicate) other).keywords));
}

}

8 changes: 8 additions & 0 deletions src/main/java/seedu/address/model/person/Person.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,14 @@ public Set<Tag> getTags() {
return Collections.unmodifiableSet(tags);
}

public String getTagsAsStringNoBracket() {
StringBuffer sb = new StringBuffer("");
for (Tag tag: tags) {
sb.append(tag.toStringNoBracket() + " ");
}
return sb.toString();
}

/**
* Returns true if both persons of the same name have at least one other identity field that is the same.
* This defines a weaker notion of equality between two persons.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package seedu.address.model.person;

import java.util.List;
import java.util.function.Predicate;

import seedu.address.commons.util.StringUtil;

/**
* Tests that a {@code Person}'s {@code Phone} matches any of the keywords given.
*/
public class PhoneContainsKeywordPredicate implements Predicate<Person> {
private final List<String> keywords;

public PhoneContainsKeywordPredicate(List<String> keywords) {
this.keywords = keywords;
}

@Override
public boolean test(Person person) {
return keywords.stream()
.anyMatch(keyword -> StringUtil.containsWordIgnoreCase(person.getPhone().value, keyword));
}

@Override
public boolean equals(Object other) {
return other == this || (other instanceof PhoneContainsKeywordPredicate
&& this.keywords.equals(((PhoneContainsKeywordPredicate) other).keywords));
}

}

Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package seedu.address.model.person;

import java.util.List;
import java.util.function.Predicate;

import seedu.address.commons.util.StringUtil;

/**
* Tests that a {@code Person}'s {@code Name} matches any of the keywords given.
*/
public class TagsContainsKeywordPredicate implements Predicate<Person> {
private final List<String> keywords;

public TagsContainsKeywordPredicate(List<String> keywords) {
this.keywords = keywords;
}

@Override
public boolean test(Person person) {
return keywords.stream()
.anyMatch(keyword -> StringUtil.containsWordIgnoreCase(person.getTagsAsStringNoBracket(), keyword));
}

@Override
public boolean equals(Object other) {
return other == this || (other instanceof TagsContainsKeywordPredicate // instanceof handles nulls
&& keywords.equals(((TagsContainsKeywordPredicate) other).keywords)); // state check
}

}
5 changes: 5 additions & 0 deletions src/main/java/seedu/address/model/tag/Tag.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public static boolean isValidTagName(String test) {
return test.matches(VALIDATION_REGEX);
}


@Override
public boolean equals(Object other) {
return other == this // short circuit if same object
Expand All @@ -51,4 +52,8 @@ public String toString() {
return '[' + tagName + ']';
}

public String toStringNoBracket() {
return tagName;
}

}
Loading

0 comments on commit bec9734

Please sign in to comment.