]> git.mxchange.org Git - jaddressbook-share-lib.git/blob
9b574f3d0390d6757e61b0aa02318cc3818d9ff1
[jaddressbook-share-lib.git] /
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.database.frontend.contact;
18
19 import java.io.IOException;
20 import java.sql.SQLException;
21 import java.text.MessageFormat;
22 import java.util.Iterator;
23 import java.util.StringTokenizer;
24 import org.mxchange.addressbook.contact.book.BookContact;
25 import org.mxchange.addressbook.contact.user.UserContact;
26 import org.mxchange.addressbook.database.contact.AddressbookContactDatabaseConstants;
27 import org.mxchange.addressbook.exceptions.ContactAlreadyAddedException;
28 import org.mxchange.addressbook.manager.contact.AddressbookContactManager;
29 import org.mxchange.jcore.contact.Contact;
30 import org.mxchange.jcore.contact.Gender;
31 import org.mxchange.jcore.criteria.searchable.SearchCriteria;
32 import org.mxchange.jcore.criteria.searchable.SearchableCritera;
33 import org.mxchange.jcore.database.frontend.BaseDatabaseFrontend;
34 import org.mxchange.jcore.database.result.Result;
35 import org.mxchange.jcore.database.storage.Storeable;
36 import org.mxchange.jcore.exceptions.BadTokenException;
37 import org.mxchange.jcore.exceptions.UnsupportedDatabaseBackendException;
38
39 /**
40  * Stores and retrieves Contact instances
41  *
42  * @author Roland Haeder
43  */
44 public class AddressbookContactDatabaseFrontend extends BaseDatabaseFrontend implements AddressbookContactFrontend {
45
46         /**
47          * Constructor which accepts a contact manager
48          *
49          * @param manager Manager instance
50          */
51         public AddressbookContactDatabaseFrontend (final AddressbookContactManager manager) {
52                 // Call own constructor
53                 this();
54
55                 // Trace message
56                 this.getLogger().trace(MessageFormat.format("manager={0} - CALLED!", manager)); //NOI18N
57
58                 // Manager instance must not be null
59                 if (manager == null) {
60                         // Abort here
61                         throw new NullPointerException("manager is null"); //NOI18N
62                 }
63
64                 // Set contact manager
65                 this.setContactManager(manager);
66         }
67
68         /**
69          * Basic constrcutor
70          * @throws org.mxchange.jcore.exceptions.UnsupportedDatabaseBackendException If the database backend is not supported
71          * @throws java.sql.SQLException Any SQL exception from e.g. MySQL connector
72          */
73         protected AddressbookContactDatabaseFrontend () throws UnsupportedDatabaseBackendException, SQLException {
74                 // Trace message
75                 this.getLogger().trace("CALLED!"); //NOI18N
76
77                 // Set "table" name
78                 this.setTableName("contacts"); //NOI18N
79
80                 // Initalize backend
81                 this.initBackend();
82         }
83
84         /**
85          * Adds given contact instance to database
86          *
87          * @param contact Contact instance
88          * @throws org.mxchange.addressbook.exceptions.ContactAlreadyAddedException If the contact instance is already found
89          */
90         @Override
91         public void addContact (final Contact contact) throws ContactAlreadyAddedException {
92                 // Trace message
93                 this.getLogger().trace("CALLED!"); //NOI18N
94
95                 // Make sure the contact is set
96                 if (contact == null) {
97                         // Abort here
98                         throw new NullPointerException("contact is null"); //NOI18N
99                 }
100
101                 try {
102                         // First check if the contact is there
103                         if (this.isContactFound(contact)) {
104                                 // Already there
105                                 throw new ContactAlreadyAddedException(contact);
106                         }
107
108                         // Then add it
109                         this.getBackend().store((Storeable) contact);
110                 } catch (final IOException | BadTokenException ex) {
111                         // Abort here
112                         this.abortProgramWithException(ex);
113                 }
114
115                 // Trace message
116                 this.getLogger().trace("CALLED!"); //NOI18N
117         }
118
119         /**
120          * Shuts down the database layer
121          */
122         @Override
123         public void doShutdown () {
124                 // Trace message
125                 this.getLogger().trace("CALLED!"); //NOI18N
126
127                 // Shutdown backend
128                 this.getBackend().doShutdown();
129
130                 // Trace message
131                 this.getLogger().trace("EXIT!"); //NOI18N
132         }
133
134         /**
135          * Some "getter" for total contact count
136          *
137          * @return Total contact count
138          */
139         @Override
140         public int getContactsCount () throws SQLException {
141                 // And deligate to backend
142                 return this.getBackend().getTotalCount(); //NOI18N
143         }
144
145         /**
146          * Some "getter" for own contact instance
147          *
148          * @return Own contact instance
149          */
150         @Override
151         public Contact getOwnContact () {
152                 // Trace message
153                 this.getLogger().trace("CALLED!"); //NOI18N
154
155                 // Get row index back from backend
156                 int rowIndex = this.getBackend().getRowIndexFromColumn(AddressbookContactDatabaseConstants.COLUMN_NAME_OWN_CONTACT, true);
157
158                 // Debug message
159                 this.getLogger().debug(MessageFormat.format("rowIndex={0}", rowIndex));
160
161                 // Init instance
162                 Contact contact = null;
163
164                 try {
165                         // Now simply read the row
166                         contact = (Contact) this.getBackend().readRow(rowIndex);
167                 } catch (final BadTokenException ex) {
168                         // Bad token found
169                         this.abortProgramWithException(ex);
170                 }
171
172                 // Trace message
173                 this.getLogger().trace(MessageFormat.format("contact={0} - EXIT!", contact));
174
175                 // Return it
176                 return contact;
177         }
178
179         /**
180          * Checks if given Contact is found
181          * 
182          * @param contact Contact instance to check
183          * @return Whether the given Contact instance is found
184          */
185         @Override
186         public boolean isContactFound (final Contact contact) throws BadTokenException {
187                 // Trace message
188                 this.getLogger().trace(MessageFormat.format("contact={0} - CALLED!", contact)); //NOI18N
189
190                 // contact should not be null
191                 if (contact == null) {
192                         // Abort here
193                         throw new NullPointerException("contact is null"); //NOI18N
194                 }
195
196                 // Default is not found
197                 boolean isFound = false;
198
199                 // Start iteration
200                 Iterator<? extends Storeable> iterator = this.getBackend().iterator();
201
202                 // Check all entries
203                 while (iterator.hasNext()) {
204                         // Get next element
205                         Contact c = (Contact) iterator.next();
206
207                         // Debug message
208                         this.getLogger().debug(MessageFormat.format("c={0},contact={1}", c, contact)); //NOI18N
209
210                         // Is it added?
211                         if (c.equals(contact)) {
212                                 // Is found
213                                 isFound = true;
214                                 break;
215                         }
216                 }
217
218                 // Trace message
219                 this.getLogger().trace(MessageFormat.format("isFound={0} - EXIT!", isFound)); //NOI18N
220
221                 // Return it
222                 return isFound;
223         }
224
225         /**
226          * Checks whether own contact is found in database
227          *
228          * @return Whether own contact is found
229          */
230         @Override
231         public boolean isOwnContactFound () throws SQLException {
232                 // Get search criteria instance
233                 SearchableCritera critera = new SearchCriteria();
234
235                 // Add condition
236                 critera.addCriteria(AddressbookContactDatabaseConstants.COLUMN_NAME_OWN_CONTACT, true);
237
238                 // Get result
239                 Result<? extends Storeable> result = this.getBackend().doSelectByCriteria(critera);
240
241                 // Deligate this call to backend
242                 return result.hasNext();
243         }
244
245         /**
246          * Parses given line from database backend into a Storeable instance. Please
247          * note that not all backends need this.
248          *
249          * @param line Line from database backend
250          * @return A Storeable instance
251          */
252         @Override
253         public Storeable parseLineToStoreable (final String line) throws BadTokenException {
254                 // Trace message
255                 this.getLogger().trace(MessageFormat.format("line={0} - CALLED!", line)); //NOI18N
256
257                 // Call inner method
258                 Contact contact = this.parseLineToContact(line);
259
260                 // Debug message
261                 this.getLogger().debug(MessageFormat.format("contact={0}", contact));
262
263                 // Return it
264                 return (Storeable) contact;
265         }
266
267         /**
268          * Reads a single row and parses it to a contact instance
269          *
270          * @param rowIndex Row index (also how much to skip)
271          * @return Contact instance
272          */
273         @Override
274         public Contact readSingleContact (final int rowIndex) {
275                 try {
276                         // Deligate this to backend instance
277                         return (Contact) this.getBackend().readRow(rowIndex);
278                 } catch (final BadTokenException ex) {
279                         // Bad token found
280                         this.abortProgramWithException(ex);
281                 }
282
283                 // Bad state, should not be reached
284                 throw new IllegalStateException("This should not be reached");
285         }
286
287         /**
288          * Parses given line and creates a Contact instance
289          * 
290          * @param line Raw line to parse
291          *
292          * @return Contact instance
293          */
294         private Contact parseLineToContact (final String line) throws BadTokenException {
295                 // Trace message
296                 this.getLogger().trace(MessageFormat.format("line={0} - CALLED!", line)); //NOI18N
297
298                 // Init A lot variables
299                 Long num = null;
300                 Boolean bool = null;
301                 Gender gender = null;
302                 int count = 0;
303                 Contact contact = null;
304
305                 // Debug message
306                 this.getLogger().debug(MessageFormat.format("line={0}", line)); //NOI18N
307
308                 // Then tokenize it
309                 // @TODO Move this into separate method
310                 StringTokenizer tokenizer = new StringTokenizer(line, ";"); //NOI18N
311
312                 // Reset variables
313                 count = 0;
314                 contact = null;
315
316                 // The tokens are now available, so get all
317                 while (tokenizer.hasMoreElements()) {
318                         // Reset variables
319                         num = null;
320                         bool = null;
321
322                         // Get next token
323                         String token = tokenizer.nextToken();
324
325                         // If char " is at pos 2 (0,1,2), then cut it of there
326                         if ((token.charAt(0) != '"') && (token.charAt(2) == '"')) {
327                                 // UTF-8 writer characters found
328                                 token = token.substring(2);
329                         }
330
331                         // Debug message
332                         this.getLogger().debug(MessageFormat.format("token={0}", token)); //NOI18N
333
334                         // Verify token, it must have double-quotes on each side
335                         if ((!token.startsWith("\"")) || (!token.endsWith("\""))) { //NOI18N
336                                 // Something bad was read
337                                 throw new BadTokenException(token, count); //NOI18N
338                         }
339
340                         // All fine, so remove it
341                         String strippedToken = token.substring(1, token.length() - 1);
342
343                         // Is the string's content "null"?
344                         if (strippedToken.equals("null")) { //NOI18N
345                                 // Debug message
346                                 this.getLogger().debug(MessageFormat.format("strippedToken={0} - NULL!", strippedToken)); //NOI18N
347
348                                 // This needs to be set to null
349                                 strippedToken = null;
350                         }
351
352                         // Debug message
353                         this.getLogger().debug(MessageFormat.format("strippedToken={0}", strippedToken)); //NOI18N
354
355                         // Now, let's try a number check, if no null
356                         if (strippedToken != null) {
357                                 // Okay, no null, maybe the string bears a decimal number?
358                                 try {
359                                         num = Long.valueOf(strippedToken);
360
361                                         // Debug message
362                                         this.getLogger().debug(MessageFormat.format("strippedToken={0} - NUMBER!", strippedToken)); //NOI18N
363                                 } catch (final NumberFormatException ex) {
364                                         // No number, then set default
365                                         num = null;
366                                 }
367                         }
368
369                         // Now, let's try a boolean check, if no null
370                         if ((strippedToken != null) && (num == null) && ((strippedToken.equals("true")) || (strippedToken.equals("false")))) { //NOI18N
371                                 // Debug message
372                                 this.getLogger().debug(MessageFormat.format("strippedToken={0} - BOOLEAN!", strippedToken)); //NOI18N
373
374                                 // parseBoolean() is relaxed, so no exceptions
375                                 bool = Boolean.valueOf(strippedToken);
376                         }
377
378                         // Debug message
379                         this.getLogger().debug(MessageFormat.format("strippedToken={0},num={1},bool={2}", strippedToken, num, bool)); //NOI18N
380
381                         // Now, let's try a gender check, if no null
382                         if ((strippedToken != null) && (num == null) && (bool == null) && (Gender.valueOf(strippedToken) instanceof Gender)) { //NOI18N
383                                 // Get first character
384                                 gender = Gender.valueOf(strippedToken);
385
386                                 // Debug message
387                                 this.getLogger().debug(MessageFormat.format("strippedToken={0},gender={1}", strippedToken, gender)); //NOI18N
388
389                                 // This instance must be there
390                                 assert (gender instanceof Gender) : MessageFormat.format("gender is not set by Gender.fromChar({0})", strippedToken); //NOI18N
391                         }
392
393                         // Now it depends on the counter which position we need to check
394                         switch (count) {
395                                 case 0: // isOwnContact
396                                         assert ((bool instanceof Boolean));
397
398                                         // Debug message
399                                         this.getLogger().debug(MessageFormat.format("bool={0}", bool)); //NOI18N
400
401                                         // Is it own contact?
402                                         if (true == bool) {
403                                                 // Debug message
404                                                 this.getLogger().debug("Creating UserContact object ..."); //NOI18N
405
406                                                 // Own entry
407                                                 contact = new UserContact();
408                                         } else {
409                                                 // Debug message
410                                                 this.getLogger().debug("Creating BookContact object ..."); //NOI18N
411
412                                                 // Other contact
413                                                 contact = new BookContact();
414                                         }
415                                         break;
416
417                                 case 1: // Gender
418                                         assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N
419
420                                         // Update data
421                                         contact.setGender(gender);
422                                         break;
423
424                                 case 2: // Surname
425                                         assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N
426                                         assert (gender instanceof Gender) : "gender instance is not set"; //NOI18N
427
428                                         // Update data
429                                         contact.setSurname(strippedToken);
430                                         break;
431
432                                 case 3: // Family name
433                                         assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N
434                                         assert (gender instanceof Gender) : "gender instance is not set"; //NOI18N
435
436                                         // Update data
437                                         contact.setFamilyName(strippedToken);
438                                         break;
439
440                                 case 4: // Company name
441                                         assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N
442                                         assert (gender instanceof Gender) : "gender instance is not set"; //NOI18N
443
444                                         // Update data
445                                         contact.setCompanyName(strippedToken);
446                                         break;
447
448                                 case 5: // Street number
449                                         assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N
450
451                                         // Update data
452                                         contact.setHouseNumber(num);
453                                         break;
454
455                                 case 6: // ZIP code
456                                         assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N
457
458                                         // Update data
459                                         contact.setZipCode(num);
460                                         break;
461
462                                 case 7: // City name
463                                         assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N
464
465                                         // Update data
466                                         contact.setCity(strippedToken);
467                                         break;
468
469                                 case 8: // Country code
470                                         assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N
471
472                                         // Update data
473                                         contact.setCountryCode(strippedToken);
474                                         break;
475
476                                 case 9: // Phone number
477                                         assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N
478
479                                         // Update data
480                                         contact.setPhoneNumber(strippedToken);
481                                         break;
482
483                                 case 10: // Fax number
484                                         assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N
485
486                                         // Update data
487                                         contact.setFaxNumber(strippedToken);
488                                         break;
489
490                                 case 11: // Cellphone number
491                                         assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N
492
493                                         // Update data
494                                         contact.setCellphoneNumber(strippedToken);
495                                         break;
496
497                                 case 12: // Email address
498                                         assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N
499
500                                         // Update data
501                                         contact.setEmailAddress(strippedToken);
502                                         break;
503
504                                 case 13: // Birthday
505                                         assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N
506
507                                         // Update data
508                                         contact.setBirthday(strippedToken);
509                                         break;
510
511                                 case 14: // Comment
512                                         assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N
513
514                                         // Update data
515                                         contact.setComment(strippedToken);
516                                         break;
517
518                                 default: // New data entry
519                                         this.getLogger().warn(MessageFormat.format("Will not handle unknown data {0} at index {1}", strippedToken, count)); //NOI18N
520                                         break;
521                         }
522
523                         // Increment counter for next round
524                         count++;
525                 }
526
527                 // Trace message
528                 this.getLogger().trace(MessageFormat.format("contact={0} - EXIT!", contact)); //NOI18N
529
530                 // Return finished instance
531                 return contact;
532         }
533 }