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