diff --git a/docs/UserGuide.md b/docs/UserGuide.md
index 576381ed784..d7e8e2fceaa 100644
--- a/docs/UserGuide.md
+++ b/docs/UserGuide.md
@@ -1,38 +1,131 @@
+# User Guide
+
+* Quick Start
+* Features
+ * Viewing help: `help`
+ * Adding a person: `add`
+ * Deleting a person: `delete`
+ * Listing all contacts: `list`
+ * Locating persons by keywords: `find`
+ * Add an image for contacts: `add-image`
+ * Delete an image for contacts: `delete-image`
+ * Quick import admin contacts: `import`
+* FAQ
+* Command summary
+
+---
+
+## Quick start
+
+1. Ensure you have Java `11` or above installed in your computer.
+2. Download the latest `bookface.jar` from [here](https://github.com/AY2223S2-CS2103-F11-4/tp/releases).
+3. Place `bookface.jar` file in the folder you would like to use as the *home directory*.
+4. Run the application. The following GUI will appear upon first use of the application.
+![GUI upon first use](images/GUIOnInitialUsage.png)
+
+5. The application is initially loaded with sample data for new users to try out the [features](#Features) listed below.
+Experienced users can delete the sample data and proceed with regular usage.
+
+---
+
+## Features
+
+### Help command: `help`
+
+Shows a link to the user guide to help new users get familiar with the commands for the application.
+
+Format: `help`
+
+### Add user contacts: `add`
+
+Format: `add [name] [year/course] [phone number] [email] [address]` Optional to add: `t/TAGS`
+
+* User is *required* to enter **name, status, phone number, email, address**
+* Tags can be optional
+* If the account exists, user can add in related field of interests to share with others
+
+Example:
+* `add n/Shenghan s/Year2 Computer-science p/99999999 e/david@gmail.com a/punngol place 696a #12-348` will displays the
+ necessary basic information that are the user's name, year/course, phone number, email, address. Optional fields are tags,
+ for which there are commitment/cca tags, module tags and lastly the general tags for users to enter non-specific typed tags.
+
+Example (with the addition of tags):
+* `add n/Shenghan s/Year2 Computer-science p/99999999 e/david@gmail.com a/punngol place 696a #12-348 t/developer ct/soccer
+ mt/cs2103` Note that the tags can be placed in any part of the command, and it will not break!
+
+Tags are categorised according to tag colors:
+* Commitment tags: `coral pink`
+* Module tags: `Dark green`
+* General tags: `default blue`
+
+### Delete user contacts: `delete`
+
+Delete a contact.
+Format: `delete INDEX`
+
+* Show contact details specified by `INDEX`
+* The index refers to the index number shown in the displayed person list.
+* The index *must* be a positive integer 1, 2, 3, …
+* Extra: Will prompt user to re-confirm again before the contact is erased from BookFace
+ Example:
+* `delete 2` Brings up the 2nd person in the address book and prompt user to confirm before deleting.
+
### Listing all contacts: `list`
List all contacts in the address book.
+
Format: `list`
-### Locating persons by name/class/group: `find`
+### Locating persons by keywords: `find`
+
+Finds persons whose contact details contain any of the given keywords based on the
+prefix specified.
-Finds persons whose names contain any of the given keywords.
-Format: `find KEYWORD [MORE_KEYWORDS]`
+Format: `find [PREFIX]/KEYWORD [MORE [PREFIX]/KEYWORD]...`
* 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`
-* Persons matching at least one keyword will be returned (i.e. `OR` search) e.g. `Hans Bo` will return `Hans Gruber`
- , `Bo Yang`
+* The search will filter by the `PREFIX` provided, e.g. `n/` searches through the
+ names of the contacts, `p/` searches through the phone number of the contacts, `t/`
+ searches through the tags of the contact, etc...
+* Each prefix must be followed by one and only one keyword. See below for example usage.
+* The search is done via the logical ***AND*** operator, i.e. `find n/john t/cs` will return
+ the list of contacts where his name is `john` and has a tag that contains `cs`.
+* The following shows a list of allowed prefixes:
+ 1. `n/` which represents the name
+ 2. `s/` which represents the status
+ 3. `p/` which represents the phone number
+ 4. `e/` which represents the email
+ 5. `a/` which represents the address
+ 6. `t/` which represents the tags
+
+Example:
+
+`find n/amy t/cs2103 e/gmail` will return the list of contacts whose names are `amy`,
+has a tag labeled `cs2103`, and whose emails contain `gmail`.
+
+### Add an image for contacts
-### Add image for contacts
+Add a contact image for each contact.
-Add a contact image for each contact
-Format: `add-image INDEX [NAME-OF-IMAGE]`
+Format: `add-image INDEX [PATH-TO-IMAGE]`
* Adds an image to the contact at the specified `INDEX`
* The index refers to the index number shown in the displayed person list.
* The index **must be a positive integer** 1, 2, 3,...
-* The image must be placed in a specific folder for BookFace to locate
* If the image cannot be found or user did not specify a contact image, a default image will be used
+> **Note:** The `[PATH-TO-IMAGE]` provided must be an absolute path, and should not be provided in quotation marks.
+> For instance: `add-image 2 "C:/Users/user/Downloads/weekiat.png"` will be invalid, whereas
+> `add-image 2 C:/Users/user/Downloads/weekiat.png` will be valid.
+
Examples:
-* `list` followed by `add-image 2 weekiat.png` adds the image `weekiat.png` to the 2nd person in the address book
+* `list` followed by `add-image 2 C:/Users/user/Downloads/weekiat.png` adds the image `weekiat.png` to the 2nd person in the address book
-## Delete Image for contacts
+### Delete an Image for contacts
Delete the image of a contact.
+
Format: `delete-image INDEX`
* Deletes the image of contact specified by `INDEX`
@@ -43,9 +136,10 @@ Format: `delete-image INDEX`
Example:
* `delete-image 2` deletes the image of the 2nd person in the address book.
-## Quick Import for admin contacts: `import`
+### Quick Import for admin contacts: `import`
Import administrative contacts for relevant faculties.
+
Format: `import [faculty]`
* Faculty acronyms (e.g. soc)
@@ -56,49 +150,7 @@ Example:
* `import soc` adds all important administrative contact for School of Computing
* `import chs` adds all important administrative contact for College of Humanities and Sciences
-
-## Add user contacts: `add`
-
-Format: `add [name] [year/course] [phone number] [email] [address]` Optional to add: `t/TAGS`
-
-* User is *required* to enter **name, status, phone number, email, address**
-* Tags can be optional
-* If the account exists, user can add in related field of interests to share with others
-
-Example:
-* `add n/Shenghan s/Year2 Computer-science p/99999999 e/david@gmail.com a/punngol place 696a #12-348` will displays the
-necessary basic information that are the user's name, year/course, phone number, email, address. Optional fields are tags,
-for which there are commitment/cca tags, module tags and lastly the general tags for users to enter non-specific typed tags.
-
-Example (with the addition of tags):
-* `add n/Shenghan s/Year2 Computer-science p/99999999 e/david@gmail.com a/punngol place 696a #12-348 t/developer ct/soccer
-mt/cs2103` Note that the tags can be placed in any part of the command, and it will not break!
-
-Tags are categorised according to tag colors:
-* Commitment tags: `coral pink`
-* Module tags: `Dark green`
-* General tags: `default blue`
-
-## Delete user contacts: `delete`
-
-Delete a contact.
-Format: `delete INDEX`
-
-* Show contact details specified by `INDEX`
-* The index refers to the index number shown in the displayed person list.
-* The index *must* be a positive integer 1, 2, 3, …
-* Extra: Will prompt user to re-confirm again before the contact is erased from BookFace
- Example:
-* `delete 2` Brings up the 2nd person in the address book and prompt user to confirm before deleting.
-
-## Help command: `help`
-
-Show a list of command to help users to navigate around
-Format: `help`
-
-* Include list of commands to enable users to refer to in terminal.
-
------------------------
+---
## Command summary
@@ -110,7 +162,7 @@ Format: `help`
| **Find** | `find KEYWORD [MORE_KEYWORDS]`
e.g., `find James Jake` |
| **List** | `list` |
| **Help** | `help` |
-| **Add-Image** | `add-image INDEX [NAME-OF-IMAGE]`
e.g., `add-image 2 weekiat.png` |
+| **Add-Image** | `add-image INDEX [PATH-TO-IMAGE]`
e.g., `add-image 2 C:/Users/user/Downloads/weekiat.png` |
| **Delete-Image** | `delete-image INDEX`
e.g., `delete-image 2` | |
| **Import** | `import [faculty]`
e.g., `import soc, import chs` |
diff --git a/docs/images/GUIOnInitialUsage.png b/docs/images/GUIOnInitialUsage.png
new file mode 100644
index 00000000000..14f513ca7ec
Binary files /dev/null and b/docs/images/GUIOnInitialUsage.png differ
diff --git a/src/main/java/seedu/address/commons/util/StringUtil.java b/src/main/java/seedu/address/commons/util/StringUtil.java
index 61cc8c9a1cb..6a8ea3b1ce6 100644
--- a/src/main/java/seedu/address/commons/util/StringUtil.java
+++ b/src/main/java/seedu/address/commons/util/StringUtil.java
@@ -26,8 +26,7 @@ public class StringUtil {
public static boolean containsWordIgnoreCase(String sentence, String word) {
requireNonNull(sentence);
requireNonNull(word);
-
- String preppedWord = word.trim();
+ String preppedWord = word.trim().toLowerCase();
checkArgument(!preppedWord.isEmpty(), "Word parameter cannot be empty");
checkArgument(preppedWord.split("\\s+").length == 1, "Word parameter should be a single word");
@@ -35,7 +34,7 @@ public static boolean containsWordIgnoreCase(String sentence, String word) {
String[] wordsInPreppedSentence = preppedSentence.split("\\s+");
return Arrays.stream(wordsInPreppedSentence)
- .anyMatch(preppedWord::equalsIgnoreCase);
+ .anyMatch(wordInPreppedSentence -> wordInPreppedSentence.toLowerCase().contains(preppedWord));
}
/**
diff --git a/src/main/java/seedu/address/logic/commands/FindCommand.java b/src/main/java/seedu/address/logic/commands/FindCommand.java
index 41b39d09bfb..5556101dc07 100644
--- a/src/main/java/seedu/address/logic/commands/FindCommand.java
+++ b/src/main/java/seedu/address/logic/commands/FindCommand.java
@@ -2,38 +2,35 @@
import static java.util.Objects.requireNonNull;
+import java.util.function.Predicate;
+
import seedu.address.commons.core.Messages;
import seedu.address.model.Model;
-import seedu.address.model.person.NameContainsKeywordsPredicate;
-import seedu.address.model.person.PhoneContainsKeywordsPredicate;
+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 details contains any of the
+ * argument keywords based on the prefixes in the user input.
* 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 "
- + "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";
-
- public static final String MESSAGE_USAGE_2 = COMMAND_WORD + ": Finds all persons whose phone numbers contain any "
- + "of the specified keywords / phone number substring and displays them as a list with index numbers.\n"
- + "Parameters: KEYWORD [MORE_KEYWORDS]...\n"
- + "Example: " + COMMAND_WORD + " 99999999";
-
- private NameContainsKeywordsPredicate predicate;
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Finds all persons whose details contain any of "
+ + "the specified keywords (case-insensitive) based on "
+ + "the prefix provided and displays them as a list with index numbers.\n"
+ + "Each prefix must be followed by one and only one keyword.\n"
+ + "Please use the \"help\" command for more information on "
+ + "the usage of this command.\n"
+ + "Parameters: [PREFIX]/KEYWORD [MORE [PREFIX]/KEYWORD]...\n"
+ + "Example: " + COMMAND_WORD + " n/alice s/y4 p/91234567"
+ + " e/alice@example.com a/blk 123 t/cs2103";
- private PhoneContainsKeywordsPredicate phonePredicate;
-
- public FindCommand(PhoneContainsKeywordsPredicate phonePredicate) {
- this.phonePredicate = phonePredicate;
- }
+ private Predicate predicate;
- public FindCommand(NameContainsKeywordsPredicate predicate) {
+ public FindCommand(Predicate predicate) {
this.predicate = predicate;
}
diff --git a/src/main/java/seedu/address/logic/parser/FindCommandParser.java b/src/main/java/seedu/address/logic/parser/FindCommandParser.java
index 5d3c9371375..5a9da390d29 100644
--- a/src/main/java/seedu/address/logic/parser/FindCommandParser.java
+++ b/src/main/java/seedu/address/logic/parser/FindCommandParser.java
@@ -1,19 +1,40 @@
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_STATUS;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
-import java.util.Arrays;
+import java.util.List;
+import java.util.function.Predicate;
import seedu.address.logic.commands.FindCommand;
import seedu.address.logic.parser.exceptions.ParseException;
-import seedu.address.model.person.NameContainsKeywordsPredicate;
-import seedu.address.model.person.PhoneContainsKeywordsPredicate;
+import seedu.address.model.person.Person;
+import seedu.address.model.person.predicates.AddressContainsKeywordPredicate;
+import seedu.address.model.person.predicates.EmailContainsKeywordPredicate;
+import seedu.address.model.person.predicates.NameContainsKeywordPredicate;
+import seedu.address.model.person.predicates.PhoneContainsKeywordsPredicate;
+import seedu.address.model.person.predicates.StatusContainsKeywordsPredicate;
+import seedu.address.model.person.predicates.TagContainsKeywordsPredicate;
/**
* Parses input arguments and creates a new FindCommand object
*/
public class FindCommandParser implements Parser {
+ private Prefix[] possiblePrefixes = {
+ PREFIX_NAME,
+ PREFIX_STATUS,
+ PREFIX_PHONE,
+ PREFIX_EMAIL,
+ PREFIX_ADDRESS,
+ PREFIX_TAG
+ };
+
/**
* Parses the given {@code String} of arguments in the context of the FindCommand
* and returns a FindCommand object for execution.
@@ -21,34 +42,57 @@ public class FindCommandParser implements Parser {
* @throws ParseException if the user input does not conform the expected format
*/
public FindCommand parse(String args) throws ParseException {
- String trimmedArgs = args.trim();
- Boolean isNumber = onlyDigits(trimmedArgs);
- if (trimmedArgs.isEmpty()) {
- if (isNumber) {
- throw new ParseException(
- String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE));
- } else {
- throw new ParseException(
- String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE));
- }
- }
- if (isNumber) {
- return new FindCommand(new PhoneContainsKeywordsPredicate(trimmedArgs));
- } else {
- String[] nameKeywords = trimmedArgs.split("\\s+");
+ if (args.isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE));
+ }
+ ArgumentMultimap argMultimap =
+ ArgumentTokenizer.tokenize(args, possiblePrefixes);
- return new FindCommand(new NameContainsKeywordsPredicate(Arrays.asList(nameKeywords)));
+ Predicate predicate = null;
+ for (Prefix p : possiblePrefixes) {
+ List prefixArguments = argMultimap.getAllValues(p);
+ if (prefixArguments.isEmpty()) {
+ continue;
+ }
+ for (String arg : prefixArguments) {
+ if (!isValidArgument(arg)) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE));
+ }
+ Predicate currentPredicate = null;
+ if (p == PREFIX_NAME) {
+ currentPredicate = new NameContainsKeywordPredicate(arg);
+ } else if (p == PREFIX_STATUS) {
+ currentPredicate = new StatusContainsKeywordsPredicate(arg);
+ } else if (p == PREFIX_PHONE) {
+ currentPredicate = new PhoneContainsKeywordsPredicate(arg);
+ } else if (p == PREFIX_EMAIL) {
+ currentPredicate = new EmailContainsKeywordPredicate(arg);
+ } else if (p == PREFIX_ADDRESS) {
+ currentPredicate = new AddressContainsKeywordPredicate(arg);
+ } else if (p == PREFIX_TAG) {
+ currentPredicate = new TagContainsKeywordsPredicate(arg);
+ }
+ assert currentPredicate != null;
+ if (predicate == null) {
+ predicate = currentPredicate;
+ } else {
+ predicate = predicate.and(currentPredicate);
+ }
+ }
+ }
+ if (predicate == null) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE));
}
+
+ return new FindCommand(predicate);
}
- private boolean onlyDigits(String str) {
- for (int i = 0; i < str.length(); i++) {
- if (!Character.isDigit(str.charAt(i))) {
- return false;
- }
+ private static boolean isValidArgument(String argument) {
+ String preppedWord = argument.trim().toLowerCase();
+ if (preppedWord.isEmpty() || preppedWord.split("\\s+").length != 1) {
+ return false;
}
return true;
}
-
}
diff --git a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java b/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java
deleted file mode 100644
index c9b5868427c..00000000000
--- a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java
+++ /dev/null
@@ -1,31 +0,0 @@
-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 NameContainsKeywordsPredicate implements Predicate {
- private final List keywords;
-
- public NameContainsKeywordsPredicate(List keywords) {
- this.keywords = keywords;
- }
-
- @Override
- public boolean test(Person person) {
- return keywords.stream()
- .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(person.getName().fullName, keyword));
- }
-
- @Override
- public boolean equals(Object other) {
- return other == this // short circuit if same object
- || (other instanceof NameContainsKeywordsPredicate // instanceof handles nulls
- && keywords.equals(((NameContainsKeywordsPredicate) other).keywords)); // state check
- }
-
-}
diff --git a/src/main/java/seedu/address/model/person/predicates/AddressContainsKeywordPredicate.java b/src/main/java/seedu/address/model/person/predicates/AddressContainsKeywordPredicate.java
new file mode 100644
index 00000000000..6eccf6ff369
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/predicates/AddressContainsKeywordPredicate.java
@@ -0,0 +1,30 @@
+package seedu.address.model.person.predicates;
+
+import java.util.function.Predicate;
+
+import seedu.address.commons.util.StringUtil;
+import seedu.address.model.person.Person;
+
+/**
+ * Tests that a {@code Person}'s {@code Address} matches any of the keywords given.
+ */
+public class AddressContainsKeywordPredicate implements Predicate {
+ private final String keyword;
+
+ public AddressContainsKeywordPredicate(String keyword) {
+ this.keyword = keyword;
+ }
+
+ @Override
+ public boolean test(Person person) {
+ return StringUtil.containsWordIgnoreCase(person.getAddress().toString(), keyword);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof AddressContainsKeywordPredicate // instanceof handles nulls
+ && keyword.equals(((AddressContainsKeywordPredicate) other).keyword)); // state check
+ }
+
+}
diff --git a/src/main/java/seedu/address/model/person/predicates/EmailContainsKeywordPredicate.java b/src/main/java/seedu/address/model/person/predicates/EmailContainsKeywordPredicate.java
new file mode 100644
index 00000000000..cce124dc277
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/predicates/EmailContainsKeywordPredicate.java
@@ -0,0 +1,30 @@
+package seedu.address.model.person.predicates;
+
+import java.util.function.Predicate;
+
+import seedu.address.commons.util.StringUtil;
+import seedu.address.model.person.Person;
+
+/**
+ * Tests that a {@code Person}'s {@code Email} matches any of the keywords given.
+ */
+public class EmailContainsKeywordPredicate implements Predicate {
+ private final String keyword;
+
+ public EmailContainsKeywordPredicate(String keyword) {
+ this.keyword = keyword;
+ }
+
+ @Override
+ public boolean test(Person person) {
+ return StringUtil.containsWordIgnoreCase(person.getEmail().toString(), keyword);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof EmailContainsKeywordPredicate // instanceof handles nulls
+ && keyword.equals(((EmailContainsKeywordPredicate) other).keyword)); // state check
+ }
+
+}
diff --git a/src/main/java/seedu/address/model/person/predicates/NameContainsKeywordPredicate.java b/src/main/java/seedu/address/model/person/predicates/NameContainsKeywordPredicate.java
new file mode 100644
index 00000000000..5b63118688f
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/predicates/NameContainsKeywordPredicate.java
@@ -0,0 +1,50 @@
+package seedu.address.model.person.predicates;
+
+import java.util.List;
+import java.util.function.Predicate;
+
+import seedu.address.commons.util.StringUtil;
+import seedu.address.model.person.Person;
+
+/**
+ * Tests that a {@code Person}'s {@code Name} matches any of the keywords given.
+ */
+public class NameContainsKeywordPredicate implements Predicate {
+ private final List keywords;
+ private final String keyword;
+
+ /**
+ * Constructor that takes in a list of keywords
+ * @param keywords The list of keywords for the predicate
+ */
+ public NameContainsKeywordPredicate(List keywords) {
+ this.keywords = keywords;
+ this.keyword = null;
+ }
+
+ /**
+ * Constructor that takes in a singular {@code String} keyword
+ * @param keyword The keyword for the predicate
+ */
+ public NameContainsKeywordPredicate(String keyword) {
+ this.keywords = null;
+ this.keyword = keyword;
+ }
+
+ @Override
+ public boolean test(Person person) {
+ if (keywords != null) {
+ return keywords.stream()
+ .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(person.getName().fullName, keyword));
+ }
+ return StringUtil.containsWordIgnoreCase(person.getName().fullName, keyword);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof NameContainsKeywordPredicate // instanceof handles nulls
+ && keyword.equals(((NameContainsKeywordPredicate) other).keyword)); // state check
+ }
+
+}
diff --git a/src/main/java/seedu/address/model/person/PhoneContainsKeywordsPredicate.java b/src/main/java/seedu/address/model/person/predicates/PhoneContainsKeywordsPredicate.java
similarity index 66%
rename from src/main/java/seedu/address/model/person/PhoneContainsKeywordsPredicate.java
rename to src/main/java/seedu/address/model/person/predicates/PhoneContainsKeywordsPredicate.java
index d3f7f550592..49bf727b3d9 100644
--- a/src/main/java/seedu/address/model/person/PhoneContainsKeywordsPredicate.java
+++ b/src/main/java/seedu/address/model/person/predicates/PhoneContainsKeywordsPredicate.java
@@ -1,9 +1,12 @@
-package seedu.address.model.person;
+package seedu.address.model.person.predicates;
import java.util.function.Predicate;
+import seedu.address.commons.util.StringUtil;
+import seedu.address.model.person.Person;
+
/**
- * Tests that a {@code Person}'s {@code Name} matches any of the keywords given.
+ * Tests that a {@code Person}'s {@code Phone} matches any of the keywords given.
*/
public class PhoneContainsKeywordsPredicate implements Predicate {
private final String keyword;
@@ -12,7 +15,7 @@ public PhoneContainsKeywordsPredicate(String keyword) {
this.keyword = keyword;
}
public boolean test(Person person) {
- return keyword.matches(person.getPhone().value);
+ return StringUtil.containsWordIgnoreCase(person.getPhone().toString(), keyword);
}
@Override
diff --git a/src/main/java/seedu/address/model/person/predicates/StatusContainsKeywordsPredicate.java b/src/main/java/seedu/address/model/person/predicates/StatusContainsKeywordsPredicate.java
new file mode 100644
index 00000000000..8251cb7ed4f
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/predicates/StatusContainsKeywordsPredicate.java
@@ -0,0 +1,23 @@
+package seedu.address.model.person.predicates;
+
+import java.util.function.Predicate;
+
+import seedu.address.commons.util.StringUtil;
+import seedu.address.model.person.Person;
+
+/**
+ * Tests that a {@code Person}'s {@code Status} matches any of the keywords given.
+ */
+public class StatusContainsKeywordsPredicate implements Predicate {
+ private final String keyword;
+
+ public StatusContainsKeywordsPredicate(String keyword) {
+ this.keyword = keyword;
+ }
+
+ @Override
+ public boolean test(Person person) {
+ return StringUtil.containsWordIgnoreCase(person.getStatus().toString(), keyword);
+ }
+
+}
diff --git a/src/main/java/seedu/address/model/person/predicates/TagContainsKeywordsPredicate.java b/src/main/java/seedu/address/model/person/predicates/TagContainsKeywordsPredicate.java
new file mode 100644
index 00000000000..778643dd69d
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/predicates/TagContainsKeywordsPredicate.java
@@ -0,0 +1,34 @@
+package seedu.address.model.person.predicates;
+
+import java.util.Set;
+import java.util.function.Predicate;
+
+import seedu.address.commons.util.StringUtil;
+import seedu.address.model.person.Person;
+import seedu.address.model.tag.Tag;
+
+/**
+ * Tests that a {@code Person}'s {@code Name} matches any of the keywords given.
+ */
+public class TagContainsKeywordsPredicate implements Predicate {
+ private final String keyword;
+
+ public TagContainsKeywordsPredicate(String keyword) {
+ this.keyword = keyword;
+ }
+
+ @Override
+ public boolean test(Person person) {
+ Set tags = person.getTags();
+ return tags.stream().anyMatch(tag ->
+ StringUtil.containsWordIgnoreCase(tag.tagName, keyword));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof TagContainsKeywordsPredicate // instanceof handles nulls
+ && keyword.equals(((TagContainsKeywordsPredicate) other).keyword)); // state check
+ }
+
+}
diff --git a/src/main/java/seedu/address/model/tag/CommitmentTag.java b/src/main/java/seedu/address/model/tag/CommitmentTag.java
index 8b795d93886..a45d64906e8 100644
--- a/src/main/java/seedu/address/model/tag/CommitmentTag.java
+++ b/src/main/java/seedu/address/model/tag/CommitmentTag.java
@@ -17,13 +17,6 @@ public String tagColor() {
return "#f88379";
}
- @Override
- public boolean equals(Object other) {
- return other == this
- || (other instanceof CommitmentTag
- && tagName.equals(((CommitmentTag) other).tagName));
- }
-
@Override
public String toString() {
return " [Commitment: " + tagName.split("XXXXX")[1] + "] ";
diff --git a/src/main/java/seedu/address/model/tag/ModuleTag.java b/src/main/java/seedu/address/model/tag/ModuleTag.java
index 0e3f94e2c47..85a2fe30a6d 100644
--- a/src/main/java/seedu/address/model/tag/ModuleTag.java
+++ b/src/main/java/seedu/address/model/tag/ModuleTag.java
@@ -9,13 +9,6 @@ public ModuleTag(String tagName) {
super(tagName);
}
- @Override
- public boolean equals(Object other) {
- return other == this
- || (other instanceof ModuleTag
- && tagName.equals(((ModuleTag) other).tagName));
- }
-
/**
* @return the corresponding color code for css
*/
diff --git a/src/main/java/seedu/address/model/tag/Tag.java b/src/main/java/seedu/address/model/tag/Tag.java
index debcdf51b6c..dc1664ab73b 100644
--- a/src/main/java/seedu/address/model/tag/Tag.java
+++ b/src/main/java/seedu/address/model/tag/Tag.java
@@ -44,7 +44,7 @@ public String tagColor() {
public boolean equals(Object other) {
return other == this // short circuit if same object
|| (other instanceof Tag // instanceof handles nulls
- && tagName.equals(((Tag) other).tagName)); // state check
+ && tagName.equalsIgnoreCase(((Tag) other).tagName)); // state check
}
@Override
diff --git a/src/main/java/seedu/address/ui/HelpWindow.java b/src/main/java/seedu/address/ui/HelpWindow.java
index 3f16b2fcf26..072d798ad0b 100644
--- a/src/main/java/seedu/address/ui/HelpWindow.java
+++ b/src/main/java/seedu/address/ui/HelpWindow.java
@@ -15,7 +15,8 @@
*/
public class HelpWindow extends UiPart {
- public static final String USERGUIDE_URL = "https://se-education.org/addressbook-level3/UserGuide.html";
+ public static final String USERGUIDE_URL =
+ "https://github.com/AY2223S2-CS2103-F11-4/tp/blob/master/docs/UserGuide.md";
public static final String HELP_MESSAGE = "Refer to the user guide: " + USERGUIDE_URL;
private static final Logger logger = LogsCenter.getLogger(HelpWindow.class);
diff --git a/src/test/java/seedu/address/commons/util/StringUtilTest.java b/src/test/java/seedu/address/commons/util/StringUtilTest.java
index c56d407bf3f..458ab21fe64 100644
--- a/src/test/java/seedu/address/commons/util/StringUtilTest.java
+++ b/src/test/java/seedu/address/commons/util/StringUtilTest.java
@@ -109,7 +109,7 @@ public void containsWordIgnoreCase_validInputs_correctResult() {
assertFalse(StringUtil.containsWordIgnoreCase(" ", "123"));
// Matches a partial word only
- assertFalse(StringUtil.containsWordIgnoreCase("aaa bbb ccc", "bb")); // Sentence word bigger than query word
+ assertTrue(StringUtil.containsWordIgnoreCase("aaa bbb ccc", "bb")); // Sentence word bigger than query word
assertFalse(StringUtil.containsWordIgnoreCase("aaa bbb ccc", "bbbb")); // Query word bigger than sentence word
// Matches word in the sentence, different upper/lower case letters
diff --git a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java b/src/test/java/seedu/address/logic/commands/CommandTestUtil.java
index 3933135a71b..e722cc07a85 100644
--- a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java
+++ b/src/test/java/seedu/address/logic/commands/CommandTestUtil.java
@@ -18,8 +18,8 @@
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.model.AddressBook;
import seedu.address.model.Model;
-import seedu.address.model.person.NameContainsKeywordsPredicate;
import seedu.address.model.person.Person;
+import seedu.address.model.person.predicates.NameContainsKeywordPredicate;
import seedu.address.testutil.EditPersonDescriptorBuilder;
/**
@@ -127,7 +127,7 @@ public static void showPersonAtIndex(Model model, Index targetIndex) {
Person person = model.getFilteredPersonList().get(targetIndex.getZeroBased());
final String[] splitName = person.getName().fullName.split("\\s+");
- model.updateFilteredPersonList(new NameContainsKeywordsPredicate(Arrays.asList(splitName[0])));
+ model.updateFilteredPersonList(new NameContainsKeywordPredicate(Arrays.asList(splitName[0])));
assertEquals(1, model.getFilteredPersonList().size());
}
diff --git a/src/test/java/seedu/address/logic/commands/FindCommandTest.java b/src/test/java/seedu/address/logic/commands/FindCommandTest.java
index 9b15db28bbb..3fbdecd178b 100644
--- a/src/test/java/seedu/address/logic/commands/FindCommandTest.java
+++ b/src/test/java/seedu/address/logic/commands/FindCommandTest.java
@@ -18,7 +18,7 @@
import seedu.address.model.Model;
import seedu.address.model.ModelManager;
import seedu.address.model.UserPrefs;
-import seedu.address.model.person.NameContainsKeywordsPredicate;
+import seedu.address.model.person.predicates.NameContainsKeywordPredicate;
/**
* Contains integration tests (interaction with the Model) for {@code FindCommand}.
@@ -29,10 +29,10 @@ public class FindCommandTest {
@Test
public void equals() {
- NameContainsKeywordsPredicate firstPredicate =
- new NameContainsKeywordsPredicate(Collections.singletonList("first"));
- NameContainsKeywordsPredicate secondPredicate =
- new NameContainsKeywordsPredicate(Collections.singletonList("second"));
+ NameContainsKeywordPredicate firstPredicate =
+ new NameContainsKeywordPredicate("John");
+ NameContainsKeywordPredicate secondPredicate =
+ new NameContainsKeywordPredicate("Adam");
FindCommand findFirstCommand = new FindCommand(firstPredicate);
FindCommand findSecondCommand = new FindCommand(secondPredicate);
@@ -57,7 +57,7 @@ public void equals() {
@Test
public void execute_zeroKeywords_noPersonFound() {
String expectedMessage = String.format(MESSAGE_PERSONS_LISTED_OVERVIEW, 0);
- NameContainsKeywordsPredicate predicate = preparePredicate(" ");
+ NameContainsKeywordPredicate predicate = preparePredicate(" ");
FindCommand command = new FindCommand(predicate);
expectedModel.updateFilteredPersonList(predicate);
assertCommandSuccess(command, model, expectedMessage, expectedModel);
@@ -67,7 +67,7 @@ public void execute_zeroKeywords_noPersonFound() {
@Test
public void execute_multipleKeywords_multiplePersonsFound() {
String expectedMessage = String.format(MESSAGE_PERSONS_LISTED_OVERVIEW, 3);
- NameContainsKeywordsPredicate predicate = preparePredicate("Kurz Elle Kunz");
+ NameContainsKeywordPredicate predicate = preparePredicate("Kurz Elle Kunz");
FindCommand command = new FindCommand(predicate);
expectedModel.updateFilteredPersonList(predicate);
assertCommandSuccess(command, model, expectedMessage, expectedModel);
@@ -75,9 +75,9 @@ public void execute_multipleKeywords_multiplePersonsFound() {
}
/**
- * Parses {@code userInput} into a {@code NameContainsKeywordsPredicate}.
+ * Parses {@code userInput} into a {@code NameContainsKeywordPredicate}.
*/
- private NameContainsKeywordsPredicate preparePredicate(String userInput) {
- return new NameContainsKeywordsPredicate(Arrays.asList(userInput.split("\\s+")));
+ private NameContainsKeywordPredicate preparePredicate(String userInput) {
+ return new NameContainsKeywordPredicate(Arrays.asList(userInput.split("\\s+")));
}
}
diff --git a/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java b/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java
index 1c548c3f854..66207adcc49 100644
--- a/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java
+++ b/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java
@@ -7,10 +7,6 @@
import static seedu.address.testutil.Assert.assertThrows;
import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON;
-import java.util.Arrays;
-import java.util.List;
-import java.util.stream.Collectors;
-
import org.junit.jupiter.api.Test;
import seedu.address.logic.commands.AddCommand;
@@ -23,8 +19,8 @@
import seedu.address.logic.commands.HelpCommand;
import seedu.address.logic.commands.ListCommand;
import seedu.address.logic.parser.exceptions.ParseException;
-import seedu.address.model.person.NameContainsKeywordsPredicate;
import seedu.address.model.person.Person;
+import seedu.address.model.person.predicates.NameContainsKeywordPredicate;
import seedu.address.testutil.EditPersonDescriptorBuilder;
import seedu.address.testutil.PersonBuilder;
import seedu.address.testutil.PersonUtil;
@@ -73,10 +69,10 @@ public void parseCommand_exit() throws Exception {
@Test
public void parseCommand_find() throws Exception {
- List keywords = Arrays.asList("foo", "bar", "baz");
+ String arguments = "n/John";
FindCommand command = (FindCommand) parser.parseCommand(
- FindCommand.COMMAND_WORD + " " + keywords.stream().collect(Collectors.joining(" ")));
- assertEquals(new FindCommand(new NameContainsKeywordsPredicate(keywords)), command);
+ FindCommand.COMMAND_WORD + " " + arguments);
+ assertEquals(new FindCommand(new NameContainsKeywordPredicate("John")), command);
}
@Test
diff --git a/src/test/java/seedu/address/logic/parser/FindCommandParserTest.java b/src/test/java/seedu/address/logic/parser/FindCommandParserTest.java
index 9db96f64a67..dcf58c2e299 100644
--- a/src/test/java/seedu/address/logic/parser/FindCommandParserTest.java
+++ b/src/test/java/seedu/address/logic/parser/FindCommandParserTest.java
@@ -2,14 +2,10 @@
import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
-import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
-
-import java.util.Arrays;
import org.junit.jupiter.api.Test;
import seedu.address.logic.commands.FindCommand;
-import seedu.address.model.person.NameContainsKeywordsPredicate;
public class FindCommandParserTest {
@@ -21,15 +17,4 @@ public void parse_emptyArg_throwsParseException() {
FindCommand.MESSAGE_USAGE));
}
- @Test
- public void parse_validArgs_returnsFindCommand() {
- // no leading and trailing whitespaces
- FindCommand expectedFindCommand =
- new FindCommand(new NameContainsKeywordsPredicate(Arrays.asList("Alice", "Bob")));
- assertParseSuccess(parser, "Alice Bob", expectedFindCommand);
-
- // multiple whitespaces between keywords
- assertParseSuccess(parser, " \n Alice \n \t Bob \t", expectedFindCommand);
- }
-
}
diff --git a/src/test/java/seedu/address/model/ModelManagerTest.java b/src/test/java/seedu/address/model/ModelManagerTest.java
index 2cf1418d116..f75921e6319 100644
--- a/src/test/java/seedu/address/model/ModelManagerTest.java
+++ b/src/test/java/seedu/address/model/ModelManagerTest.java
@@ -15,7 +15,7 @@
import org.junit.jupiter.api.Test;
import seedu.address.commons.core.GuiSettings;
-import seedu.address.model.person.NameContainsKeywordsPredicate;
+import seedu.address.model.person.predicates.NameContainsKeywordPredicate;
import seedu.address.testutil.AddressBookBuilder;
public class ModelManagerTest {
@@ -118,7 +118,7 @@ public void equals() {
// different filteredList -> returns false
String[] keywords = ALICE.getName().fullName.split("\\s+");
- modelManager.updateFilteredPersonList(new NameContainsKeywordsPredicate(Arrays.asList(keywords)));
+ modelManager.updateFilteredPersonList(new NameContainsKeywordPredicate(Arrays.asList(keywords)));
assertFalse(modelManager.equals(new ModelManager(addressBook, userPrefs)));
// resets modelManager to initial state for upcoming tests
diff --git a/src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java b/src/test/java/seedu/address/model/person/predicates/NameContainsKeywordPredicateTest.java
similarity index 57%
rename from src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java
rename to src/test/java/seedu/address/model/person/predicates/NameContainsKeywordPredicateTest.java
index f136664e017..c3f72781110 100644
--- a/src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java
+++ b/src/test/java/seedu/address/model/person/predicates/NameContainsKeywordPredicateTest.java
@@ -1,31 +1,30 @@
-package seedu.address.model.person;
+package seedu.address.model.person.predicates;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.Arrays;
import java.util.Collections;
-import java.util.List;
import org.junit.jupiter.api.Test;
import seedu.address.testutil.PersonBuilder;
-public class NameContainsKeywordsPredicateTest {
+public class NameContainsKeywordPredicateTest {
@Test
public void equals() {
- List firstPredicateKeywordList = Collections.singletonList("first");
- List secondPredicateKeywordList = Arrays.asList("first", "second");
+ String firstPredicateKeyword = "first";
+ String secondPredicateKeyword = "first";
- NameContainsKeywordsPredicate firstPredicate = new NameContainsKeywordsPredicate(firstPredicateKeywordList);
- NameContainsKeywordsPredicate secondPredicate = new NameContainsKeywordsPredicate(secondPredicateKeywordList);
+ NameContainsKeywordPredicate firstPredicate = new NameContainsKeywordPredicate(firstPredicateKeyword);
+ NameContainsKeywordPredicate secondPredicate = new NameContainsKeywordPredicate(secondPredicateKeyword);
// same object -> returns true
assertTrue(firstPredicate.equals(firstPredicate));
// same values -> returns true
- NameContainsKeywordsPredicate firstPredicateCopy = new NameContainsKeywordsPredicate(firstPredicateKeywordList);
+ NameContainsKeywordPredicate firstPredicateCopy = new NameContainsKeywordPredicate(firstPredicateKeyword);
assertTrue(firstPredicate.equals(firstPredicateCopy));
// different types -> returns false
@@ -35,40 +34,40 @@ public void equals() {
assertFalse(firstPredicate.equals(null));
// different person -> returns false
- assertFalse(firstPredicate.equals(secondPredicate));
+ assertTrue(firstPredicate.equals(secondPredicate));
}
@Test
public void test_nameContainsKeywords_returnsTrue() {
// One keyword
- NameContainsKeywordsPredicate predicate = new NameContainsKeywordsPredicate(Collections.singletonList("Alice"));
+ NameContainsKeywordPredicate predicate = new NameContainsKeywordPredicate(Collections.singletonList("Alice"));
assertTrue(predicate.test(new PersonBuilder().withName("Alice Bob").build()));
// Multiple keywords
- predicate = new NameContainsKeywordsPredicate(Arrays.asList("Alice", "Bob"));
+ predicate = new NameContainsKeywordPredicate(Arrays.asList("Alice", "Bob"));
assertTrue(predicate.test(new PersonBuilder().withName("Alice Bob").build()));
// Only one matching keyword
- predicate = new NameContainsKeywordsPredicate(Arrays.asList("Bob", "Carol"));
+ predicate = new NameContainsKeywordPredicate(Arrays.asList("Bob", "Carol"));
assertTrue(predicate.test(new PersonBuilder().withName("Alice Carol").build()));
// Mixed-case keywords
- predicate = new NameContainsKeywordsPredicate(Arrays.asList("aLIce", "bOB"));
+ predicate = new NameContainsKeywordPredicate(Arrays.asList("aLIce", "bOB"));
assertTrue(predicate.test(new PersonBuilder().withName("Alice Bob").build()));
}
@Test
public void test_nameDoesNotContainKeywords_returnsFalse() {
// Zero keywords
- NameContainsKeywordsPredicate predicate = new NameContainsKeywordsPredicate(Collections.emptyList());
+ NameContainsKeywordPredicate predicate = new NameContainsKeywordPredicate(Collections.emptyList());
assertFalse(predicate.test(new PersonBuilder().withName("Alice").build()));
// Non-matching keyword
- predicate = new NameContainsKeywordsPredicate(Arrays.asList("Carol"));
+ predicate = new NameContainsKeywordPredicate(Arrays.asList("Carol"));
assertFalse(predicate.test(new PersonBuilder().withName("Alice Bob").build()));
// Keywords match phone, email and address, but does not match name
- predicate = new NameContainsKeywordsPredicate(Arrays.asList("12345", "alice@email.com", "Main", "Street"));
+ predicate = new NameContainsKeywordPredicate(Arrays.asList("12345", "alice@email.com", "Main", "Street"));
assertFalse(predicate.test(new PersonBuilder().withName("Alice").withPhone("12345")
.withEmail("alice@email.com").withAddress("Main Street").build()));
}