Skip to content

Commit

Permalink
Merge branch 'master' into branch-tagging
Browse files Browse the repository at this point in the history
Resolve merge conflicts.
  • Loading branch information
HanB1n committed Oct 10, 2024
2 parents fb3b69d + 0c7383d commit 323697c
Show file tree
Hide file tree
Showing 34 changed files with 628 additions and 137 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ more organised and efficient.
* For the detailed documentation of this project, see the **[WedLinker Product Website](https://ay2425s1-cs2103t-f15-4.github.io/tp/)**.
* This project is based on the AddressBook-Level3 project created by the [SE-EDU initiative](https://se-education.org).
* This project is a **part of the se-education.org** initiative. If you would like to contribute code to this project,
* see [se-education.org](https://se-education.org/#contributing-to-se-edu) for more info.
see [se-education.org](https://se-education.org/#contributing-to-se-edu) for more info.
22 changes: 22 additions & 0 deletions src/main/java/seedu/address/commons/util/StringUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,28 @@ public static boolean containsWordIgnoreCase(String sentence, String word) {
.anyMatch(preppedWord::equalsIgnoreCase);
}

/**
* Returns true if the {@code sentence} contains the {@code word}.
* Ignores case, and only a partial match is required.
* <br>examples:<pre>
* containsPartialWordIgnoreCase("ABc def", "abc") == true
* containsPartialWordIgnoreCase("ABc def", "DEF") == true
* containsPartialWordIgnoreCase("ABc def", "AB") == true
* containsPartialWordIgnoreCase("ABc def", "cd") == false // no partial match with a word
* </pre>
* @param sentence cannot be null
* @param word cannot be null, cannot be empty
*/
public static boolean containsPartialWordIgnoreCase(String sentence, String word) {
requireNonNull(sentence);
requireNonNull(word);

String preppedSentence = sentence.toLowerCase();
String preppedWord = word.trim().toLowerCase();
checkArgument(!preppedWord.isEmpty(), "Word parameter cannot be empty");
return preppedSentence.contains(preppedWord);
}

/**
* Returns a detailed message of the t, including the stack trace.
*/
Expand Down
58 changes: 58 additions & 0 deletions src/main/java/seedu/address/logic/commands/FilterCommand.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package seedu.address.logic.commands;

import static java.util.Objects.requireNonNull;

import seedu.address.commons.util.ToStringBuilder;
import seedu.address.logic.Messages;
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.model.Model;
import seedu.address.model.person.TagContainsKeywordsPredicate;

/**
* Finds and lists all persons in address book whose tag contains any of the argument keywords.
* Keyword matching is case-insensitive.
*/
public class FilterCommand extends Command {

public static final String COMMAND_WORD = "filter";

public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all persons whose tags 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 + " florist";

private final TagContainsKeywordsPredicate predicate;

public FilterCommand(TagContainsKeywordsPredicate predicate) {
this.predicate = predicate;
}

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

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

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

return predicate.equals(otherFindCommand.predicate);
}

@Override
public String toString() {
return new ToStringBuilder(this)
.add("predicate", predicate)
.toString();
}
}
15 changes: 11 additions & 4 deletions src/main/java/seedu/address/logic/commands/FindCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@
import static java.util.Objects.requireNonNull;

import seedu.address.commons.util.ToStringBuilder;
import seedu.address.logic.Messages;
import seedu.address.model.Model;
import seedu.address.model.person.NameContainsKeywordsPredicate;

/**
* Finds and lists all persons in address book whose name contains any of the argument keywords.
* Keyword matching is case insensitive.
* Keyword matching is case-insensitive and allows partial matching.
*/
public class FindCommand extends Command {

Expand All @@ -20,6 +19,10 @@ public class FindCommand extends Command {
+ "Parameters: KEYWORD [MORE_KEYWORDS]...\n"
+ "Example: " + COMMAND_WORD + " alice bob charlie";

public static final String MESSAGE_FIND_PERSON_SUCCESS = "Search for \"%s\" was successful. Showing results:";

public static final String MESSAGE_FIND_PERSON_UNSUCCESSFUL = "No contacts found.";

private final NameContainsKeywordsPredicate predicate;

public FindCommand(NameContainsKeywordsPredicate predicate) {
Expand All @@ -30,8 +33,12 @@ public FindCommand(NameContainsKeywordsPredicate predicate) {
public CommandResult execute(Model model) {
requireNonNull(model);
model.updateFilteredPersonList(predicate);
return new CommandResult(
String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, model.getFilteredPersonList().size()));

if (!model.getFilteredPersonList().isEmpty()) {
return new CommandResult(String.format(MESSAGE_FIND_PERSON_SUCCESS, predicate.getDisplayString()));
} else {
return new CommandResult(MESSAGE_FIND_PERSON_UNSUCCESSFUL);
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,16 @@ public AddCommand parse(String args) throws ParseException {
ArgumentMultimap argMultimap =
ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG);

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

argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS);
Name name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get());
Phone phone = ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get());
Email email = ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get());
Address address = ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get());
Address address = ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).orElse(""));
Phone phone = ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).orElse(""));
Email email = ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).orElse(""));
Set<Tag> tagList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG));

Person person = new Person(name, phone, email, address, tagList);
Expand Down
48 changes: 15 additions & 33 deletions src/main/java/seedu/address/logic/parser/AddressBookParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import seedu.address.logic.commands.DeleteTagCommand;
import seedu.address.logic.commands.EditCommand;
import seedu.address.logic.commands.ExitCommand;
import seedu.address.logic.commands.FilterCommand;
import seedu.address.logic.commands.FindCommand;
import seedu.address.logic.commands.HelpCommand;
import seedu.address.logic.commands.ListCommand;
Expand Down Expand Up @@ -53,42 +54,23 @@ public Command parseCommand(String userInput) throws ParseException {
// Lower level log messages are used sparingly to minimize noise in the code.
logger.fine("Command word: " + commandWord + "; Arguments: " + arguments);

switch (commandWord) {

case AddCommand.COMMAND_WORD:
return new AddCommandParser().parse(arguments);

case EditCommand.COMMAND_WORD:
return new EditCommandParser().parse(arguments);

case DeleteCommand.COMMAND_WORD:
return new DeleteCommandParser().parse(arguments);

case ClearCommand.COMMAND_WORD:
return new ClearCommand();

case FindCommand.COMMAND_WORD:
return new FindCommandParser().parse(arguments);

case ListCommand.COMMAND_WORD:
return new ListCommand();

case ExitCommand.COMMAND_WORD:
return new ExitCommand();

case HelpCommand.COMMAND_WORD:
return new HelpCommand();

case CreateTagCommand.COMMAND_WORD:
return new CreateTagCommandParser().parse(arguments);

case DeleteTagCommand.COMMAND_WORD:
return new DeleteTagCommandParser().parse(arguments);

default:
return switch (commandWord) {
case AddCommand.COMMAND_WORD -> new AddCommandParser().parse(arguments);
case EditCommand.COMMAND_WORD -> new EditCommandParser().parse(arguments);
case DeleteCommand.COMMAND_WORD -> new DeleteCommandParser().parse(arguments);
case ClearCommand.COMMAND_WORD -> new ClearCommand();
case FindCommand.COMMAND_WORD -> new FindCommandParser().parse(arguments);
case FilterCommand.COMMAND_WORD -> new FilterCommandParser().parse(arguments);
case ListCommand.COMMAND_WORD -> new ListCommand();
case ExitCommand.COMMAND_WORD -> new ExitCommand();
case HelpCommand.COMMAND_WORD -> new HelpCommand();
case CreateTagCommand.COMMAND_WORD -> new CreateTagCommandParser().parse(arguments);
case DeleteTagCommand.COMMAND_WORD -> new DeleteTagCommandParser().parse(arguments);

Check warning on line 68 in src/main/java/seedu/address/logic/parser/AddressBookParser.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/logic/parser/AddressBookParser.java#L67-L68

Added lines #L67 - L68 were not covered by tests
default -> {
logger.finer("This user input caused a ParseException: " + userInput);
throw new ParseException(MESSAGE_UNKNOWN_COMMAND);
}
};
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,10 @@ public EditCommand parse(String args) throws ParseException {
editPersonDescriptor.setPhone(ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get()));
}
if (argMultimap.getValue(PREFIX_EMAIL).isPresent()) {
editPersonDescriptor.setEmail(ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get()));
editPersonDescriptor.setEmail(ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).orElse("")));
}
if (argMultimap.getValue(PREFIX_ADDRESS).isPresent()) {
editPersonDescriptor.setAddress(ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get()));
editPersonDescriptor.setAddress(ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).orElse("")));
}
parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(editPersonDescriptor::setTags);

Expand Down
34 changes: 34 additions & 0 deletions src/main/java/seedu/address/logic/parser/FilterCommandParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package seedu.address.logic.parser;

import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;

import java.util.Arrays;

import seedu.address.logic.commands.FilterCommand;
import seedu.address.logic.parser.exceptions.ParseException;
import seedu.address.model.person.TagContainsKeywordsPredicate;


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

/**
* Parses the given {@code String} of arguments in the context of the FilterCommand
* and returns a FilterCommand object for execution.
* @throws ParseException if the user input does not conform the expected format
*/
public FilterCommand parse(String args) throws ParseException {
String trimmedArgs = args.trim();
if (trimmedArgs.isEmpty()) {
throw new ParseException(
String.format(MESSAGE_INVALID_COMMAND_FORMAT, FilterCommand.MESSAGE_USAGE));
}

String[] nameKeywords = trimmedArgs.split("\\s+");

return new FilterCommand(new TagContainsKeywordsPredicate(Arrays.asList(nameKeywords)));
}

}
3 changes: 0 additions & 3 deletions src/main/java/seedu/address/logic/parser/ParserUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,6 @@ public static Phone parsePhone(String phone) throws ParseException {
public static Address parseAddress(String address) throws ParseException {
requireNonNull(address);
String trimmedAddress = address.trim();
if (!Address.isValidAddress(trimmedAddress)) {
throw new ParseException(Address.MESSAGE_CONSTRAINTS);
}
return new Address(trimmedAddress);
}

Expand Down
10 changes: 8 additions & 2 deletions src/main/java/seedu/address/model/Model.java
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,16 @@ public interface Model {
ObservableList<Person> getFilteredPersonList();

/**
* Updates the filter of the filtered person list to filter by the given {@code predicate}.
* Updates the filter of the filtered person list to filter by the given {@code personPredicate}.
* @throws NullPointerException if {@code predicate} is null.
*/
void updateFilteredPersonList(Predicate<Person> predicate);
void updateFilteredPersonList(Predicate<Person> personPredicate);

/**
* Updates the filter of the filtered person list to filter by the given {@code tagPredicate}.
* @throws NullPointerException if {@code predicate} is null.
*/
void updateFilteredPersonListByTag(Predicate<Tag> tagPredicate);

/**
* Returns true if a tag with the same name as (@code tag} exists in the addres book.
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/seedu/address/model/ModelManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,12 @@ public void updateFilteredPersonList(Predicate<Person> predicate) {
filteredPersons.setPredicate(predicate);
}

@Override
public void updateFilteredPersonListByTag(Predicate<Tag> predicate) {
requireNonNull(predicate);
filteredPersons.setPredicate(person -> person.getTags().stream().anyMatch(predicate));
}

@Override
public ObservableList<Tag> getFilteredTagList() {
return filteredTags;
Expand Down
5 changes: 2 additions & 3 deletions src/main/java/seedu/address/model/person/Address.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,9 @@ public class Address {
public static final String MESSAGE_CONSTRAINTS = "Addresses can take any values, and it should not be blank";

/*
* The first character of the address must not be a whitespace,
* otherwise " " (a blank string) becomes a valid input.
* Address is optional, so validation regex accepts blank address strings
*/
public static final String VALIDATION_REGEX = "[^\\s].*";
public static final String VALIDATION_REGEX = ".*";

public final String value;

Expand Down
2 changes: 1 addition & 1 deletion src/main/java/seedu/address/model/person/Email.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public class Email {
+ "(-" + ALPHANUMERIC_NO_UNDERSCORE + ")*";
private static final String DOMAIN_LAST_PART_REGEX = "(" + DOMAIN_PART_REGEX + "){2,}$"; // At least two chars
private static final String DOMAIN_REGEX = "(" + DOMAIN_PART_REGEX + "\\.)*" + DOMAIN_LAST_PART_REGEX;
public static final String VALIDATION_REGEX = LOCAL_PART_REGEX + "@" + DOMAIN_REGEX;
public static final String VALIDATION_REGEX = "(" + LOCAL_PART_REGEX + "@" + DOMAIN_REGEX + ")?\\s*";

public final String value;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public NameContainsKeywordsPredicate(List<String> keywords) {
@Override
public boolean test(Person person) {
return keywords.stream()
.anyMatch(keyword -> StringUtil.containsWordIgnoreCase(person.getName().fullName, keyword));
.anyMatch(keyword -> StringUtil.containsPartialWordIgnoreCase(person.getName().fullName, keyword));
}

@Override
Expand All @@ -41,4 +41,8 @@ public boolean equals(Object other) {
public String toString() {
return new ToStringBuilder(this).add("keywords", keywords).toString();
}

public String getDisplayString() {
return String.join(", ", keywords);
}
}
4 changes: 3 additions & 1 deletion src/main/java/seedu/address/model/person/Phone.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ public class Phone {

public static final String MESSAGE_CONSTRAINTS =
"Phone numbers should only contain numbers, and it should be at least 3 digits long";
public static final String VALIDATION_REGEX = "\\d{3,}";

// allow either a blank string, whitespaces (will be trimmed) or require all numbers and at least 3 digits long
public static final String VALIDATION_REGEX = "(\\d{3,})?\\s*";
public final String value;

/**
Expand Down
Loading

0 comments on commit 323697c

Please sign in to comment.