otsdaq  v2_05_02_indev
TableBase.cc
1 #include "otsdaq/TableCore/TableBase.h"
2 
3 #include <iostream> // std::cout
4 #include <typeinfo>
5 
6 #include "otsdaq/TableCore/TableInfoReader.h"
7 
8 using namespace ots;
9 
10 #undef __MF_SUBJECT__
11 #define __MF_SUBJECT__ "TableBase"
12 #undef __COUT_HDR__
13 #define __COUT_HDR__ ("TableBase-" + getTableName() + "\t<> ")
14 
15 //==============================================================================
16 // TableBase
17 // If a valid string pointer is passed in accumulatedExceptions
18 // then allowIllegalColumns is set for InfoReader
19 // If accumulatedExceptions pointer = 0, then illegal columns throw std::runtime_error
20 // exception
21 TableBase::TableBase(const std::string& tableName,
22  std::string* accumulatedExceptions)
23  : MAX_VIEWS_IN_CACHE(20) // This is done, so that inheriting table classes could have
24  // varying amounts of cache
25  , tableName_(tableName)
26  , activeTableView_(0)
27  , mockupTableView_(tableName)
28 {
29  if(tableName == "")
30  {
31  __SS__ << "Do not allow anonymous table view construction!" << __E__;
32  ss << StringMacros::stackTrace() << __E__;
33  __SS_THROW__;
34  }
35 
36  bool dbg = tableName == "ARTDAQEventBuilderTable";
37  if(dbg) __COUTV__(tableName);
38  // info reader fills up the mockup view
39  TableInfoReader tableInfoReader(accumulatedExceptions);
40  if(dbg) __COUT__ << "Reading..." << __E__;
41  try // to read info
42  {
43  std::string returnedExceptions = tableInfoReader.read(this);
44  if(dbg) __COUT__ << "Read.";
45  if(returnedExceptions != "")
46  __COUT_ERR__ << returnedExceptions << __E__;
47 
48  if(accumulatedExceptions)
49  *accumulatedExceptions += std::string("\n") + returnedExceptions;
50  }
51  catch(...) // if accumulating exceptions, continue to and return, else throw
52  {
53  __SS__ << "Failure in tableInfoReader.read(this). "
54  << "Perhaps you need to run otsdaq_convert_config_to_table ?" << __E__;
55  __COUT_ERR__ << "\n" << ss.str();
56  if(accumulatedExceptions)
57  *accumulatedExceptions += std::string("\n") + ss.str();
58  else
59  throw;
60  return; // do not proceed with mockup check if this failed
61  }
62  if(dbg) __COUT__ << "Initializing..." << __E__;
63  // call init on mockup view to verify columns
64  try
65  {
66  getMockupViewP()->init();
67  if(dbg) __COUT__ << "Init." << __E__;
68  }
69  catch(std::runtime_error& e) // if accumulating exceptions, continue to and return, else throw
70  {
71  if(accumulatedExceptions)
72  *accumulatedExceptions += std::string("\n") + e.what();
73  else
74  throw;
75  }
76 } // end constructor()
77 
78 //==============================================================================
79 // TableBase
80 // Default constructor is only used to create special tables
81 // not based on an ...Info.xml file
82 // e.g. the TableGroupMetadata table in ConfigurationManager
83 TableBase::TableBase(bool specialTable, const std::string& specialTableName)
84  : MAX_VIEWS_IN_CACHE(1) // This is done, so that inheriting table classes could have
85  // varying amounts of cache
86  , tableName_(specialTableName)
87  , activeTableView_(0)
88  , mockupTableView_(specialTableName)
89 {
90  __COUT__ << "Special table '" << tableName_ << "' constructed. " << specialTable << __E__;
91 } //special table constructor()
92 
93 
95 //TableBase::TableBase(void)
96 // : MAX_VIEWS_IN_CACHE(1)
97 // {
98 // __SS__ << "Should not call void constructor, table type is lost!" << __E__;
99 // ss << StringMacros::stackTrace() << __E__;
100 // __SS_THROW__;
101 //}
102 
103 //==============================================================================
104 TableBase::~TableBase(void) {}
105 
106 //==============================================================================
107 std::string TableBase::getTypeId() { return typeid(this).name(); }
108 
109 //==============================================================================
110 void TableBase::init(ConfigurationManager* /*tableManager*/)
111 {
112  //__COUT__ << "Default TableBase::init() called." << __E__;
113 }
114 
115 //==============================================================================
116 void TableBase::reset(bool keepTemporaryVersions)
117 {
118  // std::cout << __COUT_HDR_FL__ << "resetting" << __E__;
119  deactivate();
120  if(keepTemporaryVersions)
121  trimCache(0);
122  else // clear all
123  tableViews_.clear();
124 }
125 
126 //==============================================================================
127 void TableBase::print(std::ostream& out) const
128 {
129  // std::cout << __COUT_HDR_FL__ << "activeVersion_ " << activeVersion_ << "
130  // (INVALID_VERSION:=" << INVALID_VERSION << ")" << __E__;
131  if(!activeTableView_)
132  {
133  __COUT_ERR__ << "ERROR: No active view set" << __E__;
134  return;
135  }
136  activeTableView_->print(out);
137 }
138 
139 //==============================================================================
140 // makes active version the specified table view version
141 // if the version is not already stored, then creates a mockup version
142 void TableBase::setupMockupView(TableVersion version)
143 {
144  if(!isStored(version))
145  {
146  tableViews_.emplace(std::make_pair(version,TableView(tableName_)));
147  tableViews_.at(version).copy(mockupTableView_, version, mockupTableView_.getAuthor());
148  trimCache();
149  if(!isStored(version)) // the trim cache is misbehaving!
150  {
151  __SS__ << "IMPOSSIBLE ERROR: trimCache() is deleting the "
152  "latest view version "
153  << version << "!" << __E__;
154  __SS_THROW__;
155  }
156  }
157  else
158  {
159  __SS__ << "View to fill with mockup already exists: " << version << ". Cannot overwrite!" << __E__;
160  ss << StringMacros::stackTrace() << __E__;
161  __SS_THROW__;
162  }
163 } //end setupMockupView()
164 
165 //==============================================================================
166 // trimCache
167 // if there are more views than MAX_VIEWS_IN_CACHE, erase them.
168 // choose wisely the view to delete
169 // (by access time)
170 void TableBase::trimCache(unsigned int trimSize)
171 {
172  // delete cached views, if necessary
173 
174  if(trimSize == (unsigned int)-1) // if -1, use MAX_VIEWS_IN_CACHE
175  trimSize = MAX_VIEWS_IN_CACHE;
176 
177  //int i = 0;
178  while(getNumberOfStoredViews() > trimSize)
179  {
180  TableVersion versionToDelete;
181  time_t stalestTime = -1;
182 
183  for(auto& viewPair : tableViews_)
184  if(!viewPair.first.isTemporaryVersion())
185  {
186  if(stalestTime == -1 || viewPair.second.getLastAccessTime() < stalestTime)
187  {
188  versionToDelete = viewPair.first;
189  stalestTime = viewPair.second.getLastAccessTime();
190  if(!trimSize)
191  break; // if trimSize is 0, then just take first found
192  }
193  }
194 
195  if(versionToDelete.isInvalid())
196  {
197  __SS__ << "Can NOT have a stored view with an invalid version!" << __E__;
198  __SS_THROW__;
199  }
200 
201  eraseView(versionToDelete);
202  }
203 }
204 
205 //==============================================================================
206 // trimCache
207 // if there are more views than MAX_VIEWS_IN_CACHE, erase them.
208 // choose wisely the view to delete
209 // (by access time)
210 void TableBase::trimTemporary(TableVersion targetVersion)
211 {
212  if(targetVersion.isInvalid()) // erase all temporary
213  {
214  for(auto it = tableViews_.begin(); it != tableViews_.end(); /*no increment*/)
215  {
216  if(it->first.isTemporaryVersion())
217  {
218  //__COUT__ << "Trimming temporary version: " << it->first << __E__;
219  if(activeTableView_ && getViewVersion() == it->first) // if activeVersion is being erased!
220  deactivate(); // deactivate active view, instead of guessing at next
221  // active view
222  tableViews_.erase(it++);
223  }
224  else
225  ++it;
226  }
227  }
228  else if(targetVersion.isTemporaryVersion()) // erase target
229  {
230  //__COUT__ << "Trimming temporary version: " << targetVersion << __E__;
231  eraseView(targetVersion);
232  }
233  else
234  {
235  // else this is a persistent version!
236  __SS__ << "Temporary trim target was a persistent version: " << targetVersion << __E__;
237  __COUT_ERR__ << "\n" << ss.str();
238  __SS_THROW__;
239  }
240 }
241 
242 //==============================================================================
243 // checkForDuplicate
244 // look for a duplicate of the needleVersion in the haystack
245 // which is the cached views in tableViews_
246 //
247 // Note: ignoreVersion is useful if you know another view is already identical
248 // like when converting from temporary to persistent
249 //
250 // Return invalid if no matches
251 TableVersion TableBase::checkForDuplicate(TableVersion needleVersion, TableVersion ignoreVersion) const
252 {
253  auto needleIt = tableViews_.find(needleVersion);
254  if(needleIt == tableViews_.end())
255  {
256  // else this is a persistent version!
257  __SS__ << "needleVersion does not exist: " << needleVersion << __E__;
258  __COUT_ERR__ << "\n" << ss.str();
259  __SS_THROW__;
260  }
261 
262  const TableView* needleView = &(needleIt->second);
263  unsigned int rows = needleView->getNumberOfRows();
264  unsigned int cols = needleView->getNumberOfColumns();
265 
266  bool match;
267  unsigned int potentialMatchCount = 0;
268 
269  // needleView->print();
270 
271  // for each table in cache
272  // check each row,col
273  auto viewPairReverseIterator = tableViews_.rbegin();
274  for(; viewPairReverseIterator != tableViews_.rend(); ++viewPairReverseIterator)
275  {
276  if(viewPairReverseIterator->first == needleVersion)
277  continue; // skip needle version
278  if(viewPairReverseIterator->first == ignoreVersion)
279  continue; // skip ignore version
280  if(viewPairReverseIterator->first.isTemporaryVersion())
281  continue; // skip temporary versions
282 
283  if(viewPairReverseIterator->second.getNumberOfRows() != rows)
284  continue; // row mismatch
285 
286  if(viewPairReverseIterator->second.getDataColumnSize() != cols || viewPairReverseIterator->second.getSourceColumnMismatch() != 0)
287  continue; // col mismatch
288 
289  ++potentialMatchCount;
290  __COUT__ << "Checking version... " << viewPairReverseIterator->first << __E__;
291 
292  // viewPairReverseIterator->second.print();
293 
294  // if column source names do not match then skip
295  // source names are potentially different from
296  // getColumnsInfo()/getColumnStorageNames
297 
298  match = viewPairReverseIterator->second.getSourceColumnNames().size() == needleView->getSourceColumnNames().size();
299  if(match)
300  {
301  for(auto& haystackColName : viewPairReverseIterator->second.getSourceColumnNames())
302  if(needleView->getSourceColumnNames().find(haystackColName) == needleView->getSourceColumnNames().end())
303  {
304  __COUT__ << "Found column name mismatch for '" << haystackColName << "'... So allowing same data!" << __E__;
305 
306  match = false;
307  break;
308  }
309  }
310 
311  // checking columnsInfo seems to be wrong approach, use getSourceColumnNames
312  // (above) auto viewColInfoIt =
313  // viewPairReverseIterator->second.getColumnsInfo().begin(); for(unsigned
314  // int col=0; match && //note column size must already match
315  // viewPairReverseIterator->second.getColumnsInfo().size() > 3 &&
316  // col<viewPairReverseIterator->second.getColumnsInfo().size()-3;++col,viewColInfoIt++)
317  // if(viewColInfoIt->getName() !=
318  // needleView->getColumnsInfo()[col].getName())
319  // {
320  // match = false;
324  // }
325 
326  for(unsigned int row = 0; match && row < rows; ++row)
327  {
328  for(unsigned int col = 0; col < cols - 2; ++col) // do not consider author and timestamp
329  if(viewPairReverseIterator->second.getDataView()[row][col] != needleView->getDataView()[row][col])
330  {
331  match = false;
332 
333  // __COUT__ << "Value name mismatch " << col << ":"
334  // <<
335  // viewPairReverseIterator->second.getDataView()[row][col]
336  // << "[" <<
337  // viewPairReverseIterator->second.getDataView()[row][col].size()
338  // << "]" << " vs " <<
339  // needleView->getDataView()[row][col] << "["
340  // <<
341  // needleView->getDataView()[row][col].size()
342  // <<
343  // "]"
344  // <<
345  // __E__;
346 
347  break;
348  }
349  }
350  if(match)
351  {
352  __COUT_INFO__ << "Duplicate version found: " << viewPairReverseIterator->first << __E__;
353  return viewPairReverseIterator->first;
354  }
355  } // end table version loop
356 
357  __COUT__ << "No duplicates found in " << potentialMatchCount << " potential matches." << __E__;
358  return TableVersion(); // return invalid if no matches
359 } // end checkForDuplicate()
360 
361 //==============================================================================
362 void TableBase::changeVersionAndActivateView(TableVersion temporaryVersion, TableVersion version)
363 {
364  auto tmpIt = tableViews_.find(temporaryVersion);
365  if(tableViews_.find(temporaryVersion) == tableViews_.end())
366  {
367  __SS__ << "ERROR: Temporary view version " << temporaryVersion << " doesn't exists!" << __E__;
368  __COUT_ERR__ << "\n" << ss.str();
369  __SS_THROW__;
370  }
371  if(version.isInvalid())
372  {
373  __SS__ << "ERROR: Attempting to create an invalid version " << version << "! Did you really run out of versions? (this should never happen)" << __E__;
374  __COUT_ERR__ << "\n" << ss.str();
375  __SS_THROW__;
376  }
377 
378  if(tableViews_.find(version) != tableViews_.end())
379  __COUT_WARN__ << "WARNING: View version " << version << " already exists! Overwriting." << __E__;
380 
381  auto emplacePair /*it,bool*/ = tableViews_.emplace(std::make_pair(version,TableView(tableName_)));
382  emplacePair.first->second.copy(tmpIt->second, version, tmpIt->second.getAuthor());
383  setActiveView(version);
384  eraseView(temporaryVersion); // delete temp version from tableViews_
385 }
386 
387 //==============================================================================
388 bool TableBase::isStored(const TableVersion& version) const { return (tableViews_.find(version) != tableViews_.end()); }
389 
390 //==============================================================================
391 bool TableBase::eraseView(TableVersion version)
392 {
393  if(!isStored(version))
394  return false;
395 
396  if(activeTableView_ && getViewVersion() == version) // if activeVersion is being erased!
397  deactivate(); // deactivate active view, instead of guessing at next active view
398 
399  tableViews_.erase(version);
400 
401  return true;
402 }
403 
404 //==============================================================================
405 const std::string& TableBase::getTableName(void) const { return tableName_; }
406 
407 //==============================================================================
408 const std::string& TableBase::getTableDescription(void) const { return tableDescription_; }
409 
410 //==============================================================================
411 const TableVersion& TableBase::getViewVersion(void) const { return getView().getVersion(); }
412 
413 //==============================================================================
414 // latestAndMockupColumnNumberMismatch
415 // intended to check if the column count was recently changed
416 bool TableBase::latestAndMockupColumnNumberMismatch(void) const
417 {
418  std::set<TableVersion> retSet = getStoredVersions();
419  if(retSet.size() && !retSet.rbegin()->isTemporaryVersion())
420  {
421  return tableViews_.find(*(retSet.rbegin()))->second.getNumberOfColumns() != mockupTableView_.getNumberOfColumns();
422  }
423  // there are no latest non-temporary tables so there is a mismatch (by default)
424  return true;
425 }
426 
427 //==============================================================================
428 std::set<TableVersion> TableBase::getStoredVersions(void) const
429 {
430  std::set<TableVersion> retSet;
431  for(auto& configs : tableViews_)
432  retSet.emplace(configs.first);
433  return retSet;
434 }
435 
436 //==============================================================================
437 // getNumberOfStoredViews
438 // count number of stored views, not including temporary views
439 // (invalid views should be impossible)
440 unsigned int TableBase::getNumberOfStoredViews(void) const
441 {
442  unsigned int sz = 0;
443  for(auto& viewPair : tableViews_)
444  if(viewPair.first.isTemporaryVersion())
445  continue;
446  else if(viewPair.first.isInvalid())
447  {
448  //__SS__ << "Can NOT have a stored view with an invalid version!" << __E__;
449  //__SS_THROW__;
450 
451  // NOTE: if this starts happening a lot, could just auto-correct and remove
452  // the invalid version
453  // but it would be better to fix the cause.
454 
455  // FIXME... for now just auto correcting
456  __COUT__ << "There is an invalid version now!.. where did it come from?" << __E__;
457  }
458  else
459  ++sz;
460  return sz;
461 } // end getNumberOfStoredViews()
462 
463 //==============================================================================
464 const TableView& TableBase::getView(void) const
465 {
466  if(!activeTableView_)
467  {
468  __SS__ << "There is no active table view setup! Please check your system configuration." << __E__;
469  __SS_ONLY_THROW__;
470  }
471  return *activeTableView_;
472 }
473 
474 //==============================================================================
475 TableView* TableBase::getViewP(void)
476 {
477  if(!activeTableView_)
478  {
479  __SS__ << "There is no active table view setup! Please check your system configuration." << __E__;
480  __SS_ONLY_THROW__;
481  }
482  return activeTableView_;
483 }
484 
485 //==============================================================================
486 TableView* TableBase::getMockupViewP(void) { return &mockupTableView_; }
487 
488 //==============================================================================
489 void TableBase::setTableName(const std::string& tableName) { tableName_ = tableName; }
490 
491 //==============================================================================
492 void TableBase::setTableDescription(const std::string& tableDescription) { tableDescription_ = tableDescription; }
493 
494 //==============================================================================
495 // deactivate
496 // reset the active view
497 void TableBase::deactivate() { activeTableView_ = 0; }
498 
499 //==============================================================================
500 // isActive
501 bool TableBase::isActive() { return activeTableView_ ? true : false; }
502 
503 //==============================================================================
504 bool TableBase::setActiveView(TableVersion version)
505 {
506  if(!isStored(version))
507  { // we don't call else load for the user, because the table manager would lose
508  // track.. (I think?)
509  // so load new versions for the first time through the table manager only. (I
510  // think??)
511  __SS__ << "\nsetActiveView() ERROR: View with version " << version << " has never been stored before!" << __E__;
512  __SS_THROW__;
513  return false;
514  }
515  activeTableView_ = &tableViews_.at(version);
516 
517  if(tableViews_.at(version).getVersion() != version)
518  {
519  __SS__ << "Something has gone very wrong with the version handling!" << __E__;
520  __SS_THROW__;
521  }
522 
523  return true;
524 }
525 
526 //==============================================================================
527 // mergeViews
528 // merges source view A and B and places in
529 // destination temporary version.
530 // if destination version is invalid, then next available temporary version is chosen
531 // one error, throw exception
532 //
533 // Returns version of new temporary view that was created.
535  const TableView& sourceViewA,
536  const TableView& sourceViewB,
537  TableVersion destinationVersion,
538  const std::string& author,
539  const std::string& mergeApproach /*Rename,Replace,Skip*/,
540  std::map<std::pair<std::string /*original table*/, std::string /*original uidB*/>, std::string /*converted uidB*/>& uidConversionMap,
541  std::map<std::pair<std::string /*original table*/, std::pair<std::string /*group linkid*/, std::string /*original gidB*/> >,
542  std::string /*converted gidB*/>& groupidConversionMap,
543  bool fillRecordConversionMaps,
544  bool applyRecordConversionMaps,
545  bool generateUniqueDataColumns)
546 {
547  __COUT__ << "mergeViews starting..." << __E__;
548 
549  // There 3 modes:
550  // rename -- All records from both groups are maintained, but conflicts from B
551  // are renamed.
552  // Must maintain a map of UIDs that are remapped to new name for
553  // groupB, because linkUID fields must be preserved. replace --
554  // Any UID conflicts for a record are replaced by the record from group B.
555  // skip -- Any UID conflicts for a record are skipped so that group A record
556  // remains
557 
558  // check valid mode
559  if(!(mergeApproach == "Rename" || mergeApproach == "Replace" || mergeApproach == "Skip"))
560  {
561  __SS__ << "Error! Invalid merge approach '" << mergeApproach << ".'" << __E__;
562  __SS_THROW__;
563  }
564 
565  // check that column sizes match
566  if(sourceViewA.getNumberOfColumns() != mockupTableView_.getNumberOfColumns())
567  {
568  __SS__ << "Error! Number of Columns of source view A must match destination "
569  "mock-up view."
570  << "Dimension of source is [" << sourceViewA.getNumberOfColumns() << "] and of destination mockup is [" << mockupTableView_.getNumberOfColumns()
571  << "]." << __E__;
572  __SS_THROW__;
573  }
574  // check that column sizes match
575  if(sourceViewB.getNumberOfColumns() != mockupTableView_.getNumberOfColumns())
576  {
577  __SS__ << "Error! Number of Columns of source view B must match destination "
578  "mock-up view."
579  << "Dimension of source is [" << sourceViewB.getNumberOfColumns() << "] and of destination mockup is [" << mockupTableView_.getNumberOfColumns()
580  << "]." << __E__;
581  __SS_THROW__;
582  }
583 
584  // fill conversion map based on merge approach
585 
586  sourceViewA.print();
587  sourceViewB.print();
588 
589  if(fillRecordConversionMaps && mergeApproach == "Rename")
590  {
591  __COUT__ << "Filling record conversion map." << __E__;
592 
593  // rename -- All records from both groups are maintained, but conflicts from
594  // B are renamed.
595  // Must maintain a map of UIDs that are remapped to new name for
596  // groupB, because linkUID fields must be preserved.
597 
598  // for each B record
599  // if there is a conflict, rename
600 
601  unsigned int uniqueId;
602  std::string uniqueIdString, uniqueIdBase;
603  char indexString[1000];
604  unsigned int ra;
605  unsigned int numericStartIndex;
606  bool found;
607 
608  for(unsigned int cb = 0; cb < sourceViewB.getNumberOfColumns(); ++cb)
609  {
610  // skip columns that are not UID or GroupID columns
611  if(!(sourceViewA.getColumnInfo(cb).isUID() || sourceViewA.getColumnInfo(cb).isGroupID()))
612  continue;
613 
614  __COUT__ << "Have an ID column: " << cb << " " << sourceViewA.getColumnInfo(cb).getType() << __E__;
615 
616  // at this point we have an ID column, verify B and mockup are the same
617  if(sourceViewA.getColumnInfo(cb).getType() != sourceViewB.getColumnInfo(cb).getType() ||
618  sourceViewA.getColumnInfo(cb).getType() != mockupTableView_.getColumnInfo(cb).getType())
619  {
620  __SS__ << "Error! " << sourceViewA.getColumnInfo(cb).getType() << " column " << cb
621  << " of source view A must match source B and destination mock-up "
622  "view."
623  << " Column of source B is [" << sourceViewA.getColumnInfo(cb).getType() << "] and of destination mockup is ["
624  << mockupTableView_.getColumnInfo(cb).getType() << "]." << __E__;
625  __SS_THROW__;
626  }
627 
628  // getLinkGroupIDColumn(childLinkIndex)
629 
630  std::vector<std::string /*converted uidB*/> localConvertedIds; // used for conflict completeness check
631 
632  if(sourceViewA.getColumnInfo(cb).isGroupID())
633  {
634  std::set<std::string> aGroupids = sourceViewA.getSetOfGroupIDs(cb);
635  std::set<std::string> bGroupids = sourceViewB.getSetOfGroupIDs(cb);
636 
637  for(const auto& bGroupid : bGroupids)
638  {
639  if(aGroupids.find(bGroupid) == aGroupids.end())
640  continue;
641 
642  // if here, found conflict
643  __COUT__ << "found conflict: " << getTableName() << "/" << bGroupid << __E__;
644 
645  // extract starting uniqueId number
646  {
647  const std::string& str = bGroupid;
648  numericStartIndex = str.size();
649 
650  // find first non-numeric character
651  while(numericStartIndex - 1 < str.size() && str[numericStartIndex - 1] >= '0' && str[numericStartIndex - 1] <= '9')
652  --numericStartIndex;
653 
654  if(numericStartIndex < str.size())
655  {
656  uniqueId = atoi(str.substr(numericStartIndex).c_str()) + 1;
657  uniqueIdBase = str.substr(0, numericStartIndex);
658  }
659  else
660  {
661  uniqueId = 0;
662  uniqueIdBase = str;
663  }
664 
665  __COUTV__(uniqueIdBase);
666  __COUTV__(uniqueId);
667  } // end //extract starting uniqueId number
668 
669  // find unique id string
670  {
671  sprintf(indexString, "%u", uniqueId);
672  uniqueIdString = uniqueIdBase + indexString;
673  __COUTV__(uniqueIdString);
674 
675  found = false;
676  // check converted records and source A and B for conflicts
677  if(aGroupids.find(uniqueIdString) != aGroupids.end())
678  found = true;
679  if(!found && bGroupids.find(uniqueIdString) != bGroupids.end())
680  found = true;
681  if(!found && bGroupids.find(uniqueIdString) != bGroupids.end())
682  found = true;
683  for(ra = 0; !found && ra < localConvertedIds.size(); ++ra)
684  if(localConvertedIds[ra] == uniqueIdString)
685  found = true;
686 
687  while(found) // while conflict, change id
688  {
689  ++uniqueId;
690  sprintf(indexString, "%u", uniqueId);
691  uniqueIdString = uniqueIdBase + indexString;
692  __COUTV__(uniqueIdString);
693 
694  found = false;
695  // check converted records and source A and B for conflicts
696  if(aGroupids.find(uniqueIdString) != aGroupids.end())
697  found = true;
698  if(!found && bGroupids.find(uniqueIdString) != bGroupids.end())
699  found = true;
700  if(!found && bGroupids.find(uniqueIdString) != bGroupids.end())
701  found = true;
702  for(ra = 0; !found && ra < localConvertedIds.size(); ++ra)
703  if(localConvertedIds[ra] == uniqueIdString)
704  found = true;
705  }
706  } // end find unique id string
707 
708  // have unique id string now
709  __COUTV__(uniqueIdString);
710 
711  groupidConversionMap[std::pair<std::string /*original table*/, std::pair<std::string /*group linkid*/, std::string /*original gidB*/> >(
712  getTableName(),
713  std::pair<std::string /*group linkid*/, std::string /*original gidB*/>(sourceViewB.getColumnInfo(cb).getChildLinkIndex(), bGroupid))] =
714  uniqueIdString;
715  localConvertedIds.push_back(uniqueIdString); // save to vector for
716  // future conflict
717  // checking within table
718 
719  } // end row find unique id string loop for groupid
720 
721  // done creating conversion map
722  __COUTV__(StringMacros::mapToString(groupidConversionMap));
723 
724  } // end group id conversion map fill
725  else // start uid conversion map fill
726  {
727  for(unsigned int rb = 0; rb < sourceViewB.getNumberOfRows(); ++rb)
728  {
729  found = false;
730 
731  for(ra = 0; ra < sourceViewA.getDataView().size(); ++ra)
732  if(sourceViewA.getValueAsString(ra, cb) == sourceViewB.getValueAsString(rb, cb))
733  {
734  found = true;
735  break;
736  }
737 
738  if(!found)
739  continue;
740 
741  // found conflict
742  __COUT__ << "found conflict: " << getTableName() << "/" << sourceViewB.getDataView()[rb][cb] << __E__;
743 
744  // extract starting uniqueId number
745  {
746  const std::string& str = sourceViewB.getDataView()[rb][cb];
747  numericStartIndex = str.size();
748 
749  // find first non-numeric character
750  while(numericStartIndex - 1 < str.size() && str[numericStartIndex - 1] >= '0' && str[numericStartIndex - 1] <= '9')
751  --numericStartIndex;
752 
753  if(numericStartIndex < str.size())
754  {
755  uniqueId = atoi(str.substr(numericStartIndex).c_str()) + 1;
756  uniqueIdBase = str.substr(0, numericStartIndex);
757  }
758  else
759  {
760  uniqueId = 0;
761  uniqueIdBase = str;
762  }
763 
764  __COUTV__(uniqueIdBase);
765  __COUTV__(uniqueId);
766  } // end //extract starting uniqueId number
767 
768  // find unique id string
769  {
770  sprintf(indexString, "%u", uniqueId);
771  uniqueIdString = uniqueIdBase + indexString;
772  __COUTV__(uniqueIdString);
773 
774  found = false;
775  // check converted records and source A and B for conflicts
776  for(ra = 0; !found && ra < sourceViewA.getDataView().size(); ++ra)
777  if(sourceViewA.getValueAsString(ra, cb) == uniqueIdString)
778  found = true;
779  for(ra = 0; !found && ra < sourceViewB.getDataView().size(); ++ra)
780  if(ra == rb)
781  continue; // skip record in question
782  else if(sourceViewB.getValueAsString(ra, cb) == uniqueIdString)
783  found = true;
784  for(ra = 0; !found && ra < localConvertedIds.size(); ++ra)
785  if(localConvertedIds[ra] == uniqueIdString)
786  found = true;
787 
788  while(found) // while conflict, change id
789  {
790  ++uniqueId;
791  sprintf(indexString, "%u", uniqueId);
792  uniqueIdString = uniqueIdBase + indexString;
793  __COUTV__(uniqueIdString);
794 
795  found = false;
796  // check converted records and source A and B for conflicts
797  for(ra = 0; !found && ra < sourceViewA.getDataView().size(); ++ra)
798  if(sourceViewA.getValueAsString(ra, cb) == uniqueIdString)
799  found = true;
800  for(ra = 0; !found && ra < sourceViewB.getDataView().size(); ++ra)
801  if(ra == rb)
802  continue; // skip record in question
803  else if(sourceViewB.getValueAsString(ra, cb) == uniqueIdString)
804  found = true;
805  for(ra = 0; !found && ra < localConvertedIds.size(); ++ra)
806  if(localConvertedIds[ra] == uniqueIdString)
807  found = true;
808  }
809  } // end find unique id string
810 
811  // have unique id string now
812  __COUTV__(uniqueIdString);
813 
814  uidConversionMap[std::pair<std::string /*original table*/, std::string /*original uidB*/>(
815  getTableName(), sourceViewB.getValueAsString(rb, cb))] = uniqueIdString;
816  localConvertedIds.push_back(uniqueIdString); // save to vector for
817  // future conflict
818  // checking within table
819 
820  } // end row find unique id string loop
821 
822  // done creating conversion map
823  __COUTV__(StringMacros::mapToString(uidConversionMap));
824  }
825 
826  } // end column find unique id string loop
827 
828  } // end rename conversion map create
829  else
830  __COUT__ << "Not filling record conversion map." << __E__;
831 
832  if(!applyRecordConversionMaps)
833  {
834  __COUT__ << "Not applying record conversion map." << __E__;
835  return TableVersion(); // return invalid
836  }
837  else
838  {
839  __COUT__ << "Applying record conversion map." << __E__;
840  __COUTV__(StringMacros::mapToString(uidConversionMap));
841  __COUTV__(StringMacros::mapToString(groupidConversionMap));
842  }
843 
844  // if destinationVersion is INVALID, creates next available temporary version
845  destinationVersion = createTemporaryView(TableVersion(), destinationVersion);
846 
847  __COUT__ << "Merging from (A) " << sourceViewA.getTableName() << "_v" << sourceViewA.getVersion() << " and (B) " << sourceViewB.getTableName() << "_v"
848  << sourceViewB.getVersion() << " to " << getTableName() << "_v" << destinationVersion << " with approach '" << mergeApproach << ".'" << __E__;
849 
850  // if the merge fails then delete the destinationVersion view
851  try
852  {
853  // start with a copy of source view A
854 
855  tableViews_.emplace(std::make_pair(destinationVersion,TableView(getTableName())));
856  TableView* destinationView = &(tableViews_.at(destinationVersion).copy(
857  sourceViewA, destinationVersion, author));
858 
859  unsigned int destRow, destSize = destinationView->getDataView().size();
860  unsigned int cb;
861  bool found;
862  std::map<std::pair<std::string /*original table*/, std::string /*original uidB*/>, std::string /*converted uidB*/>::iterator uidConversionIt;
863  std::map<std::pair<std::string /*original table*/, std::pair<std::string /*group linkid*/, std::string /*original gidB*/> >,
864  std::string /*converted uidB*/>::iterator groupidConversionIt;
865 
866  bool linkIsGroup;
867  std::pair<unsigned int /*link col*/, unsigned int /*link id col*/> linkPair;
868  std::string strb;
869  size_t stri;
870 
871  unsigned int colUID = mockupTableView_.getColUID(); // setup UID column
872 
873  // handle merger with conflicts consideration
874  for(unsigned int rb = 0; rb < sourceViewB.getNumberOfRows(); ++rb)
875  {
876  if(mergeApproach == "Rename")
877  {
878  // rename -- All records from both groups are maintained, but
879  // conflicts from B are renamed. Must maintain a map of
880  // UIDs that are remapped to new name for groupB,
881  // because linkUID fields must be preserved.
882 
883  // conflict does not matter (because record conversion map is already
884  // created, always take and append the B record copy row from B to new
885  // row
886  destRow = destinationView->copyRows(
887  author, sourceViewB, rb, 1 /*srcRowsToCopy*/, -1 /*destOffsetRow*/, generateUniqueDataColumns /*generateUniqueDataColumns*/);
888 
889  // check every column and remap conflicting names
890 
891  for(cb = 0; cb < sourceViewB.getNumberOfColumns(); ++cb)
892  {
893  if(sourceViewB.getColumnInfo(cb).isChildLink())
894  continue; // skip link columns that have table name
895  else if(sourceViewB.getColumnInfo(cb).isChildLinkUID())
896  {
897  __COUT__ << "Checking UID link... col=" << cb << __E__;
898  sourceViewB.getChildLink(cb, linkIsGroup, linkPair);
899 
900  // if table and uid are in conversion map, convert
901  if((uidConversionIt = uidConversionMap.find(std::pair<std::string /*original table*/, std::string /*original uidB*/>(
902  sourceViewB.getValueAsString(rb, linkPair.first), sourceViewB.getValueAsString(rb, linkPair.second)))) !=
903  uidConversionMap.end())
904  {
905  __COUT__ << "Found entry to remap: " << sourceViewB.getDataView()[rb][linkPair.second] << " ==> " << uidConversionIt->second
906  << __E__;
907  destinationView->setValueAsString(uidConversionIt->second, destRow, linkPair.second);
908  }
909  }
910  else if(sourceViewB.getColumnInfo(cb).isChildLinkGroupID())
911  {
912  __COUT__ << "Checking GroupID link... col=" << cb << __E__;
913  sourceViewB.getChildLink(cb, linkIsGroup, linkPair);
914 
915  // if table and uid are in conversion map, convert
916  if((groupidConversionIt = groupidConversionMap.find(
917  std::pair<std::string /*original table*/, std::pair<std::string /*group linkid*/, std::string /*original gidB*/> >(
918  sourceViewB.getValueAsString(rb, linkPair.first),
919  std::pair<std::string /*group linkid*/, std::string /*original gidB*/>(
920  sourceViewB.getColumnInfo(cb).getChildLinkIndex(), sourceViewB.getValueAsString(rb, linkPair.second))))) !=
921  groupidConversionMap.end())
922  {
923  __COUT__ << "Found entry to remap: " << sourceViewB.getDataView()[rb][linkPair.second] << " ==> " << groupidConversionIt->second
924  << __E__;
925  destinationView->setValueAsString(groupidConversionIt->second, destRow, linkPair.second);
926  }
927  }
928  else if(sourceViewB.getColumnInfo(cb).isUID())
929  {
930  __COUT__ << "Checking UID... col=" << cb << __E__;
931  if((uidConversionIt = uidConversionMap.find(std::pair<std::string /*original table*/, std::string /*original uidB*/>(
932  getTableName(), sourceViewB.getValueAsString(rb, cb)))) != uidConversionMap.end())
933  {
934  __COUT__ << "Found entry to remap: " << sourceViewB.getDataView()[rb][cb] << " ==> " << uidConversionIt->second << __E__;
935  destinationView->setValueAsString(uidConversionIt->second, destRow, cb);
936  }
937  }
938  else if(sourceViewB.getColumnInfo(cb).isGroupID())
939  {
940  __COUT__ << "Checking GroupID... col=" << cb << __E__;
941  if((groupidConversionIt = groupidConversionMap.find(
942  std::pair<std::string /*original table*/, std::pair<std::string /*group linkid*/, std::string /*original gidB*/> >(
943  getTableName(),
944  std::pair<std::string /*group linkid*/, std::string /*original gidB*/>(sourceViewB.getColumnInfo(cb).getChildLinkIndex(),
945  sourceViewB.getValueAsString(rb, cb))))) !=
946  groupidConversionMap.end())
947  {
948  __COUT__ << "Found entry to remap: " << sourceViewB.getDataView()[rb][cb] << " ==> " << groupidConversionIt->second << __E__;
949  destinationView->setValueAsString(groupidConversionIt->second, destRow, cb);
950  }
951  }
952  else
953  {
954  // look for text link to a Table/UID in the map
955  strb = sourceViewB.getValueAsString(rb, cb);
956  if(strb.size() > getTableName().size() + 2 && strb[0] == '/')
957  {
958  // check for linked name
959  __COUT__ << "Checking col" << cb << " " << strb << __E__;
960 
961  // see if there is an entry in p
962  for(const auto& mapPairToPair : uidConversionMap)
963  {
964  if((stri = strb.find(mapPairToPair.first.first + "/" + mapPairToPair.first.second)) != std::string::npos)
965  {
966  __COUT__ << "Found a text link match (stri=" << stri << ")! "
967  << (mapPairToPair.first.first + "/" + mapPairToPair.first.second) << " ==> " << mapPairToPair.second << __E__;
968 
969  // insert mapped substitution into string
970  destinationView->setValueAsString(
971  strb.substr(0, stri) + (mapPairToPair.first.first + "/" + mapPairToPair.first.second) +
972  strb.substr(stri + (mapPairToPair.first.first + "/" + mapPairToPair.first.second).size()),
973  destRow,
974  cb);
975 
976  __COUT__ << "Found entry to remap: " << sourceViewB.getDataView()[rb][cb] << " ==> "
977  << destinationView->getDataView()[destRow][cb] << __E__;
978  break;
979  }
980  } // end uid conversion map loop
981  }
982  }
983  } // end column loop over B record
984 
985  continue;
986  } // end rename, no-conflict handling
987 
988  // if here, then not doing rename, so conflicts matter
989 
990  found = false;
991 
992  for(destRow = 0; destRow < destSize; ++destRow)
993  if(destinationView->getValueAsString(destRow, colUID) == sourceViewB.getValueAsString(rb, colUID))
994  {
995  found = true;
996  break;
997  }
998  if(!found) // no conflict
999  {
1000  __COUT__ << "No " << mergeApproach << " conflict: " << __E__;
1001 
1002  if(mergeApproach == "replace" || mergeApproach == "skip")
1003  {
1004  // no conflict so append the B record
1005  // copy row from B to new row
1006  destinationView->copyRows(author, sourceViewB, rb, 1 /*srcRowsToCopy*/);
1007  }
1008  else
1009 
1010  continue;
1011  } // end no-conflict handling
1012 
1013  // if here, then there was a conflict
1014 
1015  __COUT__ << "found " << mergeApproach << " conflict: " << sourceViewB.getDataView()[rb][colUID] << __E__;
1016 
1017  if(mergeApproach == "replace")
1018  {
1019  // replace -- Any UID conflicts for a record are replaced by the
1020  // record from group B.
1021 
1022  // delete row in destination
1023  destinationView->deleteRow(destRow--); // delete row and back up pointer
1024  --destSize;
1025 
1026  // append the B record now
1027  // copy row from B to new row
1028  destinationView->copyRows(author, sourceViewB, rb, 1 /*srcRowsToCopy*/);
1029  }
1030  // else if (mergeApproach == "skip") then do nothing with conflicting B record
1031  }
1032 
1033  destinationView->print();
1034  }
1035  catch(...) // if the copy fails then delete the destinationVersion view
1036  {
1037  __COUT_ERR__ << "Failed to merge " << sourceViewA.getTableName() << "_v" << sourceViewA.getVersion() << " and " << sourceViewB.getTableName() << "_v"
1038  << sourceViewB.getVersion() << " into " << getTableName() << "_v" << destinationVersion << __E__;
1039  __COUT_WARN__ << "Deleting the failed destination version " << destinationVersion << __E__;
1040  eraseView(destinationVersion);
1041  throw; // and rethrow
1042  }
1043 
1044  return destinationVersion;
1045 } // end mergeViews
1046 
1047 //==============================================================================
1048 // copyView
1049 // copies source view (including version) and places in self
1050 // as destination temporary version.
1051 // if destination version is invalid, then next available temporary version is chosen
1052 // if conflict, throw exception
1053 //
1054 // Returns version of new temporary view that was created.
1055 TableVersion TableBase::copyView(const TableView& sourceView, TableVersion destinationVersion, const std::string& author)
1056 {
1057  // check that column sizes match
1058  if(sourceView.getNumberOfColumns() != mockupTableView_.getNumberOfColumns())
1059  {
1060  __SS__ << "Error! Number of Columns of source view must match destination "
1061  "mock-up view."
1062  << "Dimension of source is [" << sourceView.getNumberOfColumns() << "] and of destination mockup is [" << mockupTableView_.getNumberOfColumns()
1063  << "]." << __E__;
1064  __SS_THROW__;
1065  }
1066 
1067  // check for destination version confict
1068  if(!destinationVersion.isInvalid() && tableViews_.find(destinationVersion) != tableViews_.end())
1069  {
1070  __SS__ << "Error! Asked to copy a view with a conflicting version: " << destinationVersion << __E__;
1071  __SS_THROW__;
1072  }
1073 
1074  // if destinationVersion is INVALID, creates next available temporary version
1075  destinationVersion = createTemporaryView(TableVersion(), destinationVersion);
1076 
1077  __COUT__ << "Copying from " << sourceView.getTableName() << "_v" << sourceView.getVersion() << " to " << getTableName() << "_v" << destinationVersion
1078  << __E__;
1079 
1080  try
1081  {
1082  tableViews_.emplace(std::make_pair(destinationVersion,TableView(tableName_)));
1083  tableViews_.at(destinationVersion).copy(sourceView, destinationVersion, author);
1084  }
1085  catch(...) // if the copy fails then delete the destinationVersion view
1086  {
1087  __COUT_ERR__ << "Failed to copy from " << sourceView.getTableName() << "_v" << sourceView.getVersion() << " to " << getTableName() << "_v"
1088  << destinationVersion << __E__;
1089  __COUT_WARN__ << "Deleting the failed destination version " << destinationVersion << __E__;
1090  eraseView(destinationVersion);
1091  throw; // and rethrow
1092  }
1093 
1094  return destinationVersion;
1095 } // end copyView()
1096 
1097 //==============================================================================
1098 // createTemporaryView
1099 // -1, from MockUp, else from valid view version
1100 // destTemporaryViewVersion is starting point for search for available temporary
1101 // versions. if destTemporaryViewVersion is invalid, starts search at
1102 // TableVersion::getNextTemporaryVersion().
1103 // returns new temporary version number (which is always negative)
1104 TableVersion TableBase::createTemporaryView(TableVersion sourceViewVersion, TableVersion destTemporaryViewVersion)
1105 {
1106  //__COUT__ << "Table: " << getTableName() << __E__;
1107 
1108  //__COUT__ << "Num of Views: " << tableViews_.size()
1109  // << " (Temporary Views: " << (tableViews_.size() - getNumberOfStoredViews())
1110  // << ")" << __E__;
1111 
1112  TableVersion tmpVersion = destTemporaryViewVersion;
1113  if(tmpVersion.isInvalid())
1114  tmpVersion = TableVersion::getNextTemporaryVersion();
1115  while(isStored(tmpVersion) && // find a new valid temporary version
1116  !(tmpVersion = TableVersion::getNextTemporaryVersion(tmpVersion)).isInvalid())
1117  ;
1118  if(isStored(tmpVersion) || tmpVersion.isInvalid())
1119  {
1120  __SS__ << "Invalid destination temporary version: " << destTemporaryViewVersion << ". Expected next temporary version < " << tmpVersion << __E__;
1121  __COUT_ERR__ << ss.str();
1122  __SS_THROW__;
1123  }
1124 
1125  if(sourceViewVersion == TableVersion::INVALID || // use mockup if sourceVersion is -1 or not found
1126  tableViews_.find(sourceViewVersion) == tableViews_.end())
1127  {
1128  if(sourceViewVersion != -1)
1129  {
1130  __SS__ << "ERROR: sourceViewVersion " << sourceViewVersion << " not found. "
1131  << "Invalid source version. Version requested is not stored (yet?) or "
1132  "does not exist."
1133  << __E__;
1134  __COUT_ERR__ << ss.str();
1135  __SS_THROW__;
1136  }
1137  __COUT__ << "Using Mock-up view" << __E__;
1138  tableViews_.emplace(std::make_pair(tmpVersion,TableView(tableName_)));
1139  tableViews_.at(tmpVersion).copy(mockupTableView_, tmpVersion, mockupTableView_.getAuthor());
1140  }
1141  else
1142  {
1143  try // do not allow init to throw an exception here..
1144  { // it's ok to copy invalid data, the user may be trying to change it
1145  tableViews_.emplace(std::make_pair(tmpVersion,TableView(tableName_)));
1146  tableViews_.at(tmpVersion).copy(
1147  tableViews_.at(sourceViewVersion), tmpVersion,
1148  tableViews_.at(sourceViewVersion).getAuthor());
1149  }
1150  catch(...)
1151  {
1152  __COUT_WARN__ << "createTemporaryView() Source view failed init(). "
1153  << "This is being ignored (hopefully the new copy is being fixed)." << __E__;
1154  }
1155  }
1156 
1157  return tmpVersion;
1158 } // end createTemporaryView()
1159 
1160 //==============================================================================
1161 // getNextAvailableTemporaryView
1162 // TableVersion::INVALID is always MockUp
1163 // returns next available temporary version number (which is always negative)
1164 TableVersion TableBase::getNextTemporaryVersion() const
1165 {
1166  TableVersion tmpVersion;
1167 
1168  // std::map guarantees versions are in increasing order!
1169  if(tableViews_.size() != 0 && tableViews_.begin()->first.isTemporaryVersion())
1170  tmpVersion = TableVersion::getNextTemporaryVersion(tableViews_.begin()->first);
1171  else
1172  tmpVersion = TableVersion::getNextTemporaryVersion();
1173 
1174  // verify tmpVersion is ok
1175  if(isStored(tmpVersion) || tmpVersion.isInvalid() || !tmpVersion.isTemporaryVersion())
1176  {
1177  __SS__ << "Invalid destination temporary version: " << tmpVersion << __E__;
1178  __COUT_ERR__ << ss.str();
1179  __SS_THROW__;
1180  }
1181  return tmpVersion;
1182 }
1183 
1184 //==============================================================================
1185 // getNextVersion
1186 // returns next available new version
1187 // the implication is any version number equal or greater is available.
1188 TableVersion TableBase::getNextVersion() const
1189 {
1190  TableVersion tmpVersion;
1191 
1192  // std::map guarantees versions are in increasing order!
1193  if(tableViews_.size() != 0 && !tableViews_.rbegin()->first.isTemporaryVersion())
1194  tmpVersion = TableVersion::getNextVersion(tableViews_.rbegin()->first);
1195  else
1196  tmpVersion = TableVersion::getNextVersion();
1197 
1198  // verify tmpVersion is ok
1199  if(isStored(tmpVersion) || tmpVersion.isInvalid() || tmpVersion.isTemporaryVersion())
1200  {
1201  __SS__ << "Invalid destination next version: " << tmpVersion << __E__;
1202  __COUT_ERR__ << ss.str();
1203  __SS_THROW__;
1204  }
1205  return tmpVersion;
1206 }
1207 
1208 //==============================================================================
1209 // getTemporaryView
1210 // must be a valid temporary version, and the view must be stored in table.
1211 // temporary version indicates it has not been saved to database and assigned a version
1212 // number
1213 TableView* TableBase::getTemporaryView(TableVersion temporaryVersion)
1214 {
1215  if(!temporaryVersion.isTemporaryVersion() || !isStored(temporaryVersion))
1216  {
1217  __SS__ << getTableName() << ":: Error! Temporary version not found!" << __E__;
1218  __COUT_ERR__ << ss.str();
1219  __SS_THROW__;
1220  }
1221  return &tableViews_.at(temporaryVersion);
1222 }
1223 
1224 //==============================================================================
1225 // convertToCaps
1226 // static utility for converting table and column names to the caps version
1227 // throw std::runtime_error if not completely alpha-numeric input
1228 std::string TableBase::convertToCaps(std::string& str, bool isTableName)
1229 {
1230  // append Table to be nice to user
1231  unsigned int configPos = (unsigned int)std::string::npos;
1232  if(isTableName && (configPos = str.find("Table")) != str.size() - strlen("Table"))
1233  str += "Table";
1234 
1235  // create all caps name and validate
1236  // only allow alpha names with Table at end
1237  std::string capsStr = "";
1238  for(unsigned int c = 0; c < str.size(); ++c)
1239  if(str[c] >= 'A' && str[c] <= 'Z')
1240  {
1241  // add _ before table and if lower case to uppercase
1242  if(c == configPos || (c && str[c - 1] >= 'a' && str[c - 1] <= 'z') || // if this is a new start of upper case
1243  (c && str[c - 1] >= 'A' && str[c - 1] <= 'Z' && // if this is a new start from running caps
1244  c + 1 < str.size() && str[c + 1] >= 'a' && str[c + 1] <= 'z'))
1245  capsStr += "_";
1246  capsStr += str[c];
1247  }
1248  else if(str[c] >= 'a' && str[c] <= 'z')
1249  capsStr += char(str[c] - 32); // capitalize
1250  else if(str[c] >= '0' && str[c] <= '9')
1251  capsStr += str[c]; // allow numbers
1252  else // error! non-alpha
1253  __THROW__(std::string("TableBase::convertToCaps::") + "Invalid character found in name (allowed: A-Z, a-z, 0-9):" + str);
1254 
1255  return capsStr;
1256 }
TableVersion mergeViews(const TableView &sourceViewA, const TableView &sourceViewB, TableVersion destinationVersion, const std::string &author, const std::string &mergeApproach, std::map< std::pair< std::string, std::string >, std::string > &uidConversionMap, std::map< std::pair< std::string, std::pair< std::string, std::string > >, std::string > &groupidConversionMap, bool fillRecordConversionMaps, bool applyRecordConversionMaps, bool generateUniqueDataColumns=false)
Definition: TableBase.cc:534