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