2 * Copyright (C) 2015 Roland Haeder
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 package org.mxchange.addressbook.client.console;
19 import java.text.MessageFormat;
20 import java.util.Arrays;
21 import java.util.Scanner;
22 import org.mxchange.addressbook.application.AddressbookApplication;
23 import org.mxchange.addressbook.application.Application;
24 import org.mxchange.addressbook.client.BaseClient;
25 import org.mxchange.addressbook.client.Client;
26 import org.mxchange.addressbook.contact.Contact;
27 import org.mxchange.addressbook.contact.user.UserContact;
28 import org.mxchange.addressbook.exceptions.UnhandledUserChoiceException;
29 import org.mxchange.addressbook.menu.Menu;
30 import org.mxchange.addressbook.menu.MenuTools;
31 import org.mxchange.addressbook.menu.console.ConsoleMenu;
32 import org.mxchange.addressbook.menu.item.SelectableMenuItem;
33 import org.mxchange.addressbook.menu.item.console.ConsoleMenuItem;
36 * A client for the console
38 * @author Roland Haeder
40 public class ConsoleClient extends BaseClient implements Client {
43 * Scanner instance for reading data from console input
45 private final Scanner scanner;
48 * Parameterless constructor
50 * @param application An instance of an Application class
52 public ConsoleClient (final Application application) {
55 // Set application instance
56 this.setApplication(application);
58 // Init scanner instance
59 this.scanner = new Scanner(System.in);
63 * Displays a textual address "box" of given contact
65 * @param contact Contact to show address for
68 public void displayAddressBox (final Contact contact) {
70 this.outputMessage(MessageFormat.format("Strasse, PLZ Ort, Land: {0}\n{1} {2}\n{3}", contact.getStreet(), contact.getZipCode(), contact.getCity(), contact.getCountryCode()));
74 * Displays a textual name "box" of given contact
76 * @param contact Contact to show name for
79 public void displayNameBox (final Contact contact) {
80 // Get translated gender as the user may want to see "Mr.", "Mrs."
81 String gender = contact.getTranslatedGender();
84 String companyName = contact.getCompanyName();
86 // If it is empty/null, then assume private contact
87 if ((companyName == null) || (companyName.isEmpty())) {
88 // Now put all together: gender, surname, family name
90 this.outputMessage(MessageFormat.format("Anrede, Vorname, Name: {0} {1} {2}", gender, contact.getSurname(), contact.getFamilyName()));
93 this.outputMessage(MessageFormat.format("Firma: {0}\nAnsprechpartner: {1} {2} {3}", companyName, gender, contact.getSurname(), contact.getFamilyName()));
98 * Displays a textual other data "box" of given contact
100 * @param contact Contact to show other data for
103 public void displayOtherDataBox (final Contact contact) {
104 // Cellphone and such ...
105 this.outputMessage(MessageFormat.format("Telefonnumer: {0}\nFaxnummer: {1}\nHandy: {2}\nKommentar:\n{3}", contact.getPhoneNumber(), contact.getFaxNumber(), contact.getCellphoneNumber(), contact.getComment()));
109 public void doChangeOwnAddressData (final Contact contact) {
110 // Make sure it is own contact
111 if (!contact.isOwnContact()) {
113 throw new IllegalArgumentException("Contact is not own data.");
117 String street = this.getContactManager().enterOwnStreet();
120 int zipCode = this.getContactManager().enterOwnZipCode();
123 String city = this.getContactManager().enterOwnCity();
126 String countryCode = this.getContactManager().enterOwnCountryCode();
128 // Update address data
129 contact.updateAddressData(street, zipCode, city, countryCode);
133 public void doChangeOwnNameData (final Contact contact) {
134 // Make sure it is own contact
135 if (!contact.isOwnContact()) {
137 throw new IllegalArgumentException("Contact is not own data.");
141 char gender = this.getContactManager().enterOwnGender();
144 String surname = this.getContactManager().enterOwnSurname();
147 String familyName = this.getContactManager().enterOwnFamilyName();
150 String companyName = this.getContactManager().enterOwnCompanyName();
152 // Update contact instance
153 contact.updateNameData(gender, surname, familyName, companyName);
157 public void doChangeOwnOtherData (final Contact contact) {
158 // Make sure it is own contact
159 if (!contact.isOwnContact()) {
161 throw new IllegalArgumentException("Contact is not own data.");
165 String phoneNumber = this.getContactManager().enterOwnPhoneNumber();
168 String cellNumber = this.getContactManager().enterOwnCellNumber();
171 String faxNumber = this.getContactManager().enterOwnFaxNumber();
174 String email = this.getContactManager().enterOwnEmailAddress();
177 String comment = this.getContactManager().enterOwnComment();
179 // Update contact instance
180 contact.updateOtherData(phoneNumber, cellNumber, faxNumber, email, null, comment);
184 public Contact doEnterOwnData () {
185 // First ask for gender
186 char gender = this.getContactManager().enterOwnGender();
189 String surname = this.getContactManager().enterOwnSurname();
191 // And 3rd for family name
192 String familyName = this.getContactManager().enterOwnFamilyName();
195 String companyName = this.getContactManager().enterOwnCompanyName();
197 // Construct UserContact instance
198 Contact contact = new UserContact(gender, surname, familyName, companyName);
205 * Shutdown this client
208 public void doShutdown () {
212 // @TODO Add other shutdown stuff
216 public void doUserMenuChoice () throws UnhandledUserChoiceException {
217 // Get all access keys from menu
218 char[] accessKeys = MenuTools.getAccessKeysFromMenuMap(this.getMenus(), this.getCurrentMenu());
220 // Output textural message and ask for a char as input
221 char choice = this.enterChar(accessKeys, "Bitte Auswahl eingeben (0=Programm beenden): ");
223 // @TODO Rewrite this ugly switch() block
225 case '1': // Enter/add own data
226 this.getContactManager().doEnterOwnData();
229 case '2': // Change own data
230 this.getContactManager().doChangeOwnData();
233 case '3': // Add new addess
234 this.getContactManager().doAddOtherAddress();
237 case '4': // List contacts
238 this.getContactManager().doListContacts();
241 case '5': // Search addresses
242 this.getContactManager().doSearchContacts();
245 case '6': // Change other addess
246 this.getContactManager().doChangeOtherAddress();
249 case '7': // Delete other address
250 this.getContactManager().doDeleteOtherAddress();
253 case '0': // Program exit
254 this.getApplication().doShutdown();
258 // @TODO throw own exception
259 throw new UnhandledUserChoiceException(MessageFormat.format("Choice '{0}' not handled yet.", choice));
264 * Asks the the user to enter a single character which must match validChars
266 * @param validChars Valid chars that are accepted
267 * @param message Message to user
268 * @return Allowed character
271 public char enterChar (final char[] validChars, final String message) {
274 // Sort array, else binarySearch() won't work
275 Arrays.sort(validChars);
277 // Keep asking until valid char has been entered
278 while (Arrays.binarySearch(validChars, input) < 0) {
280 System.out.print(message);
283 input = this.readChar();
291 * Reads an integer (int) with a textural message from the user
293 * @param minimum Minimum allowed number
294 * @param maximum Maximum allowed number
295 * @param message Messager to display in console
299 public int enterInt (final int minimum, final int maximum, final String message) {
300 // Minimum should not be below zero
301 assert (minimum >= 0);
302 assert (maximum > minimum);
307 while ((input < minimum) || (input > maximum)) {
309 System.out.print(message);
311 // Read integer from user
312 input = this.readInt();
320 * Reads a string of minimum and maximum length from the user
322 * @param minLength Minimum length of the string to read
323 * @param maxLength Maximum length of the string to read
324 * @param message Message to user
325 * @param allowEmpty Whether to allow empty string
326 * @return Entered string by user or null for empty strings
329 public String enterString (final int minLength, final int maxLength, final String message, final boolean allowEmpty) {
330 // Check on length, e.g. country codes are excactly 2 chars long
331 assert (maxLength >= minLength);
336 // Check if it is to short or to long
337 while (((input == null) || ((input.length() < minLength) && (!allowEmpty))) || ((input.length() > 0) && (input.length() < minLength) && (allowEmpty)) || ((input instanceof String) && (input.length() > maxLength))) {
339 System.out.print(message);
342 input = this.readString();
350 * Returns a console menu item
352 * @param accessKey Key to access the menu
353 * @param text Text to show to user
354 * @return A SelectableMenuItem
355 * @todo Make sure the access key is unique
358 public SelectableMenuItem getMenuItem (final char accessKey, final String text) {
359 // Return a new console menu item
360 return new ConsoleMenuItem(accessKey, text);
364 * Inizializes this client
367 public void init () {
368 // Init contact manager here
369 this.initContactManager();
376 * Displays textural message to the user
381 public void outputMessage (final String message) {
382 System.out.println(message);
386 * Shows textural menu on console
389 public void showCurrentMenu () {
390 this.showMenu(this.getCurrentMenu());
394 * Shows given menu entry to user
396 * @param item Menu entry
399 public void showEntry (final SelectableMenuItem item) {
400 // Access key then text
401 this.outputMessage("[" + item.getAccessKey() + "] " + item.getText());
405 * Shows a textural message to the user
408 public void showWelcome () {
409 this.outputMessage(MessageFormat.format("Welcome to {0}", AddressbookApplication.printableTitle()));
410 this.outputMessage("");
411 this.outputMessage("Copyright(c) 2015 by Roland Haeder, this is free software");
414 this.getLogger().debug("Intro shown to user");
418 public void userChooseChangeContactData (final Contact contact) throws UnhandledUserChoiceException {
419 // Ask the user for editing [name], [a]ddress or [other] data
420 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) ");
422 // @TODO Get rid of this ugly switch block, too
424 case 'n': // Name data
425 this.getContactManager().doChangeNameData(contact, this);
428 case 'a': // Address data
429 this.getContactManager().doChangeAddressData(contact, this);
432 case 'o': // Other data
433 this.getContactManager().doChangeOtherData(contact, this);
436 case 'x': // Exit this menu
437 // Ignored as it should go back
441 // @TODO throw own exception
442 throw new UnhandledUserChoiceException(MessageFormat.format("Choice '{0}' not handled yet.", choice));
447 * Reads one character
449 * @return A single character
451 private char readChar () {
453 String input = this.scanner.nextLine();
455 // This must be only one character
456 if (input.length() != 1) {
461 // Get char from first (and only) position
462 return input.charAt(0);
466 * Reads an integer (int) from user
468 * @return An integer number
470 private int readInt () {
471 // First read a string
472 String input = this.readString();
474 // Init number with invalid value
477 // Parse number, this can be risky
479 num = Integer.parseInt(input);
480 } catch (final NumberFormatException e) {
481 this.outputMessage("Bitte geben Sie nur Zahlen ein!");
482 this.getLogger().warn(MessageFormat.format("No numbers-only entered. input={0},message={1}", input, e.getMessage()));
485 // Return read number
490 * Reads a string from a scanner until RETURN is pressed
492 * @return Read string from scanner
494 private String readString () {
495 return this.scanner.nextLine();
499 * Fills menu map with menu entries
502 protected final void fillMenuMap () {
503 // Initialize first (main) menu
504 Menu menu = new ConsoleMenu("main", this);
507 this.getMenus().put("main", menu);