]> git.mxchange.org Git - jaddressbook-share-lib.git/blob
6fb08169feae3b14a3a8743e16f62b1d5f176acf
[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.database.frontend.BaseDatabaseFrontend;
32 import org.mxchange.jcore.database.storage.Storeable;
33 import org.mxchange.jcore.exceptions.BadTokenException;
34 import org.mxchange.jcore.exceptions.UnsupportedDatabaseBackendException;
35
36 /**
37  * Stores and retrieves Contact instances
38  *
39  * @author Roland Haeder
40  */
41 public class AddressbookContactDatabaseFrontend extends BaseDatabaseFrontend implements AddressbookContactFrontend {
42
43         /**
44          * Constructor which accepts a contact manager
45          *
46          * @param manager Manager instance
47          */
48         public AddressbookContactDatabaseFrontend (final AddressbookContactManager manager) {
49                 // Call own constructor
50                 this();
51
52                 // Trace message
53                 this.getLogger().trace(MessageFormat.format("manager={0} - CALLED!", manager)); //NOI18N
54
55                 // Manager instance must not be null
56                 if (manager == null) {
57                         // Abort here
58                         throw new NullPointerException("manager is null"); //NOI18N
59                 }
60
61                 // Set contact manager
62                 this.setContactManager(manager);
63         }
64
65         /**
66          * Basic constrcutor
67          */
68         protected AddressbookContactDatabaseFrontend () {
69                 // Trace message
70                 this.getLogger().trace("CALLED!"); //NOI18N
71
72                 // Set "table" name
73                 this.setTableName("contacts"); //NOI18N
74
75                 try {
76                         // Initalize backend
77                         this.initBackend();
78                 } catch (final UnsupportedDatabaseBackendException | SQLException ex) {
79                         // Abort program
80                         this.abortProgramWithException(ex);
81                 }
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                 // Deligate this call to backend
233                 return this.getBackend().isRowFound(AddressbookContactDatabaseConstants.COLUMN_NAME_OWN_CONTACT, true);
234         }
235
236         /**
237          * Parses given line from database backend into a Storeable instance. Please
238          * note that not all backends need this.
239          *
240          * @param line Line from database backend
241          * @return A Storeable instance
242          */
243         @Override
244         public Storeable parseLineToStoreable (final String line) throws BadTokenException {
245                 // Trace message
246                 this.getLogger().trace(MessageFormat.format("line={0} - CALLED!", line)); //NOI18N
247
248                 // Call inner method
249                 Contact contact = this.parseLineToContact(line);
250
251                 // Debug message
252                 this.getLogger().debug(MessageFormat.format("contact={0}", contact));
253
254                 // Return it
255                 return (Storeable) contact;
256         }
257
258         /**
259          * Reads a single row and parses it to a contact instance
260          *
261          * @param rowIndex Row index (also how much to skip)
262          * @return Contact instance
263          */
264         @Override
265         public Contact readSingleContact (final int rowIndex) {
266                 try {
267                         // Deligate this to backend instance
268                         return (Contact) this.getBackend().readRow(rowIndex);
269                 } catch (final BadTokenException ex) {
270                         // Bad token found
271                         this.abortProgramWithException(ex);
272                 }
273
274                 // Bad state, should not be reached
275                 throw new IllegalStateException("This should not be reached");
276         }
277
278         /**
279          * Parses given line and creates a Contact instance
280          * 
281          * @param line Raw line to parse
282          *
283          * @return Contact instance
284          */
285         private Contact parseLineToContact (final String line) throws BadTokenException {
286                 // Trace message
287                 this.getLogger().trace(MessageFormat.format("line={0} - CALLED!", line)); //NOI18N
288
289                 // Init A lot variables
290                 Long num = null;
291                 Boolean bool = null;
292                 Gender gender = null;
293                 int count = 0;
294                 Contact contact = null;
295
296                 // Debug message
297                 this.getLogger().debug(MessageFormat.format("line={0}", line)); //NOI18N
298
299                 // Then tokenize it
300                 // @TODO Move this into separate method
301                 StringTokenizer tokenizer = new StringTokenizer(line, ";"); //NOI18N
302
303                 // Reset variables
304                 count = 0;
305                 contact = null;
306
307                 // The tokens are now available, so get all
308                 while (tokenizer.hasMoreElements()) {
309                         // Reset variables
310                         num = null;
311                         bool = null;
312
313                         // Get next token
314                         String token = tokenizer.nextToken();
315
316                         // If char " is at pos 2 (0,1,2), then cut it of there
317                         if ((token.charAt(0) != '"') && (token.charAt(2) == '"')) {
318                                 // UTF-8 writer characters found
319                                 token = token.substring(2);
320                         }
321
322                         // Debug message
323                         this.getLogger().debug(MessageFormat.format("token={0}", token)); //NOI18N
324
325                         // Verify token, it must have double-quotes on each side
326                         if ((!token.startsWith("\"")) || (!token.endsWith("\""))) { //NOI18N
327                                 // Something bad was read
328                                 throw new BadTokenException(token, count); //NOI18N
329                         }
330
331                         // All fine, so remove it
332                         String strippedToken = token.substring(1, token.length() - 1);
333
334                         // Is the string's content "null"?
335                         if (strippedToken.equals("null")) { //NOI18N
336                                 // Debug message
337                                 this.getLogger().debug(MessageFormat.format("strippedToken={0} - NULL!", strippedToken)); //NOI18N
338
339                                 // This needs to be set to null
340                                 strippedToken = null;
341                         }
342
343                         // Debug message
344                         this.getLogger().debug(MessageFormat.format("strippedToken={0}", strippedToken)); //NOI18N
345
346                         // Now, let's try a number check, if no null
347                         if (strippedToken != null) {
348                                 // Okay, no null, maybe the string bears a decimal number?
349                                 try {
350                                         num = Long.valueOf(strippedToken);
351
352                                         // Debug message
353                                         this.getLogger().debug(MessageFormat.format("strippedToken={0} - NUMBER!", strippedToken)); //NOI18N
354                                 } catch (final NumberFormatException ex) {
355                                         // No number, then set default
356                                         num = null;
357                                 }
358                         }
359
360                         // Now, let's try a boolean check, if no null
361                         if ((strippedToken != null) && (num == null) && ((strippedToken.equals("true")) || (strippedToken.equals("false")))) { //NOI18N
362                                 // Debug message
363                                 this.getLogger().debug(MessageFormat.format("strippedToken={0} - BOOLEAN!", strippedToken)); //NOI18N
364
365                                 // parseBoolean() is relaxed, so no exceptions
366                                 bool = Boolean.valueOf(strippedToken);
367                         }
368
369                         // Debug message
370                         this.getLogger().debug(MessageFormat.format("strippedToken={0},num={1},bool={2}", strippedToken, num, bool)); //NOI18N
371
372                         // Now, let's try a gender check, if no null
373                         if ((strippedToken != null) && (num == null) && (bool == null) && (Gender.valueOf(strippedToken) instanceof Gender)) { //NOI18N
374                                 // Get first character
375                                 gender = Gender.valueOf(strippedToken);
376
377                                 // Debug message
378                                 this.getLogger().debug(MessageFormat.format("strippedToken={0},gender={1}", strippedToken, gender)); //NOI18N
379
380                                 // This instance must be there
381                                 assert (gender instanceof Gender) : MessageFormat.format("gender is not set by Gender.fromChar({0})", strippedToken); //NOI18N
382                         }
383
384                         // Now it depends on the counter which position we need to check
385                         switch (count) {
386                                 case 0: // isOwnContact
387                                         assert ((bool instanceof Boolean));
388
389                                         // Debug message
390                                         this.getLogger().debug(MessageFormat.format("bool={0}", bool)); //NOI18N
391
392                                         // Is it own contact?
393                                         if (true == bool) {
394                                                 // Debug message
395                                                 this.getLogger().debug("Creating UserContact object ..."); //NOI18N
396
397                                                 // Own entry
398                                                 contact = new UserContact();
399                                         } else {
400                                                 // Debug message
401                                                 this.getLogger().debug("Creating BookContact object ..."); //NOI18N
402
403                                                 // Other contact
404                                                 contact = new BookContact();
405                                         }
406                                         break;
407
408                                 case 1: // Gender
409                                         assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N
410
411                                         // Update data
412                                         contact.setGender(gender);
413                                         break;
414
415                                 case 2: // Surname
416                                         assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N
417                                         assert (gender instanceof Gender) : "gender instance is not set"; //NOI18N
418
419                                         // Update data
420                                         contact.setSurname(strippedToken);
421                                         break;
422
423                                 case 3: // Family name
424                                         assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N
425                                         assert (gender instanceof Gender) : "gender instance is not set"; //NOI18N
426
427                                         // Update data
428                                         contact.setFamilyName(strippedToken);
429                                         break;
430
431                                 case 4: // Company name
432                                         assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N
433                                         assert (gender instanceof Gender) : "gender instance is not set"; //NOI18N
434
435                                         // Update data
436                                         contact.setCompanyName(strippedToken);
437                                         break;
438
439                                 case 5: // Street number
440                                         assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N
441
442                                         // Update data
443                                         contact.setHouseNumber(num);
444                                         break;
445
446                                 case 6: // ZIP code
447                                         assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N
448
449                                         // Update data
450                                         contact.setZipCode(num);
451                                         break;
452
453                                 case 7: // City name
454                                         assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N
455
456                                         // Update data
457                                         contact.setCity(strippedToken);
458                                         break;
459
460                                 case 8: // Country code
461                                         assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N
462
463                                         // Update data
464                                         contact.setCountryCode(strippedToken);
465                                         break;
466
467                                 case 9: // Phone number
468                                         assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N
469
470                                         // Update data
471                                         contact.setPhoneNumber(strippedToken);
472                                         break;
473
474                                 case 10: // Fax number
475                                         assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N
476
477                                         // Update data
478                                         contact.setFaxNumber(strippedToken);
479                                         break;
480
481                                 case 11: // Cellphone number
482                                         assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N
483
484                                         // Update data
485                                         contact.setCellphoneNumber(strippedToken);
486                                         break;
487
488                                 case 12: // Email address
489                                         assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N
490
491                                         // Update data
492                                         contact.setEmailAddress(strippedToken);
493                                         break;
494
495                                 case 13: // Birthday
496                                         assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N
497
498                                         // Update data
499                                         contact.setBirthday(strippedToken);
500                                         break;
501
502                                 case 14: // Comment
503                                         assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N
504
505                                         // Update data
506                                         contact.setComment(strippedToken);
507                                         break;
508
509                                 default: // New data entry
510                                         this.getLogger().warn(MessageFormat.format("Will not handle unknown data {0} at index {1}", strippedToken, count)); //NOI18N
511                                         break;
512                         }
513
514                         // Increment counter for next round
515                         count++;
516                 }
517
518                 // Trace message
519                 this.getLogger().trace(MessageFormat.format("contact={0} - EXIT!", contact)); //NOI18N
520
521                 // Return finished instance
522                 return contact;
523         }
524 }