From e4b07ffab91e690d81c9538b21dd8c11610c2fc9 Mon Sep 17 00:00:00 2001 From: Nam Do Hyun Date: Fri, 13 Oct 2023 13:37:02 +0800 Subject: [PATCH] Add ViewCommand and ViewExitCommand Add ViewCommand and ViewExitCommand for enhancements of editing functionality. --- .../seedu/address/logic/LogicManager.java | 28 +++++++- .../address/logic/commands/CommandResult.java | 38 ++++++++++- .../address/logic/commands/ViewCommand.java | 65 +++++++++++++++++++ .../logic/commands/ViewExitCommand.java | 21 ++++++ .../logic/parser/AddressBookParser.java | 4 ++ .../logic/parser/ViewCommandParser.java | 19 ++++++ .../address/logic/parser/ViewModeParser.java | 56 ++++++++++++++++ .../java/seedu/address/ui/MainWindow.java | 26 ++++++++ .../java/seedu/address/ui/PersonProfile.java | 54 +++++++++++++++ src/main/resources/view/PersonProfile.fxml | 15 +++++ 10 files changed, 321 insertions(+), 5 deletions(-) create mode 100644 src/main/java/seedu/address/logic/commands/ViewCommand.java create mode 100644 src/main/java/seedu/address/logic/commands/ViewExitCommand.java create mode 100644 src/main/java/seedu/address/logic/parser/ViewCommandParser.java create mode 100644 src/main/java/seedu/address/logic/parser/ViewModeParser.java create mode 100644 src/main/java/seedu/address/ui/PersonProfile.java create mode 100644 src/main/resources/view/PersonProfile.fxml diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/seedu/address/logic/LogicManager.java index 5aa3b91c7d0..3481f7656c6 100644 --- a/src/main/java/seedu/address/logic/LogicManager.java +++ b/src/main/java/seedu/address/logic/LogicManager.java @@ -10,8 +10,11 @@ import seedu.address.commons.core.LogsCenter; import seedu.address.logic.commands.Command; import seedu.address.logic.commands.CommandResult; +import seedu.address.logic.commands.ViewCommand; +import seedu.address.logic.commands.ViewExitCommand; import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.logic.parser.AddressBookParser; +import seedu.address.logic.parser.ViewModeParser; import seedu.address.logic.parser.exceptions.ParseException; import seedu.address.model.Model; import seedu.address.model.ReadOnlyAddressBook; @@ -26,12 +29,17 @@ public class LogicManager implements Logic { public static final String FILE_OPS_PERMISSION_ERROR_FORMAT = "Could not save data to file %s due to insufficient permissions to write to the file or the folder."; + public static final String WINDOW_IN_VIEW_MODE_ERROR = + "You cannot run other commands while in view profile mode."; private final Logger logger = LogsCenter.getLogger(LogicManager.class); private final Model model; private final Storage storage; private final AddressBookParser addressBookParser; + private final ViewModeParser viewModeParser; + + private boolean isInViewMode = false; /** * Constructs a {@code LogicManager} with the given {@code Model} and {@code Storage}. @@ -40,16 +48,32 @@ public LogicManager(Model model, Storage storage) { this.model = model; this.storage = storage; addressBookParser = new AddressBookParser(); + viewModeParser = new ViewModeParser(); } @Override public CommandResult execute(String commandText) throws CommandException, ParseException { logger.info("----------------[USER COMMAND][" + commandText + "]"); - + Command command; CommandResult commandResult; - Command command = addressBookParser.parseCommand(commandText); + + if (!isInViewMode) { + command = addressBookParser.parseCommand(commandText); + + } else { + command = viewModeParser.parseCommand(commandText); + } + commandResult = command.execute(model); + if (command instanceof ViewCommand) { + isInViewMode = true; + } + + if (command instanceof ViewExitCommand) { + isInViewMode = false; + } + try { storage.saveAddressBook(model.getAddressBook()); } catch (AccessDeniedException e) { diff --git a/src/main/java/seedu/address/logic/commands/CommandResult.java b/src/main/java/seedu/address/logic/commands/CommandResult.java index 249b6072d0d..4843e52fbfa 100644 --- a/src/main/java/seedu/address/logic/commands/CommandResult.java +++ b/src/main/java/seedu/address/logic/commands/CommandResult.java @@ -4,7 +4,9 @@ import java.util.Objects; +import seedu.address.commons.core.index.Index; import seedu.address.commons.util.ToStringBuilder; +import seedu.address.model.person.Person; /** * Represents the result of a command execution. @@ -19,13 +21,32 @@ public class CommandResult { /** The application should exit. */ private final boolean exit; + private final boolean showView; + + private final Person personToView; + + private final boolean viewExit; + + public CommandResult( + String feedbackToUser, + boolean showHelp, + boolean exit, + boolean showView, + Person personToView, + boolean viewExit) { + this.feedbackToUser = requireNonNull(feedbackToUser); + this.showHelp = showHelp; + this.exit = exit; + this.showView = showView; + this.personToView = personToView; + this.viewExit = viewExit; + } + /** * Constructs a {@code CommandResult} with the specified fields. */ public CommandResult(String feedbackToUser, boolean showHelp, boolean exit) { - this.feedbackToUser = requireNonNull(feedbackToUser); - this.showHelp = showHelp; - this.exit = exit; + this(requireNonNull(feedbackToUser), showHelp, exit, false, null, false); } /** @@ -48,6 +69,17 @@ public boolean isExit() { return exit; } + public boolean isShowView() { + return showView; + } + + public boolean isViewExit() { + return viewExit; + } + + public Person getPersonToView() { + return personToView; + } @Override public boolean equals(Object other) { if (other == this) { diff --git a/src/main/java/seedu/address/logic/commands/ViewCommand.java b/src/main/java/seedu/address/logic/commands/ViewCommand.java new file mode 100644 index 00000000000..d6aac29b782 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/ViewCommand.java @@ -0,0 +1,65 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.List; + +import seedu.address.commons.core.index.Index; +import seedu.address.commons.util.ToStringBuilder; +import seedu.address.logic.Messages; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.person.Person; + +public class ViewCommand extends Command { + public static final String COMMAND_WORD = "view"; + + private final Index targetIndex; + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Shows the profile of the user.\n" + + "Example: " + COMMAND_WORD + " 1"; + public static final String VIEWING_PROFILE_SUCCESS = "Viewing Person: %1$s"; + public ViewCommand(Index index) { + this.targetIndex = index; + } + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredPersonList(); + + if (targetIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + } + + Person personToView = lastShownList.get(targetIndex.getZeroBased()); + return new CommandResult( + String.format(VIEWING_PROFILE_SUCCESS, Messages.format(personToView)), + false, + false, + true, + personToView, + false); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof ViewCommand)) { + return false; + } + + ViewCommand e = (ViewCommand) other; + return targetIndex.equals(e.targetIndex); + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .add("targetIndex", targetIndex) + .toString(); + } +} diff --git a/src/main/java/seedu/address/logic/commands/ViewExitCommand.java b/src/main/java/seedu/address/logic/commands/ViewExitCommand.java new file mode 100644 index 00000000000..7e208fee1d9 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/ViewExitCommand.java @@ -0,0 +1,21 @@ +package seedu.address.logic.commands; + +import seedu.address.model.Model; + +public class ViewExitCommand extends Command { + public static final String COMMAND_WORD = "exit"; + + public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting view mode as requested ..."; + + @Override + public CommandResult execute(Model model) { + return new CommandResult( + MESSAGE_EXIT_ACKNOWLEDGEMENT, + false, + false, + false, + null, + true + ); + } +} diff --git a/src/main/java/seedu/address/logic/parser/AddressBookParser.java b/src/main/java/seedu/address/logic/parser/AddressBookParser.java index 3149ee07e0b..b3000b4fa0a 100644 --- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java +++ b/src/main/java/seedu/address/logic/parser/AddressBookParser.java @@ -17,6 +17,7 @@ import seedu.address.logic.commands.FindCommand; import seedu.address.logic.commands.HelpCommand; import seedu.address.logic.commands.ListCommand; +import seedu.address.logic.commands.ViewCommand; import seedu.address.logic.parser.exceptions.ParseException; /** @@ -71,6 +72,9 @@ public Command parseCommand(String userInput) throws ParseException { case ListCommand.COMMAND_WORD: return new ListCommand(); + case ViewCommand.COMMAND_WORD: + return new ViewCommandParser().parse(arguments); + case ExitCommand.COMMAND_WORD: return new ExitCommand(); diff --git a/src/main/java/seedu/address/logic/parser/ViewCommandParser.java b/src/main/java/seedu/address/logic/parser/ViewCommandParser.java new file mode 100644 index 00000000000..d4fd6748b6e --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/ViewCommandParser.java @@ -0,0 +1,19 @@ +package seedu.address.logic.parser; + +import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.ViewCommand; +import seedu.address.logic.parser.exceptions.ParseException; + +public class ViewCommandParser { + public ViewCommand parse(String args) throws ParseException { + try { + Index index = ParserUtil.parseIndex(args); + return new ViewCommand(index); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, ViewCommand.MESSAGE_USAGE), pe); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/ViewModeParser.java b/src/main/java/seedu/address/logic/parser/ViewModeParser.java new file mode 100644 index 00000000000..a0662a9c45d --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/ViewModeParser.java @@ -0,0 +1,56 @@ +package seedu.address.logic.parser; + +import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.Messages.MESSAGE_UNKNOWN_COMMAND; + +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import seedu.address.commons.core.LogsCenter; +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.EditCommand; +import seedu.address.logic.commands.HelpCommand; +import seedu.address.logic.commands.ViewExitCommand; +import seedu.address.logic.parser.exceptions.ParseException; + +public class ViewModeParser { + /** + * Used for initial separation of command word and args. + */ + private static final Pattern BASIC_COMMAND_FORMAT = Pattern.compile("(?\\S+)(?.*)"); + private static final Logger logger = LogsCenter.getLogger(seedu.address.logic.parser.AddressBookParser.class); + + /** + * Parses user input into command for execution. + * + * @param userInput full user input string + * @return the command based on the user input + * @throws ParseException if the user input does not conform the expected format + */ + public Command parseCommand(String userInput) throws ParseException { + final Matcher matcher = BASIC_COMMAND_FORMAT.matcher(userInput.trim()); + if (!matcher.matches()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE)); + } + + final String commandWord = matcher.group("commandWord"); + final String arguments = matcher.group("arguments"); + + // Note to developers: Change the log level in config.json to enable lower level (i.e., FINE, FINER and lower) + // log messages such as the one below. + // Lower level log messages are used sparingly to minimize noise in the code. + logger.fine("Command word: " + commandWord + "; Arguments: " + arguments); + + switch (commandWord) { + + case ViewExitCommand.COMMAND_WORD: + return new ViewExitCommand(); + + default: + logger.finer("This user input caused a ParseException: " + userInput); + throw new ParseException(MESSAGE_UNKNOWN_COMMAND); + } + } + +} \ No newline at end of file diff --git a/src/main/java/seedu/address/ui/MainWindow.java b/src/main/java/seedu/address/ui/MainWindow.java index 79e74ef37c0..3f911802df8 100644 --- a/src/main/java/seedu/address/ui/MainWindow.java +++ b/src/main/java/seedu/address/ui/MainWindow.java @@ -16,6 +16,7 @@ import seedu.address.logic.commands.CommandResult; import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.person.Person; /** * The Main Window. Provides the basic application layout containing @@ -32,6 +33,8 @@ public class MainWindow extends UiPart { // Independent Ui parts residing in this Ui container private PersonListPanel personListPanel; + + private PersonProfile personProfile; private ResultDisplay resultDisplay; private HelpWindow helpWindow; @@ -66,6 +69,7 @@ public MainWindow(Stage primaryStage, Logic logic) { setAccelerators(); helpWindow = new HelpWindow(); + } public Stage getPrimaryStage() { @@ -167,6 +171,19 @@ public PersonListPanel getPersonListPanel() { return personListPanel; } + @FXML + private void handleView(Person personToView) { + personProfile = new PersonProfile(personToView); + personListPanelPlaceholder.getChildren().remove(personListPanel.getRoot()); + personListPanelPlaceholder.getChildren().add(personProfile.getRoot()); + } + @FXML + private void handleViewExit() { + personListPanel = new PersonListPanel(logic.getFilteredPersonList()); + personListPanelPlaceholder.getChildren().remove(personProfile.getRoot()); + personListPanelPlaceholder.getChildren().add(personListPanel.getRoot()); + } + /** * Executes the command and returns the result. * @@ -186,6 +203,14 @@ private CommandResult executeCommand(String commandText) throws CommandException handleExit(); } + if (commandResult.isShowView()) { + handleView(commandResult.getPersonToView()); + } + + if (commandResult.isViewExit()) { + handleViewExit(); + } + return commandResult; } catch (CommandException | ParseException e) { logger.info("An error occurred while executing command: " + commandText); @@ -193,4 +218,5 @@ private CommandResult executeCommand(String commandText) throws CommandException throw e; } } + } diff --git a/src/main/java/seedu/address/ui/PersonProfile.java b/src/main/java/seedu/address/ui/PersonProfile.java new file mode 100644 index 00000000000..a935821fd0e --- /dev/null +++ b/src/main/java/seedu/address/ui/PersonProfile.java @@ -0,0 +1,54 @@ +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; + +public class PersonProfile extends UiPart { + private static final String FXML = "PersonProfile.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; + + @javafx.fxml.FXML + private HBox cardPane; + @FXML + private Label name; + @FXML + private Label id; + @FXML + private Label phone; + @FXML + private Label address; + @FXML + private Label email; + @FXML + private FlowPane tags; + + /** + * Creates a {@code PersonCode} with the given {@code Person} and index to display. + */ + public PersonProfile(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))); + } +} diff --git a/src/main/resources/view/PersonProfile.fxml b/src/main/resources/view/PersonProfile.fxml new file mode 100644 index 00000000000..7c148ea00e3 --- /dev/null +++ b/src/main/resources/view/PersonProfile.fxml @@ -0,0 +1,15 @@ + + + + + + + + + + +