diff --git a/src/main/java/seedu/address/logic/commands/ListCommand.java b/src/main/java/seedu/address/logic/commands/ListCommand.java index 2dc53e4fa4b..0f4f53963cd 100644 --- a/src/main/java/seedu/address/logic/commands/ListCommand.java +++ b/src/main/java/seedu/address/logic/commands/ListCommand.java @@ -6,13 +6,13 @@ import seedu.address.model.Model; /** - * Lists all persons in the address book to the user. + * Lists all tags in the address book to the user. */ public class ListCommand extends Command { public static final String COMMAND_WORD = "list"; - public static final String MESSAGE_SUCCESS = "Listed all persons"; + public static final String MESSAGE_SUCCESS = "Listed all tags"; @Override diff --git a/src/main/java/seedu/address/logic/commands/RetagCommand.java b/src/main/java/seedu/address/logic/commands/RetagCommand.java new file mode 100644 index 00000000000..1f68fbea0ad --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/RetagCommand.java @@ -0,0 +1,93 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_OLD_TAG_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG_NAME; + +import java.util.List; + +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.tag.FileAddress; +import seedu.address.model.tag.Tag; +import seedu.address.model.tag.TagName; +import seedu.address.model.tag.TagNameEqualsKeywordPredicate; + +/** + * Adds a person to the address book. + */ +public class RetagCommand extends Command { + public static final String COMMAND_WORD = "retag"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Retags the tagged file identified by the old tag into the new tag.\n" + + "Parameters: " + + PREFIX_OLD_TAG_NAME + "OLD_TAG_NAME " + + PREFIX_TAG_NAME + "NEW_TAG_NAME\n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_OLD_TAG_NAME + "my_files " + + PREFIX_TAG_NAME + "my_old_files "; + + public static final String MESSAGE_RETAG_TAG_SUCCESS = "Retagged Tag: %s to Tag: %s"; + public static final String MESSAGE_OLD_TAG_NOT_FOUND = " %s tag not found!" + + " Please make sure that the old tag is present before retagging."; + public static final String MESSAGE_DUPLICATE_TAG = "Duplicate tag name!"; + + private final TagName newTagName; + private final TagName oldTagName; + + /** + * Creates a RetagCommand to retag the specified {@code oldTagName} to {@code newTagName} + */ + public RetagCommand(TagName oldTagName, TagName newTagName) { + requireNonNull(oldTagName); + requireNonNull(newTagName); + + this.oldTagName = oldTagName; + this.newTagName = newTagName; + } + + /** + * Executes the command and renames the tag in the model to the new tag. + * + * @param model {@code Model} which the command should operate on. + * @return A Command result of executing retag command. + * @throws CommandException + */ + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + // Check if oldTagName is in tag list + List oldTagList = model.findFilteredTagList(new TagNameEqualsKeywordPredicate(oldTagName)); + if (oldTagList.isEmpty()) { + throw new CommandException(String.format(MESSAGE_OLD_TAG_NOT_FOUND, oldTagName.toString())); + } + + // Check if newTagName is in tag list + List newTagList = model.findFilteredTagList(new TagNameEqualsKeywordPredicate(newTagName)); + if (!newTagList.isEmpty()) { + throw new CommandException(String.format(MESSAGE_DUPLICATE_TAG)); + } + + // Get oldTagName + Tag tagToChange = oldTagList.get(0); + // Get file path + FileAddress fileAddress = tagToChange.getFileAddress(); + // Delete old tag + model.deleteTag(tagToChange); + + Tag newTag = new Tag(newTagName, fileAddress); + // Add new tag + model.addTag(newTag); + return new CommandResult(String.format(MESSAGE_RETAG_TAG_SUCCESS, oldTagName, newTagName)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof RetagCommand // instanceof handles nulls + && newTagName.equals(((RetagCommand) other).newTagName) + && oldTagName.equals(((RetagCommand) other).oldTagName)); + } +} diff --git a/src/main/java/seedu/address/logic/commands/TagCommand.java b/src/main/java/seedu/address/logic/commands/TagCommand.java index ab721ba1849..a5b59c1c909 100644 --- a/src/main/java/seedu/address/logic/commands/TagCommand.java +++ b/src/main/java/seedu/address/logic/commands/TagCommand.java @@ -12,16 +12,16 @@ import seedu.address.model.tag.Tag; /** - * Adds a person to the address book. + * Adds a tag to the address book. */ public class TagCommand extends Command { public static final String COMMAND_WORD = "tag"; - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a tag to the HelleFile's address book. " + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a tag to the HelleFile's address book.\n" + "Parameters: " + PREFIX_TAG_NAME + "TAG_NAME " - + PREFIX_FILE_ADDRESS + "FILE_ADDRESS " + + PREFIX_FILE_ADDRESS + "FILE_ADDRESS\n" + "Example: " + COMMAND_WORD + " " + PREFIX_TAG_NAME + "cs2103 " + PREFIX_FILE_ADDRESS + "F:\\OneDrive\\CS2013T "; diff --git a/src/main/java/seedu/address/logic/parser/AddressBookParser.java b/src/main/java/seedu/address/logic/parser/AddressBookParser.java index e24a2a89b01..2f64de4819b 100644 --- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java +++ b/src/main/java/seedu/address/logic/parser/AddressBookParser.java @@ -13,6 +13,7 @@ import seedu.address.logic.commands.HelpCommand; import seedu.address.logic.commands.ListCommand; import seedu.address.logic.commands.OpenCommand; +import seedu.address.logic.commands.RetagCommand; import seedu.address.logic.commands.ShowCommand; import seedu.address.logic.commands.TagCommand; import seedu.address.logic.commands.UntagCommand; @@ -51,6 +52,9 @@ public Command parseCommand(String userInput) throws ParseException { case UntagCommand.COMMAND_WORD: return new UntagCommandParser().parse(arguments); + case RetagCommand.COMMAND_WORD: + return new RetagCommandParser().parse(arguments); + case OpenCommand.COMMAND_WORD: return new OpenCommandParser().parse(arguments); diff --git a/src/main/java/seedu/address/logic/parser/RetagCommandParser.java b/src/main/java/seedu/address/logic/parser/RetagCommandParser.java new file mode 100644 index 00000000000..c85fb36cdd7 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/RetagCommandParser.java @@ -0,0 +1,48 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_OLD_TAG_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG_NAME; + +import java.util.stream.Stream; + +import seedu.address.logic.commands.RetagCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.tag.TagName; + +/** + * Parses input arguments and creates a new RetagCommand object + */ +public class RetagCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the RetagCommand + * and returns a RetagCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public RetagCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_OLD_TAG_NAME, PREFIX_TAG_NAME); + + boolean a = arePrefixesPresent(argMultimap, PREFIX_OLD_TAG_NAME, PREFIX_TAG_NAME); + boolean b = argMultimap.getPreamble().isEmpty(); + Object c = argMultimap.getPreamble(); + if (!arePrefixesPresent(argMultimap, PREFIX_OLD_TAG_NAME, PREFIX_TAG_NAME) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, RetagCommand.MESSAGE_USAGE)); + } + + TagName oldTagName = ParserUtil.parseTagName(argMultimap.getValue(PREFIX_OLD_TAG_NAME).get()); + TagName newTagName = ParserUtil.parseTagName(argMultimap.getValue(PREFIX_TAG_NAME).get()); + + return new RetagCommand(oldTagName, newTagName); + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } +} diff --git a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java b/src/test/java/seedu/address/logic/commands/CommandTestUtil.java index e1b9ce6e0db..399e973a57b 100644 --- a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java +++ b/src/test/java/seedu/address/logic/commands/CommandTestUtil.java @@ -3,6 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static seedu.address.logic.parser.CliSyntax.PREFIX_FILE_ADDRESS; +import static seedu.address.logic.parser.CliSyntax.PREFIX_OLD_TAG_NAME; import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG_NAME; import static seedu.address.testutil.Assert.assertThrows; @@ -34,6 +35,8 @@ public class CommandTestUtil { public static final String VALID_TAG_HUSBAND = "husband"; public static final String VALID_TAG_FRIEND = "friend"; + public static final String NAME_DESC_OLD_CS2101 = " " + PREFIX_OLD_TAG_NAME + VALID_NAME_CS2101; + public static final String NAME_DESC_OLD_CS2103 = " " + PREFIX_OLD_TAG_NAME + VALID_NAME_CS2103; public static final String NAME_DESC_CS2103 = " " + PREFIX_TAG_NAME + VALID_NAME_CS2103; public static final String NAME_DESC_CS2101 = " " + PREFIX_TAG_NAME + VALID_NAME_CS2101; public static final String NAME_DESC_AMY = " " + PREFIX_TAG_NAME + VALID_NAME_AMY; @@ -46,6 +49,8 @@ public class CommandTestUtil { public static final String TAG_DESC_FRIEND = " " + VALID_TAG_FRIEND; public static final String TAG_DESC_HUSBAND = " " + VALID_TAG_HUSBAND; + public static final String INVALID_OLD_TAG_DESC = " " + PREFIX_OLD_TAG_NAME + "myfile&"; // '&' not allowed in names + public static final String INVALID_NEW_TAG_DESC = " " + PREFIX_TAG_NAME + "myfile&"; // '&' not allowed in names public static final String INVALID_NAME_DESC = " " + PREFIX_TAG_NAME + "James&"; // '&' not allowed in names public static final String INVALID_FILE_ADDRESS_DESC = " " + PREFIX_FILE_ADDRESS; // empty string not allowed for addresses diff --git a/src/test/java/seedu/address/logic/commands/ModelStubWithTagAndTaglist.java b/src/test/java/seedu/address/logic/commands/ModelStubWithTagAndTaglist.java new file mode 100644 index 00000000000..cd626226f69 --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/ModelStubWithTagAndTaglist.java @@ -0,0 +1,48 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import java.util.List; +import java.util.function.Predicate; + +import seedu.address.model.tag.Tag; + +public class ModelStubWithTagAndTaglist extends ModelStub { + private Tag tag; + + ModelStubWithTagAndTaglist(Tag tag) { + requireNonNull(tag); + this.tag = tag; + } + + public Tag getTag() { + return this.tag; + } + + @Override + public void addTag(Tag tag) { + this.tag = tag; + } + + @Override + public void deleteTag(Tag tag) { + + } + + @Override + public List findFilteredTagList(Predicate predicate) { + if (predicate.test(tag)) { + return List.of(tag); + } + + return List.of(); + } + + @Override + public boolean equals(Object other) { + boolean a = other instanceof ModelStubWithTagAndTaglist; + return other == this // short circuit if same object + || (other instanceof ModelStubWithTagAndTaglist // instanceof handles nulls + && ((ModelStubWithTagAndTaglist) other).tag.equals(this.tag)); + } +} diff --git a/src/test/java/seedu/address/logic/commands/RetagCommandTest.java b/src/test/java/seedu/address/logic/commands/RetagCommandTest.java new file mode 100644 index 00000000000..79128c6faea --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/RetagCommandTest.java @@ -0,0 +1,87 @@ +package seedu.address.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.address.testutil.Assert.assertThrows; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.tag.Tag; +import seedu.address.model.tag.TagName; +import seedu.address.testutil.TagBuilder; + +public class RetagCommandTest { + + private ModelStubWithTagAndTaglist modelStub; + + @BeforeEach + public void setUp() { + Tag validTag = new TagBuilder().build(); + + modelStub = new ModelStubWithTagAndTaglist(validTag); + } + + @Test + public void execute_duplicateNewTagName_throwsCommandException() { + TagName oldTag = new TagBuilder().build().getTagName(); + TagName newTag = new TagBuilder().build().getTagName(); + + RetagCommand retagCommand = new RetagCommand(oldTag, newTag); + + assertThrows(CommandException.class, RetagCommand.MESSAGE_DUPLICATE_TAG, () -> retagCommand.execute(modelStub)); + } + + @Test + public void execute_oldTagNotInModel_throwCommandException() { + TagName oldTagName = new TagName("noExist"); + TagName newTagName = new TagName("noMatter"); + + RetagCommand retagCommand = new RetagCommand(oldTagName, newTagName); + + assertThrows(CommandException.class, String.format(RetagCommand.MESSAGE_OLD_TAG_NOT_FOUND, + oldTagName.tagName), () -> retagCommand.execute(modelStub)); + } + + @Test + public void execute_validInput_retagSuccess() { + Tag newTag = new TagBuilder().withTagName("new").build(); + + TagName oldTagName = new TagBuilder().build().getTagName(); + TagName newTagName = newTag.getTagName(); + + RetagCommand retagCommand = new RetagCommand(oldTagName, newTagName); + + ModelStubWithTagAndTaglist expectedModelStub = new ModelStubWithTagAndTaglist(newTag); + + assertCommandSuccess(retagCommand, modelStub, + String.format(retagCommand.MESSAGE_RETAG_TAG_SUCCESS, oldTagName, newTagName), expectedModelStub); + } + + @Test + public void equals() { + TagName oldTagName = new TagName("oldTag"); + TagName newTagName = new TagName("newTag"); + + RetagCommand retagCommand1 = new RetagCommand(oldTagName, newTagName); + RetagCommand retagCommand2 = new RetagCommand(newTagName, oldTagName); + + // same object -> returns true + assertTrue(retagCommand1.equals(retagCommand1)); + + // same values -> returns true + RetagCommand retagCommand1Copy = new RetagCommand(oldTagName, newTagName); + assertTrue(retagCommand1.equals(retagCommand1Copy)); + + // different types -> returns false + assertFalse(retagCommand1.equals(1)); + + // null -> returns false + assertFalse(retagCommand1.equals(null)); + + // different tagName -> returns false + assertFalse(retagCommand1.equals(retagCommand2)); + } +} diff --git a/src/test/java/seedu/address/logic/commands/ShowCommandTest.java b/src/test/java/seedu/address/logic/commands/ShowCommandTest.java index 1517ff6676f..4fcb972d0cf 100644 --- a/src/test/java/seedu/address/logic/commands/ShowCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/ShowCommandTest.java @@ -59,7 +59,7 @@ public void execute_invalidTagName_throwsCommandException() { } @Test - public void execute_validTagName_throwsCommandException() { + public void execute_validTagName_showTagFileSuccess() { Tag tagToBeShown = model.getFilteredTagList().get(INDEX_FIRST_TAG.getZeroBased()); TagNameEqualsKeywordPredicate predicate = new TagNameEqualsKeywordPredicate(tagToBeShown.getTagName()); ShowCommand showCommand = new ShowCommand(predicate); diff --git a/src/test/java/seedu/address/logic/parser/RetagCommandParserTest.java b/src/test/java/seedu/address/logic/parser/RetagCommandParserTest.java new file mode 100644 index 00000000000..1b60715b512 --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/RetagCommandParserTest.java @@ -0,0 +1,62 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.commands.CommandTestUtil.INVALID_NEW_TAG_DESC; +import static seedu.address.logic.commands.CommandTestUtil.INVALID_OLD_TAG_DESC; +import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_CS2103; +import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_OLD_CS2101; +import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_CS2101; +import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_CS2103; +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.RetagCommand; +import seedu.address.model.tag.TagName; + +public class RetagCommandParserTest { + + private RetagCommandParser parser = new RetagCommandParser(); + + @Test + public void parse_allFieldsPresent_success() { + TagName oldTagName = new TagName(VALID_NAME_CS2101); + TagName newTagName = new TagName(VALID_NAME_CS2103); + + String userInput = NAME_DESC_OLD_CS2101 + NAME_DESC_CS2103; + + RetagCommand retagCommand = new RetagCommand(oldTagName, newTagName); + + assertParseSuccess(parser, userInput, retagCommand); + } + + @Test + public void parse_compulsoryFieldMissing_failure() { + String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, RetagCommand.MESSAGE_USAGE); + + // missing old tag name prefix + assertParseFailure(parser, VALID_NAME_CS2101 + NAME_DESC_CS2103, + expectedMessage); + + // missing new tag name prefix + assertParseFailure(parser, NAME_DESC_OLD_CS2101 + VALID_NAME_CS2103, + expectedMessage); + + // all prefixes missing + assertParseFailure(parser, VALID_NAME_CS2103 + VALID_NAME_CS2103, expectedMessage); + } + + @Test + public void parse_invalidTagInput_failure() { + String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, RetagCommand.MESSAGE_USAGE); + + // wrong old tag format + assertParseFailure(parser, INVALID_OLD_TAG_DESC + NAME_DESC_CS2103, + TagName.MESSAGE_CONSTRAINTS); + + // wrong new tag format + assertParseFailure(parser, VALID_NAME_CS2101 + INVALID_NEW_TAG_DESC, + expectedMessage); + } +}