otsdaq  3.09.00
ConfigurationTree.cc
1 #include "otsdaq/ConfigurationInterface/ConfigurationTree.h"
2 
3 #include <typeinfo>
4 
5 #include "otsdaq/ConfigurationInterface/ConfigurationManager.h"
6 #include "otsdaq/Macros/StringMacros.h"
7 #include "otsdaq/TableCore/TableBase.h"
8 
9 using namespace ots;
10 
11 #undef __MF_SUBJECT__
12 #define __MF_SUBJECT__ "ConfigurationTree"
13 
14 const std::string ConfigurationTree::DISCONNECTED_VALUE = "X";
15 const std::string ConfigurationTree::VALUE_TYPE_DISCONNECTED = "Disconnected";
16 const std::string ConfigurationTree::VALUE_TYPE_NODE = "Node";
17 const std::string ConfigurationTree::ROOT_NAME = "/";
18 time_t ConfigurationTree::LAST_NODE_DUMP_TIME = 0;
19 
20 //==============================================================================
22  : configMgr_(0)
23  , table_(0)
24  , groupId_("")
25  , linkParentTable_(0)
26  , linkColName_("")
27  , linkColValue_("")
28  , linkBackRow_(0)
29  , linkBackCol_(0)
30  , disconnectedTargetName_("")
31  , disconnectedLinkID_("")
32  , childLinkIndex_("")
33  , row_(0)
34  , col_(0)
35  , tableView_(0)
36 {
37  //__COUT__ << __E__;
38  //__COUT__ << "EMPTY CONSTRUCTOR ConfigManager: " << configMgr_ << " configuration: "
39  //<< table_ << __E__;
40 } // end empty constructor
41 
42 //==============================================================================
44  const TableBase* const& table)
45  : ConfigurationTree(configMgr,
46  table,
47  "" /*groupId_*/,
48  0 /*linkParentTable_*/,
49  "" /*linkColName_*/,
50  "" /*linkColValue_*/,
51  TableView::INVALID /*linkBackRow_*/,
52  TableView::INVALID /*linkBackCol_*/,
53  "" /*disconnectedTargetName_*/,
54  "" /*disconnectedLinkID_*/,
55  "" /*childLinkIndex_*/,
56  TableView::INVALID /*row_*/,
57  TableView::INVALID /*col_*/)
58 {
59  //__COUT__ << __E__;
60  //__COUT__ << "SHORT CONTRUCTOR ConfigManager: " << configMgr_ << " configuration: "
61  //<< table_ << __E__;
62 } // end short constructor
63 
64 //==============================================================================
66  const TableBase* const& table,
67  const std::string& groupId,
68  const TableBase* const& linkParentConfig,
69  const std::string& linkColName,
70  const std::string& linkColValue,
71  const unsigned int linkBackRow,
72  const unsigned int linkBackCol,
73  const std::string& disconnectedTargetName,
74  const std::string& disconnectedLinkID,
75  const std::string& childLinkIndex,
76  const unsigned int row,
77  const unsigned int col)
78  : configMgr_(configMgr)
79  , table_(table)
80  , groupId_(groupId)
81  , linkParentTable_(linkParentConfig)
82  , linkColName_(linkColName)
83  , linkColValue_(linkColValue)
84  , linkBackRow_(linkBackRow)
85  , linkBackCol_(linkBackCol)
86  , disconnectedTargetName_(disconnectedTargetName)
87  , disconnectedLinkID_(disconnectedLinkID)
88  , childLinkIndex_(childLinkIndex)
89  , row_(row)
90  , col_(col)
91  , tableView_(0)
92 {
93  if(!configMgr_)
94  {
95  __SS__ << "Invalid empty pointer given to tree!\n"
96  << "\n\tconfigMgr_=" << configMgr_ << "\n\tconfiguration_=" << table_
97  << "\n\tconfigView_=" << tableView_ << __E__;
98 
99  ss << nodeDump() << __E__;
100  __SS_THROW__;
101  }
102 
103  if(table_)
104  tableView_ = &(table_->getView());
105 
106  // verify UID column exists
107  if(tableView_ && tableView_->getColumnInfo(tableView_->getColUID()).getType() !=
109  {
110  __SS__ << "Missing UID column (must column of type "
112  << ") in config view : " << tableView_->getTableName() << __E__;
113 
114  ss << nodeDump() << __E__;
115  __SS_THROW__;
116  }
117 } // end full constructor
118 
119 //==============================================================================
122 {
123  //__COUT__ << __E__;
124 } // end destructor
125 
126 //==============================================================================
132 void ConfigurationTree::print(const unsigned int& depth, std::ostream& out) const
133 {
134  recursivePrint(*this, depth, out, "\t");
135 } // end print()
136 
137 //==============================================================================
138 void ConfigurationTree::recursivePrint(const ConfigurationTree& t,
139  unsigned int depth,
140  std::ostream& out,
141  std::string space)
142 {
143  if(t.isValueNode())
144  out << space << t.getValueName() << " :\t" << t.getValueAsString() << __E__;
145  else
146  {
147  if(t.isLinkNode())
148  {
149  out << space << t.getValueName();
150  if(t.isDisconnected())
151  {
152  out << " :\t" << t.getValueAsString() << __E__;
153  return;
154  }
155  out << " (" << (t.isGroupLinkNode() ? "Group" : "U")
156  << "ID=" << t.getValueAsString() << ") : " << __E__;
157  }
158  else
159  out << space << t.getValueAsString() << " : " << __E__;
160 
161  // if depth>=1 print all children
162  // child.print(depth-1)
163  if(depth >= 1)
164  {
165  auto C = t.getChildren();
166  if(!C.empty())
167  out << space << "{" << __E__;
168  for(auto& c : C)
169  recursivePrint(c.second, depth - 1, out, space + " ");
170  if(!C.empty())
171  out << space << "}" << __E__;
172  }
173  }
174 } // end recursivePrint()
175 
176 //==============================================================================
177 std::string ConfigurationTree::handleValidateValueForColumn(
178  const TableView* configView,
179  std::string value,
180  unsigned int col,
182 {
183  if(!configView)
184  {
185  __SS__ << "Null configView" << __E__;
186 
187  ss << nodeDump() << __E__;
188  __SS_THROW__;
189  }
190  __COUT__ << "handleValidateValueForColumn<string>" << __E__;
191  return configView->validateValueForColumn(value, col);
192 } // end std::string handleValidateValueForColumn()
193 
194 //==============================================================================
199 void ConfigurationTree::getValue(std::string& value) const
200 {
201  //__COUT__ << row_ << " " << col_ << " p: " << tableView_<< __E__;
202 
203  if(row_ != TableView::INVALID &&
204  col_ != TableView::INVALID) // this node is a value node
205  {
206  // attempt to interpret the value as a tree node path itself
207  try
208  {
209  ConfigurationTree valueAsTreeNode = getValueAsTreeNode();
210  // valueAsTreeNode.getValue<T>(value);
211  __COUT__ << "Success following path to tree node!" << __E__;
212  // value has been interpreted as a tree node value
213  // now verify result under the rules of this column
214  // if(typeid(std::string) == typeid(value))
215 
216  // Note: want to interpret table value as though it is in column of different
217  // table this allows a number to be read as a string, for example, without
218  // exceptions
219  value = tableView_->validateValueForColumn(valueAsTreeNode.getValueAsString(),
220  col_);
221 
222  __COUT__ << "Successful value!" << __E__;
223 
224  // else
225  // value = tableView_->validateValueForColumn<T>(
226  // valueAsTreeNode.getValueAsString(),col_);
227 
228  return;
229  }
230  catch(...) // tree node path interpretation failed
231  {
232  //__COUT__ << "Invalid path, just returning normal value." << __E__;
233  }
234 
235  // else normal return
236  tableView_->getValue(value, row_, col_);
237  }
238  else if(row_ == TableView::INVALID &&
239  col_ == TableView::INVALID) // this node is table node maybe with groupId
240  {
241  if(isLinkNode() && isDisconnected())
242  value = (groupId_ == "") ? getValueName() : groupId_; // a disconnected link
243  // still knows its table
244  // name or groupId
245  else
246  value = (groupId_ == "") ? table_->getTableName() : groupId_;
247  }
248  else if(row_ == TableView::INVALID)
249  {
250  __SS__ << "Malformed ConfigurationTree" << __E__;
251  __SS_THROW__;
252  }
253  else if(col_ == TableView::INVALID) // this node is uid node
254  tableView_->getValue(value, row_, tableView_->getColUID());
255  else
256  {
257  __SS__ << "Impossible." << __E__;
258  __SS_THROW__;
259  }
260 } // end getValue()
261 
262 //==============================================================================
275 std::string ConfigurationTree::getValue() const
276 {
277  std::string value;
279  return value;
280 } // end getValue()
281 //==============================================================================
294 std::string ConfigurationTree::getValueWithDefault(const std::string& defaultValue) const
295 {
296  if(isDefaultValue())
297  return defaultValue;
298  else
300 } // end getValueWithDefault()
301 
302 //==============================================================================
307 void ConfigurationTree::getValueAsBitMap(
309 {
310  __COUTS__(2) << row_ << " " << col_ << " p: " << tableView_ << __E__;
311 
312  if(row_ != TableView::INVALID &&
313  col_ != TableView::INVALID) // this node is a value node
314  {
315  std::string bitmapString;
316  tableView_->getValue(bitmapString, row_, col_);
317 
318  auto bmp = tableView_->getColumnInfo(col_).getBitMapInfo();
319 
320  __COUTVS__(2, bitmapString);
321  if(bitmapString == TableViewColumnInfo::DATATYPE_STRING_DEFAULT ||
322  bitmapString == TableViewColumnInfo::DATATYPE_STRING_ALT_DEFAULT)
323  {
324  bitmap.isDefault_ = true;
325  //create empty bitmap so size is known
326  for(unsigned int r = 0; r < bmp.numOfRows_; ++r)
327  {
328  bitmap.bitmap_.push_back(std::vector<std::string>());
329  for(unsigned int c = 0; c < bmp.numOfColumns_; ++c)
330  bitmap.bitmap_[r].push_back(std::string());
331  }
332  return;
333  }
334  else
335  bitmap.isDefault_ = false;
336 
337  std::string value;
338 
339  std::map<std::string, size_t> valueMap;
340  std::vector<std::string> valueList;
341  if(bmp.mapsToStrings_)
342  {
343  valueList = StringMacros::getVectorFromString(bmp.mapToStrings_);
344  __COUTVS__(2, StringMacros::vectorToString(valueList));
345  for(size_t i = 0; i < valueList.size(); ++i)
346  valueMap.emplace(std::make_pair(valueList[i], i));
347  __COUTVS__(2, StringMacros::mapToString(valueMap));
348  }
349 
350  // extract bit map
351  {
352  bitmap.bitmap_.clear();
353  int row = -1;
354  bool openRow = false;
355  unsigned int startInt = -1;
356  for(unsigned int i = 0; i < bitmapString.length(); i++)
357  {
358  __COUTVS__(2, bitmapString[i]);
359  __COUTVS__(2, row);
360  __COUTVS__(2, openRow);
361  __COUTVS__(2, startInt);
362  __COUTVS__(2, i);
363 
364  if(!openRow) // need start of row
365  {
366  if(bitmapString[i] == '[')
367  { // open a new row
368  openRow = true;
369  ++row;
370  bitmap.bitmap_.push_back(std::vector<std::string>());
371  }
372  else if(bitmapString[i] == ']')
373  {
374  break; // ending bracket, done with string
375  }
376  else if(bitmapString[i] == ',') // end characters found not within
377  // row
378  {
379  //ignore ,'s outside of row [ ] array
380  continue;
381  }
382  }
383  else if(startInt == (unsigned int)-1) // need to find start of number
384  {
385  if(bitmapString[i] == ']') // found end of row, instead of start of
386  // number, assume row ended
387  {
388  openRow = false;
389  }
390  else if((bitmapString[i] == '-') || (bitmapString[i] == '+') ||
391  (bitmapString[i] >= '0' &&
392  bitmapString[i] <= '9') || // found start of number
393  (bmp.mapsToStrings_ && bitmapString[i] >= 'a' &&
394  bitmapString[i] <=
395  'z') || // found start of string in map-to-string mode
396  (bmp.mapsToStrings_ && bitmapString[i] >= 'A' &&
397  bitmapString[i] <= 'Z'))
398  {
399  startInt = i;
400  }
401  else if(bitmapString[i] == ',') // comma found without number
402  {
403  __SS__ << "Too many ',' characters in bit map configuration"
404  << __E__;
405 
406  ss << nodeDump() << __E__;
407  __SS_ONLY_THROW__;
408  }
409  }
410  else
411  {
412  // looking for end of number (or string map)
413 
414  if(bitmapString[i] ==
415  ']') // found end of row, assume row and number ended
416  {
417  openRow = false;
418 
419  //drop space or end quote
420  unsigned int ii = i;
421  bool wasEndQuote = false;
422  while(ii > startInt && (bitmapString[ii - 1] == ' ' ||
423  bitmapString[ii - 1] == '\r' ||
424  bitmapString[ii - 1] == '\n' ||
425  bitmapString[ii - 1] == '\t' ||
426  bitmapString[ii - 1] == '"'))
427  {
428  if(bitmapString[ii - 1] == '"')
429  wasEndQuote = true;
430  --ii; //rewind to last character of string
431  }
432  __COUTVS__(2, bitmapString.substr(startInt, ii - startInt));
433  if(bmp.mapsToStrings_) //convert string to number (since this template function is assumed to handle numbers only; i.e., it is not the special strings version)
434  {
435  try
436  {
437  value = valueMap.at(
438  bitmapString.substr(startInt, ii - startInt));
439  }
440  catch(const std::out_of_range& e)
441  {
442  __SS__ << "Value '"
443  << bitmapString.substr(startInt, ii - startInt)
444  << "' not found in map-to-string list for bitmap "
445  "column "
446  << tableView_->getColumnInfo(col_).getName()
447  << __E__;
448  __COUTVS__(2, wasEndQuote);
449  if(!wasEndQuote)
450  {
451  __COUTS__(2)
452  << "No end-quote, assuming value is integer "
453  "index into map-to-string list."
454  << __E__;
455  size_t index = 0;
456  try
457  {
458  index = std::stoul(
459  bitmapString.substr(startInt, ii - startInt));
460  }
461  catch(...)
462  {
463  ss << "Interpreting value as integer index "
464  "failed for '"
465  << bitmapString.substr(startInt, ii - startInt)
466  << "'." << __E__;
467  __SS_THROW__;
468  }
469  if(index >= valueList.size())
470  {
471  ss << "Interpreting as index [" << index
472  << "] is also out of range for map-to-string "
473  "list of size "
474  << valueList.size() << __E__;
475  __SS_THROW__;
476  }
477  value = valueList[index];
478  }
479  else
480  __SS_THROW__;
481  }
482  __COUTVS__(2, value);
483  }
484  //ignore value map, and return raw string
485  bitmap.bitmap_[row].push_back(
486  bitmapString.substr(startInt, ii - startInt));
487 
488  startInt = -1;
489  }
490  else if(bitmapString[i] == ',') // comma found, assume end of number
491  {
492  //drop space or end quote
493  unsigned int ii = i;
494  bool wasEndQuote = false;
495  while(ii > startInt && (bitmapString[ii - 1] == ' ' ||
496  bitmapString[ii - 1] == '\r' ||
497  bitmapString[ii - 1] == '\n' ||
498  bitmapString[ii - 1] == '\t' ||
499  bitmapString[ii - 1] == '"'))
500  {
501  if(bitmapString[ii - 1] == '"')
502  wasEndQuote = true;
503  --ii; //rewind to last character of string
504  }
505  __COUTVS__(2, bitmapString.substr(startInt, ii - startInt));
506  if(bmp.mapsToStrings_) //convert string to number (since this template function is assumed to handle numbers only; i.e., it is not the special strings version)
507  {
508  try
509  {
510  value = valueMap.at(
511  bitmapString.substr(startInt, ii - startInt));
512  }
513  catch(const std::out_of_range& e)
514  {
515  __SS__ << "Value '"
516  << bitmapString.substr(startInt, ii - startInt)
517  << "' not found in map-to-string list for bitmap "
518  "column "
519  << tableView_->getColumnInfo(col_).getName()
520  << __E__;
521  __COUTVS__(2, wasEndQuote);
522  if(!wasEndQuote)
523  {
524  __COUTS__(2)
525  << "No end-quote, assuming value is integer "
526  "index into map-to-string list."
527  << __E__;
528  size_t index = 0;
529  try
530  {
531  index = std::stoul(
532  bitmapString.substr(startInt, ii - startInt));
533  }
534  catch(...)
535  {
536  ss << "Interpreting value as integer index "
537  "failed for '"
538  << bitmapString.substr(startInt, ii - startInt)
539  << "'." << __E__;
540  __SS_THROW__;
541  }
542  if(index >= valueList.size())
543  {
544  ss << "Interpreting as index [" << index
545  << "] is also out of range for map-to-string "
546  "list of size "
547  << valueList.size() << __E__;
548  __SS_THROW__;
549  }
550  value = valueList[index];
551  }
552  else
553  __SS_THROW__;
554  }
555  __COUTVS__(2, value);
556  }
557  //ignore value map, and return raw string
558  bitmap.bitmap_[row].push_back(
559  bitmapString.substr(startInt, ii - startInt));
560 
561  startInt = -1;
562  }
563  }
564  } //ene main string parsing loop
565 
566  if(TTEST(2))
567  {
568  for(unsigned int r = 0; r < bitmap.bitmap_.size(); ++r)
569  {
570  for(unsigned int c = 0; c < bitmap.bitmap_[r].size(); ++c)
571  {
572  __COUTS__(2)
573  << r << "," << c << " = " << bitmap.bitmap_[r][c] << __E__;
574  }
575  __COUTS__(2) << "================" << __E__;
576  }
577  }
578  }
579 
580  if(bitmap.bitmap_.size() != bmp.numOfRows_ ||
581  (bmp.numOfRows_ && bitmap.bitmap_[0].size() != bmp.numOfColumns_))
582  {
583  __SS__
584  << "Illegal mismatch in number of rows and columns. Extracted data was "
585  << bitmap.bitmap_.size() << " x "
586  << (bitmap.bitmap_.size() ? bitmap.bitmap_[0].size() : 0)
587  << " and the expected size is " << bmp.numOfRows_ << " x "
588  << bmp.numOfColumns_ << __E__;
589  __SS_THROW__;
590  }
591  }
592  else
593  {
594  __SS__ << "Requesting getValue must be on a value node." << __E__;
595 
596  ss << nodeDump() << __E__;
597  __SS_THROW__;
598  }
599 } // end getValueAsBitMap()
600 
601 //==============================================================================
605 ConfigurationTree::BitMap<std::string> ConfigurationTree::getValueAsBitMap() const
606 {
608  ConfigurationTree::getValueAsBitMap(value);
609  return value;
610 } // end getValueAsBitMap()
611 
612 //==============================================================================
616 {
617  if(row_ != TableView::INVALID &&
618  col_ != TableView::INVALID) // this node is a value node
619  return tableView_->getEscapedValueAsString(row_, col_);
620 
621  __SS__ << "Can not get escaped value except from a value node!"
622  << " This node is type '" << getNodeType() << "." << __E__;
623 
624  ss << nodeDump() << __E__;
625  __SS_THROW__;
626 } // end getEscapedValue()
627 
628 //==============================================================================
630 const std::string& ConfigurationTree::getTableName(void) const
631 {
632  if(!table_)
633  {
634  __SS__ << "Can not get configuration name of node with no configuration pointer! "
635  << "Is there a broken link? " << __E__;
636  if(linkParentTable_)
637  {
638  ss << "Error occurred traversing from " << linkParentTable_->getTableName()
639  << " UID '"
640  << linkParentTable_->getView().getValueAsString(
641  linkBackRow_, linkParentTable_->getView().getColUID())
642  << "' at row " << linkBackRow_ << " col '"
643  << linkParentTable_->getView().getColumnInfo(linkBackCol_).getName()
644  << ".'" << __E__;
645 
646  ss << StringMacros::stackTrace() << __E__;
647  }
648 
649  __SS_ONLY_THROW__;
650  }
651  return table_->getTableName();
652 } // end getTableName()
653 
654 //==============================================================================
656 const std::string& ConfigurationTree::getParentTableName(void) const
657 {
658  if(linkParentTable_)
659  return linkParentTable_->getTableName();
660 
661  __SS__ << "Can not get parent table name of node with no parent table pointer! "
662  << "Was this node initialized correctly? " << __E__;
663  ss << ConfigurationTree::nodeDump(true /* forcePrintout */) << __E__;
664  __SS_ONLY_THROW__;
665 } // end getParentTableName()
666 
667 //==============================================================================
669 const std::string& ConfigurationTree::getParentRecordName(void) const
670 {
671  if(linkParentTable_ && linkBackRow_ != TableView::INVALID)
672  {
673  //return parent UID
674  return linkParentTable_->getView()
675  .getDataView()[linkBackRow_][linkParentTable_->getView().getColUID()];
676  }
677 
678  __SS__ << "Can not get parent record name of node without the parent table pointer "
679  << (linkParentTable_ ? "" : "= null ") << "and row (row = " << linkBackRow_
680  << ")! Was this node initialized correctly? " << __E__;
681  ss << ConfigurationTree::nodeDump(true /* forcePrintout */) << __E__;
682  __SS_ONLY_THROW__;
683 } // end getParentRecordName()
684 
685 //==============================================================================
687 const std::string& ConfigurationTree::getParentLinkColumnName(void) const
688 {
689  if(linkParentTable_ && linkBackRow_ != TableView::INVALID &&
690  linkBackCol_ != TableView::INVALID)
691  {
692  //return parent link column name
693  return linkParentTable_->getView().getColumnInfo(linkBackCol_).getName();
694  }
695 
696  __SS__
697  << "Can not get parent link column name of node without the parent table pointer "
698  << (linkParentTable_ ? "" : "= null ") << "and row (row = " << linkBackRow_
699  << ") and col (col = " << linkBackCol_
700  << ")! Was this node initialized correctly? " << __E__;
701  ss << ConfigurationTree::nodeDump(true /* forcePrintout */) << __E__;
702  __SS_ONLY_THROW__;
703 } // end getParentLinkColumnName()
704 
705 //==============================================================================
707 std::string ConfigurationTree::getParentLinkID(void) const
708 {
709  if(linkParentTable_ && linkBackRow_ != TableView::INVALID &&
710  linkBackCol_ != TableView::INVALID)
711  {
712  //get all info associated with the link
713  bool isGroup;
714  std::pair<unsigned int /*link col*/, unsigned int /*link id col*/> linkPair;
715  linkParentTable_->getView().getChildLink(linkBackCol_, isGroup, linkPair);
716 
717  std::string linkId;
718  linkParentTable_->getView().getValue(
719  linkId, linkBackRow_, linkPair.second /* link id col */);
720 
721  return linkId;
722  }
723 
724  __SS__ << "Can not get parent link ID of node without the parent table pointer "
725  << (linkParentTable_ ? "" : "= null ") << "and row (row = " << linkBackRow_
726  << ") and col (col = " << linkBackCol_
727  << ")! Was this node initialized correctly? " << __E__;
728  ss << ConfigurationTree::nodeDump(true /* forcePrintout */) << __E__;
729  __SS_ONLY_THROW__;
730 } // end getParentLinkID()
731 
732 //==============================================================================
735 {
736  if(linkParentTable_ && linkBackRow_ != TableView::INVALID &&
737  linkBackCol_ != TableView::INVALID)
738  {
739  //return parent link index
740  return linkParentTable_->getView()
741  .getColumnInfo(linkBackCol_)
743  }
744 
745  __SS__ << "Can not get parent link index of node without the parent table pointer "
746  << (linkParentTable_ ? "" : "= null ") << "and row (row = " << linkBackRow_
747  << ") and col (col = " << linkBackCol_
748  << ")! Was this node initialized correctly? " << __E__;
749  ss << ConfigurationTree::nodeDump(true /* forcePrintout */) << __E__;
750  __SS_ONLY_THROW__;
751 } // end getParentLinkIndex()
752 
753 //==============================================================================
755 const unsigned int& ConfigurationTree::getNodeRow(void) const
756 {
757  if(isUIDNode() || isValueNode())
758  return row_;
759 
760  __SS__ << "Can only get row from a UID or value node!" << __E__;
761  if(linkParentTable_)
762  {
763  ss << "Error occurred traversing from " << linkParentTable_->getTableName()
764  << " UID '"
765  << linkParentTable_->getView().getValueAsString(
766  linkBackRow_, linkParentTable_->getView().getColUID())
767  << "' at row " << linkBackRow_ << " col '"
768  << linkParentTable_->getView().getColumnInfo(linkBackCol_).getName() << ".'"
769  << __E__;
770 
771  ss << StringMacros::stackTrace() << __E__;
772  }
773 
774  __SS_ONLY_THROW__;
775 
776 } // end getNodeRow()
777 
778 //==============================================================================
783 const std::string& ConfigurationTree::getFieldTableName(void) const
784 {
785  // if link node, need config name from parent
786  if(isLinkNode())
787  {
788  if(!linkParentTable_)
789  {
790  __SS__ << "Can not get configuration name of link node field with no parent "
791  "configuration pointer!"
792  << __E__;
793  ss << nodeDump() << __E__;
794  __SS_ONLY_THROW__;
795  }
796  return linkParentTable_->getTableName();
797  }
798  else
799  return getTableName();
800 } // end getFieldTableName()
801 
802 //==============================================================================
804 const std::string& ConfigurationTree::getDisconnectedTableName(void) const
805 {
806  if(isLinkNode() && isDisconnected())
807  return disconnectedTargetName_;
808 
809  __SS__ << "Can not get disconnected target name of node unless it is a disconnected "
810  "link node!"
811  << __E__;
812 
813  ss << nodeDump() << __E__;
814  __SS_ONLY_THROW__;
815 } // end getDisconnectedTableName()
816 
817 //==============================================================================
819 const std::string& ConfigurationTree::getDisconnectedLinkID(void) const
820 {
821  if(isLinkNode() && isDisconnected())
822  return disconnectedLinkID_;
823 
824  __SS__ << "Can not get disconnected target name of node unless it is a disconnected "
825  "link node!"
826  << __E__;
827 
828  ss << nodeDump() << __E__;
829  __SS_ONLY_THROW__;
830 } // end getDisconnectedLinkID()
831 
832 //==============================================================================
835 {
836  if(!tableView_)
837  {
838  __SS__ << "Can not get configuration version of node with no config view pointer!"
839  << __E__;
840 
841  ss << nodeDump() << __E__;
842  __SS_ONLY_THROW__;
843  }
844  return tableView_->getVersion();
845 } // end getTableVersion()
846 
847 //==============================================================================
850 {
851  if(!tableView_)
852  {
853  __SS__ << "Can not get configuration creation time of node with no config view "
854  "pointer!"
855  << __E__;
856 
857  ss << nodeDump() << __E__;
858  __SS_ONLY_THROW__;
859  }
860  return tableView_->getCreationTime();
861 } // end getTableCreationTime()
862 
863 //==============================================================================
866 std::set<std::string> ConfigurationTree::getSetOfGroupIDs(void) const
867 {
868  if(!isGroupIDNode())
869  {
870  __SS__ << "Can not get set of group IDs of node with value type of '"
871  << getNodeType() << ".' Node must be a GroupID node." << __E__;
872 
873  ss << nodeDump() << __E__;
874  __SS_ONLY_THROW__;
875  }
876 
877  return tableView_->getSetOfGroupIDs(col_, row_);
878 
879 } // end getSetOfGroupIDs()
880 
881 //==============================================================================
885 std::vector<std::string> ConfigurationTree::getFixedChoices(void) const
886 {
887  if(getValueType() != TableViewColumnInfo::TYPE_FIXED_CHOICE_DATA &&
888  getValueType() != TableViewColumnInfo::TYPE_BITMAP_DATA && !isLinkNode())
889  {
890  __SS__ << "Can not get fixed choices of node with value type of '"
891  << getValueType() << ".' Node must be a link or a value node with type '"
892  << TableViewColumnInfo::TYPE_BITMAP_DATA << "' or '"
893  << TableViewColumnInfo::TYPE_FIXED_CHOICE_DATA << ".'" << __E__;
894 
895  ss << nodeDump() << __E__;
896  __SS_ONLY_THROW__;
897  }
898 
899  std::vector<std::string> retVec;
900 
901  if(isLinkNode())
902  {
903  if(!linkParentTable_)
904  {
905  __SS__
906  << "Can not get fixed choices of node with no parent config view pointer!"
907  << __E__;
908 
909  ss << nodeDump() << __E__;
910  __SS_ONLY_THROW__;
911  }
912 
913  //__COUT__ << getChildLinkIndex() << __E__;
914  //__COUT__ << linkColName_ << __E__;
915 
916  // for links, col_ = -1, column c needs to change (to ChildLink column of pair)
917  // get column from parent config pointer
918 
919  const TableView* parentView = &(linkParentTable_->getView());
920  int c = parentView->findCol(linkColName_);
921 
922  std::pair<unsigned int /*link col*/, unsigned int /*link id col*/> linkPair;
923  bool isGroupLink;
924  parentView->getChildLink(c, isGroupLink, linkPair);
925  c = linkPair.first;
926 
927  std::vector<std::string> choices = parentView->getColumnInfo(c).getDataChoices();
928  for(const auto& choice : choices)
929  retVec.push_back(choice);
930 
931  return retVec;
932  }
933 
934  if(!tableView_)
935  {
936  __SS__ << "Can not get fixed choices of node with no config view pointer!"
937  << __E__;
938 
939  ss << nodeDump() << __E__;
940  __SS_ONLY_THROW__;
941  }
942 
943  // return vector of default + data choices
944  retVec.push_back(tableView_->getColumnInfo(col_).getDefaultValue());
945  std::vector<std::string> choices = tableView_->getColumnInfo(col_).getDataChoices();
946  for(const auto& choice : choices)
947  retVec.push_back(choice);
948 
949  return retVec;
950 } // end getFixedChoices()
951 
952 //==============================================================================
955 {
956  auto commentNode = getNode(TableViewColumnInfo::COL_NAME_COMMENT);
957  std::string comment = commentNode.getValueAsString();
958  return comment != "" && comment != TableViewColumnInfo::DATATYPE_COMMENT_DEFAULT &&
959  comment != TableViewColumnInfo::DATATYPE_COMMENT_OLD_DEFAULT &&
960  comment != commentNode.getColumnInfo().getDefaultValue();
961 } // end hasComment()
962 
963 //==============================================================================
965 const std::string& ConfigurationTree::getComment(void) const
966 {
967  return getNode(TableViewColumnInfo::COL_NAME_COMMENT).getValueAsString() == ""
968  ? TableViewColumnInfo::DATATYPE_COMMENT_DEFAULT
969  : getNode(TableViewColumnInfo::COL_NAME_COMMENT).getValueAsString();
970 } // end getComment()
971 
972 //==============================================================================
974 const std::string& ConfigurationTree::getAuthor(void) const
975 {
976  return getNode(TableViewColumnInfo::COL_NAME_AUTHOR).getValueAsString();
977 } // end getAuthor()
978 
979 //==============================================================================
987 const std::string& ConfigurationTree::getValueAsString(bool returnLinkTableValue) const
988 {
989  //__COUTV__(col_);__COUTV__(row_);__COUTV__(table_);__COUTV__(tableView_);
990 
991  if(isLinkNode())
992  {
993  if(returnLinkTableValue)
994  return linkColValue_;
995  else if(isDisconnected())
996  return ConfigurationTree::DISCONNECTED_VALUE;
997  else if(row_ == TableView::INVALID &&
998  col_ == TableView::INVALID) // this link is groupId node
999  return (groupId_ == "") ? table_->getTableName() : groupId_;
1000  else if(col_ == TableView::INVALID) // this link is uid node
1001  return tableView_->getDataView()[row_][tableView_->getColUID()];
1002  else
1003  {
1004  __SS__ << "Impossible Link." << __E__;
1005 
1006  ss << nodeDump() << __E__;
1007  __SS_THROW__;
1008  }
1009  }
1010  else if(row_ != TableView::INVALID &&
1011  col_ != TableView::INVALID) // this node is a value node
1012  return tableView_->getDataView()[row_][col_];
1013  else if(row_ == TableView::INVALID &&
1014  col_ == TableView::INVALID) // this node is table node maybe with groupId
1015  {
1016  // if root node, then no table defined
1017  if(isRootNode())
1018  return ConfigurationTree::ROOT_NAME;
1019 
1020  return (groupId_ == "") ? table_->getTableName() : groupId_;
1021  }
1022  else if(row_ == TableView::INVALID)
1023  {
1024  __SS__ << "Malformed ConfigurationTree" << __E__;
1025 
1026  ss << nodeDump() << __E__;
1027  __SS_THROW__;
1028  }
1029  else if(col_ == TableView::INVALID) // this node is uid node
1030  return tableView_->getDataView()[row_][tableView_->getColUID()];
1031  else
1032  {
1033  __SS__ << "Impossible." << __E__;
1034 
1035  ss << nodeDump() << __E__;
1036  __SS_THROW__;
1037  }
1038 } // end getValueAsString()
1039 
1040 //==============================================================================
1044 const std::string& ConfigurationTree::getUIDAsString(void) const
1045 {
1046  if(isValueNode() || isUIDLinkNode() || isUIDNode())
1047  return tableView_->getDataView()[row_][tableView_->getColUID()];
1048 
1049  {
1050  __SS__ << "Can not get UID of node with type '" << getNodeType()
1051  << ".' Node type must be '" << ConfigurationTree::NODE_TYPE_VALUE
1052  << "' or '" << ConfigurationTree::NODE_TYPE_UID_LINK << ".'" << __E__;
1053 
1054  ss << nodeDump() << __E__;
1055  __SS_ONLY_THROW__;
1056  }
1057 } // end getUIDAsString()
1058 
1059 //==============================================================================
1062 const std::string& ConfigurationTree::getValueDataType(void) const
1063 {
1064  if(isValueNode())
1065  return tableView_->getColumnInfo(col_).getDataType();
1066  else // must be std::string
1067  return TableViewColumnInfo::DATATYPE_STRING;
1068 } // end getValueDataType()
1069 
1070 //==============================================================================
1074 {
1075  if(!isValueNode())
1076  return false;
1077 
1078  if(getValueDataType() == TableViewColumnInfo::DATATYPE_STRING)
1079  {
1080  if(getValueType() == TableViewColumnInfo::TYPE_ON_OFF ||
1081  getValueType() == TableViewColumnInfo::TYPE_TRUE_FALSE ||
1082  getValueType() == TableViewColumnInfo::TYPE_YES_NO)
1083  return getValueAsString() ==
1084  TableViewColumnInfo::DATATYPE_BOOL_DEFAULT; // default to OFF, NO,
1085  // FALSE
1086  else if(getValueType() == TableViewColumnInfo::TYPE_COMMENT)
1087  return getValueAsString() == TableViewColumnInfo::DATATYPE_COMMENT_DEFAULT ||
1088  getValueAsString() ==
1089  ""; // in case people delete default comment, allow blank also
1090  else
1091  return getValueAsString() == TableViewColumnInfo::DATATYPE_STRING_DEFAULT ||
1092  getValueAsString() == TableViewColumnInfo::DATATYPE_STRING_ALT_DEFAULT;
1093  }
1095  return getValueAsString() == TableViewColumnInfo::DATATYPE_NUMBER_DEFAULT;
1096  else if(getValueDataType() == TableViewColumnInfo::DATATYPE_TIME)
1097  return getValueAsString() == TableViewColumnInfo::DATATYPE_TIME_DEFAULT;
1098  else
1099  return false;
1100 } // end isDefaultValue()
1101 
1102 //==============================================================================
1106 const std::string& ConfigurationTree::getDefaultValue(void) const
1107 {
1108  if(!isValueNode())
1109  {
1110  __SS__ << "Can only get default value from a value node! "
1111  << "The node type is " << getNodeType() << __E__;
1112 
1113  ss << nodeDump() << __E__;
1114  __SS_THROW__;
1115  }
1116 
1117  if(getValueDataType() == TableViewColumnInfo::DATATYPE_STRING)
1118  {
1119  if(getValueType() == TableViewColumnInfo::TYPE_ON_OFF ||
1120  getValueType() == TableViewColumnInfo::TYPE_TRUE_FALSE ||
1121  getValueType() == TableViewColumnInfo::TYPE_YES_NO)
1122  return TableViewColumnInfo::DATATYPE_BOOL_DEFAULT; // default to OFF, NO,
1123  // FALSE
1124  else if(getValueType() == TableViewColumnInfo::TYPE_COMMENT)
1125  return TableViewColumnInfo::
1126  DATATYPE_COMMENT_DEFAULT; // in case people delete default comment, allow blank also
1127  else
1128  return TableViewColumnInfo::DATATYPE_STRING_DEFAULT;
1129  }
1131  return TableViewColumnInfo::DATATYPE_NUMBER_DEFAULT;
1132  else if(getValueDataType() == TableViewColumnInfo::DATATYPE_TIME)
1133  return TableViewColumnInfo::DATATYPE_TIME_DEFAULT;
1134 
1135  {
1136  __SS__ << "Can only get default value from a value node! "
1137  << "The node type is " << getNodeType() << __E__;
1138 
1139  ss << nodeDump() << __E__;
1140  __SS_THROW__;
1141  }
1142 } // end isDefaultValue()
1143 
1144 //==============================================================================
1147 const std::string& ConfigurationTree::getValueType(void) const
1148 {
1149  if(isValueNode())
1150  return tableView_->getColumnInfo(col_).getType();
1151  else if(isLinkNode() && isDisconnected())
1152  return ConfigurationTree::VALUE_TYPE_DISCONNECTED;
1153  else // just call all non-value nodes data
1154  return ConfigurationTree::VALUE_TYPE_NODE;
1155 } // end getValueType()
1156 
1157 //==============================================================================
1161 {
1162  if(isValueNode())
1163  return tableView_->getColumnInfo(col_);
1164  else
1165  {
1166  __SS__ << "Can only get column info from a value node! "
1167  << "The node type is " << getNodeType() << __E__;
1168 
1169  ss << nodeDump() << __E__;
1170  __SS_THROW__;
1171  }
1172 } // end getColumnInfo()
1173 
1174 //==============================================================================
1176 const unsigned int& ConfigurationTree::getRow(void) const { return row_; }
1177 
1178 //==============================================================================
1180 const unsigned int& ConfigurationTree::getColumn(void) const { return col_; }
1181 
1182 //==============================================================================
1185 const unsigned int& ConfigurationTree::getFieldRow(void) const
1186 {
1187  if(isLinkNode())
1188  {
1189  // for links, need to use parent info to determine
1190  return linkBackRow_;
1191  }
1192  else
1193  return row_;
1194 } // end getFieldRow()
1195 
1196 //==============================================================================
1199 const unsigned int& ConfigurationTree::getFieldColumn(void) const
1200 {
1201  if(isLinkNode())
1202  {
1203  // for links, need to use parent info to determine
1204  return linkBackCol_;
1205  }
1206  else
1207  return col_;
1208 } // end getFieldColumn()
1209 
1210 //==============================================================================
1212 const std::string& ConfigurationTree::getChildLinkIndex(void) const
1213 {
1214  if(!isLinkNode())
1215  {
1216  __SS__ << "Can only get link ID from a link! "
1217  << "The node type is " << getNodeType() << __E__;
1218 
1219  ss << nodeDump() << __E__;
1220  __SS_THROW__;
1221  }
1222  return childLinkIndex_;
1223 } // end getChildLinkIndex()
1224 
1225 //==============================================================================
1228 const std::string& ConfigurationTree::getValueName(void) const
1229 {
1230  if(isValueNode())
1231  return tableView_->getColumnInfo(col_).getName();
1232  else if(isLinkNode())
1233  return linkColName_;
1234  else
1235  {
1236  __SS__ << "Can only get value name of a value node!" << __E__;
1237 
1238  ss << nodeDump() << __E__;
1239  __SS_THROW__;
1240  }
1241 } // end getValueName()
1242 
1243 //==============================================================================
1246 ConfigurationTree ConfigurationTree::recurse(const ConfigurationTree& tree,
1247  const std::string& childPath,
1248  bool doNotThrowOnBrokenUIDLinks,
1249  const std::string& originalNodeString)
1250 {
1251  __COUTS__(50) << tree.row_ << " " << tree.col_ << __E__;
1252  __COUTS__(51) << "childPath=" << childPath << " " << childPath.length() << __E__;
1253  if(childPath.length() <= 1) // only "/" or ""
1254  return tree;
1255  return tree.recursiveGetNode(
1256  childPath, doNotThrowOnBrokenUIDLinks, originalNodeString);
1257 } // end recurse()
1258 
1259 //==============================================================================
1269 ConfigurationTree ConfigurationTree::getNode(const std::string& nodeString,
1270  bool doNotThrowOnBrokenUIDLinks) const
1271 {
1272  // __COUT__ << "nodeString=" << nodeString << " len=" << nodeString.length() << __E__;
1273  return recursiveGetNode(
1274  nodeString, doNotThrowOnBrokenUIDLinks, "" /*originalNodeString*/);
1275 } // end getNode() connected to recursiveGetNode()
1276 ConfigurationTree ConfigurationTree::recursiveGetNode(
1277  const std::string& nodeString,
1278  bool doNotThrowOnBrokenUIDLinks,
1279  const std::string& originalNodeString) const
1280 {
1281  __COUTS__(51) << "nodeString=" << nodeString << " len=" << nodeString.length()
1282  << __E__;
1283  __COUTS__(52) << "doNotThrowOnBrokenUIDLinks=" << doNotThrowOnBrokenUIDLinks << __E__;
1284 
1285  // get nodeName (in case of / syntax)
1286  if(nodeString.length() < 1)
1287  {
1288  __SS__ << "Invalid empty node name! Looking for child node '" << nodeString
1289  << "' from node '" << getValue() << "'..." << __E__;
1290 
1291  ss << nodeDump() << __E__;
1292  __SS_THROW__;
1293  }
1294 
1295  // ignore multiple starting slashes
1296  size_t startingIndex = 0;
1297  while(startingIndex < nodeString.length() && nodeString[startingIndex] == '/')
1298  ++startingIndex;
1299  size_t endingIndex = nodeString.find('/', startingIndex);
1300  if(endingIndex == std::string::npos)
1301  endingIndex = nodeString.length();
1302 
1303  std::string nodeName = nodeString.substr(startingIndex, endingIndex - startingIndex);
1304  __COUTS__(51) << "nodeName=" << nodeName << " len=" << nodeName.length() << __E__;
1305 
1306  ++endingIndex;
1307  std::string childPath =
1308  (endingIndex >= nodeString.length() ? "" : nodeString.substr(endingIndex));
1309  __COUTS__(51) << "childPath=" << childPath << " len=" << childPath.length()
1310  << " endingIndex=" << endingIndex
1311  << " nodeString.length()=" << nodeString.length() << __E__;
1312 
1313  // if this tree is beginning at a configuration.. then go to uid, and vice versa
1314 
1315  try
1316  {
1317  __COUTS__(50) << row_ << " " << col_ << " " << groupId_ << " " << tableView_
1318  << __E__;
1319  if(isRootNode())
1320  {
1321  // root node
1322  // so return table node
1323  return recurse(configMgr_->getNode(nodeName),
1324  childPath,
1325  doNotThrowOnBrokenUIDLinks,
1326  originalNodeString);
1327  }
1328  else if(row_ == TableView::INVALID && col_ == TableView::INVALID)
1329  {
1330  // table node
1331 
1332  if(!tableView_)
1333  {
1334  __SS__ << "Missing configView pointer! Likely attempting to access a "
1335  "child node through a disconnected link node."
1336  << __E__;
1337 
1338  ss << nodeDump() << __E__;
1339  __SS_ONLY_THROW__;
1340  }
1341 
1342  // this node is table node, so return uid node considering groupid
1343  return recurse(
1345  configMgr_,
1346  table_,
1347  "", // no new groupId string, not a link
1348  0 /*linkParentTable_*/,
1349  "", // link node name, not a link
1350  "", // link node value, not a link
1351  TableView::INVALID /*linkBackRow_*/,
1352  TableView::INVALID /*linkBackCol_*/,
1353  "", // ignored disconnected target name, not a link
1354  "", // ignored disconnected link id, not a link
1355  "",
1356  // if this node is group table node, consider that when getting rows
1357  (groupId_ == "")
1358  ? tableView_->findRow(tableView_->getColUID(), nodeName)
1359  : tableView_->findRowInGroup(tableView_->getColUID(),
1360  nodeName,
1361  groupId_,
1362  childLinkIndex_)),
1363  childPath,
1364  doNotThrowOnBrokenUIDLinks,
1365  originalNodeString);
1366  }
1367  else if(row_ == TableView::INVALID)
1368  {
1369  __SS__ << "Malformed ConfigurationTree" << __E__;
1370 
1371  ss << nodeDump() << __E__;
1372  __SS_THROW__;
1373  }
1374  else if(col_ == TableView::INVALID)
1375  {
1376  // this node is uid node, so return link, group link, disconnected, or value
1377  // node
1378 
1379  __COUTS__(51) << "nodeName=" << nodeName << " " << nodeName.length() << __E__;
1380 
1381  // if the value is a unique link ..
1382  // return a uid node!
1383  // if the value is a group link
1384  // return a table node with group string
1385  // else.. return value node
1386 
1387  if(!tableView_)
1388  {
1389  __SS__ << "Missing configView pointer! Likely attempting to access a "
1390  "child node through a disconnected link node."
1391  << __E__;
1392 
1393  ss << nodeDump() << __E__;
1394  __SS_THROW__;
1395  }
1396 
1397  unsigned int c = tableView_->findCol(nodeName);
1398  std::pair<unsigned int /*link col*/, unsigned int /*link id col*/> linkPair;
1399  bool isGroupLink, isLink;
1400  if((isLink = tableView_->getChildLink(c, isGroupLink, linkPair)) &&
1401  !isGroupLink)
1402  {
1403  __COUTS__(50) << "nodeName=" << nodeName << " " << nodeName.length()
1404  << __E__;
1405  //is a unique link, return uid node in new configuration
1406  // need new configuration pointer
1407  // and row of linkUID in new configuration
1408 
1409  const TableBase* childConfig;
1410  try
1411  {
1412  childConfig = configMgr_->getTableByName(
1413  tableView_->getDataView()[row_][linkPair.first]);
1414  childConfig->getView(); // get view as a test for an active view
1415 
1416  if(doNotThrowOnBrokenUIDLinks) // try a test of getting row
1417  {
1418  childConfig->getView().findRow(
1419  childConfig->getView().getColUID(),
1420  tableView_->getDataView()[row_][linkPair.second]);
1421  }
1422  }
1423  catch(...)
1424  {
1425  __COUTS__(50)
1426  << "Found disconnected node! (" << nodeName << ":"
1427  << tableView_->getDataView()[row_][linkPair.first] << ")"
1428  << " at entry with UID "
1429  << tableView_->getDataView()[row_][tableView_->getColUID()]
1430  << __E__;
1431  //do not recurse further
1432  return ConfigurationTree(
1433  configMgr_,
1434  0,
1435  "",
1436  table_, // linkParentTable_
1437  nodeName,
1438  tableView_->getDataView()[row_][c], // this the link node field
1439  // associated value (matches
1440  // targeted column)
1441  row_ /*linkBackRow_*/,
1442  c /*linkBackCol_*/,
1443  tableView_->getDataView()[row_][linkPair.first], // give
1444  // disconnected
1445  // target name
1446  tableView_->getDataView()[row_][linkPair.second], // give
1447  // disconnected
1448  // link ID
1449  tableView_->getColumnInfo(c).getChildLinkIndex());
1450  }
1451 
1452  return recurse(
1453  ConfigurationTree( // this is a link node
1454  configMgr_,
1455  childConfig,
1456  "", // no new groupId string
1457  table_, // linkParentTable_
1458  nodeName, // this is a link node
1459  tableView_->getDataView()[row_][c], // this the link node field
1460  // associated value (matches
1461  // targeted column)
1462  row_ /*linkBackRow_*/,
1463  c /*linkBackCol_*/,
1464  "", // ignore since is connected
1465  "", // ignore since is connected
1466  tableView_->getColumnInfo(c).getChildLinkIndex(),
1467  childConfig->getView().findRow(
1468  childConfig->getView().getColUID(),
1469  tableView_->getDataView()[row_][linkPair.second])),
1470  childPath,
1471  doNotThrowOnBrokenUIDLinks,
1472  originalNodeString);
1473  }
1474  else if(isLink)
1475  {
1476  __COUTS__(50) << "nodeName=" << nodeName << " " << nodeName.length()
1477  << __E__;
1478  // is a group link, return new configuration with group string
1479  // need new configuration pointer
1480  // and group string
1481 
1482  const TableBase* childConfig;
1483  try
1484  {
1485  childConfig = configMgr_->getTableByName(
1486  tableView_->getDataView()[row_][linkPair.first]);
1487  childConfig->getView(); // get view as a test for an active view
1488  }
1489  catch(...)
1490  {
1491  if(tableView_->getDataView()[row_][linkPair.first] !=
1492  TableViewColumnInfo::DATATYPE_LINK_DEFAULT)
1493  __COUT_WARN__
1494  << "Found disconnected node! Failed link target "
1495  "from nodeName="
1496  << nodeName << " to table:id="
1497  << tableView_->getDataView()[row_][linkPair.first] << ":"
1498  << tableView_->getDataView()[row_][linkPair.second] << __E__;
1499 
1500  // do not recurse further
1501  return ConfigurationTree(
1502  configMgr_,
1503  0,
1504  tableView_->getDataView()[row_][linkPair.second], // groupID
1505  table_, // linkParentTable_
1506  nodeName,
1507  tableView_->getDataView()[row_][c], // this the link node field
1508  // associated value (matches
1509  // targeted column)
1510  row_ /*linkBackRow_*/,
1511  c /*linkBackCol_*/,
1512  tableView_->getDataView()[row_][linkPair.first], // give
1513  // disconnected
1514  // target name
1515  tableView_->getDataView()[row_][linkPair.second], // give
1516  // disconnected
1517  // target name
1518  tableView_->getColumnInfo(c).getChildLinkIndex());
1519  }
1520 
1521  return recurse(
1522  ConfigurationTree( // this is a link node
1523  configMgr_,
1524  childConfig,
1525  tableView_
1526  ->getDataView()[row_][linkPair.second], // groupId string
1527  table_, // linkParentTable_
1528  nodeName, // this is a link node
1529  tableView_->getDataView()[row_][c], // this the link node field
1530  // associated value (matches
1531  // targeted column)
1532  row_ /*linkBackRow_*/,
1533  c /*linkBackCol_*/,
1534  "", // ignore since is connected
1535  "", // ignore since is connected
1536  tableView_->getColumnInfo(c).getChildLinkIndex()),
1537  childPath,
1538  doNotThrowOnBrokenUIDLinks,
1539  originalNodeString);
1540  }
1541  else
1542  {
1543  __COUTS__(50) << "nodeName=" << nodeName << " " << nodeName.length()
1544  << __E__;
1545  //return value node
1546  return ConfigurationTree(configMgr_,
1547  table_,
1548  "",
1549  0 /*linkParentTable_*/,
1550  "",
1551  "",
1552  TableView::INVALID /*linkBackRow_*/,
1553  TableView::INVALID /*linkBackCol_*/,
1554  "",
1555  "" /*disconnectedLinkID*/,
1556  "",
1557  row_,
1558  c);
1559  }
1560  }
1561  }
1562  catch(std::runtime_error& e)
1563  {
1564  __SS__ << "\n\nError occurred descending from node '" << getValue()
1565  << "' in table '" << getTableName() << "' looking for child '" << nodeName
1566  << "'\n\n"
1567  << __E__;
1568  ss << "The original node search string was '" << originalNodeString << ".'"
1569  << __E__;
1570  ss << "--- Additional error detail: \n\n" << e.what() << __E__;
1571 
1572  ss << nodeDump() << __E__;
1573  __SS_ONLY_THROW__;
1574  }
1575  catch(...)
1576  {
1577  __SS__ << "\n\nError occurred descending from node '" << getValue()
1578  << "' in table '" << getTableName() << "' looking for child '" << nodeName
1579  << "'\n\n"
1580  << __E__;
1581  ss << "The original node search string was '" << originalNodeString << ".'"
1582  << __E__;
1583  try
1584  {
1585  throw;
1586  } //one more try to printout extra info
1587  catch(const std::exception& e)
1588  {
1589  ss << "Exception message: " << e.what();
1590  }
1591  catch(...)
1592  {
1593  }
1594  ss << nodeDump() << __E__;
1595  __SS_ONLY_THROW__;
1596  }
1597 
1598  // this node is value node, so has no node to choose from
1599  __SS__
1600  << "\n\nError occurred descending from node '" << getValue() << "' in table '"
1601  << getTableName() << "' looking for child '" << nodeName << "'\n\n"
1602  << "Invalid depth! getNode() called from a value point in the Configuration Tree."
1603  << __E__;
1604  ss << "The original node search string was '" << originalNodeString << ".'" << __E__;
1605 
1606  ss << nodeDump() << __E__;
1607  __SS_ONLY_THROW__; // this node is value node, cant go any deeper!
1608 } // end recursiveGetNode()
1609 
1610 //==============================================================================
1612 std::map<std::string, ConfigurationTree> ConfigurationTree::getNodes(
1613  const std::string& nodeString) const
1614 {
1615  if(nodeString.length() < 1)
1616  {
1617  return getChildrenMap();
1618  }
1619 
1620  return getNode(nodeString).getChildrenMap();
1621 } //end getNodes()
1622 
1623 //==============================================================================
1626 std::string ConfigurationTree::nodeDump(bool forcePrintout) const
1627 {
1628  //block cascading node dumps for a couple seconds
1629  // so that user can see the lowest level failure more easily
1630  if(!forcePrintout && time(0) - ConfigurationTree::LAST_NODE_DUMP_TIME < 3)
1631  {
1632  __COUTS__(20) << "Blocking cascading node dumps... "
1633  "ConfigurationTree::LAST_NODE_DUMP_TIME = "
1634  << ConfigurationTree::LAST_NODE_DUMP_TIME << __E__;
1635  return "";
1636  }
1637  ConfigurationTree::LAST_NODE_DUMP_TIME = time(0);
1638 
1639  __SS__ << __E__ << __E__;
1640 
1641  ss << "Row=" << (int)row_ << ", Col=" << (int)col_ << ", TablePointer=" << table_
1642  << __E__;
1643 
1644  // stack trace can seg fault on demangle call!... ?
1645  try
1646  {
1647  ss << "\n\n" << StringMacros::stackTrace() << __E__ << __E__;
1648  }
1649  catch(...)
1650  {
1651  } // ignore errors
1652 
1653  ss << "ConfigurationTree::nodeDump() start"
1654  "=====================================\nConfigurationTree::nodeDump():"
1655  << __E__;
1656 
1657  // try each level of debug.. and ignore errors
1658  try
1659  {
1660  ss << "\t"
1661  << "Node dump initiated from node '" << getValueAsString() << "'..." << __E__;
1662  }
1663  catch(...)
1664  {
1665  } // ignore errors
1666  try
1667  {
1668  ss << "\t"
1669  << "Node dump initiated from node '" << getValue() << "' in table '"
1670  << getTableName() << ".'" << __E__;
1671  }
1672  catch(...)
1673  {
1674  } // ignore errors
1675  ss << __E__; //add newline in case of exceptions mid-stringstream
1676 
1677  try
1678  {
1679  //try to avoid recursive throwing of getChildrenNames() until death spiral
1680  if(isTableNode() || isGroupLinkNode())
1681  {
1682  auto children = getChildrenNames();
1683  ss << "\t"
1684  << "Here is the list of possible children (count = " << children.size()
1685  << "):" << __E__;
1686  for(auto& child : children)
1687  ss << "\t\t" << child << __E__;
1688  if(tableView_)
1689  {
1690  ss << "\n\nHere is the culprit table printout:\n\n";
1691  tableView_->print(ss);
1692  }
1693  }
1694 
1695  if(isLinkNode() && isDisconnected())
1696  {
1697  ss << "Is link node." << __E__;
1698  ss << "disconnectedTargetName_ = " << disconnectedTargetName_
1699  << ", disconnectedLinkID_ = " << disconnectedLinkID_ << __E__;
1700 
1701  auto tables = getConfigurationManager()->getActiveVersions();
1702  ss << "\n\t"
1703  << "Here is the list of active tables:" << __E__;
1704  for(auto& table : tables)
1705  ss << "\t\t" << table.first << __E__;
1706  }
1707  }
1708  catch(...)
1709  {
1710  } // ignore errors trying to show children
1711 
1712  ss << "\n\nConfigurationTree::nodeDump() end ====================================="
1713  << __E__;
1714 
1715  return ss.str();
1716 } // end nodeDump()
1717 
1718 //==============================================================================
1719 ConfigurationTree ConfigurationTree::getBackNode(std::string nodeName,
1720  unsigned int backSteps) const
1721 {
1722  for(unsigned int i = 0; i < backSteps; i++)
1723  nodeName = nodeName.substr(0, nodeName.find_last_of('/'));
1724 
1725  return getNode(nodeName);
1726 } // end getBackNode()
1727 
1728 //==============================================================================
1729 ConfigurationTree ConfigurationTree::getForwardNode(std::string nodeName,
1730  unsigned int forwardSteps) const
1731 {
1732  unsigned int s = 0;
1733 
1734  // skip all leading /'s
1735  while(s < nodeName.length() && nodeName[s] == '/')
1736  ++s;
1737 
1738  for(unsigned int i = 0; i < forwardSteps; i++)
1739  s = nodeName.find('/', s) + 1;
1740 
1741  return getNode(nodeName.substr(0, s));
1742 } // end getForwardNode()
1743 
1744 //==============================================================================
1748 {
1749  return (row_ != TableView::INVALID && col_ != TableView::INVALID);
1750 } // end isValueNode()
1751 
1752 //==============================================================================
1756 {
1757  return isValueNode() && tableView_->getColumnInfo(col_).isBoolType();
1758 } // end isValueBoolType()
1759 
1760 //==============================================================================
1764 {
1765  return isValueNode() && tableView_->getColumnInfo(col_).isNumberDataType();
1766 } // end isValueBoolType()
1767 
1768 //==============================================================================
1774 {
1775  if(!isLinkNode())
1776  {
1777  __SS__ << "\n\nError occurred testing link connection at node with value '"
1778  << getValue() << "' in table '" << getTableName() << "'\n\n"
1779  << __E__;
1780  ss << "This is not a Link node! It is node type '" << getNodeType()
1781  << ".' Only a Link node can be disconnected." << __E__;
1782 
1783  ss << nodeDump() << __E__;
1784  __SS_ONLY_THROW__;
1785  }
1786 
1787  return !table_ || !tableView_;
1788 } // end isDisconnected()
1789 
1790 //==============================================================================
1793 bool ConfigurationTree::isLinkNode(void) const { return linkColName_ != ""; }
1794 
1795 //==============================================================================
1798 const std::string ConfigurationTree::NODE_TYPE_GROUP_TABLE = "GroupTableNode";
1799 const std::string ConfigurationTree::NODE_TYPE_TABLE = "TableNode";
1800 const std::string ConfigurationTree::NODE_TYPE_GROUP_LINK = "GroupLinkNode";
1801 const std::string ConfigurationTree::NODE_TYPE_UID_LINK = "UIDLinkNode";
1802 const std::string ConfigurationTree::NODE_TYPE_VALUE = "ValueNode";
1803 const std::string ConfigurationTree::NODE_TYPE_UID = "UIDNode";
1804 const std::string ConfigurationTree::NODE_TYPE_ROOT = "RootNode";
1805 
1806 std::string ConfigurationTree::getNodeType(void) const
1807 {
1808  if(isRootNode())
1809  return ConfigurationTree::NODE_TYPE_ROOT;
1810  if(isTableNode() && groupId_ != "")
1812  if(isTableNode())
1813  return ConfigurationTree::NODE_TYPE_TABLE;
1814  if(isGroupLinkNode())
1815  return ConfigurationTree::NODE_TYPE_GROUP_LINK;
1816  if(isLinkNode())
1817  return ConfigurationTree::NODE_TYPE_UID_LINK;
1818  if(isValueNode())
1819  return ConfigurationTree::NODE_TYPE_VALUE;
1820  return ConfigurationTree::NODE_TYPE_UID;
1821 } // end getNodeType()
1822 
1823 //==============================================================================
1827 {
1828  return (isLinkNode() && groupId_ != "");
1829 }
1830 
1831 //==============================================================================
1835 {
1836  return (isLinkNode() && groupId_ == "");
1837 } // end isUIDLinkNode()
1838 
1839 //==============================================================================
1843 {
1844  return (isValueNode() && tableView_->getColumnInfo(col_).isGroupID());
1845 } // end isGroupIDNode()
1846 
1847 //==============================================================================
1851 {
1852  return (row_ != TableView::INVALID && col_ == TableView::INVALID);
1853 }
1854 
1855 //==============================================================================
1871 std::vector<ConfigurationTree::RecordField> ConfigurationTree::getCommonFields(
1872  const std::vector<std::string /*uid*/>& recordList,
1873  const std::vector<std::string /*relative-path*/>& fieldAcceptList,
1874  const std::vector<std::string /*relative-path*/>& fieldRejectList,
1875  unsigned int depth,
1876  bool autoSelectFilterFields) const
1877 {
1878  // enforce that starting point is a table node
1879  if(!isRootNode() && !isTableNode())
1880  {
1881  __SS__ << "Can only get getCommonFields from a root or table node! "
1882  << "The node type is " << getNodeType() << __E__;
1883 
1884  ss << nodeDump() << __E__;
1885  __SS_THROW__;
1886  }
1887 
1888  std::vector<ConfigurationTree::RecordField> fieldCandidateList;
1889  std::vector<int> fieldCount; //-1 := guaranteed, else count must match num of records
1890 
1891  --depth; // decrement for recursion
1892 
1893  // for each record in <record list>
1894  // loop through all record's children
1895  // if isValueNode (value nodes are possible field candidates!)
1896  // if first uid record
1897  // add field to <field candidates list> if in <field filter list>
1898  // mark <field count> as guaranteed -1 (all these fields must be common
1899  // for UIDs in same table)
1900  // else not first uid record, do not need to check, must be same as first
1901  // record! else if depth > 0 and UID-Link Node recursively (call
1902  // recursiveGetCommonFields())
1903  // =====================
1904  // Start recursiveGetCommonFields()
1905  // --depth;
1906  // loop through all children
1907  // if isValueNode (value nodes are possible field candidates!)
1908  // if first uid record
1909  // add field to <field candidates list> if in <field
1910  // filter list> initial mark <field count> as 1
1911  // else
1912  // if field is in <field candidates list>,
1913  // increment <field count> for field candidate
1914  // else if field is not in list, ignore field
1915  // else if depth > 0 and is UID-Link
1916  // if Link Table/UID pair is not found in <field candidates
1917  // list> (avoid endless loops through tree)
1918  // recursiveGetCommonFields()
1919  // =====================
1920  //
1921  //
1922  // loop through all field candidates
1923  // remove those with <field count> != num of records
1924  //
1925  //
1926  // return result
1927 
1928  bool found; // used in loops
1929  // auto tableName = isRootNode()?"/":getTableName(); //all records will share this
1930  // table name
1931 
1932  // if no records, just return table fields
1933  if(!recordList.size() && tableView_)
1934  {
1935  const std::vector<TableViewColumnInfo>& colInfo = tableView_->getColumnsInfo();
1936 
1937  for(unsigned int col = 0; col < colInfo.size(); ++col)
1938  {
1939  __COUTS__(11) << "Considering field " << colInfo[col].getName() << __E__;
1940 
1941  // check field accept filter list
1942  found = fieldAcceptList.size() ? false : true; // accept if no filter
1943  // list
1944  for(const auto& fieldFilter : fieldAcceptList)
1945  if(StringMacros::wildCardMatch(fieldFilter, colInfo[col].getName()))
1946  {
1947  found = true;
1948  break;
1949  }
1950 
1951  if(found)
1952  {
1953  // check field reject filter list
1954 
1955  found = true; // accept if no filter list
1956  for(const auto& fieldFilter : fieldRejectList)
1957  if(StringMacros::wildCardMatch(fieldFilter, colInfo[col].getName()))
1958  {
1959  found = false; // reject if match
1960  break;
1961  }
1962  }
1963 
1964  // if found, new field (since this is first record)
1965  if(found)
1966  {
1967  __COUTS__(11) << "FOUND field " << colInfo[col].getName() << __E__;
1968 
1969  if(colInfo[col].isChildLink())
1970  {
1971  __COUTS__(11)
1972  << "isGroupLinkNode " << colInfo[col].getName() << __E__;
1973 
1974  // must get column info differently for group link column
1975 
1976  std::pair<unsigned int /*link col*/, unsigned int /*link id col*/>
1977  linkPair;
1978  bool isGroupLink;
1979  tableView_->getChildLink(col, isGroupLink, linkPair);
1980 
1981  // add both link columns
1982 
1983  fieldCandidateList.push_back(ConfigurationTree::RecordField(
1984  table_->getTableName(),
1985  "", // uid
1986  tableView_->getColumnInfo(linkPair.first).getName(),
1987  "", // relative path, not including columnName_
1988  &tableView_->getColumnInfo(linkPair.first)));
1989  fieldCount.push_back(-1); // mark guaranteed field
1990 
1991  fieldCandidateList.push_back(ConfigurationTree::RecordField(
1992  table_->getTableName(),
1993  "", // uid
1994  tableView_->getColumnInfo(linkPair.second).getName(),
1995  "", // relative path, not including columnName_
1996  &tableView_->getColumnInfo(linkPair.second)));
1997  fieldCount.push_back(-1); // mark guaranteed field
1998  }
1999  else // value node
2000  {
2001  fieldCandidateList.push_back(ConfigurationTree::RecordField(
2002  table_->getTableName(),
2003  "", // uid
2004  colInfo[col].getName(),
2005  "", // relative path, not including columnName_
2006  &colInfo[col]));
2007  fieldCount.push_back(1); // init count to 1
2008  }
2009  }
2010  } // end table column loop
2011  } // end no record handling
2012 
2013  for(unsigned int i = 0; i < recordList.size(); ++i)
2014  {
2015  __COUTS__(11) << "Checking " << recordList[i] << __E__;
2016  ConfigurationTree node = getNode(recordList[i]);
2017 
2018  node.recursiveGetCommonFields(fieldCandidateList,
2019  fieldCount,
2020  fieldAcceptList,
2021  fieldRejectList,
2022  depth,
2023  "", // relativePathBase
2024  !i // continue inFirstRecord (or not) depth search
2025  );
2026 
2027  } // end record loop
2028 
2029  __COUT__ << "======================= check for count = " << (int)recordList.size()
2030  << __E__;
2031 
2032  // loop through all field candidates
2033  // remove those with <field count> != num of records
2034  for(unsigned int i = 0; i < fieldCandidateList.size(); ++i)
2035  {
2036  __COUTS__(11) << "Checking " << fieldCandidateList[i].relativePath_
2037  << fieldCandidateList[i].columnName_ << " = " << fieldCount[i]
2038  << __E__;
2039  if(recordList.size() != 0 && fieldCount[i] != -1 &&
2040  fieldCount[i] != (int)recordList.size())
2041  {
2042  __COUTS__(11) << "Erasing " << fieldCandidateList[i].relativePath_
2043  << fieldCandidateList[i].columnName_ << __E__;
2044 
2045  fieldCount.erase(fieldCount.begin() + i);
2046  fieldCandidateList.erase(fieldCandidateList.begin() + i);
2047  --i; // rewind to look at next after deleted
2048  }
2049  }
2050 
2051  for(unsigned int i = 0; i < fieldCandidateList.size(); ++i)
2052  __COUTS__(11) << "Pre-Final " << fieldCandidateList[i].relativePath_
2053  << fieldCandidateList[i].columnName_ << __E__;
2054 
2055  if(autoSelectFilterFields)
2056  {
2057  // filter for just 3 of the best filter fields
2058  // i.e. preference for GroupID, On/Off, and FixedChoice fields.
2059  std::set<std::pair<unsigned int /*fieldPriority*/, unsigned int /*fieldIndex*/>>
2060  prioritySet;
2061 
2062  unsigned int priorityPenalty;
2063  for(unsigned int i = 0; i < fieldCandidateList.size(); ++i)
2064  {
2065  __COUTS__(11) << "Option [" << i << "] "
2066  << fieldCandidateList[i].relativePath_
2067  << fieldCandidateList[i].columnName_ << " : "
2068  << fieldCandidateList[i].columnInfo_->getType() << ":"
2069  << fieldCandidateList[i].columnInfo_->getDataType() << __E__;
2070 
2071  priorityPenalty = std::count(fieldCandidateList[i].relativePath_.begin(),
2072  fieldCandidateList[i].relativePath_.end(),
2073  '/') *
2074  20; // penalize if not top level
2075 
2076  if(fieldCandidateList[i].columnInfo_->isBoolType() &&
2077  (fieldCandidateList[i].columnName_ ==
2078  TableViewColumnInfo::COL_NAME_STATUS ||
2079  fieldCandidateList[i].columnName_ ==
2080  TableViewColumnInfo::COL_NAME_ENABLED))
2081  {
2082  priorityPenalty += 0;
2083  }
2084  else if(fieldCandidateList[i].columnInfo_->isGroupID())
2085  {
2086  priorityPenalty += 1;
2087  }
2088  else if(fieldCandidateList[i].columnInfo_->isBoolType())
2089  {
2090  priorityPenalty += 3;
2091  }
2092  else if(fieldCandidateList[i].columnInfo_->getType() ==
2093  TableViewColumnInfo::TYPE_FIXED_CHOICE_DATA)
2094  {
2095  priorityPenalty += 3;
2096  }
2097  else if(fieldCandidateList[i].columnInfo_->getType() ==
2098  TableViewColumnInfo::TYPE_DATA)
2099  {
2100  priorityPenalty += 10;
2101  }
2102  else // skip other fields and mark for erasing
2103  {
2104  fieldCandidateList[i].tableName_ =
2105  ""; // clear table name as indicator for erase
2106  continue;
2107  }
2108  prioritySet.emplace(
2109  std::make_pair(priorityPenalty /*fieldPriority*/, i /*fieldIndex*/));
2110  __COUTS__(11) << "Option [" << i << "] "
2111  << fieldCandidateList[i].relativePath_
2112  << fieldCandidateList[i].columnName_ << " : "
2113  << fieldCandidateList[i].columnInfo_->getType() << ":"
2114  << fieldCandidateList[i].columnInfo_->getDataType()
2115  << "... priority = " << priorityPenalty << __E__;
2116 
2117  } // done ranking fields
2118 
2119  __COUTV__(StringMacros::setToString(prioritySet));
2120 
2121  // now choose the top 3, and delete the rest
2122  // clear table name to indicate field should be erased
2123  {
2124  unsigned int cnt = 0;
2125  for(const auto& priorityFieldIndex : prioritySet)
2126  if(++cnt > 3) // then mark for erasing
2127  {
2128  __COUTS__(11)
2129  << cnt << " marking "
2130  << fieldCandidateList[priorityFieldIndex.second].relativePath_
2131  << fieldCandidateList[priorityFieldIndex.second].columnName_
2132  << __E__;
2133  fieldCandidateList[priorityFieldIndex.second].tableName_ =
2134  ""; // clear table name as indicator for erase
2135  }
2136  }
2137 
2138  for(unsigned int i = 0; i < fieldCandidateList.size(); ++i)
2139  {
2140  if(fieldCandidateList[i].tableName_ == "") // then erase
2141  {
2142  __COUTS__(11) << "Erasing " << fieldCandidateList[i].relativePath_
2143  << fieldCandidateList[i].columnName_ << __E__;
2144  fieldCandidateList.erase(fieldCandidateList.begin() + i);
2145  --i; // rewind to look at next after deleted
2146  }
2147  }
2148  } // end AUTO filter field selection
2149 
2150  for(unsigned int i = 0; i < fieldCandidateList.size(); ++i)
2151  __COUT__ << "Final " << fieldCandidateList[i].relativePath_
2152  << fieldCandidateList[i].columnName_ << __E__;
2153 
2154  return fieldCandidateList;
2155 } // end getCommonFields()
2156 
2157 //==============================================================================
2163 std::set<std::string /*unique-value*/> ConfigurationTree::getUniqueValuesForField(
2164  const std::vector<std::string /*relative-path*/>& recordList,
2165  const std::string& fieldName,
2166  std::string* fieldGroupIDChildLinkIndex /* =0 */) const
2167 {
2168  if(fieldGroupIDChildLinkIndex)
2169  *fieldGroupIDChildLinkIndex = "";
2170 
2171  // enforce that starting point is a table node
2172  if(!isTableNode())
2173  {
2174  __SS__ << "Can only get getCommonFields from a table node! "
2175  << "The node type is " << getNodeType() << __E__;
2176 
2177  ss << nodeDump() << __E__;
2178  __SS_THROW__;
2179  }
2180 
2181  std::set<std::string /*unique-value*/> uniqueValues;
2182 
2183  // for each record in <record list>
2184  // emplace value at field into set
2185  //
2186  // return result
2187 
2188  // if no records, just return fieldGroupIDChildLinkIndex
2189  if(!recordList.size() && tableView_ && fieldGroupIDChildLinkIndex)
2190  {
2191  const TableViewColumnInfo& colInfo =
2192  tableView_->getColumnInfo(tableView_->findCol(fieldName));
2193 
2194  if(colInfo.isGroupID())
2195  *fieldGroupIDChildLinkIndex = colInfo.getChildLinkIndex();
2196 
2197  } // end no records
2198 
2199  for(unsigned int i = 0; i < recordList.size(); ++i)
2200  {
2201  //__COUT__ << "Checking " << recordList[i] << __E__;
2202 
2203  // Note: that ConfigurationTree maps both fields associated with a link
2204  // to the same node instance.
2205  // The behavior is likely not expected as response for this function..
2206  // so for links return actual value for field name specified
2207  // i.e. if Table of link is requested give that; if linkID is requested give
2208  // that. use TRUE in getValueAsString for proper behavior
2209 
2210  ConfigurationTree node = getNode(recordList[i]).getNode(fieldName);
2211 
2212  if(node.isGroupIDNode())
2213  {
2214  // handle groupID node special
2215 
2216  //__COUT__ << "GroupID field " << fieldName << __E__;
2217 
2218  // first time, get field's GroupID Child Link Index, if applicable
2219  if(i == 0 && fieldGroupIDChildLinkIndex)
2220  *fieldGroupIDChildLinkIndex = node.getColumnInfo().getChildLinkIndex();
2221 
2222  // return set of groupIDs individually
2223 
2224  std::set<std::string> setOfGroupIDs = node.getSetOfGroupIDs();
2225  for(auto& groupID : setOfGroupIDs)
2226  uniqueValues.emplace(groupID);
2227  }
2228  else // normal record, return value as string
2229  uniqueValues.emplace(node.getValueAsString(true));
2230 
2231  } // end record loop
2232 
2233  return uniqueValues;
2234 } // end getUniqueValuesForField()
2235 
2236 //==============================================================================
2239 void ConfigurationTree::recursiveGetCommonFields(
2240  std::vector<ConfigurationTree::RecordField>& fieldCandidateList,
2241  std::vector<int>& fieldCount,
2242  const std::vector<std::string /*relative-path*/>& fieldAcceptList,
2243  const std::vector<std::string /*relative-path*/>& fieldRejectList,
2244  unsigned int depth,
2245  const std::string& relativePathBase,
2246  bool inFirstRecord) const
2247 {
2248  //__COUT__ << depth << ":relativePathBase " << relativePathBase <<
2249  // " + " << inFirstRecord <<__E__;
2250  --depth;
2251 
2252  // clang-format off
2253  // =====================
2254  // Start recursiveGetCommonFields()
2255  // --depth;
2256  // loop through all children
2257  // if isValueNode (value nodes are possible field candidates!)
2258  // if first uid record
2259  // add field to <field candidates list> if in <field filter list>
2260  // initial mark <field count> as 1
2261  // else
2262  // if field is in list,
2263  // increment count for field candidate
2264  // //?increment fields in list count for record
2265  // else if field is not in list, discard field
2266  // else if depth > 0 and is UID-Link
2267  // if Link Table/UID pair is not found in <field candidates list>
2268  // (avoid endless loops through tree)
2269  // recursiveGetCommonFields()
2270  // =====================
2271  // clang-format on
2272 
2273  bool found; // used in loops
2274  auto tableName = getTableName(); // all fields will share this table name
2275  auto uid = getUIDAsString(); // all fields will share this uid
2276  unsigned int j;
2277 
2278  auto recordChildren = getChildren();
2279  for(const auto& fieldNode : recordChildren)
2280  {
2281  //__COUT__ << "All... " << fieldNode.second.getNodeType() <<
2282  // " -- " << (relativePathBase + fieldNode.first) <<
2283  // " + " << inFirstRecord <<__E__;
2284 
2285  if(fieldNode.second.isValueNode() || fieldNode.second.isGroupLinkNode())
2286  {
2287  // skip author and record insertion time
2288  if(fieldNode.second.isValueNode())
2289  {
2290  if(fieldNode.second.getColumnInfo().getType() ==
2291  TableViewColumnInfo::TYPE_AUTHOR ||
2292  fieldNode.second.getColumnInfo().getType() ==
2293  TableViewColumnInfo::TYPE_TIMESTAMP)
2294  continue;
2295 
2296  //__COUT__ << "isValueNode " << fieldNode.first << __E__;
2297  }
2298 
2299  if(inFirstRecord) // first uid record
2300  {
2301  //__COUT__ << "Checking... " << fieldNode.second.getNodeType() <<
2302  // " -- " << (relativePathBase + fieldNode.first) <<
2303  // "-- depth=" << depth << __E__;
2304 
2305  // check field accept filter list
2306  found = fieldAcceptList.size() ? false : true; // accept if no filter
2307  // list
2308  for(const auto& fieldFilter : fieldAcceptList)
2309  if(fieldFilter.find('/') != std::string::npos)
2310  {
2311  // filter is for full path, so add relative path base
2313  fieldFilter, relativePathBase + fieldNode.first))
2314  {
2315  found = true;
2316  break;
2317  }
2318  }
2319  else if(StringMacros::wildCardMatch(fieldFilter, fieldNode.first))
2320  {
2321  found = true;
2322  break;
2323  }
2324 
2325  if(found)
2326  {
2327  // check field reject filter list
2328 
2329  found = true; // accept if no filter list
2330  for(const auto& fieldFilter : fieldRejectList)
2331  if(fieldFilter.find('/') != std::string::npos)
2332  {
2333  // filter is for full path, so add relative path base
2335  fieldFilter, relativePathBase + fieldNode.first))
2336  {
2337  found = false; // reject if match
2338  break;
2339  }
2340  }
2341  else if(StringMacros::wildCardMatch(fieldFilter, fieldNode.first))
2342  {
2343  found = false; // reject if match
2344  break;
2345  }
2346  }
2347 
2348  // if found, new field (since this is first record)
2349  if(found)
2350  {
2351  //__COUT__ << "FOUND field " <<
2352  // (relativePathBase + fieldNode.first) << __E__;
2353 
2354  if(fieldNode.second.isGroupLinkNode())
2355  {
2356  //__COUT__ << "isGroupLinkNode " << fieldNode.first << __E__;
2357 
2358  // must get column info differently for group link column
2359 
2360  std::pair<unsigned int /*link col*/, unsigned int /*link id col*/>
2361  linkPair;
2362  bool isGroupLink;
2363  tableView_->getChildLink(
2364  tableView_->findCol(fieldNode.first), isGroupLink, linkPair);
2365 
2366  // add both link columns
2367 
2368  fieldCandidateList.push_back(ConfigurationTree::RecordField(
2369  table_->getTableName(),
2370  uid,
2371  tableView_->getColumnInfo(linkPair.first).getName(),
2372  relativePathBase, // relative path, not including columnName_
2373  &tableView_->getColumnInfo(linkPair.first)));
2374  fieldCount.push_back(1); // init count to 1
2375 
2376  fieldCandidateList.push_back(ConfigurationTree::RecordField(
2377  table_->getTableName(),
2378  uid,
2379  tableView_->getColumnInfo(linkPair.second).getName(),
2380  relativePathBase, // relative path, not including columnName_
2381  &tableView_->getColumnInfo(linkPair.second)));
2382  fieldCount.push_back(1); // init count to 1
2383  }
2384  else // value node
2385  {
2386  fieldCandidateList.push_back(ConfigurationTree::RecordField(
2387  tableName,
2388  uid,
2389  fieldNode.first,
2390  relativePathBase, // relative path, not including columnName_
2391  &fieldNode.second.getColumnInfo()));
2392  fieldCount.push_back(1); // init count to 1
2393  }
2394  }
2395  }
2396  else // not first record
2397  {
2398  // if field is in <field candidates list>, increment <field count>
2399  // else ignore
2400  for(j = 0; j < fieldCandidateList.size(); ++j)
2401  {
2402  if((relativePathBase + fieldNode.first) ==
2403  (fieldCandidateList[j].relativePath_ +
2404  fieldCandidateList[j].columnName_))
2405  {
2406  //__COUT__ << "incrementing " << j <<
2407  // " " << fieldCandidateList[j].relativePath_ << __E__;
2408  // found, so increment <field count>
2409  ++fieldCount[j];
2410  if(fieldNode.second.isGroupLinkNode() &&
2411  j + 1 < fieldCandidateList.size())
2412  ++fieldCount[j + 1]; // increment associated link index too!
2413  break;
2414  }
2415  }
2416  }
2417  } // end value and group link node handling
2418  else if(fieldNode.second.isUIDLinkNode())
2419  {
2420  //__COUT__ << "isUIDLinkNode " << (relativePathBase + fieldNode.first) <<
2421  // " + " << inFirstRecord << __E__;
2422 
2423  if(inFirstRecord) // first uid record
2424  {
2425  // check field accept filter list
2426  found =
2427  fieldAcceptList.size() ? false : true; // accept if no filter list
2428  for(const auto& fieldFilter : fieldAcceptList)
2429  if(fieldFilter.find('/') != std::string::npos)
2430  {
2431  // filter is for full path, so add relative path base
2433  fieldFilter, relativePathBase + fieldNode.first))
2434  {
2435  found = true;
2436  break;
2437  }
2438  }
2439  else if(StringMacros::wildCardMatch(fieldFilter, fieldNode.first))
2440  {
2441  found = true;
2442  break;
2443  }
2444 
2445  if(found)
2446  {
2447  // check field reject filter list
2448 
2449  found = true; // accept if no filter list
2450  for(const auto& fieldFilter : fieldRejectList)
2451  if(fieldFilter.find('/') != std::string::npos)
2452  {
2453  // filter is for full path, so add relative path base
2455  fieldFilter, relativePathBase + fieldNode.first))
2456  {
2457  found = false; // reject if match
2458  break;
2459  }
2460  }
2461  else if(StringMacros::wildCardMatch(fieldFilter, fieldNode.first))
2462  {
2463  found = false; // reject if match
2464  break;
2465  }
2466  }
2467 
2468  //__COUTV__(found);
2469 
2470  // if found, new field (since this is first record)
2471  if(found)
2472  {
2473  std::pair<unsigned int /*link col*/, unsigned int /*link id col*/>
2474  linkPair;
2475  bool isGroupLink;
2476 
2477  //__COUTV__(fieldNode.first);
2478  tableView_->getChildLink(
2479  tableView_->findCol(fieldNode.first), isGroupLink, linkPair);
2480 
2481  // add both link columns
2482 
2483  fieldCandidateList.push_back(ConfigurationTree::RecordField(
2484  table_->getTableName(),
2485  uid,
2486  tableView_->getColumnInfo(linkPair.first).getName(),
2487  relativePathBase, // relative path, not including columnName_
2488  &tableView_->getColumnInfo(linkPair.first)));
2489  fieldCount.push_back(1); // init count to 1
2490 
2491  fieldCandidateList.push_back(ConfigurationTree::RecordField(
2492  table_->getTableName(),
2493  uid,
2494  tableView_->getColumnInfo(linkPair.second).getName(),
2495  relativePathBase, // relative path, not including columnName_
2496  &tableView_->getColumnInfo(linkPair.second)));
2497  fieldCount.push_back(1); // init count to 1
2498  }
2499  }
2500  else // not first record
2501  {
2502  // if link fields (MUST BE 2) is in <field candidates list>, increment <field count>
2503  // else ignore
2504  for(j = 0; j < fieldCandidateList.size() - 1; ++j)
2505  {
2506  if((relativePathBase + fieldNode.first) ==
2507  (fieldCandidateList[j].relativePath_ +
2508  fieldCandidateList[j].columnName_))
2509  {
2510  //__COUT__ << "incrementing " << j <<
2511  // " " << fieldCandidateList[j].relativePath_ << __E__;
2512  // found, so increment <field count>
2513  ++fieldCount[j];
2514  ++fieldCount[j + 1]; // increment associated link index too!
2515  break;
2516  }
2517  }
2518  }
2519 
2520  // if depth remaining, then follow link, recursively!
2521  if(depth > 0 && !fieldNode.second.isDisconnected())
2522  fieldNode.second.recursiveGetCommonFields(
2523  fieldCandidateList,
2524  fieldCount,
2525  fieldAcceptList,
2526  fieldRejectList,
2527  depth,
2528  (relativePathBase + fieldNode.first) + "/", // relativePathBase
2529  inFirstRecord // continue inFirstRecord (or not) depth search
2530  );
2531  } // end handle unique link node
2532  } // end field node loop
2533 } // end recursiveGetCommonFields()
2534 
2535 //==============================================================================
2541 std::vector<std::vector<std::pair<std::string, ConfigurationTree>>>
2543  std::map<std::string /*relative-path*/, std::string /*value*/> filterMap,
2544  bool onlyStatusTrue) const
2545 {
2546  std::vector<std::vector<std::pair<std::string, ConfigurationTree>>> retVector;
2547 
2548  //__COUT__ << "Children of node: " << getValueAsString() << __E__;
2549 
2550  bool filtering = filterMap.size();
2551  std::string fieldValue;
2552 
2553  bool createContainer;
2554 
2555  std::vector<std::vector<std::string>> childrenNamesByPriority =
2556  getChildrenNamesByPriority(onlyStatusTrue);
2557 
2558  for(auto& childNamesAtPriority : childrenNamesByPriority)
2559  {
2560  createContainer = true;
2561 
2562  for(auto& childName : childNamesAtPriority)
2563  {
2564  //__COUT__ << "\tChild: " << childName << __E__;
2565 
2566  if(filtering) // if all criteria are not met, then skip
2567  if(!passFilterMap(childName, filterMap))
2568  continue;
2569 
2570  if(createContainer)
2571  {
2572  retVector.push_back(
2573  std::vector<std::pair<std::string, ConfigurationTree>>());
2574  createContainer = false;
2575  }
2576 
2577  retVector[retVector.size() - 1].push_back(
2578  std::pair<std::string, ConfigurationTree>(
2579  childName, this->getNode(childName, true)));
2580  } // end children within priority loop
2581  } // end children by priority loop
2582 
2583  //__COUT__ << "Done w/Children of node: " << getValueAsString() << __E__;
2584  return retVector;
2585 } // end getChildrenByPriority()
2586 
2587 //==============================================================================
2591  const std::string& childName,
2592  std::map<std::string /*relative-path*/, std::string /*value*/> filterMap) const
2593 {
2594  // if all criteria are not met, then skip
2595  bool skip = false;
2596 
2597  // for each filter, check value
2598  for(const auto& filterPair : filterMap)
2599  {
2600  std::string filterPath = childName + "/" + filterPair.first;
2601  __COUTV__(filterPath);
2602 
2603  ConfigurationTree childNode = this->getNode(filterPath);
2604  try
2605  {
2606  // extract field value list
2607  std::vector<std::string> fieldValues;
2609  filterPair.second, fieldValues, std::set<char>({','}) /*delimiters*/);
2610 
2611  __COUTV__(fieldValues.size());
2612 
2613  skip = true;
2614  // for each field check if any match
2615  for(const auto& fieldValue : fieldValues)
2616  {
2617  // Note: that ConfigurationTree maps both fields associated with a
2618  // link to the same node instance. The behavior is likely not
2619  // expected as response for this function.. so for links
2620  // return
2621  // actual value for field name specified i.e. if Table of link
2622  // is requested give that; if linkID is requested give that. use
2623  // TRUE in getValueAsString for proper behavior
2624 
2625  if(childNode.isGroupIDNode())
2626  {
2627  // handle groupID node special, check against set of groupIDs
2628 
2629  bool groupIdFound = false;
2630  std::set<std::string> setOfGroupIDs = childNode.getSetOfGroupIDs();
2631 
2632  for(auto& groupID : setOfGroupIDs)
2633  {
2634  __COUT__ << "\t\tGroupID Check: " << filterPair.first
2635  << " == " << fieldValue << " => "
2636  << StringMacros::decodeURIComponent(fieldValue)
2637  << " ??? " << groupID << __E__;
2638 
2640  StringMacros::decodeURIComponent(fieldValue), groupID))
2641  {
2642  // found a match for the field/groupId pair
2643  __COUT__ << "Found match" << __E__;
2644  groupIdFound = true;
2645  break;
2646  }
2647  } // end groupID search
2648 
2649  if(groupIdFound)
2650  {
2651  // found a match for the field/groupId-set pair
2652  __COUT__ << "Found break match" << __E__;
2653  skip = false;
2654  break;
2655  }
2656  }
2657  else // normal child node, check against value
2658  {
2659  __COUT__ << "\t\tCheck: " << filterPair.first << " == " << fieldValue
2660  << " => " << StringMacros::decodeURIComponent(fieldValue)
2661  << " ??? " << childNode.getValueAsString(true) << __E__;
2662 
2665  childNode.getValueAsString(true)))
2666  {
2667  // found a match for the field/value pair
2668  skip = false;
2669  break;
2670  }
2671  }
2672  }
2673  }
2674  catch(...)
2675  {
2676  __SS__ << "Failed to access filter path '" << filterPath << "' - aborting."
2677  << __E__;
2678 
2679  ss << nodeDump() << __E__;
2680  __SS_THROW__;
2681  }
2682 
2683  if(skip)
2684  break; // no match for this field, so stop checking and skip this
2685  // record
2686  }
2687  return !skip;
2688 } //end passFilterMap()
2689 
2690 //==============================================================================
2699 std::vector<std::pair<std::string, ConfigurationTree>> ConfigurationTree::getChildren(
2700  std::map<std::string /*relative-path*/, std::string /*value*/> filterMap,
2701  bool byPriority,
2702  bool onlyStatusTrue) const
2703 {
2704  std::vector<std::pair<std::string, ConfigurationTree>> retVector;
2705 
2706  __COUTS__(2) << "Children of node: " << getValueAsString() << __E__;
2707 
2708  bool filtering = filterMap.size();
2709  // bool skip;
2710  std::string fieldValue;
2711 
2712  std::vector<std::string> childrenNames = getChildrenNames(byPriority, onlyStatusTrue);
2713  __COUTVS__(2, StringMacros::vectorToString(childrenNames));
2714  for(auto& childName : childrenNames)
2715  {
2716  if(filtering && // if all criteria are not met, then skip
2717  !passFilterMap(childName, filterMap))
2718  continue;
2719 
2720  retVector.push_back(std::pair<std::string, ConfigurationTree>(
2721  childName, this->getNode(childName, true)));
2722  }
2723 
2724  __COUTS__(2) << "Done w/Children of node: " << getValueAsString() << __E__;
2725  return retVector;
2726 } // end getChildren()
2727 
2728 //==============================================================================
2732 std::map<std::string, ConfigurationTree> ConfigurationTree::getChildrenMap(
2733  std::map<std::string /*relative-path*/, std::string /*value*/> filterMap,
2734  bool onlyStatusTrue) const
2735 {
2736  std::map<std::string, ConfigurationTree> retMap;
2737 
2738  bool filtering = filterMap.size();
2739 
2740  //__COUT__ << "Children of node: " << getValueAsString() << __E__;
2741  std::vector<std::string> childrenNames =
2742  getChildrenNames(false /* byPriority */, onlyStatusTrue);
2743  for(auto& childName : childrenNames)
2744  {
2745  //__COUT__ << "\tChild: " << childName << __E__;
2746 
2747  // if all criteria are not met, then skip
2748  if(filtering && !passFilterMap(childName, filterMap))
2749  continue;
2750 
2751  retMap.insert(std::pair<std::string, ConfigurationTree>(
2752  childName, this->getNode(childName)));
2753  }
2754 
2755  //__COUT__ << "Done w/Children of node: " << getValueAsString() << __E__;
2756  return retMap;
2757 } // end getChildrenMap()
2758 
2759 //==============================================================================
2762 {
2763  if(!isUIDNode())
2764  {
2765  __SS__ << "Can not get status of '" << getValueAsString()
2766  << ".' Can only check the status of a UID/Record node!" << __E__;
2767  ss << nodeDump() << __E__;
2768  __SS_THROW__;
2769  }
2770 
2771  bool tmpStatus = true;
2772  try
2773  {
2774  tableView_->getValue(tmpStatus, row_, tableView_->getColStatus());
2775  }
2776  catch(const std::runtime_error& e)
2777  {
2778  //ignore error, assuming does not have a status column
2779  //default to enabled if no status
2780  }
2781  return tmpStatus;
2782 } // end isEnabled()
2783 
2784 //==============================================================================
2785 bool ConfigurationTree::isStatusNode(void) const
2786 {
2787  if(!isValueNode())
2788  return false;
2789 
2790  return col_ == tableView_->getColStatus();
2791 } // end isStatusNode()
2792 
2793 //==============================================================================
2796 std::vector<std::vector<std::string>> ConfigurationTree::getChildrenNamesByPriority(
2797  bool onlyStatusTrue) const
2798 {
2799  std::vector<std::vector<std::string /*child name*/>> retVector;
2800 
2801  if(!tableView_)
2802  {
2803  __SS__ << "Can not get children names of '" << getValueAsString()
2804  << "' with null configuration view pointer!" << __E__;
2805  if(isLinkNode() && isDisconnected())
2806  ss << " This node is a disconnected link to " << getDisconnectedTableName()
2807  << __E__;
2808 
2809  ss << nodeDump() << __E__;
2810  __SS_ONLY_THROW__;
2811  }
2812 
2813  if(row_ == TableView::INVALID && col_ == TableView::INVALID)
2814  {
2815  // this node is table node
2816  // so return all uid node strings that match groupId
2817 
2818  // bool tmpStatus;
2819 
2820  std::vector<std::vector<unsigned int /*group row*/>> groupRowsByPriority =
2821  tableView_->getGroupRowsByPriority(
2822  groupId_ == ""
2823  ? TableView::INVALID
2824  : // if no group ID, take all rows and ignore column, do not attempt link lookup
2825  tableView_->getLinkGroupIDColumn(childLinkIndex_),
2826  groupId_,
2827  onlyStatusTrue);
2828 
2829  // now build vector of vector names by priority
2830  for(const auto& priorityChildRowVector : groupRowsByPriority)
2831  {
2832  retVector.push_back(std::vector<std::string /*child name*/>());
2833  for(const auto& priorityChildRow : priorityChildRowVector)
2834  retVector[retVector.size() - 1].push_back(
2835  tableView_->getDataView()[priorityChildRow][tableView_->getColUID()]);
2836  }
2837  }
2838  else if(row_ == TableView::INVALID)
2839  {
2840  __SS__ << "Malformed ConfigurationTree" << __E__;
2841 
2842  ss << nodeDump() << __E__;
2843  __SS_THROW__;
2844  }
2845  else if(col_ == TableView::INVALID)
2846  {
2847  // this node is uid node
2848  // so return all link and value nodes
2849 
2850  for(unsigned int c = 0; c < tableView_->getNumberOfColumns(); ++c)
2851  if(c == tableView_->getColUID() || // skip UID and linkID columns (only show
2852  // link column, to avoid duplicates)
2853  tableView_->getColumnInfo(c).isChildLinkGroupID() ||
2854  tableView_->getColumnInfo(c).isChildLinkUID())
2855  continue;
2856  else
2857  {
2858  retVector.push_back(std::vector<std::string /*child name*/>());
2859  retVector[retVector.size() - 1].push_back(
2860  tableView_->getColumnInfo(c).getName());
2861  }
2862  }
2863  else // this node is value node, so has no node to choose from
2864  {
2865  // this node is value node, cant go any deeper!
2866  __SS__ << "\n\nError occurred looking for children of nodeName=" << getValueName()
2867  << "\n\n"
2868  << "Invalid depth! getChildrenValues() called from a value point in the "
2869  "Configuration Tree."
2870  << __E__;
2871 
2872  ss << nodeDump() << __E__;
2873  __SS_THROW__;
2874  }
2875 
2876  return retVector;
2877 } // end getChildrenNamesByPriority()
2878 
2879 //==============================================================================
2882 std::vector<std::string> ConfigurationTree::getChildrenNames(
2883  bool byPriority /* = false */, bool onlyStatusTrue /* = false */) const
2884 {
2885  std::vector<std::string /*child name*/> retVector;
2886 
2887  if(isRootNode())
2888  {
2889  for(auto& configPair : configMgr_->getActiveVersions())
2890  {
2891  //__GEN_COUT__ << configPair.first << " " << (int)(configPair.second?1:0) <<
2892  // __E__;
2893  retVector.push_back(configPair.first);
2894  }
2895  return retVector;
2896  }
2897 
2898  if(!tableView_)
2899  {
2900  __SS__ << "Can not get children names of '" << getFieldName() << ":"
2901  << getValueAsString() << "' with null configuration view pointer!"
2902  << __E__;
2903  if(isLinkNode() && isDisconnected())
2904  ss << " This node is a disconnected link to " << getDisconnectedTableName()
2905  << "(" << getDisconnectedLinkID() << ")" << __E__;
2906  __SS_ONLY_THROW__;
2907  }
2908 
2909  if(row_ == TableView::INVALID && col_ == TableView::INVALID)
2910  {
2911  // this node is table node
2912  // so return all uid node strings that match groupId
2913  std::vector<unsigned int /*group row*/> groupRows = tableView_->getGroupRows(
2914  (groupId_ == ""
2915  ? TableView::INVALID
2916  : // if no group ID, take all rows, do not attempt link lookup
2917  tableView_->getLinkGroupIDColumn(childLinkIndex_)),
2918  groupId_,
2919  onlyStatusTrue,
2920  byPriority);
2921 
2922  // now build vector of vector names by priority
2923  for(const auto& groupRow : groupRows)
2924  retVector.push_back(
2925  tableView_->getDataView()[groupRow][tableView_->getColUID()]);
2926 
2927  // bool tmpStatus;
2928  //
2929  // if(byPriority) // reshuffle by priority
2930  // {
2931  // try
2932  // {
2933  // std::map<uint64_t /*priority*/, std::vector<unsigned int /*child row*/>> orderedByPriority;
2934  // std::vector<std::string /*child name*/> retPrioritySet;
2935  //
2936  // unsigned int col = tableView_->getColPriority();
2937  //
2938  // uint64_t tmpPriority;
2939  //
2940  // for(unsigned int r = 0; r < tableView_->getNumberOfRows(); ++r)
2941  // if(groupId_ == "" || tableView_->isEntryInGroup(r, childLinkIndex_, groupId_))
2942  // {
2943  // // check status if needed
2944  // if(onlyStatusTrue)
2945  // {
2946  // tableView_->getValue(tmpStatus, r, tableView_->getColStatus());
2947  //
2948  // if(!tmpStatus)
2949  // continue; // skip those with status false
2950  // }
2951  //
2952  // tableView_->getValue(tmpPriority, r, col);
2953  // // do not accept DEFAULT value of 0.. convert to 100
2954  // orderedByPriority[tmpPriority ? tmpPriority : 100].push_back(r);
2955  // }
2956  //
2957  // // at this point have priority map
2958  // // now build return vector
2959  //
2960  // for(const auto& priorityChildRowVector : orderedByPriority)
2961  // for(const auto& priorityChildRow : priorityChildRowVector.second)
2962  // retVector.push_back(tableView_->getDataView()[priorityChildRow][tableView_->getColUID()]);
2963  //
2964  // __COUT__ << "Returning priority children list." << __E__;
2965  // return retVector;
2966  // }
2967  // catch(std::runtime_error& e)
2968  // {
2969  // __COUT_WARN__ << "Priority configuration not found. Assuming all "
2970  // "children have equal priority. "
2971  // << __E__;
2972  // retVector.clear();
2973  // }
2974  // }
2975  // // else not by priority
2976  //
2977  // for(unsigned int r = 0; r < tableView_->getNumberOfRows(); ++r)
2978  // if(groupId_ == "" || tableView_->isEntryInGroup(r, childLinkIndex_, groupId_))
2979  // {
2980  // // check status if needed
2981  // if(onlyStatusTrue)
2982  // {
2983  // tableView_->getValue(tmpStatus, r, tableView_->getColStatus());
2984  //
2985  // if(!tmpStatus)
2986  // continue; // skip those with status false
2987  // }
2988  //
2989  // retVector.push_back(tableView_->getDataView()[r][tableView_->getColUID()]);
2990  // }
2991  }
2992  else if(row_ == TableView::INVALID)
2993  {
2994  __SS__ << "Malformed ConfigurationTree" << __E__;
2995 
2996  ss << nodeDump() << __E__;
2997  __SS_THROW__;
2998  }
2999  else if(col_ == TableView::INVALID)
3000  {
3001  // this node is uid node
3002  // so return all link and value nodes
3003 
3004  for(unsigned int c = 0; c < tableView_->getNumberOfColumns(); ++c)
3005  if(c == tableView_->getColUID() || // skip UID and linkID columns (only show
3006  // link column, to avoid duplicates)
3007  tableView_->getColumnInfo(c).isChildLinkGroupID() ||
3008  tableView_->getColumnInfo(c).isChildLinkUID())
3009  continue;
3010  else
3011  retVector.push_back(tableView_->getColumnInfo(c).getName());
3012  }
3013  else // this node is value node, so has no node to choose from
3014  {
3015  // this node is value node, cant go any deeper!
3016  __SS__ << "\n\nError occurred looking for children of nodeName=" << getValueName()
3017  << "\n\n"
3018  << "Invalid depth! getChildrenValues() called from a value point in the "
3019  "Configuration Tree."
3020  << __E__;
3021 
3022  ss << nodeDump() << __E__;
3023  __SS_THROW__;
3024  }
3025 
3026  return retVector;
3027 } // end getChildrenNames()
3028 
3029 //==============================================================================
3033 ConfigurationTree ConfigurationTree::getValueAsTreeNode(void) const
3034 {
3035  // check if first character is a /, .. if so try to get value in tree
3036  // if exception, just take value
3037  // note: this call will throw an error, in effect, if not a "value" node
3038  if(!tableView_)
3039  {
3040  __SS__ << "Invalid node for get value." << __E__;
3041  __SS_THROW__;
3042  }
3043 
3044  std::string valueString =
3045  tableView_->getValueAsString(row_, col_, true /* convertEnvironmentVariables */);
3046  //__COUT__ << valueString << __E__;
3047  if(valueString.size() && valueString[0] == '/')
3048  {
3049  //__COUT__ << "Starts with '/' - check if valid tree path: " << valueString <<
3050  // __E__;
3051  try
3052  {
3053  ConfigurationTree retNode = configMgr_->getNode(valueString);
3054  __COUT__ << "Found a valid tree path in value!" << __E__;
3055  return retNode;
3056  }
3057  catch(...)
3058  {
3059  __SS__ << "Invalid tree path." << __E__;
3060  __SS_ONLY_THROW__;
3061  }
3062  }
3063 
3064  {
3065  __SS__ << "Invalid value string '" << valueString
3066  << "' - must start with a '/' character." << __E__;
3067  __SS_ONLY_THROW__;
3068  }
3069 } // end getValueAsTreeNode()
std::map< std::string, TableVersion > getActiveVersions(void) const
getActiveVersions
ConfigurationTree getNode(const std::string &nodeString, bool doNotThrowOnBrokenUIDLinks=false) const
"root/parent/parent/"
const TableBase * getTableByName(const std::string &configurationName) const
const unsigned int & getRow(void) const
getRow
const std::string & getValueDataType(void) const
std::map< std::string, ConfigurationTree > getNodes(const std::string &nodeString) const
getNodes
const TableVersion & getTableVersion(void) const
getTableVersion
bool isDisconnected(void) const
const std::string & getAuthor(void) const
getAuthor
const std::string & getComment(void) const
getComment
std::vector< std::string > getChildrenNames(bool byPriority=false, bool onlyStatusTrue=false) const
bool isEnabled(void) const
same as status()
static const std::string NODE_TYPE_GROUP_TABLE
bool isValueNumberDataType(void) const
ConfigurationTree getNode(const std::string &nodeName, bool doNotThrowOnBrokenUIDLinks=false) const
navigating between nodes
const std::string & getTableName(void) const
getTableName
T getValueWithDefault(const T &defaultValue) const
const unsigned int & getFieldRow(void) const
std::map< std::string, ConfigurationTree > getChildrenMap(std::map< std::string, std::string > filterMap=std::map< std::string, std::string >(), bool onlyStatusTrue=false) const
std::string nodeDump(bool forcePrintout=false) const
used for debugging (when throwing exception)
const std::string & getValueName(void) const
const std::string & getValueAsString(bool returnLinkTableValue=false) const
const ConfigurationManager * getConfigurationManager(void) const
extracting information from node
const std::string & getChildLinkIndex(void) const
getChildLinkIndex
void print(const unsigned int &depth=-1, std::ostream &out=std::cout) const
bool isGroupIDNode(void) const
const std::string & getDisconnectedTableName(void) const
getDisconnectedTableName
bool isValueBoolType(void) const
std::vector< std::pair< std::string, ConfigurationTree > > getChildren(std::map< std::string, std::string > filterMap=std::map< std::string, std::string >(), bool byPriority=false, bool onlyStatusTrue=false) const
std::vector< std::string > getFixedChoices(void) const
bool hasComment(void) const
hasComment
const std::string & getDefaultValue(void) const
const time_t & getTableCreationTime(void) const
getTableCreationTime
std::string getParentLinkIndex(void) const
getParentLinkIndex
const std::string & getUIDAsString(void) const
std::vector< ConfigurationTree::RecordField > getCommonFields(const std::vector< std::string > &recordList, const std::vector< std::string > &fieldAcceptList, const std::vector< std::string > &fieldRejectList, unsigned int depth=-1, bool autoSelectFilterFields=false) const
const unsigned int & getNodeRow(void) const
getNodeRow
std::set< std::string > getSetOfGroupIDs(void) const
bool isValueNode(void) const
const std::string & getFieldName(void) const
alias for getValueName
std::vector< std::vector< std::string > > getChildrenNamesByPriority(bool onlyStatusTrue=false) const
std::set< std::string > getUniqueValuesForField(const std::vector< std::string > &recordList, const std::string &fieldName, std::string *fieldGroupIDChildLinkIndex=0) const
const std::string & getValueType(void) const
bool passFilterMap(const std::string &childName, std::map< std::string, std::string > filterMap) const
~ConfigurationTree(void)
destructor
bool isGroupLinkNode(void) const
std::string getParentLinkID(void) const
getParentLinkID
const std::string & getFieldTableName(void) const
const unsigned int & getColumn(void) const
getColumn
const std::string & getDisconnectedLinkID(void) const
getDisconnectedLinkID
const std::string & getParentTableName(void) const
getParentTableName
bool isUIDLinkNode(void) const
const std::string & getParentRecordName(void) const
getParentRecordName
std::string getEscapedValue(void) const
const TableViewColumnInfo & getColumnInfo(void) const
bool isDefaultValue(void) const
boolean info
std::vector< std::vector< std::pair< std::string, ConfigurationTree > > > getChildrenByPriority(std::map< std::string, std::string > filterMap=std::map< std::string, std::string >(), bool onlyStatusTrue=false) const
const std::string & getParentLinkColumnName(void) const
getParentLinkColumnName
T getValue(void) const
defined in included .icc source
const unsigned int & getFieldColumn(void) const
const std::string & getTableName(void) const
Getters.
Definition: TableBase.cc:814
static const std::string DATATYPE_NUMBER
std::string getChildLinkIndex(void) const
getChildLinkIndex
static const std::string TYPE_UID
NOTE: Do NOT put '-' in static const TYPEs because it will mess up javascript handling in the web gui...
const BitMapInfo & getBitMapInfo(void) const
uses dataChoices CSV fields if type is TYPE_BITMAP_DATA
bool isBoolType(void) const
TODO check if min and max values need a function called getallminmaxforgui or something like that for...
bool isNumberDataType(void) const
isNumberDataType
bool isChildLinkGroupID(void) const
const std::string & getDefaultValue(void) const
returns the configued default value value for this particular column
unsigned int findRow(unsigned int col, const T &value, unsigned int offsetRow=0, bool doNotThrow=false) const
< in included .icc source
std::string getEscapedValueAsString(unsigned int row, unsigned int col, bool convertEnvironmentVariables=true, bool quotesToDoubleQuotes=false) const
Definition: TableView.cc:1024
std::vector< std::vector< unsigned int > > getGroupRowsByPriority(const unsigned int groupIdCol, const std::string &groupID, bool onlyStatusTrue=false) const
Definition: TableView.cc:1520
T validateValueForColumn(const std::string &value, unsigned int col, bool doConvertEnvironmentVariables=true) const
< in included .icc source
unsigned int getColStatus(void) const
Definition: TableView.cc:1407
unsigned int getLinkGroupIDColumn(const std::string &childLinkIndex) const
Definition: TableView.cc:1858
bool getChildLink(const unsigned int &col, bool &isGroup, std::pair< unsigned int, unsigned int > &linkPair) const
Definition: TableView.cc:3605
std::string getValueAsString(unsigned int row, unsigned int col, bool convertEnvironmentVariables=true) const
Definition: TableView.cc:973
std::vector< unsigned int > getGroupRows(const unsigned int groupIdCol, const std::string &groupID, bool onlyStatusTrue=false, bool orderedByPriority=false) const
Definition: TableView.cc:1497
std::set< std::string > getSetOfGroupIDs(const std::string &childLinkIndex, unsigned int row=-1) const
Definition: TableView.cc:1757
void getValue(T &value, unsigned int row, unsigned int col, bool doConvertEnvironmentVariables=true) const
< in included .icc source
unsigned int getColUID(void) const
Definition: TableView.cc:1322
unsigned int findCol(const std::string &name) const
Definition: TableView.cc:1973
unsigned int findRowInGroup(unsigned int col, const T &value, const std::string &groupId, const std::string &childLinkIndex, unsigned int offsetRow=0) const
< in included .icc source
defines used also by OtsConfigurationWizardSupervisor
extracting information from a list of records
static void getVectorFromString(const std::string &inputString, std::vector< std::string > &listToReturn, const std::set< char > &delimiter={',', '|', '&'}, const std::set< char > &whitespace={' ', '\t', '\n', '\r'}, std::vector< char > *listOfDelimiters=0, bool decodeURIComponents=false)
static std::string setToString(const std::set< T > &setToReturn, const std::string &delimeter=", ")
setToString ~
static std::string vectorToString(const std::vector< T > &setToReturn, const std::string &delimeter=", ")
vectorToString ~
static std::string mapToString(const std::map< std::string, T > &mapToReturn, const std::string &primaryDelimeter=", ", const std::string &secondaryDelimeter=": ")
static bool wildCardMatch(const std::string &needle, const std::string &haystack, unsigned int *priorityIndex=0)
Definition: StringMacros.cc:49
static std::string decodeURIComponent(const std::string &data)
static std::string stackTrace(void)