diff options
Diffstat (limited to 'akregator/src/mk4storage/metakit/src/custom.cpp')
-rw-r--r-- | akregator/src/mk4storage/metakit/src/custom.cpp | 1066 |
1 files changed, 1066 insertions, 0 deletions
diff --git a/akregator/src/mk4storage/metakit/src/custom.cpp b/akregator/src/mk4storage/metakit/src/custom.cpp new file mode 100644 index 00000000..a6275cea --- /dev/null +++ b/akregator/src/mk4storage/metakit/src/custom.cpp @@ -0,0 +1,1066 @@ +// custom.cpp -- +// $Id$ +// This is part of Metakit, see http://www.equi4.com/metakit/ + +/** @file + * Implementation of many custom viewer classes + */ + +#include "header.h" + +#include "custom.h" +#include "format.h" + +///////////////////////////////////////////////////////////////////////////// + +class c4_CustomHandler : public c4_Handler +{ + c4_CustomSeq* _seq; + +public: + c4_CustomHandler (const c4_Property& prop_, c4_CustomSeq* seq_); + virtual ~c4_CustomHandler (); + + virtual int ItemSize(int index_); + virtual const void* Get(int index_, int& length_); + virtual void Set(int index_, const c4_Bytes& buf_); + + virtual void Insert(int index_, const c4_Bytes& buf_, int count_); + virtual void Remove(int index_, int count_); +}; + +///////////////////////////////////////////////////////////////////////////// + +c4_CustomHandler::c4_CustomHandler (const c4_Property& prop_, + c4_CustomSeq* seq_) + : c4_Handler (prop_), _seq (seq_) +{ + d4_assert(_seq != 0); +} + +c4_CustomHandler::~c4_CustomHandler () +{ +} + +int c4_CustomHandler::ItemSize(int index_) +{ + c4_Bytes& buf = _seq->Buffer(); + + int colnum = _seq->PropIndex(Property().GetId()); + d4_assert(colnum >= 0); + + if (!_seq->DoGet(index_, colnum, buf)) + return 0; + + return buf.Size(); +} + +const void* c4_CustomHandler::Get(int index_, int& length_) +{ + c4_Bytes& buf = _seq->Buffer(); + + int colnum = _seq->PropIndex(Property().GetId()); + d4_assert(colnum >= 0); + + if (!_seq->DoGet(index_, colnum, buf)) + ClearBytes(buf); + + length_ = buf.Size(); + return buf.Contents(); +} + +void c4_CustomHandler::Set(int index_, const c4_Bytes& buf_) +{ + int colnum = _seq->PropIndex(Property().GetId()); + d4_assert(colnum >= 0); + + _seq->DoSet(index_, colnum, buf_); +} + +void c4_CustomHandler::Insert(int, const c4_Bytes&, int) +{ + d4_assert(0); //! not yet +} + +void c4_CustomHandler::Remove(int, int) +{ + d4_assert(0); //! not yet +} + +c4_Handler* c4_CustomSeq::CreateHandler(const c4_Property& prop_) +{ + return d4_new c4_CustomHandler (prop_, this); +} + +///////////////////////////////////////////////////////////////////////////// + +c4_CustomSeq::c4_CustomSeq (c4_CustomViewer* viewer_) + : c4_HandlerSeq (0), _viewer (viewer_), _inited (false) +{ + d4_assert(_viewer != 0); + + // set up handlers to match a template obtained from the viewer + c4_View v = viewer_->GetTemplate(); + + for (int i = 0; i < v.NumProperties(); ++i) + PropIndex(v.NthProperty(i)); + + _inited = true; +} + +c4_CustomSeq::~c4_CustomSeq () +{ + delete _viewer; +} + +int c4_CustomSeq::NumRows() const +{ + return _inited ? _viewer->GetSize() : 0; +} + +bool c4_CustomSeq::RestrictSearch(c4_Cursor cursor_, int& pos_, int& count_) +{ + if (count_ > 0) + { + int n; + int o = _viewer->Lookup(cursor_, n); + // a -1 result means: "don't know, please scan all" + if (o < 0) + return count_ > 0; + + if (n > 0) + { + if (pos_ < o) + { + count_ -= o - pos_; + pos_ = o; + } + + if (pos_ + count_ > o + n) + count_ = o + n - pos_; + + if (count_ > 0) + return true; + } + } + + count_ = 0; + return false; +} + +void c4_CustomSeq::InsertAt(int p_, c4_Cursor c_, int n_) +{ + _viewer->InsertRows(p_, c_, n_); +} + +void c4_CustomSeq::RemoveAt(int p_, int n_) +{ + _viewer->RemoveRows(p_, n_); +} + +void c4_CustomSeq::Move(int, int) +{ + d4_assert(false); //! not yet +} + +bool c4_CustomSeq::DoGet(int row_, int col_, c4_Bytes& buf_) const +{ + d4_assert(_inited); + + return _viewer->GetItem(row_, col_, buf_); +} + +void c4_CustomSeq::DoSet(int row_, int col_, const c4_Bytes& buf_) +{ + d4_assert(_inited); + + d4_dbgdef(const bool f =) + _viewer->SetItem(row_, col_, buf_); + d4_assert(f); +} + +///////////////////////////////////////////////////////////////////////////// + +/** @class c4_CustomViewer + * + * Abstract base class for definition of custom views. + * + * A custom view is a view which can be accessed like any other view, using + * row and property operations, but which is fully managed by a customized + * "viewer" class. The viewer will eventually handle all requests for the + * view, such as defining its structure and size, as well as providing the + * actual data values when requested. + * + * Custom views cannot propagate changes. + * + * To implement a custom view, you must derive your viewer from this base + * class and define each of the virtual members. Then create a new object + * of this type on the heap and pass it to the c4_View constructor. Your + * viewer will automatically be destroyed when the last reference to its + * view goes away. See the DBF2MK sample code for an example of a viewer. + */ + +c4_CustomViewer::~c4_CustomViewer () +{ +} + + /// Locate a row in this view, try to use native searches +int c4_CustomViewer::Lookup(c4_Cursor, int& count_) +{ + count_ = GetSize(); + return 0; // not implemented, return entire view range +} + + /// Store one data item, supplied as a generic data value +bool c4_CustomViewer::SetItem(int, int, const c4_Bytes&) +{ + return false; // default is not modifiable +} + + /// Insert one or more copies of a row (if possible) +bool c4_CustomViewer::InsertRows(int, c4_Cursor, int) +{ + return false; // default is not modifiable +} + + /// Remove one or more rows (this is not always possible) +bool c4_CustomViewer::RemoveRows(int, int) +{ + return false; // default is not modifiable +} + +///////////////////////////////////////////////////////////////////////////// + +class c4_SliceViewer : public c4_CustomViewer +{ + c4_View _parent; + int _first, _limit, _step; + +public: + c4_SliceViewer (c4_Sequence& seq_, int first_, int limit_, int step_); + virtual ~c4_SliceViewer (); + + virtual c4_View GetTemplate(); + virtual int GetSize(); + virtual bool GetItem(int row_, int col_, c4_Bytes& buf_); + bool SetItem(int row_, int col_, const c4_Bytes& buf_); + virtual bool InsertRows(int pos_, c4_Cursor value_, int count_=1); + virtual bool RemoveRows(int pos_, int count_=1); +}; + +c4_SliceViewer::c4_SliceViewer (c4_Sequence& seq_, int first_, int limit_, int step_) + : _parent (&seq_), _first (first_), _limit (limit_), _step (step_) +{ + d4_assert(_step != 0); +} + +c4_SliceViewer::~c4_SliceViewer () +{ +} + +c4_View c4_SliceViewer::GetTemplate() +{ + return _parent.Clone(); // could probably return _parent just as well +} + +int c4_SliceViewer::GetSize() +{ + int n = _limit >= 0 ? _limit : _parent.GetSize(); + if (n < _first) + n = _first; + + int k = _step < 0 ? -_step : _step; + return (n - _first + k - 1) / k; +} + +bool c4_SliceViewer::GetItem(int row_, int col_, c4_Bytes& buf_) +{ + row_ = _first + _step * (_step > 0 ? row_ : row_ - GetSize() + 1); + + return _parent.GetItem(row_, col_, buf_); +} + +bool c4_SliceViewer::SetItem(int row_, int col_, const c4_Bytes& buf_) +{ + row_ = _first + _step * (_step > 0 ? row_ : row_ - GetSize() + 1); + + _parent.SetItem(row_, col_, buf_); + return true; +} + +bool c4_SliceViewer::InsertRows(int pos_, c4_Cursor value_, int count_) +{ + if (_step != 1) + return false; + + pos_ = _first + _step * (_step > 0 ? pos_ : pos_ - GetSize() + 1); + if (_limit >= 0) + _limit += count_; + + _parent.InsertAt(pos_, *value_, count_); + return true; +} + +bool c4_SliceViewer::RemoveRows(int pos_, int count_) +{ + if (_step != 1) + return false; + + pos_ = _first + _step * (_step > 0 ? pos_ : pos_ - GetSize() + 1); + if (_limit >= 0) + _limit -= count_; + + _parent.RemoveAt(pos_, count_); + return true; +} + +c4_CustomViewer* f4_CustSlice(c4_Sequence& seq_, int first_, int limit_, int step_) +{ + return d4_new c4_SliceViewer (seq_, first_, limit_, step_); +} + +///////////////////////////////////////////////////////////////////////////// + +class c4_ProductViewer : public c4_CustomViewer +{ + c4_View _parent, _argView, _template; + +public: + c4_ProductViewer (c4_Sequence& seq_, const c4_View& view_); + virtual ~c4_ProductViewer (); + + virtual c4_View GetTemplate(); + virtual int GetSize(); + virtual bool GetItem(int row_, int col_, c4_Bytes& buf_); +}; + +c4_ProductViewer::c4_ProductViewer (c4_Sequence& seq_, const c4_View& view_) + : _parent (&seq_), _argView (view_), _template (_parent.Clone()) +{ + for (int i = 0; i < _argView.NumProperties(); ++i) + _template.AddProperty(_argView.NthProperty(i)); +} + +c4_ProductViewer::~c4_ProductViewer () +{ +} + +c4_View c4_ProductViewer::GetTemplate() +{ + return _template; +} + +int c4_ProductViewer::GetSize() +{ + return _parent.GetSize() * _argView.GetSize(); +} + +bool c4_ProductViewer::GetItem(int row_, int col_, c4_Bytes& buf_) +{ + c4_View v = _parent; + + if (col_ < v.NumProperties()) + { + row_ /= _argView.GetSize(); + } + else + { + v = _argView; + row_ %= _argView.GetSize(); + col_ = v.FindProperty(_template.NthProperty(col_).GetId()); + + d4_assert(col_ >= 0); + } + + return v.GetItem(row_, col_, buf_); +} + +c4_CustomViewer* f4_CustProduct(c4_Sequence& seq_, const c4_View& view_) +{ + return d4_new c4_ProductViewer (seq_, view_); +} + +///////////////////////////////////////////////////////////////////////////// + +class c4_RemapWithViewer : public c4_CustomViewer +{ + c4_View _parent, _argView; + +public: + c4_RemapWithViewer (c4_Sequence& seq_, const c4_View& view_); + virtual ~c4_RemapWithViewer (); + + virtual c4_View GetTemplate(); + virtual int GetSize(); + virtual bool GetItem(int row_, int col_, c4_Bytes& buf_); + bool SetItem(int row_, int col_, const c4_Bytes& buf_); +}; + +c4_RemapWithViewer::c4_RemapWithViewer (c4_Sequence& seq_, const c4_View& view_) + : _parent (&seq_), _argView (view_) +{ +} + +c4_RemapWithViewer::~c4_RemapWithViewer () +{ +} + +c4_View c4_RemapWithViewer::GetTemplate() +{ + return _parent.Clone(); // could probably return _parent just as well +} + +int c4_RemapWithViewer::GetSize() +{ + return _argView.GetSize(); +} + +bool c4_RemapWithViewer::GetItem(int row_, int col_, c4_Bytes& buf_) +{ + const c4_Property& map = _argView.NthProperty(0); + d4_assert(map.Type() == 'I'); + + row_ = ((const c4_IntProp&) map) (_argView[row_]); + + return _parent.GetItem(row_, col_, buf_); +} + +bool c4_RemapWithViewer::SetItem(int row_, int col_, const c4_Bytes& buf_) +{ + const c4_Property& map = _argView.NthProperty(0); + d4_assert(map.Type() == 'I'); + + row_ = ((const c4_IntProp&) map) (_argView[row_]); + + _parent.SetItem(row_, col_, buf_); + return true; +} + +c4_CustomViewer* f4_CustRemapWith(c4_Sequence& seq_, const c4_View& view_) +{ + return d4_new c4_RemapWithViewer (seq_, view_); +} + +///////////////////////////////////////////////////////////////////////////// + +class c4_PairViewer : public c4_CustomViewer +{ + c4_View _parent, _argView, _template; + +public: + c4_PairViewer (c4_Sequence& seq_, const c4_View& view_); + virtual ~c4_PairViewer (); + + virtual c4_View GetTemplate(); + virtual int GetSize(); + virtual bool GetItem(int row_, int col_, c4_Bytes& buf_); + bool SetItem(int row_, int col_, const c4_Bytes& buf_); + virtual bool InsertRows(int pos_, c4_Cursor value_, int count_=1); + virtual bool RemoveRows(int pos_, int count_=1); +}; + +c4_PairViewer::c4_PairViewer (c4_Sequence& seq_, const c4_View& view_) + : _parent (&seq_), _argView (view_), _template (_parent.Clone()) +{ + for (int i = 0; i < _argView.NumProperties(); ++i) + _template.AddProperty(_argView.NthProperty(i)); +} + +c4_PairViewer::~c4_PairViewer () +{ +} + +c4_View c4_PairViewer::GetTemplate() +{ + return _template; +} + +int c4_PairViewer::GetSize() +{ + return _parent.GetSize(); +} + +bool c4_PairViewer::GetItem(int row_, int col_, c4_Bytes& buf_) +{ + c4_View v = _parent; + + if (col_ >= v.NumProperties()) + { + v = _argView; + col_ = v.FindProperty(_template.NthProperty(col_).GetId()); + d4_assert(col_ >= 0); + } + + return v.GetItem(row_, col_, buf_); +} + +bool c4_PairViewer::SetItem(int row_, int col_, const c4_Bytes& buf_) +{ + c4_View v = _parent; + + if (col_ >= v.NumProperties()) + { + v = _argView; + col_ = v.FindProperty(_template.NthProperty(col_).GetId()); + d4_assert(col_ >= 0); + } + + v.SetItem(row_, col_, buf_); + return true; +} + +bool c4_PairViewer::InsertRows(int pos_, c4_Cursor value_, int count_) +{ + _parent.InsertAt(pos_, *value_, count_); + _argView.InsertAt(pos_, *value_, count_); + return true; +} + +bool c4_PairViewer::RemoveRows(int pos_, int count_) +{ + _parent.RemoveAt(pos_, count_); + _argView.RemoveAt(pos_, count_); + return true; +} + +c4_CustomViewer* f4_CustPair(c4_Sequence& seq_, const c4_View& view_) +{ + return d4_new c4_PairViewer (seq_, view_); +} + +///////////////////////////////////////////////////////////////////////////// + +class c4_ConcatViewer : public c4_CustomViewer +{ + c4_View _parent, _argView; + +public: + c4_ConcatViewer (c4_Sequence& seq_, const c4_View& view_); + virtual ~c4_ConcatViewer (); + + virtual c4_View GetTemplate(); + virtual int GetSize(); + virtual bool GetItem(int row_, int col_, c4_Bytes& buf_); + bool SetItem(int row_, int col_, const c4_Bytes& buf_); +}; + +c4_ConcatViewer::c4_ConcatViewer (c4_Sequence& seq_, const c4_View& view_) + : _parent (&seq_), _argView (view_) +{ +} + +c4_ConcatViewer::~c4_ConcatViewer () +{ +} + +c4_View c4_ConcatViewer::GetTemplate() +{ + return _parent.Clone(); // could probably return _parent just as well +} + +int c4_ConcatViewer::GetSize() +{ + return _parent.GetSize() + _argView.GetSize(); +} + +bool c4_ConcatViewer::GetItem(int row_, int col_, c4_Bytes& buf_) +{ + c4_View v = _parent; + + if (row_ >= _parent.GetSize()) + { + v = _argView; + row_ -= _parent.GetSize(); + col_ = v.FindProperty(_parent.NthProperty(col_).GetId()); + + if (col_ < 0) + return false; + } + + return v.GetItem(row_, col_, buf_); +} + +bool c4_ConcatViewer::SetItem(int row_, int col_, const c4_Bytes& buf_) +{ + c4_View v = _parent; + + if (row_ >= _parent.GetSize()) + { + v = _argView; + row_ -= _parent.GetSize(); + col_ = v.FindProperty(_parent.NthProperty(col_).GetId()); + d4_assert(col_ >= 0); + } + + v.SetItem(row_, col_, buf_); + return true; +} + +c4_CustomViewer* f4_CustConcat(c4_Sequence& seq_, const c4_View& view_) +{ + return d4_new c4_ConcatViewer (seq_, view_); +} + +///////////////////////////////////////////////////////////////////////////// + +class c4_RenameViewer : public c4_CustomViewer +{ + c4_View _parent, _template; + +public: + c4_RenameViewer (c4_Sequence& seq_, const c4_Property& old_, + const c4_Property& new_); + virtual ~c4_RenameViewer (); + + virtual c4_View GetTemplate(); + virtual int GetSize(); + virtual bool GetItem(int row_, int col_, c4_Bytes& buf_); + virtual bool SetItem(int row_, int col_, const c4_Bytes& buf_); + //virtual bool InsertRows(int pos_, c4_Cursor value_, int count_=1); + //virtual bool RemoveRows(int pos_, int count_=1); +}; + +c4_RenameViewer::c4_RenameViewer (c4_Sequence& seq_, const c4_Property& old_, + const c4_Property& new_) + : _parent (&seq_) +{ + for (int i = 0; i < _parent.NumProperties(); ++i) + { + const c4_Property& prop = _parent.NthProperty(i); + _template.AddProperty(prop.GetId() == old_.GetId() ? new_ : prop); + } +} + +c4_RenameViewer::~c4_RenameViewer () +{ +} + +c4_View c4_RenameViewer::GetTemplate() +{ + return _template; +} + +int c4_RenameViewer::GetSize() +{ + return _parent.GetSize(); +} + +bool c4_RenameViewer::GetItem(int row_, int col_, c4_Bytes& buf_) +{ + return _parent.GetItem(row_, col_, buf_); +} + +bool c4_RenameViewer::SetItem(int row_, int col_, const c4_Bytes& buf_) +{ + _parent.SetItem(row_, col_, buf_); + return true; +} + +c4_CustomViewer* f4_CustRename(c4_Sequence& seq_, const c4_Property& old_, + const c4_Property& new_) +{ + return d4_new c4_RenameViewer (seq_, old_, new_); +} + +///////////////////////////////////////////////////////////////////////////// + +class c4_GroupByViewer : public c4_CustomViewer +{ + c4_View _parent, _keys, _sorted, _temp; + c4_Property _result; + c4_DWordArray _map; + + int ScanTransitions(int lo_, int hi_, + t4_byte* flags_, const c4_View& match_) const; + +public: + c4_GroupByViewer (c4_Sequence& seq_, const c4_View& keys_, + const c4_Property& result_); + virtual ~c4_GroupByViewer (); + + virtual c4_View GetTemplate(); + virtual int GetSize(); + virtual bool GetItem(int row_, int col_, c4_Bytes& buf_); +}; + +c4_GroupByViewer::c4_GroupByViewer (c4_Sequence& seq_, const c4_View& keys_, + const c4_Property& result_) + : _parent (&seq_), _keys (keys_), _result (result_) +{ + _sorted = _parent.SortOn(_keys); + int n = _sorted.GetSize(); + + c4_Bytes temp; + t4_byte* buf = temp.SetBufferClear(n); + + int groups = 0; + if (n > 0) + { + ++buf[0]; // the first entry is always a transition + groups = 1 + ScanTransitions(1, n, buf, _sorted.Project(_keys)); + } + + // set up a map pointing to each transition + _map.SetSize(groups + 1); + int j = 0; + + for (int i = 0; i < n; ++i) + if (buf[i]) + _map.SetAt(j++, i); + + // also append an entry to point just past the end + _map.SetAt(j, n); + + d4_assert(_map.GetAt(0) == 0); + d4_assert(j == groups); +} + +c4_GroupByViewer::~c4_GroupByViewer () +{ +} + +int c4_GroupByViewer::ScanTransitions(int lo_, int hi_, + t4_byte* flags_, const c4_View& match_) const +{ + d4_assert(lo_ > 0); + + int m = hi_ - lo_; + d4_assert(m >= 0); + + // done if nothing left or if entire range is identical + if (m == 0 || match_ [lo_-1] == match_ [hi_-1]) + return 0; + + // range has a transition, done if it is exactly of size one + if (m == 1) + { + ++(flags_[lo_]); + return 1; + } + + // use binary splitting if the range has enough entries + if (m >= 5) + return ScanTransitions(lo_, lo_ + m / 2, flags_, match_) + + ScanTransitions(lo_ + m / 2, hi_, flags_, match_); + + // else use a normal linear scan + int n = 0; + + for (int i = lo_; i < hi_; ++i) + if (match_ [i] != match_ [i-1]) + { + ++(flags_[i]); + ++n; + } + + return n; +} + +c4_View c4_GroupByViewer::GetTemplate() +{ + c4_View v = _keys.Clone(); + v.AddProperty(_result); + + return v; +} + +int c4_GroupByViewer::GetSize() +{ + d4_assert(_map.GetSize() > 0); + + return _map.GetSize() - 1; +} + +bool c4_GroupByViewer::GetItem(int row_, int col_, c4_Bytes& buf_) +{ + if (col_ < _keys.NumProperties()) + return _sorted.GetItem(_map.GetAt(row_), col_, buf_); + + d4_assert(col_ == _keys.NumProperties()); + + t4_i32 count; + switch (_result.Type()) + { + case 'I': count = _map.GetAt(row_ + 1) - _map.GetAt(row_); + buf_ = c4_Bytes (&count, sizeof count, true); + break; + case 'V': _temp = _sorted.Slice(_map.GetAt(row_), _map.GetAt(row_ + 1)) + .ProjectWithout(_keys); + buf_ = c4_Bytes (&_temp, sizeof _temp, true); + break; + default: d4_assert(0); + } + + return true; +} + +c4_CustomViewer* f4_CustGroupBy(c4_Sequence& seq_, const c4_View& template_, + const c4_Property& result_) +{ + return d4_new c4_GroupByViewer (seq_, template_, result_); +} + +///////////////////////////////////////////////////////////////////////////// + +class c4_JoinPropViewer : public c4_CustomViewer +{ + c4_View _parent, _template; + c4_ViewProp _sub; + int _subPos, _subWidth; + c4_DWordArray _base, _offset; + +public: + c4_JoinPropViewer (c4_Sequence& seq_, const c4_ViewProp& sub_, bool outer_); + virtual ~c4_JoinPropViewer (); + + virtual c4_View GetTemplate(); + virtual int GetSize(); + virtual bool GetItem(int row_, int col_, c4_Bytes& buf_); +}; + +c4_JoinPropViewer::c4_JoinPropViewer (c4_Sequence& seq_, + const c4_ViewProp& sub_, bool outer_) + : _parent (&seq_), + _sub (sub_), _subPos (_parent.FindProperty(sub_.GetId())), _subWidth (0) +{ + d4_assert(_subPos >= 0); + + for (int k = 0; k < _parent.NumProperties(); ++k) + { + if (k != _subPos) + _template.AddProperty(_parent.NthProperty(k)); + else // if there are no rows, then this join does very little anyway + //! OOPS: if this is an unattached view, then the subviews can differ + if (_parent.GetSize() > 0) + { + c4_View view = sub_ (_parent[0]); + for (int l = 0; l < view.NumProperties(); ++l) + { + _template.AddProperty(view.NthProperty(l)); + ++_subWidth; + } + } + } + + _base.SetSize(0, 5); + _offset.SetSize(0, 5); + + for (int i = 0; i < _parent.GetSize(); ++i) + { + c4_View v = _sub (_parent[i]); + + int n = v.GetSize(); + if (n == 0 && outer_) + { + _base.Add(i); + _offset.Add(~ (t4_i32) 0); // special null entry for outer joins + } + else + for (int j = 0; j < n; ++j) + { + _base.Add(i); + _offset.Add(j); + } + } +} + +c4_JoinPropViewer::~c4_JoinPropViewer () +{ +} + +c4_View c4_JoinPropViewer::GetTemplate() +{ + return _template; +} + +int c4_JoinPropViewer::GetSize() +{ + return _base.GetSize(); +} + +bool c4_JoinPropViewer::GetItem(int row_, int col_, c4_Bytes& buf_) +{ + c4_View v = _parent; + int r = _base.GetAt(row_); + + if (col_ >= _subPos) + if (col_ >= _subPos + _subWidth) + { + col_ -= _subWidth - 1; + } + else + { + v = _sub (_parent[r]); + r = _offset.GetAt(row_); + if (r < 0) + return false; // if this is a null row in an outer join + + col_ = v.FindProperty(_template.NthProperty(col_).GetId()); + if (col_ < 0) + return false; // if subview doesn't have all properties + } + + return v.GetItem(r, col_, buf_); +} + +c4_CustomViewer* f4_CustJoinProp(c4_Sequence& seq_, + const c4_ViewProp& sub_, bool outer_) +{ + return d4_new c4_JoinPropViewer (seq_, sub_, outer_); +} + +///////////////////////////////////////////////////////////////////////////// + +class c4_JoinViewer : public c4_CustomViewer +{ + c4_View _parent, _argView, _template; + c4_DWordArray _base, _offset; + +public: + c4_JoinViewer (c4_Sequence& seq_, const c4_View& keys_, + const c4_View& view_, bool outer_); + virtual ~c4_JoinViewer (); + + virtual c4_View GetTemplate(); + virtual int GetSize(); + virtual bool GetItem(int row_, int col_, c4_Bytes& buf_); +}; + +c4_JoinViewer::c4_JoinViewer (c4_Sequence& seq_,const c4_View& keys_, + const c4_View& view_, bool outer_) + : _parent (&seq_), _argView (view_.SortOn(keys_)) +{ + // why not in GetTemplate, since we don't need to know this... + _template = _parent.Clone(); + for (int l = 0; l < _argView.NumProperties(); ++l) + _template.AddProperty(_argView.NthProperty(l)); + + c4_View sorted = _parent.SortOn(keys_).Project(keys_); + c4_View temp = _argView.Project(keys_); + + _base.SetSize(0, 5); + _offset.SetSize(0, 5); + + int j = 0, n = 0; + + for (int i = 0; i < sorted.GetSize(); ++i) + { + int orig = _parent.GetIndexOf(sorted[i]); + d4_assert(orig >= 0); + + if (i > 0 && sorted[i] == sorted[i-1]) + { + // if last key was same, repeat the same join + int last = _offset.GetSize() - n; + for (int k = 0; k < n; ++k) + { + _base.Add(orig); + _offset.Add(_offset.GetAt(last + k)); + } + } + else // no, this is a new combination + { + bool match = false; + + // advance until the temp view entry is >= this sorted entry + while (j < temp.GetSize()) + if (sorted[i] <= temp[j]) + { + match = sorted[i] == temp[j]; + break; + } + else + ++j; + + n = 0; + + if (match) + { + do { + _base.Add(orig); + _offset.Add(j); + ++n; + } while (++j < temp.GetSize() && temp[j] == temp[j-1]); + } + else if (outer_) + { + // no match, add an entry anyway if this is an outer join + _base.Add(orig); + _offset.Add(~ (t4_i32) 0); // special null entry + ++n; + } + } + } +} + +c4_JoinViewer::~c4_JoinViewer () +{ +} + +c4_View c4_JoinViewer::GetTemplate() +{ + return _template; +} + +int c4_JoinViewer::GetSize() +{ + return _base.GetSize(); +} + +bool c4_JoinViewer::GetItem(int row_, int col_, c4_Bytes& buf_) +{ + c4_View v = _parent; + int r = _base.GetAt(row_); + + if (col_ >= v.NumProperties()) + { + v = _argView; + r = _offset.GetAt(row_); + if (r < 0) + return false; // if this is a null row in an outer join + + col_ = v.FindProperty(_template.NthProperty(col_).GetId()); + if (col_ < 0) + return false; // if second view doesn't have all properties + } + + return v.GetItem(r, col_, buf_); +} + +#if 0 +bool c4_JoinViewer::GetItem(int row_, int col_, c4_Bytes& buf_) +{ + c4_View v = _parent; + + int o = 0; + int r = _offset.GetAt(row_); + + if (r < 0) + { + o = ~r; + if (o == 0) + return false; // if this is a null row in an outer join + r -= o; + } + + if (col_ >= v.NumProperties()) + { + v = _argView; + r = _o; + + col_ = v.FindProperty(_template.NthProperty(col_)); + if (col_ < 0) + return false; // if second view doesn't have all properties + } + + return v.GetItem(r, col_, buf_); +} +#endif + +c4_CustomViewer* f4_CustJoin(c4_Sequence& seq_, const c4_View& keys_, + const c4_View& view_, bool outer_) +{ + return d4_new c4_JoinViewer (seq_, keys_, view_, outer_); +} + +///////////////////////////////////////////////////////////////////////////// |