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.io.IOException;
20 import java.lang.reflect.InvocationTargetException;
21 import java.sql.SQLException;
22 import java.text.MessageFormat;
23 import java.util.Arrays;
24 import java.util.Scanner;
25 import org.mxchange.addressbook.application.AddressbookApplication;
26 import org.mxchange.addressbook.client.AddressbookClient;
27 import org.mxchange.addressbook.client.BaseAddressbookClient;
28 import org.mxchange.addressbook.exceptions.ContactAlreadyAddedException;
29 import org.mxchange.addressbook.facade.contact.ContactFacade;
30 import org.mxchange.addressbook.menu.Menu;
31 import org.mxchange.addressbook.menu.MenuTools;
32 import org.mxchange.addressbook.menu.console.ConsoleMenu;
33 import org.mxchange.addressbook.menu.item.SelectableMenuItem;
34 import org.mxchange.addressbook.menu.item.console.ConsoleMenuItem;
35 import org.mxchange.jcontacts.contact.Contact;
36 import org.mxchange.jcontacts.contact.UserContact;
37 import org.mxchange.jcontacts.contact.gender.Gender;
38 import org.mxchange.jcontacts.contact.gender.GenderUtils;
39 import org.mxchange.jcore.application.Application;
40 import org.mxchange.jcore.exceptions.MenuInitializationException;
41 import org.mxchange.jcore.exceptions.UnhandledUserChoiceException;
44 * A client for the console
46 * @author Roland Haeder
48 public class ConsoleClient extends BaseAddressbookClient implements AddressbookClient {
51 * Scanner instance for reading data from console input
53 private final Scanner scanner;
56 * Parameterless constructor
58 * @param application An instance of an Application class
60 public ConsoleClient (final Application application) {
62 this.getLogger().logTrace(MessageFormat.format("application={0} - CALLED!", application)); //NOI18N
64 // Set application instance
65 this.setApplication(application);
67 // Init scanner instance
68 this.scanner = new Scanner(System.in, "UTF-8"); //NOI18N
71 this.getLogger().logTrace("EXIT!"); //NOI18N
75 public void displayAddressBox (final Contact contact) {
77 this.getLogger().logTrace(MessageFormat.format("contact={0} - CALLED!", contact)); //NOI18N
80 if (null == contact) {
82 throw new NullPointerException("contact is null"); //NOI18N
86 this.outputMessage(MessageFormat.format("Strasse, PLZ Ort, Land: {0}\n{1} {2}\n{3}", contact.getStreet(), contact.getZipCode(), contact.getCity(), contact.getCountryCode()));
89 this.getLogger().logTrace("EXIT!"); //NOI18N
93 public void displayNameBox (final Contact contact) {
95 this.getLogger().logTrace(MessageFormat.format("contact={0} - CALLED!", contact)); //NOI18N
98 if (null == contact) {
100 throw new NullPointerException("contact is null"); //NOI18N
103 // Get translated gender as the user may want to see "Mr.", "Mrs."
104 String gender = GenderUtils.getTranslatedGender(contact);
107 String companyName = contact.getCompanyName();
109 // If it is empty/null, then assume private contact
110 if ((null == companyName) || (companyName.isEmpty())) {
111 // Now put all together: gender, surname, family name
113 this.outputMessage(MessageFormat.format("Anrede, Vorname, Name: {0} {1} {2}", gender, contact.getFirstName(), contact.getFamilyName()));
116 this.outputMessage(MessageFormat.format("Firma: {0}\nAnsprechpartner: {1} {2} {3}", companyName, gender, contact.getFirstName(), contact.getFamilyName()));
120 this.getLogger().logTrace("EXIT!"); //NOI18N
124 public void displayOtherDataBox (final Contact contact) {
126 this.getLogger().logTrace(MessageFormat.format("contact={0} - CALLED!", contact)); //NOI18N
129 if (null == contact) {
131 throw new NullPointerException("contact is null"); //NOI18N
134 // Cellphone and such ...
135 this.outputMessage(MessageFormat.format("Telefonnumer: {0}\nFaxnummer: {1}\nHandy: {2}\nKommentar:\n{3}", contact.getPhoneNumber(), contact.getFaxNumber(), contact.getCellphoneNumber(), contact.getComment()));
138 this.getLogger().logTrace("EXIT!"); //NOI18N
142 public void doChangeOwnAddressData (final Contact contact) {
144 this.getLogger().logTrace(MessageFormat.format("contact={0} - CALLED!", contact)); //NOI18N
147 if (null == contact) {
149 throw new NullPointerException("contact is null"); //NOI18N
152 // Make sure it is own contact
153 if (!contact.isOwnContact()) {
155 throw new IllegalArgumentException("Contact is not own data."); //NOI18N
158 // Get manager and cast it
159 ContactFacade manager = (ContactFacade) this.getManager();
161 // Own street and number
162 String streetNumber = manager.enterOwnStreet();
165 Long zipCode = (long) manager.enterOwnZipCode();
168 String city = manager.enterOwnCity();
171 String countryCode = manager.enterOwnCountryCode();
173 // Update address data
174 contact.setStreet(streetNumber);
175 contact.setZipCode(zipCode);
176 contact.setCity(city);
177 contact.setCountryCode(countryCode);
180 this.getLogger().logTrace("EXIT!"); //NOI18N
184 public void doChangeOwnNameData (final Contact contact) {
186 this.getLogger().logTrace(MessageFormat.format("contact={0} - CALLED!", contact)); //NOI18N
189 if (null == contact) {
191 throw new NullPointerException("contact is null"); //NOI18N
194 // Make sure it is own contact
195 if (!contact.isOwnContact()) {
197 throw new IllegalArgumentException("Contact is not own data."); //NOI18N
200 // Get manager and cast it
201 ContactFacade manager = (ContactFacade) this.getManager();
204 Gender gender = manager.enterOwnGender();
207 String firstName = manager.enterOwnFirstName();
210 String familyName = manager.enterOwnFamilyName();
213 String companyName = manager.enterOwnCompanyName();
215 // Update contact instance
216 contact.setGender(gender);
217 contact.setFirstName(firstName);
218 contact.setFamilyName(familyName);
219 contact.setCompanyName(companyName);
222 this.getLogger().logTrace("EXIT!"); //NOI18N
226 public void doChangeOwnOtherData (final Contact contact) {
228 this.getLogger().logTrace(MessageFormat.format("contact={0} - CALLED!", contact)); //NOI18N
231 if (null == contact) {
233 throw new NullPointerException("contact is null"); //NOI18N
236 // Make sure it is own contact
237 if (!contact.isOwnContact()) {
239 throw new IllegalArgumentException("Contact is not own data."); //NOI18N
242 // Get manager and cast it
243 ContactFacade manager = (ContactFacade) this.getManager();
246 String phoneNumber = manager.enterOwnPhoneNumber();
249 String cellphonePhoneNumber = manager.enterOwnCellNumber();
252 String faxNumber = manager.enterOwnFaxNumber();
255 String email = manager.enterOwnEmailAddress();
258 String comment = manager.enterOwnComment();
260 // Update contact instance
261 contact.setPhoneNumber(phoneNumber);
262 contact.setCellphoneNumber(cellphonePhoneNumber);
263 contact.setFaxNumber(faxNumber);
264 contact.setEmailAddress(email);
265 contact.setComment(comment);
268 this.getLogger().logTrace("EXIT!"); //NOI18N
272 public Contact doEnterOwnData () {
274 this.getLogger().logTrace("CALLED!"); //NOI18N
276 // Get manager and cast it
277 ContactFacade manager = (ContactFacade) this.getManager();
279 // First ask for gender
280 Gender gender = manager.enterOwnGender();
282 // 2nd for first name
283 String firstName = manager.enterOwnFirstName();
285 // And 3rd for family name
286 String familyName = manager.enterOwnFamilyName();
289 String companyName = manager.enterOwnCompanyName();
291 // Construct UserContact instance
292 Contact contact = new UserContact(gender, firstName, familyName, companyName);
295 this.getLogger().logTrace(MessageFormat.format("contact={0} - EXIT!", contact)); //NOI18N
302 public void doShutdown () throws SQLException, IOException {
304 this.getLogger().logTrace("CALLED!"); //NOI18N
309 // TODO Add other shutdown stuff
311 this.getLogger().logTrace("EXIT!"); //NOI18N
315 public void doUserMenuChoice () throws UnhandledUserChoiceException, MenuInitializationException {
317 this.getLogger().logTrace("CALLED!"); //NOI18N
319 // Get all access keys from menu
320 char[] accessKeys = MenuTools.getAccessKeysFromMenuMap(this.getMenus(), this.getCurrentMenu());
322 // Output textural message and ask for a char as input
323 char choice = this.enterChar(accessKeys, "Bitte Auswahl eingeben (0=Programm beenden): ");
325 // Get manager and cast it
326 ContactFacade manager = (ContactFacade) this.getManager();
330 // TODO Rewrite this ugly switch() block
334 // Enter/add own data
335 manager.doEnterOwnData();
336 } catch (final ContactAlreadyAddedException ex) {
338 this.outputMessage("Sie haben bereits Ihre eigenen Daten eingegeben.");
342 case '2': // Change own data
343 manager.doChangeOwnData();
346 case '3': // Add new addess
347 manager.doAddOtherAddress();
350 case '4': // List contacts
351 manager.doListContacts();
354 case '5': // Search addresses
355 manager.doSearchContacts();
358 case '6': // Change other addess
359 manager.doChangeOtherAddress();
362 case '7': // Delete other address
363 manager.doDeleteOtherAddress();
369 this.getApplication().doShutdown();
370 } catch (final SQLException | IOException ex) {
371 this.abortProgramWithException(ex);
376 // TODO throw own exception
377 throw new UnhandledUserChoiceException(MessageFormat.format("Choice '{0}' not handled yet.", choice)); //NOI18N
379 } catch (final IOException | SQLException | NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) {
380 // Something bad happened
381 this.abortProgramWithException(ex);
385 this.getLogger().logTrace("EXIT!"); //NOI18N
389 public char enterChar (final char[] validChars, final String message) {
391 this.getLogger().logTrace(MessageFormat.format("validChars={0},message={1} - CALLED!", Arrays.toString(validChars), message)); //NOI18N
393 // The validChars must not null be null and filled with at least one char
394 if (null == validChars) {
396 throw new NullPointerException("validChars is null"); //NOI18N
397 } else if (validChars.length == 0) {
399 throw new IllegalArgumentException("validChars is not filled."); //NOI18N
404 // Sort array, else binarySearch() won't work
405 Arrays.sort(validChars);
407 // Keep asking until valid char has been entered
408 while (Arrays.binarySearch(validChars, input) < 0) {
410 System.out.print(message);
413 input = this.readChar();
417 this.getLogger().logTrace(MessageFormat.format("input={0} - EXIT!", input)); //NOI18N
424 public Gender enterGender (final String message) {
426 this.getLogger().logTrace(MessageFormat.format("message={0} - CALLED!", message)); //NOI18N
429 char[] validChars = Gender.validChars();
432 //* NOISY-DEBUG: */ System.out.println(validChars);
434 char gender = this.enterChar(validChars, message);
436 // Now get a Gender instance back
437 Gender g = Gender.fromChar(gender);
439 // g must not be null
440 assert (g instanceof Gender) : "g is not set."; //NOI18N
443 this.getLogger().logTrace(MessageFormat.format("g={0} - EXIT!", g)); //NOI18N
450 public int enterInt (final int minimum, final int maximum, final String message) {
452 this.getLogger().logTrace(MessageFormat.format("minimum={0},maximum={1},message={2} - CALLED!", minimum, maximum, message)); //NOI18N
454 // Minimum should not be below zero
455 assert (minimum >= 0) : MessageFormat.format("minimum={0} is below zero", minimum); //NOI18N
456 assert (maximum > minimum) : MessageFormat.format("maximum {0} is smaller than minimum {1}", maximum, minimum); //NOI18N
461 while ((input < minimum) || (input > maximum)) {
463 System.out.print(message);
465 // Read integer from user
466 input = this.readInt();
470 this.getLogger().logTrace(MessageFormat.format("input={0} - EXIT!", input)); //NOI18N
477 public String enterString (final int minLength, final int maxLength, final String message, final boolean allowEmpty) {
479 this.getLogger().logTrace(MessageFormat.format("minLength={0},maxLength={1},message={2}allowEmpty={3} - CALLED!", minLength, maxLength, message, allowEmpty)); //NOI18N
481 // Check on length, e.g. country codes are excactly 2 chars long
482 assert (maxLength >= minLength);
487 // Check if it is to short or to long
488 while (((null == input) || ((input.length() < minLength) && (!allowEmpty))) || ((input.length() > 0) && (input.length() < minLength) && (allowEmpty)) || ((input instanceof String) && (input.length() > maxLength))) {
490 System.out.print(message);
493 input = this.readString();
497 this.getLogger().logTrace(MessageFormat.format("input={0} - EXIT!", input)); //NOI18N
504 public SelectableMenuItem getMenuItem (final char accessKey, final String text) {
505 // Return a new console menu item
506 return new ConsoleMenuItem(accessKey, text);
510 public void init () {
512 this.getLogger().logTrace("CALLED!"); //NOI18N
514 // Init contact manager here
516 this.initContactManager();
517 } catch (final SQLException ex) {
519 this.abortProgramWithException(ex);
526 this.getLogger().logTrace("EXIT!"); //NOI18N
530 public void outputMessage (final String message) {
531 System.out.println(message);
535 public void show (final Contact contact) {
536 throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
540 public void showCurrentMenu () {
541 this.showMenu(this.getCurrentMenu());
545 public void showEntry (final SelectableMenuItem item) {
546 // Access key then text
547 this.outputMessage(MessageFormat.format("[{0}] {1}", item.getAccessKey(), item.getText())); //NOI18N
551 public void showWelcome () {
552 this.outputMessage(MessageFormat.format("Welcome to {0}", AddressbookApplication.printableTitle())); //NOI18N
553 this.outputMessage(""); //NOI18N
554 this.outputMessage("Copyright(c) 2015 by Roland Haeder, this is free software"); //NOI18N
557 this.getLogger().logDebug("Intro shown to user"); //NOI18N
561 public void userChooseChangeContactData (final Contact contact) throws UnhandledUserChoiceException {
563 this.getLogger().logTrace(MessageFormat.format("contact={0} CALLED!", contact)); //NOI18N
565 // Contact must not be null
566 if (null == contact) {
568 throw new NullPointerException("contact is null"); //NOI18N
571 // Ask the user for editing [name], [a]ddress or [other] data
572 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) ");
574 // Get manager and cast it
575 ContactFacade manager = (ContactFacade) this.getManager();
577 // TODO Get rid of this ugly switch block, too
579 case 'n': // Name data
580 manager.doChangeNameData(contact);
583 case 'a': // Address data
584 manager.doChangeAddressData(contact);
587 case 'o': // Other data
588 manager.doChangeOtherData(contact);
591 case 'x': // Exit this menu
592 // Ignored as it should go back
596 // TODO throw own exception
597 throw new UnhandledUserChoiceException(MessageFormat.format("Choice '{0}' not handled yet.", choice)); //NOI18N
601 this.getLogger().logTrace("EXIT!"); //NOI18N
605 * Reads one character
607 * @return A single character
609 private char readChar () {
611 String input = this.readString();
614 this.getLogger().logDebug(MessageFormat.format("input={0}", input)); //NOI18N
616 // This must be only one character
617 if (input.length() != 1) {
622 // Get char from first (and only) position
623 return input.charAt(0);
627 * Reads an integer (int) from user
629 * @return An integer number
631 private int readInt () {
632 // First read a string
633 String input = this.readString();
636 this.getLogger().logDebug(MessageFormat.format("input={0}", input)); //NOI18N
638 // Init number with invalid value
641 // Parse number, this can be risky
643 num = Integer.parseInt(input);
644 } catch (final NumberFormatException e) {
645 this.outputMessage("Bitte geben Sie nur Zahlen ein!");
646 this.getLogger().logWarning(MessageFormat.format("No numbers-only entered. input={0},message={1}", input, e.getMessage())); //NOI18N
650 this.getLogger().logTrace(MessageFormat.format("num={0} - EXIT!", num)); //NOI18N
652 // Return read number
657 * Reads a string from a scanner until RETURN is pressed
659 * @return Read string from scanner
661 private String readString () {
662 return this.scanner.nextLine();
666 protected final void fillMenuMap () {
668 this.getLogger().logTrace("CALLED!"); //NOI18N
670 // Initialize first (main) menu
671 Menu menu = new ConsoleMenu("main", this); //NOI18N
674 this.getMenus().put("main", menu); //NOI18N
677 this.getLogger().logTrace("EXIT!"); //NOI18N