Skip to content

Commit

Permalink
Merge pull request #201 from MuhdMusab/enable-multiple-prefix-finding
Browse files Browse the repository at this point in the history
Enable multiple prefix in find command
  • Loading branch information
wongyewjon authored Oct 27, 2022
2 parents f61c836 + 343a1cb commit 16a0b22
Show file tree
Hide file tree
Showing 19 changed files with 467 additions and 173 deletions.
59 changes: 19 additions & 40 deletions src/main/java/seedu/address/logic/commands/FindCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,11 @@
import static seedu.address.logic.parser.CliSyntax.PREFIX_RISKTAG;
import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;

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

import seedu.address.commons.core.Messages;
import seedu.address.model.Model;
import seedu.address.model.person.IncomeContainsKeywordsPredicate;
import seedu.address.model.person.MonthlyContainsKeywordsPredicate;
import seedu.address.model.person.NameContainsKeywordsPredicate;
import seedu.address.model.person.NormalTagContainsKeywordsPredicate;
import seedu.address.model.person.Person;
import seedu.address.model.person.PhoneContainsKeywordsPredicate;
import seedu.address.model.person.PlanTagContainsKeywordsPredicate;
import seedu.address.model.person.RiskTagContainsKeywordsPredicate;
import seedu.address.model.person.FindPredicate;

/**
* Finds and lists all persons in address book whose name contains any of the argument keywords.
Expand All @@ -44,47 +37,33 @@ public class FindCommand extends Command {
+ COMMAND_WORD + " " + PREFIX_PHONE.getPrefix() + " 912345678"
+ COMMAND_WORD + " " + PREFIX_INCOME.getPrefix() + " >1000";

private final Predicate<Person> predicate;
private final List<FindPredicate> predicates;

public FindCommand(NameContainsKeywordsPredicate predicate) {
this.predicate = predicate;
}

public FindCommand(RiskTagContainsKeywordsPredicate predicate) {
this.predicate = predicate;
}

public FindCommand(NormalTagContainsKeywordsPredicate predicate) {
this.predicate = predicate;
}

public FindCommand(PlanTagContainsKeywordsPredicate predicate) {
this.predicate = predicate;
}

public FindCommand(PhoneContainsKeywordsPredicate predicate) {
this.predicate = predicate;
}

public FindCommand(IncomeContainsKeywordsPredicate predicate) {
this.predicate = predicate;
}
public FindCommand(MonthlyContainsKeywordsPredicate predicate) {
this.predicate = predicate;
}
public FindCommand(List<FindPredicate> predicates) {
this.predicates = predicates;
};

@Override
public CommandResult execute(Model model) {
requireNonNull(model);
model.updateFilteredPersonList(predicate);
model.updateFilteredPersonList(predicates);
return new CommandResult(
String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, model.getFilteredPersonList().size()));
}

@Override
public boolean equals(Object other) {
return other == this // short circuit if same object
|| (other instanceof FindCommand // instanceof handles nulls
&& predicate.equals(((FindCommand) other).predicate)); // state check
// short circuit if same object
if (other == this) {
return true;
}

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

// state check
return this.predicates.equals(((FindCommand) other).predicates);
}
}
23 changes: 23 additions & 0 deletions src/main/java/seedu/address/logic/parser/ArgumentMultimap.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package seedu.address.logic.parser;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
Expand Down Expand Up @@ -52,6 +54,19 @@ public List<String> getAllValues(Prefix prefix) {
return new ArrayList<>(argMultimap.get(prefix));
}

/**
* Returns all values of {@code prefix} separated by spaces.
* If the prefix does not exist or has no values, this will return an empty list.
* Modifying the returned list will not affect the underlying data structure of the ArgumentMultimap.
*/
public List<String> getAllValuesSeparatedByRegex(Prefix prefix, String regex) {
if (!argMultimap.containsKey(prefix)) {
return new ArrayList<>();
}
String value = getValue(prefix).get();
return Arrays.stream(value.split(regex)).collect(Collectors.toList());
}

/**
* Returns the preamble (text before the first valid prefix). Trims any leading/trailing spaces.
*/
Expand All @@ -74,4 +89,12 @@ public static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Pref
public static boolean anyPrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) {
return Stream.of(prefixes).anyMatch(prefix -> argumentMultimap.getValue(prefix).isPresent());
}

/**
* Returns true if none of the prefixes are present {@code Optional} values in the given
* {@code ArgumentMultimap}.
*/
public static boolean noPrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) {
return Stream.of(prefixes).noneMatch(prefix -> argumentMultimap.getValue(prefix).isPresent());
}
}
102 changes: 70 additions & 32 deletions src/main/java/seedu/address/logic/parser/FindCommandParser.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package seedu.address.logic.parser;

import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
import static seedu.address.logic.parser.ArgumentMultimap.arePrefixesPresent;
import static seedu.address.logic.parser.ArgumentMultimap.noPrefixesPresent;
import static seedu.address.logic.parser.CliSyntax.PREFIX_APPOINTMENT_DATE;
import static seedu.address.logic.parser.CliSyntax.PREFIX_APPOINTMENT_LOCATION;
import static seedu.address.logic.parser.CliSyntax.PREFIX_INCOME;
import static seedu.address.logic.parser.CliSyntax.PREFIX_MONTHLY;
import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
Expand All @@ -9,67 +13,101 @@
import static seedu.address.logic.parser.CliSyntax.PREFIX_RISKTAG;
import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;

import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import seedu.address.logic.commands.FindCommand;
import seedu.address.logic.parser.exceptions.ParseException;
import seedu.address.model.person.FindPredicate;
import seedu.address.model.person.IncomeContainsKeywordsPredicate;
import seedu.address.model.person.MonthlyContainsKeywordsPredicate;
import seedu.address.model.person.Name;
import seedu.address.model.person.NameContainsKeywordsPredicate;
import seedu.address.model.person.NormalTagContainsKeywordsPredicate;
import seedu.address.model.person.Phone;
import seedu.address.model.person.PhoneContainsKeywordsPredicate;
import seedu.address.model.person.PlanTagContainsKeywordsPredicate;
import seedu.address.model.person.RiskTagContainsKeywordsPredicate;
import seedu.address.model.tag.NormalTag;
import seedu.address.model.tag.PlanTag;
import seedu.address.model.tag.RiskTag;

/**
* Parses input arguments and creates a new FindCommand object
*/
public class FindCommandParser implements Parser<FindCommand> {
private static final String SPACE_REGEX = "\\s+";
private static final String PLAN_REGEX = "(?<!\\G\\S+)\\s";

/**
* Parses the given {@code String} of arguments in the context of the FindCommand
* and returns a FindCommand object for execution.
* @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()) {
if (args.trim().isEmpty()) {
throw new ParseException(
String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE));
}
List<FindPredicate> predicates = new ArrayList<>();
ArgumentMultimap argMultimap =
ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_RISKTAG,
PREFIX_PLANTAG, PREFIX_TAG, PREFIX_INCOME, PREFIX_MONTHLY);

// 2 because all except PREFIX_PLANTAG has a prefix of length 2
String tokenizedArgs = trimmedArgs.substring(2);
String[] keywords = tokenizedArgs.split("\\s+");
if (noPrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_PHONE, PREFIX_RISKTAG,
PREFIX_PLANTAG, PREFIX_TAG, PREFIX_INCOME, PREFIX_MONTHLY)
|| !argMultimap.getPreamble().isEmpty()
|| arePrefixesPresent(argMultimap, PREFIX_APPOINTMENT_DATE, PREFIX_APPOINTMENT_LOCATION)) {
throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE));
}

if (trimmedArgs.startsWith(PREFIX_RISKTAG.getPrefix())) {
checkIfRiskTag(keywords);
return new FindCommand(new RiskTagContainsKeywordsPredicate(Arrays.asList(keywords)));
} else if (trimmedArgs.startsWith(PREFIX_TAG.getPrefix())) {
return new FindCommand(new NormalTagContainsKeywordsPredicate(Arrays.asList(keywords)));
} else if (trimmedArgs.startsWith(PREFIX_NAME.getPrefix())) {
return new FindCommand(new NameContainsKeywordsPredicate(Arrays.asList(keywords)));
} else if (trimmedArgs.startsWith(PREFIX_PHONE.getPrefix())) {
return new FindCommand(new PhoneContainsKeywordsPredicate(Arrays.asList(keywords)));
} else if (trimmedArgs.startsWith(PREFIX_INCOME.getPrefix())) {
String predicate = tokenizedArgs.substring(1, 2);
String[] income = tokenizedArgs.substring(2).split("\\s+");
return new FindCommand(new IncomeContainsKeywordsPredicate(Arrays.asList(income), predicate));
} else if (trimmedArgs.startsWith(PREFIX_MONTHLY.getPrefix())) {
String predicate = tokenizedArgs.substring(1, 2);
String[] monthly = tokenizedArgs.substring(2).split("\\s+");
return new FindCommand(new MonthlyContainsKeywordsPredicate(Arrays.asList(monthly), predicate));
} else if (trimmedArgs.startsWith(PREFIX_PLANTAG.getPrefix())) {
String planTag = trimmedArgs.substring(3);
// since all planTag has a space and ends with Plan, we split the input every second space.
// planTag - Savings Plan.
String[] tags = planTag.split("(?<=\\s\\S{1,100})\\s");
return new FindCommand(new PlanTagContainsKeywordsPredicate(Arrays.asList(tags)));
} else {
throw new ParseException(
String.format(MESSAGE_INVALID_COMMAND_FORMAT, "Please enter a prefix before the KEYWORDs."));
if (argMultimap.getValue(PREFIX_NAME).isPresent()) {
List<Name> names = ParserUtil.parseAllSpaceSeparatedNames(argMultimap
.getAllValuesSeparatedByRegex(PREFIX_NAME, SPACE_REGEX));
predicates.add(new NameContainsKeywordsPredicate(names.stream()
.map(x -> x.toString()).collect(Collectors.toList())));
}
if (argMultimap.getValue(PREFIX_PHONE).isPresent()) {
List<Phone> phones = ParserUtil.parseAllSpaceSeparatedPhone(argMultimap
.getAllValuesSeparatedByRegex(PREFIX_PHONE, SPACE_REGEX));
predicates.add(new PhoneContainsKeywordsPredicate(phones.stream()
.map(x -> x.toString()).collect(Collectors.toList())));
}
if (argMultimap.getValue(PREFIX_RISKTAG).isPresent()) {
List<RiskTag> riskTags = ParserUtil.parseAllSpaceSeparatedRiskTag(argMultimap
.getAllValuesSeparatedByRegex(PREFIX_RISKTAG, SPACE_REGEX));
predicates.add(new RiskTagContainsKeywordsPredicate(riskTags.stream()
.map(x -> x.tagName).collect(Collectors.toList())));
}
if (argMultimap.getValue(PREFIX_PLANTAG).isPresent()) {
List<PlanTag> planTags = ParserUtil.parseAllSpaceSeparatedPlanTags(argMultimap
.getAllValuesSeparatedByRegex(PREFIX_PLANTAG, PLAN_REGEX));
predicates.add(new PlanTagContainsKeywordsPredicate(planTags.stream()
.map(x -> x.tagName).collect(Collectors.toList())));
}
if (argMultimap.getValue(PREFIX_TAG).isPresent()) {
List<NormalTag> normalTags = ParserUtil.parseAllSpaceSeparatedNormalTags(argMultimap
.getAllValuesSeparatedByRegex(PREFIX_TAG, SPACE_REGEX));
predicates.add(new NormalTagContainsKeywordsPredicate(normalTags.stream()
.map(x -> x.tagName).collect(Collectors.toList())));
}
if (argMultimap.getValue(PREFIX_INCOME).isPresent()) {
List<String> incomeLevels = ParserUtil.parseMonetaryValues(argMultimap
.getAllValuesSeparatedByRegex(PREFIX_INCOME, SPACE_REGEX));
String equalityPredicate = incomeLevels.get(0).substring(0, 1);
incomeLevels.set(0, incomeLevels.get(0).substring(1));
predicates.add(new IncomeContainsKeywordsPredicate(incomeLevels, equalityPredicate));
}
if (argMultimap.getValue(PREFIX_MONTHLY).isPresent()) {
List<String> monthlySavings = ParserUtil.parseMonetaryValues(argMultimap
.getAllValuesSeparatedByRegex(PREFIX_MONTHLY, SPACE_REGEX));
String equalityPredicate = monthlySavings.get(0).substring(0, 1);
monthlySavings.set(0, monthlySavings.get(0).substring(1));
predicates.add(new MonthlyContainsKeywordsPredicate(monthlySavings, equalityPredicate));
}

return new FindCommand(predicates);
}

private void checkIfRiskTag(String[] tags) throws ParseException {
Expand Down
80 changes: 80 additions & 0 deletions src/main/java/seedu/address/logic/parser/ParserUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
import static seedu.address.model.person.Person.MAXIMUM_NUM_OF_APPOINTMENTS;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import seedu.address.commons.core.index.Index;
Expand Down Expand Up @@ -93,6 +95,18 @@ public static Name parseName(String name) throws ParseException {
return new Name(trimmedName);
}

/**
* Parses {@code Collection<String> names} into a {@code List<Name>}.
*/
public static List<Name> parseAllSpaceSeparatedNames(Collection<String> names) throws ParseException {
requireNonNull(names);
final List<Name> nameList = new ArrayList<>();
for (String name: names) {
nameList.add(parseName(name));
}
return nameList;
}

/**
* Parses a {@code String phone} into a {@code Phone}.
* Leading and trailing whitespaces will be trimmed.
Expand All @@ -108,6 +122,18 @@ public static Phone parsePhone(String phone) throws ParseException {
return new Phone(trimmedPhone);
}

/**
* Parses {@code Collection<String> phones} into a {@code List<Phone>}.
*/
public static List<Phone> parseAllSpaceSeparatedPhone(Collection<String> phones) throws ParseException {
requireNonNull(phones);
final List<Phone> phoneList = new ArrayList<>();
for (String phone: phones) {
phoneList.add(parsePhone(phone));
}
return phoneList;
}

/**
* Parses a {@code String address} into an {@code Address}.
* Leading and trailing whitespaces will be trimmed.
Expand Down Expand Up @@ -230,6 +256,18 @@ public static RiskTag parseRiskTag(String riskTag) throws ParseException {
return new RiskTag(trimmedRiskTag);
}

/**
* Parses {@code Collection<String> riskTags} into a {@code List<RiskTag>}.
*/
public static List<RiskTag> parseAllSpaceSeparatedRiskTag(Collection<String> riskTags) throws ParseException {
requireNonNull(riskTags);
final List<RiskTag> riskTagList = new ArrayList<>();
for (String riskTag: riskTags) {
riskTagList.add(parseRiskTag(riskTag.toUpperCase()));
}
return riskTagList;
}

/**
* Parses a {@code String planTag} into an {@code PlanTag}.
* Leading and trailing whitespaces will be trimmed.
Expand All @@ -243,6 +281,18 @@ public static PlanTag parsePlanTag(String planTag) throws ParseException {
return new PlanTag(trimmedPlanTag);
}

/**
* Parses {@code Collection<String> names} into a {@code List<Name>}.
*/
public static List<PlanTag> parseAllSpaceSeparatedPlanTags(Collection<String> planTags) throws ParseException {
requireNonNull(planTags);
final List<PlanTag> planTagList = new ArrayList<>();
for (String planTag: planTags) {
planTagList.add(parsePlanTag(planTag));
}
return planTagList;
}

/**
* Parses a {@code String tag} into a {@code Tag}.
* Leading and trailing whitespaces will be trimmed.
Expand All @@ -269,4 +319,34 @@ public static Set<NormalTag> parseTags(Collection<String> tags) throws ParseExce
}
return tagSet;
}

/**
* Parses {@code Collection<String> normalTags} into a {@code List<NormalTag>}.
*/
public static List<NormalTag> parseAllSpaceSeparatedNormalTags(Collection<String> normalTags)
throws ParseException {
requireNonNull(normalTags);
final List<NormalTag> normalTagList = new ArrayList<>();
for (String normalTag: normalTags) {
normalTagList.add(parseTag(normalTag));
}
return normalTagList;
}

/**
* Parses {@code Collection<String> monetaryValues} into a {@code List<String>}.
*/
public static List<String> parseMonetaryValues(Collection<String> monetaryValues)
throws ParseException {
requireNonNull(monetaryValues);
final List<String> incomeLevelList = new ArrayList<>(monetaryValues);
for (int i = 0; i < incomeLevelList.size(); i++) {
if (i == 0) {
parseIncomeLevel("$" + incomeLevelList.get(i).substring(1));
} else {
parseIncomeLevel("$" + incomeLevelList.get(i));
}
}
return incomeLevelList;
}
}
Loading

0 comments on commit 16a0b22

Please sign in to comment.