2 * Copyright (C) 2015 Roland Haeder
\r
4 * This program is free software: you can redistribute it and/or modify
\r
5 * it under the terms of the GNU General Public License as published by
\r
6 * the Free Software Foundation, either version 3 of the License, or
\r
7 * (at your option) any later version.
\r
9 * This program is distributed in the hope that it will be useful,
\r
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
12 * GNU General Public License for more details.
\r
14 * You should have received a copy of the GNU General Public License
\r
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
\r
17 package org.mxchange.addressbook.client.console;
\r
19 import java.text.MessageFormat;
\r
20 import java.util.Arrays;
\r
21 import java.util.Scanner;
\r
22 import org.mxchange.addressbook.UnhandledUserChoiceException;
\r
23 import org.mxchange.addressbook.application.AddressbookApplication;
\r
24 import org.mxchange.addressbook.application.Application;
\r
25 import org.mxchange.addressbook.client.BaseClient;
\r
26 import org.mxchange.addressbook.client.Client;
\r
27 import org.mxchange.addressbook.contact.Contact;
\r
28 import org.mxchange.addressbook.contact.user.UserContact;
\r
29 import org.mxchange.addressbook.menu.Menu;
\r
30 import org.mxchange.addressbook.menu.MenuTools;
\r
31 import org.mxchange.addressbook.menu.console.ConsoleMenu;
\r
32 import org.mxchange.addressbook.menu.item.SelectableMenuItem;
\r
33 import org.mxchange.addressbook.menu.item.console.ConsoleMenuItem;
\r
36 * A client for the console
\r
38 * @author Roland Haeder
\r
40 public class ConsoleClient extends BaseClient implements Client {
\r
42 * Scanner instance for reading data from console input
\r
44 private final Scanner scanner;
\r
47 * Parameterless constructor
\r
48 * @param application An instance of an Application class
\r
50 public ConsoleClient (final Application application) {
\r
53 // Set application instance
\r
54 this.setApplication(application);
\r
56 // Init scanner instance
\r
57 this.scanner = new Scanner(System.in);
\r
61 * Displays a textual address "box" of given contact
\r
63 * @param contact Contact to show address for
\r
66 public void displayAddressBox (final Contact contact) {
\r
67 // Simple display ...
\r
68 this.outputMessage(MessageFormat.format("Strasse, PLZ Ort, Land: {0}\n{1} {2}\n{3}", contact.getStreet(), contact.getZipCode(), contact.getCity(), contact.getCountryCode()));
\r
72 * Displays a textual name "box" of given contact
\r
74 * @param contact Contact to show name for
\r
77 public void displayNameBox (final Contact contact) {
\r
78 // Get translated gender as the user may want to see "Mr.", "Mrs."
\r
79 String gender = contact.getTranslatedGender();
\r
82 String companyName = contact.getCompanyName();
\r
84 // If it is empty/null, then assume private contact
\r
85 if ((companyName == null) || (companyName.isEmpty())) {
\r
86 // Now put all together: gender, surname, family name
\r
88 this.outputMessage(MessageFormat.format("Anrede, Vorname, Name: {0} {1} {2}", gender, contact.getSurname(), contact.getFamilyName()));
\r
91 this.outputMessage(MessageFormat.format("Firma: {0}\nAnsprechpartner: {1} {2} {3}", companyName, gender, contact.getSurname(), contact.getFamilyName()));
\r
96 * Displays a textual other data "box" of given contact
\r
98 * @param contact Contact to show other data for
\r
101 public void displayOtherDataBox (final Contact contact) {
\r
102 // Cellphone and such ...
\r
103 this.outputMessage(MessageFormat.format("Telefonnumer: {0}\nFaxnummer: {1}\nHandy: {2}\nKommentar:\n{3}", contact.getPhoneNumber(), contact.getFaxNumber(), contact.getCellphoneNumber(), contact.getComment()));
\r
107 public void doChangeOwnAddressData (final Contact contact) {
\r
108 // Make sure it is own contact
\r
109 if (!contact.isOwnContact()) {
\r
111 throw new IllegalArgumentException("Contact is not own data.");
\r
114 // Own address data
\r
115 String street = this.getContactManager().enterOwnStreet();
\r
118 int zipCode = this.getContactManager().enterOwnZipCode();
\r
121 String city = this.getContactManager().enterOwnCity();
\r
123 // Get country code
\r
124 String countryCode = this.getContactManager().enterOwnCountryCode();
\r
126 // Update address data
\r
127 contact.updateAddressData(street, zipCode, city, countryCode);
\r
131 public void doChangeOwnNameData (final Contact contact) {
\r
132 // Make sure it is own contact
\r
133 if (!contact.isOwnContact()) {
\r
135 throw new IllegalArgumentException("Contact is not own data.");
\r
139 char gender = this.getContactManager().enterOwnGender();
\r
142 String surname = this.getContactManager().enterOwnSurname();
\r
145 String familyName = this.getContactManager().enterOwnFamilyName();
\r
148 String companyName = this.getContactManager().enterOwnCompanyName();
\r
150 // Update contact instance
\r
151 contact.updateNameData(gender, surname, familyName, companyName);
\r
155 public void doChangeOwnOtherData (final Contact contact) {
\r
156 // Make sure it is own contact
\r
157 if (!contact.isOwnContact()) {
\r
159 throw new IllegalArgumentException("Contact is not own data.");
\r
163 String phoneNumber = this.getContactManager().enterOwnPhoneNumber();
\r
166 String cellNumber = this.getContactManager().enterOwnCellNumber();
\r
169 String faxNumber = this.getContactManager().enterOwnFaxNumber();
\r
172 String email = this.getContactManager().enterOwnEmailAddress();
\r
175 String comment = this.getContactManager().enterOwnComment();
\r
177 // Update contact instance
\r
178 contact.updateOtherData(phoneNumber, cellNumber, faxNumber, email, null, comment);
\r
182 public Contact doEnterOwnData () {
\r
183 // First ask for gender
\r
184 char gender = this.getContactManager().enterOwnGender();
\r
187 String surname = this.getContactManager().enterOwnSurname();
\r
189 // And 3rd for family name
\r
190 String familyName = this.getContactManager().enterOwnFamilyName();
\r
192 // Company name ...
\r
193 String companyName = this.getContactManager().enterOwnCompanyName();
\r
195 // Construct UserContact instance
\r
196 Contact contact = new UserContact(gender, surname, familyName, companyName);
\r
198 // And return object
\r
203 * Shutdown this client
\r
206 public void doShutdown () {
\r
208 super.doShutdown();
\r
210 // @TODO Add other shutdown stuff
\r
214 public void doUserMenuChoice () throws UnhandledUserChoiceException {
\r
215 // Get all access keys from menu
\r
216 char[] accessKeys = MenuTools.getAccessKeysFromMenuMap(this.getMenus(), this.getCurrentMenu());
\r
218 // Output textural message and ask for a char as input
\r
219 char choice = this.enterChar(accessKeys, "Bitte Auswahl eingeben (0=Programm beenden): ");
\r
221 // @TODO Rewrite this ugly switch() block
\r
223 case '1': // Enter/add own data
\r
224 this.getContactManager().doEnterOwnData();
\r
227 case '2': // Change own data
\r
228 this.getContactManager().changeOwnData();
\r
231 case '3': // Add new addess
\r
232 this.getContactManager().addOtherAddress();
\r
235 case '4': // List contacts
\r
236 this.getContactManager().listContacts();
\r
239 case '5': // Search addresses
\r
240 this.getContactManager().searchContacts();
\r
243 case '6': // Change other addess
\r
244 this.getContactManager().changeOtherAddress();
\r
247 case '7': // Delete other address
\r
248 this.getContactManager().deleteOtherAddress();
\r
251 case '0': // Program exit
\r
252 this.getApplication().doShutdown();
\r
256 // @TODO throw own exception
\r
257 throw new UnhandledUserChoiceException(MessageFormat.format("Choice '{0}' not handled yet.", choice));
\r
262 * Asks the the user to enter a single character which must match validChars
\r
263 * @param validChars Valid chars that are accepted
\r
264 * @param message Message to user
\r
265 * @return Allowed character
\r
268 public char enterChar (final char[] validChars, final String message) {
\r
271 // Sort array, else binarySearch() won't work
\r
272 Arrays.sort(validChars);
\r
274 // Keep asking until valid char has been entered
\r
275 while (Arrays.binarySearch(validChars, input) < 0) {
\r
277 System.out.print(message);
\r
280 input = this.readChar();
\r
283 // Return read char
\r
288 * Reads an integer (int) with a textural message from the user
\r
290 * @param minimum Minimum allowed number
\r
291 * @param maximum Maximum allowed number
\r
292 * @param message Messager to display in console
\r
296 public int enterInt (final int minimum, final int maximum, final String message) {
\r
297 // Minimum should not be below zero
\r
298 assert(minimum >= 0);
\r
299 assert(maximum > minimum);
\r
304 while ((input < minimum) || (input > maximum)) {
\r
306 System.out.print(message);
\r
308 // Read integer from user
\r
309 input = this.readInt();
\r
317 * Reads a string of minimum and maximum length from the user
\r
319 * @param minLength Minimum length of the string to read
\r
320 * @param maxLength Maximum length of the string to read
\r
321 * @param message Message to user
\r
322 * @param allowEmpty Whether to allow empty string
\r
323 * @return Entered string by user or null for empty strings
\r
326 public String enterString (final int minLength, final int maxLength, final String message, final boolean allowEmpty) {
\r
327 // Check on length, e.g. country codes are excactly 2 chars long
\r
328 assert(maxLength >= minLength);
\r
331 String input = null;
\r
333 // Check if it is to short or to long
\r
334 while (((input == null) || ((input.length() < minLength) && (!allowEmpty))) || ((input.length() > 0) && (input.length() < minLength) && (allowEmpty)) || ((input instanceof String) && (input.length() > maxLength))) {
\r
336 System.out.print(message);
\r
339 input = this.readString();
\r
347 * Returns a console menu item
\r
349 * @param accessKey Key to access the menu
\r
350 * @param text Text to show to user
\r
351 * @return A SelectableMenuItem
\r
352 * @todo Make sure the access key is unique
\r
355 public SelectableMenuItem getMenuItem (final char accessKey, final String text) {
\r
356 // Return a new console menu item
\r
357 return new ConsoleMenuItem(accessKey,text);
\r
361 * Inizializes this client
\r
364 public void initClient () {
\r
365 // Init contact manager here
\r
366 this.initContactManager();
\r
369 this.fillMenuMap();
\r
373 * Displays textural message to the user
\r
377 public void outputMessage (final String message) {
\r
378 System.out.println(message);
\r
382 * Shows textural menu on console
\r
385 public void showCurrentMenu () {
\r
386 this.showMenu(this.getCurrentMenu());
\r
390 * Shows given menu entry to user
\r
392 * @param item Menu entry
\r
395 public void showEntry (final SelectableMenuItem item) {
\r
396 // Access key then text
\r
397 this.outputMessage("[" + item.getAccessKey() + "] " + item.getText());
\r
401 * Shows a textural message to the user
\r
404 public void showWelcome () {
\r
405 this.outputMessage(MessageFormat.format("Welcome to {0}", AddressbookApplication.printableTitle()));
\r
406 this.outputMessage("");
\r
407 this.outputMessage("Copyright(c) 2015 by Roland Haeder, this is free software");
\r
410 this.getLogger().debug("Intro shown to user");
\r
414 public void userChooseChangeContactData (final Contact contact) throws UnhandledUserChoiceException {
\r
415 // Ask the user for editing [name], [a]ddress or [other] data
\r
416 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
418 // @TODO Get rid of this ugly switch block, too
\r
420 case 'n': // Name data
\r
421 this.getContactManager().doChangeNameData(contact, this);
\r
424 case 'a': // Address data
\r
425 this.getContactManager().doChangeAddressData(contact, this);
\r
428 case 'o': // Other data
\r
429 this.getContactManager().doChangeOtherData(contact, this);
\r
432 case 'x': // Exit this menu
\r
433 // Ignored as it should go back
\r
437 // @TODO throw own exception
\r
438 throw new UnhandledUserChoiceException(MessageFormat.format("Choice '{0}' not handled yet.", choice));
\r
443 * Reads one character
\r
445 * @return A single character
\r
447 private char readChar () {
\r
449 String input = this.scanner.nextLine();
\r
451 // This must be only one character
\r
452 if (input.length() != 1) {
\r
457 // Get char from first (and only) position
\r
458 return input.charAt(0);
\r
462 * Reads an integer (int) from user
\r
464 * @return An integer number
\r
466 private int readInt () {
\r
467 // First read a string
\r
468 String input = this.readString();
\r
470 // Init number with invalid value
\r
473 // Parse number, this can be risky
\r
475 num = Integer.parseInt(input);
\r
476 } catch (final NumberFormatException e) {
\r
477 this.outputMessage("Bitte geben Sie nur Zahlen ein!");
\r
478 this.getLogger().warn(MessageFormat.format("No numbers-only entered. input={0},message={1}", input, e.getMessage()));
\r
481 // Return read number
\r
486 * Reads a string from a scanner until RETURN is pressed
\r
488 * @return Read string from scanner
\r
490 private String readString () {
\r
491 return this.scanner.nextLine();
\r
495 * Fills menu map with menu entries
\r
498 protected final void fillMenuMap () {
\r
499 // Initialize first (main) menu
\r
500 Menu menu = new ConsoleMenu("main", this);
\r
503 this.getMenus().put("main", menu);
\r