From 3ced7a3af41e9a5c35934df541ac44c9858463df Mon Sep 17 00:00:00 2001 From: yongxiangng Date: Wed, 6 Oct 2021 10:41:55 +0800 Subject: [PATCH 1/8] Add details command and details command parser --- .../seedu/address/commons/core/Messages.java | 2 + .../logic/commands/DetailsCommand.java | 44 +++++++++++++++++++ .../logic/parser/AddressBookParser.java | 4 ++ .../logic/parser/DetailsCommandParser.java | 27 ++++++++++++ 4 files changed, 77 insertions(+) create mode 100644 src/main/java/seedu/address/logic/commands/DetailsCommand.java create mode 100644 src/main/java/seedu/address/logic/parser/DetailsCommandParser.java diff --git a/src/main/java/seedu/address/commons/core/Messages.java b/src/main/java/seedu/address/commons/core/Messages.java index 1deb3a1e469..bed459cde41 100644 --- a/src/main/java/seedu/address/commons/core/Messages.java +++ b/src/main/java/seedu/address/commons/core/Messages.java @@ -9,5 +9,7 @@ public class Messages { public static final String MESSAGE_INVALID_COMMAND_FORMAT = "Invalid command format! \n%1$s"; public static final String MESSAGE_INVALID_PERSON_DISPLAYED_INDEX = "The person index provided is invalid"; public static final String MESSAGE_PERSONS_LISTED_OVERVIEW = "%1$d persons listed!"; + public static final String MESSAGE_PERSON_DETAILS_FOUND = "Showing details of %1$s"; + public static final String MESSAGE_PERSON_DETAILS_NOT_FOUND = "Person with name %1$s not found!"; } diff --git a/src/main/java/seedu/address/logic/commands/DetailsCommand.java b/src/main/java/seedu/address/logic/commands/DetailsCommand.java new file mode 100644 index 00000000000..c24e2c4eaf9 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/DetailsCommand.java @@ -0,0 +1,44 @@ +package seedu.address.logic.commands; + +import seedu.address.commons.core.Messages; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; + +import static java.util.Objects.requireNonNull; + +/** + * Finds and displays the details of person whose name matches the keyword exactly. + * Keyword matching is exact and case sensitive. + */ +public class DetailsCommand extends Command { + + public static final String COMMAND_WORD = "details"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Displays the details of a member.\n" + + "Parameters: NAME (Case Sensitive)\n" + + "Example: " + COMMAND_WORD + " Xiao Ming"; + + private final String keyword; + + public DetailsCommand(String keyword) { + this.keyword = keyword; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + // TODO: update the logic here + requireNonNull(model); + System.out.println(keyword); +// return new CommandResult( +// String.format(Messages.MESSAGE_PERSON_DETAILS_NOT_FOUND, keyword)); + return new CommandResult( + String.format(Messages.MESSAGE_PERSON_DETAILS_FOUND, keyword)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof DetailsCommand // instanceof handles nulls + && keyword.equals(((DetailsCommand) other).keyword)); // state check + } +} diff --git a/src/main/java/seedu/address/logic/parser/AddressBookParser.java b/src/main/java/seedu/address/logic/parser/AddressBookParser.java index 1e466792b46..cad7e8058ff 100644 --- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java +++ b/src/main/java/seedu/address/logic/parser/AddressBookParser.java @@ -10,6 +10,7 @@ import seedu.address.logic.commands.ClearCommand; import seedu.address.logic.commands.Command; import seedu.address.logic.commands.DeleteCommand; +import seedu.address.logic.commands.DetailsCommand; import seedu.address.logic.commands.EditCommand; import seedu.address.logic.commands.ExitCommand; import seedu.address.logic.commands.FindCommand; @@ -53,6 +54,9 @@ public Command parseCommand(String userInput) throws ParseException { case DeleteCommand.COMMAND_WORD: return new DeleteCommandParser().parse(arguments); + case DetailsCommand.COMMAND_WORD: + return new DetailsCommandParser().parse(arguments); + case ClearCommand.COMMAND_WORD: return new ClearCommand(); diff --git a/src/main/java/seedu/address/logic/parser/DetailsCommandParser.java b/src/main/java/seedu/address/logic/parser/DetailsCommandParser.java new file mode 100644 index 00000000000..8981db920ae --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/DetailsCommandParser.java @@ -0,0 +1,27 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.address.logic.commands.DetailsCommand; +import seedu.address.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new DetailsCommand object + */ +public class DetailsCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the DetailsCommand + * and returns a DetailsCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public DetailsCommand parse(String args) throws ParseException { + String trimmedArgs = args.trim(); + if (trimmedArgs.isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DetailsCommand.MESSAGE_USAGE)); + } + return new DetailsCommand(trimmedArgs); + } + +} From 96999b99405361b5477836ea6f9ca0a9efe22403 Mon Sep 17 00:00:00 2001 From: yongxiangng Date: Wed, 6 Oct 2021 10:45:00 +0800 Subject: [PATCH 2/8] Update message usage for details command --- .../java/seedu/address/logic/commands/DetailsCommand.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/seedu/address/logic/commands/DetailsCommand.java b/src/main/java/seedu/address/logic/commands/DetailsCommand.java index c24e2c4eaf9..83e0e368c0b 100644 --- a/src/main/java/seedu/address/logic/commands/DetailsCommand.java +++ b/src/main/java/seedu/address/logic/commands/DetailsCommand.java @@ -14,8 +14,9 @@ public class DetailsCommand extends Command { public static final String COMMAND_WORD = "details"; - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Displays the details of a member.\n" - + "Parameters: NAME (Case Sensitive)\n" + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Displays the details of a member from the " + + "specified keywords (case-sensitive and exact match).\n" + + "Parameters: NAME (case-sensitive)\n" + "Example: " + COMMAND_WORD + " Xiao Ming"; private final String keyword; From 9db5d60ab85919b5e488f5b248b1252c1d2041e5 Mon Sep 17 00:00:00 2001 From: yongxiangng Date: Wed, 6 Oct 2021 10:50:33 +0800 Subject: [PATCH 3/8] Fix failing checkstyle --- .../java/seedu/address/logic/commands/DetailsCommand.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/seedu/address/logic/commands/DetailsCommand.java b/src/main/java/seedu/address/logic/commands/DetailsCommand.java index 83e0e368c0b..7bc2a5b8baf 100644 --- a/src/main/java/seedu/address/logic/commands/DetailsCommand.java +++ b/src/main/java/seedu/address/logic/commands/DetailsCommand.java @@ -1,11 +1,11 @@ package seedu.address.logic.commands; +import static java.util.Objects.requireNonNull; + import seedu.address.commons.core.Messages; import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.model.Model; -import static java.util.Objects.requireNonNull; - /** * Finds and displays the details of person whose name matches the keyword exactly. * Keyword matching is exact and case sensitive. @@ -30,8 +30,6 @@ public CommandResult execute(Model model) throws CommandException { // TODO: update the logic here requireNonNull(model); System.out.println(keyword); -// return new CommandResult( -// String.format(Messages.MESSAGE_PERSON_DETAILS_NOT_FOUND, keyword)); return new CommandResult( String.format(Messages.MESSAGE_PERSON_DETAILS_FOUND, keyword)); } From ba09598c937cd0f39e07888622a3ffa38ccb1daf Mon Sep 17 00:00:00 2001 From: yongxiangng Date: Wed, 6 Oct 2021 11:08:53 +0800 Subject: [PATCH 4/8] Update details command parser to use ParserUtil abstraction --- .../logic/commands/DetailsCommand.java | 26 ++++++++++++------- .../logic/parser/DetailsCommandParser.java | 8 +++--- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/main/java/seedu/address/logic/commands/DetailsCommand.java b/src/main/java/seedu/address/logic/commands/DetailsCommand.java index 7bc2a5b8baf..201093dce14 100644 --- a/src/main/java/seedu/address/logic/commands/DetailsCommand.java +++ b/src/main/java/seedu/address/logic/commands/DetailsCommand.java @@ -2,9 +2,12 @@ import static java.util.Objects.requireNonNull; +import java.util.List; + import seedu.address.commons.core.Messages; -import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.model.Model; +import seedu.address.model.person.Name; +import seedu.address.model.person.Person; /** * Finds and displays the details of person whose name matches the keyword exactly. @@ -19,25 +22,30 @@ public class DetailsCommand extends Command { + "Parameters: NAME (case-sensitive)\n" + "Example: " + COMMAND_WORD + " Xiao Ming"; - private final String keyword; + private final Name name; - public DetailsCommand(String keyword) { - this.keyword = keyword; + public DetailsCommand(Name name) { + this.name = name; } @Override - public CommandResult execute(Model model) throws CommandException { - // TODO: update the logic here + public CommandResult execute(Model model) { requireNonNull(model); - System.out.println(keyword); + List persons = model.getAddressBook().getPersonList(); + for (Person person : persons) { + if (person.getName().equals(name)) { + return new CommandResult( + String.format(Messages.MESSAGE_PERSON_DETAILS_FOUND, name)); + } + } return new CommandResult( - String.format(Messages.MESSAGE_PERSON_DETAILS_FOUND, keyword)); + String.format(Messages.MESSAGE_PERSON_DETAILS_NOT_FOUND, name)); } @Override public boolean equals(Object other) { return other == this // short circuit if same object || (other instanceof DetailsCommand // instanceof handles nulls - && keyword.equals(((DetailsCommand) other).keyword)); // state check + && name.equals(((DetailsCommand) other).name)); // state check } } diff --git a/src/main/java/seedu/address/logic/parser/DetailsCommandParser.java b/src/main/java/seedu/address/logic/parser/DetailsCommandParser.java index 8981db920ae..36ff39801b3 100644 --- a/src/main/java/seedu/address/logic/parser/DetailsCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/DetailsCommandParser.java @@ -4,6 +4,7 @@ import seedu.address.logic.commands.DetailsCommand; import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.person.Name; /** * Parses input arguments and creates a new DetailsCommand object @@ -16,12 +17,13 @@ public class DetailsCommandParser implements Parser { * @throws ParseException if the user input does not conform the expected format */ public DetailsCommand parse(String args) throws ParseException { - String trimmedArgs = args.trim(); - if (trimmedArgs.isEmpty()) { + try { + Name name = ParserUtil.parseName(args); + return new DetailsCommand(name); + } catch (ParseException pe) { throw new ParseException( String.format(MESSAGE_INVALID_COMMAND_FORMAT, DetailsCommand.MESSAGE_USAGE)); } - return new DetailsCommand(trimmedArgs); } } From 23c4121f6a6f4f0c2fd44e1dc147fc584ee0701e Mon Sep 17 00:00:00 2001 From: yongxiangng Date: Wed, 6 Oct 2021 11:36:35 +0800 Subject: [PATCH 5/8] Create details fxml --- .../seedu/address/ui/PersonDetailsCard.java | 73 +++++++++++++++++++ .../resources/view/PersonDetailsCard.fxml | 40 ++++++++++ 2 files changed, 113 insertions(+) create mode 100644 src/main/java/seedu/address/ui/PersonDetailsCard.java create mode 100644 src/main/resources/view/PersonDetailsCard.fxml diff --git a/src/main/java/seedu/address/ui/PersonDetailsCard.java b/src/main/java/seedu/address/ui/PersonDetailsCard.java new file mode 100644 index 00000000000..866c9a0c23a --- /dev/null +++ b/src/main/java/seedu/address/ui/PersonDetailsCard.java @@ -0,0 +1,73 @@ +package seedu.address.ui; + +import java.util.Comparator; + +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.scene.layout.FlowPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; +import seedu.address.model.person.Person; + +/** + * An UI component that displays the detailed information of a {@code Person}. + */ +public class PersonDetailsCard extends UiPart { + + private static final String FXML = "PersonDetailsCard.fxml"; + + /** + * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX. + * As a consequence, UI elements' variable names cannot be set to such keywords + * or an exception will be thrown by JavaFX during runtime. + * + * @see The issue on AddressBook level 4 + */ + + public final Person person; + + @FXML + private HBox cardPane; + @FXML + private Label name; + @FXML + private Label phone; + @FXML + private Label address; + @FXML + private Label email; + @FXML + private FlowPane tags; + + /** + * Creates a {@code PersonDetailsCard} with the given {@code Person}. + */ + public PersonDetailsCard(Person person) { + super(FXML); + this.person = person; + name.setText(person.getName().fullName); + phone.setText(person.getPhone().value); + address.setText(person.getAddress().value); + email.setText(person.getEmail().value); + person.getTags().stream() + .sorted(Comparator.comparing(tag -> tag.tagName)) + .forEach(tag -> tags.getChildren().add(new Label(tag.tagName))); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof PersonDetailsCard)) { + return false; + } + + // state check + PersonDetailsCard card = (PersonDetailsCard) other; + return person.equals(card.person); + } +} diff --git a/src/main/resources/view/PersonDetailsCard.fxml b/src/main/resources/view/PersonDetailsCard.fxml new file mode 100644 index 00000000000..932e7188e46 --- /dev/null +++ b/src/main/resources/view/PersonDetailsCard.fxml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 298be5eaf1a2e32fe9bad92ff19d3dd7ca60dd4f Mon Sep 17 00:00:00 2001 From: yongxiangng Date: Wed, 6 Oct 2021 11:59:16 +0800 Subject: [PATCH 6/8] Add NameContainsKeywordsPredicate --- .../logic/commands/DetailsCommand.java | 19 +++++------ src/main/java/seedu/address/model/Model.java | 1 + .../person/NameEqualKeywordPredicate.java | 32 +++++++++++++++++++ 3 files changed, 41 insertions(+), 11 deletions(-) create mode 100644 src/main/java/seedu/address/model/person/NameEqualKeywordPredicate.java diff --git a/src/main/java/seedu/address/logic/commands/DetailsCommand.java b/src/main/java/seedu/address/logic/commands/DetailsCommand.java index 201093dce14..b39c1a3bbcd 100644 --- a/src/main/java/seedu/address/logic/commands/DetailsCommand.java +++ b/src/main/java/seedu/address/logic/commands/DetailsCommand.java @@ -2,12 +2,10 @@ import static java.util.Objects.requireNonNull; -import java.util.List; - import seedu.address.commons.core.Messages; import seedu.address.model.Model; import seedu.address.model.person.Name; -import seedu.address.model.person.Person; +import seedu.address.model.person.NameEqualKeywordPredicate; /** * Finds and displays the details of person whose name matches the keyword exactly. @@ -31,15 +29,14 @@ public DetailsCommand(Name name) { @Override public CommandResult execute(Model model) { requireNonNull(model); - List persons = model.getAddressBook().getPersonList(); - for (Person person : persons) { - if (person.getName().equals(name)) { - return new CommandResult( - String.format(Messages.MESSAGE_PERSON_DETAILS_FOUND, name)); - } + model.updateFilteredPersonList(new NameEqualKeywordPredicate(name)); + if (model.getFilteredPersonList().size() == 0) { + return new CommandResult( + String.format(Messages.MESSAGE_PERSON_DETAILS_NOT_FOUND, name)); + } else { + return new CommandResult( + String.format(Messages.MESSAGE_PERSON_DETAILS_FOUND, name)); } - return new CommandResult( - String.format(Messages.MESSAGE_PERSON_DETAILS_NOT_FOUND, name)); } @Override diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java index d54df471c1f..36e3bba9bfb 100644 --- a/src/main/java/seedu/address/model/Model.java +++ b/src/main/java/seedu/address/model/Model.java @@ -84,4 +84,5 @@ public interface Model { * @throws NullPointerException if {@code predicate} is null. */ void updateFilteredPersonList(Predicate predicate); + } diff --git a/src/main/java/seedu/address/model/person/NameEqualKeywordPredicate.java b/src/main/java/seedu/address/model/person/NameEqualKeywordPredicate.java new file mode 100644 index 00000000000..1d20f81d16a --- /dev/null +++ b/src/main/java/seedu/address/model/person/NameEqualKeywordPredicate.java @@ -0,0 +1,32 @@ +package seedu.address.model.person; + +import java.util.function.Predicate; + +/** + * Tests that a {@code Person}'s {@code Name} matches the keyword given exactly. + */ +public class NameEqualKeywordPredicate implements Predicate { + private final Name keyword; + + public NameEqualKeywordPredicate(Name keyword) { + this.keyword = keyword; + } + + @Override + public boolean test(Person person) { + return keyword.equals(person.getName()); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof NameEqualKeywordPredicate // instanceof handles nulls + && keyword.equals(((NameEqualKeywordPredicate) other).keyword)); // state check + } + + @Override + public String toString() { + return this.keyword.toString(); + } + +} From 0f63a4d401ca6e93f52aea2f353608e1a64a2d15 Mon Sep 17 00:00:00 2001 From: yongxiangng Date: Wed, 6 Oct 2021 15:45:01 +0800 Subject: [PATCH 7/8] Connect endpoints between ui and logic unit for details --- .../address/logic/commands/CommandResult.java | 12 ++++++++++-- .../address/logic/commands/DetailsCommand.java | 17 +++++++++-------- .../address/logic/commands/ExitCommand.java | 2 +- .../address/logic/commands/HelpCommand.java | 2 +- .../logic/parser/DetailsCommandParser.java | 3 ++- src/main/java/seedu/address/ui/MainWindow.java | 2 ++ .../java/seedu/address/ui/PersonListPanel.java | 12 ++++++++++++ .../logic/commands/CommandResultTest.java | 10 +++++----- .../address/logic/commands/ExitCommandTest.java | 2 +- .../address/logic/commands/HelpCommandTest.java | 2 +- 10 files changed, 44 insertions(+), 20 deletions(-) diff --git a/src/main/java/seedu/address/logic/commands/CommandResult.java b/src/main/java/seedu/address/logic/commands/CommandResult.java index 92f900b7916..3e348c2a353 100644 --- a/src/main/java/seedu/address/logic/commands/CommandResult.java +++ b/src/main/java/seedu/address/logic/commands/CommandResult.java @@ -17,13 +17,17 @@ public class CommandResult { /** The application should exit. */ private final boolean exit; + /** The application should show the details component in the ui. */ + private final boolean showDetails; + /** * Constructs a {@code CommandResult} with the specified fields. */ - public CommandResult(String feedbackToUser, boolean showHelp, boolean exit) { + public CommandResult(String feedbackToUser, boolean showHelp, boolean exit, boolean showDetails) { this.feedbackToUser = requireNonNull(feedbackToUser); this.showHelp = showHelp; this.exit = exit; + this.showDetails = showDetails; } /** @@ -31,7 +35,7 @@ public CommandResult(String feedbackToUser, boolean showHelp, boolean exit) { * and other fields set to their default value. */ public CommandResult(String feedbackToUser) { - this(feedbackToUser, false, false); + this(feedbackToUser, false, false, false); } public String getFeedbackToUser() { @@ -46,6 +50,10 @@ public boolean isExit() { return exit; } + public boolean isShowDetails() { + return showDetails; + } + @Override public boolean equals(Object other) { if (other == this) { diff --git a/src/main/java/seedu/address/logic/commands/DetailsCommand.java b/src/main/java/seedu/address/logic/commands/DetailsCommand.java index b39c1a3bbcd..4b49bcdeb88 100644 --- a/src/main/java/seedu/address/logic/commands/DetailsCommand.java +++ b/src/main/java/seedu/address/logic/commands/DetailsCommand.java @@ -4,7 +4,6 @@ import seedu.address.commons.core.Messages; import seedu.address.model.Model; -import seedu.address.model.person.Name; import seedu.address.model.person.NameEqualKeywordPredicate; /** @@ -20,22 +19,24 @@ public class DetailsCommand extends Command { + "Parameters: NAME (case-sensitive)\n" + "Example: " + COMMAND_WORD + " Xiao Ming"; - private final Name name; + private final NameEqualKeywordPredicate predicate; - public DetailsCommand(Name name) { - this.name = name; + public DetailsCommand(NameEqualKeywordPredicate predicate) { + this.predicate = predicate; } @Override public CommandResult execute(Model model) { requireNonNull(model); - model.updateFilteredPersonList(new NameEqualKeywordPredicate(name)); + model.updateFilteredPersonList(predicate); if (model.getFilteredPersonList().size() == 0) { return new CommandResult( - String.format(Messages.MESSAGE_PERSON_DETAILS_NOT_FOUND, name)); + String.format(Messages.MESSAGE_PERSON_DETAILS_NOT_FOUND, predicate), + false, false, true); } else { return new CommandResult( - String.format(Messages.MESSAGE_PERSON_DETAILS_FOUND, name)); + String.format(Messages.MESSAGE_PERSON_DETAILS_FOUND, predicate), + false, false, true); } } @@ -43,6 +44,6 @@ public CommandResult execute(Model model) { public boolean equals(Object other) { return other == this // short circuit if same object || (other instanceof DetailsCommand // instanceof handles nulls - && name.equals(((DetailsCommand) other).name)); // state check + && predicate.equals(((DetailsCommand) other).predicate)); // state check } } diff --git a/src/main/java/seedu/address/logic/commands/ExitCommand.java b/src/main/java/seedu/address/logic/commands/ExitCommand.java index 3dd85a8ba90..acac9a21374 100644 --- a/src/main/java/seedu/address/logic/commands/ExitCommand.java +++ b/src/main/java/seedu/address/logic/commands/ExitCommand.java @@ -13,7 +13,7 @@ public class ExitCommand extends Command { @Override public CommandResult execute(Model model) { - return new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true); + return new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true, false); } } diff --git a/src/main/java/seedu/address/logic/commands/HelpCommand.java b/src/main/java/seedu/address/logic/commands/HelpCommand.java index bf824f91bd0..07d26e2a23c 100644 --- a/src/main/java/seedu/address/logic/commands/HelpCommand.java +++ b/src/main/java/seedu/address/logic/commands/HelpCommand.java @@ -16,6 +16,6 @@ public class HelpCommand extends Command { @Override public CommandResult execute(Model model) { - return new CommandResult(SHOWING_HELP_MESSAGE, true, false); + return new CommandResult(SHOWING_HELP_MESSAGE, true, false, false); } } diff --git a/src/main/java/seedu/address/logic/parser/DetailsCommandParser.java b/src/main/java/seedu/address/logic/parser/DetailsCommandParser.java index 36ff39801b3..ecf739a2191 100644 --- a/src/main/java/seedu/address/logic/parser/DetailsCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/DetailsCommandParser.java @@ -5,6 +5,7 @@ import seedu.address.logic.commands.DetailsCommand; import seedu.address.logic.parser.exceptions.ParseException; import seedu.address.model.person.Name; +import seedu.address.model.person.NameEqualKeywordPredicate; /** * Parses input arguments and creates a new DetailsCommand object @@ -19,7 +20,7 @@ public class DetailsCommandParser implements Parser { public DetailsCommand parse(String args) throws ParseException { try { Name name = ParserUtil.parseName(args); - return new DetailsCommand(name); + return new DetailsCommand(new NameEqualKeywordPredicate(name)); } catch (ParseException pe) { throw new ParseException( String.format(MESSAGE_INVALID_COMMAND_FORMAT, DetailsCommand.MESSAGE_USAGE)); diff --git a/src/main/java/seedu/address/ui/MainWindow.java b/src/main/java/seedu/address/ui/MainWindow.java index 9106c3aa6e5..6f86b3cb037 100644 --- a/src/main/java/seedu/address/ui/MainWindow.java +++ b/src/main/java/seedu/address/ui/MainWindow.java @@ -186,6 +186,8 @@ private CommandResult executeCommand(String commandText) throws CommandException handleExit(); } + personListPanel.setShowDetails(commandResult.isShowDetails()); + return commandResult; } catch (CommandException | ParseException e) { logger.info("Invalid command: " + commandText); diff --git a/src/main/java/seedu/address/ui/PersonListPanel.java b/src/main/java/seedu/address/ui/PersonListPanel.java index f4c501a897b..d62bc7da78e 100644 --- a/src/main/java/seedu/address/ui/PersonListPanel.java +++ b/src/main/java/seedu/address/ui/PersonListPanel.java @@ -16,6 +16,7 @@ public class PersonListPanel extends UiPart { private static final String FXML = "PersonListPanel.fxml"; private final Logger logger = LogsCenter.getLogger(PersonListPanel.class); + private boolean showDetails = false; @FXML private ListView personListView; @@ -29,6 +30,15 @@ public PersonListPanel(ObservableList personList) { personListView.setCellFactory(listView -> new PersonListViewCell()); } + /** + * Sets the details field to parameter passed. + * + * @param showDetails boolean value to show details. + */ + public void setShowDetails(boolean showDetails) { + this.showDetails = showDetails; + } + /** * Custom {@code ListCell} that displays the graphics of a {@code Person} using a {@code PersonCard}. */ @@ -40,6 +50,8 @@ protected void updateItem(Person person, boolean empty) { if (empty || person == null) { setGraphic(null); setText(null); + } else if (showDetails) { + setGraphic(new PersonDetailsCard(person).getRoot()); } else { setGraphic(new PersonCard(person, getIndex() + 1).getRoot()); } diff --git a/src/test/java/seedu/address/logic/commands/CommandResultTest.java b/src/test/java/seedu/address/logic/commands/CommandResultTest.java index 4f3eb46e9ef..d026fa5a9e1 100644 --- a/src/test/java/seedu/address/logic/commands/CommandResultTest.java +++ b/src/test/java/seedu/address/logic/commands/CommandResultTest.java @@ -14,7 +14,7 @@ public void equals() { // same values -> returns true assertTrue(commandResult.equals(new CommandResult("feedback"))); - assertTrue(commandResult.equals(new CommandResult("feedback", false, false))); + assertTrue(commandResult.equals(new CommandResult("feedback", false, false, false))); // same object -> returns true assertTrue(commandResult.equals(commandResult)); @@ -29,10 +29,10 @@ public void equals() { assertFalse(commandResult.equals(new CommandResult("different"))); // different showHelp value -> returns false - assertFalse(commandResult.equals(new CommandResult("feedback", true, false))); + assertFalse(commandResult.equals(new CommandResult("feedback", true, false, false))); // different exit value -> returns false - assertFalse(commandResult.equals(new CommandResult("feedback", false, true))); + assertFalse(commandResult.equals(new CommandResult("feedback", false, true, false))); } @Test @@ -46,9 +46,9 @@ public void hashcode() { assertNotEquals(commandResult.hashCode(), new CommandResult("different").hashCode()); // different showHelp value -> returns different hashcode - assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", true, false).hashCode()); + assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", true, false, false).hashCode()); // different exit value -> returns different hashcode - assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", false, true).hashCode()); + assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", false, true, false).hashCode()); } } diff --git a/src/test/java/seedu/address/logic/commands/ExitCommandTest.java b/src/test/java/seedu/address/logic/commands/ExitCommandTest.java index 9533c473875..a573f42f11b 100644 --- a/src/test/java/seedu/address/logic/commands/ExitCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/ExitCommandTest.java @@ -14,7 +14,7 @@ public class ExitCommandTest { @Test public void execute_exit_success() { - CommandResult expectedCommandResult = new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true); + CommandResult expectedCommandResult = new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true, false); assertCommandSuccess(new ExitCommand(), model, expectedCommandResult, expectedModel); } } diff --git a/src/test/java/seedu/address/logic/commands/HelpCommandTest.java b/src/test/java/seedu/address/logic/commands/HelpCommandTest.java index 4904fc4352e..3b35c387c4c 100644 --- a/src/test/java/seedu/address/logic/commands/HelpCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/HelpCommandTest.java @@ -14,7 +14,7 @@ public class HelpCommandTest { @Test public void execute_help_success() { - CommandResult expectedCommandResult = new CommandResult(SHOWING_HELP_MESSAGE, true, false); + CommandResult expectedCommandResult = new CommandResult(SHOWING_HELP_MESSAGE, true, false, false); assertCommandSuccess(new HelpCommand(), model, expectedCommandResult, expectedModel); } } From ff95db414f9941f9e1a4fcf2ddaffbde2dcc82bb Mon Sep 17 00:00:00 2001 From: yongxiangng Date: Wed, 6 Oct 2021 17:11:56 +0800 Subject: [PATCH 8/8] Add tests for details feature --- .../logic/commands/DetailsCommand.java | 1 + src/main/java/seedu/address/model/Model.java | 1 - .../logic/commands/DetailsCommandTest.java | 77 +++++++++++++++++++ .../logic/parser/AddressBookParserTest.java | 10 +++ .../parser/DetailsCommandParserTest.java | 40 ++++++++++ .../person/NameEqualKeywordPredicateTest.java | 70 +++++++++++++++++ 6 files changed, 198 insertions(+), 1 deletion(-) create mode 100644 src/test/java/seedu/address/logic/commands/DetailsCommandTest.java create mode 100644 src/test/java/seedu/address/logic/parser/DetailsCommandParserTest.java create mode 100644 src/test/java/seedu/address/model/person/NameEqualKeywordPredicateTest.java diff --git a/src/main/java/seedu/address/logic/commands/DetailsCommand.java b/src/main/java/seedu/address/logic/commands/DetailsCommand.java index 4b49bcdeb88..ce7c2b65b2f 100644 --- a/src/main/java/seedu/address/logic/commands/DetailsCommand.java +++ b/src/main/java/seedu/address/logic/commands/DetailsCommand.java @@ -29,6 +29,7 @@ public DetailsCommand(NameEqualKeywordPredicate predicate) { public CommandResult execute(Model model) { requireNonNull(model); model.updateFilteredPersonList(predicate); + assert model.getFilteredPersonList().size() < 2; if (model.getFilteredPersonList().size() == 0) { return new CommandResult( String.format(Messages.MESSAGE_PERSON_DETAILS_NOT_FOUND, predicate), diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java index 36e3bba9bfb..d54df471c1f 100644 --- a/src/main/java/seedu/address/model/Model.java +++ b/src/main/java/seedu/address/model/Model.java @@ -84,5 +84,4 @@ public interface Model { * @throws NullPointerException if {@code predicate} is null. */ void updateFilteredPersonList(Predicate predicate); - } diff --git a/src/test/java/seedu/address/logic/commands/DetailsCommandTest.java b/src/test/java/seedu/address/logic/commands/DetailsCommandTest.java new file mode 100644 index 00000000000..1e8778097cc --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/DetailsCommandTest.java @@ -0,0 +1,77 @@ +package seedu.address.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 seedu.address.commons.core.Messages.MESSAGE_PERSON_DETAILS_FOUND; +import static seedu.address.commons.core.Messages.MESSAGE_PERSON_DETAILS_NOT_FOUND; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.address.testutil.TypicalPersons.CARL; +import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; + +import java.util.Collections; + +import org.junit.jupiter.api.Test; + +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.UserPrefs; +import seedu.address.model.person.Name; +import seedu.address.model.person.NameEqualKeywordPredicate; + +/** + * Contains integration tests (interaction with the Model) for {@code DetailsCommand}. + */ +class DetailsCommandTest { + private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); + private Model expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs()); + + @Test + public void equals() { + NameEqualKeywordPredicate firstPredicate = + new NameEqualKeywordPredicate(new Name("first")); + NameEqualKeywordPredicate secondPredicate = + new NameEqualKeywordPredicate(new Name("second")); + + DetailsCommand findFirstCommand = new DetailsCommand(firstPredicate); + DetailsCommand findSecondCommand = new DetailsCommand(secondPredicate); + + // same object -> returns true + assertTrue(findFirstCommand.equals(findFirstCommand)); + + // same values -> returns true + DetailsCommand findFirstCommandCopy = new DetailsCommand(firstPredicate); + assertTrue(findFirstCommand.equals(findFirstCommandCopy)); + + // different types -> returns false + assertFalse(findFirstCommand.equals(1)); + + // null -> returns false + assertFalse(findFirstCommand.equals(null)); + + // different person -> returns false + assertFalse(findFirstCommand.equals(findSecondCommand)); + } + + @Test + public void execute_zeroKeywords_noPersonFound() { + String name = "cool name"; + String expectedMessage = String.format(MESSAGE_PERSON_DETAILS_NOT_FOUND, name); + NameEqualKeywordPredicate predicate = new NameEqualKeywordPredicate(new Name(name)); + DetailsCommand command = new DetailsCommand(predicate); + expectedModel.updateFilteredPersonList(predicate); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(Collections.emptyList(), model.getFilteredPersonList()); + } + + @Test + public void execute_multipleKeywords_multiplePersonsFound() { + Name name = CARL.getName(); + String expectedMessage = String.format(MESSAGE_PERSON_DETAILS_FOUND, name); + NameEqualKeywordPredicate predicate = new NameEqualKeywordPredicate(name); + DetailsCommand command = new DetailsCommand(predicate); + expectedModel.updateFilteredPersonList(predicate); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(Collections.singletonList(CARL), model.getFilteredPersonList()); + } +} diff --git a/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java b/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java index d9659205b57..9e1668a4396 100644 --- a/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java +++ b/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java @@ -16,6 +16,7 @@ import seedu.address.logic.commands.AddCommand; import seedu.address.logic.commands.ClearCommand; import seedu.address.logic.commands.DeleteCommand; +import seedu.address.logic.commands.DetailsCommand; import seedu.address.logic.commands.EditCommand; import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; import seedu.address.logic.commands.ExitCommand; @@ -24,6 +25,7 @@ import seedu.address.logic.commands.ListCommand; import seedu.address.logic.parser.exceptions.ParseException; import seedu.address.model.person.NameContainsKeywordsPredicate; +import seedu.address.model.person.NameEqualKeywordPredicate; import seedu.address.model.person.Person; import seedu.address.testutil.EditPersonDescriptorBuilder; import seedu.address.testutil.PersonBuilder; @@ -53,6 +55,14 @@ public void parseCommand_delete() throws Exception { assertEquals(new DeleteCommand(INDEX_FIRST_PERSON), command); } + @Test + public void parseCommand_details() throws Exception { + String keyword = "foo"; + DetailsCommand command = (DetailsCommand) parser.parseCommand( + DetailsCommand.COMMAND_WORD + " " + keyword); + assertEquals(new DetailsCommand(new NameEqualKeywordPredicate(ParserUtil.parseName(keyword))), command); + } + @Test public void parseCommand_edit() throws Exception { Person person = new PersonBuilder().build(); diff --git a/src/test/java/seedu/address/logic/parser/DetailsCommandParserTest.java b/src/test/java/seedu/address/logic/parser/DetailsCommandParserTest.java new file mode 100644 index 00000000000..16c001782c3 --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/DetailsCommandParserTest.java @@ -0,0 +1,40 @@ +package seedu.address.logic.parser; + +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 org.junit.jupiter.api.Test; + +import seedu.address.logic.commands.DetailsCommand; +import seedu.address.model.person.Name; +import seedu.address.model.person.NameEqualKeywordPredicate; + +class DetailsCommandParserTest { + + private DetailsCommandParser parser = new DetailsCommandParser(); + + @Test + public void parse_emptyArg_throwsParseException() { + assertParseFailure(parser, " ", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DetailsCommand.MESSAGE_USAGE)); + } + + @Test + public void parse_validArgs_returnsDetailsCommand() { + DetailsCommand expectedFindCommand = + new DetailsCommand(new NameEqualKeywordPredicate(new Name("Alice Bob"))); + + // No trailing and leading whitespace + assertParseSuccess(parser, "Alice Bob", expectedFindCommand); + + // Leading whitespace + assertParseSuccess(parser, " Alice Bob", expectedFindCommand); + + // Trailing whitespace + assertParseSuccess(parser, "Alice Bob ", expectedFindCommand); + + // Leading and trailing whitespace + assertParseSuccess(parser, " Alice Bob ", expectedFindCommand); + } +} diff --git a/src/test/java/seedu/address/model/person/NameEqualKeywordPredicateTest.java b/src/test/java/seedu/address/model/person/NameEqualKeywordPredicateTest.java new file mode 100644 index 00000000000..c01f6088929 --- /dev/null +++ b/src/test/java/seedu/address/model/person/NameEqualKeywordPredicateTest.java @@ -0,0 +1,70 @@ +package seedu.address.model.person; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +import seedu.address.testutil.PersonBuilder; + +class NameEqualKeywordPredicateTest { + @Test + public void equals() { + String firstKeyword = "keyword"; + String secondKeyword = "Keyword"; + String thirdKeyword = "keyword "; + + NameEqualKeywordPredicate firstPredicate = new NameEqualKeywordPredicate(new Name(firstKeyword)); + NameEqualKeywordPredicate secondPredicate = new NameEqualKeywordPredicate(new Name(secondKeyword)); + NameEqualKeywordPredicate thirdPredicate = new NameEqualKeywordPredicate(new Name(thirdKeyword)); + + // same object -> returns true + assertTrue(firstPredicate.equals(firstPredicate)); + + // same values -> returns true + NameEqualKeywordPredicate firstPredicateCopy = new NameEqualKeywordPredicate(new Name(firstKeyword)); + assertTrue(firstPredicate.equals(firstPredicateCopy)); + + // different types -> returns false + assertFalse(firstPredicate.equals(1)); + + // null -> returns false + assertFalse(firstPredicate.equals(null)); + + // different person -> returns false + assertFalse(firstPredicate.equals(secondPredicate)); + + // extra spacing -> returns false + assertFalse(thirdPredicate.equals(firstPredicate)); + } + + @Test + public void test_nameMatchKeyword_returnsTrue() { + // Exact 1 word match + NameEqualKeywordPredicate predicate = new NameEqualKeywordPredicate(new Name("Alice")); + assertTrue(predicate.test(new PersonBuilder().withName("Alice").build())); + + // Exact 2 word match + predicate = new NameEqualKeywordPredicate(new Name("Alice Bob")); + assertTrue(predicate.test(new PersonBuilder().withName("Alice Bob").build())); + } + + @Test + public void test_nameDoesNotMatchKeyword_returnsFalse() { + // Non-matching keyword + NameEqualKeywordPredicate predicate = new NameEqualKeywordPredicate(new Name("Carol")); + assertFalse(predicate.test(new PersonBuilder().withName("Alice").build())); + + // Partial match + predicate = new NameEqualKeywordPredicate(new Name("Carol")); + assertFalse(predicate.test(new PersonBuilder().withName("Caroline").build())); + + // Partial match 2 words + predicate = new NameEqualKeywordPredicate(new Name("Bob")); + assertFalse(predicate.test(new PersonBuilder().withName("Bob boy").build())); + + // Predicate with more words than person + predicate = new NameEqualKeywordPredicate(new Name("Bob the builder")); + assertFalse(predicate.test(new PersonBuilder().withName("Bob").build())); + } +}