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