From b36c8456e5101449d0be2c5c6b90a84f0b0ef5ea Mon Sep 17 00:00:00 2001 From: Roland Haeder Date: Fri, 17 Jul 2015 15:18:03 +0200 Subject: [PATCH] =?utf8?q?Continued:=20-=20Reading/writing=20to=20database?= =?utf8?q?=20file=20fixed=20-=20Tokenizer=20used=20to=20get=20every=20part?= =?utf8?q?=20of=20each=20data=20line=20-=20updateData()=20needs=20exp?= =?utf8?q?ansion=20Signed-off-by:Roland=20H=C3=A4der=20?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- Addressbook/src/log4j2.xml | 2 +- .../addressbook/BadTokenException.java | 29 +++ .../addressbook/contact/BaseContact.java | 116 +++++---- .../mxchange/addressbook/contact/Contact.java | 6 - .../addressbook/contact/book/BookContact.java | 6 + .../addressbook/contact/user/UserContact.java | 6 +- .../database/backend/DatabaseBackend.java | 7 + .../database/backend/csv/CsvBackend.java | 3 +- .../backend/csv/CsvDatabaseBackend.java | 242 +++++++++++++++++- .../contact/ContactDatabaseFrontend.java | 12 +- .../manager/contact/ContactManager.java | 21 +- .../manager/contact/ManageableContact.java | 7 + 12 files changed, 389 insertions(+), 68 deletions(-) create mode 100644 Addressbook/src/org/mxchange/addressbook/BadTokenException.java diff --git a/Addressbook/src/log4j2.xml b/Addressbook/src/log4j2.xml index d3ade86..65614c6 100644 --- a/Addressbook/src/log4j2.xml +++ b/Addressbook/src/log4j2.xml @@ -23,7 +23,7 @@ along with this program. If not, see . - + diff --git a/Addressbook/src/org/mxchange/addressbook/BadTokenException.java b/Addressbook/src/org/mxchange/addressbook/BadTokenException.java new file mode 100644 index 0000000..ce02e50 --- /dev/null +++ b/Addressbook/src/org/mxchange/addressbook/BadTokenException.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2015 Roland Haeder + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.mxchange.addressbook; + +/** + * + * @author Roland Haeder + */ +public class BadTokenException extends Exception { + + public BadTokenException (final String str) { + super(str); + } + +} diff --git a/Addressbook/src/org/mxchange/addressbook/contact/BaseContact.java b/Addressbook/src/org/mxchange/addressbook/contact/BaseContact.java index 3e9da4c..bb7df12 100644 --- a/Addressbook/src/org/mxchange/addressbook/contact/BaseContact.java +++ b/Addressbook/src/org/mxchange/addressbook/contact/BaseContact.java @@ -116,14 +116,6 @@ public class BaseContact extends BaseFrameworkSystem { super(); } - /** - * Enables the flag "own data" which signals that this contact is the user's - * own data. - */ - public void enableFlagOwnContact () { - this.ownContact = true; - } - /** * Check if contacts are same or throw an exception * @@ -259,6 +251,36 @@ public class BaseContact extends BaseFrameworkSystem { this.countryCode = countryCode; } + /** + * "Serializes" this object into a CSV string (this time with semicolons) + * + * @return "CSV-serialized" version of the stored data + */ + public String getCsvStringFromStoreableObject () { + // Get all together + String csvString = String.format( + "\"%s\";\"%s\";\"%s\";\"%s\";\"%s\";\"%s\";\"%s\";\"%s\";\"%s\";\"%s\";\"%s\";\"%s\";\"%s\";\"%s\";\"%s\"\n", + this.isOwnContact(), + this.getGender(), + this.getSurname(), + this.getFamilyName(), + this.getCompanyName(), + this.getStreet(), + this.getZipCode(), + this.getCity(), + this.getCountryCode(), + this.getPhoneNumber(), + this.getFaxNumber(), + this.getCellphoneNumber(), + this.getEmailAddress(), + this.getBirthday(), + this.getComment() + ); + + // Then return it + return csvString; + } + /** * Email address * @@ -496,10 +518,18 @@ public class BaseContact extends BaseFrameworkSystem { */ public void updateAddressData (final String street, final int zipCode, final String city, final String countryCode) { // Set all - this.setStreet(street); - this.setZipCode(zipCode); - this.setCity(city); - this.setCountryCode(countryCode); + if (street != null) { + this.setStreet(street); + } + if (city != null) { + this.setZipCode(zipCode); + } + if (city != null) { + this.setCity(city); + } + if (countryCode != null) { + this.setCountryCode(countryCode); + } } /** @@ -512,9 +542,15 @@ public class BaseContact extends BaseFrameworkSystem { public void updateNameData (final char gender, final String surname, final String familyName, final String companyName) { // Set all this.setGender(gender); - this.setSurname(surname); - this.setFamilyName(familyName); - this.setCompanyName(companyName); + if (surname != null) { + this.setSurname(surname); + } + if (familyName != null) { + this.setFamilyName(familyName); + } + if (companyName != null) { + this.setCompanyName(companyName); + } } /** @@ -528,40 +564,28 @@ public class BaseContact extends BaseFrameworkSystem { */ public void updateOtherData (final String phoneNumber, final String cellphoneNumber, final String faxNumber, final String emailAddress, final String comment) { // Set all - this.setPhoneNumber(phoneNumber); - this.setCellphoneNumber(cellphoneNumber); - this.setFaxNumber(faxNumber); - this.setEmailAddress(emailAddress); - this.setComment(comment); + if (phoneNumber != null) { + this.setPhoneNumber(phoneNumber); + } + if (cellphoneNumber != null) { + this.setCellphoneNumber(cellphoneNumber); + } + if (faxNumber != null) { + this.setFaxNumber(faxNumber); + } + if (emailAddress != null) { + this.setEmailAddress(emailAddress); + } + if (comment != null) { + this.setComment(comment); + } } /** - * "Serializes" this object into a CSV string (this time with semicolons) - * - * @return "CSV-serialized" version of the stored data + * Enables the flag "own data" which signals that this contact is the user's + * own data. */ - public String getCsvStringFromStoreableObject () { - // Get all together - String csvString = String.format( - "\"%s\";\"%s\";\"%s\";\"%s\";\"%s\";\"%s\";\"%s\";\"%s\";\"%s\";\"%s\";\"%s\";\"%s\";\"%s\";\"%s\";\"%s\"\n", - this.isOwnContact(), - this.getGender(), - this.getSurname(), - this.getFamilyName(), - this.getCompanyName(), - this.getStreet(), - this.getZipCode(), - this.getCity(), - this.getCountryCode(), - this.getPhoneNumber(), - this.getFaxNumber(), - this.getCellphoneNumber(), - this.getEmailAddress(), - this.getBirthday(), - this.getComment() - ); - - // Then return it - return csvString; + protected void enableFlagOwnContact () { + this.ownContact = true; } } diff --git a/Addressbook/src/org/mxchange/addressbook/contact/Contact.java b/Addressbook/src/org/mxchange/addressbook/contact/Contact.java index fb75fdb..9b13801 100644 --- a/Addressbook/src/org/mxchange/addressbook/contact/Contact.java +++ b/Addressbook/src/org/mxchange/addressbook/contact/Contact.java @@ -25,12 +25,6 @@ import org.mxchange.addressbook.client.Client; */ public interface Contact extends FrameworkInterface { - /** - * Enables the flag "own data" which signals that this contact is the user's - * own data. - */ - public void enableFlagOwnContact(); - /** * Some "getter" for translated gender of the contact * @return Translated / human-readable gender diff --git a/Addressbook/src/org/mxchange/addressbook/contact/book/BookContact.java b/Addressbook/src/org/mxchange/addressbook/contact/book/BookContact.java index c7a2657..02d4218 100644 --- a/Addressbook/src/org/mxchange/addressbook/contact/book/BookContact.java +++ b/Addressbook/src/org/mxchange/addressbook/contact/book/BookContact.java @@ -29,4 +29,10 @@ import org.mxchange.addressbook.database.storage.csv.StoreableCsv; */ public class BookContact extends BaseContact implements Contact, StoreableCsv { + /** + * Default constructor, may only be used from database backend + */ + public BookContact () { + } + } diff --git a/Addressbook/src/org/mxchange/addressbook/contact/user/UserContact.java b/Addressbook/src/org/mxchange/addressbook/contact/user/UserContact.java index 81a5d30..1f9b141 100644 --- a/Addressbook/src/org/mxchange/addressbook/contact/user/UserContact.java +++ b/Addressbook/src/org/mxchange/addressbook/contact/user/UserContact.java @@ -47,9 +47,9 @@ public class UserContact extends BookContact implements Contact, StoreableCsv { } /** - * No empty instances can be created of this class + * Default constructor, may only be used from database backend */ - protected UserContact () { - super(); + public UserContact () { + this.enableFlagOwnContact(); } } diff --git a/Addressbook/src/org/mxchange/addressbook/database/backend/DatabaseBackend.java b/Addressbook/src/org/mxchange/addressbook/database/backend/DatabaseBackend.java index 0d83aca..fad842e 100644 --- a/Addressbook/src/org/mxchange/addressbook/database/backend/DatabaseBackend.java +++ b/Addressbook/src/org/mxchange/addressbook/database/backend/DatabaseBackend.java @@ -32,6 +32,13 @@ public interface DatabaseBackend extends FrameworkInterface { */ public void rewind (); + /** + * Get length of underlaying file + * + * @return Length of underlaying file + */ + public long length (); + /** * Stores an object in the database. * diff --git a/Addressbook/src/org/mxchange/addressbook/database/backend/csv/CsvBackend.java b/Addressbook/src/org/mxchange/addressbook/database/backend/csv/CsvBackend.java index 4a4df5c..704cbb6 100644 --- a/Addressbook/src/org/mxchange/addressbook/database/backend/csv/CsvBackend.java +++ b/Addressbook/src/org/mxchange/addressbook/database/backend/csv/CsvBackend.java @@ -17,6 +17,7 @@ package org.mxchange.addressbook.database.backend.csv; import java.util.Iterator; +import org.mxchange.addressbook.BadTokenException; import org.mxchange.addressbook.contact.Contact; import org.mxchange.addressbook.database.backend.DatabaseBackend; @@ -31,5 +32,5 @@ public interface CsvBackend extends DatabaseBackend { * * @return Iterator for contacts */ - public Iterator contactIterator (); + public Iterator contactIterator () throws BadTokenException; } diff --git a/Addressbook/src/org/mxchange/addressbook/database/backend/csv/CsvDatabaseBackend.java b/Addressbook/src/org/mxchange/addressbook/database/backend/csv/CsvDatabaseBackend.java index a070abb..8a026e1 100644 --- a/Addressbook/src/org/mxchange/addressbook/database/backend/csv/CsvDatabaseBackend.java +++ b/Addressbook/src/org/mxchange/addressbook/database/backend/csv/CsvDatabaseBackend.java @@ -21,6 +21,14 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.StringTokenizer; +import org.mxchange.addressbook.BadTokenException; +import org.mxchange.addressbook.contact.Contact; +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.storage.Storeable; import org.mxchange.addressbook.database.storage.csv.StoreableCsv; @@ -67,8 +75,54 @@ public class CsvDatabaseBackend extends BaseDatabaseBackend implements CsvBacken this.getLogger().debug(MessageFormat.format("Database for {0} has been initialized.", tableName)); } + /** + * Gets an iterator for contacts + * + * @return Iterator for contacts + * @throws org.mxchange.addressbook.BadTokenException If the underlaying method has found an invalid token + */ + @Override + public Iterator contactIterator () throws BadTokenException { + /* + * Then read the file into RAM (yes, not perfect for >1000 entries ...) + * and get a List back. + */ + List list = this.readContactList(); + + // Get iterator from list and return it + return list.iterator(); + } + + /** + * Get length of underlaying file + * + * @return Length of underlaying file + */ @Override - public void rewind () { + public long length () { + long length = 0; + + try { + length = this.storageFile.length(); + this.getLogger().debug(MessageFormat.format("length={0}", length)); + } catch (final IOException ex) { + // Length cannot be determined + this.getLogger().catching(ex); + System.exit(1); + } + + // Return result + this.getLogger().trace(MessageFormat.format("length={0} : EXIT!", length)); + return length; + } + + /** + * Rewinds backend + */ + @Override + public void rewind (){ + this.getLogger().trace("CALLED!"); + try { // Rewind underlaying database file this.storageFile.seek(0); @@ -76,6 +130,8 @@ public class CsvDatabaseBackend extends BaseDatabaseBackend implements CsvBacken this.getLogger().catching(ex); System.exit(1); } + + this.getLogger().trace("EXIT!"); } /** @@ -85,7 +141,7 @@ public class CsvDatabaseBackend extends BaseDatabaseBackend implements CsvBacken * @throws java.io.IOException From "inner" class */ @Override - public void store (final Storeable object) throws IOException{ + public void store (final Storeable object) throws IOException { // Make sure the instance is there (DataOutput flawor) assert(this.storageFile instanceof DataOutput); @@ -99,6 +155,186 @@ public class CsvDatabaseBackend extends BaseDatabaseBackend implements CsvBacken this.getLogger().debug(MessageFormat.format("str({0})={1}", str.length(), str)); // The string is now a valid CSV string - this.storageFile.writeUTF(str); + this.storageFile.writeChars(str); + } + + /** + * Checks whether end of file has been reached + * + * @return Whether lines are left to read + */ + private boolean isEndOfFile () { + // Default is EOF + boolean isEof = true; + + try { + isEof = (this.storageFile.getFilePointer() >= this.length()); + } catch (final IOException ex) { + // Length cannot be determined + this.getLogger().catching(ex); + } + + // Return status + this.getLogger().trace(MessageFormat.format("isEof={0} : EXIT!", isEof)); + return isEof; + } + + /** + * Reads the database file, if available, and adds all read lines into + * the list. + * + * @return A list with Contact instances + */ + private List readContactList () throws BadTokenException { + this.getLogger().trace("CALLED!"); + + // First rewind + this.rewind(); + + // Instance list + // @TODO The maximum length could be guessed from file size? + List list = new ArrayList<>(); + + // Init variables + StringTokenizer tokenizer; + String line; + + // Read all lines + while (!this.isEndOfFile()) { + // Then read a line + line = this.readLine(); + + // Debug message + this.getLogger().debug(MessageFormat.format("line={0}", line)); + + // Then tokenize it + // @TODO Move this into separate method + tokenizer = new StringTokenizer(line, ";"); + + // Count round + int count = 0; + + // Init contact object + Contact contact = null; + + // The tokens are now available, so get all + while (tokenizer.hasMoreElements()) { + // Get next token + String token = tokenizer.nextToken(); + + // Debug message + this.getLogger().debug(MessageFormat.format("token={0}", token)); + + // Verify token, it must have double-quotes on each side + if ((!token.startsWith("\"")) || (!token.endsWith("\""))) { + // Something bad was read + throw new BadTokenException(MessageFormat.format("Token {0} has not double-quotes on both ends.", token)); + } + + // All fine, so remove it + String strippedToken = token.substring(1, token.length() - 1); + + // Is the string's content "null"? + if (strippedToken.equals("null")) { + // Debug message + this.getLogger().debug(MessageFormat.format("strippedToken={0} - NULL!", strippedToken)); + + // This needs to be set to null + strippedToken = null; + } + + // Debug message + this.getLogger().debug(MessageFormat.format("strippedToken={0}", strippedToken)); + + // Init number/string data values + String strData = strippedToken; + Long num = null; + Boolean bool = null; + char gender = '?'; + + // 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)); + } 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")))) { + // Debug message + this.getLogger().debug(MessageFormat.format("strippedToken={0} - BOOLEAN!", strippedToken)); + + // parseBoolean() is relaxed, so no exceptions + bool = Boolean.valueOf(strippedToken); + } + + // Now, let's try a boolean check, if no null + if ((strippedToken != null) && (num == null) && (bool == null) && ((strippedToken.equals("M")) || (strippedToken.equals("F")) || (strippedToken.equals("C")))) { + // Get first character + gender = strippedToken.charAt(0); + } + + // Now it depends on the counter which position we need to check + switch (count) { + case 0: // isOwnContact + assert((bool instanceof Boolean)); + + // Is it own contact? + if (true == bool) { + // Own entry + contact = new UserContact(); + } else { + // Other contact + contact = new BookContact(); + } + break; + + case 1: // Gender + assert(contact instanceof Contact) : "First token was not boolean"; + assert(gender != '?') : "Gender is not detected."; + + // Update data + contact.updateNameData(gender, null, null, null); + break; + + default: // New data entry + this.getLogger().warn("Will not handle unknown data " + strippedToken + " at index " + count); + break; + } + + // Increment counter for next round + count++; + } + } + + // Return finished list + this.getLogger().trace(MessageFormat.format("list.size()={0} : EXIT!", list.size())); + return list; + } + + /** + * Reads a line from file base + * + * @return Read line from file + */ + private String readLine () { + // Init input + String input = null; + + try { + input = this.storageFile.readLine(); + } catch (IOException ex) { + this.getLogger().catching(ex); + } + + // Return read string or null + return input; } } 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 2f55241..87126f0 100644 --- a/Addressbook/src/org/mxchange/addressbook/database/frontend/contact/ContactDatabaseFrontend.java +++ b/Addressbook/src/org/mxchange/addressbook/database/frontend/contact/ContactDatabaseFrontend.java @@ -19,6 +19,7 @@ package org.mxchange.addressbook.database.frontend.contact; import java.io.IOException; import java.util.Iterator; import java.util.List; +import org.mxchange.addressbook.BadTokenException; import org.mxchange.addressbook.contact.Contact; import org.mxchange.addressbook.database.backend.csv.CsvBackend; import org.mxchange.addressbook.database.frontend.BaseDatabaseFrontend; @@ -90,12 +91,21 @@ public class ContactDatabaseFrontend extends BaseDatabaseFrontend implements Con this.getBackend().rewind(); // Get backend iterator - Iterator iterator = .contactIterator(); + Iterator iterator = null; + try { + iterator = backend.contactIterator(); + } catch (final BadTokenException ex) { + this.getLogger().catching(ex); + System.exit(1); + } // Read all entries while (iterator.hasNext()) { // Get next entry Contact contact = iterator.next(); + + // Add contact instance to manager + contactManager.addContact(contact); } } } diff --git a/Addressbook/src/org/mxchange/addressbook/manager/contact/ContactManager.java b/Addressbook/src/org/mxchange/addressbook/manager/contact/ContactManager.java index d4f7c32..3199f81 100644 --- a/Addressbook/src/org/mxchange/addressbook/manager/contact/ContactManager.java +++ b/Addressbook/src/org/mxchange/addressbook/manager/contact/ContactManager.java @@ -73,13 +73,13 @@ public class ContactManager extends BaseManager implements ManageableContact { } /** - * Adds given contact to address book + * Adds given contact to address book and flushes all entries to database * * @param contact Contact being added * @todo Add check for book size */ @Override - public void addContact (final Contact contact) { + public void registerContact (final Contact contact) { // Check if contact is found if (this.isContactAlreadyAdded(contact)) { // Contact already added @@ -93,7 +93,7 @@ public class ContactManager extends BaseManager implements ManageableContact { /* NOISY-DEBUG: */ this.getLogger().debug("Adding '" + contact.getSurname() + "' '" + contact.getFamilyName() + "' at pos '" + this.size () + "' ..."); // Add contact to internal list - this.contacts.add(contact); + this.addContact(contact); // Flush whole list this.flush(); @@ -290,11 +290,8 @@ public class ContactManager extends BaseManager implements ManageableContact { // Construct UserContact instance Contact contact = new UserContact(gender, surname, familyName, companyName); - // Mark contact as own - contact.enableFlagOwnContact(); - // Add it to contact "book" - this.addContact(contact); + this.registerContact(contact); } /** @@ -317,6 +314,16 @@ public class ContactManager extends BaseManager implements ManageableContact { return this.contacts.size(); } + /** + * Adds given Contact instance to list + * + * @param contact Contact instance to add + */ + @Override + public void addContact (final Contact contact) { + this.contacts.add(contact); + } + /** * Asks the user for his/her cellphone number * diff --git a/Addressbook/src/org/mxchange/addressbook/manager/contact/ManageableContact.java b/Addressbook/src/org/mxchange/addressbook/manager/contact/ManageableContact.java index 14c3be6..94d4d55 100644 --- a/Addressbook/src/org/mxchange/addressbook/manager/contact/ManageableContact.java +++ b/Addressbook/src/org/mxchange/addressbook/manager/contact/ManageableContact.java @@ -32,6 +32,13 @@ public interface ManageableContact extends Manageable { * @param contact Contact being added * @todo Add check for book size */ + public void registerContact (final Contact contact); + + /** + * Adds given Contact instance to list + * + * @param contact Contact instance to add + */ public void addContact (final Contact contact); /** -- 2.39.2