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