]> git.mxchange.org Git - jaddressbook-lib.git/blob - Addressbook/src/org/mxchange/addressbook/client/console/ConsoleClient.java
5e8f7dab61d57806641088b4c288b73ba02a31a0
[jaddressbook-lib.git] / Addressbook / src / org / mxchange / addressbook / client / console / ConsoleClient.java
1 /*
2  * Copyright (C) 2015 Roland Haeder
3  *
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.
8  *
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.
13  *
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/>.
16  */
17 package org.mxchange.addressbook.client.console;
18
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.client.AddressbookClient;
24 import org.mxchange.addressbook.client.BaseAddressbookClient;
25 import org.mxchange.jcore.contact.Contact;
26 import org.mxchange.jcore.contact.Gender;
27 import org.mxchange.addressbook.contact.user.UserContact;
28 import org.mxchange.addressbook.exceptions.ContactAlreadyAddedException;
29 import org.mxchange.addressbook.manager.contact.ManageableAddressbookContact;
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.jcore.application.Application;
36 import org.mxchange.jcore.exceptions.UnhandledUserChoiceException;
37
38 /**
39  * A client for the console
40  *
41  * @author Roland Haeder
42  */
43 public class ConsoleClient extends BaseAddressbookClient implements AddressbookClient {
44
45         /**
46          * Scanner instance for reading data from console input
47          */
48         private final Scanner scanner;
49
50         /**
51          * Parameterless constructor
52          *
53          * @param application An instance of an Application class
54          */
55         public ConsoleClient (final Application application) {
56                 // Trace message
57                 this.getLogger().trace(MessageFormat.format("application={0} - CALLED!", application)); //NOI18N
58
59                 // Set application instance
60                 this.setApplication(application);
61
62                 // Init scanner instance
63                 this.scanner = new Scanner(System.in, "UTF-8"); //NOI18N
64
65                 // Trace message
66                 this.getLogger().trace("EXIT!"); //NOI18N
67         }
68
69         /**
70          * Displays a textual address "box" of given contact
71          *
72          * @param contact Contact to show address for
73          */
74         @Override
75         public void displayAddressBox (final Contact contact) {
76                 // Trace message
77                 this.getLogger().trace(MessageFormat.format("contact={0} - CALLED!", contact)); //NOI18N
78
79                 // Is it null?
80                 if (contact == null) {
81                         // Abort here
82                         throw new NullPointerException("contact is null"); //NOI18N
83                 }
84
85                 // Simple display ...
86                 this.outputMessage(MessageFormat.format("Strasse, PLZ Ort, Land: {0}\n{1} {2}\n{3}", contact.getStreet(), contact.getZipCode(), contact.getCity(), contact.getCountryCode()));
87
88                 // Trace message
89                 this.getLogger().trace("EXIT!"); //NOI18N
90         }
91
92         /**
93          * Displays a textual name "box" of given contact
94          *
95          * @param contact Contact to show name for
96          */
97         @Override
98         public void displayNameBox (final Contact contact) {
99                 // Trace message
100                 this.getLogger().trace(MessageFormat.format("contact={0} - CALLED!", contact)); //NOI18N
101
102                 // Is it null?
103                 if (contact == null) {
104                         // Abort here
105                         throw new NullPointerException("contact is null"); //NOI18N
106                 }
107
108                 // Get translated gender as the user may want to see "Mr.", "Mrs."
109                 String gender = contact.getTranslatedGender();
110
111                 // Get company name
112                 String companyName = contact.getCompanyName();
113
114                 // If it is empty/null, then assume private contact
115                 if ((companyName == null) || (companyName.isEmpty())) {
116                         // Now put all together: gender, surname, family name
117                         // @todo Use mask
118                         this.outputMessage(MessageFormat.format("Anrede, Vorname, Name: {0} {1} {2}", gender, contact.getSurname(), contact.getFamilyName()));
119                 } else {
120                         // Company contact
121                         this.outputMessage(MessageFormat.format("Firma: {0}\nAnsprechpartner: {1} {2} {3}", companyName, gender, contact.getSurname(), contact.getFamilyName()));
122                 }
123
124                 // Trace message
125                 this.getLogger().trace("EXIT!"); //NOI18N
126         }
127
128         /**
129          * Displays a textual other data "box" of given contact
130          *
131          * @param contact Contact to show other data for
132          */
133         @Override
134         public void displayOtherDataBox (final Contact contact) {
135                 // Trace message
136                 this.getLogger().trace(MessageFormat.format("contact={0} - CALLED!", contact)); //NOI18N
137
138                 // Is it null?
139                 if (contact == null) {
140                         // Abort here
141                         throw new NullPointerException("contact is null"); //NOI18N
142                 }
143
144                 // Cellphone and such ...
145                 this.outputMessage(MessageFormat.format("Telefonnumer: {0}\nFaxnummer: {1}\nHandy: {2}\nKommentar:\n{3}", contact.getPhoneNumber(), contact.getFaxNumber(), contact.getCellphoneNumber(), contact.getComment()));
146
147                 // Trace message
148                 this.getLogger().trace("EXIT!"); //NOI18N
149         }
150
151         @Override
152         public void doChangeOwnAddressData (final Contact contact) {
153                 // Trace message
154                 this.getLogger().trace(MessageFormat.format("contact={0} - CALLED!", contact)); //NOI18N
155
156                 // Is it null?
157                 if (contact == null) {
158                         // Abort here
159                         throw new NullPointerException("contact is null"); //NOI18N
160                 }
161
162                 // Make sure it is own contact
163                 if (!contact.isOwnContact()) {
164                         // Not own contact
165                         throw new IllegalArgumentException("Contact is not own data."); //NOI18N
166                 }
167
168                 // Get manager and cast it
169                 ManageableAddressbookContact manager = (ManageableAddressbookContact) this.getManager();
170
171                 // Own street and number
172                 String streetNumber = manager.enterOwnStreet();
173
174                 // Get zip code
175                 int zipCode = manager.enterOwnZipCode();
176
177                 // Get city name
178                 String city = manager.enterOwnCity();
179
180                 // Get country code
181                 String countryCode = manager.enterOwnCountryCode();
182
183                 // Update address data
184                 contact.setStreet(streetNumber);
185                 contact.setZipCode(zipCode);
186                 contact.setCity(city);
187                 contact.setCountryCode(countryCode);
188
189                 // Trace message
190                 this.getLogger().trace("EXIT!"); //NOI18N
191         }
192
193         @Override
194         public void doChangeOwnNameData (final Contact contact) {
195                 // Trace message
196                 this.getLogger().trace(MessageFormat.format("contact={0} - CALLED!", contact)); //NOI18N
197
198                 // Is it null?
199                 if (contact == null) {
200                         // Abort here
201                         throw new NullPointerException("contact is null"); //NOI18N
202                 }
203
204                 // Make sure it is own contact
205                 if (!contact.isOwnContact()) {
206                         // Not own contact
207                         throw new IllegalArgumentException("Contact is not own data."); //NOI18N
208                 }
209
210                 // Get manager and cast it
211                 ManageableAddressbookContact manager = (ManageableAddressbookContact) this.getManager();
212
213                 // Gender:
214                 Gender gender = manager.enterOwnGender();
215
216                 // Surname
217                 String surname = manager.enterOwnSurname();
218
219                 // Family name
220                 String familyName = manager.enterOwnFamilyName();
221
222                 // And company
223                 String companyName = manager.enterOwnCompanyName();
224
225                 // Update contact instance
226                 contact.setGender(gender);
227                 contact.setSurname(surname);
228                 contact.setFamilyName(familyName);
229                 contact.setCompanyName(companyName);
230
231                 // Trace message
232                 this.getLogger().trace("EXIT!"); //NOI18N
233         }
234
235         @Override
236         public void doChangeOwnOtherData (final Contact contact) {
237                 // Trace message
238                 this.getLogger().trace(MessageFormat.format("contact={0} - CALLED!", contact)); //NOI18N
239
240                 // Is it null?
241                 if (contact == null) {
242                         // Abort here
243                         throw new NullPointerException("contact is null"); //NOI18N
244                 }
245
246                 // Make sure it is own contact
247                 if (!contact.isOwnContact()) {
248                         // Not own contact
249                         throw new IllegalArgumentException("Contact is not own data."); //NOI18N
250                 }
251
252                 // Get manager and cast it
253                 ManageableAddressbookContact manager = (ManageableAddressbookContact) this.getManager();
254
255                 // Phone number
256                 String phoneNumber = manager.enterOwnPhoneNumber();
257
258                 // Phone number
259                 String cellphonePhoneNumber = manager.enterOwnCellNumber();
260
261                 // Fax number
262                 String faxNumber = manager.enterOwnFaxNumber();
263
264                 // Email address
265                 String email = manager.enterOwnEmailAddress();
266
267                 // Comment
268                 String comment = manager.enterOwnComment();
269
270                 // Update contact instance
271                 contact.setPhoneNumber(phoneNumber);
272                 contact.setCellphoneNumber(cellphonePhoneNumber);
273                 contact.setFaxNumber(faxNumber);
274                 contact.setEmailAddress(email);
275                 contact.setComment(comment);
276
277                 // Trace message
278                 this.getLogger().trace("EXIT!"); //NOI18N
279         }
280
281         @Override
282         public Contact doEnterOwnData () {
283                 // Trace message
284                 this.getLogger().trace("CALLED!"); //NOI18N
285
286                 // Get manager and cast it
287                 ManageableAddressbookContact manager = (ManageableAddressbookContact) this.getManager();
288
289                 // First ask for gender
290                 Gender gender = manager.enterOwnGender();
291
292                 // 2nd for surname
293                 String surname = manager.enterOwnSurname();
294
295                 // And 3rd for family name
296                 String familyName = manager.enterOwnFamilyName();
297
298                 // Company name ...
299                 String companyName = manager.enterOwnCompanyName();
300
301                 // Construct UserContact instance
302                 Contact contact = new UserContact(gender, surname, familyName, companyName);
303
304                 // Trace message
305                 this.getLogger().trace(MessageFormat.format("contact={0} - EXIT!", contact)); //NOI18N
306
307                 // And return object
308                 return contact;
309         }
310
311         /**
312          * Shutdown this client
313          */
314         @Override
315         public void doShutdown () {
316                 // Trace message
317                 this.getLogger().trace("CALLED!"); //NOI18N
318
319                 // Parent call
320                 super.doShutdown();
321
322                 // @TODO Add other shutdown stuff
323
324                 // Trace message
325                 this.getLogger().trace("EXIT!"); //NOI18N
326         }
327
328         @Override
329         public void doUserMenuChoice () throws UnhandledUserChoiceException {
330                 // Trace message
331                 this.getLogger().trace("CALLED!"); //NOI18N
332
333                 // Get all access keys from menu
334                 char[] accessKeys = MenuTools.getAccessKeysFromMenuMap(this.getMenus(), this.getCurrentMenu());
335
336                 // Output textural message and ask for a char as input
337                 char choice = this.enterChar(accessKeys, "Bitte Auswahl eingeben (0=Programm beenden): ");
338
339                 // Get manager and cast it
340                 ManageableAddressbookContact manager = (ManageableAddressbookContact) this.getManager();
341
342                 // @TODO Rewrite this ugly switch() block
343                 switch (choice) {
344                         case '1':
345                                 try {
346                                         // Enter/add own data
347                                         manager.doEnterOwnData();
348                                 } catch (final ContactAlreadyAddedException ex) {
349                                         // Already added
350                                         this.outputMessage("Sie haben bereits Ihre eigenen Daten eingegeben.");
351                                 }
352                                 break;
353
354                         case '2': // Change own data
355                                 manager.doChangeOwnData();
356                                 break;
357
358                         case '3': // Add new addess
359                                 manager.doAddOtherAddress();
360                                 break;
361
362                         case '4': // List contacts
363                                 manager.doListContacts();
364                                 break;
365
366                         case '5': // Search addresses
367                                 manager.doSearchContacts();
368                                 break;
369
370                         case '6': // Change other addess
371                                 manager.doChangeOtherAddress();
372                                 break;
373
374                         case '7': // Delete other address
375                                 manager.doDeleteOtherAddress();
376                                 break;
377
378                         case '0': // Program exit
379                                 this.getApplication().doShutdown();
380                                 break;
381
382                         default:
383                                 // @TODO throw own exception
384                                 throw new UnhandledUserChoiceException(MessageFormat.format("Choice '{0}' not handled yet.", choice)); //NOI18N
385                 }
386
387                 // Trace message
388                 this.getLogger().trace("EXIT!"); //NOI18N
389         }
390
391         /**
392          * Asks the the user to enter a single character which must match validChars
393          *
394          * @param       validChars Valid chars that are accepted
395          * @param       message Message to user
396          * @return      Allowed character
397          */
398         @Override
399         public char enterChar (final char[] validChars, final String message) {
400                 // Trace message
401                 this.getLogger().trace(MessageFormat.format("validChars={0},message={1} - CALLED!", Arrays.toString(validChars), message)); //NOI18N
402
403                 // The validChars must not null be null and filled with at least one char
404                 if (validChars == null) {
405                         // Is null
406                         throw new NullPointerException("validChars is null"); //NOI18N
407                 } else if (validChars.length == 0) {
408                         // Is not filled
409                         throw new IllegalArgumentException("validChars is not filled."); //NOI18N
410                 }
411
412                 char input = 0;
413
414                 // Sort array, else binarySearch() won't work
415                 Arrays.sort(validChars);
416
417                 // Keep asking until valid char has been entered
418                 while (Arrays.binarySearch(validChars, input) < 0) {
419                         // Output message
420                         System.out.print(message);
421
422                         // Read char
423                         input = this.readChar();
424                 }
425
426                 // Trace message
427                 this.getLogger().trace(MessageFormat.format("input={0} - EXIT!", input)); //NOI18N
428
429                 // Return read char
430                 return input;
431         }
432
433         /**
434          * Asks the user to enter his/her gender
435          *
436          * @param message Message to the user
437          * @return Gender enum
438          */
439         @Override
440         public Gender enterGender (final String message) {
441                 // Trace message
442                 this.getLogger().trace(MessageFormat.format("message={0} - CALLED!", message)); //NOI18N
443
444                 // Get valid chars
445                 char[] validChars = Gender.validChars();
446
447                 // Debug message
448                 //* NOISY-DEBUG: */ System.out.println(validChars);
449                 // Call inner method
450                 char gender = this.enterChar(validChars, message);
451
452                 // Now get a Gender instance back
453                 Gender g = Gender.fromChar(gender);
454
455                 // g must not be null
456                 assert(g instanceof Gender) : "g is not set."; //NOI18N
457
458                 // Trace message
459                 this.getLogger().trace(MessageFormat.format("g={0} - EXIT!", g)); //NOI18N
460
461                 // Return it
462                 return g;
463         }
464
465         /**
466          * Reads an integer (int) with a textural message from the user
467          *
468          * @param minimum Minimum allowed number
469          * @param maximum Maximum allowed number
470          * @param message Messager to display in console
471          * @return
472          */
473         @Override
474         public int enterInt (final int minimum, final int maximum, final String message) {
475                 // Trace message
476                 this.getLogger().trace(MessageFormat.format("minimum={0},maximum={1},message={2} - CALLED!", minimum, maximum, message)); //NOI18N
477
478                 // Minimum should not be below zero
479                 assert (minimum >= 0);
480                 assert (maximum > minimum);
481
482                 // Init input
483                 int input = -1;
484
485                 while ((input < minimum) || (input > maximum)) {
486                         // Output message
487                         System.out.print(message);
488
489                         // Read integer from user
490                         input = this.readInt();
491                 }
492
493                 // Trace message
494                 this.getLogger().trace(MessageFormat.format("input={0} - EXIT!", input)); //NOI18N
495
496                 // Return it
497                 return input;
498         }
499
500         /**
501          * Reads a string of minimum and maximum length from the user
502          *
503          * @param minLength     Minimum length of the string to read
504          * @param maxLength     Maximum length of the string to read
505          * @param message       Message to user
506          * @param allowEmpty Whether to allow empty string
507          * @return Entered string by user or null for empty strings
508          */
509         @Override
510         public String enterString (final int minLength, final int maxLength, final String message, final boolean allowEmpty) {
511                 // Trace message
512                 this.getLogger().trace(MessageFormat.format("minLength={0},maxLength={1},message={2}allowEmpty={3} - CALLED!", minLength, maxLength, message, allowEmpty)); //NOI18N
513
514                 // Check on length, e.g. country codes are excactly 2 chars long
515                 assert (maxLength >= minLength);
516
517                 // Init input
518                 String input = null;
519
520                 // Check if it is to short or to long
521                 while (((input == null) || ((input.length() < minLength) && (!allowEmpty))) || ((input.length() > 0) && (input.length() < minLength) && (allowEmpty)) || ((input instanceof String) && (input.length() > maxLength))) {
522                         // Output message
523                         System.out.print(message);
524
525                         // Read line
526                         input = this.readString();
527                 }
528
529                 // Trace message
530                 this.getLogger().trace(MessageFormat.format("input={0} - EXIT!", input)); //NOI18N
531
532                 // Return it
533                 return input;
534         }
535
536         /**
537          * Returns a console menu item
538          *
539          * @param accessKey Key to access the menu
540          * @param text Text to show to user
541          * @return A SelectableMenuItem
542          * @todo Make sure the access key is unique
543          */
544         @Override
545         public SelectableMenuItem getMenuItem (final char accessKey, final String text) {
546                 // Return a new console menu item
547                 return new ConsoleMenuItem(accessKey, text);
548         }
549
550         /**
551          * Inizializes this client
552          */
553         @Override
554         public void init () {
555                 // Trace message
556                 this.getLogger().trace("CALLED!"); //NOI18N
557
558                 // Init contact manager here
559                 this.initContactManager();
560
561                 // Fill menu map
562                 this.fillMenuMap();
563
564                 // Trace message
565                 this.getLogger().trace("EXIT!"); //NOI18N
566         }
567
568         /**
569          * Displays textural message to the user
570          *
571          * @param message
572          */
573         @Override
574         public void outputMessage (final String message) {
575                 System.out.println(message);
576         }
577
578         /**
579          * Shows textural menu on console
580          */
581         @Override
582         public void showCurrentMenu () {
583                 this.showMenu(this.getCurrentMenu());
584         }
585
586         /**
587          * Shows given menu entry to user
588          *
589          * @param item Menu entry
590          */
591         @Override
592         public void showEntry (final SelectableMenuItem item) {
593                 // Access key then text
594                 this.outputMessage(MessageFormat.format("[{0}] {1}", item.getAccessKey(), item.getText()));
595         }
596
597         /**
598          * Shows a textural message to the user
599          */
600         @Override
601         public void showWelcome () {
602                 this.outputMessage(MessageFormat.format("Welcome to {0}", AddressbookApplication.printableTitle()));
603                 this.outputMessage("");
604                 this.outputMessage("Copyright(c) 2015 by Roland Haeder, this is free software");
605
606                 // Debug message
607                 this.getLogger().debug("Intro shown to user"); //NOI18N
608         }
609
610         @Override
611         public void userChooseChangeContactData (final Contact contact) throws UnhandledUserChoiceException {
612                 // Trace message
613                 this.getLogger().trace(MessageFormat.format("contact={0} CALLED!", contact)); //NOI18N
614
615                 // Contact must not be null
616                 if (contact == null) {
617                         // Abort here
618                         throw new NullPointerException("contact is null"); //NOI18N
619                 }
620
621                 // Ask the user for editing [name], [a]ddress or [other] data
622                 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) ");
623
624                 // Get manager and cast it
625                 ManageableAddressbookContact manager = (ManageableAddressbookContact) this.getManager();
626
627                 // @TODO Get rid of this ugly switch block, too
628                 switch (choice) {
629                         case 'n': // Name data
630                                 manager.doChangeNameData(contact);
631                                 break;
632
633                         case 'a': // Address data
634                                 manager.doChangeAddressData(contact);
635                                 break;
636
637                         case 'o': // Other data
638                                 manager.doChangeOtherData(contact);
639                                 break;
640
641                         case 'x': // Exit this menu
642                                 // Ignored as it should go back
643                                 break;
644
645                         default:
646                                 // @TODO throw own exception
647                                 throw new UnhandledUserChoiceException(MessageFormat.format("Choice '{0}' not handled yet.", choice)); //NOI18N
648                 }
649
650                 // Trace message
651                 this.getLogger().trace("EXIT!"); //NOI18N
652         }
653
654         /**
655          * Reads one character
656          *
657          * @return A single character
658          */
659         private char readChar () {
660                 // Read line
661                 String input = this.readString();
662
663                 // Debug message
664                 this.getLogger().debug(MessageFormat.format("input={0}", input)); //NOI18N
665
666                 // This must be only one character
667                 if (input.length() != 1) {
668                         // Return zero
669                         return 0;
670                 }
671
672                 // Get char from first (and only) position
673                 return input.charAt(0);
674         }
675
676         /**
677          * Reads an integer (int) from user
678          *
679          * @return An integer number
680          */
681         private int readInt () {
682                 // First read a string
683                 String input = this.readString();
684
685                 // Debug message
686                 this.getLogger().debug(MessageFormat.format("input={0}", input)); //NOI18N
687
688                 // Init number with invalid value
689                 int num = -1;
690
691                 // Parse number, this can be risky
692                 try {
693                         num = Integer.parseInt(input);
694                 } catch (final NumberFormatException e) {
695                         this.outputMessage("Bitte geben Sie nur Zahlen ein!");
696                         this.getLogger().warn(MessageFormat.format("No numbers-only entered. input={0},message={1}", input, e.getMessage())); //NOI18N
697                 }
698
699                 // Trace message
700                 this.getLogger().trace(MessageFormat.format("num={0} - EXIT!", num)); //NOI18N
701
702                 // Return read number
703                 return num;
704         }
705
706         /**
707          * Reads a string from a scanner until RETURN is pressed
708          *
709          * @return Read string from scanner
710          */
711         private String readString () {
712                 return this.scanner.nextLine();
713         }
714
715         /**
716          * Fills menu map with menu entries
717          */
718         @Override
719         protected final void fillMenuMap () {
720                 // Trace message
721                 this.getLogger().trace("CALLED!"); //NOI18N
722
723                 // Initialize first (main) menu
724                 Menu menu = new ConsoleMenu("main", this); //NOI18N
725
726                 // Add it
727                 this.getMenus().put("main", menu); //NOI18N
728
729                 // Trace message
730                 this.getLogger().trace("EXIT!"); //NOI18N
731         }
732 }