diff options
Diffstat (limited to 'akregator/src/mk4storage/metakit/src/derived.cpp')
-rw-r--r-- | akregator/src/mk4storage/metakit/src/derived.cpp | 1003 |
1 files changed, 1003 insertions, 0 deletions
diff --git a/akregator/src/mk4storage/metakit/src/derived.cpp b/akregator/src/mk4storage/metakit/src/derived.cpp new file mode 100644 index 00000000..3baf5e3e --- /dev/null +++ b/akregator/src/mk4storage/metakit/src/derived.cpp @@ -0,0 +1,1003 @@ +// derived.cpp -- +// $Id$ +// This is part of Metakit, see http://www.equi4.com/metakit/ + +/** @file + * Derived views are virtual views which track changes + */ + +#include "header.h" +#include "handler.h" +#include "store.h" +#include "derived.h" + +#include <stdlib.h> // qsort + +///////////////////////////////////////////////////////////////////////////// +// Implemented in this file + +// class c4_Sequence; + class c4_DerivedSeq; + class c4_FilterSeq; + class c4_SortSeq; + class c4_ProjectSeq; + +///////////////////////////////////////////////////////////////////////////// + +class c4_FilterSeq : public c4_DerivedSeq +{ +protected: + c4_DWordArray _rowMap; + c4_DWordArray _revMap; + c4_Row _lowRow; + c4_Row _highRow; + c4_Bytes _rowIds; + +protected: + c4_FilterSeq (c4_Sequence& seq_); + virtual ~c4_FilterSeq (); + + void FixupReverseMap(); + int PosInMap(int index_) const; + bool Match(int index_, c4_Sequence& seq_, + const int* =0, const int* =0) const; + bool MatchOne(int prop_, const c4_Bytes& data_) const; + +public: + c4_FilterSeq (c4_Sequence& seq_, c4_Cursor low_, c4_Cursor high_); + + virtual int RemapIndex(int, const c4_Sequence*) const; + + virtual int NumRows() const; + + virtual int Compare(int, c4_Cursor) const; + virtual bool Get(int, int, c4_Bytes&); + + virtual void InsertAt(int, c4_Cursor, int =1); + virtual void RemoveAt(int, int =1); + virtual void Set(int, const c4_Property&, const c4_Bytes&); + virtual void SetSize(int); + + virtual c4_Notifier* PreChange(c4_Notifier& nf_); + virtual void PostChange(c4_Notifier& nf_); +}; + +///////////////////////////////////////////////////////////////////////////// + +c4_FilterSeq::c4_FilterSeq (c4_Sequence& seq_) + : c4_DerivedSeq (seq_) +{ + _rowMap.SetSize(_seq.NumRows()); + _revMap.SetSize(_seq.NumRows()); + d4_assert(NumRows() == _seq.NumRows()); + + for (int i = 0; i < NumRows(); ++i) + { + _rowMap.SetAt(i, i); + _revMap.SetAt(i, i); + } +} + +c4_FilterSeq::c4_FilterSeq (c4_Sequence& seq_, c4_Cursor low_, + c4_Cursor high_) + : c4_DerivedSeq (seq_), _lowRow (*low_), _highRow (*high_) +{ + d4_assert((&_lowRow)._index == 0); + d4_assert((&_highRow)._index == 0); + + // use a sneaky way to obtain the sequence pointers and indices + c4_Sequence* lowSeq = (& _lowRow)._seq; + c4_Sequence* highSeq = (& _highRow)._seq; + d4_assert(lowSeq && highSeq); + + // prepare column numbers to avoid looking them up on every row + // lowCols is a vector of column numbers to use for the low limits + // highCols is a vector of column numbers to use for the high limits + int nl = lowSeq->NumHandlers(), nh = highSeq->NumHandlers(); + c4_Bytes lowVec, highVec; + int* lowCols = (int*) lowVec.SetBufferClear(nl * sizeof (int)); + int* highCols = (int*) highVec.SetBufferClear(nh * sizeof (int)); + + for (int il = 0; il < nl; ++il) + lowCols[il] = seq_.PropIndex(lowSeq->NthPropId(il)); + for (int ih = 0; ih < nh; ++ih) + highCols[ih] = seq_.PropIndex(highSeq->NthPropId(ih)); + + // set _rowIds flag buffer for fast matching + { + int max = -1; + + { + for (int i1 = 0; i1 < nl; ++i1) + { + int n = lowSeq->NthPropId(i1); + if (max < n) + max = n; + } + for (int i2 = 0; i2 < nh; ++i2) + { + int n = highSeq->NthPropId(i2); + if (max < n) + max = n; + } + } + + t4_byte* p = _rowIds.SetBufferClear(max + 1); + + { + for (int i1 = 0; i1 < nl; ++i1) + p[lowSeq->NthPropId(i1)] |= 1; + for (int i2 = 0; i2 < nh; ++i2) + p[highSeq->NthPropId(i2)] |= 2; + } + } + + // now go through all rows and select the ones that are in range + + _rowMap.SetSize(_seq.NumRows()); // avoid growing, use safe upper bound + + int n = 0; + + for (int i = 0; i < _seq.NumRows(); ++i) + if (Match(i, _seq, lowCols, highCols)) + _rowMap.SetAt(n++, i); + + _rowMap.SetSize(n); + + FixupReverseMap(); +} + +c4_FilterSeq::~c4_FilterSeq () +{ +} + +void c4_FilterSeq::FixupReverseMap() +{ + int n = _seq.NumRows(); + + _revMap.SetSize(0); + + if (n > 0) + { + _revMap.InsertAt(0, ~ (t4_i32) 0, n); //! + + for (int i = 0; i < _rowMap.GetSize(); ++i) + _revMap.SetAt((int) _rowMap.GetAt(i), i); + } +} + +bool c4_FilterSeq::MatchOne(int prop_, const c4_Bytes& data_) const +{ + d4_assert(prop_ < _rowIds.Size()); + + t4_byte flag = _rowIds.Contents()[prop_]; + d4_assert(flag); + + if (flag & 1) + { + c4_Sequence* lowSeq = (& _lowRow)._seq; + + c4_Handler& h = lowSeq->NthHandler(lowSeq->PropIndex(prop_)); + if (h.Compare(0, data_) > 0) + return false; + } + + if (flag & 2) + { + c4_Sequence* highSeq = (& _highRow)._seq; + + c4_Handler& h = highSeq->NthHandler(highSeq->PropIndex(prop_)); + if (h.Compare(0, data_) < 0) + return false; + } + + return true; +} + +bool c4_FilterSeq::Match(int index_, c4_Sequence& seq_, + const int* lowCols_, const int* highCols_) const +{ + // use a sneaky way to obtain the sequence pointers and indices + c4_Sequence* lowSeq = (& _lowRow)._seq; + c4_Sequence* highSeq = (& _highRow)._seq; + d4_assert(lowSeq && highSeq); + + int nl = lowSeq->NumHandlers(), nh = highSeq->NumHandlers(); + + c4_Bytes data; + + // check each of the lower limits + for (int cl = 0; cl < nl; ++cl) + { + c4_Handler& hl = lowSeq->NthHandler(cl); + + int n = lowCols_ ? lowCols_[cl] + : seq_.PropIndex(lowSeq->NthPropId(cl)); + if (n >= 0) + { + c4_Handler& h = seq_.NthHandler(n); + const c4_Sequence* hc = seq_.HandlerContext(n); + int i = seq_.RemapIndex(index_, hc); + + h.GetBytes(i, data); + } + else + hl.ClearBytes(data); + + if (hl.Compare(0, data) > 0) + return false; + } + + // check each of the upper limits + for (int ch = 0; ch < nh; ++ch) + { + c4_Handler& hh = highSeq->NthHandler(ch); + + int n = highCols_ ? highCols_[ch] + : seq_.PropIndex(highSeq->NthPropId(ch)); + if (n >= 0) + { + c4_Handler& h = seq_.NthHandler(n); + const c4_Sequence* hc = seq_.HandlerContext(n); + int i = seq_.RemapIndex(index_, hc); + + h.GetBytes(i, data); + } + else + hh.ClearBytes(data); + + if (hh.Compare(0, data) < 0) + return false; + } + + return true; +} + +int c4_FilterSeq::RemapIndex(int index_, const c4_Sequence* seq_) const +{ + return seq_ == this ? index_ + : _seq.RemapIndex((int) _rowMap.GetAt(index_), seq_); +} + +int c4_FilterSeq::NumRows() const +{ + return _rowMap.GetSize(); +} + +int c4_FilterSeq::Compare(int index_, c4_Cursor cursor_) const +{ + return _seq.Compare((int) _rowMap.GetAt(index_), cursor_); +} + +bool c4_FilterSeq::Get(int index_, int propId_, c4_Bytes& bytes_) +{ + return _seq.Get((int) _rowMap.GetAt(index_), propId_, bytes_); +} + +void c4_FilterSeq::InsertAt(int, c4_Cursor, int) +{ + d4_assert(0); +} + +void c4_FilterSeq::RemoveAt(int, int) +{ + d4_assert(0); +} + +void c4_FilterSeq::Set(int, const c4_Property&, const c4_Bytes&) +{ + d4_assert(0); +} + +void c4_FilterSeq::SetSize(int) +{ + d4_assert(0); +} + +int c4_FilterSeq::PosInMap(int index_) const +{ + int i = 0; + + while (i < NumRows()) + if ((int) _rowMap.GetAt(i) >= index_) + break; + else + ++i; + + return i; +} + +c4_Notifier* c4_FilterSeq::PreChange(c4_Notifier& nf_) +{ + if (!GetDependencies()) + return 0; + + c4_Notifier* chg = d4_new c4_Notifier (this); + + bool pass = false; + + switch (nf_._type) + { + case c4_Notifier::kSet: + pass = nf_._propId >= _rowIds.Size() || + _rowIds.Contents()[nf_._propId] == 0; + // fall through... + + case c4_Notifier::kSetAt: + { + int r = (int) _revMap.GetAt(nf_._index); + + bool includeRow = r >= 0; + if (!pass) + if (nf_._type == c4_Notifier::kSetAt) + { + d4_assert(nf_._cursor != 0); + includeRow = Match(nf_._cursor->_index, + *nf_._cursor->_seq); + } + else // set just one property, and it's not in a row yet + includeRow = MatchOne(nf_._propId, *nf_._bytes); + + if (r >= 0 && !includeRow) + chg->StartRemoveAt(r, 1); + else if (r < 0 && includeRow) + chg->StartInsertAt(PosInMap(nf_._index), *nf_._cursor, 1); + else if (includeRow) + { + d4_assert(r >= 0); + + if (nf_._type == c4_Notifier::kSetAt) + chg->StartSetAt(r, *nf_._cursor); + else + chg->StartSet(r, nf_._propId, *nf_._bytes); + } + } + break; + + case c4_Notifier::kInsertAt: + { + int i = PosInMap(nf_._index); + + d4_assert(nf_._cursor != 0); + if (Match(nf_._cursor->_index, *nf_._cursor->_seq)) + chg->StartInsertAt(i, *nf_._cursor, nf_._count); + } + break; + + case c4_Notifier::kRemoveAt: + { + int i = PosInMap(nf_._index); + int j = PosInMap(nf_._index + nf_._count); + d4_assert(j >= i); + + if (j > i) + chg->StartRemoveAt(i, j - i); + } + break; + + case c4_Notifier::kMove: + { + int i = PosInMap(nf_._index); + bool inMap = i < NumRows() && (int) _rowMap.GetAt(i) == nf_._index; + + if (inMap && nf_._index != nf_._count) + chg->StartMove(i, PosInMap(nf_._count)); + } + break; + } + + return chg; +} + +void c4_FilterSeq::PostChange(c4_Notifier& nf_) +{ + bool pass = false; + + switch (nf_._type) + { + case c4_Notifier::kSet: + pass = nf_._propId >= _rowIds.Size() || + _rowIds.Contents()[nf_._propId] == 0; + // fall through... + + case c4_Notifier::kSetAt: + { + int r = (int) _revMap.GetAt(nf_._index); + + bool includeRow = r >= 0; + if (!pass) + if (nf_._type == c4_Notifier::kSetAt) + { + d4_assert(nf_._cursor != 0); + includeRow = Match(nf_._cursor->_index, + *nf_._cursor->_seq); + } + else // set just one property, and it's not in a row yet + includeRow = MatchOne(nf_._propId, *nf_._bytes); + + if (r >= 0 && !includeRow) + _rowMap.RemoveAt(r); + else if (r < 0 && includeRow) + _rowMap.InsertAt(PosInMap(nf_._index), nf_._index); + else + break; + + FixupReverseMap(); + } + break; + + case c4_Notifier::kInsertAt: + { + int i = PosInMap(nf_._index); + + if (Match(nf_._index, _seq)) + { + _rowMap.InsertAt(i, 0, nf_._count); + + for (int j = 0; j < nf_._count; ++j) + _rowMap.SetAt(i++, nf_._index + j); + } + + while (i < NumRows()) + _rowMap.ElementAt(i++) += nf_._count; + + FixupReverseMap(); + } + break; + + case c4_Notifier::kRemoveAt: + { + int i = PosInMap(nf_._index); + int j = PosInMap(nf_._index + nf_._count); + d4_assert(j >= i); + + if (j > i) + _rowMap.RemoveAt(i, j - i); + + while (i < NumRows()) + _rowMap.ElementAt(i++) -= nf_._count; + + FixupReverseMap(); + } + break; + + case c4_Notifier::kMove: + { + int i = PosInMap(nf_._index); + bool inMap = i < NumRows() && (int) _rowMap.GetAt(i) == nf_._index; + + if (inMap && nf_._index != nf_._count) + { + int j = PosInMap(nf_._count); + + _rowMap.RemoveAt(i); + + if (j > i) + --j; + + _rowMap.InsertAt(j, nf_._count); + + FixupReverseMap(); + } + } + break; + } +} + +///////////////////////////////////////////////////////////////////////////// + +class c4_SortSeq : public c4_FilterSeq +{ +public: + typedef t4_i32 T; + + c4_SortSeq (c4_Sequence& seq_, c4_Sequence* down_); + virtual ~c4_SortSeq (); + + virtual c4_Notifier* PreChange(c4_Notifier& nf_); + virtual void PostChange(c4_Notifier& nf_); + +private: + struct c4_SortInfo + { + c4_Handler* _handler; + const c4_Sequence* _context; + c4_Bytes _buffer; + + int CompareOne(c4_Sequence& seq_, T a, T b) + { + _handler->GetBytes(seq_.RemapIndex((int) b, _context), _buffer, true); + return _handler->Compare(seq_.RemapIndex((int) a, _context), _buffer); + } + }; + + bool LessThan(T a, T b); + bool TestSwap( T& first , T& second ); + void MergeSortThis( T* ar, int size , T scratch[] ); + void MergeSort( T ar[] , int size ); + + virtual int Compare(int, c4_Cursor) const; + int PosInMap(c4_Cursor cursor_) const; + + c4_SortInfo* _info; + c4_Bytes _down; + int _width; +}; + +///////////////////////////////////////////////////////////////////////////// + +bool c4_SortSeq::LessThan(T a, T b) +{ + if (a == b) + return false; + + // go through each of the columns and compare values, but since + // handler access is used, we must be careful to remap indices + + c4_SortInfo* info; + + for (info = _info; info->_handler; ++info) + { + int f = info->CompareOne(_seq, a, b); + if (f) + { + int n = info - _info; + if (_width < n) + _width = n; + + return (_down.Contents()[n] ? -f : f) < 0; + } + } + + _width = info - _info; + return a < b; +} + +inline bool c4_SortSeq::TestSwap( T& first , T& second ) +{ + if ( LessThan( second , first ) ) + { + T temp = first; first = second; second = temp; + return true; + } + + return false; +} + +void c4_SortSeq::MergeSortThis( T* ar, int size , T scratch[] ) +{ + switch( size ) + { + //Handle the special cases for speed: + case 2: + TestSwap( ar[ 0 ] , ar[ 1 ] ); + break; + + case 3: + TestSwap( ar[ 0 ] , ar[ 1 ] ); + if ( TestSwap( ar[ 1 ] , ar[ 2 ] ) ) + TestSwap( ar[ 0 ] , ar[ 1 ] ); + break; + + case 4: + //Gotta optimize this.... + TestSwap( ar[ 0 ] , ar[ 1 ] ); + TestSwap( ar[ 2 ] , ar[ 3 ] ); + TestSwap( ar[ 0 ] , ar[ 2 ] ); + TestSwap( ar[ 1 ] , ar[ 3 ] ); + TestSwap( ar[ 1 ] , ar[ 2 ] ); + break; + + //Gotta do special case for list of five. + + default: + //Subdivide the list, recurse, and merge + { + int s1 = size / 2; + int s2 = size - s1; + T* from1_ = scratch; + T* from2_ = scratch + s1; + MergeSortThis( from1_ , s1 , ar ); + MergeSortThis( from2_ , s2 , ar + s1 ); + + T* to1_ = from1_ + s1; + T* to2_ = from2_ + s2; + + for (;;) + { + if ( LessThan( *from1_, *from2_) ) + { + *ar++ = *from1_++; + + if (from1_ >= to1_) + { + while( from2_ < to2_ ) + *ar++ = *from2_++; + break; + } + } + else + { + *ar++ = *from2_++; + + if (from2_ >= to2_) + { + while( from1_ < to1_ ) + *ar++ = *from1_++; + break; + } + } + } + } + } +} + +void c4_SortSeq::MergeSort( T ar[] , int size ) +{ + if ( size > 1 ) + { + T* scratch = d4_new T [size]; + memcpy(scratch , ar , size * sizeof (T)); + MergeSortThis(ar , size , scratch); + delete [] scratch; + } +} + +c4_SortSeq::c4_SortSeq (c4_Sequence& seq_, c4_Sequence* down_) + : c4_FilterSeq (seq_), _info (0), _width (-1) +{ + d4_assert(NumRows() == seq_.NumRows()); + + if (NumRows() > 0) + { + // down is a vector of flags, true to sort in reverse order + char* down = (char*) _down.SetBufferClear(NumHandlers()); + + // set the down flag for all properties to be sorted in reverse + if (down_) + for (int i = 0; i < NumHandlers(); ++i) + if (down_->PropIndex(NthPropId(i)) >= 0) + down[i] = 1; + + _width = -1; + int n = NumHandlers() + 1; + _info = d4_new c4_SortInfo [n]; + + int j; + + for (j = 0; j < NumHandlers(); ++j) + { + _info[j]._handler = & _seq.NthHandler(j); + _info[j]._context = _seq.HandlerContext(j); + } + + _info[j]._handler = 0; + + // everything is ready, go sort the row index vector + MergeSort((T*) &_rowMap.ElementAt(0), NumRows()); + + delete [] _info; + _info = 0; + + FixupReverseMap(); + } +} + +c4_SortSeq::~c4_SortSeq () +{ + d4_assert(!_info); +} + +int c4_SortSeq::Compare(int index_, c4_Cursor cursor_) const +{ + d4_assert(cursor_._seq != 0); + + const char* down = (const char*) _down.Contents(); + d4_assert(_down.Size() <= NumHandlers()); + + c4_Bytes data; + + for (int colNum = 0; colNum < NumHandlers(); ++colNum) + { + c4_Handler& h = NthHandler(colNum); + const c4_Sequence* hc = HandlerContext(colNum); + + if (!cursor_._seq->Get(cursor_._index, h.PropId(), data)) + h.ClearBytes(data); + + int f = h.Compare(RemapIndex(index_, hc), data); + if (f != 0) + return colNum < _down.Size() && down[colNum] ? -f : +f; + } + + return 0; +} + +int c4_SortSeq::PosInMap(c4_Cursor cursor_) const +{ + int i = 0; + + while (i < NumRows()) + if (Compare(i, cursor_) >= 0) + break; + else + ++i; + + d4_assert(i == NumRows() || Compare(i, cursor_) >= 0); + return i; +} + +c4_Notifier* c4_SortSeq::PreChange(c4_Notifier& /*nf_*/) +{ + if (!GetDependencies()) + return 0; + +#if 0 + c4_Notifier* chg = d4_new c4_Notifier (this); + + switch (nf_._type) + { + case c4_Notifier::kSetAt: + case c4_Notifier::kSet: + { + d4_assert(0); // also needs nested propagation + + /* + change can require a move *and* a change of contents + */ + } + break; + + case c4_Notifier::kInsertAt: + { + d4_assert(0); // this case isn't really difficult + } + break; + + case c4_Notifier::kRemoveAt: + { + d4_assert(0); // nested propagation is too difficult for now + // i.e. can only use sort as last derived view + /* + possible solution: + + if 1 row, simple + else if contig in map, also simple + else propagate reorder first, then delete contig + + it can be done here, as multiple notifications, + by simulating n-1 SetAt's of first row in others + needs some map juggling, allow temp dup entries? + + or perhaps more consistent with n separate removes + */ + } + break; + + case c4_Notifier::kMove: + { + // incorrect: may need to move if recnum matters (recs same) + } + break; + } + + return chg; +#endif + +// d4_assert(0); // fail, cannot handle a view dependent on this one yet + return 0; +} + +void c4_SortSeq::PostChange(c4_Notifier& nf_) +{ + switch (nf_._type) + { + case c4_Notifier::kSet: + if (_seq.PropIndex(nf_._propId) > _width) + break; // cannot affect sort order, valuable optimization + + case c4_Notifier::kSetAt: + { + int oi = (int) _revMap.GetAt(nf_._index); + d4_assert(oi >= 0); + + c4_Cursor cursor (_seq, nf_._index); + + // move the entry if the sort order has been disrupted + if ((oi > 0 && Compare(oi - 1, cursor) > 0) || + (oi+1 < NumRows() && Compare(oi+1, cursor) < 0)) + { + _rowMap.RemoveAt(oi); + _rowMap.InsertAt(PosInMap(cursor), nf_._index); + + FixupReverseMap(); + } + + _width = NumHandlers(); // sorry, no more optimization + } + break; + + case c4_Notifier::kInsertAt: + { + // if cursor was not set, it started out as a single Set + c4_Cursor cursor (_seq, nf_._index); + if (nf_._cursor) + cursor = *nf_._cursor; + + for (int n = 0; n < NumRows(); ++n) + if ((int) _rowMap.GetAt(n) >= nf_._index) + _rowMap.ElementAt(n) += nf_._count; + + int i = PosInMap(cursor); + _rowMap.InsertAt(i, 0, nf_._count); + + for (int j = 0; j < nf_._count; ++j) + _rowMap.SetAt(i++, nf_._index + j); + + FixupReverseMap(); + + _width = NumHandlers(); // sorry, no more optimization + } + break; + + case c4_Notifier::kRemoveAt: + { + int lo = nf_._index; + int hi = nf_._index + nf_._count; + + int j = 0; + for (int i = 0; i < NumRows(); ++i) + { + int n = (int) _rowMap.GetAt(i); + + if (n >= hi) + _rowMap.ElementAt(i) -= nf_._count; + + if (!(lo <= n && n < hi)) + _rowMap.SetAt(j++, _rowMap.GetAt(i)); + } + + d4_assert(j + nf_._count == NumRows()); + _rowMap.SetSize(j); + + FixupReverseMap(); + + _width = NumHandlers(); // sorry, no more optimization + } + break; + + case c4_Notifier::kMove: + { + // incorrect: may need to move if recnum matters (recs same) + } + break; + } +} + +///////////////////////////////////////////////////////////////////////////// + +class c4_ProjectSeq : public c4_DerivedSeq +{ + c4_DWordArray _colMap; // a bit large, but bytes would be too small + bool _frozen; + int _omitCount; // if > 0 then this is a dynamic "project without" + +public: + c4_ProjectSeq (c4_Sequence& seq_, c4_Sequence& in_, bool, c4_Sequence* out_); + virtual ~c4_ProjectSeq (); + + virtual int NumHandlers() const; + virtual c4_Handler& NthHandler(int) const; + virtual const c4_Sequence* HandlerContext(int) const; + virtual int AddHandler(c4_Handler*); + + virtual bool Get(int, int, c4_Bytes&); + virtual void Set(int, const c4_Property&, const c4_Bytes&); +}; + +///////////////////////////////////////////////////////////////////////////// + +c4_ProjectSeq::c4_ProjectSeq (c4_Sequence& seq_, c4_Sequence& in_, + bool reorder_, c4_Sequence* out_) + : c4_DerivedSeq (seq_), _frozen (!reorder_ && !out_), _omitCount (0) +{ + // build the array with column indexes + for (int j = 0; j < in_.NumHandlers(); ++j) + { + int propId = in_.NthPropId(j); + int idx = _seq.PropIndex(propId); + + // if the j'th property is in the sequence, add it + if (idx >= 0) + { + // but only if it's not in the out_ view + if (out_ && out_->PropIndex(propId) >= 0) + ++_omitCount; + else + _colMap.Add(idx); + } + } + + // if only reordering, append remaining columns from original view + if (reorder_) + { + for (int i = 0; i < _seq.NumHandlers(); ++i) + { + int propId = _seq.NthPropId(i); + + // only consider properties we did not deal with before + if (in_.PropIndex(propId) < 0) + _colMap.Add(i); + } + + d4_assert(_colMap.GetSize() == _seq.NumHandlers()); + } +} + +c4_ProjectSeq::~c4_ProjectSeq () +{ +} + +int c4_ProjectSeq::NumHandlers() const +{ + return _frozen ? _colMap.GetSize() : _seq.NumHandlers() - _omitCount; +} + +c4_Handler& c4_ProjectSeq::NthHandler(int colNum_) const +{ + int n = colNum_ < _colMap.GetSize() ? _colMap.GetAt(colNum_) : colNum_; + return _seq.NthHandler(n); +} + +const c4_Sequence* c4_ProjectSeq::HandlerContext(int colNum_) const +{ + int n = colNum_ < _colMap.GetSize() ? _colMap.GetAt(colNum_) : colNum_; + return _seq.HandlerContext(n); +} + +int c4_ProjectSeq::AddHandler(c4_Handler* handler_) +{ + int n = _seq.AddHandler(handler_); + return _frozen ? _colMap.Add(n) : n - _omitCount; +} + +bool c4_ProjectSeq::Get(int index_, int propId_, c4_Bytes& buf_) +{ + // fixed in 1.8: check that the property is visible + return PropIndex(propId_) >= 0 && _seq.Get(index_, propId_, buf_); +} + +void c4_ProjectSeq::Set(int index_, const c4_Property& prop_, const c4_Bytes& bytes_) +{ + int n = _seq.NumHandlers(); + _seq.Set(index_, prop_, bytes_); + + // if the number of handlers changed, then one must have been added + if (n != _seq.NumHandlers()) + { + d4_assert(n == _seq.NumHandlers() - 1); + + if (_frozen) + _colMap.Add(n); + } +} + +///////////////////////////////////////////////////////////////////////////// + +c4_Sequence* f4_CreateFilter(c4_Sequence& seq_, c4_Cursor l_, c4_Cursor h_) +{ + return d4_new c4_FilterSeq (seq_, l_, h_); +} + +c4_Sequence* f4_CreateSort(c4_Sequence& seq_, c4_Sequence* down_) +{ + return d4_new c4_SortSeq (seq_, down_); +} + +c4_Sequence* f4_CreateProject(c4_Sequence& seq_, c4_Sequence& in_, + bool reorder_, c4_Sequence* out_) +{ + return d4_new c4_ProjectSeq (seq_, in_, reorder_, out_); +} + +///////////////////////////////////////////////////////////////////////////// |