summaryrefslogtreecommitdiff
path: root/noncore/apps/tableviewer/db
authorkergoth <kergoth>2002-01-25 22:14:26 (UTC)
committer kergoth <kergoth>2002-01-25 22:14:26 (UTC)
commit15318cad33835e4e2dc620d033e43cd930676cdd (patch) (side-by-side diff)
treec2fa0399a2c47fda8e2cd0092c73a809d17f68eb /noncore/apps/tableviewer/db
downloadopie-15318cad33835e4e2dc620d033e43cd930676cdd.zip
opie-15318cad33835e4e2dc620d033e43cd930676cdd.tar.gz
opie-15318cad33835e4e2dc620d033e43cd930676cdd.tar.bz2
Initial revision
Diffstat (limited to 'noncore/apps/tableviewer/db') (more/less context) (ignore whitespace changes)
-rw-r--r--noncore/apps/tableviewer/db/.cvsignore3
-rw-r--r--noncore/apps/tableviewer/db/common.cpp1470
-rw-r--r--noncore/apps/tableviewer/db/common.h285
-rw-r--r--noncore/apps/tableviewer/db/csvsource.cpp207
-rw-r--r--noncore/apps/tableviewer/db/csvsource.h53
-rw-r--r--noncore/apps/tableviewer/db/datacache.cpp293
-rw-r--r--noncore/apps/tableviewer/db/datacache.h130
-rw-r--r--noncore/apps/tableviewer/db/xmlsource.cpp295
-rw-r--r--noncore/apps/tableviewer/db/xmlsource.h119
9 files changed, 2855 insertions, 0 deletions
diff --git a/noncore/apps/tableviewer/db/.cvsignore b/noncore/apps/tableviewer/db/.cvsignore
new file mode 100644
index 0000000..a433295
--- a/dev/null
+++ b/noncore/apps/tableviewer/db/.cvsignore
@@ -0,0 +1,3 @@
+moc_*
+Makefile
+
diff --git a/noncore/apps/tableviewer/db/common.cpp b/noncore/apps/tableviewer/db/common.cpp
new file mode 100644
index 0000000..4c70e54
--- a/dev/null
+++ b/noncore/apps/tableviewer/db/common.cpp
@@ -0,0 +1,1470 @@
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** This file is part of Qtopia Environment.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+#include <stdlib.h>
+#include <qstring.h>
+#include <qheader.h>
+#include <qvector.h>
+#include <qdatetime.h>
+#include <timestring.h>
+#include "common.h"
+#include "datacache.h"
+#include <assert.h>
+
+static const int del_flag = 0x1;
+static const int new_flag = 0x2;
+
+/* Helper function */
+
+int parseNextNumber(QString *q) {
+ QChar c;
+ uint i;
+ int result = 0;
+
+ bool found_digits = FALSE;
+ for(i = 0; i < q->length(); i++) {
+ c = q->at(i);
+ if (c.isDigit()) {
+ if (found_digits)
+ result *= 10;
+ found_digits = TRUE;
+ result += c.digitValue();
+ } else {
+ if (found_digits)
+ break;
+ /* just skip this char */
+ }
+ }
+ /* now truncate q */
+ if (found_digits)
+ q->remove(0, i);
+ return result;
+}
+
+/*!
+ \class QStringVector
+ \brief A Vector of QStrings that can be sorted and searched
+
+ Implmented in order to allow reverse lookup on the string name
+
+*/
+
+/*!
+ This function implements the compare function in order to allow the
+ searching and sorting of the QStringVector to occur
+
+ \returns an int which is either
+ <UL>
+ <LI> < 0 if the first string is smaller than the second,</LI>
+ <LI> > 0 if the first string is bigger then the second,</LI>
+ <LI> == 0 if the first string is equal to the second.</LI>
+ </UL>
+*/
+int QStringVector::compareItems(Item a, Item b)
+{
+ QString *qa = (QString *)a;
+ QString *qb = (QString *)b;
+
+ return QString::compare(*qa, *qb);
+}
+
+/*!
+ \class TVVariant
+ A way of abstracting void * and keeping information on
+ the keytypes and behaviours in one place
+*/
+
+TVVariantPrivate::TVVariantPrivate()
+{
+ typ = TVVariant::Invalid;
+}
+
+TVVariantPrivate::TVVariantPrivate( TVVariantPrivate *d)
+{
+ switch(d->typ)
+ {
+ case TVVariant::Invalid:
+ break;
+ case TVVariant::String:
+ value.ptr = new QString(*((QString *)d->value.ptr));
+ break;
+ case TVVariant::Date:
+ value.ptr = new QDate(*((QDate *)d->value.ptr));
+ break;
+ case TVVariant::Time:
+ value.ptr = new QTime(*((QTime *)d->value.ptr));
+ break;
+ case TVVariant::Int:
+ value.i = d->value.i;
+ break;
+ default:
+ ASSERT( 0 );
+ }
+
+ typ = d->typ;
+}
+
+TVVariantPrivate::~TVVariantPrivate()
+{
+ clear();
+}
+
+void TVVariantPrivate::clear()
+{
+ switch( typ )
+ {
+ case TVVariant::String:
+ delete (QString *)value.ptr;
+ break;
+ case TVVariant::Date:
+ delete (QDate *)value.ptr;
+ break;
+ case TVVariant::Time:
+ delete (QTime *)value.ptr;
+ break;
+ case TVVariant::Invalid:
+ case TVVariant::Int:
+ break;
+ }
+
+ typ = TVVariant::Invalid;
+}
+
+/*!
+ \class TVVariant
+ blah
+*/
+
+TVVariant::TVVariant()
+{
+ d = new TVVariantPrivate;
+}
+
+TVVariant::~TVVariant()
+{
+ if (d->deref())
+ delete d;
+}
+
+TVVariant::TVVariant(const TVVariant& p)
+{
+ d = new TVVariantPrivate;
+ *this = p;
+}
+
+TVVariant::TVVariant(QDataStream& s)
+{
+ d = new TVVariantPrivate;
+ s >> *this;
+}
+
+TVVariant::TVVariant(const QString &val)
+{
+ d = new TVVariantPrivate;
+ d->typ = String;
+ d->value.ptr = new QString(val);
+}
+
+TVVariant::TVVariant(const QDate &val)
+{
+ d = new TVVariantPrivate;
+ d->typ = Date;
+ d->value.ptr = new QDate(val);
+}
+
+TVVariant::TVVariant(const QTime &val)
+{
+ d = new TVVariantPrivate;
+ d->typ = Time;
+ d->value.ptr = new QTime(val);
+}
+
+TVVariant::TVVariant( int val )
+{
+ d = new TVVariantPrivate;
+ d->typ = Int;
+ d->value.i = val;
+}
+
+TVVariant& TVVariant::operator=(const TVVariant& variant )
+{
+ TVVariant& other = (TVVariant&) variant;
+
+ other.d->ref();
+ if ( d->deref() )
+ delete d;
+
+ d = other.d;
+
+ return *this;
+}
+
+void TVVariant::detach()
+{
+ if (d->count == 1)
+ return;
+
+ d->deref();
+ d = new TVVariantPrivate(d);
+}
+
+const QString TVVariant::typeName() const
+{
+ return typeToName(d->typ);
+}
+
+void TVVariant::clear()
+{
+ if (d->count > 1)
+ {
+ d->deref();
+ d = new TVVariantPrivate;
+ return;
+ }
+
+ d->clear();
+}
+
+const QString TVVariant::typeToName(KeyType typ)
+{
+ switch(typ) {
+ case String:
+ return QString("String");
+ case Date:
+ return QString("Date");
+ case Time:
+ return QString("Time");
+ case Int:
+ return QString("Int");
+ case Invalid:
+ default:
+ return QString("Invalid");
+ }
+ return QString("Invalid");
+}
+
+TVVariant::KeyType TVVariant::nameToType(const QString &name)
+{
+ if(!qstrcmp("String", name))
+ return String;
+ if(!qstrcmp("Date", name))
+ return Date;
+ if(!qstrcmp("Time", name))
+ return Time;
+ if(!qstrcmp("Int", name))
+ return Int;
+
+ return Invalid;
+}
+
+void TVVariant::load(QDataStream &s )
+{
+ KeyType t;
+ s >> t;
+
+ d->typ = t;
+ switch(t) {
+ case Invalid:
+ d->typ = t;
+ break;
+ case String:
+ {
+ QString *x = new QString;
+ s >> *x;
+ d->value.ptr = x;
+ }
+ break;
+ case Time:
+ {
+ QTime *x = new QTime;
+ s >> *x;
+ d->value.ptr = x;
+ }
+ break;
+ case Date:
+ {
+ QDate *x = new QDate;
+ s >> *x;
+ d->value.ptr = x;
+ }
+ break;
+ case Int:
+ {
+ int x;
+ s >> x;
+ d->value.i = x;
+ }
+ break;
+ default:
+ qFatal("Unrecognized data type");
+ }
+}
+
+void TVVariant::save( QDataStream &s ) const
+{
+ s << type();
+
+ switch( d->typ ) {
+ case String:
+ s << *((QString *)d->value.ptr);
+ break;
+ case Date:
+ s << *((QDate *)d->value.ptr);
+ break;
+ case Time:
+ s << *((QTime *)d->value.ptr);
+ break;
+ case Int:
+ s << d->value.i;
+ break;
+ case Invalid:
+ break;
+ }
+}
+
+QDataStream& operator>>(QDataStream& s, TVVariant& p)
+{
+ p.load( s );
+ return s;
+}
+
+QDataStream& operator<<(QDataStream &s, const TVVariant& p)
+{
+ p.save( s );
+ return s;
+}
+
+QDataStream& operator>> (QDataStream &s, TVVariant::KeyType& p)
+{
+ Q_UINT8 u = 0;
+ s >> u;
+ p = (TVVariant::KeyType) u;
+
+ return s;
+}
+
+QDataStream& operator<< (QDataStream& s, const TVVariant::KeyType& p)
+{
+ s << (Q_UINT8)p;
+ return s;
+}
+
+const QString TVVariant::toString() const
+{
+ switch(d->typ) {
+ case String:
+ return *((QString*)d->value.ptr);
+ case Date:
+ return ((QDate*)d->value.ptr)->toString();
+ case Time:
+ return ((QTime*)d->value.ptr)->toString();
+ case Int:
+ return QString::number(d->value.i);
+ case Invalid:
+ default:
+ return QString::null;
+ }
+ return QString::null;
+}
+
+// TODO DO, this properly, */
+int TVVariant::toInt() const
+{
+ if(d->typ == Int)
+ return d->value.i;
+
+ if(d->typ == String) {
+ QString tmpq(*(QString *)d->value.ptr);
+ return parseNextNumber(&tmpq);
+ }
+
+ return 0;
+}
+
+const QDate TVVariant::toDate() const
+{
+ if(d->typ == Date)
+ return *((QDate *)d->value.ptr);
+
+ if(d->typ == String) {
+ QString q = toString();
+
+ /* date format is day mon d yyyy */
+ /* ignore the first three letters, read the next
+ three for month.. etc */
+
+ int day = parseNextNumber(&q);
+ int month = parseNextNumber(&q);
+ int year = parseNextNumber(&q);
+ if (!QDate::isValid(year, month, day))
+ return QDate();
+ return QDate(year, month, day);
+ }
+
+
+ return QDate();
+}
+
+const QTime TVVariant::toTime() const
+{
+ if(d->typ == Time)
+ return *((QTime *)d->value.ptr);
+
+ if(d->typ == String) {
+ QString q = toString();
+ int hour = parseNextNumber(&q);
+ int minute = parseNextNumber(&q);
+ int second = parseNextNumber(&q);
+ int msecond = parseNextNumber(&q);
+ if (!QTime::isValid(hour, minute, second, msecond))
+ return QTime();
+ return QTime(hour, minute, second, msecond);
+ }
+
+ return QTime();
+}
+
+#define TV_VARIANT_AS( f ) Q##f& TVVariant::as##f() { \
+ if ( d->typ != f ) \
+ *this = TVVariant( to##f() ); \
+ else \
+ detach(); \
+ return *((Q##f*)d->value.ptr); }
+
+TV_VARIANT_AS(String)
+TV_VARIANT_AS(Date)
+TV_VARIANT_AS(Time)
+
+#undef TV_VARIANT_AS
+
+int& TVVariant::asInt()
+{
+ detach();
+ if (d->typ != Int) {
+ d->value.i = toInt();
+ d->typ = Int;
+ }
+ return d->value.i;
+}
+
+/*!
+ valid cast is
+ anything to String
+ same to same
+*/
+bool TVVariant::canCast(KeyType t) const
+{
+ if(d->typ == t)
+ return TRUE;
+
+ if(t == String)
+ return TRUE;
+
+ if(t == Int) {
+ if (d->typ == Date)
+ return TRUE;
+ if (d->typ == Time)
+ return TRUE;
+ if (d->typ == String)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+bool TVVariant::operator==( const TVVariant &v ) const
+{
+ switch(d->typ) {
+ case String:
+ return v.toString() == toString();
+ case Date:
+ return v.toDate() == toDate();
+ case Time:
+ return v.toTime() == toTime();
+ case Int:
+ return v.toInt() == toInt();
+ case Invalid:
+ break;
+ }
+
+ return FALSE;
+}
+
+bool TVVariant::operator!=( const TVVariant &v ) const
+{
+ return !( v == *this);
+}
+
+bool TVVariant::operator<( const TVVariant &v ) const
+{
+ switch(d->typ) {
+ case String:
+ return toString().lower() < v.toString().lower();
+ case Date:
+ return toDate() < v.toDate();
+ case Time:
+ return toTime() < v.toTime();
+ case Int:
+ return toInt() < v.toInt();
+ case Invalid:
+ default:
+ break;
+ }
+ return FALSE;
+}
+
+bool TVVariant::operator>( const TVVariant &v ) const
+{
+ switch(d->typ) {
+ case String:
+ return toString().lower() > v.toString().lower();
+ case Date:
+ return toDate() > v.toDate();
+ case Time:
+ return toTime() > v.toTime();
+ case Int:
+ return toInt() > v.toInt();
+ case Invalid:
+ default:
+ break;
+ }
+ return FALSE;
+}
+
+/*! True if n is closer to this than o */
+bool TVVariant::closer(TVVariant n, TVVariant o)
+{
+ /* Nothing is close to an invalid, so nothing can be closer */
+ if(d->typ == Invalid)
+ return FALSE;
+
+ /* can't be closer if of different type */
+ if(n.type() != type())
+ return FALSE;
+
+ /* if new shares type, and old doesn't, then new is closer */
+ if(o.type() != type())
+ return TRUE;
+
+ switch(type()){
+ case String: {
+ /* case for strings is close is a substring.. closer is
+ * earlier alphabetically */
+ QString qs1 = n.toString().lower();
+ QString qs2 = o.toString().lower();
+ QString qsv = toString().lower();
+
+ if (!qs1.startsWith(qsv))
+ return FALSE;
+
+ /* contains sub-str, if later than is not closer */
+ if(QString::compare(qs1, qs2) > 0)
+ return FALSE;
+ return TRUE;
+ }
+ case Int: {
+ /* case for int is smallest absolute difference */
+ int i1 = n.toInt();
+ int i2 = o.toInt();
+ int iv = toInt();
+
+ int diff1 = (i1 - iv);
+ if (diff1 < 0)
+ diff1 = -diff1;
+ int diff2 = (i2 - iv);
+ if (diff2 < 0)
+ diff2 = -diff2;
+
+ if (diff1 < diff2)
+ return TRUE;
+ return FALSE;
+ }
+ case Date: {
+ QDate i1 = n.toDate();
+ QDate i2 = o.toDate();
+ QDate iv = toDate();
+
+ /* definition of closer is the least difference in days */
+ int diff1 = i1.daysTo(iv);
+ if (diff1 < 0)
+ diff1 = -diff1;
+ int diff2 = i2.daysTo(iv);
+ if (diff2 < 0)
+ diff2 = -diff2;
+
+ if (diff1 < diff2)
+ return TRUE;
+ return FALSE;
+ }
+ case Time: {
+ QTime i1 = n.toTime();
+ QTime i2 = o.toTime();
+ QTime iv = toTime();
+
+ /* definition of closer is the least difference in days */
+ int diff1 = i1.msecsTo(iv);
+ if (diff1 < 0)
+ diff1 = -diff1;
+ int diff2 = i2.msecsTo(iv);
+ if (diff2 < 0)
+ diff2 = -diff2;
+ if (diff1 < diff2)
+ return TRUE;
+ return FALSE;
+ }
+ default:
+ /* don't know how to do 'closer' on this type, hence never closer
+ * or even close */
+ break;
+ }
+ return FALSE;
+}
+
+/*! True if n is close to this */
+bool TVVariant::close(TVVariant n)
+{
+ /* Nothing is close to an invalid, so nothing can be closer */
+ if(type() == Invalid)
+ return FALSE;
+
+ /* can't be close if of different type */
+ if(n.type() != type())
+ return FALSE;
+
+ switch(type()){
+ case String: {
+ /* case for strings is close is a substring.. closer is
+ * earlier alphabetically */
+ QString qs1 = n.toString().lower();
+ QString qsv = toString().lower();
+
+ if (!qs1.startsWith(qsv))
+ return FALSE;
+ return TRUE;
+ }
+ case Int:
+ case Date:
+ case Time:
+ return TRUE;
+ default:
+ /* don't know how to do 'closer' on this type, hence never closer
+ * or even close */
+ break;
+ }
+ return FALSE;
+}
+
+/*!
+ \class Key
+ \brief document me!
+
+ document me!
+*/
+
+Key::Key() : kname(), kexample(), kflags(0) { }
+
+Key::Key(QString name, TVVariant example, int flags = 0) :
+ kname(name), kexample(example), kflags(flags) { }
+
+Key::Key(const Key &other)
+{
+ kname = other.kname;
+ kexample = other.kexample;
+ kflags = other.kflags;
+}
+
+Key& Key::operator=(const Key& key)
+{
+ kname = key.kname;
+ kexample = key.kexample;
+ kflags = key.kflags;
+ return *this;
+}
+
+QString Key::name() const
+{
+ return QString(kname);
+}
+
+TVVariant Key::example() const
+{
+ return TVVariant(kexample);
+}
+
+TVVariant::KeyType Key::type() const
+{
+ return kexample.type();
+}
+
+void Key::setName(const QString &name)
+{
+ kname = QString(name);
+}
+
+void Key::setExample(const TVVariant &e)
+{
+ kexample = TVVariant(e);
+}
+
+int Key::flags() const
+{
+ return kflags;
+}
+
+void Key::setFlags(int fl)
+{
+ kflags = fl;
+}
+
+bool Key::delFlag() const
+{
+ if(kflags & del_flag)
+ return TRUE;
+ return FALSE;
+}
+
+bool Key::newFlag() const
+{
+ if(kflags & new_flag)
+ return TRUE;
+ return FALSE;
+}
+
+void Key::setDelFlag(bool v)
+{
+ if(delFlag() != v)
+ kflags = kflags ^ del_flag;
+}
+
+void Key::setNewFlag(bool v)
+{
+ if(newFlag() != v)
+ kflags = kflags ^ new_flag;
+}
+
+/*!
+ \class KeyList
+ \brief A represntation of keys used for a table.
+
+ The KeyList class is used to store the representation of keys used in table
+ headings by DBStore. It stores the names and types of the keys
+*/
+
+/*!
+ Constructs a KeyList
+*/
+KeyList::KeyList() : QIntDict<Key>(20)
+{
+ setAutoDelete(TRUE);
+}
+
+/* Should be deep copy, but isn't */
+KeyList::KeyList(const KeyList &k) : QIntDict<Key>(k)
+{
+ KeyListIterator it(k);
+ while(it.current()) {
+ replace(it.currentKey(), new Key(*it.current()));
+ ++it;
+ }
+
+ setAutoDelete(TRUE);
+}
+
+/*!
+ Destroys a KeyList
+*/
+KeyList::~KeyList() {
+}
+
+/* Do a comparision base on Keys */
+bool KeyList::operator!=(const KeyList &other)
+{
+ KeyListIterator it(*this);
+
+ if (other.getNumFields() != getNumFields())
+ return TRUE;
+
+ while(it.current()) {
+ //it.currentKey(), it.current();
+ if (other.getKeyName(it.currentKey()) != getKeyName(it.currentKey()))
+ return TRUE;
+ if (other.getKeyType(it.currentKey()) != getKeyType(it.currentKey()))
+ return TRUE;
+ ++it;
+ }
+ return FALSE;
+}
+
+/*!
+ Returns the number of keys stored in the KeyList
+*/
+int KeyList::getNumFields() const
+{
+ return count();
+}
+
+/*!
+ Adds a new key to the KeyList
+
+ \param name the name of the new key
+ \param type the type of the new key
+*/
+int KeyList::addKey(QString name, TVVariant example)
+{
+ int i = count();
+ while(find(i) && (i > -1))
+ i--;
+ replace(i, new Key(name, example, 0));
+ return i;
+}
+
+int KeyList::addKey(QString name, TVVariant::KeyType type)
+{
+ /* generate a valid type for the example? */
+ TVVariant e = TVVariant("0");
+ switch(type) {
+ case TVVariant::String:
+ return addKey(name, TVVariant("<undefined>").asString());
+ break;
+ case TVVariant::Date:
+ return addKey(name, TVVariant(QDate::currentDate()).asDate());
+ break;
+ case TVVariant::Time:
+ return addKey(name, TVVariant(QTime(0,0,0)).toTime());
+ break;
+ case TVVariant::Int:
+ return addKey(name, TVVariant(0).toInt());
+ break;
+ default:
+ qWarning(QObject::tr("KeyList::addKey() Cannot make default "
+ "value for type %1, Key not added.").arg(type));
+ break;
+ }
+ return -1;
+}
+
+void KeyList::setKeyFlags(int i, int flag)
+{
+ if(find(i))
+ find(i)->setFlags(flag);
+}
+
+int KeyList::getKeyFlags(int i) const
+{
+ if(find(i))
+ return find(i)->flags();
+ return 0;
+}
+
+bool KeyList::checkNewFlag(int i) const
+{
+ if (find(i))
+ return find(i)->newFlag();
+ return false;
+}
+
+void KeyList::setNewFlag(int i, bool f)
+{
+ if(!find(i))
+ return;
+ find(i)->setNewFlag(f);
+}
+
+bool KeyList::checkDeleteFlag(int i) const
+{
+ if (find(i))
+ return find(i)->delFlag();
+ return false;
+}
+
+void KeyList::setDeleteFlag(int i, bool f)
+{
+ if(!find(i))
+ return;
+ find(i)->setDelFlag(f);
+}
+
+/*!
+ Returns the name of the key at index i
+*/
+QString KeyList::getKeyName(int i) const
+{
+ if (find (i))
+ return find(i)->name();
+ return QString();
+}
+
+void KeyList::setKeyName(int i, const QString &n)
+{
+ if(find(i))
+ find(i)->setName(n);
+}
+
+/*!
+ Returns the type of the key at index i
+*/
+TVVariant::KeyType KeyList::getKeyType(int i) const
+{
+ if(find(i))
+ return find(i)->type();
+ return TVVariant::Invalid;
+}
+
+void KeyList::setKeyType(int i, TVVariant::KeyType t)
+{
+ if(!find(i))
+ return;
+ switch(t) {
+ case TVVariant::String:
+ find(i)->setExample(TVVariant(QString("default")));
+ return;
+ case TVVariant::Int:
+ find(i)->setExample(TVVariant(int(0)));
+ return;
+ case TVVariant::Date:
+ find(i)->setExample(TVVariant(QDate::currentDate()));
+ return;
+ case TVVariant::Time:
+ find(i)->setExample(TVVariant(QTime(0,0,0,0)));
+ return;
+ default:
+ break;
+ }
+ return;
+}
+
+TVVariant KeyList::getKeyExample(int i) const
+{
+ if(find(i))
+ return find(i)->example();
+ return TVVariant();
+}
+
+void KeyList::setKeyExample(int i, TVVariant example)
+{
+ if(find(i))
+ find(i)->setExample(example);
+}
+
+/*!
+ Returns the index of the key with name q
+*/
+int KeyList::getKeyIndex(QString q) const
+{
+ KeyListIterator it(*this);
+
+ while(it.current()) {
+ if(it.current()->name() == q)
+ return it.currentKey();
+ ++it;
+ }
+ return -1;
+}
+
+bool KeyList::validIndex(int i) const
+{
+ if(!find(i))
+ return FALSE;
+ if(find(i)->delFlag())
+ return FALSE;
+ return TRUE;
+}
+
+QDataStream &operator<<( QDataStream &s, const KeyList &k)
+{
+ s << k.getNumFields();
+
+ KeyListIterator it(k);
+
+ while(it.current()) {
+ s << (Q_UINT16)it.currentKey();
+ s << it.current()->name();
+ s << it.current()->example();
+ s << (Q_UINT16)it.current()->flags();
+ ++it;
+ }
+ return s;
+}
+
+QDataStream &operator>>( QDataStream &s, KeyList &k)
+{
+ int i;
+ int size;
+ int index = 0;
+ int flags = 0;
+ TVVariant type = TVVariant();
+ QString name;
+
+ s >> size;
+
+ for (i=0; i < size; i++) {
+ s >> (Q_UINT16 &)index;
+ s >> name;
+ s >> type;
+ s >> (Q_UINT16 &)flags;
+ k.replace(index, new Key(name, type, flags));
+ }
+ return s;
+}
+
+/*!
+ \class DataElem
+ \brief A class representing a single row or element of a table in a DBStore
+
+ This class holds the data of a row in a table.
+*/
+
+
+/*!
+ Constructs a DataElem. This function needs a container because the
+ size, types of keys and primary key are all defined by the containing
+ database
+*/
+DataElem::DataElem(DBStore *c) : values(20)
+{
+ int size;
+ contained = c;
+ size = c->getNumFields();
+ values.setAutoDelete(TRUE);
+}
+
+/*!
+ Destroys a DataElem and frees memory used by the DataElem
+*/
+DataElem::~DataElem() {
+}
+
+
+
+QDataStream &operator<<( QDataStream &s, const DataElem &d)
+{
+ int size = d.getNumFields();
+
+ s << size; /* redundent data but makes streaming easier */
+ KeyList k = d.getKeys();
+
+ KeyListIterator it(k);
+
+ while(it.current()) {
+ s << (Q_UINT16)it.currentKey();
+ s << d.getField(it.currentKey());
+ ++it;
+ }
+ return s;
+}
+
+QDataStream &operator>>( QDataStream &s, DataElem &d)
+{
+ int i;
+ int size;
+ TVVariant t;
+ int index = 0;
+
+ s >> size; /* redundent data but makes streaming easier */
+ if (size != d.getNumFields()) {
+ qWarning("DataSize mis-match");
+ return s; /* sanity check failed.. don't load */
+ }
+
+ for(i = 0; i < size; i++) {
+ s >> (Q_UINT16)index;
+ s >> t;
+ d.setField(index, t);
+ }
+ return s;
+}
+
+/*! Returns the number of possible (not valid) fields in the data element */
+int DataElem::getNumFields() const
+{
+ return contained->getNumFields();
+}
+
+KeyList DataElem::getKeys() const
+{
+ return *(contained->getKeys());
+}
+
+/*!
+ This function determines whether field index i of the element has been
+ set yet.
+
+ \return A boolean value that is TRUE if the specfied field of this
+ element has been set and FALSE if the field has not yet been set
+*/
+bool DataElem::hasValidValue(int i) const
+{
+ if(!values.find(i))
+ return FALSE;
+ if(!contained->getKeys()->validIndex(i))
+ return FALSE;
+ return values.find(i)->isValid();
+}
+
+/*!
+ This function determines whether field name qs of the element has been
+ set yet.
+
+ \return A boolean value that is TRUE if the specfied field of this
+ element has been set and FALSE if the field has not yet been set
+*/
+bool DataElem::hasValidValue(QString qs) const
+{
+ int i = contained->getKeyIndex(qs);
+ return hasValidValue(i);
+}
+
+/*! returns the type of the field specified by index i */
+TVVariant::KeyType DataElem::getFieldType(int i) const
+{
+ return contained->getKeyType(i);
+}
+
+/*! returns the type of the field specified by name qs */
+TVVariant::KeyType DataElem::getFieldType(QString qs) const
+{
+ int i = contained->getKeyIndex(qs);
+ return contained->getKeyType(i);
+}
+
+/*!
+ returns a pointer to the data stored in field index i for this
+ data element, (value may not be valid)
+*/
+TVVariant DataElem::getField(int i) const
+{
+ if(hasValidValue(i))
+ return TVVariant(*values.find(i));
+ return TVVariant();
+}
+
+/*!
+ returns a pointer to the data stored in field name qs for this
+ data element, (value may not be valid)
+*/
+TVVariant DataElem::getField(QString qs) const
+{
+ int i = contained->getKeyIndex(qs);
+ return getField(i);
+}
+
+/*!
+ Sets the value of the elements field index i to the value represented in
+ the QString q.
+
+ \param i index of the field to set
+ \param q a string that can be parsed to get the value to be set
+*/
+void DataElem::setField(int i, QString q)
+{
+ /* from the type of the field, parse q and store */
+ TVVariant::KeyType kt = contained->getKeyType(i);
+
+ TVVariant t = TVVariant(q);
+
+ switch(kt) {
+ case TVVariant::Int: {
+ t.asInt();
+ setField(i, t);
+ return;
+ }
+ case TVVariant::String: {
+ t.asString();
+ setField(i, t);
+ return;
+ }
+ case TVVariant::Date: {
+ t.asDate();
+ setField(i, t);
+ return;
+ }
+ case TVVariant::Time: {
+ t.asTime();
+ setField(i, t);
+ return;
+ }
+ default:
+ qWarning(
+ QObject::tr("DataElem::setField(%1, %2) No valid type found").arg(i).arg(q)
+ );
+ }
+}
+
+/*!
+ Sets the value of the elements field index i to the value at the pointer
+ value.
+
+ \param i index of the field to set
+ \param value a pointer to the (already allocated) value to set
+*/
+void DataElem::setField(int i, TVVariant value)
+{
+ if (value.isValid()) {
+ values.remove(i);
+ values.replace(i, new TVVariant(value));
+ }
+}
+
+/*!
+ Sets the value of the elements field name qs to the value represented in
+ the QString q.
+
+ \param qs name of the field to set
+ \param q a string that can be parsed to get the value to be set
+*/
+void DataElem::setField(QString qs, QString q)
+{
+ /* from the type of the field, parse q and store */
+ int i = contained->getKeyIndex(qs);
+ setField(i, qs);
+}
+
+/*!
+ Sets the value of the elements field name qs to the value at the pointer
+ value.
+
+ \param qs name of the field to set
+ \param value a pointer to the (already allocated) value to set
+*/
+void DataElem::setField(QString qs, TVVariant value)
+{
+ int i = contained->getKeyIndex(qs);
+ setField(i, value);
+}
+
+void DataElem::unsetField(int i) {
+ values.remove(i);
+}
+
+void DataElem::unsetField(QString qs)
+{
+ int i = contained->getKeyIndex(qs);
+ unsetField(i);
+}
+
+/*!
+ Converts the data element to a Rich Text QString
+*/
+QString DataElem::toQString() const
+{
+ /* lets make an attempt at this function */
+ int i;
+ QString scratch = "";
+
+ QIntDictIterator<TVVariant> it(values);
+
+ while (it.current()) {
+ i = it.currentKey();
+ if(hasValidValue(i)) {
+ scratch += "<B>" + contained->getKeyName(i) + ":</B> ";
+ scratch += getField(i).toString();
+ scratch += "<br>";
+ }
+ ++it;
+ }
+ return scratch;
+}
+
+/*! formats individual fields to strings so can be displayed */
+QString DataElem::toQString(int i) const
+{
+ if(hasValidValue(i)) {
+ return getField(i).toString();
+ }
+ return "";
+}
+/*! formats individual fields to strings so can be sorted by QListView */
+QString DataElem::toSortableQString(int i) const
+{
+ QString scratch = "";
+ if(hasValidValue(i)) {
+ switch (contained->getKeyType(i)) {
+ case TVVariant::String: {
+ scratch += getField(i).toString();
+ break;
+ }
+ case TVVariant::Int: {
+ scratch.sprintf("%08d", getField(i).toInt());
+ break;
+ }
+ case TVVariant::Date: {
+ static QDate epochD(1800, 1, 1);
+ scratch.sprintf("%08d",
+ epochD.daysTo(getField(i).toDate()));
+ break;
+ }
+ case TVVariant::Time: {
+ static QTime epochT(0, 0, 0);
+ scratch.sprintf("%08d",
+ epochT.msecsTo(getField(i).toTime()));
+ break;
+ }
+ default:
+ scratch += "Unknown type";
+ break;
+ }
+ }
+ return scratch;
+}
+
+/* compare functions */
+
+bool DataElem::lessThan(int i, TVVariant v) const
+{
+ if (!hasValidValue(i)) return FALSE;
+
+ if (getField(i).type() != v.type())
+ return FALSE;
+
+ return (getField(i) < v);
+}
+
+bool DataElem::moreThan(int i, TVVariant v) const
+{
+ if (!hasValidValue(i)) return FALSE;
+
+ if (getField(i).type() != v.type())
+ return FALSE;
+
+ return (getField(i) > v);
+}
+
+bool DataElem::equalTo(int i, TVVariant v) const
+{
+ if (!hasValidValue(i)) return FALSE;
+
+ if (getField(i).type() != v.type())
+ return FALSE;
+
+ return (getField(i) == v);
+}
+
+bool DataElem::contains(int i, TVVariant v) const
+{
+ if (!hasValidValue(i)) return FALSE;
+
+ if (getField(i).type() != v.type())
+ return FALSE;
+
+ switch(getField(i).type()) {
+ case TVVariant::String: {
+ QString qs1 = getField(i).toString().lower();
+ QString qs2 = v.toString().lower();
+ if (qs1.contains(qs2) > 0) return TRUE;
+ break;
+ }
+ /* meaningless for ints */
+ /* meaningless for time */
+ /* meaningless for dates */
+ case TVVariant::Int:
+ case TVVariant::Time:
+ case TVVariant::Date:
+ break;
+ default:
+ qWarning("Tried to compare unknown data type");
+ }
+ return FALSE;
+}
+
+bool DataElem::startsWith(int i, TVVariant v) const
+{
+ if (!hasValidValue(i)) return FALSE;
+
+ if (getField(i).type() != v.type())
+ return FALSE;
+
+ switch(getField(i).type()) {
+ case TVVariant::String: {
+ QString qs1 = getField(i).toString().lower();
+ QString qs2 = v.toString().lower();
+ return qs1.startsWith(qs2);
+ }
+ /* meaningless for ints */
+ /* meaningless for time */
+ /* meaningless for dates */
+ case TVVariant::Int:
+ case TVVariant::Time:
+ case TVVariant::Date:
+ return FALSE;
+ default:
+ qWarning("Tried to compare unknown data type");
+ }
+ return FALSE;
+}
+
+bool DataElem::endsWith(int i, TVVariant v) const
+{
+ if (!hasValidValue(i)) return FALSE;
+
+ if (getField(i).type() != v.type())
+ return FALSE;
+
+ switch(getField(i).type()) {
+ case TVVariant::String: {
+ QString qs1 = getField(i).toString().lower();
+ QString qs2 = v.toString().lower();
+ return qs1.startsWith(qs2);
+ }
+ /* meaningless for ints */
+ /* meaningless for time */
+ /* meaningless for dates */
+ case TVVariant::Int:
+ case TVVariant::Time:
+ case TVVariant::Date:
+ return FALSE;
+ default:
+ qWarning("Tried to compare unknown data type");
+ }
+ return FALSE;
+}
+
+/*!
+ Determins which of the first to parameters are closer to the third, target
+ parameter.
+
+ \return
+ <UL>
+ <LI>TRUE if the first element is a closer match to the target than the
+ second element</LI>
+ <LI>FALSE if the first element is not a closer match to the target than
+ the second element</LI>
+ </UL>
+*/
+bool DataElem::closer(DataElem*d1, DataElem *d2, TVVariant target, int column)
+{
+ int type;
+
+ if(!d1) return FALSE;
+
+ if (!d1->hasValidValue(column)) return FALSE;
+
+ if(!target.isValid()) return FALSE;
+
+ type = d1->getField(column).type();
+
+ if(d2) {
+ if (type != d2->getField(column).type()) {
+ /* can't do compare */
+ qWarning("Tried to compare two incompatable types");
+ return FALSE;
+ }
+ return target.closer(d1->getField(column), d2->getField(column));
+ }
+ return target.close(d1->getField(column));
+}
diff --git a/noncore/apps/tableviewer/db/common.h b/noncore/apps/tableviewer/db/common.h
new file mode 100644
index 0000000..bb0a953
--- a/dev/null
+++ b/noncore/apps/tableviewer/db/common.h
@@ -0,0 +1,285 @@
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** This file is part of Qtopia Environment.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+
+/* This file represents shared data structures that will be passed
+ * around often.
+ */
+#ifndef __SHAREDDATA_H__
+#define __SHAREDDATA_H__
+
+// TODO rename this to a sensable class name
+
+#include <qvector.h>
+#include <qstring.h>
+#include <qdatetime.h>
+#include <qcstring.h>
+#include <qdatastream.h>
+#include <qintdict.h>
+
+class DBStore;
+
+/* helper classes to common classes */
+class QStringVector : public QVector<QString>
+{
+public:
+ int compareItems(Item a, Item b);
+};
+
+/* in QT 2.3, dates and times not supported int QVariant. So.....
+ * for now use my special Variant type which is basically identical
+ * except that does it for my types. TODO replace with QVariant when
+ * qvariant supports all the types we require */
+
+class TVVariantPrivate;
+
+class TVVariant
+{
+public:
+ enum KeyType {
+ Invalid = 0,
+ Int,
+ String,
+ Date,
+ Time,
+ };
+
+ TVVariant();
+ ~TVVariant();
+
+ TVVariant(const TVVariant&);
+ TVVariant(QDataStream&);
+
+ TVVariant(const QString &);
+ TVVariant(const int);
+ TVVariant(const QDate &);
+ TVVariant(const QTime &);
+
+ TVVariant& operator=(const TVVariant& );
+ bool operator==(const TVVariant&) const;
+ bool operator!=(const TVVariant&) const;
+ bool operator<(const TVVariant&) const;
+ bool operator>(const TVVariant&) const;
+
+ bool closer(TVVariant, TVVariant);
+ bool close(TVVariant);
+
+ KeyType type() const;
+ const QString typeName() const;
+ int numTypes() const;
+
+ const QString typeName(KeyType) const;
+ bool canCast(KeyType) const;
+ bool isValid() const;
+ void clear();
+
+ const QString toString() const;
+ const QDate toDate() const;
+ const QTime toTime() const;
+ int toInt() const;
+
+ QString& asString();
+ QDate& asDate();
+ QTime& asTime();
+ int& asInt();
+
+ void load(QDataStream&);
+ void save(QDataStream&) const;
+
+ static const QString typeToName(KeyType typ);
+ static KeyType nameToType(const QString &);
+private:
+ void detach();
+
+ TVVariantPrivate *d;
+};
+
+class TVVariantPrivate : public QShared
+{
+ public:
+ TVVariantPrivate();
+ TVVariantPrivate(TVVariantPrivate *);
+
+ ~TVVariantPrivate();
+
+ void clear();
+
+ TVVariant::KeyType typ;
+
+ union {
+ int i;
+ void *ptr;
+ } value;
+};
+
+inline TVVariant::KeyType TVVariant::type() const
+{
+ return d->typ;
+}
+
+inline bool TVVariant::isValid() const
+{
+ return (d->typ != Invalid);
+}
+
+inline int TVVariant::numTypes() const
+{
+ return 4;
+}
+
+class Key {
+public:
+ Key();
+ Key(QString name, TVVariant example, int flags = 0);
+ Key(const Key &);
+ Key& operator=(const Key& );
+
+ QString name() const;
+ TVVariant example() const;
+ TVVariant::KeyType type() const;
+ int flags() const;
+
+ void setName(const QString &);
+ void setExample(const TVVariant &);
+ void setFlags(int);
+
+ bool delFlag() const;
+ bool newFlag() const;
+
+ void setDelFlag(bool);
+ void setNewFlag(bool);
+
+private:
+ QString kname;
+ TVVariant kexample;
+ int kflags;
+};
+
+class KeyList : public QIntDict<Key> {
+public:
+ KeyList();
+ KeyList(const KeyList&);
+
+ ~KeyList();
+
+ bool operator!=(const KeyList &);
+
+ int getNumFields() const;
+
+ int addKey(QString KeyName, TVVariant example);
+ int addKey(QString KeyName, TVVariant::KeyType type);
+
+ TVVariant getKeyExample(int ) const;
+ void setKeyExample(int, TVVariant e);
+
+ QString getKeyName(int i) const;
+ void setKeyName(int i, const QString &n);
+
+ TVVariant::KeyType getKeyType(int i) const;
+ void setKeyType(int i, TVVariant::KeyType);
+
+ int getKeyIndex(QString q) const;
+
+ int getKeyFlags(int i) const;
+ void setKeyFlags(int i, int flag);
+
+ /* Below should be abstracted a bit more */
+ bool checkNewFlag(int i) const;
+ void setNewFlag(int i, bool f);
+ bool checkDeleteFlag(int i) const;
+ void setDeleteFlag(int i, bool f);
+
+ bool validIndex(int) const;
+};
+
+class KeyListIterator : public QIntDictIterator<Key>
+{
+public:
+ KeyListIterator(const KeyList &k) : QIntDictIterator<Key>(k) {};
+};
+
+/* TODO start using this */
+class DataElem {
+public:
+ DataElem(DBStore *container);
+ ~DataElem();
+
+ int getNumFields() const;
+ KeyList getKeys() const;
+
+ bool hasValidValue(int) const;
+ bool hasValidValue(QString) const;
+ TVVariant::KeyType getFieldType(int) const;
+ TVVariant::KeyType getFieldType(QString) const;
+ TVVariant getField(int) const;
+ TVVariant getField(QString) const;
+
+ void setField(int, QString);
+ void setField(int, TVVariant);
+ void setField(QString, QString);
+ void setField(QString, TVVariant);
+ void unsetField(int);
+ void unsetField(QString);
+
+ QString toQString() const;
+ QString toQString(int i) const;
+ QString toSortableQString(int i) const;
+
+ /* compare functions */
+ bool lessThan(int i, TVVariant) const;
+ bool moreThan(int i, TVVariant) const;
+ bool equalTo(int i, TVVariant) const;
+ bool contains(int i, TVVariant) const;
+ bool startsWith(int i, TVVariant) const;
+ bool endsWith(int i, TVVariant) const;
+
+ /* class functions... Compare is based of the primary key, which
+ is determined by the containing DBStores of each element. */
+ static int compare(const TVVariant, const TVVariant, int i);
+
+ /* False, second element's primary key is closer to target.
+ * True, first element's primary key is a closer match to target */
+ static bool closer(DataElem*, DataElem *, TVVariant, int column);
+private:
+ QIntDict<TVVariant> values;
+ DBStore *contained;
+};
+
+typedef struct _TableState {
+ int current_column;
+ KeyList *kRep;
+ DataElem *current_elem;
+} TableState;
+
+/* Stream functions */
+#ifndef QT_NO_DATASTREAM
+Q_EXPORT QDataStream &operator<<( QDataStream &, const KeyList & );
+Q_EXPORT QDataStream &operator<<( QDataStream &, const DataElem & );
+Q_EXPORT QDataStream &operator>>( QDataStream &, KeyList & );
+Q_EXPORT QDataStream &operator>>( QDataStream &, DataElem & );
+
+
+Q_EXPORT QDataStream &operator>>( QDataStream &, TVVariant & );
+Q_EXPORT QDataStream &operator<<( QDataStream &, const TVVariant & );
+Q_EXPORT QDataStream &operator>>( QDataStream &, TVVariant::KeyType& );
+Q_EXPORT QDataStream &operator<<( QDataStream &, const TVVariant::KeyType& );
+#endif
+
+#endif
diff --git a/noncore/apps/tableviewer/db/csvsource.cpp b/noncore/apps/tableviewer/db/csvsource.cpp
new file mode 100644
index 0000000..2561b4b
--- a/dev/null
+++ b/noncore/apps/tableviewer/db/csvsource.cpp
@@ -0,0 +1,207 @@
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** This file is part of Qtopia Environment.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+#include "csvsource.h"
+#include "common.h"
+#include "datacache.h"
+#include <qtextstream.h>
+#include <qstringlist.h>
+#include <qmap.h>
+#include <qregexp.h>
+
+DBCsv::DBCsv(DBStore *d)
+{
+ dstore = d;
+}
+
+DBCsv::~DBCsv()
+{
+}
+
+QString DBCsv::type()
+{
+ return "csv";
+}
+
+QStringList readElem(QString in)
+{
+ QStringList out;
+
+ if (in.isEmpty())
+ return out;
+
+ bool firstChar = TRUE;
+ bool quotedElem = FALSE;
+ uint index = 0;
+ while(index < in.length()) {
+ if(firstChar) {
+ /* skip whitespace */
+ while(index < in.length() && in[index] == ' ')
+ index++;
+ if(in[index] == '"') {
+ quotedElem = TRUE;
+ index++;
+ }
+ }
+ /* real first char */
+ QString elem;
+ if(quotedElem) {
+ while(index < in.length() && in[index] != '"') {
+ /* check for escape character */
+ if (in[index] == '\\') {
+ if (index++ < in.length()) {
+ elem.append(in[index]);
+ index++;
+ }
+ } else {
+ elem.append(in[index]);
+ index++;
+ }
+ }
+ } else {
+ while(index < in.length() && in[index] != ',') {
+ if (in[index] == '\\') {
+ if (index++ < in.length()) {
+ elem.append(in[index]);
+ index++;
+ }
+ } else {
+ elem.append(in[index]);
+ index++;
+ }
+ }
+ }
+ /* we have our current elem */
+ out << elem.stripWhiteSpace();
+ firstChar = TRUE;
+ quotedElem = FALSE;
+ /* skip till a , or end of line */
+ while (index < in.length() && in[index] != ',') index++;
+ if(index == in.length())
+ return out;
+ else
+ index++;
+ }
+}
+
+bool DBCsv::openSource(QIODevice *inDev)
+{
+ QTextStream tsIn(inDev);
+ QString in = tsIn.readLine().stripWhiteSpace();
+ QStringList keys;
+
+ keys = readElem(in);
+
+ QMap<int,int> keyIndexes;
+
+ KeyList *keyR = new KeyList();
+ QStringList::Iterator i = keys.begin();
+
+ uint fileIndex = 0;
+ while(i != keys.end()) {
+ if ((*i).isEmpty())
+ keyIndexes.insert(fileIndex, keyR->addKey("Unamed", TVVariant::String));
+ else
+ keyIndexes.insert(fileIndex, keyR->addKey(*i, TVVariant::String));
+ i++;
+ fileIndex++;
+ }
+ dstore->setKeys(keyR);
+
+ in = tsIn.readLine().stripWhiteSpace();
+ while(!in.isNull()) {
+ QStringList elems = readElem(in);
+
+ i = elems.begin();
+ fileIndex = 0;
+ DataElem *current_data = new DataElem(dstore);
+ while(i != elems.end()) {
+ if(!(*i).isEmpty()) {
+ current_data->setField(keyIndexes[fileIndex], *i);
+ }
+ fileIndex++;
+ i++;
+ }
+ dstore->addItem(current_data);
+ in = tsIn.readLine().stripWhiteSpace();
+ }
+
+ return TRUE;
+}
+
+bool DBCsv::saveSource(QIODevice *outDev)
+{
+ /* try not to use the escape character when possible. */
+ int i;
+ DataElem *elem;
+ KeyList *k;
+ QTextStream outstream(outDev);
+
+ k = dstore->getKeys();
+ KeyListIterator it(*k);
+ while(it.current()) {
+ if(!it.current()->delFlag()) {
+ QString name = it.current()->name();
+
+ name.replace(QRegExp("\\"), "\\\\");
+ name.replace(QRegExp("\""), "\\\"");
+ if(name.find(',') != -1) {
+ name.prepend('\"');
+ name.append('\"');
+ }
+
+ outstream << name;
+ }
+ ++it;
+ if(it.current())
+ outstream << ", ";
+ }
+ outstream << "\n";
+
+ dstore->first();
+
+ do {
+ elem = dstore->getCurrentData();
+ if(!elem)
+ break;
+ it.toFirst();
+ while(it.current()) {
+ i = it.currentKey();
+ if (elem->hasValidValue(i)) {
+ QString name = elem->toQString(i);
+
+ name.replace(QRegExp("\\"), "\\\\");
+ name.replace(QRegExp("\""), "\\\"");
+ if(name.find(',') != -1) {
+ name.prepend('\"');
+ name.append('\"');
+ }
+
+ outstream << name;
+ }
+ ++it;
+ if(it.current())
+ outstream << ", ";
+ }
+ outstream << "\n";
+ } while (dstore->next());
+
+ return TRUE;
+}
+
diff --git a/noncore/apps/tableviewer/db/csvsource.h b/noncore/apps/tableviewer/db/csvsource.h
new file mode 100644
index 0000000..691234c
--- a/dev/null
+++ b/noncore/apps/tableviewer/db/csvsource.h
@@ -0,0 +1,53 @@
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** This file is part of Qtopia Environment.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+/* A Class to parse an comma seperated values docment of the form
+
+"Displayed Name", key2name, key 3 name, key name 4
+value1, , value 3, "value 4"
+value1, , value 3, "value 4"
+
+ * '\' is the escape character. Basically \", \' and \\ escape the ", ' or \
+ * into the actual value of the field.
+ * Before any other character will have the effect of dropping the '\'.
+ *
+ * Currently there is no support for comments. */
+
+#ifndef __CSVSOURCE_H__
+#define __CSVSOURCE_H__
+
+#include <qstring.h>
+#include "datacache.h"
+#include "common.h"
+
+
+class DBCsv : public DBAccess
+{
+public:
+ /* create connection and either open or initialize */
+ DBCsv(DBStore *d);
+ QString type();
+ bool openSource(QIODevice *);
+ bool saveSource(QIODevice *);
+ /* does a db write */
+ ~DBCsv();
+};
+
+#endif
diff --git a/noncore/apps/tableviewer/db/datacache.cpp b/noncore/apps/tableviewer/db/datacache.cpp
new file mode 100644
index 0000000..7c14eef
--- a/dev/null
+++ b/noncore/apps/tableviewer/db/datacache.cpp
@@ -0,0 +1,293 @@
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** This file is part of Qtopia Environment.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+/*
+ * This file is used to load the xml files that represent the database.
+ * The main requirment for said file is each data entry must contain a key,
+ * otherwise any other data headings are allowed.
+ */
+
+#include "datacache.h"
+#include "xmlsource.h"
+#include "csvsource.h"
+#include <stdlib.h>
+#include <qheader.h>
+
+#define INIT_TABLE_SIZE 128
+
+/*!
+ \class DBStore datastore.h
+
+ \brief The DBStore class is the class responsible for storing, sorting and
+ searching the data loaded by the application
+
+*/
+
+/*!
+ Constructs a DBStore item
+*/
+DBStore::DBStore()
+{
+ name = "";
+ number_elems = 0;
+ full = false;
+ kRep = new KeyList();
+ master_table.resize(INIT_TABLE_SIZE);
+ table_size = INIT_TABLE_SIZE;
+
+ current_elem = 0;
+ archive = 0;
+}
+
+//TODO
+/*!
+ Reinitializes the table to empty (include a resize of the master table,
+ which should free some memory)
+*/
+void DBStore::freeTable()
+{
+ name = "";
+ if(archive) {
+ delete archive;
+ archive = 0;
+ }
+ kRep->clear(); /* clear the current key list */
+
+ number_elems = 0;
+ table_size = INIT_TABLE_SIZE;
+ master_table.resize(table_size);
+ full = false;
+ current_elem = 0;
+}
+
+/*!
+ Removes all items from the DBStore and destroys the DBStore
+*/
+DBStore::~DBStore()
+{
+ freeTable();
+}
+
+/*!
+ This function opens the given xml file, loads it and sets up the
+ appropriate data structures.
+
+ \param file_name A string representing the name of the file to be opened
+ \return true if successful, false otherwise.
+*/
+bool DBStore::openSource(QIODevice *inDev, const QString &source) {
+
+ /* first check if db is already open, if contains data.. then clear */
+ if(number_elems > 0) {
+ freeTable();
+ }
+
+ if (source == "text/x-xml-tableviewer") {
+ archive = new DBXml(this);
+ } else if (source == "text/csv") {
+ archive = new DBCsv(this);
+ } else
+ return false;
+
+ return (archive->openSource(inDev));
+}
+
+bool DBStore::saveSource(QIODevice *outDev, const QString &source)
+{
+ /* saving a new file */
+ if(!archive) {
+ if (source == "text/x-xml-tableviewer") {
+ archive = new DBXml(this);
+ } else if (source == "text/x-xml-tableviewer") {
+ archive = new DBCsv(this);
+ } else
+ return false;
+ }
+
+ /* changing file type */
+ if(archive->type() != source) {
+ delete archive;
+ if (source == "text/x-xml-tableviewer") {
+ archive = new DBXml(this);
+ } else if (source == "text/x-xml-tableviewer") {
+ archive = new DBCsv(this);
+ } else
+ return false;
+ }
+
+ return (archive->saveSource(outDev));
+}
+
+/*!
+ This function is used to add new elements to the database. If the database
+ has already reached the maximum allowable size this function does not alter
+ the database.
+
+ \param delm An already allocated and initialized data element to be added
+*/
+void DBStore::addItem(DataElem *delem)
+{
+ addItemInternal(delem);
+}
+
+void DBStore::addItemInternal(DataElem *delem)
+{
+ /* if already full, don't over fill, do a qWarning though */
+ if (full) {
+ qWarning("Attempted to add items to already full table");
+ return;
+ }
+
+ master_table.insert(number_elems, delem);
+
+ current_elem = number_elems;
+ number_elems++;
+
+ if(number_elems >= table_size) {
+ /* filled current table, double if we can */
+ table_size = table_size << 1;
+
+ /* check that the new table size is still valid, i.e. that we didn't
+ just shift the 1 bit of the end of the int. */
+ if (!table_size) {
+ full = true;
+ /* no point in doing antying else. */
+ return;
+ }
+ master_table.resize(table_size);
+ }
+}
+
+void DBStore::removeItem(DataElem *r)
+{
+ int position = master_table.findRef(r);
+ if(position != -1) {
+ /* there is at least one item, this is it */
+ /* replace this with the last element, decrease the element count */
+ master_table.insert(position, master_table.at(--number_elems));
+ master_table.remove(number_elems);
+ delete r;
+ }
+}
+
+/*!
+ Sets the name of the database
+
+ \param n A string representing the new name of the database.
+*/
+void DBStore::setName(const QString &n)
+{
+ name = n;
+}
+
+/*!
+ Gets the name of the database
+
+ \return A string representing the name of the database.
+*/
+QString DBStore::getName()
+{
+ return name;
+}
+
+/*!
+ Retrieves a pointer to the key representation of the database for
+ other classes to use as reference.
+
+ \return a pointer to the databases key representaion
+*/
+KeyList *DBStore::getKeys()
+{
+ return kRep;
+}
+
+/*!
+ sets the database's key representation the passed pointer
+ \param a pointer to a key representaton
+*/
+void DBStore::setKeys(KeyList *k)
+{
+ kRep = k;
+}
+
+/*!
+ Sets the current element to the first element of the database
+*/
+void DBStore::first()
+{
+ current_elem = 0;
+}
+
+/*!
+ Sets the current element to the last element of the database
+*/
+void DBStore::last()
+{
+ current_elem = number_elems - 1;
+}
+
+/*!
+ Sets the current element to the next element of the database if
+ there exists an element after the current one.
+*/
+bool DBStore::next()
+{
+ unsigned int new_current_elem = current_elem + 1;
+ if (current_elem < number_elems)
+ /* was valid before inc (it is possible but unlikely that inc current
+ elem will change it from invalid to valid) */
+ if (new_current_elem < number_elems) {
+ /* is valid after inc */
+ current_elem = new_current_elem;
+ return true;
+ }
+ return false;
+}
+
+/*!
+ Sets the current element to the previous element of the database if
+ there exists an element before the current one.
+*/
+bool DBStore::previous()
+{
+ unsigned int new_current_elem = current_elem -1;
+ if (current_elem < number_elems)
+ /* was valid */
+ if (new_current_elem < number_elems) {
+ /* still is (if was 0, then now -1, but as is unsigned will wrap
+ and hence be invalid */
+ current_elem = new_current_elem;
+ return true;
+ }
+ return false;
+}
+
+/*!
+ Returns the current data element in the database. Which element is current
+ is affected by newly added items, findItem, next, previous, first and
+ last functions
+
+ \return a pointer to the current data element
+*/
+DataElem *DBStore::getCurrentData()
+{
+ if (current_elem >= number_elems)
+ return NULL;
+ return master_table[current_elem];
+}
diff --git a/noncore/apps/tableviewer/db/datacache.h b/noncore/apps/tableviewer/db/datacache.h
new file mode 100644
index 0000000..c5dc637
--- a/dev/null
+++ b/noncore/apps/tableviewer/db/datacache.h
@@ -0,0 +1,130 @@
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** This file is part of Qtopia Environment.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+/*
+ * This file is used to load the xml files that represent the database.
+ * The main requirment for said file is each data entry must contain a key,
+ * otherwise any other data headings are allowed.
+ */
+
+#ifndef __DATACACHE_H__
+#define __DATACACHE_H__
+
+#include <qstring.h>
+#include <qvector.h>
+#include "common.h"
+
+class DBStore;
+
+/*! Abstract class that defines how database stuff can be accessed */
+class DBAccess {
+public:
+
+// DBAccess(DBStore *d) { dstore = d; }
+ virtual ~DBAccess() {}
+
+ virtual QString type() {
+ return QString();
+ }
+
+ virtual bool openSource(QIODevice *) {
+ qWarning("DBAccess::openSource not yet implemented");
+ return false;
+ }
+
+ virtual bool saveSource(QIODevice *) {
+ qWarning("DBAccess::saveSource(QString) not yet implemented");
+ return false;
+ }
+
+protected:
+ DBStore *dstore;
+ QString source_name;
+};
+
+class DBStore {
+public:
+ DBStore();
+ ~DBStore();
+
+ bool openSource(QIODevice *, const QString &source);
+ bool saveSource(QIODevice *, const QString &source);
+
+ // Add an item
+ void addItem(DataElem *);
+ void addItemInternal(DataElem *);
+ void removeItem(DataElem *);
+
+ // Set the name of the database
+ void setName(const QString &name);
+
+ // Get the name of the database
+ QString getName();
+
+ KeyList *getKeys();
+ void setKeys(KeyList *);
+
+ /*! gets the number of fields defined in the database */
+ inline int getNumFields() {
+ return kRep->getNumFields();
+ }
+
+ /*! gets the index of a key given its name */
+ inline int getKeyIndex(QString qs) {
+ return kRep->getKeyIndex(qs);
+ }
+
+ /*! gets the type of a key given its index */
+ inline TVVariant::KeyType getKeyType(int i) {
+ return kRep->getKeyType(i);
+ }
+
+ /*! gets the name of a key given its index */
+ inline QString getKeyName(int i) {
+ return kRep->getKeyName(i);
+ }
+
+// Access functions.. iterator type stuff
+
+ void first();
+ void last();
+
+ bool next();
+ bool previous();
+
+ DataElem* getCurrentData();
+
+private:
+ /* does the work of freeing used memory */
+ void freeTable();
+ QString name;
+
+ QVector<DataElem> master_table;
+ DBAccess *archive;
+
+ KeyList *kRep;
+
+ unsigned int number_elems;
+ unsigned int table_size; /* should always be a power of 2 */
+ bool full; /* since because we are using an int for indexing there is
+ an upper limit on the number of items we can store. */
+ unsigned int current_elem;
+};
+#endif
diff --git a/noncore/apps/tableviewer/db/xmlsource.cpp b/noncore/apps/tableviewer/db/xmlsource.cpp
new file mode 100644
index 0000000..7418a85
--- a/dev/null
+++ b/noncore/apps/tableviewer/db/xmlsource.cpp
@@ -0,0 +1,295 @@
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** This file is part of Qtopia Environment.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+#include "xmlsource.h"
+#include <qdict.h>
+#include <stdlib.h>
+#include <qtextstream.h>
+
+
+DBXml::DBXml(DBStore *d)
+{
+ dstore = d;
+}
+
+QString DBXml::type()
+{
+ return "xml";
+}
+
+bool DBXml::openSource(QIODevice *inDev)
+{
+ bool ok;
+
+ DBXmlHandler h(dstore);
+
+ QTextStream tsIn(inDev);
+ QXmlInputSource source(tsIn);
+ QXmlSimpleReader reader;
+ reader.setContentHandler(&h);
+ reader.setErrorHandler(&h);
+ ok = reader.parse(source);
+
+ return ok;
+}
+
+bool DBXml::saveSource(QIODevice *outDev)
+{
+ int i;
+ DataElem *elem;
+ KeyList *k;
+
+ QTextStream outstream(outDev);
+
+ outstream << "<database name=\"" << dstore->getName() << "\">" << endl;
+ outstream << "<header>" << endl;
+
+ k = dstore->getKeys();
+ KeyListIterator it(*k);
+ while(it.current()) {
+ if (!it.current()->delFlag()) {
+ outstream << "<key name=\"KEYID" << it.currentKey() << "\" ";
+ outstream << "type=\""
+ << TVVariant::typeToName(it.current()->type())
+ << "\">";
+ outstream << it.current()->name() << "</key>" << endl;
+ }
+ ++it;
+ }
+
+ outstream << "</header>" << endl;
+
+ dstore->first();
+
+ do {
+ elem = dstore->getCurrentData();
+ if (!elem)
+ break;
+ outstream << "<record>" << endl;
+ it.toFirst();
+ while (it.current()) {
+ i = it.currentKey();
+ if (elem->hasValidValue(i)) {
+ outstream << "<KEYID" << i << ">";
+ if (dstore->getKeyType(i) == TVVariant::Date) {
+ // dates in files are different from displayed dates
+ QDate date = elem->getField(i).toDate();
+ outstream << date.day() << "/"
+ << date.month() << "/"
+ << date.year();
+ } else {
+ outstream << elem->toQString(i);
+ }
+ outstream << "</KEYID" << i << ">" << endl;
+ }
+ ++it;
+ }
+ outstream << "</record>" << endl;
+ } while(dstore->next());
+
+ outstream << "</database>" << endl;
+ return TRUE;
+}
+
+DBXml::~DBXml() {}
+
+/*!
+ \class DBXmlHandler
+ \brief An Xml parser for flat tables.
+
+ An xml parser for parsing the files used by the table viewer application.
+
+ The format of the xml files can be found at the front of the file
+ dataparser.h
+*/
+
+/*!
+ Constructs a new DBXmlHandler, and sets that the table should be
+ constructed in the DBStore pointed to by ds.
+*/
+DBXmlHandler::DBXmlHandler(DBStore *ds)
+{
+ data_store = ds;
+ current_keyrep = 0;
+}
+
+/*!
+ Destroys the DBXmlHandler
+*/
+DBXmlHandler::~DBXmlHandler()
+{
+}
+
+QString DBXmlHandler::errorProtocol()
+{
+ qWarning("Error reading file");
+ return errorProt;
+}
+
+bool DBXmlHandler::startDocument()
+{
+ errorProt = "";
+ state = StateInit;
+ return TRUE;
+}
+
+bool DBXmlHandler::startElement(const QString&, const QString&,
+ const QString& qName, const QXmlAttributes& atts)
+{
+ if (state == StateInit && qName == "database") {
+ // First thing it expects is a <document name="..."> tag
+ state = StateDocument;
+ data_store->setName(atts.value("name"));
+ return TRUE;
+ }
+ if (state == StateDocument && qName == "header") {
+ state = StateHeader;
+ if (current_keyrep) delete current_keyrep;
+ current_keyrep = new KeyList();
+ return TRUE;
+ }
+ if (state == StateHeader && qName == "key") {
+ /* Ok, adding a new key to our KeyList TODO */
+ state = StateKey;
+ last_key_type = TVVariant::String;
+ key = atts.value("name");
+ if (key.isEmpty()) {
+ qWarning("empty key name");
+ return FALSE;
+ }
+ if(!atts.value("type").isEmpty())
+ last_key_type = TVVariant::nameToType(atts.value("type"));
+ return TRUE;
+ }
+ if (state == StateDocument && qName == "record") {
+ state = StateRecord;
+ current_data = new DataElem(data_store);
+ // Now expecting a <record> tag
+ return TRUE;
+ }
+ if (state == StateRecord) {
+ state = StateField;
+ /* the qName is the name of a key */
+ if (!keyIndexList[qName]) {
+ /* invalid key, we failed */
+ qWarning("Invalid key in record");
+ return FALSE;
+ }
+ keyIndex = *keyIndexList[qName];
+ return TRUE;
+ }
+ qWarning("Unable to determine tag type");
+ return FALSE;
+}
+
+bool DBXmlHandler::endElement(const QString&, const QString&,
+ const QString& qName)
+{
+ switch(state) {
+ case StateField:
+ // TODO checks 'could' be done of the popped value
+ state = StateRecord;
+ break;
+ case StateKey:
+ // TODO checks 'could' be done of the popped value
+ state = StateHeader;
+ break;
+ case StateHeader:
+ data_store->setKeys(current_keyrep);
+ state = StateDocument;
+ break;
+ case StateRecord:
+ data_store->addItem(current_data);
+ state = StateDocument;
+ break;
+ case StateDocument:
+ // we are done...
+ break;
+ default:
+ // should only get a 'endElement' from one of the above states.
+ qWarning("Invalid end tag");
+ return FALSE;
+ break;
+ }
+ return TRUE;
+}
+
+bool DBXmlHandler::characters(const QString& ch)
+{
+ // this is where the 'between tag' stuff happens.
+ // e.g. the stuff between tags.
+ QString ch_simplified = ch.simplifyWhiteSpace();
+
+ if (ch_simplified.isEmpty())
+ return TRUE;
+
+ if (state == StateKey) {
+ int *tmp_val = new int;
+ /* We just grabbed the display name of a key */
+ *tmp_val = current_keyrep->addKey(ch_simplified, last_key_type);
+ keyIndexList.insert(key, tmp_val);
+ return TRUE;
+ }
+ if (state == StateField) {
+ /* Ok, need to add data here */
+ current_data->setField(keyIndex, ch_simplified);
+ return TRUE;
+ }
+
+ qWarning("Junk characters found... ignored");
+ return TRUE;
+}
+
+QString DBXmlHandler::errorString()
+{
+ return "the document is not in the expected file format";
+}
+
+bool DBXmlHandler::warning(const QXmlParseException& exception)
+{
+ errorProt += QString("warning parsing error: %1 in line %2, column %3\n" )
+ .arg(exception.message())
+ .arg(exception.lineNumber())
+ .arg(exception.columnNumber());
+
+ qWarning(errorProt);
+ return QXmlDefaultHandler::fatalError(exception);
+}
+
+bool DBXmlHandler::error(const QXmlParseException& exception)
+{
+ errorProt += QString("error parsing error: %1 in line %2, column %3\n" )
+ .arg(exception.message())
+ .arg(exception.lineNumber())
+ .arg(exception.columnNumber());
+
+ qWarning(errorProt);
+ return QXmlDefaultHandler::fatalError(exception);
+}
+
+bool DBXmlHandler::fatalError(const QXmlParseException& exception)
+{
+ errorProt += QString("fatal parsing error: %1 in line %2, column %3\n" )
+ .arg(exception.message())
+ .arg(exception.lineNumber())
+ .arg(exception.columnNumber());
+
+ qWarning(errorProt);
+ return QXmlDefaultHandler::fatalError(exception);
+}
diff --git a/noncore/apps/tableviewer/db/xmlsource.h b/noncore/apps/tableviewer/db/xmlsource.h
new file mode 100644
index 0000000..ec267a6
--- a/dev/null
+++ b/noncore/apps/tableviewer/db/xmlsource.h
@@ -0,0 +1,119 @@
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** This file is part of Qtopia Environment.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+/* A Class to parse an xml docment of the form
+ * <database name="...">
+ * <header>
+ * <key name=key_name type=String>Displayed Name</key>
+ * <key name=key2name>key2name</key>
+ * <key name=key3name type=Date>Key 3</key>
+ * <key name=key4name type=Int>key 4</key>
+ * </header>
+ * <record>
+ * <key_name>string</key_name>
+ * <key4name>int</key4name>
+ * <key2name>string</key2name>
+ * </record>
+ * <record>
+ * ....
+ * </record>
+ * ....
+ * </database>
+ *
+ * There is some room for improvment mostly around using better checking
+ * and the use of more advanced xml features.
+ */
+
+#ifndef __XMLSOURCE_H__
+#define __XMLSOURCE_H__
+
+#include <qxml.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qstack.h>
+#include <qdict.h>
+#include "datacache.h"
+#include "common.h"
+
+
+class DBXml : public DBAccess
+{
+public:
+ /* create connection and either open or initialize */
+ DBXml(DBStore *d);
+ QString type();
+ bool openSource(QIODevice *);
+ bool saveSource(QIODevice *);
+ /* does a db write */
+ ~DBXml();
+};
+
+
+class DBXmlHandler : public QXmlDefaultHandler
+{
+
+public:
+ DBXmlHandler(DBStore *ds);
+ virtual ~DBXmlHandler();
+
+ // return the error protocol if parsing failed
+ QString errorProtocol();
+
+ // overloaded handler functions
+ bool startDocument();
+ bool startElement(const QString& namespaceURI, const QString& localName,
+ const QString& qName, const QXmlAttributes& atts);
+ bool endElement(const QString& namespaceURI, const QString& localName,
+ const QString& qName);
+ bool characters(const QString& ch);
+
+ QString errorString();
+
+ bool warning(const QXmlParseException& exception);
+ bool error(const QXmlParseException& exception);
+ bool fatalError(const QXmlParseException& exception);
+
+private:
+
+ QStack<QString> stack;
+ KeyList *current_keyrep;
+ DataElem *current_data;
+ TVVariant::KeyType last_key_type;
+
+ QString errorProt;
+ DBStore *data_store;
+
+ enum State {
+ StateInit,
+ StateHeader,
+ StateKey,
+ StateDocument,
+ StateRecord,
+ StateField
+ };
+
+ State state;
+
+ QDict<int> keyIndexList;
+ int keyIndex;
+ QString key;
+};
+
+#endif