diff --git a/src/main/java/seedu/address/logic/Messages.java b/src/main/java/seedu/address/logic/Messages.java index 1dc2c940e22..699b8f13cc4 100644 --- a/src/main/java/seedu/address/logic/Messages.java +++ b/src/main/java/seedu/address/logic/Messages.java @@ -84,17 +84,28 @@ public static String format(University university) { } /** - * Formats the {@code university} for display to the user. + * Formats the {@code observableList} for display to the user. * Overloaded method. - * @param universityObservableList + * @param observableList * @return */ - public static String format(ObservableList universityObservableList) { - final StringBuilder builder = new StringBuilder("UniversityName: "); - String universityNames = universityObservableList.stream() - .map(University::getUniversityName).map(UniversityName::getName).collect(Collectors.joining(", ")); - builder.append(universityNames); - return builder.toString(); + public static String format(ObservableList observableList) { + T item = observableList.get(0); + if (item instanceof University) { + final StringBuilder builder = new StringBuilder("UniversityName: "); + String universityNames = observableList.stream().map(c -> (University) c) + .map(University::getUniversityName) + .map(UniversityName::getName).collect(Collectors.joining(", ")); + builder.append(universityNames); + return builder.toString(); + } else if (item instanceof Note) { + final StringBuilder builder = new StringBuilder("Note: "); + String tag = observableList.stream().findFirst().map(c -> (Note) c) + .map(Note::getTags).get().toString(); + builder.append(tag); + return builder.toString(); + } + return "default"; } /** @@ -140,5 +151,4 @@ public static String format(Note note) { .append(note.getTags()); return builder.toString(); } - } diff --git a/src/main/java/seedu/address/logic/commands/NoteSearchCommand.java b/src/main/java/seedu/address/logic/commands/NoteSearchCommand.java new file mode 100644 index 00000000000..313c137c754 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/NoteSearchCommand.java @@ -0,0 +1,72 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.address.commons.util.ToStringBuilder; +import seedu.address.logic.Messages; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.SeplendidModel; +import seedu.address.model.notes.NoteTagContainsKeywordsPredicate; +import seedu.address.seplendidui.UiUtil; + +/** + * Finds and lists all notes in SEPlendid whose tags contains any of the argument keywords. + * Keyword matching is case-insensitive. + */ +public class NoteSearchCommand extends NoteCommand { + public static final String ACTION_WORD = "search"; + + public static final String MESSAGE_SUCCESS = "Notes searched: %1$s"; + + public static final String MESSAGE_NONEXISTENT_NOTES = "This note does not exist in SEPlendid"; + + public static final String NOTE_SEARCH_MESSAGE_USAGE = COMMAND_WORD + + " search [note_tag_keyword]: Search notes with the same tag keyword"; + private final NoteTagContainsKeywordsPredicate predicate; + + public NoteSearchCommand(NoteTagContainsKeywordsPredicate predicate) { + this.predicate = predicate; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + throw new CommandException("TBD: this is a stub and should be removed after morph."); + } + + @Override + public CommandResult execute(SeplendidModel model) throws CommandException { + requireNonNull(model); + + model.getSearchNoteIfExists(predicate); + + if (model.getFilteredLocalCourseList().isEmpty()) { + throw new CommandException(MESSAGE_NONEXISTENT_NOTES); + } + + return new CommandResult(String.format(MESSAGE_SUCCESS, Messages.format(model.getFilteredNoteList())), + UiUtil.ListViewModel.NOTE_LIST); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof NoteSearchCommand)) { + return false; + } + + NoteSearchCommand otherNoteSearchCommand = (NoteSearchCommand) other; + return predicate.equals(otherNoteSearchCommand.predicate); + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .add("predicate", predicate) + .toString(); + } +} diff --git a/src/main/java/seedu/address/logic/parser/NoteSearchCommandParser.java b/src/main/java/seedu/address/logic/parser/NoteSearchCommandParser.java new file mode 100644 index 00000000000..78ebdde5cb2 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/NoteSearchCommandParser.java @@ -0,0 +1,46 @@ +package seedu.address.logic.parser; + +import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PARAMETER_TAGS; + +import seedu.address.logic.commands.NoteSearchCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.notes.NoteTagContainsKeywordsPredicate; +import seedu.address.model.tag.Tag; + +/** + * Parses the given {@code String} of arguments in the context of the SearchCommand + * and returns a SearchCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ +public class NoteSearchCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the NoteSearchCommand + * and returns a NoteSearchCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public NoteSearchCommand parse(String args) throws ParseException { + String trimmedArgs = args.trim(); + if (!ParserUtil.areValuesEnclosedAndNonEmpty(args)) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, + NoteSearchCommand.NOTE_SEARCH_MESSAGE_USAGE) + ); + } + + SeplendidArgumentMap parameterToArgMap = + SeplendidArgumentTokenizer.tokenize(args, + PARAMETER_TAGS); + + if (!ParserUtil.areArgumentsPresent(parameterToArgMap, + PARAMETER_TAGS)) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + NoteSearchCommand.NOTE_SEARCH_MESSAGE_USAGE)); + } + + Tag tag = ParserUtil.parseTag( + parameterToArgMap.getValue(PARAMETER_TAGS).get()); + return new NoteSearchCommand(new NoteTagContainsKeywordsPredicate(tag.toString())); + + } +} diff --git a/src/main/java/seedu/address/logic/parser/SeplendidParser.java b/src/main/java/seedu/address/logic/parser/SeplendidParser.java index 7971c243681..da3adc20b95 100644 --- a/src/main/java/seedu/address/logic/parser/SeplendidParser.java +++ b/src/main/java/seedu/address/logic/parser/SeplendidParser.java @@ -23,6 +23,7 @@ import seedu.address.logic.commands.NoteAddCommand; import seedu.address.logic.commands.NoteCommand; import seedu.address.logic.commands.NoteListCommand; +import seedu.address.logic.commands.NoteSearchCommand; import seedu.address.logic.commands.PartnerCourseAddCommand; import seedu.address.logic.commands.PartnerCourseCommand; import seedu.address.logic.commands.PartnerCourseDeleteCommand; @@ -127,7 +128,7 @@ public Command parseCommand(String userInput) throws ParseException { return getPartnerCourseCommandWithArg(userInput, actionWord, arguments); case NoteCommand.COMMAND_WORD: - return getNoteCommand(userInput, actionWord, arguments); + return getNoteCommandWithArg(userInput, actionWord, arguments); case UniversityCommand.COMMAND_WORD: return getUniversityCommandWithArg(userInput, actionWord, arguments); @@ -240,11 +241,13 @@ private MappingCommand getMappingCommandWithoutArg(String userInput, String acti } } - private NoteAddCommand getNoteCommand(String userInput, String actionWord, String arguments) + private NoteCommand getNoteCommandWithArg(String userInput, String actionWord, String arguments) throws ParseException { switch (actionWord) { case NoteAddCommand.ACTION_WORD: return new NoteAddCommandParser().parse(arguments); + case NoteSearchCommand.ACTION_WORD: + return new NoteSearchCommandParser().parse(arguments); default: logger.finer("This user input caused a ParseException: " + userInput); throw new ParseException(MESSAGE_UNKNOWN_COMMAND); diff --git a/src/main/java/seedu/address/model/SeplendidModel.java b/src/main/java/seedu/address/model/SeplendidModel.java index 1cf923a36c5..130fc8a9373 100644 --- a/src/main/java/seedu/address/model/SeplendidModel.java +++ b/src/main/java/seedu/address/model/SeplendidModel.java @@ -12,6 +12,7 @@ import seedu.address.model.localcourse.LocalCourse; import seedu.address.model.mapping.Mapping; import seedu.address.model.notes.Note; +import seedu.address.model.notes.NoteTagContainsKeywordsPredicate; import seedu.address.model.partnercourse.PartnerCode; import seedu.address.model.partnercourse.PartnerCourse; import seedu.address.model.university.University; @@ -238,6 +239,13 @@ public interface SeplendidModel { */ void setNote(Note note, Note editedNote); + /** + * Gets the notes with the specific tags + * @param notePredicate + */ + void getSearchNoteIfExists(NoteTagContainsKeywordsPredicate notePredicate); + + //=========== FilteredLocalCourseList Accessors ============================================================= /** diff --git a/src/main/java/seedu/address/model/SeplendidModelManager.java b/src/main/java/seedu/address/model/SeplendidModelManager.java index 27aea78de31..db25f10680b 100644 --- a/src/main/java/seedu/address/model/SeplendidModelManager.java +++ b/src/main/java/seedu/address/model/SeplendidModelManager.java @@ -21,6 +21,7 @@ import seedu.address.model.localcourse.LocalCourse; import seedu.address.model.mapping.Mapping; import seedu.address.model.notes.Note; +import seedu.address.model.notes.NoteTagContainsKeywordsPredicate; import seedu.address.model.partnercourse.PartnerCode; import seedu.address.model.partnercourse.PartnerCourse; import seedu.address.model.university.University; @@ -442,6 +443,13 @@ public void setNote(Note target, Note editedNote) { noteCatalogue.setNote(target, editedNote); } + @Override + public void getSearchNoteIfExists(NoteTagContainsKeywordsPredicate notePredicate) { + requireNonNull(notePredicate); + filteredNoteCatalogue.setPredicate(notePredicate); + + } + //=========== FilteredNoteList Accessors ============================================================= /** diff --git a/src/main/java/seedu/address/model/notes/NoteTagContainsKeywordsPredicate.java b/src/main/java/seedu/address/model/notes/NoteTagContainsKeywordsPredicate.java new file mode 100644 index 00000000000..291493d5c73 --- /dev/null +++ b/src/main/java/seedu/address/model/notes/NoteTagContainsKeywordsPredicate.java @@ -0,0 +1,42 @@ +package seedu.address.model.notes; + +import java.util.function.Predicate; + +import seedu.address.commons.util.ToStringBuilder; + +/** + * Tests that a {@code Note}'s {@code Tag} matches any of the keywords given. + */ +public class NoteTagContainsKeywordsPredicate implements Predicate { + private final String keyword; + public NoteTagContainsKeywordsPredicate(String keywords) { + this.keyword = keywords; + } + + @Override + public boolean test(Note note) { + return note.getTags().toString().toLowerCase() + .contains(keyword.toLowerCase()); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof seedu.address.model.notes.NoteTagContainsKeywordsPredicate)) { + return false; + } + + NoteTagContainsKeywordsPredicate otherTagContainsKeywordsPredicate = + (NoteTagContainsKeywordsPredicate) other; + return keyword.equals(otherTagContainsKeywordsPredicate.keyword); + } + + @Override + public String toString() { + return new ToStringBuilder(this).add("keyword", keyword).toString(); + } +} diff --git a/src/test/java/seedu/address/logic/commands/LocalCourseAddCommandTest.java b/src/test/java/seedu/address/logic/commands/LocalCourseAddCommandTest.java index fd1805aa2d2..b8faeddd202 100644 --- a/src/test/java/seedu/address/logic/commands/LocalCourseAddCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/LocalCourseAddCommandTest.java @@ -32,6 +32,7 @@ import seedu.address.model.localcourse.LocalCourse; import seedu.address.model.mapping.Mapping; import seedu.address.model.notes.Note; +import seedu.address.model.notes.NoteTagContainsKeywordsPredicate; import seedu.address.model.partnercourse.PartnerCode; import seedu.address.model.partnercourse.PartnerCourse; import seedu.address.model.university.University; @@ -347,6 +348,11 @@ public void setNote(Note note, Note editedNote) { throw new AssertionError("This method should not be called."); } + @Override + public void getSearchNoteIfExists(NoteTagContainsKeywordsPredicate notePredicate) { + throw new AssertionError("This method should not be called."); + } + @Override public ObservableList getFilteredNoteList() { throw new AssertionError("This method should not be called."); diff --git a/src/test/java/seedu/address/logic/commands/LocalCourseDeleteCommandTest.java b/src/test/java/seedu/address/logic/commands/LocalCourseDeleteCommandTest.java index fb193fd24ad..cf6a288f7cd 100644 --- a/src/test/java/seedu/address/logic/commands/LocalCourseDeleteCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/LocalCourseDeleteCommandTest.java @@ -35,6 +35,7 @@ import seedu.address.model.localcourse.LocalCourse; import seedu.address.model.mapping.Mapping; import seedu.address.model.notes.Note; +import seedu.address.model.notes.NoteTagContainsKeywordsPredicate; import seedu.address.model.partnercourse.PartnerCode; import seedu.address.model.partnercourse.PartnerCourse; import seedu.address.model.university.University; @@ -348,6 +349,11 @@ public void setNote(Note note, Note editedNote) { throw new AssertionError("This method should not be called."); } + @Override + public void getSearchNoteIfExists(NoteTagContainsKeywordsPredicate notePredicate) { + throw new AssertionError("This method should not be called."); + } + @Override public ObservableList getFilteredNoteList() { throw new AssertionError("This method should not be called."); diff --git a/src/test/java/seedu/address/logic/commands/PartnerCourseAddCommandTest.java b/src/test/java/seedu/address/logic/commands/PartnerCourseAddCommandTest.java index 0a2c09e810b..0ea41e0ea74 100644 --- a/src/test/java/seedu/address/logic/commands/PartnerCourseAddCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/PartnerCourseAddCommandTest.java @@ -31,6 +31,7 @@ import seedu.address.model.localcourse.LocalCourse; import seedu.address.model.mapping.Mapping; import seedu.address.model.notes.Note; +import seedu.address.model.notes.NoteTagContainsKeywordsPredicate; import seedu.address.model.partnercourse.PartnerCode; import seedu.address.model.partnercourse.PartnerCourse; import seedu.address.model.university.University; @@ -337,6 +338,11 @@ public void setNote(Note note, Note editedNote) { throw new AssertionError("This method should not be called."); } + @Override + public void getSearchNoteIfExists(NoteTagContainsKeywordsPredicate notePredicate) { + throw new AssertionError("This method should not be called."); + } + @Override public ObservableList getFilteredNoteList() { throw new AssertionError("This method should not be called."); diff --git a/src/test/java/seedu/address/logic/commands/PartnerCourseDeleteCommandTest.java b/src/test/java/seedu/address/logic/commands/PartnerCourseDeleteCommandTest.java index cf5af1917d4..c0356a92de5 100644 --- a/src/test/java/seedu/address/logic/commands/PartnerCourseDeleteCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/PartnerCourseDeleteCommandTest.java @@ -36,6 +36,7 @@ import seedu.address.model.localcourse.LocalCourse; import seedu.address.model.mapping.Mapping; import seedu.address.model.notes.Note; +import seedu.address.model.notes.NoteTagContainsKeywordsPredicate; import seedu.address.model.partnercourse.PartnerCode; import seedu.address.model.partnercourse.PartnerCourse; import seedu.address.model.university.University; @@ -346,6 +347,11 @@ public void setNote(Note note, Note editedNote) { throw new AssertionError("This method should not be called."); } + @Override + public void getSearchNoteIfExists(NoteTagContainsKeywordsPredicate notePredicate) { + throw new AssertionError("This method should not be called."); + } + @Override public ObservableList getFilteredNoteList() { throw new AssertionError("This method should not be called.");