diff --git a/doc/UserGuide.md b/doc/UserGuide.md index d03dfce73..10b2639fc 100644 --- a/doc/UserGuide.md +++ b/doc/UserGuide.md @@ -66,6 +66,21 @@ Examples: `delete 1`
Deletes the 1st person in the results of the `find` command. +### Changing a person's phone number : `changenum` +Changes the specified person's phone number from the address book. Irreversible.
+Format: `changenum INDEX NEW_NUMBER` + +> Changes the person's phone number at the specified `INDEX`. + The index refers to the index number shown in the most recent listing. + +Examples: +* `list`
+ `changenum 2 99997777`
+ Changes the phone number of the 2nd person in the address book to 99997777. +* `find Betsy`
+ `changenum 1 88886666`
+ Changes the phone number of the 1st person in the results of the `find` command to 88886666. + ## View non-private details of a person : `view` Displays the non-private details of the specified person.
Format: `view INDEX` diff --git a/src/seedu/addressbook/commands/ChangeNumCommand.java b/src/seedu/addressbook/commands/ChangeNumCommand.java new file mode 100644 index 000000000..5e11e4d5c --- /dev/null +++ b/src/seedu/addressbook/commands/ChangeNumCommand.java @@ -0,0 +1,44 @@ +package seedu.addressbook.commands; + +import seedu.addressbook.common.Messages; +import seedu.addressbook.data.person.Person; +import seedu.addressbook.data.person.UniquePersonList; + + +/** + * Changes the phone number of a person identified using it's last displayed index from the address book. + */ +public class ChangeNumCommand extends Command { + + public static final String COMMAND_WORD = "changenum"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Changes the phone number of the person identified by the index number used in the last person listing.\n" + + "Parameters: INDEX NEW_NUMBER\n" + + "Example: " + COMMAND_WORD + " 1" + " 99997777"; + + public static final String MESSAGE_CHANGE_NUMBER_PERSON_SUCCESS = "Changed Person's Number: %1$s"; + + private String newNum; + + + public ChangeNumCommand(int targetVisibleIndex, String num) { + + super(targetVisibleIndex); + newNum = num; + } + + + @Override + public CommandResult execute() { + try { + final Person target = (Person)getTargetPerson(); + target.setPhone(newNum); + return new CommandResult(String.format(MESSAGE_CHANGE_NUMBER_PERSON_SUCCESS, target)); + + } catch (IndexOutOfBoundsException ie) { + return new CommandResult(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + } + } + +} diff --git a/src/seedu/addressbook/commands/HelpCommand.java b/src/seedu/addressbook/commands/HelpCommand.java index ef2ed7d0e..368c77e05 100644 --- a/src/seedu/addressbook/commands/HelpCommand.java +++ b/src/seedu/addressbook/commands/HelpCommand.java @@ -13,6 +13,7 @@ public class HelpCommand extends Command { public static final String MESSAGE_ALL_USAGES = AddCommand.MESSAGE_USAGE + "\n" + DeleteCommand.MESSAGE_USAGE + + "\n" + ChangeNumCommand.MESSAGE_USAGE + "\n" + ClearCommand.MESSAGE_USAGE + "\n" + FindCommand.MESSAGE_USAGE + "\n" + ListCommand.MESSAGE_USAGE diff --git a/src/seedu/addressbook/common/Messages.java b/src/seedu/addressbook/common/Messages.java index 02cfe6155..eefdecd65 100644 --- a/src/seedu/addressbook/common/Messages.java +++ b/src/seedu/addressbook/common/Messages.java @@ -7,6 +7,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_INVALID_PHONE_NUMBER = "Person phone numbers should only contain numbers"; public static final String MESSAGE_PERSON_NOT_IN_ADDRESSBOOK = "Person could not be found in address book"; public static final String MESSAGE_PERSONS_LISTED_OVERVIEW = "%1$d persons listed!"; public static final String MESSAGE_PROGRAM_LAUNCH_ARGS_USAGE = "Launch command format: " + diff --git a/src/seedu/addressbook/data/person/Person.java b/src/seedu/addressbook/data/person/Person.java index cf6211841..c14fc41eb 100644 --- a/src/seedu/addressbook/data/person/Person.java +++ b/src/seedu/addressbook/data/person/Person.java @@ -66,6 +66,13 @@ public void setTags(UniqueTagList replacement) { tags.setTags(replacement); } + /** + * Updates the person's phone number to the one in the argument. + */ + public void setPhone(String num) { + this.phone.setNum(num); + } + @Override public boolean equals(Object other) { return other == this // short circuit if same object diff --git a/src/seedu/addressbook/data/person/Phone.java b/src/seedu/addressbook/data/person/Phone.java index b5a556de4..22c4ad60e 100644 --- a/src/seedu/addressbook/data/person/Phone.java +++ b/src/seedu/addressbook/data/person/Phone.java @@ -12,7 +12,7 @@ public class Phone { public static final String MESSAGE_PHONE_CONSTRAINTS = "Person phone numbers should only contain numbers"; public static final String PHONE_VALIDATION_REGEX = "\\d+"; - public final String value; + public String value; private boolean isPrivate; /** @@ -41,6 +41,10 @@ public String toString() { return value; } + public void setNum(String num) { + this.value = num; + } + @Override public boolean equals(Object other) { return other == this // short circuit if same object diff --git a/src/seedu/addressbook/parser/Parser.java b/src/seedu/addressbook/parser/Parser.java index 58f4f7e6c..2e263dbb8 100644 --- a/src/seedu/addressbook/parser/Parser.java +++ b/src/seedu/addressbook/parser/Parser.java @@ -8,6 +8,8 @@ import java.util.regex.Pattern; import static seedu.addressbook.common.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.addressbook.common.Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX; +import static seedu.addressbook.common.Messages.MESSAGE_INVALID_PHONE_NUMBER; /** * Parses user input. @@ -26,6 +28,8 @@ public class Parser { + " (?p?)a/(?
[^/]+)" + "(?(?: t/[^/]+)*)"); // variable number of tags + public static final Pattern CHANGE_NUMBER_FORMAT = + Pattern.compile("(?.+)" + " (?[^/]+)"); /** * Signals that the user input could not be parsed. @@ -47,7 +51,7 @@ public static class ParseException extends Exception { * @param userInput full user input string * @return the command based on the user input */ - public Command parseCommand(String userInput) { + public Command parseCommand(String userInput) throws IllegalValueException { final Matcher matcher = BASIC_COMMAND_FORMAT.matcher(userInput.trim()); if (!matcher.matches()) { return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE)); @@ -62,6 +66,9 @@ public Command parseCommand(String userInput) { case DeleteCommand.COMMAND_WORD: return prepareDelete(arguments); + + case ChangeNumCommand.COMMAND_WORD: + return prepareChangeNum(arguments); case ClearCommand.COMMAND_WORD: return new ClearCommand(); @@ -156,6 +163,43 @@ private Command prepareDelete(String args) { } } + /** + * Checks whether this string only contains digits. + * + * @param str a string + */ + private void checkIsPureDigitString (String str) throws IllegalValueException { + if (!str.matches("[0-9]+")) { + throw new IllegalValueException("Person phone numbers should only contain numbers"); + } + } + + /** + * Parses arguments in the context of the change num person command. + * + * @param args full command args string + * @return the prepared command + */ + private Command prepareChangeNum(String args) throws IllegalValueException { + final Matcher matcher = CHANGE_NUMBER_FORMAT.matcher(args.trim()); + + // Validate arg string format + if (!matcher.matches()) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); + } + String delims = "[ ]+"; + String[] tokens = args.trim().split(delims); + + try { + checkIsPureDigitString(tokens[1]); + return new ChangeNumCommand(Integer.parseInt(tokens[0]), tokens[1]); + } catch (IllegalValueException ive) { + return new IncorrectCommand(MESSAGE_INVALID_PHONE_NUMBER); + } catch (NumberFormatException nfe) { + return new IncorrectCommand(MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + } + } + /** * Parses arguments in the context of the view command. * @@ -227,4 +271,4 @@ private Command prepareFind(String args) { } -} \ No newline at end of file +} diff --git a/test/java/seedu/addressbook/logic/LogicTest.java b/test/java/seedu/addressbook/logic/LogicTest.java index 7efa921ca..e9efea295 100644 --- a/test/java/seedu/addressbook/logic/LogicTest.java +++ b/test/java/seedu/addressbook/logic/LogicTest.java @@ -391,6 +391,65 @@ public void execute_delete_missingInAddressBook() throws Exception { threePersons); } + @Test + public void execute_changenum_invalidArgsFormat() throws Exception { + String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, ChangeNumCommand.MESSAGE_USAGE); + assertCommandBehavior("changenum ", expectedMessage); + assertCommandBehavior("changenum arg not number", expectedMessage); + } + + @Test + public void execute_changenum_invalidIndex() throws Exception { + assertInvalidIndexBehaviorForCommand("changenum"); + } + + @Test + public void execute_changenum_changesSuccessfully() throws Exception { + TestDataHelper helper = new TestDataHelper(); + Person p1 = helper.generatePerson(1, false); + Person p2 = helper.adam(); + Person p3 = helper.generatePerson(3, true); + Person p4 = helper.adam(); + + List threePersons = helper.generatePersonList(p1, p2, p3); + + p4.setPhone("99998888"); + List updatedThreePersons = helper.generatePersonList(p1, p4, p3); + + AddressBook expectedAB = helper.generateAddressBook(updatedThreePersons); + + helper.addToAddressBook(addressBook, threePersons); + logic.setLastShownList(threePersons); + + assertCommandBehavior("changenum 2 99998888", + String.format(ChangeNumCommand.MESSAGE_CHANGE_NUMBER_PERSON_SUCCESS, p4), + expectedAB, + false, + updatedThreePersons); + } + + @Test + public void execute_changenum_invalid_index() throws Exception { + + TestDataHelper helper = new TestDataHelper(); + Person p1 = helper.generatePerson(1, false); + Person p2 = helper.generatePerson(2, true); + Person p3 = helper.generatePerson(3, true); + + List threePersons = helper.generatePersonList(p1, p2, p3); + + AddressBook expectedAB = helper.generateAddressBook(threePersons); + + helper.addToAddressBook(addressBook, threePersons); + logic.setLastShownList(threePersons); + + assertCommandBehavior("changenum 4 99998888", + Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX, + expectedAB, + false, + threePersons); + } + @Test public void execute_find_invalidArgsFormat() throws Exception { String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE); diff --git a/test/java/seedu/addressbook/parser/ParserTest.java b/test/java/seedu/addressbook/parser/ParserTest.java index f01be613c..e53843b5d 100644 --- a/test/java/seedu/addressbook/parser/ParserTest.java +++ b/test/java/seedu/addressbook/parser/ParserTest.java @@ -25,14 +25,14 @@ public void setup() { } @Test - public void emptyInput_returnsIncorrect() { + public void emptyInput_returnsIncorrect() throws IllegalValueException { final String[] emptyInputs = { "", " ", "\n \n" }; final String resultMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE); parseAndAssertIncorrectWithMessage(resultMessage, emptyInputs); } @Test - public void unknownCommandWord_returnsHelp() { + public void unknownCommandWord_returnsHelp() throws IllegalValueException { final String input = "unknowncommandword arguments arguments"; parseAndAssertCommandType(input, HelpCommand.class); } @@ -42,25 +42,25 @@ public void unknownCommandWord_returnsHelp() { */ @Test - public void helpCommand_parsedCorrectly() { + public void helpCommand_parsedCorrectly() throws IllegalValueException { final String input = "help"; parseAndAssertCommandType(input, HelpCommand.class); } @Test - public void clearCommand_parsedCorrectly() { + public void clearCommand_parsedCorrectly() throws IllegalValueException { final String input = "clear"; parseAndAssertCommandType(input, ClearCommand.class); } @Test - public void listCommand_parsedCorrectly() { + public void listCommand_parsedCorrectly() throws IllegalValueException { final String input = "list"; parseAndAssertCommandType(input, ListCommand.class); } @Test - public void exitCommand_parsedCorrectly() { + public void exitCommand_parsedCorrectly() throws IllegalValueException { final String input = "exit"; parseAndAssertCommandType(input, ExitCommand.class); } @@ -70,21 +70,21 @@ public void exitCommand_parsedCorrectly() { */ @Test - public void deleteCommand_noArgs() { + public void deleteCommand_noArgs() throws IllegalValueException { final String[] inputs = { "delete", "delete " }; final String resultMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE); parseAndAssertIncorrectWithMessage(resultMessage, inputs); } @Test - public void deleteCommand_argsIsNotSingleNumber() { + public void deleteCommand_argsIsNotSingleNumber() throws IllegalValueException { final String[] inputs = { "delete notAnumber ", "delete 8*wh12", "delete 1 2 3 4 5" }; final String resultMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE); parseAndAssertIncorrectWithMessage(resultMessage, inputs); } @Test - public void deleteCommand_numericArg_indexParsedCorrectly() { + public void deleteCommand_numericArg_indexParsedCorrectly() throws IllegalValueException { final int testIndex = 1; final String input = "delete " + testIndex; final DeleteCommand result = parseAndAssertCommandType(input, DeleteCommand.class); @@ -92,21 +92,21 @@ public void deleteCommand_numericArg_indexParsedCorrectly() { } @Test - public void viewCommand_noArgs() { + public void viewCommand_noArgs() throws IllegalValueException { final String[] inputs = { "view", "view " }; final String resultMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, ViewCommand.MESSAGE_USAGE); parseAndAssertIncorrectWithMessage(resultMessage, inputs); } @Test - public void viewCommand_argsIsNotSingleNumber() { + public void viewCommand_argsIsNotSingleNumber() throws IllegalValueException { final String[] inputs = { "view notAnumber ", "view 8*wh12", "view 1 2 3 4 5" }; final String resultMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, ViewCommand.MESSAGE_USAGE); parseAndAssertIncorrectWithMessage(resultMessage, inputs); } @Test - public void viewCommand_numericArg_indexParsedCorrectly() { + public void viewCommand_numericArg_indexParsedCorrectly() throws IllegalValueException { final int testIndex = 2; final String input = "view " + testIndex; final ViewCommand result = parseAndAssertCommandType(input, ViewCommand.class); @@ -114,7 +114,7 @@ public void viewCommand_numericArg_indexParsedCorrectly() { } @Test - public void viewAllCommand_noArgs() { + public void viewAllCommand_noArgs() throws IllegalValueException { final String[] inputs = { "viewall", "viewall " }; final String resultMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, ViewAllCommand.MESSAGE_USAGE); @@ -122,14 +122,14 @@ public void viewAllCommand_noArgs() { } @Test - public void viewAllCommand_argsIsNotSingleNumber() { + public void viewAllCommand_argsIsNotSingleNumber() throws IllegalValueException { final String[] inputs = { "viewall notAnumber ", "viewall 8*wh12", "viewall 1 2 3 4 5" }; final String resultMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, ViewAllCommand.MESSAGE_USAGE); parseAndAssertIncorrectWithMessage(resultMessage, inputs); } @Test - public void viewAllCommand_numericArg_indexParsedCorrectly() { + public void viewAllCommand_numericArg_indexParsedCorrectly() throws IllegalValueException { final int testIndex = 3; final String input = "viewall " + testIndex; final ViewAllCommand result = parseAndAssertCommandType(input, ViewAllCommand.class); @@ -141,7 +141,7 @@ public void viewAllCommand_numericArg_indexParsedCorrectly() { */ @Test - public void findCommand_invalidArgs() { + public void findCommand_invalidArgs() throws IllegalValueException { // no keywords final String[] inputs = { "find", @@ -153,7 +153,7 @@ public void findCommand_invalidArgs() { } @Test - public void findCommand_validArgs_parsedCorrectly() { + public void findCommand_validArgs_parsedCorrectly() throws IllegalValueException { final String[] keywords = { "key1", "key2", "key3" }; final Set keySet = new HashSet<>(Arrays.asList(keywords)); @@ -164,7 +164,7 @@ public void findCommand_validArgs_parsedCorrectly() { } @Test - public void findCommand_duplicateKeys_parsedCorrectly() { + public void findCommand_duplicateKeys_parsedCorrectly() throws IllegalValueException { final String[] keywords = { "key1", "key2", "key3" }; final Set keySet = new HashSet<>(Arrays.asList(keywords)); @@ -180,7 +180,7 @@ public void findCommand_duplicateKeys_parsedCorrectly() { */ @Test - public void addCommand_invalidArgs() { + public void addCommand_invalidArgs() throws IllegalValueException { final String[] inputs = { "add", "add ", @@ -197,7 +197,7 @@ public void addCommand_invalidArgs() { } @Test - public void addCommand_invalidPersonDataInArgs() { + public void addCommand_invalidPersonDataInArgs() throws IllegalValueException { final String invalidName = "[]\\[;]"; final String validName = Name.EXAMPLE; final String invalidPhoneArg = "p/not__numbers"; @@ -226,7 +226,7 @@ public void addCommand_invalidPersonDataInArgs() { } @Test - public void addCommand_validPersonData_parsedCorrectly() { + public void addCommand_validPersonData_parsedCorrectly() throws IllegalValueException { final Person testPerson = generateTestPerson(); final String input = convertPersonToAddCommandString(testPerson); final AddCommand result = parseAndAssertCommandType(input, AddCommand.class); @@ -279,7 +279,7 @@ private static String convertPersonToAddCommandString(ReadOnlyPerson person) { /** * Asserts that parsing the given inputs will return IncorrectCommand with the given feedback message. */ - private void parseAndAssertIncorrectWithMessage(String feedbackMessage, String... inputs) { + private void parseAndAssertIncorrectWithMessage(String feedbackMessage, String... inputs) throws IllegalValueException { for (String input : inputs) { final IncorrectCommand result = parseAndAssertCommandType(input, IncorrectCommand.class); assertEquals(result.feedbackToUser, feedbackMessage); @@ -293,7 +293,7 @@ private void parseAndAssertIncorrectWithMessage(String feedbackMessage, String.. * @param expectedCommandClass expected class of returned command * @return the parsed command object */ - private T parseAndAssertCommandType(String input, Class expectedCommandClass) { + private T parseAndAssertCommandType(String input, Class expectedCommandClass) throws IllegalValueException { final Command result = parser.parseCommand(input); assertTrue(result.getClass().isAssignableFrom(expectedCommandClass)); return (T) result;