Skip to content
This repository has been archived by the owner on Dec 27, 2023. It is now read-only.

Add filter by tag command #126

Merged
merged 2 commits into from
Oct 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions src/main/java/swe/context/logic/commands/FilterCommand.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package swe.context.logic.commands;

import static java.util.Objects.requireNonNull;

import swe.context.commons.util.ToStringBuilder;
import swe.context.logic.Messages;
import swe.context.model.Model;
import swe.context.model.contact.ContainsTagPredicate;

/**
* Filters and lists {@link Contact}s whose tags match the specified
* tag in full. Case insensitive.
*/
public class FilterCommand extends Command {
public static final String COMMAND_WORD = "filter";

public static final String MESSAGE_USAGE = COMMAND_WORD
+ ": Filters and lists all contacts whose tags match the"
+ " specified tag in full. Case insensitive."
+ "\nParameters: TAG"
+ "\nExample: " + COMMAND_WORD + " Friend";

private final ContainsTagPredicate predicate;

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

@Override
public CommandResult execute(Model model) {
requireNonNull(model);
model.setContactsFilter(predicate);
return new CommandResult(
String.format(Messages.CONTACTS_LISTED_OVERVIEW, model.getFilteredContactList().size()));
}

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

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

FilterCommand otherFilterCommand = (FilterCommand) other;
return predicate.equals(otherFilterCommand.predicate);
}

@Override
public String toString() {
return new ToStringBuilder(this)
.add("predicate", predicate)
.toString();
}
}
28 changes: 28 additions & 0 deletions src/main/java/swe/context/logic/parser/FilterCommandParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package swe.context.logic.parser;

import swe.context.logic.Messages;
import swe.context.logic.commands.FilterCommand;
import swe.context.logic.parser.exceptions.ParseException;
import swe.context.model.contact.ContainsTagPredicate;


/**
* Parses input arguments and creates a new FilterCommand object
*/
public class FilterCommandParser implements Parser<FilterCommand> {
/**
* Returns a {@link FilterCommand} from parsing the specified arguments.
*
* @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(
Messages.commandInvalidFormat(FilterCommand.MESSAGE_USAGE)
);
}

return new FilterCommand(new ContainsTagPredicate(trimmedArgs));
}
}
3 changes: 3 additions & 0 deletions src/main/java/swe/context/logic/parser/InputParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import swe.context.logic.commands.DeleteCommand;
import swe.context.logic.commands.EditCommand;
import swe.context.logic.commands.ExitCommand;
import swe.context.logic.commands.FilterCommand;
import swe.context.logic.commands.FindCommand;
import swe.context.logic.commands.HelpCommand;
import swe.context.logic.commands.ListCommand;
Expand Down Expand Up @@ -52,6 +53,8 @@ public static Command parseCommand(String userInput) throws ParseException {
return new ListCommand();
case FindCommand.COMMAND_WORD:
return new FindCommandParser().parse(arguments);
case FilterCommand.COMMAND_WORD:
return new FilterCommandParser().parse(arguments);
case EditCommand.COMMAND_WORD:
return new EditCommandParser().parse(arguments);
case DeleteCommand.COMMAND_WORD:
Expand Down
50 changes: 50 additions & 0 deletions src/main/java/swe/context/model/contact/ContainsTagPredicate.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package swe.context.model.contact;

import java.util.function.Predicate;
import java.util.Set;

import swe.context.commons.util.ToStringBuilder;
import swe.context.model.tag.Tag;

/**
* Tests that a {@code Contact}'s Tags matches the tag given in full, case insensitive.
*/
public class ContainsTagPredicate implements Predicate<Contact> {
private final String keyword;

public ContainsTagPredicate(String keyword) {
this.keyword = keyword;
}

@Override
public boolean test(Contact contact) {
Set<Tag> tagSet = contact.getTags();

for (Tag tag : tagSet) {
if (tag.toString().equalsIgnoreCase(this.keyword)) {
return true;
}
}
return false;
}

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

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

ContainsTagPredicate otherContainsTagPredicate = (ContainsTagPredicate) other;
return keyword.equals(otherContainsTagPredicate.keyword);
}

@Override
public String toString() {
return new ToStringBuilder(this).add("keyword", keyword).toString();
}
}
92 changes: 92 additions & 0 deletions src/test/java/swe/context/logic/commands/FilterCommandTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package swe.context.logic.commands;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static swe.context.logic.commands.CommandTestUtil.assertCommandSuccess;

import java.util.Arrays;
import java.util.Collections;

import org.junit.jupiter.api.Test;

import swe.context.logic.Messages;
import swe.context.model.Model;
import swe.context.model.ModelManager;
import swe.context.model.Settings;
import swe.context.model.contact.ContainsTagPredicate;
import swe.context.testutil.TestData;

/**
* Contains integration tests (interaction with the Model) for {@code FilterCommand}.
*/
public class FilterCommandTest {
private Model model = new ModelManager(TestData.Valid.Contact.getTypicalContacts(), new Settings());
private Model expectedModel = new ModelManager(TestData.Valid.Contact.getTypicalContacts(), new Settings());

@Test
public void equals() {
ContainsTagPredicate firstPredicate =
new ContainsTagPredicate("first");
ContainsTagPredicate secondPredicate =
new ContainsTagPredicate("second");

FilterCommand findFirstCommand = new FilterCommand(firstPredicate);
FilterCommand findSecondCommand = new FilterCommand(secondPredicate);

// same object -> returns true
assertTrue(findFirstCommand.equals(findFirstCommand));

// same values -> returns true
FilterCommand findFirstCommandCopy = new FilterCommand(firstPredicate);
assertTrue(findFirstCommand.equals(findFirstCommandCopy));

// different types -> returns false
assertFalse(findFirstCommand.equals(1));

// null -> returns false
assertFalse(findFirstCommand.equals(null));

// different contact -> returns false
assertFalse(findFirstCommand.equals(findSecondCommand));
}

@Test
public void execute_zeroKeywords_noContactFound() {
String expectedMessage = String.format(Messages.CONTACTS_LISTED_OVERVIEW, 0);
ContainsTagPredicate predicate = preparePredicate(" ");
FilterCommand command = new FilterCommand(predicate);
expectedModel.setContactsFilter(predicate);
assertCommandSuccess(command, model, expectedMessage, expectedModel);
assertEquals(Collections.emptyList(), model.getFilteredContactList());
}

@Test
public void execute_multipleKeywords_multipleContactsFound() {
String expectedMessage = String.format(Messages.CONTACTS_LISTED_OVERVIEW, 3);
ContainsTagPredicate predicate = preparePredicate("Friends");
FilterCommand command = new FilterCommand(predicate);
expectedModel.setContactsFilter(predicate);
assertCommandSuccess(command, model, expectedMessage, expectedModel);
assertEquals(
Arrays.asList(TestData.Valid.Contact.ALICE, TestData.Valid.Contact.BENSON,
TestData.Valid.Contact.DANIEL),
model.getFilteredContactList()
);
}

@Test
public void toStringMethod() {
ContainsTagPredicate predicate = new ContainsTagPredicate("keyword");
FilterCommand findCommand = new FilterCommand(predicate);
String expected = FilterCommand.class.getCanonicalName() + "{predicate=" + predicate + "}";
assertEquals(expected, findCommand.toString());
}

/**
* Parses {@code userInput} into a {@code ContainsTagPredicate}.
*/
private ContainsTagPredicate preparePredicate(String userInput) {
return new ContainsTagPredicate(userInput);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package swe.context.logic.parser;

import static swe.context.logic.parser.CommandParserTestUtil.assertParseFailure;
import static swe.context.logic.parser.CommandParserTestUtil.assertParseSuccess;

import org.junit.jupiter.api.Test;

import swe.context.logic.Messages;
import swe.context.logic.commands.FilterCommand;
import swe.context.model.contact.ContainsTagPredicate;


public class FilterCommandParserTest {
private FilterCommandParser parser = new FilterCommandParser();

@Test
public void parse_emptyArg_throwsParseException() {
assertParseFailure(parser, " ", Messages.commandInvalidFormat(FilterCommand.MESSAGE_USAGE));
}

@Test
public void parse_validArgs_returnsFilterCommand() {
FilterCommand expectedFindCommand =
new FilterCommand(new ContainsTagPredicate("Friends"));
assertParseSuccess(parser, "Friends", expectedFindCommand);
}
}
10 changes: 10 additions & 0 deletions src/test/java/swe/context/logic/parser/InputParserTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@
import swe.context.logic.commands.EditCommand;
import swe.context.logic.commands.EditCommand.EditContactDescriptor;
import swe.context.logic.commands.ExitCommand;
import swe.context.logic.commands.FilterCommand;
import swe.context.logic.commands.FindCommand;
import swe.context.logic.commands.HelpCommand;
import swe.context.logic.commands.ListCommand;
import swe.context.logic.parser.exceptions.ParseException;
import swe.context.model.contact.Contact;
import swe.context.model.contact.ContainsTagPredicate;
import swe.context.model.contact.NameContainsKeywordsPredicate;
import swe.context.testutil.CommandUtil;
import swe.context.testutil.ContactBuilder;
Expand Down Expand Up @@ -84,6 +86,14 @@ public void parseCommand_exit() throws Exception {
assertTrue(InputParser.parseCommand(ExitCommand.COMMAND_WORD + " 3") instanceof ExitCommand);
}

@Test
public void parseCommand_filter() throws Exception {
String keyword = "foobar";
FilterCommand command = (FilterCommand) InputParser.parseCommand(
FilterCommand.COMMAND_WORD + " " + keyword);
assertEquals(new FilterCommand(new ContainsTagPredicate(keyword)), command);
}

@Test
public void parseCommand_find() throws Exception {
List<String> keywords = Arrays.asList("foo", "bar", "baz");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package swe.context.model.contact;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

import org.junit.jupiter.api.Test;

import swe.context.testutil.ContactBuilder;


public class ContainsTagPredicateTest {

@Test
public void equals() {
String firstPredicateKeyword = "friend";
String secondPredicateKeyword = "colleague";

ContainsTagPredicate firstPredicate = new ContainsTagPredicate(firstPredicateKeyword);
ContainsTagPredicate secondPredicate = new ContainsTagPredicate(secondPredicateKeyword);

// same object -> returns true
assertTrue(firstPredicate.equals(firstPredicate));

// same values -> returns true
ContainsTagPredicate firstPredicateCopy = new ContainsTagPredicate(firstPredicateKeyword);
assertTrue(firstPredicate.equals(firstPredicateCopy));

// different types -> returns false
assertFalse(firstPredicate.equals(1));

// null -> returns false
assertFalse(firstPredicate.equals(null));

// different contact -> returns false
assertFalse(firstPredicate.equals(secondPredicate));
}

@Test
public void test_nameContainsKeywords_returnsTrue() {
// One tag
ContainsTagPredicate predicate = new ContainsTagPredicate("friend");
assertTrue(predicate.test(new ContactBuilder().withTags("friend").build()));

// Mixed-case tag
predicate = new ContainsTagPredicate("Friend");
assertTrue(predicate.test(new ContactBuilder().withTags("friend").build()));

// Multiple tags
predicate = new ContainsTagPredicate("friend");
assertTrue(predicate.test(new ContactBuilder().withTags("friend", "colleague").build()));
}

@Test
public void test_nameDoesNotContainKeywords_returnsFalse() {
// Non-matching tag
ContainsTagPredicate predicate = new ContainsTagPredicate("colleague");
assertFalse(predicate.test(new ContactBuilder().withTags("friend").build()));

// Multiple non-matching tags
predicate = new ContainsTagPredicate("colleague");
assertFalse(predicate.test(new ContactBuilder().withTags("friend", "student").build()));
}

@Test
public void toStringMethod() {
String keyword = "keyword1";
ContainsTagPredicate predicate = new ContainsTagPredicate(keyword);

String expected = ContainsTagPredicate.class.getCanonicalName() + "{keyword=" + keyword + "}";
assertEquals(expected, predicate.toString());
}
}
Loading