summaryrefslogtreecommitdiffstats
path: root/akregator/src/mk4storage/metakit/src/custom.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'akregator/src/mk4storage/metakit/src/custom.cpp')
-rw-r--r--akregator/src/mk4storage/metakit/src/custom.cpp1066
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_);
+}
+
+/////////////////////////////////////////////////////////////////////////////