diff --git a/doc/Diagrams.pptx b/doc/Diagrams.pptx
index f3ddedff3..45171f705 100644
Binary files a/doc/Diagrams.pptx and b/doc/Diagrams.pptx differ
diff --git a/doc/LearningOutcomes.md b/doc/LearningOutcomes.md
index fc15e8fc1..88ac9be37 100644
--- a/doc/LearningOutcomes.md
+++ b/doc/LearningOutcomes.md
@@ -8,11 +8,9 @@ After studying this code and completing the corresponding exercises, you should
1. [Use abstract classes/methods `[LO-Abstract]`](#use-abstract-classesmethods-lo-abstract)
1. [Follow Liskov Substitution Principle `[LO-LSP]`](#follow-liskov-substitution-principle-lo-lsp)
1. [Use Java-FX for GUI programming `[LO-JavaFx]`](#use-java-fx-for-gui-programming-lo-javafx)
-1. [Apply Dependency Inversion Principle `[LO-DIP]`](#apply-dependency-inversion-principle-lo-dip)
-1. [Use Dependency Injection `[LO-DI]`](#use-dependency-injection-lo-di)
-1. [Apply Open-Closed Principle `[LO-OCP]`](#apply-open-closed-principle-lo-ocp)
-1. [Analyze Coupling and Cohesion of designs `[LO-CouplingCohesion]`](#analyze-coupling-and-cohesion-of-designs-lo-couplingcohesion)
-1. [Work in a 2KLoC code base `[LO-2KLoC]`](#work-in-a-2kloc-code-base-lo-2kloc)
+1. [Apply Dependency Inversion Principle `[LO-DIP]`](#apply-dependency-inversion-principle)
+1. [Use Dependency Injection `[LO-DI]`](#use-dependency-injection)
+1. [Apply Open-Closed Principle `[LO-OCP]`](#apply-open-closed-principle)
------------------------------------------------------------------------------------------------------
@@ -99,73 +97,43 @@ Covered by `[LO-Polymorphism]`
* Note how `Logic` class depends on the `StorageFile` class. This is a violation of DIP.
* Modify the implementation as follows so that both `Logic` and `StorageFile` now depend on the abstraction
- `Storage`. (Note: The term *abstraction* here is referring to the conception of abstracting, not to the fact that `Storage` class is `abastract`)
-
-* Where else in the code do you notice the application of DIP?
+ `Storage`.
+
------------------------------------------------------------------------------------------------------
## Use Dependency Injection `[LO-DI]`
-Note how `Logic` class depends on the `StorageFile` class. This means when testing the `Logic` class,
-our test cases execute the `StorageFile` class as well. What if we want to test the `Logic` class without
-getting the `StorageFile` class involved? That is a situation where we can use *Dependency Injection*.
-
#### Exercise: Facilitate injecting a StorageStub
-* Change the implementation as follows so that we can inject a `StorageStub` when testing the `Logic`
+* Note how `Logic` class depends on the `StorageFile` class. This means when testing the `Logic` class,
+ our test cases executes the `StorageFile` class as well. What if we want to test the `Logic` class without
+ getting the `StorageFile` class involved?
+
+* Now, change the implementation as follows so that we can inject a `StorageStub` when testing the `Logic`
class.
-
+
- > If you did the exercise in [`LO-DIP`](#apply-dependency-inversion-principle-lo-dip)
+ > If you did the exercise in [`LO-DIP`](#apply-dependency-inversion-principle)
already but those changes are in a different branch, you may be able to reuse some of those commits
by cherry picking them from that branch to the branch you created for this exercise.
Note: *cherry picking* is simply copy-pasting a commit from one branch to another. In SourceTree, you can
right-click on the commit your want to copy to the current branch, and choose 'Cherry pick'
-* Implement the `StorageStub` such that calls to the `save` method do nothing (i.e. empty method body).
-* Update the `LogicTest` to work with the `StorageStub` instead of the actual `StorageFile` object.
- i.e. `Logic` injects a `StorageStub` object to replace the dependency of `Logic` on `Storage` before
- testing `Logic`.
+* Implement the `StorageStub` to ignore calls to the `save` method.
+ Update the `LogicTest` to work with the `StorageStub` instead of the actual `StorageFile` object.
------------------------------------------------------------------------------------------------------
## Apply Open-Closed Principle `[LO-OCP]`
-#### Exercise: Analyze OCP-compliance of the `Logic` class
+#### Exercise: Add a new command
-* Consider adding a new command to the Address Book. e.g. an `edit` command.
-* Notice how little you need to change in the `Logic` class to extend its behavior so that it can execute
- the new command.
- That is because `Logic` follows the OCP i.e. `Logic` is *open to be extended* with more
+* Add a new command to the Address Book. e.g. an `edit` command
+* Notice how little you need to change in the `Logic` class that is responsible for executing the commands.
+ That is because classes `Logic` and `*Command` follow the OCP i.e. `Logic` is *open to be extended* with more
commands but *closed for modifications*.
-* Is it possible to make the `Parser` class more OCP-compliant in terms of extending it to handle more
- command types?
-* In terms of how it saves data, does `Logic` become more OCP-compliant
- after applying DIP as given in [`LO-DIP`](#apply-dependency-inversion-principle-lo-dip)?
- How can you improve `Logic`'s OCP-compliance further so that it can not only work with different types
- of storages, but different number of storages (e.g. save to both a text file and a database).
-
+* Think about how to make the `Person` class similarly open to be extended with more contact details
+ (e.g. `SkypeId`) without needing modifications to its code during those extensions.
------------------------------------------------------------------------------------------------------
-## Analyze Coupling and Cohesion of designs `[LO-CouplingCohesion]`
-
-* As you saw above, DIP helps us to avoid *coupling* from higher-level classes to lower-level classes.
-
-* Notice how having a separate `Formattter` class (an application of SIP) improves the *cohesion* of
- the `MainWindow` class as well as the `Formatter` class.
-
-#### Exercise: Identify places to reduce coupling and increase cohesion
-
-* Where else in the design coupling can be reduced further, or cohesion can be increased further?
-
-------------------------------------------------------------------------------------------------------
-
-## Work in a 2KLoC code base `[LO-2KLoC]`
-
-#### Exercise: Enhance AddressBook
-
-* Enhance AddressBook in some way. e.g. add a new command
-
-------------------------------------------------------------------------------------------------------
-
diff --git a/doc/UserGuide.md b/doc/UserGuide.md
index 3e4baae95..4ee8e719f 100644
--- a/doc/UserGuide.md
+++ b/doc/UserGuide.md
@@ -1,6 +1,6 @@
# User Guide
-This product is not meant for end-users and therefore there is no user-friendly installer.
+This product is not meant for end-users and therefore there is no user-friendly installer.
Please refer to the [Setting up](DeveloperGuide.md#setting-up) section to learn how to set up the project.
## Starting the program
@@ -16,23 +16,39 @@ Please refer to the [Setting up](DeveloperGuide.md#setting-up) section to learn
Format: `help`
> Help is also shown if you enter an incorrect command e.g. `abcd`
-
+
## Adding a person: `add`
Adds a person to the address book
-Format: `add NAME [p]p/PHONE_NUMBER [p]e/EMAIL [p]a/ADDRESS [t/TAG]...`
-
-> Words in `UPPER_CASE` are the parameters, items in `SQUARE_BRACKETS` are optional,
-> items with `...` after them can have multiple instances. Order of parameters are fixed.
->
+Format: `add NAME [p]p/PHONE_NUMBER [p]e/EMAIL [p]a/ADDRESS [t/TAG]...`
+
+> Words in `UPPER_CASE` are the parameters, items in `SQUARE_BRACKETS` are optional,
+> items with `...` after them can have multiple instances. Order of parameters are fixed.
+>
> Put a `p` before the phone / email / address prefixes to mark it as `private`. `private` details can only
> be seen using the `viewall` command.
->
+>
> Persons can have any number of tags (including 0)
-Examples:
+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`
+## Editting a person: `add`
+Edit a existing person's detail in the address book
+Format: `edit INDEX n/NAME [p]p/PHONE_NUMBER [p]e/EMAIL [p]a/ADDRESS [t/TAG]...`
+
+> Words in `UPPER_CASE` are the parameters, items in `SQUARE_BRACKETS` are optional,
+> items with `...` after them can have multiple instances. Order of parameters are fixed.
+>
+> Put a `p` before the phone / email / address prefixes to mark it as `private`. `private` details can only
+> be seen using the `viewall` command.
+>
+> Persons can have any number of tags (including 0)
+
+Examples:
+* `edit 1 n/John Doe p/98765432 e/johnd@gmail.com a/John street, block 123, #01-01`
+* `edit 2 n/Betsy Crowe pp/1234567 e/betsycrowe@gmail.com pa/Newgate Prison t/criminal t/friend`
+
## Listing all persons : `list`
Shows a list of all persons in the address book.
Format: `list`
@@ -41,10 +57,10 @@ Format: `list`
Finds persons whose names contain any of the given keywords.
Format: `find KEYWORD [MORE_KEYWORDS]`
-> The search is case sensitive, the order of the keywords does not matter, only the name is searched,
+> The search is case sensitive, the order of the keywords does not matter, only the name is searched,
and persons matching at least one keyword will be returned (i.e. `OR` search).
-Examples:
+Examples:
* `find John`
Returns `John Doe` but not `john`
* `find Betsy Tim John`
@@ -54,14 +70,14 @@ Examples:
Deletes the specified person from the address book. Irreversible.
Format: `delete INDEX`
-> Deletes the person at the specified `INDEX`.
+> Deletes the person at the specified `INDEX`.
The index refers to the index number shown in the most recent listing.
-Examples:
+Examples:
* `list`
`delete 2`
Deletes the 2nd person in the address book.
-* `find Betsy`
+* `find Betsy`
`delete 1`
Deletes the 1st person in the results of the `find` command.
@@ -69,14 +85,14 @@ Examples:
Displays the non-private details of the specified person.
Format: `view INDEX`
-> Views the person at the specified `INDEX`.
+> Views the person at the specified `INDEX`.
The index refers to the index number shown in the most recent listing.
-Examples:
+Examples:
* `list`
`view 2`
Views the 2nd person in the address book.
-* `find Betsy`
+* `find Betsy`
`view 1`
Views the 1st person in the results of the `find` command.
@@ -84,26 +100,33 @@ Examples:
Displays all details (including private details) of the specified person.
Format: `viewall INDEX`
-> Views all details of the person at the specified `INDEX`.
+> Views all details of the person at the specified `INDEX`.
The index refers to the index number shown in the most recent listing.
-Examples:
+Examples:
* `list`
`viewall 2`
Views all details of the 2nd person in the address book.
-* `find Betsy`
+* `find Betsy`
`viewall 1`
Views all details of the 1st person in the results of the `find` command.
+## View all the tags : `viewalltags`
+Displays all tags in current storage file.
+Format: `viewalltags`
+
+Examples:
+* `viewalltags`
+
## Clearing all entries : `clear`
Clears all entries from the address book.
-Format: `clear`
+Format: `clear`
## Exiting the program : `exit`
Exits the program.
-Format: `exit`
+Format: `exit`
-## Saving the data
+## Saving the data
Address book data are saved in the hard disk automatically after any command that changes the data.
There is no need to save manually.
@@ -113,5 +136,5 @@ You can change the location by specifying the file path as a program argument. The file name must end in `.txt` for it to be acceptable to the program.
>
-> When running the program inside Eclipse, you can
+> When running the program inside Eclipse, you can
[set command line parameters before running the program](http://stackoverflow.com/questions/7574543/how-to-pass-console-arguments-to-application-in-eclipse).
diff --git a/doc/images/DependencyInjection.png b/doc/images/DependencyInjection.png
index 8fd318efe..562ae6b6a 100644
Binary files a/doc/images/DependencyInjection.png and b/doc/images/DependencyInjection.png differ
diff --git a/doc/images/LogicStroageFileDIP.png b/doc/images/LogicStroageFileDIP.png
index 971895ffd..6733e7c2d 100644
Binary files a/doc/images/LogicStroageFileDIP.png and b/doc/images/LogicStroageFileDIP.png differ
diff --git a/src/seedu/addressbook/commands/AddCommand.java b/src/seedu/addressbook/commands/AddCommand.java
index b0f202d42..e28bf739e 100644
--- a/src/seedu/addressbook/commands/AddCommand.java
+++ b/src/seedu/addressbook/commands/AddCommand.java
@@ -58,13 +58,19 @@ public ReadOnlyPerson getPerson() {
}
@Override
- public CommandResult execute() {
+ public CommandResult execute() throws Exception{
try {
addressBook.addPerson(toAdd);
return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd));
} catch (UniquePersonList.DuplicatePersonException dpe) {
return new CommandResult(MESSAGE_DUPLICATE_PERSON);
+ } catch (Exception e) {
+ throw new Exception();
}
}
-
+
+ @Override
+ public boolean isMutating(){
+ return true;
+ }
}
diff --git a/src/seedu/addressbook/commands/ClearCommand.java b/src/seedu/addressbook/commands/ClearCommand.java
index b09706013..39f2ab58e 100644
--- a/src/seedu/addressbook/commands/ClearCommand.java
+++ b/src/seedu/addressbook/commands/ClearCommand.java
@@ -19,4 +19,9 @@ public CommandResult execute() {
addressBook.clear();
return new CommandResult(MESSAGE_SUCCESS);
}
+
+ @Override
+ public boolean isMutating(){
+ return true;
+ }
}
diff --git a/src/seedu/addressbook/commands/Command.java b/src/seedu/addressbook/commands/Command.java
index ccd4fcbf4..9df90a8ad 100644
--- a/src/seedu/addressbook/commands/Command.java
+++ b/src/seedu/addressbook/commands/Command.java
@@ -38,8 +38,9 @@ public static String getMessageForPersonListShownSummary(List extends ReadOnly
/**
* Executes the command and returns the result.
+ * @throws Exception
*/
- public abstract CommandResult execute();
+ public abstract CommandResult execute() throws Exception;
/**
* Supplies the data the command will operate on.
@@ -65,4 +66,10 @@ public int getTargetIndex() {
public void setTargetIndex(int targetIndex) {
this.targetIndex = targetIndex;
}
+
+ /**
+ * @return true if the command mutate the data. Otherwise return false
+ */
+ public abstract boolean isMutating();
+
}
diff --git a/src/seedu/addressbook/commands/DeleteCommand.java b/src/seedu/addressbook/commands/DeleteCommand.java
index 1dd78f85e..4d06e17e5 100644
--- a/src/seedu/addressbook/commands/DeleteCommand.java
+++ b/src/seedu/addressbook/commands/DeleteCommand.java
@@ -38,5 +38,9 @@ public CommandResult execute() {
return new CommandResult(Messages.MESSAGE_PERSON_NOT_IN_ADDRESSBOOK);
}
}
-
+
+ @Override
+ public boolean isMutating(){
+ return true;
+ }
}
diff --git a/src/seedu/addressbook/commands/EditCommand.java b/src/seedu/addressbook/commands/EditCommand.java
new file mode 100644
index 000000000..afa33ec75
--- /dev/null
+++ b/src/seedu/addressbook/commands/EditCommand.java
@@ -0,0 +1,77 @@
+package seedu.addressbook.commands;
+
+import seedu.addressbook.common.Messages;
+import seedu.addressbook.data.exception.IllegalValueException;
+import seedu.addressbook.data.person.*;
+import seedu.addressbook.data.tag.Tag;
+import seedu.addressbook.data.tag.UniqueTagList;
+import seedu.addressbook.ui.Gui;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class EditCommand extends Command {
+ public static final String COMMAND_WORD = "edit";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + "Edits a exsting person's detail identified using index number in the list. \n\t"
+ + "Contact details can be marked private by prepending 'p' to the prefix.\n\t"
+ + "Parameters: INDEX n/NAME [p]p/PHONE [p]e/EMAIL [p]a/ADDRESS [t/TAG]...\n\t"
+ + "Example: " + COMMAND_WORD
+ + " 1 n/John Doe p/98765432 e/johnd@gmail.com a/311, Clementi Ave 2, #02-25 t/friends t/owesMoney";
+
+ public static final String MESSAGE_SUCCESS = "This person's detail has been edited: %1$s";
+
+ private final Person toEdit;
+
+ /**
+ * Convenience constructor using raw values.
+ *
+ * @throws IllegalValueException if any of the raw values are invalid
+ */
+ public EditCommand(int indexOfPerson, String name,
+ String phone, boolean isPhonePrivate,
+ String email, boolean isEmailPrivate,
+ String address, boolean isAddressPrivate,
+ Set tags) throws IllegalValueException {
+ super(indexOfPerson);
+ final Set tagSet = new HashSet<>();
+ for (String tagName : tags) {
+ tagSet.add(new Tag(tagName));
+ }
+ this.toEdit = new Person(
+ new Name(name),
+ new Phone(phone, isPhonePrivate),
+ new Email(email, isEmailPrivate),
+ new Address(address, isAddressPrivate),
+ new UniqueTagList(tagSet)
+ );
+ }
+
+ @Override
+ public CommandResult execute() {
+ try {
+ editPersonInAddressbook(findPersonUsingIndex());
+ return new CommandResult(String.format(MESSAGE_SUCCESS, toEdit));
+ } catch (IndexOutOfBoundsException ie) {
+ return new CommandResult(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX +
+ String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE));
+ }
+ }
+
+ public Person findPersonUsingIndex() throws IndexOutOfBoundsException {
+ return ((List) addressBook.getAllPersons().getList()).get(getTargetIndex() - Gui.DISPLAYED_INDEX_OFFSET);
+ }
+
+ private void editPersonInAddressbook(Person person) {
+ person.setName(toEdit.getName());
+ person.setPhone(toEdit.getPhone());
+ person.setEmail(toEdit.getEmail());
+ person.setAddress(toEdit.getAddress());
+ person.setTags(toEdit.getTags());
+ }
+
+ @Override
+ public boolean isMutating(){
+ return true;
+ }
+}
diff --git a/src/seedu/addressbook/commands/ExitCommand.java b/src/seedu/addressbook/commands/ExitCommand.java
index 0585451f1..006ef161c 100644
--- a/src/seedu/addressbook/commands/ExitCommand.java
+++ b/src/seedu/addressbook/commands/ExitCommand.java
@@ -15,5 +15,9 @@ public class ExitCommand extends Command {
public CommandResult execute() {
return new CommandResult(MESSAGE_EXIT_ACKNOWEDGEMENT);
}
-
+
+ @Override
+ public boolean isMutating(){
+ return false;
+ }
}
diff --git a/src/seedu/addressbook/commands/FindCommand.java b/src/seedu/addressbook/commands/FindCommand.java
index c8e9a380f..969466117 100644
--- a/src/seedu/addressbook/commands/FindCommand.java
+++ b/src/seedu/addressbook/commands/FindCommand.java
@@ -1,6 +1,7 @@
package seedu.addressbook.commands;
import seedu.addressbook.data.person.ReadOnlyPerson;
+import seedu.addressbook.data.tag.Tag;
import java.util.*;
@@ -12,10 +13,14 @@ public class FindCommand extends Command {
public static final String COMMAND_WORD = "find";
- public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + "Finds all persons whose names contain any of "
- + "the specified keywords (case-sensitive) and displays them as a list with index numbers.\n\t"
- + "Parameters: KEYWORD [MORE_KEYWORDS]...\n\t"
- + "Example: " + COMMAND_WORD + " alice bob charlie";
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + "Finds all persons whose names / tags contain any of "
+ + "the specified keywords (case-sensitive) and displays them as a list with index numbers.\n"
+ + "Default Search\n\t"
+ + "Parameters: KEYWORD [MORE_KEYWORDS]...\n\t\t"
+ + "Example: " + COMMAND_WORD + " alice bob charlie\n"
+ + "Search by Tags\n\t"
+ + "Parameters: -t KEYWORD [MORE_KEYWORDS]...\n\t\t"
+ + "Example: " + COMMAND_WORD + "-t alice bob charlie";
private final Set keywords;
@@ -32,7 +37,17 @@ public Set getKeywords() {
@Override
public CommandResult execute() {
- final List personsFound = getPersonsWithNameContainingAnyKeyword(keywords);
+ List personsFound = null;
+
+ if (keywords.contains("-t")) {
+ keywords.remove("-t");
+ personsFound = getPersonsWithTagContainingAnyKeyword(keywords);
+ }
+ else{
+ personsFound = getPersonsWithNameContainingAnyKeyword(keywords);
+ }
+
+
return new CommandResult(getMessageForPersonListShownSummary(personsFound), personsFound);
}
@@ -44,13 +59,35 @@ public CommandResult execute() {
*/
private List getPersonsWithNameContainingAnyKeyword(Set keywords) {
final List matchedPersons = new ArrayList<>();
+
for (ReadOnlyPerson person : addressBook.getAllPersons()) {
final Set wordsInName = new HashSet<>(person.getName().getWordsInName());
if (!Collections.disjoint(wordsInName, keywords)) {
matchedPersons.add(person);
}
+
}
return matchedPersons;
}
+ private List getPersonsWithTagContainingAnyKeyword(Set keywords) {
+ final List matchedPersons = new ArrayList<>();
+
+ for (ReadOnlyPerson person : addressBook.getAllPersons()) {
+ final Set tagNames = new HashSet<>();
+ for (Tag tag : person.getTags())
+ tagNames.add(tag.getName());
+
+ if (!Collections.disjoint(tagNames, keywords)) {
+ matchedPersons.add(person);
+ }
+
+ }
+ return matchedPersons;
+ }
+
+ @Override
+ public boolean isMutating(){
+ return false;
+ }
}
diff --git a/src/seedu/addressbook/commands/HelpCommand.java b/src/seedu/addressbook/commands/HelpCommand.java
index 82530440c..73420e7a3 100644
--- a/src/seedu/addressbook/commands/HelpCommand.java
+++ b/src/seedu/addressbook/commands/HelpCommand.java
@@ -12,12 +12,15 @@ 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" + ViewAllTags.MESSAGE_USAGE
+ "\n" + HelpCommand.MESSAGE_USAGE
+ "\n" + ExitCommand.MESSAGE_USAGE;
@@ -27,4 +30,9 @@ public HelpCommand() {}
public CommandResult execute() {
return new CommandResult(MESSAGE_ALL_USAGES);
}
+
+ @Override
+ public boolean isMutating(){
+ return true;
+ }
}
diff --git a/src/seedu/addressbook/commands/IncorrectCommand.java b/src/seedu/addressbook/commands/IncorrectCommand.java
index 81abba7a1..8f7ffa5f2 100644
--- a/src/seedu/addressbook/commands/IncorrectCommand.java
+++ b/src/seedu/addressbook/commands/IncorrectCommand.java
@@ -17,4 +17,8 @@ public CommandResult execute() {
return new CommandResult(feedbackToUser);
}
+ @Override
+ public boolean isMutating(){
+ return false;
+ }
}
diff --git a/src/seedu/addressbook/commands/ListCommand.java b/src/seedu/addressbook/commands/ListCommand.java
index cb604a8e9..b8718732d 100644
--- a/src/seedu/addressbook/commands/ListCommand.java
+++ b/src/seedu/addressbook/commands/ListCommand.java
@@ -22,4 +22,9 @@ public CommandResult execute() {
List allPersons = addressBook.getAllPersons().immutableListView();
return new CommandResult(getMessageForPersonListShownSummary(allPersons), allPersons);
}
+
+ @Override
+ public boolean isMutating(){
+ return false;
+ }
}
diff --git a/src/seedu/addressbook/commands/SortCommand.java b/src/seedu/addressbook/commands/SortCommand.java
new file mode 100644
index 000000000..7ef6acb9a
--- /dev/null
+++ b/src/seedu/addressbook/commands/SortCommand.java
@@ -0,0 +1,52 @@
+package seedu.addressbook.commands;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import seedu.addressbook.data.exception.IllegalValueException;
+import seedu.addressbook.data.person.Address;
+import seedu.addressbook.data.person.Email;
+import seedu.addressbook.data.person.Name;
+import seedu.addressbook.data.person.Person;
+import seedu.addressbook.data.person.Phone;
+import seedu.addressbook.data.person.ReadOnlyPerson;
+import seedu.addressbook.data.tag.Tag;
+import seedu.addressbook.data.tag.UniqueTagList;
+
+public class SortCommand extends Command{
+
+
+ public static final String COMMAND_WORD = "sort";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n"
+ + "Sort all persons in the address book by specified field.\n\t"
+ + "Fields allowed are: name, phone, email, address.\n\t"
+ + "Default field is name.\n\t"
+ + "Example: " + COMMAND_WORD + " name";
+
+ public static final String MESSAGE_SUCCESS = "List Sorted";
+
+ private final String field;
+
+ public SortCommand() throws IllegalValueException {
+ this("name");
+ }
+
+ public SortCommand(String field) throws IllegalValueException {
+ this.field = field;
+ }
+
+ @Override
+ public CommandResult execute() {
+
+ addressBook.sort(field);
+
+ return new CommandResult(MESSAGE_SUCCESS + " by " + field + "!");
+ }
+
+ @Override
+ public boolean isMutating(){
+ return true;
+ }
+}
diff --git a/src/seedu/addressbook/commands/ViewAllCommand.java b/src/seedu/addressbook/commands/ViewAllCommand.java
index ed2c16e83..03c2a0c1e 100644
--- a/src/seedu/addressbook/commands/ViewAllCommand.java
+++ b/src/seedu/addressbook/commands/ViewAllCommand.java
@@ -37,4 +37,9 @@ public CommandResult execute() {
return new CommandResult(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
}
}
+
+ @Override
+ public boolean isMutating(){
+ return false;
+ }
}
diff --git a/src/seedu/addressbook/commands/ViewAllTags.java b/src/seedu/addressbook/commands/ViewAllTags.java
new file mode 100644
index 000000000..7d1efc02a
--- /dev/null
+++ b/src/seedu/addressbook/commands/ViewAllTags.java
@@ -0,0 +1,35 @@
+package seedu.addressbook.commands;
+
+import java.util.List;
+
+import seedu.addressbook.data.tag.Tag;
+import seedu.addressbook.data.tag.UniqueTagList;
+
+public class ViewAllTags extends Command{
+
+ public static final String COMMAND_WORD = "viewalltags";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + "Views all the tags. \n\t"
+ + "Example: " + COMMAND_WORD;
+ public static final String MESSAGE_VIEW_ALL_TAGS_SUCCESS = "All Tags has been displayed.";
+
+ @Override
+ public CommandResult execute() {
+ List allTags = addressBook.getAllTags().getList();
+ return new CommandResult(getTagsShowAll(allTags) + MESSAGE_VIEW_ALL_TAGS_SUCCESS);
+ }
+
+ public String getTagsShowAll(List internalList) {
+ int index=1;
+ final StringBuilder builder = new StringBuilder();
+ for (Tag tag : internalList) {
+ builder.append(index++ + " " + tag + "\n");
+ }
+ return builder.toString();
+ }
+
+ @Override
+ public boolean isMutating() {
+ return true;
+ }
+}
diff --git a/src/seedu/addressbook/commands/ViewCommand.java b/src/seedu/addressbook/commands/ViewCommand.java
index 1058c4b52..1f73bbe1a 100644
--- a/src/seedu/addressbook/commands/ViewCommand.java
+++ b/src/seedu/addressbook/commands/ViewCommand.java
@@ -38,4 +38,8 @@ public CommandResult execute() {
}
}
+ @Override
+ public boolean isMutating(){
+ return false;
+ }
}
diff --git a/src/seedu/addressbook/common/Messages.java b/src/seedu/addressbook/common/Messages.java
index 02cfe6155..3d7ca0d78 100644
--- a/src/seedu/addressbook/common/Messages.java
+++ b/src/seedu/addressbook/common/Messages.java
@@ -13,4 +13,5 @@ public class Messages {
"java seedu.addressbook.Main [STORAGE_FILE_PATH]";
public static final String MESSAGE_WELCOME = "Welcome to your Address Book!";
public static final String MESSAGE_USING_STORAGE_FILE = "Using storage file : %1$s";
+ public static final String MESSAGE_INVALID_FIELD = "Invalid field! \n%1$s";
}
diff --git a/src/seedu/addressbook/data/AddressBook.java b/src/seedu/addressbook/data/AddressBook.java
index b3ab0dfeb..b69ef6ffe 100644
--- a/src/seedu/addressbook/data/AddressBook.java
+++ b/src/seedu/addressbook/data/AddressBook.java
@@ -131,6 +131,14 @@ public void clear() {
allTags.clear();
}
+ /**
+ * Sorts the list by name.
+ */
+ public void sort(String field) {
+ allPersons.sort(field);
+
+ }
+
/**
* Defensively copied UniquePersonList of all persons in the address book at the time of the call.
*/
diff --git a/src/seedu/addressbook/data/person/Person.java b/src/seedu/addressbook/data/person/Person.java
index cf6211841..55f5be1d7 100644
--- a/src/seedu/addressbook/data/person/Person.java
+++ b/src/seedu/addressbook/data/person/Person.java
@@ -2,13 +2,14 @@
import seedu.addressbook.data.tag.UniqueTagList;
+import java.util.Comparator;
import java.util.Objects;
/**
* Represents a Person in the address book.
* Guarantees: details are present and not null, field values are validated.
*/
-public class Person implements ReadOnlyPerson {
+public class Person implements ReadOnlyPerson, Comparable{
private Name name;
private Phone phone;
@@ -83,5 +84,54 @@ public int hashCode() {
public String toString() {
return getAsTextShowAll();
}
+
+ @Override
+ public int compareTo(Person person2) {
+ return Comparators.NAME.compare(this, person2);
+ }
+
+ public static class Comparators {
+
+ public static Comparator NAME = new Comparator() {
+ @Override
+ public int compare(Person person1, Person person2) {
+ return person1.getName().fullName.compareTo(person2.getName().fullName);
+ }
+ };
+ public static Comparator PHONE = new Comparator() {
+ @Override
+ public int compare(Person person1, Person person2) {
+ return person1.getPhone().value.compareTo(person2.getPhone().value);
+ }
+ };
+ public static Comparator EMAIL = new Comparator() {
+ @Override
+ public int compare(Person person1, Person person2) {
+ return person1.getEmail().value.compareTo(person2.getEmail().value);
+ }
+ };
+ public static Comparator ADDRESS = new Comparator() {
+ @Override
+ public int compare(Person person1, Person person2) {
+ return person1.getAddress().value.compareTo(person2.getAddress().value);
+ }
+ };
+ }
+
+ public void setName(Name name) {
+ this.name =name;
+ }
+
+ public void setPhone(Phone phone) {
+ this.phone = phone;
+ }
+
+ public void setEmail(Email email) {
+ this.email = email;
+ }
+
+ public void setAddress(Address address) {
+ this.address = address;
+ }
}
diff --git a/src/seedu/addressbook/data/person/UniquePersonList.java b/src/seedu/addressbook/data/person/UniquePersonList.java
index c4848a1b4..32944b03a 100644
--- a/src/seedu/addressbook/data/person/UniquePersonList.java
+++ b/src/seedu/addressbook/data/person/UniquePersonList.java
@@ -93,6 +93,25 @@ public void add(Person toAdd) throws DuplicatePersonException {
}
internalList.add(toAdd);
}
+
+ /**
+ * Sorts the list by given field.
+ *
+ * @param field field for the address book to be sorted by
+ */
+ public void sort(String field) {
+
+ switch (field){
+ case "name":
+ Collections.sort(internalList, Person.Comparators.NAME); break;
+ case "phone":
+ Collections.sort(internalList, Person.Comparators.PHONE); break;
+ case "email":
+ Collections.sort(internalList, Person.Comparators.EMAIL); break;
+ case "address":
+ Collections.sort(internalList, Person.Comparators.ADDRESS); break;
+ }
+ }
/**
* Removes the equivalent person from the list.
@@ -131,4 +150,8 @@ public int hashCode() {
return internalList.hashCode();
}
+ public Object getList() {
+ return internalList;
+ }
+
}
diff --git a/src/seedu/addressbook/data/tag/Tag.java b/src/seedu/addressbook/data/tag/Tag.java
index 4e5f595dc..c89cbca42 100644
--- a/src/seedu/addressbook/data/tag/Tag.java
+++ b/src/seedu/addressbook/data/tag/Tag.java
@@ -49,5 +49,8 @@ public int hashCode() {
public String toString() {
return '[' + tagName + ']';
}
-
+
+ public String getName() {
+ return tagName;
+ }
}
diff --git a/src/seedu/addressbook/data/tag/UniqueTagList.java b/src/seedu/addressbook/data/tag/UniqueTagList.java
index 65f37e261..e88903959 100644
--- a/src/seedu/addressbook/data/tag/UniqueTagList.java
+++ b/src/seedu/addressbook/data/tag/UniqueTagList.java
@@ -164,4 +164,8 @@ public boolean equals(Object other) {
public int hashCode() {
return internalList.hashCode();
}
+
+ public List getList() {
+ return internalList;
+ }
}
diff --git a/src/seedu/addressbook/logic/Logic.java b/src/seedu/addressbook/logic/Logic.java
index 17afd61a0..b28634f68 100644
--- a/src/seedu/addressbook/logic/Logic.java
+++ b/src/seedu/addressbook/logic/Logic.java
@@ -76,7 +76,8 @@ public CommandResult execute(String userCommandText) throws Exception {
}
/**
- * Executes the command, updates storage, and returns the result.
+ * Executes the command and returns the result.
+ * If there is any contact deleted or added, data will be saved.
*
* @param command user command
* @return result of the command
@@ -85,7 +86,9 @@ public CommandResult execute(String userCommandText) throws Exception {
private CommandResult execute(Command command) throws Exception {
command.setData(addressBook, lastShownList);
CommandResult result = command.execute();
- storage.save(addressBook);
+ if (command.isMutating()) {
+ storage.save(addressBook);
+ }
return result;
}
diff --git a/src/seedu/addressbook/parser/Parser.java b/src/seedu/addressbook/parser/Parser.java
index 3a8305f28..cb3f14f98 100644
--- a/src/seedu/addressbook/parser/Parser.java
+++ b/src/seedu/addressbook/parser/Parser.java
@@ -7,7 +7,7 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import static seedu.addressbook.common.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.addressbook.common.Messages.*;
/**
* Parses user input.
@@ -25,7 +25,22 @@ public class Parser {
+ " (?p?)e/(?[^/]+)"
+ " (?p?)a/(?[^/]+)"
+ "(?(?: t/[^/]+)*)"); // variable number of tags
-
+
+ public static final ArrayList VALID_FIELDS = new ArrayList() {{
+ add("name");
+ add("phone");
+ add("email");
+ add("address");
+ add("");
+ }};
+
+ public static final Pattern EDIT_PERSON_DATA_ARGS_FORMAT = // '/' forward slashes are reserved for delimiter prefixes
+ Pattern.compile("(?.+)"
+ + " n/(?[^/]+)"
+ + " (?p?)p/(?[^/]+)"
+ + " (?p?)e/(?[^/]+)"
+ + " (?p?)a/(?[^/]+)"
+ + "(?(?: t/[^/]+)*)"); // variable number of tags
/**
* Signals that the user input could not be parsed.
@@ -61,6 +76,12 @@ public Command parseCommand(String userInput) {
case AddCommand.COMMAND_WORD:
return prepareAdd(arguments);
+
+ case SortCommand.COMMAND_WORD:
+ return prepareSort(arguments);
+
+ case EditCommand.COMMAND_WORD:
+ return prepareEdit(arguments);
case DeleteCommand.COMMAND_WORD:
return prepareDelete(arguments);
@@ -80,6 +101,9 @@ public Command parseCommand(String userInput) {
case ViewAllCommand.COMMAND_WORD:
return prepareViewAll(arguments);
+ case ViewAllTags.COMMAND_WORD:
+ return new ViewAllTags();
+
case ExitCommand.COMMAND_WORD:
return new ExitCommand();
@@ -89,6 +113,39 @@ public Command parseCommand(String userInput) {
}
}
+
+ /**
+ * 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 = EDIT_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 {
+ return new EditCommand(Integer.parseInt(matcher.group("targetIndex")),
+ matcher.group("name"),
+
+ 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());
+ }
+ }
+
/**
* Parses arguments in the context of the add person command.
*
@@ -121,6 +178,42 @@ private Command prepareAdd(String args){
}
}
+ /**
+ * Parses arguments in the context of the sort command.
+ *
+ * @param args full command args string
+ * @return the prepared command
+ */
+ private Command prepareSort(String args){
+
+ if (args.length() > 1){
+ args = args.substring(1);
+ }else{
+ args = "name";
+ }
+
+ if(!isValid(args)){
+ return new IncorrectCommand(String.format(MESSAGE_INVALID_FIELD, SortCommand.MESSAGE_USAGE));
+ }
+ try {
+ return new SortCommand(args);
+ } catch (IllegalValueException ive) {
+ return new IncorrectCommand(ive.getMessage());
+ }
+ }
+
+ private boolean isValid(String str){
+
+ for(String field : VALID_FIELDS){
+ if(str.equals(field)){
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+
/**
* Checks whether the private prefix of a contact detail in the add command's arguments string is present.
*/