From b65c62081dde8af7e231a2950cdd7e36b22c6cca Mon Sep 17 00:00:00 2001 From: Roland Haeder Date: Fri, 31 Jul 2015 15:14:45 +0200 Subject: [PATCH] =?utf8?q?Added=20a=20lot=20more=20generic=20methods=20for?= =?utf8?q?=20columnName=20lookup=20(a=20field=20must=20be=20there)=20+=20r?= =?utf8?q?enamed=20some=20classes=20Signed-off-by:Roland=20H=C3=A4der=20?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- .../addressbook/BaseFrameworkSystem.java | 153 ++++++-- .../addressbook/contact/BaseContact.java | 8 +- .../database/backend/DatabaseBackend.java | 12 +- .../backend/csv/Base64CsvDatabaseBackend.java | 362 +++++------------- .../backend/mysql/MySqlDatabaseBackend.java | 12 + .../frontend/BaseDatabaseFrontend.java | 2 +- ...baseWrapper.java => DatabaseFrontend.java} | 7 +- .../contact/ContactDatabaseFrontend.java | 303 ++++++++++++++- ...ntactWrapper.java => ContactFrontend.java} | 4 +- .../database/storage/csv/StoreableCsv.java | 6 + .../manager/contact/ContactManager.java | 49 ++- .../manager/contact/ManageableContact.java | 10 +- .../model/contact/ContactTableModel.java | 2 +- 13 files changed, 608 insertions(+), 322 deletions(-) rename Addressbook/src/org/mxchange/addressbook/database/frontend/{DatabaseWrapper.java => DatabaseFrontend.java} (78%) rename Addressbook/src/org/mxchange/addressbook/database/frontend/contact/{ContactWrapper.java => ContactFrontend.java} (95%) diff --git a/Addressbook/src/org/mxchange/addressbook/BaseFrameworkSystem.java b/Addressbook/src/org/mxchange/addressbook/BaseFrameworkSystem.java index f0fd4d4..92fe258 100644 --- a/Addressbook/src/org/mxchange/addressbook/BaseFrameworkSystem.java +++ b/Addressbook/src/org/mxchange/addressbook/BaseFrameworkSystem.java @@ -25,6 +25,7 @@ import java.io.PrintWriter; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.text.MessageFormat; +import java.util.Arrays; import java.util.Properties; import java.util.ResourceBundle; import java.util.StringTokenizer; @@ -32,7 +33,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.mxchange.addressbook.application.Application; import org.mxchange.addressbook.client.Client; -import org.mxchange.addressbook.database.frontend.DatabaseWrapper; +import org.mxchange.addressbook.database.frontend.DatabaseFrontend; import org.mxchange.addressbook.manager.contact.ManageableContact; /** @@ -41,6 +42,7 @@ import org.mxchange.addressbook.manager.contact.ManageableContact; * @author Roland Haeder */ public class BaseFrameworkSystem implements FrameworkInterface { + /** * Instance for own properties */ @@ -71,16 +73,15 @@ public class BaseFrameworkSystem implements FrameworkInterface { */ private ManageableContact contactManager; - /** * Name of used database table, handled over to backend */ private String tableName; /** - * DatabaseWrapper instance + * DatabaseFrontend instance */ - private DatabaseWrapper wrapper; + private DatabaseFrontend wrapper; /** * Initialize object @@ -133,21 +134,20 @@ public class BaseFrameworkSystem implements FrameworkInterface { } /** - * Some "getter" for a Method instance from given method name + * Some "getter" for target class instance from given name. * - * @param instance Actual instance to call - * @param targetClass Target class name - * @param methodName Method name - * @return A Method instance + * @param instance Instance to iterate on + * @param targetClass Class name to look for + * @return Class instance */ @SuppressWarnings ("unchecked") - private Method getMethodFromName (final FrameworkInterface instance, final String targetClass, final String methodName) { - // Trace messahe - this.getLogger().trace(MessageFormat.format("targetClass={0},methodName={1}", targetClass, methodName)); + private Class getClassFromTarget (final FrameworkInterface instance, final String targetClass) { + // Trace message + this.getLogger().debug(MessageFormat.format("instance={0},targetClass={1}", instance, targetClass)); // Instance reflaction of this class Class c = instance.getClass(); - + // Analyze class while (!targetClass.equals(c.getSimpleName())) { // Debug message @@ -157,6 +157,28 @@ public class BaseFrameworkSystem implements FrameworkInterface { c = (Class) c.getSuperclass(); } + // Trace message + this.getLogger().trace(MessageFormat.format("c={0} - EXIT!", c)); + + // Return it + return c; + } + + /** + * Some "getter" for a Method instance from given method name + * + * @param instance Actual instance to call + * @param targetClass Target class name + * @param methodName Method name + * @return A Method instance + */ + private Method getMethodFromName (final FrameworkInterface instance, final String targetClass, final String methodName) { + // Trace messahe + this.getLogger().trace(MessageFormat.format("targetClass={0},methodName={1}", targetClass, methodName)); + + // Get target class instance + Class c = this.getClassFromTarget(instance, targetClass); + // Init field instance Method method = null; @@ -172,7 +194,7 @@ public class BaseFrameworkSystem implements FrameworkInterface { } // Assert on field - assert(method instanceof Method) : "method is not a Method instance"; + assert (method instanceof Method) : "method is not a Method instance"; // Trace message this.getLogger().trace(MessageFormat.format("method={0} - EXIT!", method)); @@ -192,7 +214,7 @@ public class BaseFrameworkSystem implements FrameworkInterface { // .. and exit System.exit(1); - + } /** @@ -397,7 +419,7 @@ public class BaseFrameworkSystem implements FrameworkInterface { // Increment counter count++; } - + // Trace message this.getLogger().trace(MessageFormat.format("builder={0} - EXIT!", builder)); @@ -417,11 +439,8 @@ public class BaseFrameworkSystem implements FrameworkInterface { // Trace message this.getLogger().trace(MessageFormat.format("columnName={0} - CALLED!", columnName)); - // First all lower case - String lower = columnName.toLowerCase(); - // Then split on "_" - StringTokenizer tokenizer = new StringTokenizer(lower, "_"); + StringTokenizer tokenizer = new StringTokenizer(columnName, "_"); // Resulting string StringBuilder builder = new StringBuilder(tokenizer.countTokens()); @@ -450,7 +469,7 @@ public class BaseFrameworkSystem implements FrameworkInterface { // Add token builder.append(token); } - + // Trace message this.getLogger().trace(MessageFormat.format("builder={0} - EXIT!", builder)); @@ -527,7 +546,7 @@ public class BaseFrameworkSystem implements FrameworkInterface { // Return value return object; } - + /** * Getter for logger * @@ -566,20 +585,100 @@ public class BaseFrameworkSystem implements FrameworkInterface { } /** - * Getter for DatabaseWrapper instance + * Getter for DatabaseFrontend instance * - * @return DatabaseWrapper instance + * @return DatabaseFrontend instance */ - protected DatabaseWrapper getWrapper () { + protected DatabaseFrontend getWrapper () { return this.wrapper; } /** * Setter for wrapper instance * - * @param wrapper A DatabaseWrapper instance + * @param wrapper A DatabaseFrontend instance */ - protected void setWrapper (final DatabaseWrapper wrapper) { + protected void setWrapper (final DatabaseFrontend wrapper) { this.wrapper = wrapper; } + + /** + * Some "getter" for an array from given string and tokenizer + * + * @param str String to tokenize and get array from + * @param delimiter Delimiter + * @param size Size of array + * @return Array from tokenized string + */ + protected String[] getArrayFromString (final String str, final String delimiter, final int size) { + // Trace message + this.getLogger().trace(MessageFormat.format("str={0},delimiter={1},size={2} - CALLED!", str, delimiter, size)); + + // Get tokenizer + StringTokenizer tokenizer = new StringTokenizer(str, delimiter); + + // Init array and index + String[] tokens = new String[size]; + int index = 0; + + // Run through all tokens + while (tokenizer.hasMoreTokens()) { + // Get current token and add it + tokens[index] = tokenizer.nextToken(); + + // Debug message + this.getLogger().debug(MessageFormat.format("Token at index{0}: {1}", index, tokens[1])); + + // Increment index + index++; + } + + // Trace message + this.getLogger().trace(MessageFormat.format("tokens({0})={1} - EXIT!", tokens.length, Arrays.toString(tokens))); + + // Return it + return tokens; + } + + /** + * Checks whether the given field is a boolean field by probing it. + * + * @param instance Instance to call + * @param targetClass Target class + * @param columnName Column name to check + * @return Whether the given column name represents a boolean field + */ + protected boolean isBooleanField (final FrameworkInterface instance, final String targetClass, final String columnName) { + // Trace message + this.getLogger().trace(MessageFormat.format("instance={0},targetCLass={1},columnName={2} - CALLED!", instance, targetClass, columnName)); + + // Convert column name to getter name (boolean) + String methodName = this.convertColumnNameToGetterMethod(columnName, true); + + // Get class instance + Class c = this.getClassFromTarget(instance, targetClass); + + // Defauzlt is boolean + boolean isBool = true; + + try { + // Now try to instance the method + Method method = c.getDeclaredMethod(methodName, new Class[0]); + } catch (final NoSuchMethodException ex) { + // Debug message + this.getLogger().debug(MessageFormat.format("Method {0} does not exist, field {1} cannot be boolean: {2}", methodName, columnName, ex)); + + // Not found + isBool = false; + } catch (final SecurityException ex) { + // Really bad? + this.abortProgramWithException(ex); + } + + // Trace message + this.getLogger().trace(MessageFormat.format("isBool={0} - EXIT!", isBool)); + + // Return result + return isBool; + } } diff --git a/Addressbook/src/org/mxchange/addressbook/contact/BaseContact.java b/Addressbook/src/org/mxchange/addressbook/contact/BaseContact.java index 4ecf0c3..45d2e17 100644 --- a/Addressbook/src/org/mxchange/addressbook/contact/BaseContact.java +++ b/Addressbook/src/org/mxchange/addressbook/contact/BaseContact.java @@ -655,8 +655,14 @@ public class BaseContact extends BaseFrameworkSystem { // Trace message this.getLogger().trace(MessageFormat.format("columnName={0} - CALLED!", columnName)); + // Determine if the given column is boolean + if (this.isBooleanField(this, "BaseContact", columnName)) { + // Yes, then call other method + return this.getBooleanField(this, "BaseContact", columnName); + } + // Convert column name to field name - String methodName = this.convertColumnNameToGetterMethod(columnName, true); + String methodName = this.convertColumnNameToGetterMethod(columnName, false); // Debug message this.getLogger().debug(MessageFormat.format("field={0}", methodName)); diff --git a/Addressbook/src/org/mxchange/addressbook/database/backend/DatabaseBackend.java b/Addressbook/src/org/mxchange/addressbook/database/backend/DatabaseBackend.java index d2def25..8fc30e6 100644 --- a/Addressbook/src/org/mxchange/addressbook/database/backend/DatabaseBackend.java +++ b/Addressbook/src/org/mxchange/addressbook/database/backend/DatabaseBackend.java @@ -42,6 +42,15 @@ public interface DatabaseBackend extends FrameworkInterface { */ public void doShutdown (); + /** + * Some "getter" for row index from given boolean row value + * + * @param columnName Name of column + * @param bool Boolean value to look for + * @return Row index + */ + public int getRowIndexFromColumn (final String columnName, final boolean bool); + /** * Some "getter" for total table row count * @@ -93,6 +102,7 @@ public interface DatabaseBackend extends FrameworkInterface { * * @param rowIndex Row index (or how much to skip) * @return A Storeable instance + * @throws org.mxchange.addressbook.exceptions.BadTokenException If a token was badly formatted */ - public Storeable readRow (final int rowIndex); + public Storeable readRow (final int rowIndex) throws BadTokenException; } diff --git a/Addressbook/src/org/mxchange/addressbook/database/backend/csv/Base64CsvDatabaseBackend.java b/Addressbook/src/org/mxchange/addressbook/database/backend/csv/Base64CsvDatabaseBackend.java index ea1b7bf..39eef66 100644 --- a/Addressbook/src/org/mxchange/addressbook/database/backend/csv/Base64CsvDatabaseBackend.java +++ b/Addressbook/src/org/mxchange/addressbook/database/backend/csv/Base64CsvDatabaseBackend.java @@ -26,15 +26,11 @@ import java.util.ArrayList; import java.util.Base64; import java.util.Iterator; import java.util.List; -import java.util.StringTokenizer; import org.mxchange.addressbook.FrameworkInterface; import org.mxchange.addressbook.contact.Contact; -import org.mxchange.addressbook.contact.Gender; -import org.mxchange.addressbook.contact.book.BookContact; -import org.mxchange.addressbook.contact.user.UserContact; import org.mxchange.addressbook.database.backend.BaseDatabaseBackend; import org.mxchange.addressbook.database.backend.DatabaseBackend; -import org.mxchange.addressbook.database.frontend.DatabaseWrapper; +import org.mxchange.addressbook.database.frontend.DatabaseFrontend; import org.mxchange.addressbook.database.storage.Storeable; import org.mxchange.addressbook.database.storage.csv.StoreableCsv; import org.mxchange.addressbook.exceptions.BadTokenException; @@ -57,7 +53,7 @@ public class Base64CsvDatabaseBackend extends BaseDatabaseBackend implements Dat * @param tableName Name of "table" * @param wrapper Wrapper instance to call back */ - public Base64CsvDatabaseBackend (final String tableName, final DatabaseWrapper wrapper) { + public Base64CsvDatabaseBackend (final String tableName, final DatabaseFrontend wrapper) { // Trace message this.getLogger().trace(MessageFormat.format("tableName={0},wrapper={1}", tableName, wrapper)); //NOI18N @@ -117,6 +113,60 @@ public class Base64CsvDatabaseBackend extends BaseDatabaseBackend implements Dat this.getLogger().trace("EXIT!"); //NOI18N } + /** + * Some "getter" for row index from given boolean row value + * + * @param columnName Name of column + * @param bool Boolean value to look for + * @return Row index + */ + @Override + public int getRowIndexFromColumn (final String columnName, final boolean bool) { + // Trace message + this.getLogger().trace(MessageFormat.format("columnName={0},bool={1} - CALLED!", columnName, bool)); //NOI18N + + // Row indexes start with zero + int rowIndex = 0; + + // First rewind + this.rewind(); + + // Try to find the proper row + while (!this.isEndOfFile()) { + // Read line + String line = this.readLine(); + + // Debug message + this.getLogger().debug(MessageFormat.format("line={0}", line)); + + // Callback the wrapper to handle parsing + try { + Storeable storeable = this.getWrapper().parseLineToStoreable(line); + + // Debug message + this.getLogger().debug(MessageFormat.format("storeable={0}", storeable)); + + // Is the value qual + if (storeable.isValueEqual(columnName, bool)) { + // Debug message + this.getLogger().debug(MessageFormat.format("Found storeable={0} for columnName={1} and bool={2}", storeable, columnName, bool)); + + // Found it + break; + } + } catch (final BadTokenException ex) { + // Bad token found + this.abortProgramWithException(ex); + } + + // Count up + rowIndex++; + } + + // Return it + return rowIndex; + } + /** * Some "getter" for total row count * @@ -174,7 +224,7 @@ public class Base64CsvDatabaseBackend extends BaseDatabaseBackend implements Dat try { // And parse it to a Contact instance - Contact contact = this.parseLineToContact(line); + Contact contact = (Contact) this.getWrapper().parseLineToStoreable(line); // Debug message this.getLogger().debug(MessageFormat.format("contact={0}", contact)); @@ -208,25 +258,26 @@ public class Base64CsvDatabaseBackend extends BaseDatabaseBackend implements Dat * Gets an iterator for contacts * * @return Iterator for contacts - * @throws org.mxchange.addressbook.exceptions.BadTokenException If the underlaying method has found an invalid token + * @throws org.mxchange.addressbook.exceptions.BadTokenException If the + * underlaying method has found an invalid token */ @Override public Iterator iterator () throws BadTokenException { // Trace message this.getLogger().trace("CALLED!"); //NOI18N - + /* * Then read the file into RAM (yes, not perfect for >1000 entries ...) * and get a List back. */ List list = this.readList(); - + // List must be set assert (list instanceof List) : "list has not been set."; //NOI18N - + // Trace message this.getLogger().trace(MessageFormat.format("list.iterator()={0} - EXIT!", list.iterator())); //NOI18N - + // Get iterator from list and return it return list.iterator(); } @@ -256,31 +307,41 @@ public class Base64CsvDatabaseBackend extends BaseDatabaseBackend implements Dat /** * Reads a single row from database. - * + * * @param rowIndex Row index (or how much to skip) * @return A Storeable instance + * @throws org.mxchange.addressbook.exceptions.BadTokenException If a token + * was badly formatted */ @Override - public Storeable readRow (final int rowIndex) { + public Storeable readRow (final int rowIndex) throws BadTokenException { // First rewind this.rewind(); // Intialize variables - int count = 0; + int count = -1; Storeable storeable = null; // Read all rows - do { + while (!this.isEndOfFile() || (count < rowIndex)) { // Read row String line = this.readLine(); + // Debug message + this.getLogger().debug(MessageFormat.format("line={0}", line)); + // Callback the wrapper to handle parsing storeable = this.getWrapper().parseLineToStoreable(line); + // Debug message + this.getLogger().debug(MessageFormat.format("storeable={0}", storeable)); + // Increment counter count++; - } while (!this.isEndOfFile() || (count > rowIndex)); + } + // Trace message + this.getLogger().trace(MessageFormat.format("storeable={0} - EXIT!", storeable)); // Return found element return storeable; } @@ -415,253 +476,6 @@ public class Base64CsvDatabaseBackend extends BaseDatabaseBackend implements Dat return isEof; } - /** - * Parses given line and creates a Contact instance - * - * @param line Raw line to parse - * - * @return Contact instance - */ - private Contact parseLineToContact (final String line) throws BadTokenException { - // Trace message - this.getLogger().trace(MessageFormat.format("line={0} - CALLED!", line)); //NOI18N - - // Init A lot variables - Long num = null; - Boolean bool = null; - Gender gender = null; - int count = 0; - Contact contact = null; - - // Debug message - this.getLogger().debug(MessageFormat.format("line={0}", line)); //NOI18N - - // Then tokenize it - // @TODO Move this into separate method - StringTokenizer tokenizer = new StringTokenizer(line, ";"); //NOI18N - - // Reset variables - count = 0; - contact = null; - - // The tokens are now available, so get all - while (tokenizer.hasMoreElements()) { - // Reset variables - num = null; - bool = null; - - // Get next token - String token = tokenizer.nextToken(); - - // If char " is at pos 2 (0,1,2), then cut it of there - if ((token.charAt(0) != '"') && (token.charAt(2) == '"')) { - // UTF-8 writer characters found - token = token.substring(2); - } - - // Debug message - this.getLogger().debug(MessageFormat.format("token={0}", token)); //NOI18N - - // Verify token, it must have double-quotes on each side - if ((!token.startsWith("\"")) || (!token.endsWith("\""))) { //NOI18N - // Something bad was read - throw new BadTokenException(token, count); //NOI18N - } - - // All fine, so remove it - String strippedToken = token.substring(1, token.length() - 1); - - // Is the string's content "null"? - if (strippedToken.equals("null")) { //NOI18N - // Debug message - this.getLogger().debug(MessageFormat.format("strippedToken={0} - NULL!", strippedToken)); //NOI18N - - // This needs to be set to null - strippedToken = null; - } - - // Debug message - this.getLogger().debug(MessageFormat.format("strippedToken={0}", strippedToken)); //NOI18N - - // Now, let's try a number check, if no null - if (strippedToken != null) { - // Okay, no null, maybe the string bears a decimal number? - try { - num = Long.valueOf(strippedToken); - - // Debug message - this.getLogger().debug(MessageFormat.format("strippedToken={0} - NUMBER!", strippedToken)); //NOI18N - } catch (final NumberFormatException ex) { - // No number, then set default - num = null; - } - } - - // Now, let's try a boolean check, if no null - if ((strippedToken != null) && (num == null) && ((strippedToken.equals("true")) || (strippedToken.equals("false")))) { //NOI18N - // Debug message - this.getLogger().debug(MessageFormat.format("strippedToken={0} - BOOLEAN!", strippedToken)); //NOI18N - - // parseBoolean() is relaxed, so no exceptions - bool = Boolean.valueOf(strippedToken); - } - - // Debug message - this.getLogger().debug(MessageFormat.format("strippedToken={0},num={1},bool={2}", strippedToken, num, bool)); //NOI18N - - // Now, let's try a gender check, if no null - if ((strippedToken != null) && (num == null) && (bool == null) && ((strippedToken.equals("M")) || (strippedToken.equals("F")) || (strippedToken.equals("C")))) { //NOI18N - // Get first character - gender = Gender.fromChar(strippedToken.charAt(0)); - - // Debug message - this.getLogger().debug(MessageFormat.format("strippedToken={0},gender={1}", strippedToken, gender)); //NOI18N - - // This instance must be there - assert (gender instanceof Gender) : MessageFormat.format("gender is not set by Gender.fromChar({0})", strippedToken); //NOI18N - } - - // Now it depends on the counter which position we need to check - switch (count) { - case 0: // isOwnContact - assert ((bool instanceof Boolean)); - - // Debug message - this.getLogger().debug(MessageFormat.format("bool={0}", bool)); //NOI18N - - // Is it own contact? - if (true == bool) { - // Debug message - this.getLogger().debug("Creating UserContact object ..."); //NOI18N - - // Own entry - contact = new UserContact(); - } else { - // Debug message - this.getLogger().debug("Creating BookContact object ..."); //NOI18N - - // Other contact - contact = new BookContact(); - } - break; - - case 1: // Gender - assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N - - // Update data - contact.updateNameData(gender, null, null, null); - break; - - case 2: // Surname - assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N - assert (gender instanceof Gender) : "gender instance is not set"; //NOI18N - - // Update data - contact.updateNameData(gender, strippedToken, null, null); - break; - - case 3: // Family name - assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N - assert (gender instanceof Gender) : "gender instance is not set"; //NOI18N - - // Update data - contact.updateNameData(gender, null, strippedToken, null); - break; - - case 4: // Company name - assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N - assert (gender instanceof Gender) : "gender instance is not set"; //NOI18N - - // Update data - contact.updateNameData(gender, null, null, strippedToken); - break; - - case 5: // Street number - assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N - - // Update data - contact.updateAddressData(strippedToken, 0, null, null); - break; - - case 6: // ZIP code - assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N - - // Update data - contact.updateAddressData(null, num, null, null); - break; - - case 7: // City name - assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N - - // Update data - contact.updateAddressData(null, 0, strippedToken, null); - break; - - case 8: // Country code - assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N - - // Update data - contact.updateAddressData(null, 0, null, strippedToken); - break; - - case 9: // Phone number - assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N - - // Update data - contact.updateOtherData(strippedToken, null, null, null, null, null); - break; - - case 10: // Fax number - assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N - - // Update data - contact.updateOtherData(null, strippedToken, null, null, null, null); - break; - - case 11: // Cellphone number - assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N - - // Update data - contact.updateOtherData(null, null, strippedToken, null, null, null); - break; - - case 12: // Email address - assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N - - // Update data - contact.updateOtherData(null, null, null, strippedToken, null, null); - break; - - case 13: // Birthday - assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N - - // Update data - contact.updateOtherData(null, null, null, null, strippedToken, null); - break; - - case 14: // Birthday - assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N - - // Update data - contact.updateOtherData(null, null, null, null, null, strippedToken); - break; - - default: // New data entry - this.getLogger().warn(MessageFormat.format("Will not handle unknown data {0} at index {1}", strippedToken, count)); //NOI18N - break; - } - - // Increment counter for next round - count++; - } - - // Trace message - this.getLogger().trace(MessageFormat.format("contact={0} - EXIT!", contact)); //NOI18N - - // Return finished instance - return contact; - } - /** * Reads a line from file base * @@ -670,26 +484,32 @@ public class Base64CsvDatabaseBackend extends BaseDatabaseBackend implements Dat private String readLine () { // Trace message this.getLogger().trace("CALLED!"); //NOI18N - + // Init input String input = null; - + try { // Read single line String base64 = this.getStorageFile().readLine(); - + + // Is the line null? + if (base64 == null) { + // Then throw NPE here + throw new NullPointerException("base64 is null"); + } + // Decode BASE-64 byte[] decoded = Base64.getDecoder().decode(base64); - + // Convert to string input = new String(decoded); } catch (final IOException ex) { this.getLogger().catching(ex); } - + // Trace message this.getLogger().trace(MessageFormat.format("input={0} - EXIT!", input)); //NOI18N - + // Return read string or null return input; } @@ -726,7 +546,7 @@ public class Base64CsvDatabaseBackend extends BaseDatabaseBackend implements Dat line = this.readLine(); // Parse line - instance = (Storeable) this.parseLineToContact(line); + instance = this.getWrapper().parseLineToStoreable(line); // The contact instance should be there now assert (instance instanceof FrameworkInterface) : MessageFormat.format("instance is not set: {0}", instance); //NOI18N diff --git a/Addressbook/src/org/mxchange/addressbook/database/backend/mysql/MySqlDatabaseBackend.java b/Addressbook/src/org/mxchange/addressbook/database/backend/mysql/MySqlDatabaseBackend.java index c2c63f9..5d8b6ed 100644 --- a/Addressbook/src/org/mxchange/addressbook/database/backend/mysql/MySqlDatabaseBackend.java +++ b/Addressbook/src/org/mxchange/addressbook/database/backend/mysql/MySqlDatabaseBackend.java @@ -120,6 +120,18 @@ public class MySqlDatabaseBackend extends BaseDatabaseBackend implements Databas } } + /** + * Some "getter" for row index from given boolean row value + * + * @param columnName Name of column + * @param bool Boolean value to look for + * @return Row index + */ + @Override + public int getRowIndexFromColumn (final String columnName, final boolean bool) { + throw new UnsupportedOperationException(MessageFormat.format("columnName={0},bool={1}", columnName, bool)); + } + @Override public int getTotalCount () throws SQLException { // Trace message diff --git a/Addressbook/src/org/mxchange/addressbook/database/frontend/BaseDatabaseFrontend.java b/Addressbook/src/org/mxchange/addressbook/database/frontend/BaseDatabaseFrontend.java index 10bd035..462b096 100644 --- a/Addressbook/src/org/mxchange/addressbook/database/frontend/BaseDatabaseFrontend.java +++ b/Addressbook/src/org/mxchange/addressbook/database/frontend/BaseDatabaseFrontend.java @@ -29,7 +29,7 @@ import org.mxchange.addressbook.exceptions.UnsupportedDatabaseDriverException; * * @author Roland Haeder */ -public abstract class BaseDatabaseFrontend extends BaseFrameworkSystem implements DatabaseWrapper { +public abstract class BaseDatabaseFrontend extends BaseFrameworkSystem implements DatabaseFrontend { /** * Instance for database backend diff --git a/Addressbook/src/org/mxchange/addressbook/database/frontend/DatabaseWrapper.java b/Addressbook/src/org/mxchange/addressbook/database/frontend/DatabaseFrontend.java similarity index 78% rename from Addressbook/src/org/mxchange/addressbook/database/frontend/DatabaseWrapper.java rename to Addressbook/src/org/mxchange/addressbook/database/frontend/DatabaseFrontend.java index d49e630..8df158d 100644 --- a/Addressbook/src/org/mxchange/addressbook/database/frontend/DatabaseWrapper.java +++ b/Addressbook/src/org/mxchange/addressbook/database/frontend/DatabaseFrontend.java @@ -18,20 +18,21 @@ package org.mxchange.addressbook.database.frontend; import org.mxchange.addressbook.FrameworkInterface; import org.mxchange.addressbook.database.storage.Storeable; +import org.mxchange.addressbook.exceptions.BadTokenException; /** * A generic interface for database frontends * * @author Roland Haeder */ -public interface DatabaseWrapper extends FrameworkInterface { - +public interface DatabaseFrontend extends FrameworkInterface { /** * Parses given line from database backend into a Storeable instance. Please * note that not all backends need this. * * @param line Line from database backend * @return A Storeable instance + * @throws org.mxchange.addressbook.exceptions.BadTokenException If a token was badly formatted */ - public Storeable parseLineToStoreable (final String line); + public Storeable parseLineToStoreable (final String line) throws BadTokenException; } diff --git a/Addressbook/src/org/mxchange/addressbook/database/frontend/contact/ContactDatabaseFrontend.java b/Addressbook/src/org/mxchange/addressbook/database/frontend/contact/ContactDatabaseFrontend.java index 1155aef..ecd30b4 100644 --- a/Addressbook/src/org/mxchange/addressbook/database/frontend/contact/ContactDatabaseFrontend.java +++ b/Addressbook/src/org/mxchange/addressbook/database/frontend/contact/ContactDatabaseFrontend.java @@ -20,7 +20,11 @@ import java.io.IOException; import java.sql.SQLException; import java.text.MessageFormat; import java.util.Iterator; +import java.util.StringTokenizer; import org.mxchange.addressbook.contact.Contact; +import org.mxchange.addressbook.contact.Gender; +import org.mxchange.addressbook.contact.book.BookContact; +import org.mxchange.addressbook.contact.user.UserContact; import org.mxchange.addressbook.database.contact.ContactDatabaseConstants; import org.mxchange.addressbook.database.frontend.BaseDatabaseFrontend; import org.mxchange.addressbook.database.storage.Storeable; @@ -34,7 +38,7 @@ import org.mxchange.addressbook.manager.contact.ContactManager; * * @author Roland Haeder */ -public class ContactDatabaseFrontend extends BaseDatabaseFrontend implements ContactWrapper { +public class ContactDatabaseFrontend extends BaseDatabaseFrontend implements ContactFrontend { /** * Constructor which accepts a contact manager @@ -154,7 +158,28 @@ public class ContactDatabaseFrontend extends BaseDatabaseFrontend implements Con // Trace message this.getLogger().trace("CALLED!"); //NOI18N - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + // Get row index back from backend + int rowIndex = this.getBackend().getRowIndexFromColumn(ContactDatabaseConstants.COLUMN_NAME_OWN_CONTACT, true); + + // Debug message + this.getLogger().debug(MessageFormat.format("rowIndex={0}", rowIndex)); + + // Init instance + Contact contact = null; + + try { + // Now simply read the row + contact = (Contact) this.getBackend().readRow(rowIndex); + } catch (final BadTokenException ex) { + // Bad token found + this.abortProgramWithException(ex); + } + + // Trace message + this.getLogger().trace(MessageFormat.format("contact={0} - EXIT!", contact)); + + // Return it + return contact; } /** @@ -222,8 +247,18 @@ public class ContactDatabaseFrontend extends BaseDatabaseFrontend implements Con * @return A Storeable instance */ @Override - public Storeable parseLineToStoreable (final String line) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + public Storeable parseLineToStoreable (final String line) throws BadTokenException { + // Trace message + this.getLogger().trace(MessageFormat.format("line={0} - CALLED!", line)); //NOI18N + + // Call inner method + Contact contact = this.parseLineToContact(line); + + // Debug message + this.getLogger().debug(MessageFormat.format("contact={0}", contact)); + + // Return it + return (Storeable) contact; } /** @@ -234,8 +269,262 @@ public class ContactDatabaseFrontend extends BaseDatabaseFrontend implements Con */ @Override public Contact readSingleContact (final int rowIndex) { - // Deligate this to backend instance - return (Contact) this.getBackend().readRow(rowIndex); - + try { + // Deligate this to backend instance + return (Contact) this.getBackend().readRow(rowIndex); + } catch (final BadTokenException ex) { + // Bad token found + this.abortProgramWithException(ex); + } + + // Bad state, should not be reached + throw new IllegalStateException("This should not be reached"); + } + + /** + * Parses given line and creates a Contact instance + * + * @param line Raw line to parse + * + * @return Contact instance + */ + private Contact parseLineToContact (final String line) throws BadTokenException { + // Trace message + this.getLogger().trace(MessageFormat.format("line={0} - CALLED!", line)); //NOI18N + + // Init A lot variables + Long num = null; + Boolean bool = null; + Gender gender = null; + int count = 0; + Contact contact = null; + + // Debug message + this.getLogger().debug(MessageFormat.format("line={0}", line)); //NOI18N + + // Then tokenize it + // @TODO Move this into separate method + StringTokenizer tokenizer = new StringTokenizer(line, ";"); //NOI18N + + // Reset variables + count = 0; + contact = null; + + // The tokens are now available, so get all + while (tokenizer.hasMoreElements()) { + // Reset variables + num = null; + bool = null; + + // Get next token + String token = tokenizer.nextToken(); + + // If char " is at pos 2 (0,1,2), then cut it of there + if ((token.charAt(0) != '"') && (token.charAt(2) == '"')) { + // UTF-8 writer characters found + token = token.substring(2); + } + + // Debug message + this.getLogger().debug(MessageFormat.format("token={0}", token)); //NOI18N + + // Verify token, it must have double-quotes on each side + if ((!token.startsWith("\"")) || (!token.endsWith("\""))) { //NOI18N + // Something bad was read + throw new BadTokenException(token, count); //NOI18N + } + + // All fine, so remove it + String strippedToken = token.substring(1, token.length() - 1); + + // Is the string's content "null"? + if (strippedToken.equals("null")) { //NOI18N + // Debug message + this.getLogger().debug(MessageFormat.format("strippedToken={0} - NULL!", strippedToken)); //NOI18N + + // This needs to be set to null + strippedToken = null; + } + + // Debug message + this.getLogger().debug(MessageFormat.format("strippedToken={0}", strippedToken)); //NOI18N + + // Now, let's try a number check, if no null + if (strippedToken != null) { + // Okay, no null, maybe the string bears a decimal number? + try { + num = Long.valueOf(strippedToken); + + // Debug message + this.getLogger().debug(MessageFormat.format("strippedToken={0} - NUMBER!", strippedToken)); //NOI18N + } catch (final NumberFormatException ex) { + // No number, then set default + num = null; + } + } + + // Now, let's try a boolean check, if no null + if ((strippedToken != null) && (num == null) && ((strippedToken.equals("true")) || (strippedToken.equals("false")))) { //NOI18N + // Debug message + this.getLogger().debug(MessageFormat.format("strippedToken={0} - BOOLEAN!", strippedToken)); //NOI18N + + // parseBoolean() is relaxed, so no exceptions + bool = Boolean.valueOf(strippedToken); + } + + // Debug message + this.getLogger().debug(MessageFormat.format("strippedToken={0},num={1},bool={2}", strippedToken, num, bool)); //NOI18N + + // Now, let's try a gender check, if no null + if ((strippedToken != null) && (num == null) && (bool == null) && ((strippedToken.equals("M")) || (strippedToken.equals("F")) || (strippedToken.equals("C")))) { //NOI18N + // Get first character + gender = Gender.fromChar(strippedToken.charAt(0)); + + // Debug message + this.getLogger().debug(MessageFormat.format("strippedToken={0},gender={1}", strippedToken, gender)); //NOI18N + + // This instance must be there + assert (gender instanceof Gender) : MessageFormat.format("gender is not set by Gender.fromChar({0})", strippedToken); //NOI18N + } + + // Now it depends on the counter which position we need to check + switch (count) { + case 0: // isOwnContact + assert ((bool instanceof Boolean)); + + // Debug message + this.getLogger().debug(MessageFormat.format("bool={0}", bool)); //NOI18N + + // Is it own contact? + if (true == bool) { + // Debug message + this.getLogger().debug("Creating UserContact object ..."); //NOI18N + + // Own entry + contact = new UserContact(); + } else { + // Debug message + this.getLogger().debug("Creating BookContact object ..."); //NOI18N + + // Other contact + contact = new BookContact(); + } + break; + + case 1: // Gender + assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N + + // Update data + contact.updateNameData(gender, null, null, null); + break; + + case 2: // Surname + assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N + assert (gender instanceof Gender) : "gender instance is not set"; //NOI18N + + // Update data + contact.updateNameData(gender, strippedToken, null, null); + break; + + case 3: // Family name + assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N + assert (gender instanceof Gender) : "gender instance is not set"; //NOI18N + + // Update data + contact.updateNameData(gender, null, strippedToken, null); + break; + + case 4: // Company name + assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N + assert (gender instanceof Gender) : "gender instance is not set"; //NOI18N + + // Update data + contact.updateNameData(gender, null, null, strippedToken); + break; + + case 5: // Street number + assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N + + // Update data + contact.updateAddressData(strippedToken, 0, null, null); + break; + + case 6: // ZIP code + assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N + + // Update data + contact.updateAddressData(null, num, null, null); + break; + + case 7: // City name + assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N + + // Update data + contact.updateAddressData(null, 0, strippedToken, null); + break; + + case 8: // Country code + assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N + + // Update data + contact.updateAddressData(null, 0, null, strippedToken); + break; + + case 9: // Phone number + assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N + + // Update data + contact.updateOtherData(strippedToken, null, null, null, null, null); + break; + + case 10: // Fax number + assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N + + // Update data + contact.updateOtherData(null, strippedToken, null, null, null, null); + break; + + case 11: // Cellphone number + assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N + + // Update data + contact.updateOtherData(null, null, strippedToken, null, null, null); + break; + + case 12: // Email address + assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N + + // Update data + contact.updateOtherData(null, null, null, strippedToken, null, null); + break; + + case 13: // Birthday + assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N + + // Update data + contact.updateOtherData(null, null, null, null, strippedToken, null); + break; + + case 14: // Birthday + assert (contact instanceof Contact) : "First token was not boolean"; //NOI18N + + // Update data + contact.updateOtherData(null, null, null, null, null, strippedToken); + break; + + default: // New data entry + this.getLogger().warn(MessageFormat.format("Will not handle unknown data {0} at index {1}", strippedToken, count)); //NOI18N + break; + } + + // Increment counter for next round + count++; + } + + // Trace message + this.getLogger().trace(MessageFormat.format("contact={0} - EXIT!", contact)); //NOI18N + + // Return finished instance + return contact; } } diff --git a/Addressbook/src/org/mxchange/addressbook/database/frontend/contact/ContactWrapper.java b/Addressbook/src/org/mxchange/addressbook/database/frontend/contact/ContactFrontend.java similarity index 95% rename from Addressbook/src/org/mxchange/addressbook/database/frontend/contact/ContactWrapper.java rename to Addressbook/src/org/mxchange/addressbook/database/frontend/contact/ContactFrontend.java index 45e015f..66c224a 100644 --- a/Addressbook/src/org/mxchange/addressbook/database/frontend/contact/ContactWrapper.java +++ b/Addressbook/src/org/mxchange/addressbook/database/frontend/contact/ContactFrontend.java @@ -18,7 +18,7 @@ package org.mxchange.addressbook.database.frontend.contact; import java.sql.SQLException; import org.mxchange.addressbook.contact.Contact; -import org.mxchange.addressbook.database.frontend.DatabaseWrapper; +import org.mxchange.addressbook.database.frontend.DatabaseFrontend; import org.mxchange.addressbook.exceptions.BadTokenException; import org.mxchange.addressbook.exceptions.ContactAlreadyAddedException; @@ -26,7 +26,7 @@ import org.mxchange.addressbook.exceptions.ContactAlreadyAddedException; * * @author Roland Häder */ -public interface ContactWrapper extends DatabaseWrapper { +public interface ContactFrontend extends DatabaseFrontend { /** * Adds given contact instance to database diff --git a/Addressbook/src/org/mxchange/addressbook/database/storage/csv/StoreableCsv.java b/Addressbook/src/org/mxchange/addressbook/database/storage/csv/StoreableCsv.java index 31df57a..faf976b 100644 --- a/Addressbook/src/org/mxchange/addressbook/database/storage/csv/StoreableCsv.java +++ b/Addressbook/src/org/mxchange/addressbook/database/storage/csv/StoreableCsv.java @@ -22,13 +22,19 @@ import org.mxchange.addressbook.database.storage.Storeable; * An interface for classes which should be storeable as a CSV string * * @author Roland Haeder + * @deprecated This method was for an old way of storing data into + * comma-separated value files. Now that there is BASE64-encoding, this formating + * is no longer needed. */ +@Deprecated public interface StoreableCsv extends Storeable { /** * Getter for a CSV-formated string from object * * @return + * @deprecated See interface deprecation */ + @Deprecated public String getCsvStringFromStoreableObject (); } diff --git a/Addressbook/src/org/mxchange/addressbook/manager/contact/ContactManager.java b/Addressbook/src/org/mxchange/addressbook/manager/contact/ContactManager.java index 6942ed2..4613e0d 100644 --- a/Addressbook/src/org/mxchange/addressbook/manager/contact/ContactManager.java +++ b/Addressbook/src/org/mxchange/addressbook/manager/contact/ContactManager.java @@ -19,13 +19,14 @@ package org.mxchange.addressbook.manager.contact; import java.sql.SQLException; import java.text.MessageFormat; import java.util.ArrayList; +import java.util.Arrays; import java.util.Iterator; import java.util.List; import org.mxchange.addressbook.client.Client; import org.mxchange.addressbook.contact.Contact; import org.mxchange.addressbook.contact.Gender; import org.mxchange.addressbook.database.frontend.contact.ContactDatabaseFrontend; -import org.mxchange.addressbook.database.frontend.contact.ContactWrapper; +import org.mxchange.addressbook.database.frontend.contact.ContactFrontend; import org.mxchange.addressbook.exceptions.BadTokenException; import org.mxchange.addressbook.exceptions.ContactAlreadyAddedException; import org.mxchange.addressbook.exceptions.UnhandledUserChoiceException; @@ -46,9 +47,14 @@ public class ContactManager extends BaseManager implements ManageableContact { private final List columnNames; /** - * A ContactWrapper instance + * Translated column name list */ - private final ContactWrapper contactDatabase; + private final List translatedColumnNames; + + /** + * A ContactFrontend instance + */ + private final ContactFrontend contactDatabase; /** * Constructor which accepts maxContacts for maximum (initial) contacts and @@ -74,6 +80,7 @@ public class ContactManager extends BaseManager implements ManageableContact { // Initialize list this.columnNames = new ArrayList<>(15); + this.translatedColumnNames = new ArrayList<>(15); // And fill it this.fillColumnNamesFromBundle(); @@ -498,7 +505,7 @@ public class ContactManager extends BaseManager implements ManageableContact { * Getter for column name at given index. * * @param columnIndex Column index - * @return Human-readable column name + * @return Database column name */ @Override public String getColumnName (final int columnIndex) { @@ -508,6 +515,20 @@ public class ContactManager extends BaseManager implements ManageableContact { return this.columnNames.get(columnIndex); } + /** + * Getter for translated column name at given index. + * + * @param columnIndex Column index + * @return Human-readable column name + */ + @Override + public String getTranslatedColumnName (final int columnIndex) { + assert (this.translatedColumnNames instanceof List) : "this.translatedColumnNames is not initialized"; //NOI18N + + // Get column name at index + return this.translatedColumnNames.get(columnIndex); + } + /** * Somewhat "getter" for value from given row and column index * @@ -647,6 +668,7 @@ public class ContactManager extends BaseManager implements ManageableContact { */ private void fillColumnNamesFromBundle () { assert (this.columnNames instanceof List) : "this.columnNames is not initialized"; //NOI18N + assert (this.translatedColumnNames instanceof List) : "this.translatedColumnNames is not initialized"; //NOI18N // Debug message this.getLogger().trace("CALLED!"); //NOI18N @@ -664,8 +686,21 @@ public class ContactManager extends BaseManager implements ManageableContact { // This is the wanted entry. this.getLogger().debug(MessageFormat.format("key={0}", key)); //NOI18N + // Convert string to array based on delimiter '.' + String[] tokens = this.getArrayFromString(key, ".", 4); + + // Token array must contain 4 elements (ContactManager.columnName.foo.text) + assert(tokens.length == 4) : MessageFormat.format("Array tokens contains not 4 elements: {0}", Arrays.toString(tokens)); + + // Get pre-last element + String columnName = tokens[tokens.length - 2]; + + // Debug message + this.getLogger().debug(MessageFormat.format("columnName={0} - adding ...", columnName)); + // So add it - this.columnNames.add(this.getBundle().getString(key)); + this.columnNames.add(columnName); + this.translatedColumnNames.add(this.getBundle().getString(key)); } } @@ -674,11 +709,11 @@ public class ContactManager extends BaseManager implements ManageableContact { } /** - * A ContactWrapper instance + * A ContactFrontend instance * * @return the database */ - private ContactWrapper getContactDatabase () { + private ContactFrontend getContactDatabase () { return this.contactDatabase; } diff --git a/Addressbook/src/org/mxchange/addressbook/manager/contact/ManageableContact.java b/Addressbook/src/org/mxchange/addressbook/manager/contact/ManageableContact.java index e2d0ce0..1dd2fc9 100644 --- a/Addressbook/src/org/mxchange/addressbook/manager/contact/ManageableContact.java +++ b/Addressbook/src/org/mxchange/addressbook/manager/contact/ManageableContact.java @@ -139,10 +139,18 @@ public interface ManageableContact extends Manageable { * Getter for column name at given index. * * @param columnIndex Column index - * @return Human-readable column name + * @return Database column name */ public String getColumnName (final int columnIndex); + /** + * Getter for translated column name at given index. + * + * @param columnIndex Column index + * @return Human-readable column name + */ + public String getTranslatedColumnName (int columnIndex); + /** * Somewhat "getter" for value from given row and column index * diff --git a/Addressbook/src/org/mxchange/addressbook/model/contact/ContactTableModel.java b/Addressbook/src/org/mxchange/addressbook/model/contact/ContactTableModel.java index 7a0a48e..d0e2866 100644 --- a/Addressbook/src/org/mxchange/addressbook/model/contact/ContactTableModel.java +++ b/Addressbook/src/org/mxchange/addressbook/model/contact/ContactTableModel.java @@ -62,7 +62,7 @@ public class ContactTableModel extends BaseModel implements TableModel { @Override public String getColumnName (final int columnIndex) { // Deligate this call to contact manager - return this.getClient().getContactManager().getColumnName(columnIndex); + return this.getClient().getContactManager().getTranslatedColumnName(columnIndex); } @Override -- 2.39.2