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 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. */