Skip to content

Commit

Permalink
Merge pull request #48 from AriellaCallista/branch-SearchFeature
Browse files Browse the repository at this point in the history
Branch search feature
  • Loading branch information
AriellaCallista committed Oct 14, 2023
2 parents c99a9ec + 46d8a36 commit 4a0198e
Show file tree
Hide file tree
Showing 15 changed files with 266 additions and 39 deletions.
37 changes: 28 additions & 9 deletions src/main/java/seedu/address/logic/commands/FindCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,43 @@
import seedu.address.logic.Messages;
import seedu.address.model.Model;
import seedu.address.model.person.NameContainsKeywordsPredicate;
import seedu.address.model.person.Status;
import seedu.address.model.person.StatusContainsKeywordsPredicate;

/**
* Finds and lists all persons in address book whose name 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 COMMAND_WORD = "search";

public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all persons whose names contain any of "
+ "the specified keywords (case-insensitive) and displays them as a list with index numbers.\n"
+ "the name keywords (case-insensitive) and (if specified) whose status contain any of the status"
+ "keywords (case-insensitive) and displays them as a list with index numbers.\n"
+ "Parameters: KEYWORD [MORE_KEYWORDS]...\n"
+ "Example: " + COMMAND_WORD + " alice bob charlie";
+ "Example: " + COMMAND_WORD + " n/alex bernice s/interviewed";

private final NameContainsKeywordsPredicate predicate;
private final NameContainsKeywordsPredicate namePredicate;
private final StatusContainsKeywordsPredicate statusPredicate;

public FindCommand(NameContainsKeywordsPredicate predicate) {
this.predicate = predicate;
/**
* Creates an FindCommand to find the specified {@code Person}
*/
public FindCommand(NameContainsKeywordsPredicate namePredicate, StatusContainsKeywordsPredicate statusPredicate) {
this.namePredicate = namePredicate;
this.statusPredicate = statusPredicate;
}


@Override
public CommandResult execute(Model model) {
requireNonNull(model);
model.updateFilteredPersonList(predicate);
if (!Status.isValidStatus(statusPredicate.toString())) {
model.updateFilteredPersonList(namePredicate);
} else {
model.updateFilteredPersonList(namePredicate, statusPredicate);
}
return new CommandResult(
String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, model.getFilteredPersonList().size()));
}
Expand All @@ -46,13 +59,19 @@ public boolean equals(Object other) {
}

FindCommand otherFindCommand = (FindCommand) other;
return predicate.equals(otherFindCommand.predicate);
if (Status.isValidStatus(statusPredicate.toString())) {
return namePredicate.equals(otherFindCommand.namePredicate)
&& statusPredicate.equals(otherFindCommand.statusPredicate);
}
return namePredicate.equals(otherFindCommand.namePredicate);

}

@Override
public String toString() {
return new ToStringBuilder(this)
.add("predicate", predicate)
.add("name predicate", namePredicate)
.add("status predicate", statusPredicate)
.toString();
}
}
2 changes: 2 additions & 0 deletions src/main/java/seedu/address/logic/parser/CliSyntax.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,7 @@ public class CliSyntax {

public static final Prefix PREFIX_REMARK = new Prefix("r/");
public static final Prefix PREFIX_TAG = new Prefix("t/");
public static final Prefix PREFIX_STATUS = new Prefix("s/");


}
50 changes: 44 additions & 6 deletions src/main/java/seedu/address/logic/parser/FindCommandParser.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
package seedu.address.logic.parser;

import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
import static seedu.address.logic.parser.CliSyntax.PREFIX_STATUS;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

import seedu.address.logic.commands.FindCommand;
import seedu.address.logic.parser.exceptions.ParseException;
import seedu.address.model.person.NameContainsKeywordsPredicate;
import seedu.address.model.person.StatusContainsKeywordsPredicate;




/**
* Parses input arguments and creates a new FindCommand object
Expand All @@ -19,15 +27,45 @@ public class FindCommandParser implements Parser<FindCommand> {
* @throws ParseException if the user input does not conform the expected format
*/
public FindCommand parse(String args) throws ParseException {
String trimmedArgs = args.trim();
if (trimmedArgs.isEmpty()) {
throw new ParseException(
String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE));
ArgumentMultimap argMultimap =
ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_STATUS);

if (!arePrefixesPresent(argMultimap, PREFIX_NAME)
|| !argMultimap.getPreamble().isEmpty()) {
throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE));
}

String[] nameKeywords = trimmedArgs.split("\\s+");
argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_STATUS);

String[] nameKeywords = parseKeywordsList(argMultimap.getAllValues(PREFIX_NAME));
String[] statusKeywords = parseKeywordsList(argMultimap.getAllValues(PREFIX_STATUS));

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

/**
* Parses a list of keywords into an array of strings.
*
* @param keywordsList A list of keywords, where each element may contain multiple words.
* @return An array of strings where each element represents an individual keyword.
*
* The method first converts the list of keywords into a string representation,
* e.g., [Alex, Yeoh] (including square brackets). It then removes the square brackets
* from the string representation, resulting in a cleaned string, e.g., Alex, Yeoh (no square brackets).
* Finally, the cleaned string is split into an array of strings, where each word separated
* by a whitespace or comma is considered a single element.
*
* Example:
* If keywordsList is ["John Doe"], the returned array will be ["John", "Doe"].
*/
private String[] parseKeywordsList(List<String> keywordsList) {
String list = keywordsList.toString();
String cleanedList = list.replaceAll("[\\[\\]]", "");
return cleanedList.split("\\s+");
}

private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) {
return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent());
}
}
18 changes: 18 additions & 0 deletions src/main/java/seedu/address/logic/parser/ParserUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import seedu.address.model.person.Email;
import seedu.address.model.person.Name;
import seedu.address.model.person.Phone;
import seedu.address.model.person.Status;
import seedu.address.model.tag.Tag;

/**
Expand Down Expand Up @@ -121,4 +122,21 @@ public static Set<Tag> parseTags(Collection<String> tags) throws ParseException
}
return tagSet;
}

/**
* Parses {@code Collection<String> status} into a {@code Set<String> of status}.
*/
public static Set<String> parseStatus(Collection<String> statusList) throws ParseException {
requireNonNull(statusList);
final Set<String> statusSet = new HashSet<>();
for (String status : statusList) {
status = status.trim();
if (!Status.isValidStatus(status)) {
throw new ParseException(Status.MESSAGE_CONSTRAINTS);
}
statusSet.add(status);
}
return statusSet;
}

}
6 changes: 6 additions & 0 deletions src/main/java/seedu/address/model/Model.java
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,10 @@ public interface Model {
* @throws NullPointerException if {@code predicate} is null.
*/
void updateFilteredPersonList(Predicate<Person> predicate);

/**
* Updates the filter of the filtered person list to filter by the given {@code predicates}.
* @throws NullPointerException if any of the {@code predicates} is null.
*/
void updateFilteredPersonList(Predicate<Person> predicate1, Predicate<Person> predicate2);
}
7 changes: 7 additions & 0 deletions src/main/java/seedu/address/model/ModelManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,13 @@ public void updateFilteredPersonList(Predicate<Person> predicate) {
filteredPersons.setPredicate(predicate);
}

@Override
public void updateFilteredPersonList(Predicate<Person> predicate1, Predicate<Person> predicate2) {
requireNonNull(predicate1);
requireNonNull(predicate2);;
filteredPersons.setPredicate(person -> predicate1.test(person) && predicate2.test(person));
}

@Override
public boolean equals(Object other) {
if (other == this) {
Expand Down
19 changes: 19 additions & 0 deletions src/main/java/seedu/address/model/person/Status.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
package seedu.address.model.person;

import java.util.Arrays;
import java.util.List;

import seedu.address.commons.util.StringUtil;

/**
* Represents a Status in the Person class
*/
public class Status {
public static final String MESSAGE_CONSTRAINTS = "Status should be either one of the following: 'Preliminary',"
+ "'Interviewed', 'Offered', 'Rejected'";
private StatusTypes statusType;


Expand All @@ -19,6 +26,18 @@ public void setStatusType(StatusTypes newStatus) {
this.statusType = newStatus;
}

/**
* Checks if the provided status is a valid status.
*
* @param status The status to be checked for validity.
* @return {@code true} if the status is valid, {@code false} otherwise.
*/
public static boolean isValidStatus(String status) {
List<String> validStatus = Arrays.asList("Preliminary", "Interviewed", "Rejected", "Offered");
return validStatus.stream()
.anyMatch(keyword -> StringUtil.containsWordIgnoreCase(status, keyword));
}

@Override
public String toString() {
return statusType.toString();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
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 StatusContainsKeywordsPredicate implements Predicate<Person> {
private final List<String> keywords;

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

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

@Override
public boolean equals(Object other) {
if (other == this) {
return true;
}

// instanceof handles nulls
if (!(other instanceof StatusContainsKeywordsPredicate)) {
return false;
}

StatusContainsKeywordsPredicate otherStatusContainsKeywordsPredicate = (StatusContainsKeywordsPredicate) other;
return keywords.equals(otherStatusContainsKeywordsPredicate.keywords);
}

@Override
public String toString() {
return keywords.toString().replaceAll("[\\[\\]]", "");
}
}
1 change: 1 addition & 0 deletions src/main/java/seedu/address/model/person/StatusTypes.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public enum StatusTypes {
this.statusName = statusName;
}


@Override
public String toString() {
return this.statusName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,11 @@ public ObservableList<Person> getFilteredPersonList() {
public void updateFilteredPersonList(Predicate<Person> predicate) {
throw new AssertionError("This method should not be called.");
}

@Override
public void updateFilteredPersonList(Predicate<Person> predicate1, Predicate<Person> predicate2) {
throw new AssertionError("This method should not be called.");
}
}

/**
Expand Down
Loading

0 comments on commit 4a0198e

Please sign in to comment.