+/*\r
+ * Copyright (C) 2015 Roland Haeder\r
+ *\r
+ * This program is free software: you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation, either version 3 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.\r
+ */\r
+package org.mxchange.addressbook.client.console;\r
+\r
+import java.text.MessageFormat;\r
+import java.util.Arrays;\r
+import java.util.Scanner;\r
+import org.mxchange.addressbook.UnhandledUserChoiceException;\r
+import org.mxchange.addressbook.application.AddressbookApplication;\r
+import org.mxchange.addressbook.application.Application;\r
+import org.mxchange.addressbook.client.BaseClient;\r
+import org.mxchange.addressbook.client.Client;\r
+import org.mxchange.addressbook.contact.Contact;\r
+import org.mxchange.addressbook.contact.user.UserContact;\r
+import org.mxchange.addressbook.menu.Menu;\r
+import org.mxchange.addressbook.menu.MenuTools;\r
+import org.mxchange.addressbook.menu.console.ConsoleMenu;\r
+import org.mxchange.addressbook.menu.item.SelectableMenuItem;\r
+import org.mxchange.addressbook.menu.item.console.ConsoleMenuItem;\r
+\r
+/**\r
+ * A client for the console\r
+ *\r
+ * @author Roland Haeder\r
+ */\r
+public class ConsoleClient extends BaseClient implements Client {\r
+ /**\r
+ * Scanner instance for reading data from console input\r
+ */\r
+ private final Scanner scanner;\r
+\r
+ /**\r
+ * Parameterless constructor\r
+ * @param application An instance of an Application class\r
+ */\r
+ public ConsoleClient (final Application application) {\r
+ super();\r
+\r
+ // Set application instance\r
+ this.setApplication(application);\r
+\r
+ // Init scanner instance\r
+ this.scanner = new Scanner(System.in);\r
+ }\r
+\r
+ /**\r
+ * Displays a textual address "box" of given contact\r
+ *\r
+ * @param contact Contact to show address for\r
+ */\r
+ @Override\r
+ public void displayAddressBox (final Contact contact) {\r
+ // Simple display ...\r
+ this.outputMessage(MessageFormat.format("Strasse, PLZ Ort, Land: {0}\n{1} {2}\n{3}", contact.getStreet(), contact.getZipCode(), contact.getCity(), contact.getCountryCode()));\r
+ }\r
+\r
+ /**\r
+ * Displays a textual name "box" of given contact\r
+ *\r
+ * @param contact Contact to show name for\r
+ */\r
+ @Override\r
+ public void displayNameBox (final Contact contact) {\r
+ // Get translated gender as the user may want to see "Mr.", "Mrs."\r
+ String gender = contact.getTranslatedGender();\r
+\r
+ // Get company name\r
+ String companyName = contact.getCompanyName();\r
+\r
+ // If it is empty/null, then assume private contact\r
+ if ((companyName == null) || (companyName.isEmpty())) {\r
+ // Now put all together: gender, surname, family name\r
+ // @todo Use mask\r
+ this.outputMessage(MessageFormat.format("Anrede, Vorname, Name: {0} {1} {2}", gender, contact.getSurname(), contact.getFamilyName()));\r
+ } else {\r
+ // Company contact\r
+ this.outputMessage(MessageFormat.format("Firma: {0}\nAnsprechpartner: {1} {2} {3}", companyName, gender, contact.getSurname(), contact.getFamilyName()));\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Displays a textual other data "box" of given contact\r
+ *\r
+ * @param contact Contact to show other data for\r
+ */\r
+ @Override\r
+ public void displayOtherDataBox (final Contact contact) {\r
+ // Cellphone and such ...\r
+ this.outputMessage(MessageFormat.format("Telefonnumer: {0}\nFaxnummer: {1}\nHandy: {2}\nKommentar:\n{3}", contact.getPhoneNumber(), contact.getFaxNumber(), contact.getCellphoneNumber(), contact.getComment()));\r
+ }\r
+\r
+ @Override\r
+ public void doChangeOwnAddressData (final Contact contact) {\r
+ // Make sure it is own contact\r
+ if (!contact.isOwnContact()) {\r
+ // Not own contact\r
+ throw new IllegalArgumentException("Contact is not own data.");\r
+ }\r
+\r
+ // Own address data\r
+ String street = this.getContactManager().enterOwnStreet();\r
+\r
+ // Get zip code\r
+ int zipCode = this.getContactManager().enterOwnZipCode();\r
+\r
+ // Get city name\r
+ String city = this.getContactManager().enterOwnCity();\r
+\r
+ // Get country code\r
+ String countryCode = this.getContactManager().enterOwnCountryCode();\r
+\r
+ // Update address data\r
+ contact.updateAddressData(street, zipCode, city, countryCode);\r
+ }\r
+\r
+ @Override\r
+ public void doChangeOwnNameData (final Contact contact) {\r
+ // Make sure it is own contact\r
+ if (!contact.isOwnContact()) {\r
+ // Not own contact\r
+ throw new IllegalArgumentException("Contact is not own data.");\r
+ }\r
+\r
+ // Gender:\r
+ char gender = this.getContactManager().enterOwnGender();\r
+\r
+ // Surname\r
+ String surname = this.getContactManager().enterOwnSurname();\r
+\r
+ // Family name\r
+ String familyName = this.getContactManager().enterOwnFamilyName();\r
+\r
+ // And company\r
+ String companyName = this.getContactManager().enterOwnCompanyName();\r
+\r
+ // Update contact instance\r
+ contact.updateNameData(gender, surname, familyName, companyName);\r
+ }\r
+\r
+ @Override\r
+ public void doChangeOwnOtherData (final Contact contact) {\r
+ // Make sure it is own contact\r
+ if (!contact.isOwnContact()) {\r
+ // Not own contact\r
+ throw new IllegalArgumentException("Contact is not own data.");\r
+ }\r
+\r
+ // Phone number\r
+ String phoneNumber = this.getContactManager().enterOwnPhoneNumber();\r
+\r
+ // Phone number\r
+ String cellNumber = this.getContactManager().enterOwnCellNumber();\r
+\r
+ // Fax number\r
+ String faxNumber = this.getContactManager().enterOwnFaxNumber();\r
+\r
+ // Email address\r
+ String email = this.getContactManager().enterOwnEmailAddress();\r
+\r
+ // Comment\r
+ String comment = this.getContactManager().enterOwnComment();\r
+\r
+ // Update contact instance\r
+ contact.updateOtherData(phoneNumber, cellNumber, faxNumber, email, null, comment);\r
+ }\r
+\r
+ @Override\r
+ public Contact doEnterOwnData () {\r
+ // First ask for gender\r
+ char gender = this.getContactManager().enterOwnGender();\r
+\r
+ // 2nd for surname\r
+ String surname = this.getContactManager().enterOwnSurname();\r
+ \r
+ // And 3rd for family name\r
+ String familyName = this.getContactManager().enterOwnFamilyName();\r
+\r
+ // Company name ...\r
+ String companyName = this.getContactManager().enterOwnCompanyName();\r
+\r
+ // Construct UserContact instance\r
+ Contact contact = new UserContact(gender, surname, familyName, companyName);\r
+\r
+ // And return object\r
+ return contact;\r
+ }\r
+\r
+ /**\r
+ * Shutdown this client\r
+ */\r
+ @Override\r
+ public void doShutdown () {\r
+ // Parent call\r
+ super.doShutdown();\r
+\r
+ // @TODO Add other shutdown stuff\r
+ }\r
+\r
+ @Override\r
+ public void doUserMenuChoice () throws UnhandledUserChoiceException {\r
+ // Get all access keys from menu\r
+ char[] accessKeys = MenuTools.getAccessKeysFromMenuMap(this.getMenus(), this.getCurrentMenu());\r
+\r
+ // Output textural message and ask for a char as input\r
+ char choice = this.enterChar(accessKeys, "Bitte Auswahl eingeben (0=Programm beenden): ");\r
+\r
+ // @TODO Rewrite this ugly switch() block\r
+ switch (choice) {\r
+ case '1': // Enter/add own data\r
+ this.getContactManager().doEnterOwnData();\r
+ break;\r
+ \r
+ case '2': // Change own data\r
+ this.getContactManager().changeOwnData();\r
+ break;\r
+ \r
+ case '3': // Add new addess\r
+ this.getContactManager().addOtherAddress();\r
+ break;\r
+ \r
+ case '4': // List contacts\r
+ this.getContactManager().listContacts();\r
+ break;\r
+ \r
+ case '5': // Search addresses\r
+ this.getContactManager().searchContacts();\r
+ break;\r
+\r
+ case '6': // Change other addess\r
+ this.getContactManager().changeOtherAddress();\r
+ break;\r
+ \r
+ case '7': // Delete other address\r
+ this.getContactManager().deleteOtherAddress();\r
+ break;\r
+\r
+ case '0': // Program exit\r
+ this.getApplication().doShutdown();\r
+ break;\r
+ \r
+ default:\r
+ // @TODO throw own exception\r
+ throw new UnhandledUserChoiceException(MessageFormat.format("Choice '{0}' not handled yet.", choice));\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Asks the the user to enter a single character which must match validChars\r
+ * @param validChars Valid chars that are accepted\r
+ * @param message Message to user\r
+ * @return Allowed character\r
+ */\r
+ @Override\r
+ public char enterChar (final char[] validChars, final String message) {\r
+ char input = 0;\r
+\r
+ // Sort array, else binarySearch() won't work\r
+ Arrays.sort(validChars);\r
+\r
+ // Keep asking until valid char has been entered\r
+ while (Arrays.binarySearch(validChars, input) < 0) {\r
+ // Output message\r
+ System.out.print(message);\r
+\r
+ // Read char\r
+ input = this.readChar();\r
+ }\r
+\r
+ // Return read char\r
+ return input;\r
+ }\r
+\r
+ /**\r
+ * Reads an integer (int) with a textural message from the user\r
+ * \r
+ * @param minimum Minimum allowed number\r
+ * @param maximum Maximum allowed number\r
+ * @param message Messager to display in console\r
+ * @return \r
+ */\r
+ @Override\r
+ public int enterInt (final int minimum, final int maximum, final String message) {\r
+ // Minimum should not be below zero\r
+ assert(minimum >= 0);\r
+ assert(maximum > minimum);\r
+\r
+ // Init input\r
+ int input = -1;\r
+\r
+ while ((input < minimum) || (input > maximum)) {\r
+ // Output message\r
+ System.out.print(message);\r
+\r
+ // Read integer from user\r
+ input = this.readInt();\r
+ }\r
+\r
+ // Return it\r
+ return input;\r
+ }\r
+\r
+ /**\r
+ * Reads a string of minimum and maximum length from the user\r
+ * \r
+ * @param minLength Minimum length of the string to read\r
+ * @param maxLength Maximum length of the string to read\r
+ * @param message Message to user\r
+ * @param allowEmpty Whether to allow empty string\r
+ * @return Entered string by user or null for empty strings\r
+ */\r
+ @Override\r
+ public String enterString (final int minLength, final int maxLength, final String message, final boolean allowEmpty) {\r
+ // Check on length, e.g. country codes are excactly 2 chars long\r
+ assert(maxLength >= minLength);\r
+\r
+ // Init input\r
+ String input = null;\r
+\r
+ // Check if it is to short or to long\r
+ while (((input == null) || ((input.length() < minLength) && (!allowEmpty))) || ((input.length() > 0) && (input.length() < minLength) && (allowEmpty)) || ((input instanceof String) && (input.length() > maxLength))) {\r
+ // Output message\r
+ System.out.print(message);\r
+\r
+ // Read line\r
+ input = this.readString();\r
+ }\r
+\r
+ // Return it\r
+ return input;\r
+ }\r
+\r
+ /**\r
+ * Returns a console menu item\r
+ * \r
+ * @param accessKey Key to access the menu\r
+ * @param text Text to show to user\r
+ * @return A SelectableMenuItem\r
+ * @todo Make sure the access key is unique\r
+ */\r
+ @Override\r
+ public SelectableMenuItem getMenuItem (final char accessKey, final String text) {\r
+ // Return a new console menu item\r
+ return new ConsoleMenuItem(accessKey,text);\r
+ }\r
+\r
+ /**\r
+ * Inizializes this client\r
+ */\r
+ @Override\r
+ public void initClient () {\r
+ // Init contact manager here\r
+ this.initContactManager();\r
+\r
+ // Fill menu map\r
+ this.fillMenuMap();\r
+ }\r
+\r
+ /**\r
+ * Displays textural message to the user\r
+ * @param message\r
+ */\r
+ @Override\r
+ public void outputMessage (final String message) {\r
+ System.out.println(message);\r
+ }\r
+\r
+ /**\r
+ * Shows textural menu on console\r
+ */\r
+ @Override\r
+ public void showCurrentMenu () {\r
+ this.showMenu(this.getCurrentMenu());\r
+ }\r
+\r
+ /**\r
+ * Shows given menu entry to user\r
+ * \r
+ * @param item Menu entry\r
+ */\r
+ @Override\r
+ public void showEntry (final SelectableMenuItem item) {\r
+ // Access key then text\r
+ this.outputMessage("[" + item.getAccessKey() + "] " + item.getText());\r
+ }\r
+\r
+ /**\r
+ * Shows a textural message to the user\r
+ */\r
+ @Override\r
+ public void showWelcome () {\r
+ this.outputMessage(MessageFormat.format("Welcome to {0}", AddressbookApplication.printableTitle()));\r
+ this.outputMessage("");\r
+ this.outputMessage("Copyright(c) 2015 by Roland Haeder, this is free software");\r
+ \r
+ // Debug message\r
+ this.getLogger().debug("Intro shown to user");\r
+ }\r
+\r
+ @Override\r
+ public void userChooseChangeContactData (final Contact contact) throws UnhandledUserChoiceException {\r
+ // Ask the user for editing [name], [a]ddress or [other] data\r
+ char choice = this.enterChar(new char[]{'n', 'a', 'o', 'x'}, "Welchen Daten möchten Sie ändern? (n=Namensdaten, a=Anschriftsdaten, o=Andere, x=Zurück zur Hauptauswahl) ");\r
+ \r
+ // @TODO Get rid of this ugly switch block, too\r
+ switch (choice) {\r
+ case 'n': // Name data\r
+ this.getContactManager().doChangeNameData(contact, this);\r
+ break;\r
+ \r
+ case 'a': // Address data\r
+ this.getContactManager().doChangeAddressData(contact, this);\r
+ break;\r
+ \r
+ case 'o': // Other data\r
+ this.getContactManager().doChangeOtherData(contact, this);\r
+ break;\r
+ \r
+ case 'x': // Exit this menu\r
+ // Ignored as it should go back\r
+ break;\r
+ \r
+ default:\r
+ // @TODO throw own exception\r
+ throw new UnhandledUserChoiceException(MessageFormat.format("Choice '{0}' not handled yet.", choice));\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Reads one character\r
+ * \r
+ * @return A single character\r
+ */\r
+ private char readChar () {\r
+ // Read line\r
+ String input = this.scanner.nextLine();\r
+\r
+ // This must be only one character\r
+ if (input.length() != 1) {\r
+ // Return zero\r
+ return 0;\r
+ }\r
+\r
+ // Get char from first (and only) position\r
+ return input.charAt(0);\r
+ }\r
+\r
+ /**\r
+ * Reads an integer (int) from user\r
+ * \r
+ * @return An integer number\r
+ */\r
+ private int readInt () {\r
+ // First read a string\r
+ String input = this.readString();\r
+\r
+ // Init number with invalid value\r
+ int num = -1;\r
+\r
+ // Parse number, this can be risky\r
+ try {\r
+ num = Integer.parseInt(input);\r
+ } catch (final NumberFormatException e) {\r
+ this.outputMessage("Bitte geben Sie nur Zahlen ein!");\r
+ this.getLogger().warn(MessageFormat.format("No numbers-only entered. input={0},message={1}", input, e.getMessage()));\r
+ }\r
+\r
+ // Return read number\r
+ return num;\r
+ }\r
+\r
+ /**\r
+ * Reads a string from a scanner until RETURN is pressed\r
+ * \r
+ * @return Read string from scanner\r
+ */\r
+ private String readString () {\r
+ return this.scanner.nextLine();\r
+ }\r
+\r
+ /**\r
+ * Fills menu map with menu entries\r
+ */\r
+ @Override\r
+ protected final void fillMenuMap () {\r
+ // Initialize first (main) menu\r
+ Menu menu = new ConsoleMenu("main", this);\r
+ \r
+ // Add it\r
+ this.getMenus().put("main", menu);\r
+ }\r
+}\r