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