</Appenders>
<Loggers>
<Root level="trace">
- <AppenderRef ref="STDOUT" level="DEBUG"/>
+ <AppenderRef ref="STDOUT" level="TRACE"/>
</Root>
</Loggers>
</Configuration>
--- /dev/null
+/*\r
+ * Copyright (C) 2015 Roland Haeder\r
+ *\r
+ * This program is free software: you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation, either version 3 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.\r
+ */\r
+package org.mxchange.addressbook;\r
+\r
+/**\r
+ *\r
+ * @author Roland Haeder\r
+ */\r
+public class BadTokenException extends Exception {\r
+\r
+ public BadTokenException (final String str) {\r
+ super(str);\r
+ }\r
+ \r
+}\r
super();\r
}\r
\r
- /**\r
- * Enables the flag "own data" which signals that this contact is the user's\r
- * own data.\r
- */\r
- public void enableFlagOwnContact () {\r
- this.ownContact = true;\r
- }\r
-\r
/**\r
* Check if contacts are same or throw an exception\r
*\r
this.countryCode = countryCode;\r
}\r
\r
+ /**\r
+ * "Serializes" this object into a CSV string (this time with semicolons)\r
+ *\r
+ * @return "CSV-serialized" version of the stored data\r
+ */\r
+ public String getCsvStringFromStoreableObject () {\r
+ // Get all together\r
+ String csvString = String.format(\r
+ "\"%s\";\"%s\";\"%s\";\"%s\";\"%s\";\"%s\";\"%s\";\"%s\";\"%s\";\"%s\";\"%s\";\"%s\";\"%s\";\"%s\";\"%s\"\n",\r
+ this.isOwnContact(),\r
+ this.getGender(),\r
+ this.getSurname(),\r
+ this.getFamilyName(),\r
+ this.getCompanyName(),\r
+ this.getStreet(),\r
+ this.getZipCode(),\r
+ this.getCity(),\r
+ this.getCountryCode(),\r
+ this.getPhoneNumber(),\r
+ this.getFaxNumber(),\r
+ this.getCellphoneNumber(),\r
+ this.getEmailAddress(),\r
+ this.getBirthday(),\r
+ this.getComment()\r
+ );\r
+ \r
+ // Then return it\r
+ return csvString;\r
+ }\r
+\r
/**\r
* Email address\r
*\r
*/\r
public void updateAddressData (final String street, final int zipCode, final String city, final String countryCode) {\r
// Set all\r
- this.setStreet(street);\r
- this.setZipCode(zipCode);\r
- this.setCity(city);\r
- this.setCountryCode(countryCode);\r
+ if (street != null) {\r
+ this.setStreet(street);\r
+ }\r
+ if (city != null) {\r
+ this.setZipCode(zipCode);\r
+ }\r
+ if (city != null) {\r
+ this.setCity(city);\r
+ }\r
+ if (countryCode != null) {\r
+ this.setCountryCode(countryCode);\r
+ }\r
}\r
\r
/**\r
public void updateNameData (final char gender, final String surname, final String familyName, final String companyName) {\r
// Set all\r
this.setGender(gender);\r
- this.setSurname(surname);\r
- this.setFamilyName(familyName);\r
- this.setCompanyName(companyName);\r
+ if (surname != null) {\r
+ this.setSurname(surname);\r
+ }\r
+ if (familyName != null) {\r
+ this.setFamilyName(familyName);\r
+ }\r
+ if (companyName != null) {\r
+ this.setCompanyName(companyName);\r
+ }\r
}\r
\r
/**\r
*/\r
public void updateOtherData (final String phoneNumber, final String cellphoneNumber, final String faxNumber, final String emailAddress, final String comment) {\r
// Set all\r
- this.setPhoneNumber(phoneNumber);\r
- this.setCellphoneNumber(cellphoneNumber);\r
- this.setFaxNumber(faxNumber);\r
- this.setEmailAddress(emailAddress);\r
- this.setComment(comment);\r
+ if (phoneNumber != null) {\r
+ this.setPhoneNumber(phoneNumber);\r
+ }\r
+ if (cellphoneNumber != null) {\r
+ this.setCellphoneNumber(cellphoneNumber);\r
+ }\r
+ if (faxNumber != null) {\r
+ this.setFaxNumber(faxNumber);\r
+ }\r
+ if (emailAddress != null) {\r
+ this.setEmailAddress(emailAddress);\r
+ }\r
+ if (comment != null) {\r
+ this.setComment(comment);\r
+ }\r
}\r
\r
/**\r
- * "Serializes" this object into a CSV string (this time with semicolons)\r
- * \r
- * @return "CSV-serialized" version of the stored data\r
+ * Enables the flag "own data" which signals that this contact is the user's\r
+ * own data.\r
*/\r
- public String getCsvStringFromStoreableObject () {\r
- // Get all together\r
- String csvString = String.format(\r
- "\"%s\";\"%s\";\"%s\";\"%s\";\"%s\";\"%s\";\"%s\";\"%s\";\"%s\";\"%s\";\"%s\";\"%s\";\"%s\";\"%s\";\"%s\"\n",\r
- this.isOwnContact(),\r
- this.getGender(),\r
- this.getSurname(),\r
- this.getFamilyName(),\r
- this.getCompanyName(),\r
- this.getStreet(),\r
- this.getZipCode(),\r
- this.getCity(),\r
- this.getCountryCode(),\r
- this.getPhoneNumber(),\r
- this.getFaxNumber(),\r
- this.getCellphoneNumber(),\r
- this.getEmailAddress(),\r
- this.getBirthday(),\r
- this.getComment()\r
- );\r
-\r
- // Then return it\r
- return csvString;\r
+ protected void enableFlagOwnContact () {\r
+ this.ownContact = true;\r
}\r
}\r
*/\r
public interface Contact extends FrameworkInterface {\r
\r
- /**\r
- * Enables the flag "own data" which signals that this contact is the user's\r
- * own data.\r
- */\r
- public void enableFlagOwnContact();\r
-\r
/**\r
* Some "getter" for translated gender of the contact\r
* @return Translated / human-readable gender\r
*/\r
public class BookContact extends BaseContact implements Contact, StoreableCsv {\r
\r
+ /**\r
+ * Default constructor, may only be used from database backend\r
+ */\r
+ public BookContact () {\r
+ }\r
+\r
}\r
}\r
\r
/**\r
- * No empty instances can be created of this class\r
+ * Default constructor, may only be used from database backend\r
*/\r
- protected UserContact () {\r
- super();\r
+ public UserContact () {\r
+ this.enableFlagOwnContact();\r
}\r
}\r
*/\r
public void rewind ();\r
\r
+ /**\r
+ * Get length of underlaying file\r
+ *\r
+ * @return Length of underlaying file\r
+ */\r
+ public long length ();\r
+\r
/**\r
* Stores an object in the database.\r
* \r
package org.mxchange.addressbook.database.backend.csv;\r
\r
import java.util.Iterator;\r
+import org.mxchange.addressbook.BadTokenException;\r
import org.mxchange.addressbook.contact.Contact;\r
import org.mxchange.addressbook.database.backend.DatabaseBackend;\r
\r
* \r
* @return Iterator for contacts\r
*/\r
- public Iterator<Contact> contactIterator ();\r
+ public Iterator<Contact> contactIterator () throws BadTokenException;\r
}\r
import java.io.IOException;\r
import java.io.RandomAccessFile;\r
import java.text.MessageFormat;\r
+import java.util.ArrayList;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+import java.util.StringTokenizer;\r
+import org.mxchange.addressbook.BadTokenException;\r
+import org.mxchange.addressbook.contact.Contact;\r
+import org.mxchange.addressbook.contact.book.BookContact;\r
+import org.mxchange.addressbook.contact.user.UserContact;\r
import org.mxchange.addressbook.database.backend.BaseDatabaseBackend;\r
import org.mxchange.addressbook.database.storage.Storeable;\r
import org.mxchange.addressbook.database.storage.csv.StoreableCsv;\r
this.getLogger().debug(MessageFormat.format("Database for {0} has been initialized.", tableName));\r
}\r
\r
+ /**\r
+ * Gets an iterator for contacts\r
+ * \r
+ * @return Iterator for contacts\r
+ * @throws org.mxchange.addressbook.BadTokenException If the underlaying method has found an invalid token\r
+ */\r
+ @Override\r
+ public Iterator<Contact> contactIterator () throws BadTokenException {\r
+ /*\r
+ * Then read the file into RAM (yes, not perfect for >1000 entries ...)\r
+ * and get a List back.\r
+ */\r
+ List<Contact> list = this.readContactList();\r
+\r
+ // Get iterator from list and return it\r
+ return list.iterator();\r
+ }\r
+\r
+ /**\r
+ * Get length of underlaying file\r
+ *\r
+ * @return Length of underlaying file\r
+ */\r
@Override\r
- public void rewind () {\r
+ public long length () {\r
+ long length = 0;\r
+ \r
+ try {\r
+ length = this.storageFile.length();\r
+ this.getLogger().debug(MessageFormat.format("length={0}", length));\r
+ } catch (final IOException ex) {\r
+ // Length cannot be determined\r
+ this.getLogger().catching(ex);\r
+ System.exit(1);\r
+ }\r
+ \r
+ // Return result\r
+ this.getLogger().trace(MessageFormat.format("length={0} : EXIT!", length));\r
+ return length;\r
+ }\r
+\r
+ /**\r
+ * Rewinds backend\r
+ */\r
+ @Override\r
+ public void rewind (){\r
+ this.getLogger().trace("CALLED!");\r
+\r
try {\r
// Rewind underlaying database file\r
this.storageFile.seek(0);\r
this.getLogger().catching(ex);\r
System.exit(1);\r
}\r
+\r
+ this.getLogger().trace("EXIT!");\r
}\r
\r
/**\r
* @throws java.io.IOException From "inner" class\r
*/\r
@Override\r
- public void store (final Storeable object) throws IOException{\r
+ public void store (final Storeable object) throws IOException {\r
// Make sure the instance is there (DataOutput flawor)\r
assert(this.storageFile instanceof DataOutput);\r
\r
this.getLogger().debug(MessageFormat.format("str({0})={1}", str.length(), str));\r
\r
// The string is now a valid CSV string\r
- this.storageFile.writeUTF(str);\r
+ this.storageFile.writeChars(str);\r
+ }\r
+\r
+ /**\r
+ * Checks whether end of file has been reached\r
+ * \r
+ * @return Whether lines are left to read\r
+ */\r
+ private boolean isEndOfFile () {\r
+ // Default is EOF\r
+ boolean isEof = true;\r
+\r
+ try {\r
+ isEof = (this.storageFile.getFilePointer() >= this.length());\r
+ } catch (final IOException ex) {\r
+ // Length cannot be determined\r
+ this.getLogger().catching(ex);\r
+ }\r
+\r
+ // Return status\r
+ this.getLogger().trace(MessageFormat.format("isEof={0} : EXIT!", isEof));\r
+ return isEof;\r
+ }\r
+\r
+ /**\r
+ * Reads the database file, if available, and adds all read lines into\r
+ * the list.\r
+ * \r
+ * @return A list with Contact instances\r
+ */\r
+ private List<Contact> readContactList () throws BadTokenException {\r
+ this.getLogger().trace("CALLED!");\r
+\r
+ // First rewind\r
+ this.rewind();\r
+\r
+ // Instance list\r
+ // @TODO The maximum length could be guessed from file size?\r
+ List<Contact> list = new ArrayList<>();\r
+\r
+ // Init variables\r
+ StringTokenizer tokenizer;\r
+ String line;\r
+\r
+ // Read all lines\r
+ while (!this.isEndOfFile()) {\r
+ // Then read a line\r
+ line = this.readLine();\r
+\r
+ // Debug message\r
+ this.getLogger().debug(MessageFormat.format("line={0}", line));\r
+\r
+ // Then tokenize it\r
+ // @TODO Move this into separate method\r
+ tokenizer = new StringTokenizer(line, ";");\r
+\r
+ // Count round\r
+ int count = 0;\r
+\r
+ // Init contact object\r
+ Contact contact = null;\r
+\r
+ // The tokens are now available, so get all\r
+ while (tokenizer.hasMoreElements()) {\r
+ // Get next token\r
+ String token = tokenizer.nextToken();\r
+\r
+ // Debug message\r
+ this.getLogger().debug(MessageFormat.format("token={0}", token));\r
+\r
+ // Verify token, it must have double-quotes on each side\r
+ if ((!token.startsWith("\"")) || (!token.endsWith("\""))) {\r
+ // Something bad was read\r
+ throw new BadTokenException(MessageFormat.format("Token {0} has not double-quotes on both ends.", token));\r
+ }\r
+\r
+ // All fine, so remove it\r
+ String strippedToken = token.substring(1, token.length() - 1);\r
+\r
+ // Is the string's content "null"?\r
+ if (strippedToken.equals("null")) {\r
+ // Debug message\r
+ this.getLogger().debug(MessageFormat.format("strippedToken={0} - NULL!", strippedToken));\r
+\r
+ // This needs to be set to null\r
+ strippedToken = null;\r
+ }\r
+\r
+ // Debug message\r
+ this.getLogger().debug(MessageFormat.format("strippedToken={0}", strippedToken));\r
+\r
+ // Init number/string data values\r
+ String strData = strippedToken;\r
+ Long num = null;\r
+ Boolean bool = null;\r
+ char gender = '?';\r
+ \r
+ // Now, let's try a number check, if no null\r
+ if (strippedToken != null) {\r
+ // Okay, no null, maybe the string bears a decimal number?\r
+ try {\r
+ num = Long.valueOf(strippedToken);\r
+\r
+ // Debug message\r
+ this.getLogger().debug(MessageFormat.format("strippedToken={0} - NUMBER!", strippedToken));\r
+ } catch (final NumberFormatException ex) {\r
+ // No number, then set default\r
+ num = null;\r
+ }\r
+ }\r
+ \r
+ // Now, let's try a boolean check, if no null\r
+ if ((strippedToken != null) && (num == null) && ((strippedToken.equals("true")) || (strippedToken.equals("false")))) {\r
+ // Debug message\r
+ this.getLogger().debug(MessageFormat.format("strippedToken={0} - BOOLEAN!", strippedToken));\r
+\r
+ // parseBoolean() is relaxed, so no exceptions\r
+ bool = Boolean.valueOf(strippedToken);\r
+ }\r
+ \r
+ // Now, let's try a boolean check, if no null\r
+ if ((strippedToken != null) && (num == null) && (bool == null) && ((strippedToken.equals("M")) || (strippedToken.equals("F")) || (strippedToken.equals("C")))) {\r
+ // Get first character\r
+ gender = strippedToken.charAt(0);\r
+ }\r
+\r
+ // Now it depends on the counter which position we need to check\r
+ switch (count) {\r
+ case 0: // isOwnContact\r
+ assert((bool instanceof Boolean));\r
+\r
+ // Is it own contact?\r
+ if (true == bool) {\r
+ // Own entry\r
+ contact = new UserContact();\r
+ } else {\r
+ // Other contact\r
+ contact = new BookContact();\r
+ }\r
+ break;\r
+\r
+ case 1: // Gender\r
+ assert(contact instanceof Contact) : "First token was not boolean";\r
+ assert(gender != '?') : "Gender is not detected.";\r
+\r
+ // Update data\r
+ contact.updateNameData(gender, null, null, null);\r
+ break;\r
+\r
+ default: // New data entry\r
+ this.getLogger().warn("Will not handle unknown data " + strippedToken + " at index " + count);\r
+ break;\r
+ }\r
+\r
+ // Increment counter for next round\r
+ count++;\r
+ }\r
+ }\r
+\r
+ // Return finished list\r
+ this.getLogger().trace(MessageFormat.format("list.size()={0} : EXIT!", list.size()));\r
+ return list;\r
+ }\r
+\r
+ /**\r
+ * Reads a line from file base\r
+ *\r
+ * @return Read line from file\r
+ */\r
+ private String readLine () {\r
+ // Init input\r
+ String input = null;\r
+\r
+ try {\r
+ input = this.storageFile.readLine();\r
+ } catch (IOException ex) {\r
+ this.getLogger().catching(ex);\r
+ }\r
+\r
+ // Return read string or null\r
+ return input;\r
}\r
}\r
import java.io.IOException;\r
import java.util.Iterator;\r
import java.util.List;\r
+import org.mxchange.addressbook.BadTokenException;\r
import org.mxchange.addressbook.contact.Contact;\r
import org.mxchange.addressbook.database.backend.csv.CsvBackend;\r
import org.mxchange.addressbook.database.frontend.BaseDatabaseFrontend;\r
this.getBackend().rewind();\r
\r
// Get backend iterator\r
- Iterator<Contact> iterator = .contactIterator();\r
+ Iterator<Contact> iterator = null;\r
+ try {\r
+ iterator = backend.contactIterator();\r
+ } catch (final BadTokenException ex) {\r
+ this.getLogger().catching(ex);\r
+ System.exit(1);\r
+ }\r
\r
// Read all entries\r
while (iterator.hasNext()) {\r
// Get next entry\r
Contact contact = iterator.next();\r
+\r
+ // Add contact instance to manager\r
+ contactManager.addContact(contact);\r
}\r
}\r
}\r
}\r
\r
/**\r
- * Adds given contact to address book\r
+ * Adds given contact to address book and flushes all entries to database\r
*\r
* @param contact Contact being added\r
* @todo Add check for book size\r
*/\r
@Override\r
- public void addContact (final Contact contact) {\r
+ public void registerContact (final Contact contact) {\r
// Check if contact is found\r
if (this.isContactAlreadyAdded(contact)) {\r
// Contact already added\r
/* NOISY-DEBUG: */ this.getLogger().debug("Adding '" + contact.getSurname() + "' '" + contact.getFamilyName() + "' at pos '" + this.size () + "' ...");\r
\r
// Add contact to internal list\r
- this.contacts.add(contact);\r
+ this.addContact(contact);\r
\r
// Flush whole list\r
this.flush();\r
// Construct UserContact instance\r
Contact contact = new UserContact(gender, surname, familyName, companyName);\r
\r
- // Mark contact as own\r
- contact.enableFlagOwnContact();\r
-\r
// Add it to contact "book"\r
- this.addContact(contact);\r
+ this.registerContact(contact);\r
}\r
\r
/**\r
return this.contacts.size();\r
}\r
\r
+ /**\r
+ * Adds given Contact instance to list\r
+ * \r
+ * @param contact Contact instance to add\r
+ */\r
+ @Override\r
+ public void addContact (final Contact contact) {\r
+ this.contacts.add(contact);\r
+ }\r
+\r
/**\r
* Asks the user for his/her cellphone number\r
* \r
* @param contact Contact being added\r
* @todo Add check for book size\r
*/\r
+ public void registerContact (final Contact contact);\r
+\r
+ /**\r
+ * Adds given Contact instance to list\r
+ * \r
+ * @param contact Contact instance to add\r
+ */\r
public void addContact (final Contact contact);\r
\r
/**\r