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