// Copyright (c) 2003 Charles Samuels // See the file COPYING for redistribution terms. #include "base.h" #include "file.h" #include "kdbt.h" #include "kbuffer.h" #include #include #include #include #include #include #include struct Base::Private { Private() : db(0, DB_CXX_NO_EXCEPTIONS) { } Db db; typedef KDbt Key; typedef KDbt Data; FileId high; FileId cachedId; mutable TQMap cachedProperties; TQPtrList slices; int sliceHigh; }; Base::Base(const TQString &file) { d = new Private; d->cachedId = 0; TQCString filename = TQFile::encodeName(file); bool create = true; if (d->db.open( #if DB_VERSION_MINOR > 0 && DB_VERSION_MAJOR >= 4 NULL, #endif filename, 0, DB_BTREE, DB_NOMMAP, 0 )==0) { // success Private::Data data; Private::Key key(0); if (d->db.get(0, &key, &data, 0)==0) { TQStringList strs; data.get(strs); mFormatVersion = strs[0].toUInt(0, 16); // TODO d->high = strs[1].toUInt(); if (strs.count() == 3) loadMetaXML(strs[2]); else loadMetaXML(""); create=false; } } if (create) { // failure TQFile(filename).remove(); d->db.open( #if DB_VERSION_MINOR > 0 && DB_VERSION_MAJOR >= 4 NULL, #endif filename,0, DB_BTREE, DB_NOMMAP|DB_CREATE,0 ); d->high=0; TQStringList strs; strs << "00010002" << "0" << ""; resetFormatVersion(); loadMetaXML(""); Private::Data data(strs); Private::Key key(0); // in the stringlist for Key(0), we have the following list: // { "version of the file", // "the high extreme (auto-increment counter in SQL terminology)", // "the metaxml" // } d->db.put(0, &key, &data, 0); } } void Base::resetFormatVersion() { mFormatVersion = 0x00010002; } Base::~Base() { TQStringList strs; strs << TQString::number(mFormatVersion, 16) << TQString::number(d->high); strs << saveMetaXML(); Private::Data data(strs); Private::Key key(0); d->db.put(0, &key, &data, 0); d->db.sync(0); d->db.close(0); delete d; } File Base::add(const TQString &file) { Private::Key key(++d->high); TQStringList properties; properties << "file" << file; Private::Data data(properties); unless (d->db.put(0, &key, &data, 0)) { // success ! File f(this, d->high); f.makeCache(); emit added(f); return f; } return File(); } File Base::find(FileId id) { if (id == 0) return File(); Private::Key key(id); Private::Data data; unless (d->db.get(0, &key, &data, 0)) { // exists return File(this, id); } else { return File(); // null item } } void Base::clear() { for (FileId id = high(); id >= 1; id--) { File f = find(id); if (f) f.remove(); } } FileId Base::high() const { return d->high; } File Base::first(FileId first) { if (first > high()) return File(); while (!find(first)) { ++first; if (first > high()) return File(); } return File(this, first); } TQString Base::property(FileId id, const TQString &property) const { loadIntoCache(id); if (!d->cachedProperties.contains(property)) return TQString(); TQMap::Iterator i = d->cachedProperties.find(property); return i.data(); } void Base::setProperty(FileId id, const TQString &key, const TQString &value) { loadIntoCache(id); d->cachedProperties.insert(key, value); // reinsert it into the DB TQStringList props; for ( TQMap::Iterator i(d->cachedProperties.begin()); i != d->cachedProperties.end(); ++i ) { props << i.key() << i.data(); } Private::Data data(props); Private::Key dbkey(id); d->db.put(0, &dbkey, &data, 0); d->db.sync(0); emit modified(File(this, id)); } TQStringList Base::properties(FileId id) const { loadIntoCache(id); TQStringList props; for ( TQMap::Iterator i(d->cachedProperties.begin()); i != d->cachedProperties.end(); ++i ) { props << i.key(); } return props; } void Base::clearProperty(FileId id, const TQString &key) { loadIntoCache(id); d->cachedProperties.remove(key); // reinsert it into the DB TQStringList props; for ( TQMap::Iterator i(d->cachedProperties.begin()); i != d->cachedProperties.end(); ++i ) { if (i.key() != key) props << i.key() << i.data(); } Private::Data data(props); Private::Key dbkey(id); d->db.put(0, &dbkey, &data, 0); d->db.sync(0); emit modified(File(this, id)); } void Base::remove(File file) { Private::Key key(file.id()); unless (d->db.del(0, &key, 0)) { emit removed(file); if (file.id() == d->high) { d->high--; // optimization } } d->db.sync(0); } void Base::loadIntoCache(FileId id) const { if (d->cachedId == id) return; d->cachedId = id; d->cachedProperties.clear(); Private::Key key(id); Private::Data data; unless (d->db.get(0, &key, &data, 0)) { TQStringList props; data.get(props); if (props.count() % 2) { // corrupt? const_cast(this)->remove(File(const_cast(this), id)); return; } for (TQStringList::Iterator i(props.begin()); i != props.end(); ++i) { TQString &key = *i; TQString &value = *++i; d->cachedProperties.insert(key, value); } } } TQString Base::saveMetaXML() { TQDomDocument doc; doc.setContent(TQString("")); TQDomElement doce = doc.documentElement(); TQDomElement e = doc.createElement("slices"); e.setAttribute("highslice", TQString::number(d->sliceHigh)); doce.appendChild(e); for (TQPtrListIterator i(d->slices); *i; ++i) { TQDomElement slice = doc.createElement("slice"); slice.setAttribute("id", (*i)->id()); slice.setAttribute("name", (*i)->name()); e.appendChild(slice); } return doc.toString(); } void Base::move(FileId oldid, FileId newid) { Private::Key key(oldid); Private::Data data; unless (d->db.get(0, &key, &data, 0)) { TQStringList props; data.get(props); d->db.del(0, &key, 0); Private::Key key2(newid); d->db.put(0, &key2, &data, 0); } } void Base::dump() { for (FileId id=1; id <= high(); id++) { TQStringList props = properties(id); std::cerr << id << '.'; for (TQStringList::Iterator i(props.begin()); i != props.end(); ++i) { TQString prop = *i; std::cerr << ' ' << prop.latin1() << '=' << property(id, prop).latin1(); } std::cerr << std::endl; } } void Base::notifyChanged(const File &file) { emit modified(file); } TQPtrList Base::slices() { return d->slices; } Slice *Base::addSlice(const TQString &name) { Slice *sl = new Slice(this, d->sliceHigh++, name); d->slices.append(sl); slicesModified(); return sl; } Slice *Base::defaultSlice() { for (TQPtrListIterator i(d->slices); *i; ++i) { if ((*i)->id() == 0) return *i; } abort(); return 0; } void Base::removeSlice(Slice *slice) { assert(slice->id() > 0); d->slices.removeRef(slice); delete slice; } Slice *Base::sliceById(int id) { for (TQPtrListIterator i(d->slices); *i; ++i) { if ((*i)->id() == id) return *i; } return 0; } void Base::loadMetaXML(const TQString &xml) { d->slices.setAutoDelete(true); d->slices.clear(); d->slices.setAutoDelete(false); TQDomDocument doc; doc.setContent(xml); TQDomElement doce = doc.documentElement(); bool loadedId0=false; for (TQDomNode n = doce.firstChild(); !n.isNull(); n = n.nextSibling()) { TQDomElement e = n.toElement(); if (e.isNull()) continue; if (e.tagName().lower() == "slices") { d->sliceHigh = e.attribute("highslice", "1").toInt(); for (TQDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) { TQDomElement e = n.toElement(); if (e.isNull()) continue; if (e.tagName().lower() == "slice") { int id = e.attribute("id").toInt(); if (id==0 && loadedId0) break; loadedId0=true; TQString name = e.attribute("name"); d->slices.append(new Slice(this, id, name)); } } } } if (d->slices.count() == 0) { // we must have a default d->slices.append(new Slice(this, 0, "")); } } #include "base.moc"