2 * Copyright (C) 2015 Roland Haeder
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.
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.
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/>.
17 package org.mxchange.addressbook.database.frontend.contact;
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.Contact;
25 import org.mxchange.addressbook.contact.Gender;
26 import org.mxchange.addressbook.contact.book.BookContact;
27 import org.mxchange.addressbook.contact.user.UserContact;
28 import org.mxchange.addressbook.database.contact.ContactDatabaseConstants;
29 import org.mxchange.addressbook.exceptions.ContactAlreadyAddedException;
30 import org.mxchange.addressbook.manager.contact.ContactManager;
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;
37 * Stores and retrieves Contact instances
39 * @author Roland Haeder
41 public class ContactDatabaseFrontend extends BaseDatabaseFrontend implements ContactFrontend {
44 * Constructor which accepts a contact manager
46 * @param manager Manager instance
48 public ContactDatabaseFrontend (final ContactManager manager) {
49 // Call own constructor
53 this.getLogger().trace(MessageFormat.format("manager={0} - CALLED!", manager)); //NOI18N
55 // Manager instance must not be null
56 if (manager == null) {
58 throw new NullPointerException("manager is null"); //NOI18N
61 // Set contact manager
62 this.setContactManager(manager);
68 protected ContactDatabaseFrontend () {
70 this.getLogger().trace("CALLED!"); //NOI18N
73 this.setTableName("contacts"); //NOI18N
78 } catch (final UnsupportedDatabaseBackendException ex) {
80 this.abortProgramWithException(ex);
81 } catch (final SQLException ex) {
83 this.abortProgramWithException(ex);
88 * Adds given contact instance to database
90 * @param contact Contact instance
91 * @throws org.mxchange.addressbook.exceptions.ContactAlreadyAddedException If the contact instance is already found
94 public void addContact (final Contact contact) throws ContactAlreadyAddedException {
96 this.getLogger().trace("CALLED!"); //NOI18N
98 // Make sure the contact is set
99 if (contact == null) {
101 throw new NullPointerException("contact is null"); //NOI18N
105 // First check if the contact is there
106 if (this.isContactFound(contact)) {
108 throw new ContactAlreadyAddedException(contact);
112 this.getBackend().store((Storeable) contact);
113 } catch (final IOException ex) {
115 this.abortProgramWithException(ex);
116 } catch (final BadTokenException ex) {
118 this.abortProgramWithException(ex);
122 this.getLogger().trace("CALLED!"); //NOI18N
126 * Shuts down the database layer
129 public void doShutdown () {
131 this.getLogger().trace("CALLED!"); //NOI18N
134 this.getBackend().doShutdown();
137 this.getLogger().trace("EXIT!"); //NOI18N
141 * Some "getter" for total contact count
143 * @return Total contact count
146 public int getContactsCount () throws SQLException {
147 // And deligate to backend
148 return this.getBackend().getTotalCount(); //NOI18N
152 * Some "getter" for own contact instance
154 * @return Own contact instance
157 public Contact getOwnContact () {
159 this.getLogger().trace("CALLED!"); //NOI18N
161 // Get row index back from backend
162 int rowIndex = this.getBackend().getRowIndexFromColumn(ContactDatabaseConstants.COLUMN_NAME_OWN_CONTACT, true);
165 this.getLogger().debug(MessageFormat.format("rowIndex={0}", rowIndex));
168 Contact contact = null;
171 // Now simply read the row
172 contact = (Contact) this.getBackend().readRow(rowIndex);
173 } catch (final BadTokenException ex) {
175 this.abortProgramWithException(ex);
179 this.getLogger().trace(MessageFormat.format("contact={0} - EXIT!", contact));
186 * Checks if given Contact is found
188 * @param contact Contact instance to check
189 * @return Whether the given Contact instance is found
192 public boolean isContactFound (final Contact contact) throws BadTokenException {
194 this.getLogger().trace(MessageFormat.format("contact={0} - CALLED!", contact)); //NOI18N
196 // contact should not be null
197 if (contact == null) {
199 throw new NullPointerException("contact is null"); //NOI18N
202 // Default is not found
203 boolean isFound = false;
206 Iterator<? extends Storeable> iterator = this.getBackend().iterator();
209 while (iterator.hasNext()) {
211 Contact c = (Contact) iterator.next();
214 this.getLogger().debug(MessageFormat.format("c={0},contact={1}", c, contact)); //NOI18N
217 if (c.equals(contact)) {
225 this.getLogger().trace(MessageFormat.format("isFound={0} - EXIT!", isFound)); //NOI18N
232 * Checks whether own contact is found in database
234 * @return Whether own contact is found
237 public boolean isOwnContactFound () throws SQLException {
238 // Deligate this call to backend
239 return this.getBackend().isRowFound(ContactDatabaseConstants.COLUMN_NAME_OWN_CONTACT, true);
243 * Parses given line from database backend into a Storeable instance. Please
244 * note that not all backends need this.
246 * @param line Line from database backend
247 * @return A Storeable instance
250 public Storeable parseLineToStoreable (final String line) throws BadTokenException {
252 this.getLogger().trace(MessageFormat.format("line={0} - CALLED!", line)); //NOI18N
255 Contact contact = this.parseLineToContact(line);
258 this.getLogger().debug(MessageFormat.format("contact={0}", contact));
261 return (Storeable) contact;
265 * Reads a single row and parses it to a contact instance
267 * @param rowIndex Row index (also how much to skip)
268 * @return Contact instance
271 public Contact readSingleContact (final int rowIndex) {
273 // Deligate this to backend instance
274 return (Contact) this.getBackend().readRow(rowIndex);
275 } catch (final BadTokenException ex) {
277 this.abortProgramWithException(ex);
280 // Bad state, should not be reached
281 throw new IllegalStateException("This should not be reached");
285 * Parses given line and creates a Contact instance
287 * @param line Raw line to parse
289 * @return Contact instance
291 private Contact parseLineToContact (final String line) throws BadTokenException {
293 this.getLogger().trace(MessageFormat.format("line={0} - CALLED!", line)); //NOI18N
295 // Init A lot variables
298 Gender gender = null;
300 Contact contact = null;
303 this.getLogger().debug(MessageFormat.format("line={0}", line)); //NOI18N
306 // @TODO Move this into separate method
307 StringTokenizer tokenizer = new StringTokenizer(line, ";"); //NOI18N
313 // The tokens are now available, so get all
314 while (tokenizer.hasMoreElements()) {
320 String token = tokenizer.nextToken();
322 // If char " is at pos 2 (0,1,2), then cut it of there
323 if ((token.charAt(0) != '"') && (token.charAt(2) == '"')) {
324 // UTF-8 writer characters found
325 token = token.substring(2);
329 this.getLogger().debug(MessageFormat.format("token={0}", token)); //NOI18N
331 // Verify token, it must have double-quotes on each side
332 if ((!token.startsWith("\"")) || (!token.endsWith("\""))) { //NOI18N
333 // Something bad was read
334 throw new BadTokenException(token, count); //NOI18N
337 // All fine, so remove it
338 String strippedToken = token.substring(1, token.length() - 1);
340 // Is the string's content "null"?
341 if (strippedToken.equals("null")) { //NOI18N
343 this.getLogger().debug(MessageFormat.format("strippedToken={0} - NULL!", strippedToken)); //NOI18N
345 // This needs to be set to null
346 strippedToken = null;
350 this.getLogger().debug(MessageFormat.format("strippedToken={0}", strippedToken)); //NOI18N
352 // Now, let's try a number check, if no null
353 if (strippedToken != null) {
354 // Okay, no null, maybe the string bears a decimal number?
356 num = Long.valueOf(strippedToken);
359 this.getLogger().debug(MessageFormat.format("strippedToken={0} - NUMBER!", strippedToken)); //NOI18N
360 } catch (final NumberFormatException ex) {
361 // No number, then set default
366 // Now, let's try a boolean check, if no null
367 if ((strippedToken != null) && (num == null) && ((strippedToken.equals("true")) || (strippedToken.equals("false")))) { //NOI18N
369 this.getLogger().debug(MessageFormat.format("strippedToken={0} - BOOLEAN!", strippedToken)); //NOI18N
371 // parseBoolean() is relaxed, so no exceptions
372 bool = Boolean.valueOf(strippedToken);
376 this.getLogger().debug(MessageFormat.format("strippedToken={0},num={1},bool={2}", strippedToken, num, bool)); //NOI18N
378 // Now, let's try a gender check, if no null
379 if ((strippedToken != null) && (num == null) && (bool == null) && ((strippedToken.equals("M")) || (strippedToken.equals("F")) || (strippedToken.equals("C")))) { //NOI18N
380 // Get first character
381 gender = Gender.fromChar(strippedToken.charAt(0));
384 this.getLogger().debug(MessageFormat.format("strippedToken={0},gender={1}", strippedToken, gender)); //NOI18N
386 // This instance must be there
387 assert (gender instanceof Gender) : MessageFormat.format("gender is not set by Gender.fromChar({0})", strippedToken); //NOI18N
390 // Now it depends on the counter which position we need to check
392 case 0: // isOwnContact
393 assert ((bool instanceof Boolean));
396 this.getLogger().debug(MessageFormat.format("bool={0}", bool)); //NOI18N
398 // Is it own contact?
401 this.getLogger().debug("Creating UserContact object ..."); //NOI18N
404 contact = new UserContact();
407 this.getLogger().debug("Creating BookContact object ..."); //NOI18N
410 contact = new BookContact();
415 assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N
418 contact.updateNameData(gender, null, null, null);
422 assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N
423 assert (gender instanceof Gender) : "gender instance is not set"; //NOI18N
426 contact.updateNameData(gender, strippedToken, null, null);
429 case 3: // Family name
430 assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N
431 assert (gender instanceof Gender) : "gender instance is not set"; //NOI18N
434 contact.updateNameData(gender, null, strippedToken, null);
437 case 4: // Company name
438 assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N
439 assert (gender instanceof Gender) : "gender instance is not set"; //NOI18N
442 contact.updateNameData(gender, null, null, strippedToken);
445 case 5: // Street number
446 assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N
449 contact.updateAddressData(strippedToken, 0, null, null);
453 assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N
456 contact.updateAddressData(null, num, null, null);
460 assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N
463 contact.updateAddressData(null, 0, strippedToken, null);
466 case 8: // Country code
467 assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N
470 contact.updateAddressData(null, 0, null, strippedToken);
473 case 9: // Phone number
474 assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N
477 contact.updateOtherData(strippedToken, null, null, null, null, null);
480 case 10: // Fax number
481 assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N
484 contact.updateOtherData(null, strippedToken, null, null, null, null);
487 case 11: // Cellphone number
488 assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N
491 contact.updateOtherData(null, null, strippedToken, null, null, null);
494 case 12: // Email address
495 assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N
498 contact.updateOtherData(null, null, null, strippedToken, null, null);
502 assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N
505 contact.updateOtherData(null, null, null, null, strippedToken, null);
509 assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N
512 contact.updateOtherData(null, null, null, null, null, strippedToken);
515 default: // New data entry
516 this.getLogger().warn(MessageFormat.format("Will not handle unknown data {0} at index {1}", strippedToken, count)); //NOI18N
520 // Increment counter for next round
525 this.getLogger().trace(MessageFormat.format("contact={0} - EXIT!", contact)); //NOI18N
527 // Return finished instance