diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | 460c52653ab0dcca6f19a4f492ed2c5e4e963ab0 (patch) | |
tree | 67208f7c145782a7e90b123b982ca78d88cc2c87 /akregator/src/mk4storage/metakit/src/store.cpp | |
download | tdepim-460c52653ab0dcca6f19a4f492ed2c5e4e963ab0.tar.gz tdepim-460c52653ab0dcca6f19a4f492ed2c5e4e963ab0.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdepim@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'akregator/src/mk4storage/metakit/src/store.cpp')
-rw-r--r-- | akregator/src/mk4storage/metakit/src/store.cpp | 587 |
1 files changed, 587 insertions, 0 deletions
diff --git a/akregator/src/mk4storage/metakit/src/store.cpp b/akregator/src/mk4storage/metakit/src/store.cpp new file mode 100644 index 00000000..a32de665 --- /dev/null +++ b/akregator/src/mk4storage/metakit/src/store.cpp @@ -0,0 +1,587 @@ +// store.cpp -- +// $Id$ +// This is part of Metakit, the homepage is http://www.equi4.com/metakit/ + +/** @file + * Storage management and several other loose ends + */ + +#include "header.h" +#include "handler.h" // 19990906 +#include "store.h" +#include "field.h" +#include "persist.h" +#include "format.h" // 19990906 + +#include "mk4io.h" // 19991104 + +#if !q4_INLINE +#include "store.inl" +#endif + +///////////////////////////////////////////////////////////////////////////// + +c4_Dependencies::c4_Dependencies () +{ + _refs.SetSize(0, 3); // a little optimization +} + +c4_Dependencies::~c4_Dependencies () +{ +} + +void c4_Dependencies::Add(c4_Sequence* seq_) +{ + for (int i = 0; i < _refs.GetSize(); ++i) + d4_assert(_refs.GetAt(i) != seq_); + + _refs.Add(seq_); +} + +bool c4_Dependencies::Remove(c4_Sequence* seq_) +{ + int n = _refs.GetSize() - 1; + d4_assert(n >= 0); + + for (int i = 0; i <= n; ++i) + if (_refs.GetAt(i) == seq_) { + _refs.SetAt(i, _refs.GetAt(n)); + _refs.SetSize(n); + return n > 0; + } + + d4_assert(0); // dependency not found + return true; +} + +///////////////////////////////////////////////////////////////////////////// + +c4_Notifier::~c4_Notifier () +{ + if (_type > kNone && _origin->GetDependencies()) { + c4_PtrArray& refs = _origin->GetDependencies()->_refs; + + for (int i = 0; i < refs.GetSize(); ++i) { + c4_Sequence* seq = (c4_Sequence*) refs.GetAt(i); + d4_assert(seq != 0); + + seq->PostChange(*this); + + if (_chain && _chain->_origin == seq) { + c4_Notifier* next = _chain->_next; + _chain->_next = 0; + + delete _chain; + + _chain = next; + } + } + } + + d4_assert(!_chain); + d4_assert(!_next); +} + +void c4_Notifier::StartSetAt(int index_, c4_Cursor& cursor_) +{ + _type = kSetAt; + _index = index_; + _cursor = &cursor_; + + Notify(); +} + +void c4_Notifier::StartInsertAt(int i_, c4_Cursor& cursor_, int n_) +{ + _type = kInsertAt; + _index = i_; + _cursor = &cursor_; + _count = n_; + + Notify(); +} + +void c4_Notifier::StartRemoveAt(int index_, int count_) +{ + _type = kRemoveAt; + _index = index_; + _count = count_; + + Notify(); +} + +void c4_Notifier::StartMove(int from_, int to_) +{ + _type = kMove; + _index = from_; + _count = to_; + + Notify(); +} + +void c4_Notifier::StartSet(int i_, int propId_, const c4_Bytes& buf_) +{ + _type = kSet; + _index = i_; + _propId = propId_; + _bytes = &buf_; + + Notify(); +} + +void c4_Notifier::Notify() +{ + d4_assert(_origin->GetDependencies() != 0); + c4_PtrArray& refs = _origin->GetDependencies()->_refs; + + int n = refs.GetSize(); + d4_assert(n > 0); + + c4_Notifier** rover = &_chain; + + for (int i = 0; i < n; ++i) { + c4_Sequence* seq = (c4_Sequence*) refs.GetAt(i); + d4_assert(seq != 0); + + c4_Notifier* ptr = seq->PreChange(*this); + if (ptr) { + d4_assert(ptr->_origin == seq); + + d4_assert(!*rover); + *rover = ptr; + rover = &ptr->_next; + } + } +} + +///////////////////////////////////////////////////////////////////////////// + +/** @class c4_Storage + * + * Manager for persistent storage of view structures. + * + * The storage class uses a view, with additional functionality to be able + * to store and reload the data it contains (including nested subviews). + * + * By default, data is loaded on demand, i.e. whenever data which has + * not yet been referenced is used for the first time. Loading is limited + * to the lifetime of this storage object, since the storage object carries + * the file descriptor with it that is needed to access the data file. + * + * To save changes, call the Commit member. This is the only time + * that data is written to file - when using a read-only file simply avoid + * calling Commit. + * + * The LoadFromStream and SaveToStream members can be used to + * serialize the contents of this storage row using only sequential I/O + * (no seeking, only read or write calls). + * + * The data storage mechanism implementation provides fail-safe operation: + * if anything prevents Commit from completing its task, the last + * succesfully committed version of the saved data will be recovered on + * the next open. This also includes changes made to the table structure. + * + * The following code creates a view with 1 row and stores it on file: + * @code + * c4_StringProp pName ("Name"); + * c4_IntProp pAge ("Age"); + * + * c4_Storage storage ("myfile.dat", true); + * c4_View myView = storage.GetAs("Musicians[Name:S,Age:I]"); + * + * myView.Add(pName ["John Williams"] + pAge [43]); + * + * storage.Commit(); + * @endcode + */ + +c4_Storage::c4_Storage () +{ + // changed to r/o, now that commits don't crash on it anymore + Initialize(*d4_new c4_Strategy, true, 0); +} + +c4_Storage::c4_Storage (c4_Strategy& strategy_, bool owned_, int mode_) +{ + Initialize(strategy_, owned_, mode_); + Persist()->LoadAll(); +} + +c4_Storage::c4_Storage (const char* fname_, int mode_) +{ + c4_FileStrategy* strat = d4_new c4_FileStrategy; + strat->DataOpen(fname_, mode_); + + Initialize(*strat, true, mode_); + if (strat->IsValid()) + Persist()->LoadAll(); +} + +c4_Storage::c4_Storage (const c4_View& root_) +{ + if (root_.Persist() != 0) // only restore if view was indeed persistent + *(c4_View*) this = root_; + else // if this was not possible, start with a fresh empty storage + Initialize(*d4_new c4_Strategy, true, 0); +} + +c4_Storage::~c4_Storage () +{ + // cannot unmap here, because there may still be an autocommit pending + //((c4_HandlerSeq*) _seq)->UnmapAll(); +} + +void c4_Storage::Initialize(c4_Strategy& strategy_, bool owned_, int mode_) +{ + c4_Persist* pers = d4_new c4_Persist (strategy_, owned_, mode_); + c4_HandlerSeq* seq = d4_new c4_HandlerSeq (pers); + seq->DefineRoot(); + *(c4_View*) this = seq; + pers->SetRoot(seq); +} + + /// Get or set a named view in this storage object +c4_ViewRef c4_Storage::View(const char* name_) +{ + /* + The easy solution would seem to be: + + c4_ViewProp prop (name_); + return prop (Contents()); + + But this does not work, because the return value would point to + an object allocated on the stack. + + Instead, make sure the view *has* such a property, and use the + one inside the c4_Handler for it (since this will stay around). + */ + +// int n = _root->PropIndex(c4_ViewProp (name_)); + + c4_ViewProp prop (name_); + int n = AddProperty(prop); + d4_assert(n >= 0); + + // the following is an expression of the form "property (rowref)" + return NthProperty(n) (GetAt(0)); +} + + /// Get a named view, redefining it to match the given structure +c4_View c4_Storage::GetAs(const char* description_) +{ + d4_assert(description_ != 0); + + // Dec 2001: now that GetAs is being used so much more frequently, + // add a quick check to see whether restructuring is needed at all + const char* q = strchr(description_, '['); + if (q != 0) { + c4_String vname (description_, q - description_); + const char* d = Description(vname); + if (d != 0) { + c4_String desc (d); + if (("[" + desc + "]").CompareNoCase(q) == 0) + return View(vname); + } + } + + c4_Field* field = d4_new c4_Field (description_); + d4_assert(field != 0); + + d4_assert(!*description_); + + c4_String name = field->Name(); + d4_assert(!name.IsEmpty()); + + c4_Field& curr = Persist()->Root().Definition(); + + c4_String newField = "," + field->Description(); + bool keep = newField.Find('[') >= 0; + + c4_String newDef; + + // go through all subfields + for (int i = 0; i < curr.NumSubFields(); ++i) { + c4_Field& of = curr.SubField(i); + if (of.Name().CompareNoCase(name) == 0) { + if (field->IsRepeating()) + newDef += newField; + // else new is not a repeating entry, so drop this entire field + + newField.Empty(); // don't append it later on + continue; + } + + newDef += "," + of.Description(); // keep original field + } + + if (keep) // added 19990824 ignore if deletion + newDef += newField; // appends new definition if not found earlier + + delete field; + + const char* p = newDef; + SetStructure(*p ? ++p : p); // skip the leading comma + + if (!keep) // 19990916: avoid adding an empty view again + return c4_View (); + + return View(name); +} + + /// Define the complete view structure of the storage +void c4_Storage::SetStructure(const char* description_) +{ + d4_assert(description_ != 0); + + if (description_ != Description()) { + c4_String s = "[" + c4_String (description_) + "]"; + description_ = s; + + c4_Field* field = d4_new c4_Field (description_); + d4_assert(!*description_); + + d4_assert(field != 0); + Persist()->Root().Restructure(*field, false); + } +} + + /// Return the strategy object associated with this storage +c4_Strategy& c4_Storage::Strategy() const +{ + return Persist()->Strategy(); +} + + /// Return a description of the view structure (default is all) +const char* c4_Storage::Description(const char* name_) +{ + if (name_ == 0 || *name_ == 0) + return c4_View::Description(); + + c4_View v = View(name_); + return v.Description(); +} + + /// Define the storage to use for differential commits +bool c4_Storage::SetAside(c4_Storage& aside_) +{ + c4_Persist* pers = Persist(); + bool f = pers->SetAside(aside_); + // adjust our copy when the root view has been replaced + *(c4_View*) this = &pers->Root(); + return f; +} + + /// Return storage used for differential commits, or null +c4_Storage* c4_Storage::GetAside() const +{ + return Persist()->GetAside(); +} + + /// Flush pending changes to file right now +bool c4_Storage::Commit(bool full_) +{ + return Strategy().IsValid() && Persist()->Commit(full_); +} + +/** (Re)initialize for on-demand loading + * + * Calling Rollback will cancel all uncommitted changes. + */ +bool c4_Storage::Rollback(bool full_) +{ + c4_Persist* pers = Persist(); + bool f = Strategy().IsValid() && pers->Rollback(full_); + // adjust our copy when the root view has been replaced + *(c4_View*) this = &pers->Root(); + return f; +} + + /// Set storage up to always call Commit in the destructor +bool c4_Storage::AutoCommit(bool flag_) +{ + return Persist()->AutoCommit(flag_); +} + + /// Load contents from the specified input stream +bool c4_Storage::LoadFrom(c4_Stream& stream_) +{ + c4_HandlerSeq* newRoot = c4_Persist::Load(&stream_); + if (newRoot == 0) + return false; + + // fix commit-after-load bug, by using a full view copy + // this is inefficient, but avoids mapping/strategy problems + c4_View temp (newRoot); + + SetSize(0); + SetStructure(temp.Description()); + InsertAt(0, temp); + + return true; +} + + /// Save contents to the specified output stream +void c4_Storage::SaveTo(c4_Stream& stream_) +{ + c4_Persist::Save(&stream_, Persist()->Root()); +} + +///////////////////////////////////////////////////////////////////////////// + +c4_DerivedSeq::c4_DerivedSeq (c4_Sequence& seq_) + : _seq (seq_) +{ + _seq.Attach(this); +} + +c4_DerivedSeq::~c4_DerivedSeq () +{ + _seq.Detach(this); +} + +int c4_DerivedSeq::RemapIndex(int index_, const c4_Sequence* seq_) const +{ + return seq_ == this ? index_ : _seq.RemapIndex(index_, seq_); +} + +int c4_DerivedSeq::NumRows() const +{ + return _seq.NumRows(); +} + +int c4_DerivedSeq::NumHandlers() const +{ + return _seq.NumHandlers(); +} + +c4_Handler& c4_DerivedSeq::NthHandler(int colNum_) const +{ + return _seq.NthHandler(colNum_); +} + +const c4_Sequence* c4_DerivedSeq::HandlerContext(int colNum_) const +{ + return _seq.HandlerContext(colNum_); +} + +int c4_DerivedSeq::AddHandler(c4_Handler* handler_) +{ + return _seq.AddHandler(handler_); +} + +c4_Handler* c4_DerivedSeq::CreateHandler(const c4_Property& prop_) +{ + return _seq.CreateHandler(prop_); +} + +void c4_DerivedSeq::SetNumRows(int size_) +{ + _seq.SetNumRows(size_); +} + +c4_Notifier* c4_DerivedSeq::PreChange(c4_Notifier& nf_) +{ + if (!GetDependencies()) + return 0; + + c4_Notifier* chg = d4_new c4_Notifier (this); + + switch (nf_._type) + { + case c4_Notifier::kSetAt: + chg->StartSetAt(nf_._index, *nf_._cursor); + break; + + case c4_Notifier::kSet: + chg->StartSet(nf_._index, nf_._propId, *nf_._bytes); + break; + + case c4_Notifier::kInsertAt: + chg->StartInsertAt(nf_._index, *nf_._cursor, nf_._count); + break; + + case c4_Notifier::kRemoveAt: + chg->StartRemoveAt(nf_._index, nf_._count); + break; + + case c4_Notifier::kMove: + chg->StartMove(nf_._index, nf_._count); + break; + } + + return chg; +} + +///////////////////////////////////////////////////////////////////////////// + +c4_StreamStrategy::c4_StreamStrategy (t4_i32 buflen_) + : _stream (0), _buffer (d4_new t4_byte [buflen_]), _buflen (buflen_), _position (0) +{ + _mapStart = _buffer; + _dataSize = buflen_; +} + +c4_StreamStrategy::c4_StreamStrategy (c4_Stream* stream_) + : _stream (stream_), _buffer (0), _buflen (0), _position (0) +{ +} + +c4_StreamStrategy::~c4_StreamStrategy () +{ + _mapStart = 0; + _dataSize = 0; + + if (_buffer != 0) + delete [] _buffer; +} + +bool c4_StreamStrategy::IsValid() const +{ + return true; +} + +int c4_StreamStrategy::DataRead(t4_i32 pos_, void* buffer_, int length_) +{ + if (_buffer != 0) { + d4_assert(pos_ <= _buflen); + _position = pos_ + _baseOffset; + + if (length_ > _buflen - _position) + length_ = _buflen - _position; + if (length_ > 0) + memcpy(buffer_, _buffer + _position, length_); + } else { + d4_assert(_position == pos_ + _baseOffset); + length_ = _stream != 0 ? _stream->Read(buffer_, length_) : 0; + } + + _position += length_; + return length_; +} + +void c4_StreamStrategy::DataWrite(t4_i32 pos_, const void* buffer_, int length_) +{ + if (_buffer != 0) { + d4_assert(pos_ <= _buflen); + _position = pos_ + _baseOffset; + + int n = length_; + if (n > _buflen - _position) + n = _buflen - _position; + if (n > 0) + memcpy(_buffer + _position, buffer_, n); + } else { + d4_assert(_position == pos_ + _baseOffset); + if (_stream != 0 && !_stream->Write(buffer_, length_)) + ++_failure; + } + + _position += length_; +} + +t4_i32 c4_StreamStrategy::FileSize() +{ + return _position; +} + +///////////////////////////////////////////////////////////////////////////// |