diff --git a/README.md b/README.md index f1ca56ee..2a49ff20 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # AddressBook (Level 1) -* This is a CLI (Command Line Interface) Address Book application **written in procedural fashion**. -* It is a Java sample application intended for students learning Software Engineering while using Java as - the main programming language. -* It provides a **reasonably well-written** code example that is **significantly bigger** than what students - usually write in data structure modules. -* It can be used to achieve a number of beginner-level [learning outcomes](#learning-outcomes) without +* This is a CLI (Command Line Interface) Address Book application **written in procedural fashion**. +* It is a Java sample application intended for students learning Software Engineering while using Java as + the main programming language. +* It provides a **reasonably well-written** code example that is **significantly bigger** than what students + usually write in data structure modules. +* It can be used to achieve a number of beginner-level [learning outcomes](#learning-outcomes) without running into the complications of OOP or GUI programmings. **Table of Contents** @@ -35,7 +35,7 @@ ----------------------------------------------------------------------------------------------------- # 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](#setting-up) section to learn how to set up the project. ## Starting the program @@ -59,9 +59,9 @@ Please refer to the [Setting up](#setting-up) section to learn how to set up the ## List of commands #### Viewing help: `help` -Format: `help` +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 @@ -69,43 +69,50 @@ Format: `add NAME p/PHONE_NUMBER e/EMAIL` > Words in `UPPER_CASE` are the parameters
Phone number and email can be in any order but the name must come first. -Examples: +Examples: * `add John Doe p/98765432 e/johnd@gmail.com` * `add Betsy Crowe e/bencrowe@gmail.com p/1234567 ` #### Listing all persons: `list` -> Shows a list of persons, as an indexed list, in the order they were added to the address book, +> Shows a list of persons, as an indexed list, in the order they were added to the address book, oldest first. Format: `list` +#### Sorting all persons by name: `sort` + +> Sorts the list of persons by name, and displays persons +in ascending or descending lexicographical order depending on user input. + +Format: `sort [ASC/DESC]` + #### Finding a person by keyword `find` > Finds persons that match 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` > Returns Any person having names `Betsy`, `Tim`, or `John` #### Deleting a person: `delete` Format: `delete INDEX` -> Deletes the person at the specified `INDEX`. +> Deletes the person at the specified `INDEX`. The index refers to the index numbers 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. @@ -116,8 +123,8 @@ Format: `clear` #### Exiting the program: `exit` Format: `exit` -#### Saving the data -Address book data are saved in the hard disk automatically after any command that changes 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. #### Changing the save location @@ -144,7 +151,7 @@ Example: **Prerequisites** -* JDK 8 or later +* JDK 8 or later * IntelliJ IDE **Importing the project into IntelliJ** @@ -181,14 +188,14 @@ In-memory data are held in a `ArrayList` where each `String[]` object 1. Open a DOS window in the `test` folder 2. Run the `runtests.bat` script -3. If the script reports that there is no difference between `actual.txt` and `expected.txt`, +3. If the script reports that there is no difference between `actual.txt` and `expected.txt`, the test has passed. **Mac/Unix/Linux** 1. Open a terminal window in the `test` folder 2. Run the `runtests.sh` script -3. If the script reports that there is no difference between `actual.txt` and `expected.txt`, +3. If the script reports that there is no difference between `actual.txt` and `expected.txt`, the test has passed. **Troubleshooting test failures** @@ -211,7 +218,7 @@ corresponding exercises. * Learn [how to set up a project in IntelliJ](https://se-edu.github.io/se-book/intellij/projectSetup/). -#### Exercise: Setup project in IntelliJ +#### Exercise: Setup project in IntelliJ Part A: * Create a new project in IntelliJ and write a small HelloWorld program. @@ -226,10 +233,10 @@ Part B: ## Navigate code efficiently `[LO-CodeNavigation]` -The `AddressBook.java` code is rather long, which makes it cumbersome to navigate by scrolling alone. -Navigating code using IDE shortcuts is a more efficient option. +The `AddressBook.java` code is rather long, which makes it cumbersome to navigate by scrolling alone. +Navigating code using IDE shortcuts is a more efficient option. For example, CTRL+B will navigate to the definition of the method/field at the cursor. - + Learn [basic IntelliJ code navigation features](https://se-edu.github.io/se-book/intellij/codeNavigation/). #### Exercise: Learn to navigate code using shortcuts @@ -244,7 +251,7 @@ Learn [basic IntelliJ debugging features](https://se-edu.github.io/se-book/intel Prerequisite: `[LO-IdeSetup]` -Demonstrate your debugging skills using the AddressBook code. +Demonstrate your debugging skills using the AddressBook code. Here are some things you can do in your demonstration: @@ -262,7 +269,7 @@ Learn [how to automate testing of CLIs](https://se-edu.github.io/se-book/testing * Run the tests as explained in the [Testing](#testing) section. * Examine the test script to understand how the script works. -* Add a few more tests to the `input.txt`. Run the tests. It should fail.
+* Add a few more tests to the `input.txt`. Run the tests. It should fail.
Modify `expected.txt` to make the tests pass again. * Edit the `AddressBook.java` to modify the behavior slightly and modify tests to match. @@ -314,7 +321,7 @@ Compare the code with and without the varargs feature. ## Follow a coding standard `[LO-CodingStandard]` -The given code follows the [coding standard][coding-standard] +The given code follows the [coding standard][coding-standard] for the most part. This learning outcome is covered by the exercise in `[LO-Refactor]`. @@ -376,12 +383,12 @@ A well-abstracted method should do only one thing. While it is short, there are some problems with how it has been abstracted. 1. It contains the term `sign` which is not a term used by the AddressBook vocabulary. - + > **A method adds a new term to the vocabulary used to express the solution**. > Therefore, it is not good when a method name contains terms that are not strictly necessary to express the > solution (e.g. there is another term already used to express the same thing) or not in tune with the solution > (e.g. it does not go well with the other terms already used). - + 2. Its implementation is not doing exactly what is advertised by the method name and the header comment. For example, the code does not remove only prefixes; it removes `sign` from anywhere in the `s`. 3. The method can be _more general_ and _more independent_ from the rest of the code. For example, @@ -394,7 +401,7 @@ While it is short, there are some problems with how it has been abstracted. */ private static String removePrefix(String fullString, String prefix) { ... } ``` - + If needed, a more AddressBook-specific method that works on parameter strings only can be defined. In that case, that method can make use of the more general method suggested above. @@ -432,11 +439,11 @@ readability. #### Exercise 2: Refactor the code to make it worse! -Sometimes, going in the wrong direction can be a good learning experience too. +Sometimes, going in the wrong direction can be a good learning experience too. In this exercise, we explore how low code qualities can go. * Refactor the code to make the code as bad as possible.
- i.e. How bad can you make it without breaking the functionality while still making it look like it was written by a + i.e. How bad can you make it without breaking the functionality while still making it look like it was written by a programmer (but a very bad programmer :-)). * In particular, inlining methods can worsen the code quality fast. @@ -468,4 +475,4 @@ The full list of contributors for se-edu can be found [here](https://se-edu.gith * If you would like to contact us, refer to [our website](https://se-edu.github.io/#contact). [coding-standard]: https://github.com/oss-generic/process/blob/master/codingStandards/CodingStandard-Java.md "Java Coding Standard" -[code-quality]: https://se-edu.github.io/se-book/codeQuality/ "Code Quality Best Practices" \ No newline at end of file +[code-quality]: https://se-edu.github.io/se-book/codeQuality/ "Code Quality Best Practices" diff --git a/src/seedu/addressbook/AddressBook.java b/src/seedu/addressbook/AddressBook.java index 5a158b67..9b394b32 100644 --- a/src/seedu/addressbook/AddressBook.java +++ b/src/seedu/addressbook/AddressBook.java @@ -14,14 +14,7 @@ import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.Optional; -import java.util.Scanner; -import java.util.Set; +import java.util.*; /* * NOTE : ============================================================= @@ -105,6 +98,11 @@ public class AddressBook { + PERSON_DATA_PREFIX_EMAIL + "EMAIL"; private static final String COMMAND_ADD_EXAMPLE = COMMAND_ADD_WORD + " John Doe p/98765432 e/johnd@gmail.com"; + private static final String COMMAND_SORT_WORD = "sort"; + private static final String COMMAND_SORT_DESC = "Sorts persons list by asc/desc name order"; + private static final String COMMAND_SORT_PARAMETER = "ASC/DESC"; + private static final String COMMAND_SORT_EXAMPLE = COMMAND_SORT_WORD + " ASC/DESC"; + private static final String COMMAND_FIND_WORD = "find"; private static final String COMMAND_FIND_DESC = "Finds all persons whose names contain any of the specified " + "keywords (case-sensitive) and displays them as a list with index numbers."; @@ -141,9 +139,15 @@ public class AddressBook { * used by the internal String[] storage format. * For example, a person's name is stored as the 0th element in the array. */ - private static final int PERSON_DATA_INDEX_NAME = 0; - private static final int PERSON_DATA_INDEX_PHONE = 1; - private static final int PERSON_DATA_INDEX_EMAIL = 2; + +// private static final String PERSON_PROPERTY_NAME = "name"; +// private static final String PERSON_PROPERTY_EMAIL = "email"; +// private static final String PERSON_PROPERTY_PHONE = "phone"; + + // Replaces PERSON_PROPERTY_ with enums variant + private enum PersonProperty { + NAME, EMAIL, PHONE + }; /** * The number of data elements for a single person. @@ -181,14 +185,14 @@ public class AddressBook { /** * List of all persons in the address book. */ - private static final ArrayList ALL_PERSONS = new ArrayList<>(); + private static final ArrayList> ALL_PERSONS = new ArrayList<>(); /** * Stores the most recent list of persons shown to the user as a result of a user command. * This is a subset of the full list. Deleting persons in the pull list does not delete * those persons from this list. */ - private static ArrayList latestPersonListingView = getAllPersonsInAddressBook(); // initial view is of all + private static ArrayList> latestPersonListingView = getAllPersonsInAddressBook(); // initial view is of all /** * The path to the file used for storing person data. @@ -369,22 +373,24 @@ private static String executeCommand(String userInputString) { final String commandType = commandTypeAndParams[0]; final String commandArgs = commandTypeAndParams[1]; switch (commandType) { - case COMMAND_ADD_WORD: - return executeAddPerson(commandArgs); - case COMMAND_FIND_WORD: - return executeFindPersons(commandArgs); - case COMMAND_LIST_WORD: - return executeListAllPersonsInAddressBook(); - case COMMAND_DELETE_WORD: - return executeDeletePerson(commandArgs); - case COMMAND_CLEAR_WORD: - return executeClearAddressBook(); - case COMMAND_HELP_WORD: - return getUsageInfoForAllCommands(); - case COMMAND_EXIT_WORD: - executeExitProgramRequest(); - default: - return getMessageForInvalidCommandInput(commandType, getUsageInfoForAllCommands()); + case COMMAND_ADD_WORD: + return executeAddPerson(commandArgs); + case COMMAND_SORT_WORD: + return executeSortPerson(commandArgs); + case COMMAND_FIND_WORD: + return executeFindPersons(commandArgs); + case COMMAND_LIST_WORD: + return executeListAllPersonsInAddressBook(); + case COMMAND_DELETE_WORD: + return executeDeletePerson(commandArgs); + case COMMAND_CLEAR_WORD: + return executeClearAddressBook(); + case COMMAND_HELP_WORD: + return getUsageInfoForAllCommands(); + case COMMAND_EXIT_WORD: + executeExitProgramRequest(); + default: + return getMessageForInvalidCommandInput(commandType, getUsageInfoForAllCommands()); } } @@ -417,7 +423,7 @@ private static String getMessageForInvalidCommandInput(String userCommand, Strin */ private static String executeAddPerson(String commandArgs) { // try decoding a person from the raw args - final Optional decodeResult = decodePersonFromString(commandArgs); + final Optional> decodeResult = decodePersonFromString(commandArgs); // checks if args are valid (decode result will not be present if the person is invalid) if (!decodeResult.isPresent()) { @@ -425,7 +431,7 @@ private static String executeAddPerson(String commandArgs) { } // add the person as specified - final String[] personToAdd = decodeResult.get(); + final HashMap personToAdd = decodeResult.get(); addPersonToAddressBook(personToAdd); return getMessageForSuccessfulAddPerson(personToAdd); } @@ -437,7 +443,7 @@ private static String executeAddPerson(String commandArgs) { * @param addedPerson person who was successfully added * @return successful add person feedback message */ - private static String getMessageForSuccessfulAddPerson(String[] addedPerson) { + private static String getMessageForSuccessfulAddPerson(HashMap addedPerson) { return String.format(MESSAGE_ADDED, getNameFromPerson(addedPerson), getPhoneFromPerson(addedPerson), getEmailFromPerson(addedPerson)); } @@ -451,7 +457,7 @@ private static String getMessageForSuccessfulAddPerson(String[] addedPerson) { */ private static String executeFindPersons(String commandArgs) { final Set keywords = extractKeywordsFromFindPersonArgs(commandArgs); - final ArrayList personsFound = getPersonsWithNameContainingAnyKeyword(keywords); + final ArrayList> personsFound = getPersonsWithNameContainingAnyKeyword(keywords); showToUser(personsFound); return getMessageForPersonsDisplayedSummary(personsFound); } @@ -462,7 +468,7 @@ private static String executeFindPersons(String commandArgs) { * @param personsDisplayed used to generate summary * @return summary message for persons displayed */ - private static String getMessageForPersonsDisplayedSummary(ArrayList personsDisplayed) { + private static String getMessageForPersonsDisplayedSummary(ArrayList> personsDisplayed) { return String.format(MESSAGE_PERSONS_FOUND_OVERVIEW, personsDisplayed.size()); } @@ -482,9 +488,9 @@ private static Set extractKeywordsFromFindPersonArgs(String findPersonCo * @param keywords for searching * @return list of persons in full model with name containing some of the keywords */ - private static ArrayList getPersonsWithNameContainingAnyKeyword(Collection keywords) { - final ArrayList matchedPersons = new ArrayList<>(); - for (String[] person : getAllPersonsInAddressBook()) { + private static ArrayList> getPersonsWithNameContainingAnyKeyword(Collection keywords) { + final ArrayList> matchedPersons = new ArrayList<>(); + for (HashMap person : getAllPersonsInAddressBook()) { final Set wordsInName = new HashSet<>(splitByWhitespace(getNameFromPerson(person))); if (!Collections.disjoint(wordsInName, keywords)) { matchedPersons.add(person); @@ -507,11 +513,43 @@ private static String executeDeletePerson(String commandArgs) { if (!isDisplayIndexValidForLastPersonListingView(targetVisibleIndex)) { return MESSAGE_INVALID_PERSON_DISPLAYED_INDEX; } - final String[] targetInModel = getPersonByLastVisibleIndex(targetVisibleIndex); + final HashMap targetInModel = getPersonByLastVisibleIndex(targetVisibleIndex); return deletePersonFromAddressBook(targetInModel) ? getMessageForSuccessfulDelete(targetInModel) // success : MESSAGE_PERSON_NOT_IN_ADDRESSBOOK; // not found } + /** + * Sorts entries by ascending or descending order by NAME + * + * @param commandArgs full command args string from the user + * @return feedback display message for the operation result + */ + private static String executeSortPerson(String commandArgs) { + ArrayList> targetPersonList = ALL_PERSONS; + + ArrayList personNames = new ArrayList (); + HashMap> bruteHash = new HashMap> (); + for (HashMap person: targetPersonList) { + personNames.add(person.get(PersonProperty.NAME)); + bruteHash.put(person.get(PersonProperty.NAME), person); + } + + if (commandArgs.equals("ASC")) { + Collections.sort(personNames); + } else if (commandArgs.equals("DESC")) { + Collections.sort(personNames, Collections.reverseOrder()); + } else { + return getMessageForInvalidCommandInput(COMMAND_SORT_WORD, getUsageInfoForSortCommand()); + } + + targetPersonList.clear(); + for (String name: personNames) { + addPersonToAddressBook(bruteHash.get(name)); + } + + return getMessageForPersonsDisplayedSummary(targetPersonList); + } + /** * Checks validity of delete person argument string's format. * @@ -554,7 +592,7 @@ private static boolean isDisplayIndexValidForLastPersonListingView(int index) { * @param deletedPerson successfully deleted * @return successful delete person feedback message */ - private static String getMessageForSuccessfulDelete(String[] deletedPerson) { + private static String getMessageForSuccessfulDelete(HashMap deletedPerson) { return String.format(MESSAGE_DELETE_PERSON_SUCCESS, getMessageForFormattedPersonData(deletedPerson)); } @@ -574,7 +612,7 @@ private static String executeClearAddressBook() { * @return feedback display message for the operation result */ private static String executeListAllPersonsInAddressBook() { - ArrayList toBeDisplayed = getAllPersonsInAddressBook(); + ArrayList> toBeDisplayed = getAllPersonsInAddressBook(); showToUser(toBeDisplayed); return getMessageForPersonsDisplayedSummary(toBeDisplayed); } @@ -624,12 +662,24 @@ private static void showToUser(String... message) { } } + /* + private static void showToUser(String[] message) { + for (String m : message) { + System.out.println(LINE_PREFIX + m); + } + } + + private static void showToUser(String message) { + System.out.println(LINE_PREFIX + message); + } + */ + /** * Shows the list of persons to the user. * The list will be indexed, starting from 1. * */ - private static void showToUser(ArrayList persons) { + private static void showToUser(ArrayList> persons) { String listAsString = getDisplayString(persons); showToUser(listAsString); updateLatestViewedPersonListing(persons); @@ -638,10 +688,10 @@ private static void showToUser(ArrayList persons) { /** * Returns the display string representation of the list of persons. */ - private static String getDisplayString(ArrayList persons) { + private static String getDisplayString(ArrayList> persons) { final StringBuilder messageAccumulator = new StringBuilder(); for (int i = 0; i < persons.size(); i++) { - final String[] person = persons.get(i); + final HashMap person = persons.get(i); final int displayIndex = i + DISPLAYED_INDEX_OFFSET; messageAccumulator.append('\t') .append(getIndexedPersonListElementMessage(displayIndex, person)) @@ -657,7 +707,7 @@ private static String getDisplayString(ArrayList persons) { * @param person to show * @return formatted listing message with index */ - private static String getIndexedPersonListElementMessage(int visibleIndex, String[] person) { + private static String getIndexedPersonListElementMessage(int visibleIndex, HashMap person) { return String.format(MESSAGE_DISPLAY_LIST_ELEMENT_INDEX, visibleIndex) + getMessageForFormattedPersonData(person); } @@ -667,7 +717,7 @@ private static String getIndexedPersonListElementMessage(int visibleIndex, Strin * @param person to show * @return formatted message showing internal state */ - private static String getMessageForFormattedPersonData(String[] person) { + private static String getMessageForFormattedPersonData(HashMap person) { return String.format(MESSAGE_DISPLAY_PERSON_DATA, getNameFromPerson(person), getPhoneFromPerson(person), getEmailFromPerson(person)); } @@ -677,7 +727,7 @@ private static String getMessageForFormattedPersonData(String[] person) { * * @param newListing the new listing of persons */ - private static void updateLatestViewedPersonListing(ArrayList newListing) { + private static void updateLatestViewedPersonListing(ArrayList> newListing) { // clone to insulate from future changes to arg list latestPersonListingView = new ArrayList<>(newListing); } @@ -688,7 +738,7 @@ private static void updateLatestViewedPersonListing(ArrayList newListi * @param lastVisibleIndex displayed index from last shown person listing * @return the actual person object in the last shown person listing */ - private static String[] getPersonByLastVisibleIndex(int lastVisibleIndex) { + private static HashMap getPersonByLastVisibleIndex(int lastVisibleIndex) { return latestPersonListingView.get(lastVisibleIndex - DISPLAYED_INDEX_OFFSET); } @@ -728,8 +778,8 @@ private static void createFileIfMissing(String filePath) { * @param filePath file to load from * @return the list of decoded persons */ - private static ArrayList loadPersonsFromFile(String filePath) { - final Optional> successfullyDecoded = decodePersonsFromStrings(getLinesInFile(filePath)); + private static ArrayList> loadPersonsFromFile(String filePath) { + final Optional>> successfullyDecoded = decodePersonsFromStrings(getLinesInFile(filePath)); if (!successfullyDecoded.isPresent()) { showToUser(MESSAGE_INVALID_STORAGE_FILE_CONTENT); exitProgram(); @@ -760,7 +810,7 @@ private static ArrayList getLinesInFile(String filePath) { * * @param filePath file for saving */ - private static void savePersonsToFile(ArrayList persons, String filePath) { + private static void savePersonsToFile(ArrayList> persons, String filePath) { final ArrayList linesToWrite = encodePersonsToStrings(persons); try { Files.write(Paths.get(storageFilePath), linesToWrite); @@ -782,7 +832,7 @@ private static void savePersonsToFile(ArrayList persons, String filePa * * @param person to add */ - private static void addPersonToAddressBook(String[] person) { + private static void addPersonToAddressBook(HashMap person) { ALL_PERSONS.add(person); savePersonsToFile(getAllPersonsInAddressBook(), storageFilePath); } @@ -793,7 +843,7 @@ private static void addPersonToAddressBook(String[] person) { * @param exactPerson the actual person inside the address book (exactPerson == the person to delete in the full list) * @return true if the given person was found and deleted in the model */ - private static boolean deletePersonFromAddressBook(String[] exactPerson) { + private static boolean deletePersonFromAddressBook(HashMap exactPerson) { final boolean changed = ALL_PERSONS.remove(exactPerson); if (changed) { savePersonsToFile(getAllPersonsInAddressBook(), storageFilePath); @@ -804,7 +854,7 @@ private static boolean deletePersonFromAddressBook(String[] exactPerson) { /** * Returns all persons in the address book */ - private static ArrayList getAllPersonsInAddressBook() { + private static ArrayList> getAllPersonsInAddressBook() { return ALL_PERSONS; } @@ -821,7 +871,7 @@ private static void clearAddressBook() { * * @param persons list of persons to initialise the model with */ - private static void initialiseAddressBookModel(ArrayList persons) { + private static void initialiseAddressBookModel(ArrayList> persons) { ALL_PERSONS.clear(); ALL_PERSONS.addAll(persons); } @@ -838,8 +888,8 @@ private static void initialiseAddressBookModel(ArrayList persons) { * * @param person whose name you want */ - private static String getNameFromPerson(String[] person) { - return person[PERSON_DATA_INDEX_NAME]; + private static String getNameFromPerson(HashMap person) { + return person.get(PersonProperty.NAME); } /** @@ -847,8 +897,8 @@ private static String getNameFromPerson(String[] person) { * * @param person whose phone number you want */ - private static String getPhoneFromPerson(String[] person) { - return person[PERSON_DATA_INDEX_PHONE]; + private static String getPhoneFromPerson(HashMap person) { + return person.get(PersonProperty.PHONE); } /** @@ -856,8 +906,8 @@ private static String getPhoneFromPerson(String[] person) { * * @param person whose email you want */ - private static String getEmailFromPerson(String[] person) { - return person[PERSON_DATA_INDEX_EMAIL]; + private static String getEmailFromPerson(HashMap person) { + return person.get(PersonProperty.EMAIL); } /** @@ -868,11 +918,11 @@ private static String getEmailFromPerson(String[] person) { * @param email without data prefix * @return constructed person */ - private static String[] makePersonFromData(String name, String phone, String email) { - final String[] person = new String[PERSON_DATA_COUNT]; - person[PERSON_DATA_INDEX_NAME] = name; - person[PERSON_DATA_INDEX_PHONE] = phone; - person[PERSON_DATA_INDEX_EMAIL] = email; + private static HashMap makePersonFromData(String name, String phone, String email) { + final HashMap person = new HashMap (); + person.put(PersonProperty.NAME, name); + person.put(PersonProperty.EMAIL, email); + person.put(PersonProperty.PHONE, phone); return person; } @@ -882,7 +932,7 @@ private static String[] makePersonFromData(String name, String phone, String ema * @param person to be encoded * @return encoded string */ - private static String encodePersonToString(String[] person) { + private static String encodePersonToString(HashMap person) { return String.format(PERSON_STRING_REPRESENTATION, getNameFromPerson(person), getPhoneFromPerson(person), getEmailFromPerson(person)); } @@ -893,9 +943,9 @@ private static String encodePersonToString(String[] person) { * @param persons to be encoded * @return encoded strings */ - private static ArrayList encodePersonsToStrings(ArrayList persons) { + private static ArrayList encodePersonsToStrings(ArrayList> persons) { final ArrayList encoded = new ArrayList<>(); - for (String[] person : persons) { + for (HashMap person : persons) { encoded.add(encodePersonToString(person)); } return encoded; @@ -915,18 +965,18 @@ private static ArrayList encodePersonsToStrings(ArrayList pers * @return if cannot decode: empty Optional * else: Optional containing decoded person */ - private static Optional decodePersonFromString(String encoded) { + private static Optional> decodePersonFromString(String encoded) { // check that we can extract the parts of a person from the encoded string - if (!isPersonDataExtractableFrom(encoded)) { - return Optional.empty(); + if (isPersonDataExtractableFrom(encoded)) { + final HashMap decodedPerson = makePersonFromData( + extractNameFromPersonString(encoded), + extractPhoneFromPersonString(encoded), + extractEmailFromPersonString(encoded) + ); + // check that the constructed person is valid + return isPersonDataValid(decodedPerson) ? Optional.of(decodedPerson) : Optional.empty(); } - final String[] decodedPerson = makePersonFromData( - extractNameFromPersonString(encoded), - extractPhoneFromPersonString(encoded), - extractEmailFromPersonString(encoded) - ); - // check that the constructed person is valid - return isPersonDataValid(decodedPerson) ? Optional.of(decodedPerson) : Optional.empty(); + return Optional.empty(); } /** @@ -936,10 +986,10 @@ private static Optional decodePersonFromString(String encoded) { * @return if cannot decode any: empty Optional * else: Optional containing decoded persons */ - private static Optional> decodePersonsFromStrings(ArrayList encodedPersons) { - final ArrayList decodedPersons = new ArrayList<>(); + private static Optional>> decodePersonsFromStrings(ArrayList encodedPersons) { + final ArrayList> decodedPersons = new ArrayList<>(); for (String encodedPerson : encodedPersons) { - final Optional decodedPerson = decodePersonFromString(encodedPerson); + final Optional> decodedPerson = decodePersonFromString(encodedPerson); if (!decodedPerson.isPresent()) { return Optional.empty(); } @@ -957,10 +1007,12 @@ private static Optional> decodePersonsFromStrings(ArrayList< private static boolean isPersonDataExtractableFrom(String personData) { final String matchAnyPersonDataPrefix = PERSON_DATA_PREFIX_PHONE + '|' + PERSON_DATA_PREFIX_EMAIL; final String[] splitArgs = personData.trim().split(matchAnyPersonDataPrefix); - return splitArgs.length == 3 // 3 arguments - && !splitArgs[0].isEmpty() // non-empty arguments - && !splitArgs[1].isEmpty() - && !splitArgs[2].isEmpty(); + for (String arg: splitArgs) { + if (arg.isEmpty()) { + return false; + } + } + return splitArgs.length == PERSON_DATA_COUNT; } /** @@ -1028,10 +1080,10 @@ private static String extractEmailFromPersonString(String encoded) { * * @param person String array representing the person (used in internal data) */ - private static boolean isPersonDataValid(String[] person) { - return isPersonNameValid(person[PERSON_DATA_INDEX_NAME]) - && isPersonPhoneValid(person[PERSON_DATA_INDEX_PHONE]) - && isPersonEmailValid(person[PERSON_DATA_INDEX_EMAIL]); + private static boolean isPersonDataValid(HashMap person) { + return isPersonNameValid(person.get(PersonProperty.NAME)) + && isPersonPhoneValid(person.get(PersonProperty.PHONE)) + && isPersonEmailValid(person.get(PersonProperty.EMAIL)); } /* @@ -1083,6 +1135,7 @@ private static boolean isPersonEmailValid(String email) { /** Returns usage info for all commands */ private static String getUsageInfoForAllCommands() { return getUsageInfoForAddCommand() + LS + + getUsageInfoForSortCommand() + LS + getUsageInfoForFindCommand() + LS + getUsageInfoForViewCommand() + LS + getUsageInfoForDeleteCommand() + LS @@ -1098,6 +1151,13 @@ private static String getUsageInfoForAddCommand() { + String.format(MESSAGE_COMMAND_HELP_EXAMPLE, COMMAND_ADD_EXAMPLE) + LS; } + /** Returns the string for showing 'sort' command usage instruction */ + private static String getUsageInfoForSortCommand() { + return String.format(MESSAGE_COMMAND_HELP, COMMAND_SORT_WORD, COMMAND_SORT_DESC) + LS + + String.format(MESSAGE_COMMAND_HELP_PARAMETERS, COMMAND_SORT_PARAMETER) + LS + + String.format(MESSAGE_COMMAND_HELP_EXAMPLE, COMMAND_SORT_EXAMPLE) + LS; + } + /** Returns the string for showing 'find' command usage instruction */ private static String getUsageInfoForFindCommand() { return String.format(MESSAGE_COMMAND_HELP, COMMAND_FIND_WORD, COMMAND_FIND_DESC) + LS