1// custom.cpp --
2// $Id: custom.cpp 1230 2007-03-09 15:58:53Z jcw $
3// This is part of Metakit, see http://www.equi4.com/metakit.html
4
5/** @file
6 * Implementation of many custom viewer classes
7 */
8
9#include "header.h"
10
11#include "custom.h"
12#include "format.h"
13
14/////////////////////////////////////////////////////////////////////////////
15
16class c4_CustomHandler: public c4_Handler {
17    c4_CustomSeq *_seq;
18
19  public:
20    c4_CustomHandler(const c4_Property &prop_, c4_CustomSeq *seq_);
21    virtual ~c4_CustomHandler();
22
23    virtual int ItemSize(int index_);
24    virtual const void *Get(int index_, int &length_);
25    virtual void Set(int index_, const c4_Bytes &buf_);
26
27    virtual void Insert(int index_, const c4_Bytes &buf_, int count_);
28    virtual void Remove(int index_, int count_);
29};
30
31/////////////////////////////////////////////////////////////////////////////
32
33c4_CustomHandler::c4_CustomHandler(const c4_Property &prop_, c4_CustomSeq *seq_)
34  : c4_Handler(prop_), _seq(seq_) {
35  d4_assert(_seq != 0);
36}
37
38c4_CustomHandler::~c4_CustomHandler(){}
39
40int c4_CustomHandler::ItemSize(int index_) {
41  c4_Bytes &buf = _seq->Buffer();
42
43  int colnum = _seq->PropIndex(Property().GetId());
44  d4_assert(colnum >= 0);
45
46  if (!_seq->DoGet(index_, colnum, buf))
47    return 0;
48
49  return buf.Size();
50}
51
52const void *c4_CustomHandler::Get(int index_, int &length_) {
53  c4_Bytes &buf = _seq->Buffer();
54
55  int colnum = _seq->PropIndex(Property().GetId());
56  d4_assert(colnum >= 0);
57
58  if (!_seq->DoGet(index_, colnum, buf))
59    ClearBytes(buf);
60
61  length_ = buf.Size();
62  return buf.Contents();
63}
64
65void c4_CustomHandler::Set(int index_, const c4_Bytes &buf_) {
66  int colnum = _seq->PropIndex(Property().GetId());
67  d4_assert(colnum >= 0);
68
69  _seq->DoSet(index_, colnum, buf_);
70}
71
72void c4_CustomHandler::Insert(int, const c4_Bytes &, int) {
73  d4_assert(0); //! not yet
74}
75
76void c4_CustomHandler::Remove(int, int) {
77  d4_assert(0); //! not yet
78}
79
80c4_Handler *c4_CustomSeq::CreateHandler(const c4_Property &prop_) {
81  return d4_new c4_CustomHandler(prop_, this);
82}
83
84/////////////////////////////////////////////////////////////////////////////
85
86c4_CustomSeq::c4_CustomSeq(c4_CustomViewer *viewer_): c4_HandlerSeq(0), _viewer
87  (viewer_), _inited(false) {
88  d4_assert(_viewer != 0);
89
90  // set up handlers to match a template obtained from the viewer
91  c4_View v = viewer_->GetTemplate();
92
93  for (int i = 0; i < v.NumProperties(); ++i)
94    PropIndex(v.NthProperty(i));
95
96  _inited = true;
97}
98
99c4_CustomSeq::~c4_CustomSeq() {
100  delete _viewer;
101}
102
103int c4_CustomSeq::NumRows()const {
104  return _inited ? _viewer->GetSize(): 0;
105}
106
107bool c4_CustomSeq::RestrictSearch(c4_Cursor cursor_, int &pos_, int &count_) {
108  if (count_ > 0) {
109    int n;
110    int o = _viewer->Lookup(cursor_, n);
111    // a -1 result means: "don't know, please scan all"
112    if (o < 0)
113      return count_ > 0;
114
115    if (n > 0) {
116      if (pos_ < o) {
117        count_ -= o - pos_;
118        pos_ = o;
119      }
120
121      if (pos_ + count_ > o + n)
122        count_ = o + n - pos_;
123
124      if (count_ > 0)
125        return true;
126    }
127  }
128
129  count_ = 0;
130  return false;
131}
132
133void c4_CustomSeq::InsertAt(int p_, c4_Cursor c_, int n_) {
134  _viewer->InsertRows(p_, c_, n_);
135}
136
137void c4_CustomSeq::RemoveAt(int p_, int n_) {
138  _viewer->RemoveRows(p_, n_);
139}
140
141void c4_CustomSeq::Move(int, int) {
142  d4_assert(false); //! not yet
143}
144
145bool c4_CustomSeq::DoGet(int row_, int col_, c4_Bytes &buf_)const {
146  d4_assert(_inited);
147
148  return _viewer->GetItem(row_, col_, buf_);
149}
150
151void c4_CustomSeq::DoSet(int row_, int col_, const c4_Bytes &buf_) {
152  d4_assert(_inited);
153
154  d4_dbgdef(const bool f = )_viewer->SetItem(row_, col_, buf_);
155  d4_assert(f);
156}
157
158/////////////////////////////////////////////////////////////////////////////
159
160/** @class c4_CustomViewer
161 *
162 *  Abstract base class for definition of custom views.
163 *
164 *  A custom view is a view which can be accessed like any other view, using
165 *  row and property operations, but which is fully managed by a customized
166 *  "viewer" class.  The viewer will eventually handle all requests for the
167 *  view, such as defining its structure and size, as well as providing the
168 *  actual data values when requested.
169 *
170 *  Custom views cannot propagate changes.
171 *
172 *  To implement a custom view, you must derive your viewer from this base
173 *  class and define each of the virtual members.  Then create a new object
174 *  of this type on the heap and pass it to the c4_View constructor.  Your
175 *  viewer will automatically be destroyed when the last reference to its
176 *  view goes away.  See the DBF2MK sample code for an example of a viewer.
177 */
178
179c4_CustomViewer::~c4_CustomViewer(){}
180
181/// Locate a row in this view, try to use native searches
182int c4_CustomViewer::Lookup(c4_Cursor, int &count_) {
183  count_ = GetSize();
184  return 0; // not implemented, return entire view range
185}
186
187/// Store one data item, supplied as a generic data value
188bool c4_CustomViewer::SetItem(int, int, const c4_Bytes &) {
189  return false; // default is not modifiable
190}
191
192/// Insert one or more copies of a row (if possible)
193bool c4_CustomViewer::InsertRows(int, c4_Cursor, int) {
194  return false; // default is not modifiable
195}
196
197/// Remove one or more rows (this is not always possible)
198bool c4_CustomViewer::RemoveRows(int, int) {
199  return false; // default is not modifiable
200}
201
202/////////////////////////////////////////////////////////////////////////////
203
204class c4_SliceViewer: public c4_CustomViewer {
205    c4_View _parent;
206    int _first, _limit, _step;
207
208  public:
209    c4_SliceViewer(c4_Sequence &seq_, int first_, int limit_, int step_);
210    virtual ~c4_SliceViewer();
211
212    virtual c4_View GetTemplate();
213    virtual int GetSize();
214    virtual bool GetItem(int row_, int col_, c4_Bytes &buf_);
215    bool SetItem(int row_, int col_, const c4_Bytes &buf_);
216    virtual bool InsertRows(int pos_, c4_Cursor value_, int count_ = 1);
217    virtual bool RemoveRows(int pos_, int count_ = 1);
218};
219
220c4_SliceViewer::c4_SliceViewer(c4_Sequence &seq_, int first_, int limit_, int
221  step_): _parent(&seq_), _first(first_), _limit(limit_), _step(step_) {
222  d4_assert(_step != 0);
223}
224
225c4_SliceViewer::~c4_SliceViewer(){}
226
227c4_View c4_SliceViewer::GetTemplate() {
228  return _parent.Clone(); // could probably return _parent just as well
229}
230
231int c4_SliceViewer::GetSize() {
232  int n = _limit >= 0 ? _limit : _parent.GetSize();
233  if (n < _first)
234    n = _first;
235
236  int k = _step < 0 ?  - _step: _step;
237  return (n - _first + k - 1) / k;
238}
239
240bool c4_SliceViewer::GetItem(int row_, int col_, c4_Bytes &buf_) {
241  row_ = _first + _step *(_step > 0 ? row_ : row_ - GetSize() + 1);
242
243  return _parent.GetItem(row_, col_, buf_);
244}
245
246bool c4_SliceViewer::SetItem(int row_, int col_, const c4_Bytes &buf_) {
247  row_ = _first + _step *(_step > 0 ? row_ : row_ - GetSize() + 1);
248
249  _parent.SetItem(row_, col_, buf_);
250  return true;
251}
252
253bool c4_SliceViewer::InsertRows(int pos_, c4_Cursor value_, int count_) {
254  if (_step != 1)
255    return false;
256
257  pos_ = _first + _step *(_step > 0 ? pos_ : pos_ - GetSize() + 1);
258  if (_limit >= 0)
259    _limit += count_;
260
261  _parent.InsertAt(pos_,  *value_, count_);
262  return true;
263}
264
265bool c4_SliceViewer::RemoveRows(int pos_, int count_) {
266  if (_step != 1)
267    return false;
268
269  pos_ = _first + _step *(_step > 0 ? pos_ : pos_ - GetSize() + 1);
270  if (_limit >= 0)
271    _limit -= count_;
272
273  _parent.RemoveAt(pos_, count_);
274  return true;
275}
276
277c4_CustomViewer *f4_CustSlice(c4_Sequence &seq_, int first_, int limit_, int
278  step_) {
279  return d4_new c4_SliceViewer(seq_, first_, limit_, step_);
280}
281
282/////////////////////////////////////////////////////////////////////////////
283
284class c4_ProductViewer: public c4_CustomViewer {
285    c4_View _parent, _argView, _template;
286
287  public:
288    c4_ProductViewer(c4_Sequence &seq_, const c4_View &view_);
289    virtual ~c4_ProductViewer();
290
291    virtual c4_View GetTemplate();
292    virtual int GetSize();
293    virtual bool GetItem(int row_, int col_, c4_Bytes &buf_);
294};
295
296c4_ProductViewer::c4_ProductViewer(c4_Sequence &seq_, const c4_View &view_):
297  _parent(&seq_), _argView(view_), _template(_parent.Clone()) {
298  for (int i = 0; i < _argView.NumProperties(); ++i)
299    _template.AddProperty(_argView.NthProperty(i));
300}
301
302c4_ProductViewer::~c4_ProductViewer(){}
303
304c4_View c4_ProductViewer::GetTemplate() {
305  return _template;
306}
307
308int c4_ProductViewer::GetSize() {
309  return _parent.GetSize() *_argView.GetSize();
310}
311
312bool c4_ProductViewer::GetItem(int row_, int col_, c4_Bytes &buf_) {
313  c4_View v = _parent;
314
315  if (col_ < v.NumProperties()) {
316    row_ /= _argView.GetSize();
317  } else {
318    v = _argView;
319    row_ %= _argView.GetSize();
320    col_ = v.FindProperty(_template.NthProperty(col_).GetId());
321
322    d4_assert(col_ >= 0);
323  }
324
325  return v.GetItem(row_, col_, buf_);
326}
327
328c4_CustomViewer *f4_CustProduct(c4_Sequence &seq_, const c4_View &view_) {
329  return d4_new c4_ProductViewer(seq_, view_);
330}
331
332/////////////////////////////////////////////////////////////////////////////
333
334class c4_RemapWithViewer: public c4_CustomViewer {
335    c4_View _parent, _argView;
336
337  public:
338    c4_RemapWithViewer(c4_Sequence &seq_, const c4_View &view_);
339    virtual ~c4_RemapWithViewer();
340
341    virtual c4_View GetTemplate();
342    virtual int GetSize();
343    virtual bool GetItem(int row_, int col_, c4_Bytes &buf_);
344    bool SetItem(int row_, int col_, const c4_Bytes &buf_);
345};
346
347c4_RemapWithViewer::c4_RemapWithViewer(c4_Sequence &seq_, const c4_View &view_)
348  : _parent(&seq_), _argView(view_){}
349
350c4_RemapWithViewer::~c4_RemapWithViewer(){}
351
352c4_View c4_RemapWithViewer::GetTemplate() {
353  return _parent.Clone(); // could probably return _parent just as well
354}
355
356int c4_RemapWithViewer::GetSize() {
357  return _argView.GetSize();
358}
359
360bool c4_RemapWithViewer::GetItem(int row_, int col_, c4_Bytes &buf_) {
361  const c4_Property &map = _argView.NthProperty(0);
362  d4_assert(map.Type() == 'I');
363
364  row_ = ((const c4_IntProp &)map)(_argView[row_]);
365
366  return _parent.GetItem(row_, col_, buf_);
367}
368
369bool c4_RemapWithViewer::SetItem(int row_, int col_, const c4_Bytes &buf_) {
370  const c4_Property &map = _argView.NthProperty(0);
371  d4_assert(map.Type() == 'I');
372
373  row_ = ((const c4_IntProp &)map)(_argView[row_]);
374
375  _parent.SetItem(row_, col_, buf_);
376  return true;
377}
378
379c4_CustomViewer *f4_CustRemapWith(c4_Sequence &seq_, const c4_View &view_) {
380  return d4_new c4_RemapWithViewer(seq_, view_);
381}
382
383/////////////////////////////////////////////////////////////////////////////
384
385class c4_PairViewer: public c4_CustomViewer {
386    c4_View _parent, _argView, _template;
387
388  public:
389    c4_PairViewer(c4_Sequence &seq_, const c4_View &view_);
390    virtual ~c4_PairViewer();
391
392    virtual c4_View GetTemplate();
393    virtual int GetSize();
394    virtual bool GetItem(int row_, int col_, c4_Bytes &buf_);
395    bool SetItem(int row_, int col_, const c4_Bytes &buf_);
396    virtual bool InsertRows(int pos_, c4_Cursor value_, int count_ = 1);
397    virtual bool RemoveRows(int pos_, int count_ = 1);
398};
399
400c4_PairViewer::c4_PairViewer(c4_Sequence &seq_, const c4_View &view_): _parent
401  (&seq_), _argView(view_), _template(_parent.Clone()) {
402  for (int i = 0; i < _argView.NumProperties(); ++i)
403    _template.AddProperty(_argView.NthProperty(i));
404}
405
406c4_PairViewer::~c4_PairViewer(){}
407
408c4_View c4_PairViewer::GetTemplate() {
409  return _template;
410}
411
412int c4_PairViewer::GetSize() {
413  return _parent.GetSize();
414}
415
416bool c4_PairViewer::GetItem(int row_, int col_, c4_Bytes &buf_) {
417  c4_View v = _parent;
418
419  if (col_ >= v.NumProperties()) {
420    v = _argView;
421    col_ = v.FindProperty(_template.NthProperty(col_).GetId());
422    d4_assert(col_ >= 0);
423  }
424
425  return v.GetItem(row_, col_, buf_);
426}
427
428bool c4_PairViewer::SetItem(int row_, int col_, const c4_Bytes &buf_) {
429  c4_View v = _parent;
430
431  if (col_ >= v.NumProperties()) {
432    v = _argView;
433    col_ = v.FindProperty(_template.NthProperty(col_).GetId());
434    d4_assert(col_ >= 0);
435  }
436
437  v.SetItem(row_, col_, buf_);
438  return true;
439}
440
441bool c4_PairViewer::InsertRows(int pos_, c4_Cursor value_, int count_) {
442  _parent.InsertAt(pos_,  *value_, count_);
443  _argView.InsertAt(pos_,  *value_, count_);
444  return true;
445}
446
447bool c4_PairViewer::RemoveRows(int pos_, int count_) {
448  _parent.RemoveAt(pos_, count_);
449  _argView.RemoveAt(pos_, count_);
450  return true;
451}
452
453c4_CustomViewer *f4_CustPair(c4_Sequence &seq_, const c4_View &view_) {
454  return d4_new c4_PairViewer(seq_, view_);
455}
456
457/////////////////////////////////////////////////////////////////////////////
458
459class c4_ConcatViewer: public c4_CustomViewer {
460    c4_View _parent, _argView;
461
462  public:
463    c4_ConcatViewer(c4_Sequence &seq_, const c4_View &view_);
464    virtual ~c4_ConcatViewer();
465
466    virtual c4_View GetTemplate();
467    virtual int GetSize();
468    virtual bool GetItem(int row_, int col_, c4_Bytes &buf_);
469    bool SetItem(int row_, int col_, const c4_Bytes &buf_);
470};
471
472c4_ConcatViewer::c4_ConcatViewer(c4_Sequence &seq_, const c4_View &view_):
473  _parent(&seq_), _argView(view_){}
474
475c4_ConcatViewer::~c4_ConcatViewer(){}
476
477c4_View c4_ConcatViewer::GetTemplate() {
478  return _parent.Clone(); // could probably return _parent just as well
479}
480
481int c4_ConcatViewer::GetSize() {
482  return _parent.GetSize() + _argView.GetSize();
483}
484
485bool c4_ConcatViewer::GetItem(int row_, int col_, c4_Bytes &buf_) {
486  c4_View v = _parent;
487
488  if (row_ >= _parent.GetSize()) {
489    v = _argView;
490    row_ -= _parent.GetSize();
491    col_ = v.FindProperty(_parent.NthProperty(col_).GetId());
492
493    if (col_ < 0)
494      return false;
495  }
496
497  return v.GetItem(row_, col_, buf_);
498}
499
500bool c4_ConcatViewer::SetItem(int row_, int col_, const c4_Bytes &buf_) {
501  c4_View v = _parent;
502
503  if (row_ >= _parent.GetSize()) {
504    v = _argView;
505    row_ -= _parent.GetSize();
506    col_ = v.FindProperty(_parent.NthProperty(col_).GetId());
507    d4_assert(col_ >= 0);
508  }
509
510  v.SetItem(row_, col_, buf_);
511  return true;
512}
513
514c4_CustomViewer *f4_CustConcat(c4_Sequence &seq_, const c4_View &view_) {
515  return d4_new c4_ConcatViewer(seq_, view_);
516}
517
518/////////////////////////////////////////////////////////////////////////////
519
520class c4_RenameViewer: public c4_CustomViewer {
521    c4_View _parent, _template;
522
523  public:
524    c4_RenameViewer(c4_Sequence &seq_, const c4_Property &old_, const
525      c4_Property &new_);
526    virtual ~c4_RenameViewer();
527
528    virtual c4_View GetTemplate();
529    virtual int GetSize();
530    virtual bool GetItem(int row_, int col_, c4_Bytes &buf_);
531    virtual bool SetItem(int row_, int col_, const c4_Bytes &buf_);
532    //virtual bool InsertRows(int pos_, c4_Cursor value_, int count_=1);
533    //virtual bool RemoveRows(int pos_, int count_=1);
534};
535
536c4_RenameViewer::c4_RenameViewer(c4_Sequence &seq_, const c4_Property &old_,
537  const c4_Property &new_): _parent(&seq_) {
538  for (int i = 0; i < _parent.NumProperties(); ++i) {
539    const c4_Property &prop = _parent.NthProperty(i);
540    _template.AddProperty(prop.GetId() == old_.GetId() ? new_ : prop);
541  }
542}
543
544c4_RenameViewer::~c4_RenameViewer(){}
545
546c4_View c4_RenameViewer::GetTemplate() {
547  return _template;
548}
549
550int c4_RenameViewer::GetSize() {
551  return _parent.GetSize();
552}
553
554bool c4_RenameViewer::GetItem(int row_, int col_, c4_Bytes &buf_) {
555  return _parent.GetItem(row_, col_, buf_);
556}
557
558bool c4_RenameViewer::SetItem(int row_, int col_, const c4_Bytes &buf_) {
559  _parent.SetItem(row_, col_, buf_);
560  return true;
561}
562
563c4_CustomViewer *f4_CustRename(c4_Sequence &seq_, const c4_Property &old_,
564  const c4_Property &new_) {
565  return d4_new c4_RenameViewer(seq_, old_, new_);
566}
567
568/////////////////////////////////////////////////////////////////////////////
569
570class c4_GroupByViewer: public c4_CustomViewer {
571    c4_View _parent, _keys, _sorted, _temp;
572    c4_Property _result;
573    c4_DWordArray _map;
574
575    int ScanTransitions(int lo_, int hi_, t4_byte *flags_, const c4_View
576      &match_)const;
577
578  public:
579    c4_GroupByViewer(c4_Sequence &seq_, const c4_View &keys_, const c4_Property
580      &result_);
581    virtual ~c4_GroupByViewer();
582
583    virtual c4_View GetTemplate();
584    virtual int GetSize();
585    virtual bool GetItem(int row_, int col_, c4_Bytes &buf_);
586};
587
588c4_GroupByViewer::c4_GroupByViewer(c4_Sequence &seq_, const c4_View &keys_,
589  const c4_Property &result_): _parent(&seq_), _keys(keys_), _result(result_) {
590  _sorted = _parent.SortOn(_keys);
591  int n = _sorted.GetSize();
592
593  c4_Bytes temp;
594  t4_byte *buf = temp.SetBufferClear(n);
595
596  int groups = 0;
597  if (n > 0) {
598    ++buf[0]; // the first entry is always a transition
599    groups = 1+ScanTransitions(1, n, buf, _sorted.Project(_keys));
600  }
601
602  // set up a map pointing to each transition
603  _map.SetSize(groups + 1);
604  int j = 0;
605
606  for (int i = 0; i < n; ++i)
607    if (buf[i])
608      _map.SetAt(j++, i);
609
610  // also append an entry to point just past the end
611  _map.SetAt(j, n);
612
613  d4_assert(_map.GetAt(0) == 0);
614  d4_assert(j == groups);
615}
616
617c4_GroupByViewer::~c4_GroupByViewer(){}
618
619int c4_GroupByViewer::ScanTransitions(int lo_, int hi_, t4_byte *flags_, const
620  c4_View &match_)const {
621  d4_assert(lo_ > 0);
622
623  int m = hi_ - lo_;
624  d4_assert(m >= 0);
625
626  // done if nothing left or if entire range is identical
627  if (m == 0 || match_[lo_ - 1] == match_[hi_ - 1])
628    return 0;
629
630  // range has a transition, done if it is exactly of size one
631  if (m == 1) {
632    ++(flags_[lo_]);
633    return 1;
634  }
635
636  // use binary splitting if the range has enough entries
637  if (m >= 5)
638    return ScanTransitions(lo_, lo_ + m / 2, flags_, match_) + ScanTransitions
639      (lo_ + m / 2, hi_, flags_, match_);
640
641  // else use a normal linear scan
642  int n = 0;
643
644  for (int i = lo_; i < hi_; ++i)
645  if (match_[i] != match_[i - 1]) {
646    ++(flags_[i]);
647    ++n;
648  }
649
650  return n;
651}
652
653c4_View c4_GroupByViewer::GetTemplate() {
654  c4_View v = _keys.Clone();
655  v.AddProperty(_result);
656
657  return v;
658}
659
660int c4_GroupByViewer::GetSize() {
661  d4_assert(_map.GetSize() > 0);
662
663  return _map.GetSize() - 1;
664}
665
666bool c4_GroupByViewer::GetItem(int row_, int col_, c4_Bytes &buf_) {
667  if (col_ < _keys.NumProperties())
668    return _sorted.GetItem(_map.GetAt(row_), col_, buf_);
669
670  d4_assert(col_ == _keys.NumProperties());
671
672  t4_i32 count;
673  switch (_result.Type()) {
674    case 'I':
675      count = _map.GetAt(row_ + 1) - _map.GetAt(row_);
676      buf_ = c4_Bytes(&count, sizeof count, true);
677      break;
678    case 'V':
679      _temp = _sorted.Slice(_map.GetAt(row_), _map.GetAt(row_ + 1))
680        .ProjectWithout(_keys);
681      buf_ = c4_Bytes(&_temp, sizeof _temp, true);
682      break;
683    default:
684      d4_assert(0);
685  }
686
687  return true;
688}
689
690c4_CustomViewer *f4_CustGroupBy(c4_Sequence &seq_, const c4_View &template_,
691  const c4_Property &result_) {
692  return d4_new c4_GroupByViewer(seq_, template_, result_);
693}
694
695/////////////////////////////////////////////////////////////////////////////
696
697class c4_JoinPropViewer: public c4_CustomViewer {
698    c4_View _parent, _template;
699    c4_ViewProp _sub;
700    int _subPos, _subWidth;
701    c4_DWordArray _base, _offset;
702
703  public:
704    c4_JoinPropViewer(c4_Sequence &seq_, const c4_ViewProp &sub_, bool outer_);
705    virtual ~c4_JoinPropViewer();
706
707    virtual c4_View GetTemplate();
708    virtual int GetSize();
709    virtual bool GetItem(int row_, int col_, c4_Bytes &buf_);
710};
711
712c4_JoinPropViewer::c4_JoinPropViewer(c4_Sequence &seq_, const c4_ViewProp &sub_,
713  bool outer_): _parent(&seq_), _sub(sub_), _subPos(_parent.FindProperty
714  (sub_.GetId())), _subWidth(0) {
715  d4_assert(_subPos >= 0);
716
717  for (int k = 0; k < _parent.NumProperties(); ++k) {
718    if (k != _subPos)
719      _template.AddProperty(_parent.NthProperty(k));
720    else
721    // if there are no rows, then this join does very little anyway
722    //! OOPS: if this is an unattached view, then the subviews can differ
723    if (_parent.GetSize() > 0) {
724      c4_View view = sub_(_parent[0]);
725      for (int l = 0; l < view.NumProperties(); ++l) {
726        _template.AddProperty(view.NthProperty(l));
727        ++_subWidth;
728      }
729    }
730  }
731
732  _base.SetSize(0, 5);
733  _offset.SetSize(0, 5);
734
735  for (int i = 0; i < _parent.GetSize(); ++i) {
736    c4_View v = _sub(_parent[i]);
737
738    int n = v.GetSize();
739    if (n == 0 && outer_) {
740      _base.Add(i);
741      _offset.Add(~(t4_i32)0); // special null entry for outer joins
742    } else
743    for (int j = 0; j < n; ++j) {
744      _base.Add(i);
745      _offset.Add(j);
746    }
747  }
748}
749
750c4_JoinPropViewer::~c4_JoinPropViewer(){}
751
752c4_View c4_JoinPropViewer::GetTemplate() {
753  return _template;
754}
755
756int c4_JoinPropViewer::GetSize() {
757  return _base.GetSize();
758}
759
760bool c4_JoinPropViewer::GetItem(int row_, int col_, c4_Bytes &buf_) {
761  c4_View v = _parent;
762  int r = _base.GetAt(row_);
763
764  if (col_ >= _subPos)
765  if (col_ >= _subPos + _subWidth) {
766    col_ -= _subWidth - 1;
767  } else {
768    v = _sub(_parent[r]);
769    r = _offset.GetAt(row_);
770    if (r < 0)
771      return false;
772    // if this is a null row in an outer join
773
774    col_ = v.FindProperty(_template.NthProperty(col_).GetId());
775    if (col_ < 0)
776      return false;
777    // if subview doesn't have all properties
778  }
779
780  return v.GetItem(r, col_, buf_);
781}
782
783c4_CustomViewer *f4_CustJoinProp(c4_Sequence &seq_, const c4_ViewProp &sub_,
784  bool outer_) {
785  return d4_new c4_JoinPropViewer(seq_, sub_, outer_);
786}
787
788/////////////////////////////////////////////////////////////////////////////
789
790class c4_JoinViewer: public c4_CustomViewer {
791    c4_View _parent, _argView, _template;
792    c4_DWordArray _base, _offset;
793
794  public:
795    c4_JoinViewer(c4_Sequence &seq_, const c4_View &keys_, const c4_View &view_,
796      bool outer_);
797    virtual ~c4_JoinViewer();
798
799    virtual c4_View GetTemplate();
800    virtual int GetSize();
801    virtual bool GetItem(int row_, int col_, c4_Bytes &buf_);
802};
803
804c4_JoinViewer::c4_JoinViewer(c4_Sequence &seq_, const c4_View &keys_, const
805  c4_View &view_, bool outer_): _parent(&seq_), _argView(view_.SortOn(keys_)) {
806  // why not in GetTemplate, since we don't need to know this...
807  _template = _parent.Clone();
808  for (int l = 0; l < _argView.NumProperties(); ++l)
809    _template.AddProperty(_argView.NthProperty(l));
810
811  c4_View sorted = _parent.SortOn(keys_).Project(keys_);
812  c4_View temp = _argView.Project(keys_);
813
814  _base.SetSize(0, 5);
815  _offset.SetSize(0, 5);
816
817  int j = 0, n = 0;
818
819  for (int i = 0; i < sorted.GetSize(); ++i) {
820    int orig = _parent.GetIndexOf(sorted[i]);
821    d4_assert(orig >= 0);
822
823    if (i > 0 && sorted[i] == sorted[i - 1]) {
824      // if last key was same, repeat the same join
825      int last = _offset.GetSize() - n;
826      for (int k = 0; k < n; ++k) {
827        _base.Add(orig);
828        _offset.Add(_offset.GetAt(last + k));
829      }
830    } else
831     { // no, this is a new combination
832      bool match = false;
833
834      // advance until the temp view entry is >= this sorted entry
835      while (j < temp.GetSize())
836      if (sorted[i] <= temp[j]) {
837        match = sorted[i] == temp[j];
838        break;
839      } else
840        ++j;
841
842      n = 0;
843
844      if (match) {
845        do {
846          _base.Add(orig);
847          _offset.Add(j);
848          ++n;
849        } while (++j < temp.GetSize() && temp[j] == temp[j - 1]);
850      } else if (outer_) {
851        // no match, add an entry anyway if this is an outer join
852        _base.Add(orig);
853        _offset.Add(~(t4_i32)0); // special null entry
854        ++n;
855      }
856    }
857  }
858}
859
860c4_JoinViewer::~c4_JoinViewer(){}
861
862c4_View c4_JoinViewer::GetTemplate() {
863  return _template;
864}
865
866int c4_JoinViewer::GetSize() {
867  return _base.GetSize();
868}
869
870bool c4_JoinViewer::GetItem(int row_, int col_, c4_Bytes &buf_) {
871  c4_View v = _parent;
872  int r = _base.GetAt(row_);
873
874  if (col_ >= v.NumProperties()) {
875    v = _argView;
876    r = _offset.GetAt(row_);
877    if (r < 0)
878      return false;
879    // if this is a null row in an outer join
880
881    col_ = v.FindProperty(_template.NthProperty(col_).GetId());
882    if (col_ < 0)
883      return false;
884    // if second view doesn't have all properties
885  }
886
887  return v.GetItem(r, col_, buf_);
888}
889
890#if 0
891bool c4_JoinViewer::GetItem(int row_, int col_, c4_Bytes &buf_) {
892  c4_View v = _parent;
893
894  int o = 0;
895  int r = _offset.GetAt(row_);
896
897  if (r < 0) {
898    o = ~r;
899    if (o == 0)
900      return false;
901    // if this is a null row in an outer join
902    r -= o;
903  }
904
905  if (col_ >= v.NumProperties()) {
906    v = _argView;
907    r = _o;
908
909    col_ = v.FindProperty(_template.NthProperty(col_));
910    if (col_ < 0)
911      return false;
912    // if second view doesn't have all properties
913  }
914
915  return v.GetItem(r, col_, buf_);
916}
917
918#endif
919
920c4_CustomViewer *f4_CustJoin(c4_Sequence &seq_, const c4_View &keys_, const
921  c4_View &view_, bool outer_) {
922  return d4_new c4_JoinViewer(seq_, keys_, view_, outer_);
923}
924
925/////////////////////////////////////////////////////////////////////////////
926