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.Gender;
28 import org.mxchange.addressbook.contact.user.UserContact;
29 import org.mxchange.addressbook.exceptions.ContactAlreadyAddedException;
30 import org.mxchange.addressbook.exceptions.UnhandledUserChoiceException;
31 import org.mxchange.addressbook.menu.Menu;
32 import org.mxchange.addressbook.menu.MenuTools;
33 import org.mxchange.addressbook.menu.console.ConsoleMenu;
34 import org.mxchange.addressbook.menu.item.SelectableMenuItem;
35 import org.mxchange.addressbook.menu.item.console.ConsoleMenuItem;
38 * A client for the console
40 * @author Roland Haeder
42 public class ConsoleClient extends BaseClient implements Client {
45 * Scanner instance for reading data from console input
47 private final Scanner scanner;
50 * Parameterless constructor
52 * @param application An instance of an Application class
54 public ConsoleClient (final Application application) {
56 this.getLogger().trace(MessageFormat.format("application={0} - CALLED!", application)); //NOI18N
58 // Set application instance
59 this.setApplication(application);
61 // Init scanner instance
62 this.scanner = new Scanner(System.in);
65 this.getLogger().trace("EXIT!"); //NOI18N
69 * Displays a textual address "box" of given contact
71 * @param contact Contact to show address for
74 public void displayAddressBox (final Contact contact) {
76 this.getLogger().trace(MessageFormat.format("contact={0} - CALLED!", contact)); //NOI18N
79 if (contact == null) {
81 throw new NullPointerException("contact is null"); //NOI18N
85 this.outputMessage(MessageFormat.format("Strasse, PLZ Ort, Land: {0}\n{1} {2}\n{3}", contact.getStreet(), contact.getZipCode(), contact.getCity(), contact.getCountryCode()));
88 this.getLogger().trace("EXIT!"); //NOI18N
92 * Displays a textual name "box" of given contact
94 * @param contact Contact to show name for
97 public void displayNameBox (final Contact contact) {
99 this.getLogger().trace(MessageFormat.format("contact={0} - CALLED!", contact)); //NOI18N
102 if (contact == null) {
104 throw new NullPointerException("contact is null"); //NOI18N
107 // Get translated gender as the user may want to see "Mr.", "Mrs."
108 String gender = contact.getTranslatedGender();
111 String companyName = contact.getCompanyName();
113 // If it is empty/null, then assume private contact
114 if ((companyName == null) || (companyName.isEmpty())) {
115 // Now put all together: gender, surname, family name
117 this.outputMessage(MessageFormat.format("Anrede, Vorname, Name: {0} {1} {2}", gender, contact.getSurname(), contact.getFamilyName()));
120 this.outputMessage(MessageFormat.format("Firma: {0}\nAnsprechpartner: {1} {2} {3}", companyName, gender, contact.getSurname(), contact.getFamilyName()));
124 this.getLogger().trace("EXIT!"); //NOI18N
128 * Displays a textual other data "box" of given contact
130 * @param contact Contact to show other data for
133 public void displayOtherDataBox (final Contact contact) {
135 this.getLogger().trace(MessageFormat.format("contact={0} - CALLED!", contact)); //NOI18N
138 if (contact == null) {
140 throw new NullPointerException("contact is null"); //NOI18N
143 // Cellphone and such ...
144 this.outputMessage(MessageFormat.format("Telefonnumer: {0}\nFaxnummer: {1}\nHandy: {2}\nKommentar:\n{3}", contact.getPhoneNumber(), contact.getFaxNumber(), contact.getCellphoneNumber(), contact.getComment()));
147 this.getLogger().trace("EXIT!"); //NOI18N
151 public void doChangeOwnAddressData (final Contact contact) {
153 this.getLogger().trace(MessageFormat.format("contact={0} - CALLED!", contact)); //NOI18N
156 if (contact == null) {
158 throw new NullPointerException("contact is null"); //NOI18N
161 // Make sure it is own contact
162 if (!contact.isOwnContact()) {
164 throw new IllegalArgumentException("Contact is not own data."); //NOI18N
168 String street = this.getContactManager().enterOwnStreet();
171 int zipCode = this.getContactManager().enterOwnZipCode();
174 String city = this.getContactManager().enterOwnCity();
177 String countryCode = this.getContactManager().enterOwnCountryCode();
179 // Update address data
180 contact.updateAddressData(street, zipCode, city, countryCode);
183 this.getLogger().trace("EXIT!"); //NOI18N
187 public void doChangeOwnNameData (final Contact contact) {
189 this.getLogger().trace(MessageFormat.format("contact={0} - CALLED!", contact)); //NOI18N
192 if (contact == null) {
194 throw new NullPointerException("contact is null"); //NOI18N
197 // Make sure it is own contact
198 if (!contact.isOwnContact()) {
200 throw new IllegalArgumentException("Contact is not own data."); //NOI18N
204 Gender gender = this.getContactManager().enterOwnGender();
207 String surname = this.getContactManager().enterOwnSurname();
210 String familyName = this.getContactManager().enterOwnFamilyName();
213 String companyName = this.getContactManager().enterOwnCompanyName();
215 // Update contact instance
216 contact.updateNameData(gender, surname, familyName, companyName);
219 this.getLogger().trace("EXIT!"); //NOI18N
223 public void doChangeOwnOtherData (final Contact contact) {
225 this.getLogger().trace(MessageFormat.format("contact={0} - CALLED!", contact)); //NOI18N
228 if (contact == null) {
230 throw new NullPointerException("contact is null"); //NOI18N
233 // Make sure it is own contact
234 if (!contact.isOwnContact()) {
236 throw new IllegalArgumentException("Contact is not own data."); //NOI18N
240 String phoneNumber = this.getContactManager().enterOwnPhoneNumber();
243 String cellNumber = this.getContactManager().enterOwnCellNumber();
246 String faxNumber = this.getContactManager().enterOwnFaxNumber();
249 String email = this.getContactManager().enterOwnEmailAddress();
252 String comment = this.getContactManager().enterOwnComment();
254 // Update contact instance
255 contact.updateOtherData(phoneNumber, cellNumber, faxNumber, email, null, comment);
258 this.getLogger().trace("EXIT!"); //NOI18N
262 public Contact doEnterOwnData () {
264 this.getLogger().trace("CALLED!"); //NOI18N
266 // First ask for gender
267 Gender gender = this.getContactManager().enterOwnGender();
270 String surname = this.getContactManager().enterOwnSurname();
272 // And 3rd for family name
273 String familyName = this.getContactManager().enterOwnFamilyName();
276 String companyName = this.getContactManager().enterOwnCompanyName();
278 // Construct UserContact instance
279 Contact contact = new UserContact(gender, surname, familyName, companyName);
282 this.getLogger().trace(MessageFormat.format("contact={0} - EXIT!", contact)); //NOI18N
289 * Shutdown this client
292 public void doShutdown () {
294 this.getLogger().trace("CALLED!"); //NOI18N
299 // @TODO Add other shutdown stuff
302 this.getLogger().trace("EXIT!"); //NOI18N
306 public void doUserMenuChoice () throws UnhandledUserChoiceException {
308 this.getLogger().trace("CALLED!"); //NOI18N
310 // Get all access keys from menu
311 char[] accessKeys = MenuTools.getAccessKeysFromMenuMap(this.getMenus(), this.getCurrentMenu());
313 // Output textural message and ask for a char as input
314 char choice = this.enterChar(accessKeys, "Bitte Auswahl eingeben (0=Programm beenden): ");
316 // @TODO Rewrite this ugly switch() block
320 // Enter/add own data
321 this.getContactManager().doEnterOwnData();
322 } catch (final ContactAlreadyAddedException ex) {
324 this.outputMessage("Sie haben bereits Ihre eigenen Daten eingegeben.");
328 case '2': // Change own data
329 this.getContactManager().doChangeOwnData();
332 case '3': // Add new addess
333 this.getContactManager().doAddOtherAddress();
336 case '4': // List contacts
337 this.getContactManager().doListContacts();
340 case '5': // Search addresses
341 this.getContactManager().doSearchContacts();
344 case '6': // Change other addess
345 this.getContactManager().doChangeOtherAddress();
348 case '7': // Delete other address
349 this.getContactManager().doDeleteOtherAddress();
352 case '0': // Program exit
353 this.getApplication().doShutdown();
357 // @TODO throw own exception
358 throw new UnhandledUserChoiceException(MessageFormat.format("Choice '{0}' not handled yet.", choice)); //NOI18N
362 this.getLogger().trace("EXIT!"); //NOI18N
366 * Asks the the user to enter a single character which must match validChars
368 * @param validChars Valid chars that are accepted
369 * @param message Message to user
370 * @return Allowed character
373 public char enterChar (final char[] validChars, final String message) {
375 this.getLogger().trace(MessageFormat.format("validChars={0},message={1} - CALLED!", Arrays.toString(validChars), message)); //NOI18N
377 // The validChars must not null be null and filled with at least one char
378 if (validChars == null) {
380 throw new NullPointerException("validChars is null"); //NOI18N
381 } else if (validChars.length == 0) {
383 throw new IllegalArgumentException("validChars is not filled."); //NOI18N
388 // Sort array, else binarySearch() won't work
389 Arrays.sort(validChars);
391 // Keep asking until valid char has been entered
392 while (Arrays.binarySearch(validChars, input) < 0) {
394 System.out.print(message);
397 input = this.readChar();
401 this.getLogger().trace(MessageFormat.format("input={0} - EXIT!", input)); //NOI18N
408 * Asks the user to enter his/her gender
410 * @param message Message to the user
411 * @return Gender enum
414 public Gender enterGender (final String message) {
416 this.getLogger().trace(MessageFormat.format("message={0} - CALLED!", message)); //NOI18N
419 char[] validChars = Gender.validChars();
422 //* NOISY-DEBUG: */ System.out.println(validChars);
424 char gender = this.enterChar(validChars, message);
426 // Now get a Gender instance back
427 Gender g = Gender.fromChar(gender);
429 // g must not be null
430 assert(g instanceof Gender) : "g is not set."; //NOI18N
433 this.getLogger().trace(MessageFormat.format("g={0} - EXIT!", g)); //NOI18N
440 * Reads an integer (int) with a textural message from the user
442 * @param minimum Minimum allowed number
443 * @param maximum Maximum allowed number
444 * @param message Messager to display in console
448 public int enterInt (final int minimum, final int maximum, final String message) {
450 this.getLogger().trace(MessageFormat.format("minimum={0},maximum={1},message={2} - CALLED!", minimum, maximum, message)); //NOI18N
452 // Minimum should not be below zero
453 assert (minimum >= 0);
454 assert (maximum > minimum);
459 while ((input < minimum) || (input > maximum)) {
461 System.out.print(message);
463 // Read integer from user
464 input = this.readInt();
468 this.getLogger().trace(MessageFormat.format("input={0} - EXIT!", input)); //NOI18N
475 * Reads a string of minimum and maximum length from the user
477 * @param minLength Minimum length of the string to read
478 * @param maxLength Maximum length of the string to read
479 * @param message Message to user
480 * @param allowEmpty Whether to allow empty string
481 * @return Entered string by user or null for empty strings
484 public String enterString (final int minLength, final int maxLength, final String message, final boolean allowEmpty) {
486 this.getLogger().trace(MessageFormat.format("minLength={0},maxLength={1},message={2}allowEmpty={3} - CALLED!", minLength, maxLength, message, allowEmpty)); //NOI18N
488 // Check on length, e.g. country codes are excactly 2 chars long
489 assert (maxLength >= minLength);
494 // Check if it is to short or to long
495 while (((input == null) || ((input.length() < minLength) && (!allowEmpty))) || ((input.length() > 0) && (input.length() < minLength) && (allowEmpty)) || ((input instanceof String) && (input.length() > maxLength))) {
497 System.out.print(message);
500 input = this.readString();
504 this.getLogger().trace(MessageFormat.format("input={0} - EXIT!", input)); //NOI18N
511 * Returns a console menu item
513 * @param accessKey Key to access the menu
514 * @param text Text to show to user
515 * @return A SelectableMenuItem
516 * @todo Make sure the access key is unique
519 public SelectableMenuItem getMenuItem (final char accessKey, final String text) {
520 // Return a new console menu item
521 return new ConsoleMenuItem(accessKey, text);
525 * Inizializes this client
528 public void init () {
530 this.getLogger().trace("CALLED!"); //NOI18N
532 // Init contact manager here
533 this.initContactManager();
539 this.getLogger().trace("EXIT!"); //NOI18N
543 * Displays textural message to the user
548 public void outputMessage (final String message) {
549 System.out.println(message);
553 * Shows textural menu on console
556 public void showCurrentMenu () {
557 this.showMenu(this.getCurrentMenu());
561 * Shows given menu entry to user
563 * @param item Menu entry
566 public void showEntry (final SelectableMenuItem item) {
567 // Access key then text
568 this.outputMessage(MessageFormat.format("[{0}] {1}", item.getAccessKey(), item.getText()));
572 * Shows a textural message to the user
575 public void showWelcome () {
576 this.outputMessage(MessageFormat.format("Welcome to {0}", AddressbookApplication.printableTitle()));
577 this.outputMessage("");
578 this.outputMessage("Copyright(c) 2015 by Roland Haeder, this is free software");
581 this.getLogger().debug("Intro shown to user");
585 public void userChooseChangeContactData (final Contact contact) throws UnhandledUserChoiceException {
587 this.getLogger().trace(MessageFormat.format("contact={0} CALLED!", contact)); //NOI18N
589 // Contact must not be null
590 if (contact == null) {
592 throw new NullPointerException("contact is null");
595 // Ask the user for editing [name], [a]ddress or [other] data
596 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) ");
598 // @TODO Get rid of this ugly switch block, too
600 case 'n': // Name data
601 this.getContactManager().doChangeNameData(contact);
604 case 'a': // Address data
605 this.getContactManager().doChangeAddressData(contact);
608 case 'o': // Other data
609 this.getContactManager().doChangeOtherData(contact);
612 case 'x': // Exit this menu
613 // Ignored as it should go back
617 // @TODO throw own exception
618 throw new UnhandledUserChoiceException(MessageFormat.format("Choice '{0}' not handled yet.", choice));
622 this.getLogger().trace("EXIT!"); //NOI18N
626 * Reads one character
628 * @return A single character
630 private char readChar () {
632 String input = this.readString();
634 // This must be only one character
635 if (input.length() != 1) {
640 // Get char from first (and only) position
641 return input.charAt(0);
645 * Reads an integer (int) from user
647 * @return An integer number
649 private int readInt () {
650 // First read a string
651 String input = this.readString();
653 // Init number with invalid value
656 // Parse number, this can be risky
658 num = Integer.parseInt(input);
659 } catch (final NumberFormatException e) {
660 this.outputMessage("Bitte geben Sie nur Zahlen ein!");
661 this.getLogger().warn(MessageFormat.format("No numbers-only entered. input={0},message={1}", input, e.getMessage()));
664 // Return read number
669 * Reads a string from a scanner until RETURN is pressed
671 * @return Read string from scanner
673 private String readString () {
674 return this.scanner.nextLine();
678 * Fills menu map with menu entries
681 protected final void fillMenuMap () {
683 this.getLogger().trace("CALLED!"); //NOI18N
685 // Initialize first (main) menu
686 Menu menu = new ConsoleMenu("main", this);
689 this.getMenus().put("main", menu);
692 this.getLogger().trace("EXIT!"); //NOI18N