author | kergoth <kergoth> | 2002-01-25 22:14:26 (UTC) |
---|---|---|
committer | kergoth <kergoth> | 2002-01-25 22:14:26 (UTC) |
commit | 15318cad33835e4e2dc620d033e43cd930676cdd (patch) (side-by-side diff) | |
tree | c2fa0399a2c47fda8e2cd0092c73a809d17f68eb /noncore/apps/tableviewer/db | |
download | opie-15318cad33835e4e2dc620d033e43cd930676cdd.zip opie-15318cad33835e4e2dc620d033e43cd930676cdd.tar.gz opie-15318cad33835e4e2dc620d033e43cd930676cdd.tar.bz2 |
Initial revision
-rw-r--r-- | noncore/apps/tableviewer/db/.cvsignore | 3 | ||||
-rw-r--r-- | noncore/apps/tableviewer/db/common.cpp | 1470 | ||||
-rw-r--r-- | noncore/apps/tableviewer/db/common.h | 285 | ||||
-rw-r--r-- | noncore/apps/tableviewer/db/csvsource.cpp | 207 | ||||
-rw-r--r-- | noncore/apps/tableviewer/db/csvsource.h | 53 | ||||
-rw-r--r-- | noncore/apps/tableviewer/db/datacache.cpp | 293 | ||||
-rw-r--r-- | noncore/apps/tableviewer/db/datacache.h | 130 | ||||
-rw-r--r-- | noncore/apps/tableviewer/db/xmlsource.cpp | 295 | ||||
-rw-r--r-- | noncore/apps/tableviewer/db/xmlsource.h | 119 |
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 |