diff --git a/doc/DeveloperGuide.md b/doc/DeveloperGuide.md
index f77866560..ff6378650 100644
--- a/doc/DeveloperGuide.md
+++ b/doc/DeveloperGuide.md
@@ -87,6 +87,61 @@ Use case ends.
> 3a1. AddressBook shows an error message
Use case resumes at step 2
+
+#### Use case: Delete person
+
+**MSS**
+
+1. User requests to list persons
+2. AddressBook shows a list of persons
+3. User requests to delete a specific person in the list
+4. AddressBook deletes the person
+Use case ends.
+
+**Extensions**
+
+2a. The list is empty
+
+> Use case ends
+
+3a. The given index is invalid
+
+> 3a1. AddressBook shows an error message
+ Use case resumes at step 2
+
+#### Use case: Edit person
+
+**MSS**
+
+1. User requests to list persons
+2. AddressBook shows a list of persons
+3. User requests to edit a specific person in the list
+4. AddressBook edits the person
+Use case ends.
+
+**Extensions**
+
+2a. The list is empty
+
+> Use case ends
+
+3a. The given index is invalid
+
+> 3a1. AddressBook shows an error message
+ Use case resumes at step 2
+
+4a. The given data has incorrect format
+
+> 4a1. AddressBook shows an error message
+ Use case resumes at step 2
+
+#### Use case: Sort all persons
+
+**MSS**
+
+1. User requests to sort persons
+2. AddressBook sort the list of all persons and display it
+Use case ends.
## Appendix C : Non Functional Requirements
diff --git a/doc/UserGuide.md b/doc/UserGuide.md
index d03dfce73..694e8e441 100644
--- a/doc/UserGuide.md
+++ b/doc/UserGuide.md
@@ -34,10 +34,29 @@ Examples:
* `add John Doe p/98765432 e/johnd@gmail.com a/John street, block 123, #01-01`
* `add Betsy Crowe pp/1234567 e/betsycrowe@gmail.com pa/Newgate Prison t/criminal t/friend`
+## Editing a person : `edit`
+Edits the specified person from the address book.
+Format: `edit INDEX [p]p/PHONE_NUMBER [p]e/EMAIL [p]a/ADDRESS [t/TAG]...`
+
+> Edits the person at the specified `INDEX`.
+ The index refers to the index number shown in the most recent listing.
+
+Examples:
+* `list`
+ `edit 2 pp/999 pe/betsy@gmail.com pa/somewhere t/secretive`
+ Edits the 2nd person in the address book.
+* `find Betsy`
+ `edit 1 pp/999 pe/betsy@gmail.com pa/somewhere t/secretive`
+ Edits the 1st person in the results of the `find` command.
+
## Listing all persons : `list`
Shows a list of all persons in the address book.
Format: `list`
+## Sorting all persons: `sort`
+Sorts the address book by name in ascending order.
+Format: `sort`
+
## Finding all persons containing any keyword in their name: `find`
Finds persons whose names contain any of the given keywords.
Format: `find KEYWORD [MORE_KEYWORDS]`
diff --git a/src/seedu/addressbook/commands/EditCommand.java b/src/seedu/addressbook/commands/EditCommand.java
new file mode 100644
index 000000000..e92a6c7a2
--- /dev/null
+++ b/src/seedu/addressbook/commands/EditCommand.java
@@ -0,0 +1,75 @@
+package seedu.addressbook.commands;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import seedu.addressbook.common.Messages;
+import seedu.addressbook.data.exception.IllegalValueException;
+import seedu.addressbook.data.person.*;
+import seedu.addressbook.data.person.UniquePersonList.PersonNotFoundException;
+import seedu.addressbook.data.tag.Tag;
+import seedu.addressbook.data.tag.UniqueTagList;
+
+
+/**
+ * Edits a person identified using it's last displayed index from the address book.
+ */
+public class EditCommand extends Command {
+
+ public static final String COMMAND_WORD = "edit";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n"
+ + "Edits the person identified by the index number used in the last person listing.\n\t"
+ + "Parameters: INDEX [p]p/PHONE [p]e/EMAIL [p]a/ADDRESS [t/TAG]...\n\t"
+ + "Example: " + COMMAND_WORD
+ + " 1 p/98765432 e/johnd@gmail.com a/311, Clementi Ave 2, #02-25 t/friends t/owesMoney";
+
+ public static final String MESSAGE_EDIT_PERSON_SUCCESS = "Edited Person: %1$s";
+ public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book";
+
+ private final Person toEdit;
+
+ /**
+ * Convenience constructor using raw values.
+ *
+ * @throws IllegalValueException if any of the raw values are invalid
+ */
+ public EditCommand(int targetIndex,
+ String phone, boolean isPhonePrivate,
+ String email, boolean isEmailPrivate,
+ String address, boolean isAddressPrivate,
+ Set tags) throws IllegalValueException {
+ super(targetIndex);
+ final Set tagSet = new HashSet<>();
+ for (String tagName : tags) {
+ tagSet.add(new Tag(tagName));
+ }
+ this.toEdit = new Person(
+ new Name("dummy"),
+ new Phone(phone, isPhonePrivate),
+ new Email(email, isEmailPrivate),
+ new Address(address, isAddressPrivate),
+ new UniqueTagList(tagSet)
+ );
+ }
+
+ public ReadOnlyPerson getPerson() {
+ return toEdit;
+ }
+
+ @Override
+ public CommandResult execute() {
+ try {
+ final ReadOnlyPerson target = getTargetPerson();
+ toEdit.changeName(target.getName());
+ addressBook.editPerson(target, toEdit);
+ return new CommandResult(String.format(MESSAGE_EDIT_PERSON_SUCCESS, toEdit));
+
+ } catch (IndexOutOfBoundsException ie) {
+ return new CommandResult(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ } catch (PersonNotFoundException pnfe) {
+ return new CommandResult(Messages.MESSAGE_PERSON_NOT_IN_ADDRESSBOOK);
+ }
+ }
+
+}
diff --git a/src/seedu/addressbook/commands/HelpCommand.java b/src/seedu/addressbook/commands/HelpCommand.java
index ef2ed7d0e..ad2fbe778 100644
--- a/src/seedu/addressbook/commands/HelpCommand.java
+++ b/src/seedu/addressbook/commands/HelpCommand.java
@@ -12,10 +12,12 @@ public class HelpCommand extends Command {
+ "Example: " + COMMAND_WORD;
public static final String MESSAGE_ALL_USAGES = AddCommand.MESSAGE_USAGE
+ + "\n" + EditCommand.MESSAGE_USAGE
+ "\n" + DeleteCommand.MESSAGE_USAGE
+ "\n" + ClearCommand.MESSAGE_USAGE
+ "\n" + FindCommand.MESSAGE_USAGE
+ "\n" + ListCommand.MESSAGE_USAGE
+ + "\n" + SortCommand.MESSAGE_USAGE
+ "\n" + ViewCommand.MESSAGE_USAGE
+ "\n" + ViewAllCommand.MESSAGE_USAGE
+ "\n" + HelpCommand.MESSAGE_USAGE
diff --git a/src/seedu/addressbook/commands/SortCommand.java b/src/seedu/addressbook/commands/SortCommand.java
new file mode 100644
index 000000000..0c9019dd1
--- /dev/null
+++ b/src/seedu/addressbook/commands/SortCommand.java
@@ -0,0 +1,33 @@
+package seedu.addressbook.commands;
+
+import seedu.addressbook.data.exception.IllegalValueException;
+import seedu.addressbook.data.person.*;
+import seedu.addressbook.data.tag.Tag;
+import seedu.addressbook.data.tag.UniqueTagList;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Sorts the address book by name in ascending order.
+ */
+public class SortCommand extends Command {
+
+ public static final String COMMAND_WORD = "sort";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n"
+ + "Sorts the address book by name in ascending order, then display it.\n\t"
+ + "Example: " + COMMAND_WORD;
+
+ public static final String MESSAGE_SUCCESS = "Address book is sorted as above.\n\n";
+
+ @Override
+ public CommandResult execute() {
+ addressBook.sort();
+ List allPersons = addressBook.getAllPersons().immutableListView();
+ return new CommandResult(MESSAGE_SUCCESS
+ + getMessageForPersonListShownSummary(allPersons), allPersons);
+ }
+
+}
\ No newline at end of file
diff --git a/src/seedu/addressbook/data/AddressBook.java b/src/seedu/addressbook/data/AddressBook.java
index 7af05d271..3186fc457 100644
--- a/src/seedu/addressbook/data/AddressBook.java
+++ b/src/seedu/addressbook/data/AddressBook.java
@@ -82,6 +82,19 @@ public void addPerson(Person toAdd) throws DuplicatePersonException {
allPersons.add(toAdd);
}
+ public void editPerson(ReadOnlyPerson target, Person editedPerson) throws PersonNotFoundException {
+ syncTagsWithMasterList(editedPerson);
+ allPersons.editPerson(target, editedPerson);
+ }
+
+
+ /**
+ * Sorts the address book by name in ascending order.
+ */
+ public void sort() {
+ allPersons.sort();
+ }
+
/**
* Checks if an equivalent person exists in the address book.
*/
diff --git a/src/seedu/addressbook/data/person/Person.java b/src/seedu/addressbook/data/person/Person.java
index cf6211841..a3b6d5f87 100644
--- a/src/seedu/addressbook/data/person/Person.java
+++ b/src/seedu/addressbook/data/person/Person.java
@@ -1,5 +1,6 @@
package seedu.addressbook.data.person;
+import seedu.addressbook.data.exception.IllegalValueException;
import seedu.addressbook.data.tag.UniqueTagList;
import java.util.Objects;
@@ -39,6 +40,15 @@ public Name getName() {
return name;
}
+ /**
+ * Change the name of a person
+ *
+ * @param newName
+ */
+ public void changeName(Name newName) {
+ name = newName;
+ }
+
@Override
public Phone getPhone() {
return phone;
diff --git a/src/seedu/addressbook/data/person/UniquePersonList.java b/src/seedu/addressbook/data/person/UniquePersonList.java
index c4848a1b4..c9b21bced 100644
--- a/src/seedu/addressbook/data/person/UniquePersonList.java
+++ b/src/seedu/addressbook/data/person/UniquePersonList.java
@@ -106,6 +106,30 @@ public void remove(ReadOnlyPerson toRemove) throws PersonNotFoundException {
}
}
+ /**
+ * Edits the person {@code target} in the list into {@code editedPerson}.
+ *
+ * @throws PersonNotFoundException if {@code target} could not be found in the list.
+ */
+ public void editPerson(ReadOnlyPerson target, Person editedPerson) throws PersonNotFoundException {
+ int index = internalList.indexOf(target);
+ if (index == -1) {
+ throw new PersonNotFoundException();
+ }
+ internalList.set(index, editedPerson);
+ }
+
+ /**
+ * Sorts the address book by name in ascending order.
+ */
+ public void sort() {
+ internalList.sort(new Comparator() {
+ public int compare(Person p1, Person p2) {
+ return p1.getName().toString().compareTo(p2.getName().toString());
+ }
+ });
+ }
+
/**
* Clears all persons in list.
*/
diff --git a/src/seedu/addressbook/parser/Parser.java b/src/seedu/addressbook/parser/Parser.java
index 58f4f7e6c..e96d25007 100644
--- a/src/seedu/addressbook/parser/Parser.java
+++ b/src/seedu/addressbook/parser/Parser.java
@@ -63,6 +63,9 @@ public Command parseCommand(String userInput) {
case DeleteCommand.COMMAND_WORD:
return prepareDelete(arguments);
+ case EditCommand.COMMAND_WORD:
+ return prepareEdit(arguments);
+
case ClearCommand.COMMAND_WORD:
return new ClearCommand();
@@ -72,6 +75,9 @@ public Command parseCommand(String userInput) {
case ListCommand.COMMAND_WORD:
return new ListCommand();
+ case SortCommand.COMMAND_WORD:
+ return new SortCommand();
+
case ViewCommand.COMMAND_WORD:
return prepareView(arguments);
@@ -156,6 +162,41 @@ private Command prepareDelete(String args) {
}
}
+ /**
+ * Parses arguments in the context of the edit person command.
+ *
+ * @param args full command args string
+ * @return the prepared command
+ */
+ private Command prepareEdit(String args){
+ final Matcher matcher = PERSON_DATA_ARGS_FORMAT.matcher(args.trim());
+ // Validate arg string format
+ if (!matcher.matches()) {
+ return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE));
+ }
+ try {
+ final int targetIndex = parseArgsAsDisplayedIndex(matcher.group("name"));
+ return new EditCommand(
+ targetIndex,
+
+ matcher.group("phone"),
+ isPrivatePrefixPresent(matcher.group("isPhonePrivate")),
+
+ matcher.group("email"),
+ isPrivatePrefixPresent(matcher.group("isEmailPrivate")),
+
+ matcher.group("address"),
+ isPrivatePrefixPresent(matcher.group("isAddressPrivate")),
+
+ getTagsFromArgs(matcher.group("tagArguments"))
+ );
+ } catch (IllegalValueException ive) {
+ return new IncorrectCommand(ive.getMessage());
+ } catch (ParseException | NumberFormatException e) {
+ return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE));
+ }
+ }
+
/**
* Parses arguments in the context of the view command.
*
diff --git a/test/java/seedu/addressbook/logic/LogicTest.java b/test/java/seedu/addressbook/logic/LogicTest.java
index 7efa921ca..842200e65 100644
--- a/test/java/seedu/addressbook/logic/LogicTest.java
+++ b/test/java/seedu/addressbook/logic/LogicTest.java
@@ -181,7 +181,64 @@ public void execute_addDuplicate_notAllowed() throws Exception {
expectedAB,
false,
Collections.emptyList());
+ }
+
+ @Test
+ public void execute_edit_invalidArgsFormat() throws Exception {
+ String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE);
+ assertCommandBehavior(
+ "edit wrong args wrong args", expectedMessage);
+ assertCommandBehavior(
+ "edit invalidIndex p/12345 e/valid@email.butNoPhonePrefix a/valid, address", expectedMessage);
+ assertCommandBehavior(
+ "edit 1 12345 e/valid@email.butNoPhonePrefix a/valid, address", expectedMessage);
+ assertCommandBehavior(
+ "edit 1 p/12345 valid@email.butNoPrefix a/valid, address", expectedMessage);
+ assertCommandBehavior(
+ "edit 1 p/12345 e/valid@email.butNoAddressPrefix valid, address", expectedMessage);
+ }
+
+ @Test
+ public void execute_edit_invalidPersonData() throws Exception {
+ assertCommandBehavior(
+ "edit 1 p/not_numbers e/valid@e.mail a/valid, address", Phone.MESSAGE_PHONE_CONSTRAINTS);
+ assertCommandBehavior(
+ "edit 1 p/12345 e/notAnEmail a/valid, address", Email.MESSAGE_EMAIL_CONSTRAINTS);
+ assertCommandBehavior(
+ "edit 1 p/12345 e/valid@e.mail a/valid, address t/invalid_-[.tag", Tag.MESSAGE_TAG_CONSTRAINTS);
+
+ }
+
+ @Test
+ public void execute_edit_successful() throws Exception {
+ // prepare expectations
+ TestDataHelper helper = new TestDataHelper();
+ Person p1 = helper.generatePersonWithName("Alpha");
+ Person p2 = helper.generatePersonWithName("Beta");
+ Person p3 = helper.generatePersonWithName("Gamma");
+
+ List personList = helper.generatePersonList(p1, p2, p3);
+ AddressBook expectedAB = helper.generateAddressBook(personList);
+ // prepare address book state
+ helper.addToAddressBook(addressBook, personList);
+ final Set tagSet = new HashSet<>();
+ tagSet.add(new Tag("secretive"));
+ Person editedPerson = new Person(
+ new Name("Beta"),
+ new Phone("911", true),
+ new Email("beta@gmail.com", false),
+ new Address("somewhere", true),
+ new UniqueTagList(tagSet)
+ );
+ logic.setLastShownList(personList);
+ expectedAB.editPerson(p2, editedPerson);
+
+ assertCommandBehavior("edit 2 pp/911 e/beta@gmail.com pa/somewhere t/secretive",
+ String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, editedPerson),
+ expectedAB,
+ false,
+ personList);
}
@Test
@@ -201,6 +258,52 @@ public void execute_list_showsAllPersons() throws Exception {
expectedList);
}
+ @Test
+ public void execute_sortEmptyList_emptyAddressBook() throws Exception {
+ TestDataHelper helper = new TestDataHelper();
+ List emptyList = new ArrayList<>();
+ List expectedList = new ArrayList<>();
+ AddressBook expectedAB = helper.generateAddressBook(expectedList);
+
+ // prepare address book state
+ helper.addToAddressBook(addressBook, emptyList);
+
+ String messageSuccess = SortCommand.MESSAGE_SUCCESS
+ + SortCommand.getMessageForPersonListShownSummary(expectedList);
+
+ assertCommandBehavior("sort",
+ messageSuccess,
+ expectedAB,
+ true,
+ expectedList);
+ }
+
+ @Test
+ public void execute_sortUnorderedList_showsAllPersons() throws Exception {
+ // prepare expectations
+ TestDataHelper helper = new TestDataHelper();
+ Person p1 = helper.generatePersonWithName("Alpha");
+ Person p2 = helper.generatePersonWithName("Beta");
+ Person p3 = helper.generatePersonWithName("Gamma");
+ Person p4 = helper.generatePersonWithName("Delta");
+
+ List unorderedList = helper.generatePersonList(p3, p4, p1, p2);
+ List expectedList = helper.generatePersonList(p1, p2, p4, p3);
+ AddressBook expectedAB = helper.generateAddressBook(expectedList);
+
+ // prepare address book state
+ helper.addToAddressBook(addressBook, unorderedList);
+
+ String messageSuccess = SortCommand.MESSAGE_SUCCESS
+ + SortCommand.getMessageForPersonListShownSummary(expectedList);
+
+ assertCommandBehavior("sort",
+ messageSuccess,
+ expectedAB,
+ true,
+ expectedList);
+ }
+
@Test
public void execute_view_invalidArgsFormat() throws Exception {
String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, ViewCommand.MESSAGE_USAGE);
diff --git a/test/java/seedu/addressbook/parser/ParserTest.java b/test/java/seedu/addressbook/parser/ParserTest.java
index f01be613c..b2a732129 100644
--- a/test/java/seedu/addressbook/parser/ParserTest.java
+++ b/test/java/seedu/addressbook/parser/ParserTest.java
@@ -59,6 +59,12 @@ public void listCommand_parsedCorrectly() {
parseAndAssertCommandType(input, ListCommand.class);
}
+ @Test
+ public void sortCommand_parsedCorrectly() {
+ final String input = "sort";
+ parseAndAssertCommandType(input, SortCommand.class);
+ }
+
@Test
public void exitCommand_parsedCorrectly() {
final String input = "exit";
@@ -246,6 +252,79 @@ public void addCommand_duplicateTags_merged() throws IllegalValueException {
assertEquals(result.getPerson(), testPerson);
}
+ @Test
+ public void editCommand_invalidArgs() {
+ final String[] inputs = {
+ "edit",
+ "edit ",
+ "edit wrong args format",
+ // not an index
+ String.format("edit $s $s e/$s a/$s", Name.EXAMPLE, Phone.EXAMPLE, Email.EXAMPLE, Address.EXAMPLE),
+ // no phone prefix
+ String.format("edit 1 $s e/$s a/$s", Phone.EXAMPLE, Email.EXAMPLE, Address.EXAMPLE),
+ // no email prefix
+ String.format("edit 1 p/$s $s a/$s", Phone.EXAMPLE, Email.EXAMPLE, Address.EXAMPLE),
+ // no address prefix
+ String.format("edit 1 p/$s e/$s $s", Phone.EXAMPLE, Email.EXAMPLE, Address.EXAMPLE)
+ };
+ final String resultMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE);
+ parseAndAssertIncorrectWithMessage(resultMessage, inputs);
+ }
+
+ @Test
+ public void editCommand_invalidPersonDataInArgs() {
+ final String invalidIndex = "[@]\\!?/[;]";
+ final String validIndex = "1";
+ final String invalidPhoneArg = "p/not__numbers";
+ final String validPhoneArg = "p/" + Phone.EXAMPLE;
+ final String invalidEmailArg = "e/notAnEmail123";
+ final String validEmailArg = "e/" + Email.EXAMPLE;
+ final String invalidTagArg = "t/invalid_-[.tag";
+
+ // address can be any string, so no invalid address
+ final String editCommandFormatString = "edit $s $s $s a/" + Address.EXAMPLE;
+
+ // test each incorrect person data field argument individually
+ final String[] inputs = {
+ // invalid index
+ String.format(editCommandFormatString, invalidIndex, validPhoneArg, validEmailArg),
+ // invalid phone
+ String.format(editCommandFormatString, validIndex, invalidPhoneArg, validEmailArg),
+ // invalid email
+ String.format(editCommandFormatString, validIndex, validPhoneArg, invalidEmailArg),
+ // invalid tag
+ String.format(editCommandFormatString, validIndex, validPhoneArg, validEmailArg) + " " + invalidTagArg
+ };
+ for (String input : inputs) {
+ parseAndAssertCommandType(input, IncorrectCommand.class);
+ }
+ }
+
+ @Test
+ public void editCommand_validPersonData_parsedCorrectly() throws IllegalValueException{
+ final Person testPerson = generateTestPerson();
+ testPerson.changeName(new Name("dummy"));
+ final String addInput = convertPersonToAddCommandString(testPerson);
+ final String input = "edit 1 " + addInput.substring(10);
+ final EditCommand result = parseAndAssertCommandType(input, EditCommand.class);
+ assertEquals(result.getPerson(), testPerson);
+ }
+
+ @Test
+ public void editCommand_duplicateTags_merged() throws IllegalValueException {
+ final Person testPerson = generateTestPerson();
+ testPerson.changeName(new Name("dummy"));
+ final String addInput = convertPersonToAddCommandString(testPerson);
+ String input = "edit 1 " + addInput.substring(10);
+ for (Tag tag : testPerson.getTags()) {
+ // create duplicates by doubling each tag
+ input += " t/" + tag.tagName;
+ }
+
+ final EditCommand result = parseAndAssertCommandType(input, EditCommand.class);
+ assertEquals(result.getPerson(), testPerson);
+ }
+
private static Person generateTestPerson() {
try {
return new Person(