otsdaq  3.03.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  __COUTV__(bitmapString);
321  if(bitmapString == TableViewColumnInfo::DATATYPE_STRING_DEFAULT)
322  {
323  bitmap.isDefault_ = true;
324  //create empty bitmap so size is known
325  for(unsigned int r = 0; r < bmp.numOfRows_; ++r)
326  {
327  bitmap.bitmap_.push_back(std::vector<std::string>());
328  for(unsigned int c = 0; c < bmp.numOfColumns_; ++c)
329  bitmap.bitmap_[r].push_back(std::string());
330  }
331  return;
332  }
333  else
334  bitmap.isDefault_ = false;
335 
336  std::string value;
337 
338  std::map<std::string, size_t> valueMap;
339  if(bmp.mapsToStrings_)
340  {
341  std::vector<std::string> list =
342  StringMacros::getVectorFromString(bmp.mapToStrings_);
343  __COUTTV__(StringMacros::vectorToString(list));
344  for(size_t i = 0; i < list.size(); ++i)
345  valueMap.emplace(std::make_pair(list[i], i));
346  __COUTTV__(StringMacros::mapToString(valueMap));
347  }
348 
349  // extract bit map
350  {
351  bitmap.bitmap_.clear();
352  int row = -1;
353  bool openRow = false;
354  unsigned int startInt = -1;
355  for(unsigned int i = 0; i < bitmapString.length(); i++)
356  {
357  __COUTVS__(2, bitmapString[i]);
358  __COUTVS__(2, row);
359  __COUTVS__(2, openRow);
360  __COUTVS__(2, startInt);
361  __COUTVS__(2, i);
362 
363  if(!openRow) // need start of row
364  {
365  if(bitmapString[i] == '[')
366  { // open a new row
367  openRow = true;
368  ++row;
369  bitmap.bitmap_.push_back(std::vector<std::string>());
370  }
371  else if(bitmapString[i] == ']')
372  {
373  break; // ending bracket, done with string
374  }
375  else if(bitmapString[i] == ',') // end characters found not within
376  // row
377  {
378  //ignore ,'s outside of row [ ] array
379  continue;
380  }
381  }
382  else if(startInt == (unsigned int)-1) // need to find start of number
383  {
384  if(bitmapString[i] == ']') // found end of row, instead of start of
385  // number, assume row ended
386  {
387  openRow = false;
388  }
389  else if((bitmapString[i] >= '0' &&
390  bitmapString[i] <= '9') || // found start of number
391  (bmp.mapsToStrings_ && bitmapString[i] >= 'a' &&
392  bitmapString[i] <=
393  'z') || // found start of string in map-to-string mode
394  (bmp.mapsToStrings_ && bitmapString[i] >= 'A' &&
395  bitmapString[i] <= 'Z'))
396  {
397  startInt = i;
398  }
399  else if(bitmapString[i] == ',') // comma found without number
400  {
401  __SS__ << "Too many ',' characters in bit map configuration"
402  << __E__;
403 
404  ss << nodeDump() << __E__;
405  __SS_ONLY_THROW__;
406  }
407  }
408  else
409  {
410  // looking for end of number (or string map)
411 
412  if(bitmapString[i] ==
413  ']') // found end of row, assume row and number ended
414  {
415  openRow = false;
416 
417  //drop space or end quote
418  unsigned int ii = i;
419  while(ii - startInt > 2 && (bitmapString[ii - 1] == ' ' ||
420  bitmapString[ii - 1] == '\r' ||
421  bitmapString[ii - 1] == '\n' ||
422  bitmapString[ii - 1] == '\t' ||
423  bitmapString[ii - 1] == '"'))
424  --ii; //rewind to last character of string
425  __COUTVS__(2, bitmapString.substr(startInt, ii - startInt));
426  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)
427  {
428  value =
429  valueMap.at(bitmapString.substr(startInt, ii - startInt));
430  __COUTVS__(2, value);
431  }
432  //ignore value map, and return raw string
433  bitmap.bitmap_[row].push_back(
434  bitmapString.substr(startInt, ii - startInt));
435 
436  startInt = -1;
437  }
438  else if(bitmapString[i] == ',') // comma found, assume end of number
439  {
440  //drop space or end quote
441  unsigned int ii = i;
442  while(ii - startInt > 2 && (bitmapString[ii - 1] == ' ' ||
443  bitmapString[ii - 1] == '\r' ||
444  bitmapString[ii - 1] == '\n' ||
445  bitmapString[ii - 1] == '\t' ||
446  bitmapString[ii - 1] == '"'))
447  --ii; //rewind to last character of string
448  __COUTVS__(2, bitmapString.substr(startInt, ii - startInt));
449  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)
450  {
451  value =
452  valueMap.at(bitmapString.substr(startInt, ii - startInt));
453  __COUTVS__(2, value);
454  }
455  //ignore value map, and return raw string
456  bitmap.bitmap_[row].push_back(
457  bitmapString.substr(startInt, ii - startInt));
458 
459  startInt = -1;
460  }
461  }
462  } //ene main string parsing loop
463 
464  if(TTEST(1))
465  {
466  for(unsigned int r = 0; r < bitmap.bitmap_.size(); ++r)
467  {
468  for(unsigned int c = 0; c < bitmap.bitmap_[r].size(); ++c)
469  {
470  __COUTT__ << r << "," << c << " = " << bitmap.bitmap_[r][c]
471  << __E__;
472  }
473  __COUTT__ << "================" << __E__;
474  }
475  }
476  }
477 
478  if(bitmap.bitmap_.size() != bmp.numOfRows_ ||
479  (bmp.numOfRows_ && bitmap.bitmap_[0].size() != bmp.numOfColumns_))
480  {
481  __SS__
482  << "Illegal mismatch in number of rows and columns. Extracted data was "
483  << bitmap.bitmap_.size() << " x "
484  << (bitmap.bitmap_.size() ? bitmap.bitmap_[0].size() : 0)
485  << " and the expected size is " << bmp.numOfRows_ << " x "
486  << bmp.numOfColumns_ << __E__;
487  __SS_THROW__;
488  }
489  }
490  else
491  {
492  __SS__ << "Requesting getValue must be on a value node." << __E__;
493 
494  ss << nodeDump() << __E__;
495  __SS_THROW__;
496  }
497 } // end getValueAsBitMap()
498 
499 //==============================================================================
503 ConfigurationTree::BitMap<std::string> ConfigurationTree::getValueAsBitMap() const
504 {
506  ConfigurationTree::getValueAsBitMap(value);
507  return value;
508 } // end getValueAsBitMap()
509 
510 //==============================================================================
514 {
515  if(row_ != TableView::INVALID &&
516  col_ != TableView::INVALID) // this node is a value node
517  return tableView_->getEscapedValueAsString(row_, col_);
518 
519  __SS__ << "Can not get escaped value except from a value node!"
520  << " This node is type '" << getNodeType() << "." << __E__;
521 
522  ss << nodeDump() << __E__;
523  __SS_THROW__;
524 } // end getEscapedValue()
525 
526 //==============================================================================
528 const std::string& ConfigurationTree::getTableName(void) const
529 {
530  if(!table_)
531  {
532  __SS__ << "Can not get configuration name of node with no configuration pointer! "
533  << "Is there a broken link? " << __E__;
534  if(linkParentTable_)
535  {
536  ss << "Error occurred traversing from " << linkParentTable_->getTableName()
537  << " UID '"
538  << linkParentTable_->getView().getValueAsString(
539  linkBackRow_, linkParentTable_->getView().getColUID())
540  << "' at row " << linkBackRow_ << " col '"
541  << linkParentTable_->getView().getColumnInfo(linkBackCol_).getName()
542  << ".'" << __E__;
543 
544  ss << StringMacros::stackTrace() << __E__;
545  }
546 
547  __SS_ONLY_THROW__;
548  }
549  return table_->getTableName();
550 } // end getTableName()
551 
552 //==============================================================================
554 const std::string& ConfigurationTree::getParentTableName(void) const
555 {
556  if(linkParentTable_)
557  return linkParentTable_->getTableName();
558 
559  __SS__ << "Can not get parent table name of node with no parent table pointer! "
560  << "Was this node initialized correctly? " << __E__;
561  __SS_ONLY_THROW__;
562 } // end getParentTableName()
563 
564 //==============================================================================
566 const unsigned int& ConfigurationTree::getNodeRow(void) const
567 {
568  if(isUIDNode() || isValueNode())
569  return row_;
570 
571  __SS__ << "Can only get row from a UID or value node!" << __E__;
572  if(linkParentTable_)
573  {
574  ss << "Error occurred traversing from " << linkParentTable_->getTableName()
575  << " UID '"
576  << linkParentTable_->getView().getValueAsString(
577  linkBackRow_, linkParentTable_->getView().getColUID())
578  << "' at row " << linkBackRow_ << " col '"
579  << linkParentTable_->getView().getColumnInfo(linkBackCol_).getName() << ".'"
580  << __E__;
581 
582  ss << StringMacros::stackTrace() << __E__;
583  }
584 
585  __SS_ONLY_THROW__;
586 
587 } // end getNodeRow()
588 
589 //==============================================================================
594 const std::string& ConfigurationTree::getFieldTableName(void) const
595 {
596  // if link node, need config name from parent
597  if(isLinkNode())
598  {
599  if(!linkParentTable_)
600  {
601  __SS__ << "Can not get configuration name of link node field with no parent "
602  "configuration pointer!"
603  << __E__;
604  ss << nodeDump() << __E__;
605  __SS_ONLY_THROW__;
606  }
607  return linkParentTable_->getTableName();
608  }
609  else
610  return getTableName();
611 } // end getFieldTableName()
612 
613 //==============================================================================
615 const std::string& ConfigurationTree::getDisconnectedTableName(void) const
616 {
617  if(isLinkNode() && isDisconnected())
618  return disconnectedTargetName_;
619 
620  __SS__ << "Can not get disconnected target name of node unless it is a disconnected "
621  "link node!"
622  << __E__;
623 
624  ss << nodeDump() << __E__;
625  __SS_ONLY_THROW__;
626 } // end getDisconnectedTableName()
627 
628 //==============================================================================
630 const std::string& ConfigurationTree::getDisconnectedLinkID(void) const
631 {
632  if(isLinkNode() && isDisconnected())
633  return disconnectedLinkID_;
634 
635  __SS__ << "Can not get disconnected target name of node unless it is a disconnected "
636  "link node!"
637  << __E__;
638 
639  ss << nodeDump() << __E__;
640  __SS_ONLY_THROW__;
641 } // end getDisconnectedLinkID()
642 
643 //==============================================================================
646 {
647  if(!tableView_)
648  {
649  __SS__ << "Can not get configuration version of node with no config view pointer!"
650  << __E__;
651 
652  ss << nodeDump() << __E__;
653  __SS_ONLY_THROW__;
654  }
655  return tableView_->getVersion();
656 } // end getTableVersion()
657 
658 //==============================================================================
661 {
662  if(!tableView_)
663  {
664  __SS__ << "Can not get configuration creation time of node with no config view "
665  "pointer!"
666  << __E__;
667 
668  ss << nodeDump() << __E__;
669  __SS_ONLY_THROW__;
670  }
671  return tableView_->getCreationTime();
672 } // end getTableCreationTime()
673 
674 //==============================================================================
677 std::set<std::string> ConfigurationTree::getSetOfGroupIDs(void) const
678 {
679  if(!isGroupIDNode())
680  {
681  __SS__ << "Can not get set of group IDs of node with value type of '"
682  << getNodeType() << ".' Node must be a GroupID node." << __E__;
683 
684  ss << nodeDump() << __E__;
685  __SS_ONLY_THROW__;
686  }
687 
688  return tableView_->getSetOfGroupIDs(col_, row_);
689 
690 } // end getSetOfGroupIDs()
691 
692 //==============================================================================
696 std::vector<std::string> ConfigurationTree::getFixedChoices(void) const
697 {
698  if(getValueType() != TableViewColumnInfo::TYPE_FIXED_CHOICE_DATA &&
699  getValueType() != TableViewColumnInfo::TYPE_BITMAP_DATA && !isLinkNode())
700  {
701  __SS__ << "Can not get fixed choices of node with value type of '"
702  << getValueType() << ".' Node must be a link or a value node with type '"
703  << TableViewColumnInfo::TYPE_BITMAP_DATA << "' or '"
704  << TableViewColumnInfo::TYPE_FIXED_CHOICE_DATA << ".'" << __E__;
705 
706  ss << nodeDump() << __E__;
707  __SS_ONLY_THROW__;
708  }
709 
710  std::vector<std::string> retVec;
711 
712  if(isLinkNode())
713  {
714  if(!linkParentTable_)
715  {
716  __SS__
717  << "Can not get fixed choices of node with no parent config view pointer!"
718  << __E__;
719 
720  ss << nodeDump() << __E__;
721  __SS_ONLY_THROW__;
722  }
723 
724  //__COUT__ << getChildLinkIndex() << __E__;
725  //__COUT__ << linkColName_ << __E__;
726 
727  // for links, col_ = -1, column c needs to change (to ChildLink column of pair)
728  // get column from parent config pointer
729 
730  const TableView* parentView = &(linkParentTable_->getView());
731  int c = parentView->findCol(linkColName_);
732 
733  std::pair<unsigned int /*link col*/, unsigned int /*link id col*/> linkPair;
734  bool isGroupLink;
735  parentView->getChildLink(c, isGroupLink, linkPair);
736  c = linkPair.first;
737 
738  std::vector<std::string> choices = parentView->getColumnInfo(c).getDataChoices();
739  for(const auto& choice : choices)
740  retVec.push_back(choice);
741 
742  return retVec;
743  }
744 
745  if(!tableView_)
746  {
747  __SS__ << "Can not get fixed choices of node with no config view pointer!"
748  << __E__;
749 
750  ss << nodeDump() << __E__;
751  __SS_ONLY_THROW__;
752  }
753 
754  // return vector of default + data choices
755  retVec.push_back(tableView_->getColumnInfo(col_).getDefaultValue());
756  std::vector<std::string> choices = tableView_->getColumnInfo(col_).getDataChoices();
757  for(const auto& choice : choices)
758  retVec.push_back(choice);
759 
760  return retVec;
761 } // end getFixedChoices()
762 
763 //==============================================================================
765 const std::string& ConfigurationTree::getComment(void) const
766 {
767  return getNode(TableViewColumnInfo::COL_NAME_COMMENT).getValueAsString() == ""
768  ? TableViewColumnInfo::DATATYPE_COMMENT_DEFAULT
769  : getNode(TableViewColumnInfo::COL_NAME_COMMENT).getValueAsString();
770 } // end getComment()
771 
772 //==============================================================================
774 const std::string& ConfigurationTree::getAuthor(void) const
775 {
776  return getNode(TableViewColumnInfo::COL_NAME_AUTHOR).getValueAsString();
777 } // end getAuthor()
778 
779 //==============================================================================
787 const std::string& ConfigurationTree::getValueAsString(bool returnLinkTableValue) const
788 {
789  //__COUTV__(col_);__COUTV__(row_);__COUTV__(table_);__COUTV__(tableView_);
790 
791  if(isLinkNode())
792  {
793  if(returnLinkTableValue)
794  return linkColValue_;
795  else if(isDisconnected())
796  return ConfigurationTree::DISCONNECTED_VALUE;
797  else if(row_ == TableView::INVALID &&
798  col_ == TableView::INVALID) // this link is groupId node
799  return (groupId_ == "") ? table_->getTableName() : groupId_;
800  else if(col_ == TableView::INVALID) // this link is uid node
801  return tableView_->getDataView()[row_][tableView_->getColUID()];
802  else
803  {
804  __SS__ << "Impossible Link." << __E__;
805 
806  ss << nodeDump() << __E__;
807  __SS_THROW__;
808  }
809  }
810  else if(row_ != TableView::INVALID &&
811  col_ != TableView::INVALID) // this node is a value node
812  return tableView_->getDataView()[row_][col_];
813  else if(row_ == TableView::INVALID &&
814  col_ == TableView::INVALID) // this node is table node maybe with groupId
815  {
816  // if root node, then no table defined
817  if(isRootNode())
818  return ConfigurationTree::ROOT_NAME;
819 
820  return (groupId_ == "") ? table_->getTableName() : groupId_;
821  }
822  else if(row_ == TableView::INVALID)
823  {
824  __SS__ << "Malformed ConfigurationTree" << __E__;
825 
826  ss << nodeDump() << __E__;
827  __SS_THROW__;
828  }
829  else if(col_ == TableView::INVALID) // this node is uid node
830  return tableView_->getDataView()[row_][tableView_->getColUID()];
831  else
832  {
833  __SS__ << "Impossible." << __E__;
834 
835  ss << nodeDump() << __E__;
836  __SS_THROW__;
837  }
838 } // end getValueAsString()
839 
840 //==============================================================================
844 const std::string& ConfigurationTree::getUIDAsString(void) const
845 {
846  if(isValueNode() || isUIDLinkNode() || isUIDNode())
847  return tableView_->getDataView()[row_][tableView_->getColUID()];
848 
849  {
850  __SS__ << "Can not get UID of node with type '" << getNodeType()
851  << ".' Node type must be '" << ConfigurationTree::NODE_TYPE_VALUE
852  << "' or '" << ConfigurationTree::NODE_TYPE_UID_LINK << ".'" << __E__;
853 
854  ss << nodeDump() << __E__;
855  __SS_ONLY_THROW__;
856  }
857 } // end getUIDAsString()
858 
859 //==============================================================================
862 const std::string& ConfigurationTree::getValueDataType(void) const
863 {
864  if(isValueNode())
865  return tableView_->getColumnInfo(col_).getDataType();
866  else // must be std::string
867  return TableViewColumnInfo::DATATYPE_STRING;
868 } // end getValueDataType()
869 
870 //==============================================================================
874 {
875  if(!isValueNode())
876  return false;
877 
878  if(getValueDataType() == TableViewColumnInfo::DATATYPE_STRING)
879  {
880  if(getValueType() == TableViewColumnInfo::TYPE_ON_OFF ||
881  getValueType() == TableViewColumnInfo::TYPE_TRUE_FALSE ||
882  getValueType() == TableViewColumnInfo::TYPE_YES_NO)
883  return getValueAsString() ==
884  TableViewColumnInfo::DATATYPE_BOOL_DEFAULT; // default to OFF, NO,
885  // FALSE
886  else if(getValueType() == TableViewColumnInfo::TYPE_COMMENT)
887  return getValueAsString() == TableViewColumnInfo::DATATYPE_COMMENT_DEFAULT ||
888  getValueAsString() ==
889  ""; // in case people delete default comment, allow blank also
890  else
891  return getValueAsString() == TableViewColumnInfo::DATATYPE_STRING_DEFAULT;
892  }
894  return getValueAsString() == TableViewColumnInfo::DATATYPE_NUMBER_DEFAULT;
895  else if(getValueDataType() == TableViewColumnInfo::DATATYPE_TIME)
896  return getValueAsString() == TableViewColumnInfo::DATATYPE_TIME_DEFAULT;
897  else
898  return false;
899 } // end isDefaultValue()
900 
901 //==============================================================================
905 const std::string& ConfigurationTree::getDefaultValue(void) const
906 {
907  if(!isValueNode())
908  {
909  __SS__ << "Can only get default value from a value node! "
910  << "The node type is " << getNodeType() << __E__;
911 
912  ss << nodeDump() << __E__;
913  __SS_THROW__;
914  }
915 
916  if(getValueDataType() == TableViewColumnInfo::DATATYPE_STRING)
917  {
918  if(getValueType() == TableViewColumnInfo::TYPE_ON_OFF ||
919  getValueType() == TableViewColumnInfo::TYPE_TRUE_FALSE ||
920  getValueType() == TableViewColumnInfo::TYPE_YES_NO)
921  return TableViewColumnInfo::DATATYPE_BOOL_DEFAULT; // default to OFF, NO,
922  // FALSE
923  else if(getValueType() == TableViewColumnInfo::TYPE_COMMENT)
924  return TableViewColumnInfo::
925  DATATYPE_COMMENT_DEFAULT; // in case people delete default comment, allow blank also
926  else
927  return TableViewColumnInfo::DATATYPE_STRING_DEFAULT;
928  }
930  return TableViewColumnInfo::DATATYPE_NUMBER_DEFAULT;
931  else if(getValueDataType() == TableViewColumnInfo::DATATYPE_TIME)
932  return TableViewColumnInfo::DATATYPE_TIME_DEFAULT;
933 
934  {
935  __SS__ << "Can only get default value from a value node! "
936  << "The node type is " << getNodeType() << __E__;
937 
938  ss << nodeDump() << __E__;
939  __SS_THROW__;
940  }
941 } // end isDefaultValue()
942 
943 //==============================================================================
946 const std::string& ConfigurationTree::getValueType(void) const
947 {
948  if(isValueNode())
949  return tableView_->getColumnInfo(col_).getType();
950  else if(isLinkNode() && isDisconnected())
951  return ConfigurationTree::VALUE_TYPE_DISCONNECTED;
952  else // just call all non-value nodes data
953  return ConfigurationTree::VALUE_TYPE_NODE;
954 } // end getValueType()
955 
956 //==============================================================================
960 {
961  if(isValueNode())
962  return tableView_->getColumnInfo(col_);
963  else
964  {
965  __SS__ << "Can only get column info from a value node! "
966  << "The node type is " << getNodeType() << __E__;
967 
968  ss << nodeDump() << __E__;
969  __SS_THROW__;
970  }
971 } // end getColumnInfo()
972 
973 //==============================================================================
975 const unsigned int& ConfigurationTree::getRow(void) const { return row_; }
976 
977 //==============================================================================
979 const unsigned int& ConfigurationTree::getColumn(void) const { return col_; }
980 
981 //==============================================================================
984 const unsigned int& ConfigurationTree::getFieldRow(void) const
985 {
986  if(isLinkNode())
987  {
988  // for links, need to use parent info to determine
989  return linkBackRow_;
990  }
991  else
992  return row_;
993 } // end getFieldRow()
994 
995 //==============================================================================
998 const unsigned int& ConfigurationTree::getFieldColumn(void) const
999 {
1000  if(isLinkNode())
1001  {
1002  // for links, need to use parent info to determine
1003  return linkBackCol_;
1004  }
1005  else
1006  return col_;
1007 } // end getFieldColumn()
1008 
1009 //==============================================================================
1011 const std::string& ConfigurationTree::getChildLinkIndex(void) const
1012 {
1013  if(!isLinkNode())
1014  {
1015  __SS__ << "Can only get link ID from a link! "
1016  << "The node type is " << getNodeType() << __E__;
1017 
1018  ss << nodeDump() << __E__;
1019  __SS_THROW__;
1020  }
1021  return childLinkIndex_;
1022 } // end getChildLinkIndex()
1023 
1024 //==============================================================================
1027 const std::string& ConfigurationTree::getValueName(void) const
1028 {
1029  if(isValueNode())
1030  return tableView_->getColumnInfo(col_).getName();
1031  else if(isLinkNode())
1032  return linkColName_;
1033  else
1034  {
1035  __SS__ << "Can only get value name of a value node!" << __E__;
1036 
1037  ss << nodeDump() << __E__;
1038  __SS_THROW__;
1039  }
1040 } // end getValueName()
1041 
1042 //==============================================================================
1045 ConfigurationTree ConfigurationTree::recurse(const ConfigurationTree& tree,
1046  const std::string& childPath,
1047  bool doNotThrowOnBrokenUIDLinks,
1048  const std::string& originalNodeString)
1049 {
1050  __COUTS__(50) << tree.row_ << " " << tree.col_ << __E__;
1051  __COUTS__(51) << "childPath=" << childPath << " " << childPath.length() << __E__;
1052  if(childPath.length() <= 1) // only "/" or ""
1053  return tree;
1054  return tree.recursiveGetNode(
1055  childPath, doNotThrowOnBrokenUIDLinks, originalNodeString);
1056 } // end recurse()
1057 
1058 //==============================================================================
1068 ConfigurationTree ConfigurationTree::getNode(const std::string& nodeString,
1069  bool doNotThrowOnBrokenUIDLinks) const
1070 {
1071  // __COUT__ << "nodeString=" << nodeString << " len=" << nodeString.length() << __E__;
1072  return recursiveGetNode(
1073  nodeString, doNotThrowOnBrokenUIDLinks, "" /*originalNodeString*/);
1074 } // end getNode() connected to recursiveGetNode()
1075 ConfigurationTree ConfigurationTree::recursiveGetNode(
1076  const std::string& nodeString,
1077  bool doNotThrowOnBrokenUIDLinks,
1078  const std::string& originalNodeString) const
1079 {
1080  __COUTS__(51) << "nodeString=" << nodeString << " len=" << nodeString.length()
1081  << __E__;
1082  __COUTS__(52) << "doNotThrowOnBrokenUIDLinks=" << doNotThrowOnBrokenUIDLinks << __E__;
1083 
1084  // get nodeName (in case of / syntax)
1085  if(nodeString.length() < 1)
1086  {
1087  __SS__ << "Invalid empty node name! Looking for child node '" << nodeString
1088  << "' from node '" << getValue() << "'..." << __E__;
1089 
1090  ss << nodeDump() << __E__;
1091  __SS_THROW__;
1092  }
1093 
1094  // ignore multiple starting slashes
1095  size_t startingIndex = 0;
1096  while(startingIndex < nodeString.length() && nodeString[startingIndex] == '/')
1097  ++startingIndex;
1098  size_t endingIndex = nodeString.find('/', startingIndex);
1099  if(endingIndex == std::string::npos)
1100  endingIndex = nodeString.length();
1101 
1102  std::string nodeName = nodeString.substr(startingIndex, endingIndex - startingIndex);
1103  __COUTS__(51) << "nodeName=" << nodeName << " len=" << nodeName.length() << __E__;
1104 
1105  ++endingIndex;
1106  std::string childPath =
1107  (endingIndex >= nodeString.length() ? "" : nodeString.substr(endingIndex));
1108  __COUTS__(51) << "childPath=" << childPath << " len=" << childPath.length()
1109  << " endingIndex=" << endingIndex
1110  << " nodeString.length()=" << nodeString.length() << __E__;
1111 
1112  // if this tree is beginning at a configuration.. then go to uid, and vice versa
1113 
1114  try
1115  {
1116  __COUTS__(50) << row_ << " " << col_ << " " << groupId_ << " " << tableView_
1117  << __E__;
1118  if(isRootNode())
1119  {
1120  // root node
1121  // so return table node
1122  return recurse(configMgr_->getNode(nodeName),
1123  childPath,
1124  doNotThrowOnBrokenUIDLinks,
1125  originalNodeString);
1126  }
1127  else if(row_ == TableView::INVALID && col_ == TableView::INVALID)
1128  {
1129  // table node
1130 
1131  if(!tableView_)
1132  {
1133  __SS__ << "Missing configView pointer! Likely attempting to access a "
1134  "child node through a disconnected link node."
1135  << __E__;
1136 
1137  ss << nodeDump() << __E__;
1138  __SS_THROW__;
1139  }
1140 
1141  // this node is table node, so return uid node considering groupid
1142  return recurse(
1144  configMgr_,
1145  table_,
1146  "", // no new groupId string, not a link
1147  0 /*linkParentTable_*/,
1148  "", // link node name, not a link
1149  "", // link node value, not a link
1150  TableView::INVALID /*linkBackRow_*/,
1151  TableView::INVALID /*linkBackCol_*/,
1152  "", // ignored disconnected target name, not a link
1153  "", // ignored disconnected link id, not a link
1154  "",
1155  // if this node is group table node, consider that when getting rows
1156  (groupId_ == "")
1157  ? tableView_->findRow(tableView_->getColUID(), nodeName)
1158  : tableView_->findRowInGroup(tableView_->getColUID(),
1159  nodeName,
1160  groupId_,
1161  childLinkIndex_)),
1162  childPath,
1163  doNotThrowOnBrokenUIDLinks,
1164  originalNodeString);
1165  }
1166  else if(row_ == TableView::INVALID)
1167  {
1168  __SS__ << "Malformed ConfigurationTree" << __E__;
1169 
1170  ss << nodeDump() << __E__;
1171  __SS_THROW__;
1172  }
1173  else if(col_ == TableView::INVALID)
1174  {
1175  // this node is uid node, so return link, group link, disconnected, or value
1176  // node
1177 
1178  __COUTS__(51) << "nodeName=" << nodeName << " " << nodeName.length() << __E__;
1179 
1180  // if the value is a unique link ..
1181  // return a uid node!
1182  // if the value is a group link
1183  // return a table node with group string
1184  // else.. return value node
1185 
1186  if(!tableView_)
1187  {
1188  __SS__ << "Missing configView pointer! Likely attempting to access a "
1189  "child node through a disconnected link node."
1190  << __E__;
1191 
1192  ss << nodeDump() << __E__;
1193  __SS_THROW__;
1194  }
1195 
1196  unsigned int c = tableView_->findCol(nodeName);
1197  std::pair<unsigned int /*link col*/, unsigned int /*link id col*/> linkPair;
1198  bool isGroupLink, isLink;
1199  if((isLink = tableView_->getChildLink(c, isGroupLink, linkPair)) &&
1200  !isGroupLink)
1201  {
1202  __COUTS__(50) << "nodeName=" << nodeName << " " << nodeName.length()
1203  << __E__;
1204  //is a unique link, return uid node in new configuration
1205  // need new configuration pointer
1206  // and row of linkUID in new configuration
1207 
1208  const TableBase* childConfig;
1209  try
1210  {
1211  childConfig = configMgr_->getTableByName(
1212  tableView_->getDataView()[row_][linkPair.first]);
1213  childConfig->getView(); // get view as a test for an active view
1214 
1215  if(doNotThrowOnBrokenUIDLinks) // try a test of getting row
1216  {
1217  childConfig->getView().findRow(
1218  childConfig->getView().getColUID(),
1219  tableView_->getDataView()[row_][linkPair.second]);
1220  }
1221  }
1222  catch(...)
1223  {
1224  __COUTS__(50)
1225  << "Found disconnected node! (" << nodeName << ":"
1226  << tableView_->getDataView()[row_][linkPair.first] << ")"
1227  << " at entry with UID "
1228  << tableView_->getDataView()[row_][tableView_->getColUID()]
1229  << __E__;
1230  //do not recurse further
1231  return ConfigurationTree(
1232  configMgr_,
1233  0,
1234  "",
1235  table_, // linkParentTable_
1236  nodeName,
1237  tableView_->getDataView()[row_][c], // this the link node field
1238  // associated value (matches
1239  // targeted column)
1240  row_ /*linkBackRow_*/,
1241  c /*linkBackCol_*/,
1242  tableView_->getDataView()[row_][linkPair.first], // give
1243  // disconnected
1244  // target name
1245  tableView_->getDataView()[row_][linkPair.second], // give
1246  // disconnected
1247  // link ID
1248  tableView_->getColumnInfo(c).getChildLinkIndex());
1249  }
1250 
1251  return recurse(
1252  ConfigurationTree( // this is a link node
1253  configMgr_,
1254  childConfig,
1255  "", // no new groupId string
1256  table_, // linkParentTable_
1257  nodeName, // this is a link node
1258  tableView_->getDataView()[row_][c], // this the link node field
1259  // associated value (matches
1260  // targeted column)
1261  row_ /*linkBackRow_*/,
1262  c /*linkBackCol_*/,
1263  "", // ignore since is connected
1264  "", // ignore since is connected
1265  tableView_->getColumnInfo(c).getChildLinkIndex(),
1266  childConfig->getView().findRow(
1267  childConfig->getView().getColUID(),
1268  tableView_->getDataView()[row_][linkPair.second])),
1269  childPath,
1270  doNotThrowOnBrokenUIDLinks,
1271  originalNodeString);
1272  }
1273  else if(isLink)
1274  {
1275  __COUTS__(50) << "nodeName=" << nodeName << " " << nodeName.length()
1276  << __E__;
1277  // is a group link, return new configuration with group string
1278  // need new configuration pointer
1279  // and group string
1280 
1281  const TableBase* childConfig;
1282  try
1283  {
1284  childConfig = configMgr_->getTableByName(
1285  tableView_->getDataView()[row_][linkPair.first]);
1286  childConfig->getView(); // get view as a test for an active view
1287  }
1288  catch(...)
1289  {
1290  if(tableView_->getDataView()[row_][linkPair.first] !=
1291  TableViewColumnInfo::DATATYPE_LINK_DEFAULT)
1292  __COUT_WARN__
1293  << "Found disconnected node! Failed link target "
1294  "from nodeName="
1295  << nodeName << " to table:id="
1296  << tableView_->getDataView()[row_][linkPair.first] << ":"
1297  << tableView_->getDataView()[row_][linkPair.second] << __E__;
1298 
1299  // do not recurse further
1300  return ConfigurationTree(
1301  configMgr_,
1302  0,
1303  tableView_->getDataView()[row_][linkPair.second], // groupID
1304  table_, // linkParentTable_
1305  nodeName,
1306  tableView_->getDataView()[row_][c], // this the link node field
1307  // associated value (matches
1308  // targeted column)
1309  row_ /*linkBackRow_*/,
1310  c /*linkBackCol_*/,
1311  tableView_->getDataView()[row_][linkPair.first], // give
1312  // disconnected
1313  // target name
1314  tableView_->getDataView()[row_][linkPair.second], // give
1315  // disconnected
1316  // target name
1317  tableView_->getColumnInfo(c).getChildLinkIndex());
1318  }
1319 
1320  return recurse(
1321  ConfigurationTree( // this is a link node
1322  configMgr_,
1323  childConfig,
1324  tableView_
1325  ->getDataView()[row_][linkPair.second], // groupId string
1326  table_, // linkParentTable_
1327  nodeName, // this is a link node
1328  tableView_->getDataView()[row_][c], // this the link node field
1329  // associated value (matches
1330  // targeted column)
1331  row_ /*linkBackRow_*/,
1332  c /*linkBackCol_*/,
1333  "", // ignore since is connected
1334  "", // ignore since is connected
1335  tableView_->getColumnInfo(c).getChildLinkIndex()),
1336  childPath,
1337  doNotThrowOnBrokenUIDLinks,
1338  originalNodeString);
1339  }
1340  else
1341  {
1342  __COUTS__(50) << "nodeName=" << nodeName << " " << nodeName.length()
1343  << __E__;
1344  //return value node
1345  return ConfigurationTree(configMgr_,
1346  table_,
1347  "",
1348  0 /*linkParentTable_*/,
1349  "",
1350  "",
1351  TableView::INVALID /*linkBackRow_*/,
1352  TableView::INVALID /*linkBackCol_*/,
1353  "",
1354  "" /*disconnectedLinkID*/,
1355  "",
1356  row_,
1357  c);
1358  }
1359  }
1360  }
1361  catch(std::runtime_error& e)
1362  {
1363  __SS__ << "\n\nError occurred descending from node '" << getValue()
1364  << "' in table '" << getTableName() << "' looking for child '" << nodeName
1365  << "'\n\n"
1366  << __E__;
1367  ss << "The original node search string was '" << originalNodeString << ".'"
1368  << __E__;
1369  ss << "--- Additional error detail: \n\n" << e.what() << __E__;
1370 
1371  ss << nodeDump() << __E__;
1372  __SS_ONLY_THROW__;
1373  }
1374  catch(...)
1375  {
1376  __SS__ << "\n\nError occurred descending from node '" << getValue()
1377  << "' in table '" << getTableName() << "' looking for child '" << nodeName
1378  << "'\n\n"
1379  << __E__;
1380  ss << "The original node search string was '" << originalNodeString << ".'"
1381  << __E__;
1382  try
1383  {
1384  throw;
1385  } //one more try to printout extra info
1386  catch(const std::exception& e)
1387  {
1388  ss << "Exception message: " << e.what();
1389  }
1390  catch(...)
1391  {
1392  }
1393  ss << nodeDump() << __E__;
1394  __SS_ONLY_THROW__;
1395  }
1396 
1397  // this node is value node, so has no node to choose from
1398  __SS__
1399  << "\n\nError occurred descending from node '" << getValue() << "' in table '"
1400  << getTableName() << "' looking for child '" << nodeName << "'\n\n"
1401  << "Invalid depth! getNode() called from a value point in the Configuration Tree."
1402  << __E__;
1403  ss << "The original node search string was '" << originalNodeString << ".'" << __E__;
1404 
1405  ss << nodeDump() << __E__;
1406  __SS_ONLY_THROW__; // this node is value node, cant go any deeper!
1407 } // end recursiveGetNode()
1408 
1409 //==============================================================================
1411 std::map<std::string, ConfigurationTree> ConfigurationTree::getNodes(
1412  const std::string& nodeString) const
1413 {
1414  if(nodeString.length() < 1)
1415  {
1416  return getChildrenMap();
1417  }
1418 
1419  return getNode(nodeString).getChildrenMap();
1420 }
1421 //==============================================================================
1424 std::string ConfigurationTree::nodeDump(void) const
1425 {
1426  //block cascading node dumps for a couple seconds
1427  // so that user can see the lowest level failure more easily
1428  if(time(0) - ConfigurationTree::LAST_NODE_DUMP_TIME < 3)
1429  {
1430  __COUTS__(20) << "Blocking cascading node dumps..." << __E__;
1431  return "";
1432  }
1433  ConfigurationTree::LAST_NODE_DUMP_TIME = time(0);
1434 
1435  __SS__ << __E__ << __E__;
1436 
1437  ss << "Row=" << (int)row_ << ", Col=" << (int)col_ << ", TablePointer=" << table_
1438  << __E__;
1439 
1440  // stack trace can seg fault on demangle call!... ?
1441  try
1442  {
1443  ss << "\n\n" << StringMacros::stackTrace() << __E__ << __E__;
1444  }
1445  catch(...)
1446  {
1447  } // ignore errors
1448 
1449  ss << "ConfigurationTree::nodeDump() start"
1450  "=====================================\nConfigurationTree::nodeDump():"
1451  << __E__;
1452 
1453  // try each level of debug.. and ignore errors
1454  try
1455  {
1456  ss << "\t"
1457  << "Node dump initiated from node '" << getValueAsString() << "'..." << __E__;
1458  }
1459  catch(...)
1460  {
1461  } // ignore errors
1462  try
1463  {
1464  ss << "\t"
1465  << "Node dump initiated from node '" << getValue() << "' in table '"
1466  << getTableName() << ".'" << __E__;
1467  }
1468  catch(...)
1469  {
1470  } // ignore errors
1471  ss << __E__; //add newline in case of exceptions mid-stringstream
1472 
1473  try
1474  {
1475  //try to avoid recursive throwing of getChildrenNames() until death spiral
1476  if(isTableNode() || isGroupLinkNode())
1477  {
1478  auto children = getChildrenNames();
1479  ss << "\t"
1480  << "Here is the list of possible children (count = " << children.size()
1481  << "):" << __E__;
1482  for(auto& child : children)
1483  ss << "\t\t" << child << __E__;
1484  if(tableView_)
1485  {
1486  ss << "\n\nHere is the culprit table printout:\n\n";
1487  tableView_->print(ss);
1488  }
1489  }
1490 
1491  if(isLinkNode() && isDisconnected())
1492  {
1493  ss << "Is link node." << __E__;
1494  ss << "disconnectedTargetName_ = " << disconnectedTargetName_
1495  << ", disconnectedLinkID_ = " << disconnectedLinkID_ << __E__;
1496 
1497  auto tables = getConfigurationManager()->getActiveVersions();
1498  ss << "\n\t"
1499  << "Here is the list of active tables:" << __E__;
1500  for(auto& table : tables)
1501  ss << "\t\t" << table.first << __E__;
1502  }
1503  }
1504  catch(...)
1505  {
1506  } // ignore errors trying to show children
1507 
1508  ss << "\n\nConfigurationTree::nodeDump() end ====================================="
1509  << __E__;
1510 
1511  return ss.str();
1512 } // end nodeDump()
1513 
1514 //==============================================================================
1515 ConfigurationTree ConfigurationTree::getBackNode(std::string nodeName,
1516  unsigned int backSteps) const
1517 {
1518  for(unsigned int i = 0; i < backSteps; i++)
1519  nodeName = nodeName.substr(0, nodeName.find_last_of('/'));
1520 
1521  return getNode(nodeName);
1522 } // end getBackNode()
1523 
1524 //==============================================================================
1525 ConfigurationTree ConfigurationTree::getForwardNode(std::string nodeName,
1526  unsigned int forwardSteps) const
1527 {
1528  unsigned int s = 0;
1529 
1530  // skip all leading /'s
1531  while(s < nodeName.length() && nodeName[s] == '/')
1532  ++s;
1533 
1534  for(unsigned int i = 0; i < forwardSteps; i++)
1535  s = nodeName.find('/', s) + 1;
1536 
1537  return getNode(nodeName.substr(0, s));
1538 } // end getForwardNode()
1539 
1540 //==============================================================================
1544 {
1545  return (row_ != TableView::INVALID && col_ != TableView::INVALID);
1546 } // end isValueNode()
1547 
1548 //==============================================================================
1552 {
1553  return isValueNode() && tableView_->getColumnInfo(col_).isBoolType();
1554 } // end isValueBoolType()
1555 
1556 //==============================================================================
1560 {
1561  return isValueNode() && tableView_->getColumnInfo(col_).isNumberDataType();
1562 } // end isValueBoolType()
1563 
1564 //==============================================================================
1570 {
1571  if(!isLinkNode())
1572  {
1573  __SS__ << "\n\nError occurred testing link connection at node with value '"
1574  << getValue() << "' in table '" << getTableName() << "'\n\n"
1575  << __E__;
1576  ss << "This is not a Link node! It is node type '" << getNodeType()
1577  << ".' Only a Link node can be disconnected." << __E__;
1578 
1579  ss << nodeDump() << __E__;
1580  __SS_ONLY_THROW__;
1581  }
1582 
1583  return !table_ || !tableView_;
1584 } // end isDisconnected()
1585 
1586 //==============================================================================
1589 bool ConfigurationTree::isLinkNode(void) const { return linkColName_ != ""; }
1590 
1591 //==============================================================================
1594 const std::string ConfigurationTree::NODE_TYPE_GROUP_TABLE = "GroupTableNode";
1595 const std::string ConfigurationTree::NODE_TYPE_TABLE = "TableNode";
1596 const std::string ConfigurationTree::NODE_TYPE_GROUP_LINK = "GroupLinkNode";
1597 const std::string ConfigurationTree::NODE_TYPE_UID_LINK = "UIDLinkNode";
1598 const std::string ConfigurationTree::NODE_TYPE_VALUE = "ValueNode";
1599 const std::string ConfigurationTree::NODE_TYPE_UID = "UIDNode";
1600 const std::string ConfigurationTree::NODE_TYPE_ROOT = "RootNode";
1601 
1602 std::string ConfigurationTree::getNodeType(void) const
1603 {
1604  if(isRootNode())
1605  return ConfigurationTree::NODE_TYPE_ROOT;
1606  if(isTableNode() && groupId_ != "")
1608  if(isTableNode())
1609  return ConfigurationTree::NODE_TYPE_TABLE;
1610  if(isGroupLinkNode())
1611  return ConfigurationTree::NODE_TYPE_GROUP_LINK;
1612  if(isLinkNode())
1613  return ConfigurationTree::NODE_TYPE_UID_LINK;
1614  if(isValueNode())
1615  return ConfigurationTree::NODE_TYPE_VALUE;
1616  return ConfigurationTree::NODE_TYPE_UID;
1617 } // end getNodeType()
1618 
1619 //==============================================================================
1623 {
1624  return (isLinkNode() && groupId_ != "");
1625 }
1626 
1627 //==============================================================================
1631 {
1632  return (isLinkNode() && groupId_ == "");
1633 } // end isUIDLinkNode()
1634 
1635 //==============================================================================
1639 {
1640  return (isValueNode() && tableView_->getColumnInfo(col_).isGroupID());
1641 } // end isGroupIDNode()
1642 
1643 //==============================================================================
1647 {
1648  return (row_ != TableView::INVALID && col_ == TableView::INVALID);
1649 }
1650 
1651 //==============================================================================
1667 std::vector<ConfigurationTree::RecordField> ConfigurationTree::getCommonFields(
1668  const std::vector<std::string /*uid*/>& recordList,
1669  const std::vector<std::string /*relative-path*/>& fieldAcceptList,
1670  const std::vector<std::string /*relative-path*/>& fieldRejectList,
1671  unsigned int depth,
1672  bool autoSelectFilterFields) const
1673 {
1674  // enforce that starting point is a table node
1675  if(!isRootNode() && !isTableNode())
1676  {
1677  __SS__ << "Can only get getCommonFields from a root or table node! "
1678  << "The node type is " << getNodeType() << __E__;
1679 
1680  ss << nodeDump() << __E__;
1681  __SS_THROW__;
1682  }
1683 
1684  std::vector<ConfigurationTree::RecordField> fieldCandidateList;
1685  std::vector<int> fieldCount; //-1 := guaranteed, else count must match num of records
1686 
1687  --depth; // decrement for recursion
1688 
1689  // for each record in <record list>
1690  // loop through all record's children
1691  // if isValueNode (value nodes are possible field candidates!)
1692  // if first uid record
1693  // add field to <field candidates list> if in <field filter list>
1694  // mark <field count> as guaranteed -1 (all these fields must be common
1695  // for UIDs in same table)
1696  // else not first uid record, do not need to check, must be same as first
1697  // record! else if depth > 0 and UID-Link Node recursively (call
1698  // recursiveGetCommonFields())
1699  // =====================
1700  // Start recursiveGetCommonFields()
1701  // --depth;
1702  // loop through all children
1703  // if isValueNode (value nodes are possible field candidates!)
1704  // if first uid record
1705  // add field to <field candidates list> if in <field
1706  // filter list> initial mark <field count> as 1
1707  // else
1708  // if field is in <field candidates list>,
1709  // increment <field count> for field candidate
1710  // else if field is not in list, ignore field
1711  // else if depth > 0 and is UID-Link
1712  // if Link Table/UID pair is not found in <field candidates
1713  // list> (avoid endless loops through tree)
1714  // recursiveGetCommonFields()
1715  // =====================
1716  //
1717  //
1718  // loop through all field candidates
1719  // remove those with <field count> != num of records
1720  //
1721  //
1722  // return result
1723 
1724  bool found; // used in loops
1725  // auto tableName = isRootNode()?"/":getTableName(); //all records will share this
1726  // table name
1727 
1728  // if no records, just return table fields
1729  if(!recordList.size() && tableView_)
1730  {
1731  const std::vector<TableViewColumnInfo>& colInfo = tableView_->getColumnsInfo();
1732 
1733  for(unsigned int col = 0; col < colInfo.size(); ++col)
1734  {
1735  __COUTS__(11) << "Considering field " << colInfo[col].getName() << __E__;
1736 
1737  // check field accept filter list
1738  found = fieldAcceptList.size() ? false : true; // accept if no filter
1739  // list
1740  for(const auto& fieldFilter : fieldAcceptList)
1741  if(StringMacros::wildCardMatch(fieldFilter, colInfo[col].getName()))
1742  {
1743  found = true;
1744  break;
1745  }
1746 
1747  if(found)
1748  {
1749  // check field reject filter list
1750 
1751  found = true; // accept if no filter list
1752  for(const auto& fieldFilter : fieldRejectList)
1753  if(StringMacros::wildCardMatch(fieldFilter, colInfo[col].getName()))
1754  {
1755  found = false; // reject if match
1756  break;
1757  }
1758  }
1759 
1760  // if found, new field (since this is first record)
1761  if(found)
1762  {
1763  __COUTS__(11) << "FOUND field " << colInfo[col].getName() << __E__;
1764 
1765  if(colInfo[col].isChildLink())
1766  {
1767  __COUTS__(11)
1768  << "isGroupLinkNode " << colInfo[col].getName() << __E__;
1769 
1770  // must get column info differently for group link column
1771 
1772  std::pair<unsigned int /*link col*/, unsigned int /*link id col*/>
1773  linkPair;
1774  bool isGroupLink;
1775  tableView_->getChildLink(col, isGroupLink, linkPair);
1776 
1777  // add both link columns
1778 
1779  fieldCandidateList.push_back(ConfigurationTree::RecordField(
1780  table_->getTableName(),
1781  "", // uid
1782  tableView_->getColumnInfo(linkPair.first).getName(),
1783  "", // relative path, not including columnName_
1784  &tableView_->getColumnInfo(linkPair.first)));
1785  fieldCount.push_back(-1); // mark guaranteed field
1786 
1787  fieldCandidateList.push_back(ConfigurationTree::RecordField(
1788  table_->getTableName(),
1789  "", // uid
1790  tableView_->getColumnInfo(linkPair.second).getName(),
1791  "", // relative path, not including columnName_
1792  &tableView_->getColumnInfo(linkPair.second)));
1793  fieldCount.push_back(-1); // mark guaranteed field
1794  }
1795  else // value node
1796  {
1797  fieldCandidateList.push_back(ConfigurationTree::RecordField(
1798  table_->getTableName(),
1799  "", // uid
1800  colInfo[col].getName(),
1801  "", // relative path, not including columnName_
1802  &colInfo[col]));
1803  fieldCount.push_back(1); // init count to 1
1804  }
1805  }
1806  } // end table column loop
1807  } // end no record handling
1808 
1809  for(unsigned int i = 0; i < recordList.size(); ++i)
1810  {
1811  __COUTS__(11) << "Checking " << recordList[i] << __E__;
1812  ConfigurationTree node = getNode(recordList[i]);
1813 
1814  node.recursiveGetCommonFields(fieldCandidateList,
1815  fieldCount,
1816  fieldAcceptList,
1817  fieldRejectList,
1818  depth,
1819  "", // relativePathBase
1820  !i // continue inFirstRecord (or not) depth search
1821  );
1822 
1823  } // end record loop
1824 
1825  __COUT__ << "======================= check for count = " << (int)recordList.size()
1826  << __E__;
1827 
1828  // loop through all field candidates
1829  // remove those with <field count> != num of records
1830  for(unsigned int i = 0; i < fieldCandidateList.size(); ++i)
1831  {
1832  __COUTS__(11) << "Checking " << fieldCandidateList[i].relativePath_
1833  << fieldCandidateList[i].columnName_ << " = " << fieldCount[i]
1834  << __E__;
1835  if(recordList.size() != 0 && fieldCount[i] != -1 &&
1836  fieldCount[i] != (int)recordList.size())
1837  {
1838  __COUTS__(11) << "Erasing " << fieldCandidateList[i].relativePath_
1839  << fieldCandidateList[i].columnName_ << __E__;
1840 
1841  fieldCount.erase(fieldCount.begin() + i);
1842  fieldCandidateList.erase(fieldCandidateList.begin() + i);
1843  --i; // rewind to look at next after deleted
1844  }
1845  }
1846 
1847  for(unsigned int i = 0; i < fieldCandidateList.size(); ++i)
1848  __COUTS__(11) << "Pre-Final " << fieldCandidateList[i].relativePath_
1849  << fieldCandidateList[i].columnName_ << __E__;
1850 
1851  if(autoSelectFilterFields)
1852  {
1853  // filter for just 3 of the best filter fields
1854  // i.e. preference for GroupID, On/Off, and FixedChoice fields.
1855  std::set<std::pair<unsigned int /*fieldPriority*/, unsigned int /*fieldIndex*/>>
1856  prioritySet;
1857 
1858  unsigned int priorityPenalty;
1859  for(unsigned int i = 0; i < fieldCandidateList.size(); ++i)
1860  {
1861  __COUTS__(11) << "Option [" << i << "] "
1862  << fieldCandidateList[i].relativePath_
1863  << fieldCandidateList[i].columnName_ << " : "
1864  << fieldCandidateList[i].columnInfo_->getType() << ":"
1865  << fieldCandidateList[i].columnInfo_->getDataType() << __E__;
1866 
1867  priorityPenalty = std::count(fieldCandidateList[i].relativePath_.begin(),
1868  fieldCandidateList[i].relativePath_.end(),
1869  '/') *
1870  20; // penalize if not top level
1871 
1872  if(fieldCandidateList[i].columnInfo_->isBoolType() &&
1873  (fieldCandidateList[i].columnName_ ==
1874  TableViewColumnInfo::COL_NAME_STATUS ||
1875  fieldCandidateList[i].columnName_ ==
1876  TableViewColumnInfo::COL_NAME_ENABLED))
1877  {
1878  priorityPenalty += 0;
1879  }
1880  else if(fieldCandidateList[i].columnInfo_->isGroupID())
1881  {
1882  priorityPenalty += 1;
1883  }
1884  else if(fieldCandidateList[i].columnInfo_->isBoolType())
1885  {
1886  priorityPenalty += 3;
1887  }
1888  else if(fieldCandidateList[i].columnInfo_->getType() ==
1889  TableViewColumnInfo::TYPE_FIXED_CHOICE_DATA)
1890  {
1891  priorityPenalty += 3;
1892  }
1893  else if(fieldCandidateList[i].columnInfo_->getType() ==
1894  TableViewColumnInfo::TYPE_DATA)
1895  {
1896  priorityPenalty += 10;
1897  }
1898  else // skip other fields and mark for erasing
1899  {
1900  fieldCandidateList[i].tableName_ =
1901  ""; // clear table name as indicator for erase
1902  continue;
1903  }
1904  prioritySet.emplace(
1905  std::make_pair(priorityPenalty /*fieldPriority*/, i /*fieldIndex*/));
1906  __COUTS__(11) << "Option [" << i << "] "
1907  << fieldCandidateList[i].relativePath_
1908  << fieldCandidateList[i].columnName_ << " : "
1909  << fieldCandidateList[i].columnInfo_->getType() << ":"
1910  << fieldCandidateList[i].columnInfo_->getDataType()
1911  << "... priority = " << priorityPenalty << __E__;
1912 
1913  } // done ranking fields
1914 
1915  __COUTV__(StringMacros::setToString(prioritySet));
1916 
1917  // now choose the top 3, and delete the rest
1918  // clear table name to indicate field should be erased
1919  {
1920  unsigned int cnt = 0;
1921  for(const auto& priorityFieldIndex : prioritySet)
1922  if(++cnt > 3) // then mark for erasing
1923  {
1924  __COUTS__(11)
1925  << cnt << " marking "
1926  << fieldCandidateList[priorityFieldIndex.second].relativePath_
1927  << fieldCandidateList[priorityFieldIndex.second].columnName_
1928  << __E__;
1929  fieldCandidateList[priorityFieldIndex.second].tableName_ =
1930  ""; // clear table name as indicator for erase
1931  }
1932  }
1933 
1934  for(unsigned int i = 0; i < fieldCandidateList.size(); ++i)
1935  {
1936  if(fieldCandidateList[i].tableName_ == "") // then erase
1937  {
1938  __COUTS__(11) << "Erasing " << fieldCandidateList[i].relativePath_
1939  << fieldCandidateList[i].columnName_ << __E__;
1940  fieldCandidateList.erase(fieldCandidateList.begin() + i);
1941  --i; // rewind to look at next after deleted
1942  }
1943  }
1944  } // end AUTO filter field selection
1945 
1946  for(unsigned int i = 0; i < fieldCandidateList.size(); ++i)
1947  __COUT__ << "Final " << fieldCandidateList[i].relativePath_
1948  << fieldCandidateList[i].columnName_ << __E__;
1949 
1950  return fieldCandidateList;
1951 } // end getCommonFields()
1952 
1953 //==============================================================================
1959 std::set<std::string /*unique-value*/> ConfigurationTree::getUniqueValuesForField(
1960  const std::vector<std::string /*relative-path*/>& recordList,
1961  const std::string& fieldName,
1962  std::string* fieldGroupIDChildLinkIndex /* =0 */) const
1963 {
1964  if(fieldGroupIDChildLinkIndex)
1965  *fieldGroupIDChildLinkIndex = "";
1966 
1967  // enforce that starting point is a table node
1968  if(!isTableNode())
1969  {
1970  __SS__ << "Can only get getCommonFields from a table node! "
1971  << "The node type is " << getNodeType() << __E__;
1972 
1973  ss << nodeDump() << __E__;
1974  __SS_THROW__;
1975  }
1976 
1977  std::set<std::string /*unique-value*/> uniqueValues;
1978 
1979  // for each record in <record list>
1980  // emplace value at field into set
1981  //
1982  // return result
1983 
1984  // if no records, just return fieldGroupIDChildLinkIndex
1985  if(!recordList.size() && tableView_ && fieldGroupIDChildLinkIndex)
1986  {
1987  const TableViewColumnInfo& colInfo =
1988  tableView_->getColumnInfo(tableView_->findCol(fieldName));
1989 
1990  if(colInfo.isGroupID())
1991  *fieldGroupIDChildLinkIndex = colInfo.getChildLinkIndex();
1992 
1993  } // end no records
1994 
1995  for(unsigned int i = 0; i < recordList.size(); ++i)
1996  {
1997  //__COUT__ << "Checking " << recordList[i] << __E__;
1998 
1999  // Note: that ConfigurationTree maps both fields associated with a link
2000  // to the same node instance.
2001  // The behavior is likely not expected as response for this function..
2002  // so for links return actual value for field name specified
2003  // i.e. if Table of link is requested give that; if linkID is requested give
2004  // that. use TRUE in getValueAsString for proper behavior
2005 
2006  ConfigurationTree node = getNode(recordList[i]).getNode(fieldName);
2007 
2008  if(node.isGroupIDNode())
2009  {
2010  // handle groupID node special
2011 
2012  //__COUT__ << "GroupID field " << fieldName << __E__;
2013 
2014  // first time, get field's GroupID Child Link Index, if applicable
2015  if(i == 0 && fieldGroupIDChildLinkIndex)
2016  *fieldGroupIDChildLinkIndex = node.getColumnInfo().getChildLinkIndex();
2017 
2018  // return set of groupIDs individually
2019 
2020  std::set<std::string> setOfGroupIDs = node.getSetOfGroupIDs();
2021  for(auto& groupID : setOfGroupIDs)
2022  uniqueValues.emplace(groupID);
2023  }
2024  else // normal record, return value as string
2025  uniqueValues.emplace(node.getValueAsString(true));
2026 
2027  } // end record loop
2028 
2029  return uniqueValues;
2030 } // end getUniqueValuesForField()
2031 
2032 //==============================================================================
2035 void ConfigurationTree::recursiveGetCommonFields(
2036  std::vector<ConfigurationTree::RecordField>& fieldCandidateList,
2037  std::vector<int>& fieldCount,
2038  const std::vector<std::string /*relative-path*/>& fieldAcceptList,
2039  const std::vector<std::string /*relative-path*/>& fieldRejectList,
2040  unsigned int depth,
2041  const std::string& relativePathBase,
2042  bool inFirstRecord) const
2043 {
2044  //__COUT__ << depth << ":relativePathBase " << relativePathBase <<
2045  // " + " << inFirstRecord <<__E__;
2046  --depth;
2047 
2048  // clang-format off
2049  // =====================
2050  // Start recursiveGetCommonFields()
2051  // --depth;
2052  // loop through all children
2053  // if isValueNode (value nodes are possible field candidates!)
2054  // if first uid record
2055  // add field to <field candidates list> if in <field filter list>
2056  // initial mark <field count> as 1
2057  // else
2058  // if field is in list,
2059  // increment count for field candidate
2060  // //?increment fields in list count for record
2061  // else if field is not in list, discard field
2062  // else if depth > 0 and is UID-Link
2063  // if Link Table/UID pair is not found in <field candidates list>
2064  // (avoid endless loops through tree)
2065  // recursiveGetCommonFields()
2066  // =====================
2067  // clang-format on
2068 
2069  bool found; // used in loops
2070  auto tableName = getTableName(); // all fields will share this table name
2071  auto uid = getUIDAsString(); // all fields will share this uid
2072  unsigned int j;
2073 
2074  auto recordChildren = getChildren();
2075  for(const auto& fieldNode : recordChildren)
2076  {
2077  //__COUT__ << "All... " << fieldNode.second.getNodeType() <<
2078  // " -- " << (relativePathBase + fieldNode.first) <<
2079  // " + " << inFirstRecord <<__E__;
2080 
2081  if(fieldNode.second.isValueNode() || fieldNode.second.isGroupLinkNode())
2082  {
2083  // skip author and record insertion time
2084  if(fieldNode.second.isValueNode())
2085  {
2086  if(fieldNode.second.getColumnInfo().getType() ==
2087  TableViewColumnInfo::TYPE_AUTHOR ||
2088  fieldNode.second.getColumnInfo().getType() ==
2089  TableViewColumnInfo::TYPE_TIMESTAMP)
2090  continue;
2091 
2092  //__COUT__ << "isValueNode " << fieldNode.first << __E__;
2093  }
2094 
2095  if(inFirstRecord) // first uid record
2096  {
2097  //__COUT__ << "Checking... " << fieldNode.second.getNodeType() <<
2098  // " -- " << (relativePathBase + fieldNode.first) <<
2099  // "-- depth=" << depth << __E__;
2100 
2101  // check field accept filter list
2102  found = fieldAcceptList.size() ? false : true; // accept if no filter
2103  // list
2104  for(const auto& fieldFilter : fieldAcceptList)
2105  if(fieldFilter.find('/') != std::string::npos)
2106  {
2107  // filter is for full path, so add relative path base
2109  fieldFilter, relativePathBase + fieldNode.first))
2110  {
2111  found = true;
2112  break;
2113  }
2114  }
2115  else if(StringMacros::wildCardMatch(fieldFilter, fieldNode.first))
2116  {
2117  found = true;
2118  break;
2119  }
2120 
2121  if(found)
2122  {
2123  // check field reject filter list
2124 
2125  found = true; // accept if no filter list
2126  for(const auto& fieldFilter : fieldRejectList)
2127  if(fieldFilter.find('/') != std::string::npos)
2128  {
2129  // filter is for full path, so add relative path base
2131  fieldFilter, relativePathBase + fieldNode.first))
2132  {
2133  found = false; // reject if match
2134  break;
2135  }
2136  }
2137  else if(StringMacros::wildCardMatch(fieldFilter, fieldNode.first))
2138  {
2139  found = false; // reject if match
2140  break;
2141  }
2142  }
2143 
2144  // if found, new field (since this is first record)
2145  if(found)
2146  {
2147  //__COUT__ << "FOUND field " <<
2148  // (relativePathBase + fieldNode.first) << __E__;
2149 
2150  if(fieldNode.second.isGroupLinkNode())
2151  {
2152  //__COUT__ << "isGroupLinkNode " << fieldNode.first << __E__;
2153 
2154  // must get column info differently for group link column
2155 
2156  std::pair<unsigned int /*link col*/, unsigned int /*link id col*/>
2157  linkPair;
2158  bool isGroupLink;
2159  tableView_->getChildLink(
2160  tableView_->findCol(fieldNode.first), isGroupLink, linkPair);
2161 
2162  // add both link columns
2163 
2164  fieldCandidateList.push_back(ConfigurationTree::RecordField(
2165  table_->getTableName(),
2166  uid,
2167  tableView_->getColumnInfo(linkPair.first).getName(),
2168  relativePathBase, // relative path, not including columnName_
2169  &tableView_->getColumnInfo(linkPair.first)));
2170  fieldCount.push_back(1); // init count to 1
2171 
2172  fieldCandidateList.push_back(ConfigurationTree::RecordField(
2173  table_->getTableName(),
2174  uid,
2175  tableView_->getColumnInfo(linkPair.second).getName(),
2176  relativePathBase, // relative path, not including columnName_
2177  &tableView_->getColumnInfo(linkPair.second)));
2178  fieldCount.push_back(1); // init count to 1
2179  }
2180  else // value node
2181  {
2182  fieldCandidateList.push_back(ConfigurationTree::RecordField(
2183  tableName,
2184  uid,
2185  fieldNode.first,
2186  relativePathBase, // relative path, not including columnName_
2187  &fieldNode.second.getColumnInfo()));
2188  fieldCount.push_back(1); // init count to 1
2189  }
2190  }
2191  }
2192  else // not first record
2193  {
2194  // if field is in <field candidates list>, increment <field count>
2195  // else ignore
2196  for(j = 0; j < fieldCandidateList.size(); ++j)
2197  {
2198  if((relativePathBase + fieldNode.first) ==
2199  (fieldCandidateList[j].relativePath_ +
2200  fieldCandidateList[j].columnName_))
2201  {
2202  //__COUT__ << "incrementing " << j <<
2203  // " " << fieldCandidateList[j].relativePath_ << __E__;
2204  // found, so increment <field count>
2205  ++fieldCount[j];
2206  if(fieldNode.second.isGroupLinkNode() &&
2207  j + 1 < fieldCandidateList.size())
2208  ++fieldCount[j + 1]; // increment associated link index too!
2209  break;
2210  }
2211  }
2212  }
2213  } // end value and group link node handling
2214  else if(fieldNode.second.isUIDLinkNode())
2215  {
2216  //__COUT__ << "isUIDLinkNode " << (relativePathBase + fieldNode.first) <<
2217  // " + " << inFirstRecord << __E__;
2218 
2219  if(inFirstRecord) // first uid record
2220  {
2221  // check field accept filter list
2222  found =
2223  fieldAcceptList.size() ? false : true; // accept if no filter list
2224  for(const auto& fieldFilter : fieldAcceptList)
2225  if(fieldFilter.find('/') != std::string::npos)
2226  {
2227  // filter is for full path, so add relative path base
2229  fieldFilter, relativePathBase + fieldNode.first))
2230  {
2231  found = true;
2232  break;
2233  }
2234  }
2235  else if(StringMacros::wildCardMatch(fieldFilter, fieldNode.first))
2236  {
2237  found = true;
2238  break;
2239  }
2240 
2241  if(found)
2242  {
2243  // check field reject filter list
2244 
2245  found = true; // accept if no filter list
2246  for(const auto& fieldFilter : fieldRejectList)
2247  if(fieldFilter.find('/') != std::string::npos)
2248  {
2249  // filter is for full path, so add relative path base
2251  fieldFilter, relativePathBase + fieldNode.first))
2252  {
2253  found = false; // reject if match
2254  break;
2255  }
2256  }
2257  else if(StringMacros::wildCardMatch(fieldFilter, fieldNode.first))
2258  {
2259  found = false; // reject if match
2260  break;
2261  }
2262  }
2263 
2264  //__COUTV__(found);
2265 
2266  // if found, new field (since this is first record)
2267  if(found)
2268  {
2269  std::pair<unsigned int /*link col*/, unsigned int /*link id col*/>
2270  linkPair;
2271  bool isGroupLink;
2272 
2273  //__COUTV__(fieldNode.first);
2274  tableView_->getChildLink(
2275  tableView_->findCol(fieldNode.first), isGroupLink, linkPair);
2276 
2277  // add both link columns
2278 
2279  fieldCandidateList.push_back(ConfigurationTree::RecordField(
2280  table_->getTableName(),
2281  uid,
2282  tableView_->getColumnInfo(linkPair.first).getName(),
2283  relativePathBase, // relative path, not including columnName_
2284  &tableView_->getColumnInfo(linkPair.first)));
2285  fieldCount.push_back(1); // init count to 1
2286 
2287  fieldCandidateList.push_back(ConfigurationTree::RecordField(
2288  table_->getTableName(),
2289  uid,
2290  tableView_->getColumnInfo(linkPair.second).getName(),
2291  relativePathBase, // relative path, not including columnName_
2292  &tableView_->getColumnInfo(linkPair.second)));
2293  fieldCount.push_back(1); // init count to 1
2294  }
2295  }
2296  else // not first record
2297  {
2298  // if link fields (MUST BE 2) is in <field candidates list>, increment <field count>
2299  // else ignore
2300  for(j = 0; j < fieldCandidateList.size() - 1; ++j)
2301  {
2302  if((relativePathBase + fieldNode.first) ==
2303  (fieldCandidateList[j].relativePath_ +
2304  fieldCandidateList[j].columnName_))
2305  {
2306  //__COUT__ << "incrementing " << j <<
2307  // " " << fieldCandidateList[j].relativePath_ << __E__;
2308  // found, so increment <field count>
2309  ++fieldCount[j];
2310  ++fieldCount[j + 1]; // increment associated link index too!
2311  break;
2312  }
2313  }
2314  }
2315 
2316  // if depth remaining, then follow link, recursively!
2317  if(depth > 0 && !fieldNode.second.isDisconnected())
2318  fieldNode.second.recursiveGetCommonFields(
2319  fieldCandidateList,
2320  fieldCount,
2321  fieldAcceptList,
2322  fieldRejectList,
2323  depth,
2324  (relativePathBase + fieldNode.first) + "/", // relativePathBase
2325  inFirstRecord // continue inFirstRecord (or not) depth search
2326  );
2327  } // end handle unique link node
2328  } // end field node loop
2329 } // end recursiveGetCommonFields()
2330 
2331 //==============================================================================
2337 std::vector<std::vector<std::pair<std::string, ConfigurationTree>>>
2339  std::map<std::string /*relative-path*/, std::string /*value*/> filterMap,
2340  bool onlyStatusTrue) const
2341 {
2342  std::vector<std::vector<std::pair<std::string, ConfigurationTree>>> retVector;
2343 
2344  //__COUT__ << "Children of node: " << getValueAsString() << __E__;
2345 
2346  bool filtering = filterMap.size();
2347  std::string fieldValue;
2348 
2349  bool createContainer;
2350 
2351  std::vector<std::vector<std::string>> childrenNamesByPriority =
2352  getChildrenNamesByPriority(onlyStatusTrue);
2353 
2354  for(auto& childNamesAtPriority : childrenNamesByPriority)
2355  {
2356  createContainer = true;
2357 
2358  for(auto& childName : childNamesAtPriority)
2359  {
2360  //__COUT__ << "\tChild: " << childName << __E__;
2361 
2362  if(filtering) // if all criteria are not met, then skip
2363  if(!passFilterMap(childName, filterMap))
2364  continue;
2365 
2366  if(createContainer)
2367  {
2368  retVector.push_back(
2369  std::vector<std::pair<std::string, ConfigurationTree>>());
2370  createContainer = false;
2371  }
2372 
2373  retVector[retVector.size() - 1].push_back(
2374  std::pair<std::string, ConfigurationTree>(
2375  childName, this->getNode(childName, true)));
2376  } // end children within priority loop
2377  } // end children by priority loop
2378 
2379  //__COUT__ << "Done w/Children of node: " << getValueAsString() << __E__;
2380  return retVector;
2381 } // end getChildrenByPriority()
2382 
2383 //==============================================================================
2387  const std::string& childName,
2388  std::map<std::string /*relative-path*/, std::string /*value*/> filterMap) const
2389 {
2390  // if all criteria are not met, then skip
2391  bool skip = false;
2392 
2393  // for each filter, check value
2394  for(const auto& filterPair : filterMap)
2395  {
2396  std::string filterPath = childName + "/" + filterPair.first;
2397  __COUTV__(filterPath);
2398 
2399  ConfigurationTree childNode = this->getNode(filterPath);
2400  try
2401  {
2402  // extract field value list
2403  std::vector<std::string> fieldValues;
2405  filterPair.second, fieldValues, std::set<char>({','}) /*delimiters*/);
2406 
2407  __COUTV__(fieldValues.size());
2408 
2409  skip = true;
2410  // for each field check if any match
2411  for(const auto& fieldValue : fieldValues)
2412  {
2413  // Note: that ConfigurationTree maps both fields associated with a
2414  // link to the same node instance. The behavior is likely not
2415  // expected as response for this function.. so for links
2416  // return
2417  // actual value for field name specified i.e. if Table of link
2418  // is requested give that; if linkID is requested give that. use
2419  // TRUE in getValueAsString for proper behavior
2420 
2421  if(childNode.isGroupIDNode())
2422  {
2423  // handle groupID node special, check against set of groupIDs
2424 
2425  bool groupIdFound = false;
2426  std::set<std::string> setOfGroupIDs = childNode.getSetOfGroupIDs();
2427 
2428  for(auto& groupID : setOfGroupIDs)
2429  {
2430  __COUT__ << "\t\tGroupID Check: " << filterPair.first
2431  << " == " << fieldValue << " => "
2432  << StringMacros::decodeURIComponent(fieldValue)
2433  << " ??? " << groupID << __E__;
2434 
2436  StringMacros::decodeURIComponent(fieldValue), groupID))
2437  {
2438  // found a match for the field/groupId pair
2439  __COUT__ << "Found match" << __E__;
2440  groupIdFound = true;
2441  break;
2442  }
2443  } // end groupID search
2444 
2445  if(groupIdFound)
2446  {
2447  // found a match for the field/groupId-set pair
2448  __COUT__ << "Found break match" << __E__;
2449  skip = false;
2450  break;
2451  }
2452  }
2453  else // normal child node, check against value
2454  {
2455  __COUT__ << "\t\tCheck: " << filterPair.first << " == " << fieldValue
2456  << " => " << StringMacros::decodeURIComponent(fieldValue)
2457  << " ??? " << childNode.getValueAsString(true) << __E__;
2458 
2461  childNode.getValueAsString(true)))
2462  {
2463  // found a match for the field/value pair
2464  skip = false;
2465  break;
2466  }
2467  }
2468  }
2469  }
2470  catch(...)
2471  {
2472  __SS__ << "Failed to access filter path '" << filterPath << "' - aborting."
2473  << __E__;
2474 
2475  ss << nodeDump() << __E__;
2476  __SS_THROW__;
2477  }
2478 
2479  if(skip)
2480  break; // no match for this field, so stop checking and skip this
2481  // record
2482  }
2483  return !skip;
2484 } //end passFilterMap()
2485 
2486 //==============================================================================
2495 std::vector<std::pair<std::string, ConfigurationTree>> ConfigurationTree::getChildren(
2496  std::map<std::string /*relative-path*/, std::string /*value*/> filterMap,
2497  bool byPriority,
2498  bool onlyStatusTrue) const
2499 {
2500  std::vector<std::pair<std::string, ConfigurationTree>> retVector;
2501 
2502  //__COUT__ << "Children of node: " << getValueAsString() << __E__;
2503 
2504  bool filtering = filterMap.size();
2505  // bool skip;
2506  std::string fieldValue;
2507 
2508  std::vector<std::string> childrenNames = getChildrenNames(byPriority, onlyStatusTrue);
2509  for(auto& childName : childrenNames)
2510  {
2511  if(filtering && // if all criteria are not met, then skip
2512  !passFilterMap(childName, filterMap))
2513  continue;
2514 
2515  retVector.push_back(std::pair<std::string, ConfigurationTree>(
2516  childName, this->getNode(childName, true)));
2517  }
2518 
2519  //__COUT__ << "Done w/Children of node: " << getValueAsString() << __E__;
2520  return retVector;
2521 } // end getChildren()
2522 
2523 //==============================================================================
2527 std::map<std::string, ConfigurationTree> ConfigurationTree::getChildrenMap(
2528  std::map<std::string /*relative-path*/, std::string /*value*/> filterMap,
2529  bool onlyStatusTrue) const
2530 {
2531  std::map<std::string, ConfigurationTree> retMap;
2532 
2533  bool filtering = filterMap.size();
2534 
2535  //__COUT__ << "Children of node: " << getValueAsString() << __E__;
2536  std::vector<std::string> childrenNames =
2537  getChildrenNames(false /* byPriority */, onlyStatusTrue);
2538  for(auto& childName : childrenNames)
2539  {
2540  //__COUT__ << "\tChild: " << childName << __E__;
2541 
2542  // if all criteria are not met, then skip
2543  if(filtering && !passFilterMap(childName, filterMap))
2544  continue;
2545 
2546  retMap.insert(std::pair<std::string, ConfigurationTree>(
2547  childName, this->getNode(childName)));
2548  }
2549 
2550  //__COUT__ << "Done w/Children of node: " << getValueAsString() << __E__;
2551  return retMap;
2552 } // end getChildrenMap()
2553 
2554 //==============================================================================
2557 {
2558  if(!isUIDNode())
2559  {
2560  __SS__ << "Can not get status of '" << getValueAsString()
2561  << ".' Can only check the status of a UID/Record node!" << __E__;
2562  ss << nodeDump() << __E__;
2563  __SS_THROW__;
2564  }
2565 
2566  bool tmpStatus = true;
2567  try
2568  {
2569  tableView_->getValue(tmpStatus, row_, tableView_->getColStatus());
2570  }
2571  catch(const std::runtime_error& e)
2572  {
2573  //ignore error, assuming does not have a status column
2574  //default to enabled if no status
2575  }
2576  return tmpStatus;
2577 } // end isEnabled()
2578 
2579 //==============================================================================
2580 bool ConfigurationTree::isStatusNode(void) const
2581 {
2582  if(!isValueNode())
2583  return false;
2584 
2585  return col_ == tableView_->getColStatus();
2586 } // end isStatusNode()
2587 
2588 //==============================================================================
2591 std::vector<std::vector<std::string>> ConfigurationTree::getChildrenNamesByPriority(
2592  bool onlyStatusTrue) const
2593 {
2594  std::vector<std::vector<std::string /*child name*/>> retVector;
2595 
2596  if(!tableView_)
2597  {
2598  __SS__ << "Can not get children names of '" << getValueAsString()
2599  << "' with null configuration view pointer!" << __E__;
2600  if(isLinkNode() && isDisconnected())
2601  ss << " This node is a disconnected link to " << getDisconnectedTableName()
2602  << __E__;
2603 
2604  ss << nodeDump() << __E__;
2605  __SS_ONLY_THROW__;
2606  }
2607 
2608  if(row_ == TableView::INVALID && col_ == TableView::INVALID)
2609  {
2610  // this node is table node
2611  // so return all uid node strings that match groupId
2612 
2613  // bool tmpStatus;
2614 
2615  std::vector<std::vector<unsigned int /*group row*/>> groupRowsByPriority =
2616  tableView_->getGroupRowsByPriority(
2617  groupId_ == ""
2618  ? TableView::INVALID
2619  : // if no group ID, take all rows and ignore column, do not attempt link lookup
2620  tableView_->getLinkGroupIDColumn(childLinkIndex_),
2621  groupId_,
2622  onlyStatusTrue);
2623 
2624  // now build vector of vector names by priority
2625  for(const auto& priorityChildRowVector : groupRowsByPriority)
2626  {
2627  retVector.push_back(std::vector<std::string /*child name*/>());
2628  for(const auto& priorityChildRow : priorityChildRowVector)
2629  retVector[retVector.size() - 1].push_back(
2630  tableView_->getDataView()[priorityChildRow][tableView_->getColUID()]);
2631  }
2632  }
2633  else if(row_ == TableView::INVALID)
2634  {
2635  __SS__ << "Malformed ConfigurationTree" << __E__;
2636 
2637  ss << nodeDump() << __E__;
2638  __SS_THROW__;
2639  }
2640  else if(col_ == TableView::INVALID)
2641  {
2642  // this node is uid node
2643  // so return all link and value nodes
2644 
2645  for(unsigned int c = 0; c < tableView_->getNumberOfColumns(); ++c)
2646  if(c == tableView_->getColUID() || // skip UID and linkID columns (only show
2647  // link column, to avoid duplicates)
2648  tableView_->getColumnInfo(c).isChildLinkGroupID() ||
2649  tableView_->getColumnInfo(c).isChildLinkUID())
2650  continue;
2651  else
2652  {
2653  retVector.push_back(std::vector<std::string /*child name*/>());
2654  retVector[retVector.size() - 1].push_back(
2655  tableView_->getColumnInfo(c).getName());
2656  }
2657  }
2658  else // this node is value node, so has no node to choose from
2659  {
2660  // this node is value node, cant go any deeper!
2661  __SS__ << "\n\nError occurred looking for children of nodeName=" << getValueName()
2662  << "\n\n"
2663  << "Invalid depth! getChildrenValues() called from a value point in the "
2664  "Configuration Tree."
2665  << __E__;
2666 
2667  ss << nodeDump() << __E__;
2668  __SS_THROW__;
2669  }
2670 
2671  return retVector;
2672 } // end getChildrenNamesByPriority()
2673 
2674 //==============================================================================
2677 std::vector<std::string> ConfigurationTree::getChildrenNames(bool byPriority,
2678  bool onlyStatusTrue) const
2679 {
2680  std::vector<std::string /*child name*/> retVector;
2681 
2682  if(isRootNode())
2683  {
2684  for(auto& configPair : configMgr_->getActiveVersions())
2685  {
2686  //__GEN_COUT__ << configPair.first << " " << (int)(configPair.second?1:0) <<
2687  // __E__;
2688  retVector.push_back(configPair.first);
2689  }
2690  return retVector;
2691  }
2692 
2693  if(!tableView_)
2694  {
2695  __SS__ << "Can not get children names of '" << getFieldName() << ":"
2696  << getValueAsString() << "' with null configuration view pointer!"
2697  << __E__;
2698  if(isLinkNode() && isDisconnected())
2699  ss << " This node is a disconnected link to " << getDisconnectedTableName()
2700  << "(" << getDisconnectedLinkID() << ")" << __E__;
2701  __SS_ONLY_THROW__;
2702  }
2703 
2704  if(row_ == TableView::INVALID && col_ == TableView::INVALID)
2705  {
2706  // this node is table node
2707  // so return all uid node strings that match groupId
2708  std::vector<unsigned int /*group row*/> groupRows = tableView_->getGroupRows(
2709  (groupId_ == ""
2710  ? TableView::INVALID
2711  : // if no group ID, take all rows, do not attempt link lookup
2712  tableView_->getLinkGroupIDColumn(childLinkIndex_)),
2713  groupId_,
2714  onlyStatusTrue,
2715  byPriority);
2716 
2717  // now build vector of vector names by priority
2718  for(const auto& groupRow : groupRows)
2719  retVector.push_back(
2720  tableView_->getDataView()[groupRow][tableView_->getColUID()]);
2721 
2722  // bool tmpStatus;
2723  //
2724  // if(byPriority) // reshuffle by priority
2725  // {
2726  // try
2727  // {
2728  // std::map<uint64_t /*priority*/, std::vector<unsigned int /*child row*/>> orderedByPriority;
2729  // std::vector<std::string /*child name*/> retPrioritySet;
2730  //
2731  // unsigned int col = tableView_->getColPriority();
2732  //
2733  // uint64_t tmpPriority;
2734  //
2735  // for(unsigned int r = 0; r < tableView_->getNumberOfRows(); ++r)
2736  // if(groupId_ == "" || tableView_->isEntryInGroup(r, childLinkIndex_, groupId_))
2737  // {
2738  // // check status if needed
2739  // if(onlyStatusTrue)
2740  // {
2741  // tableView_->getValue(tmpStatus, r, tableView_->getColStatus());
2742  //
2743  // if(!tmpStatus)
2744  // continue; // skip those with status false
2745  // }
2746  //
2747  // tableView_->getValue(tmpPriority, r, col);
2748  // // do not accept DEFAULT value of 0.. convert to 100
2749  // orderedByPriority[tmpPriority ? tmpPriority : 100].push_back(r);
2750  // }
2751  //
2752  // // at this point have priority map
2753  // // now build return vector
2754  //
2755  // for(const auto& priorityChildRowVector : orderedByPriority)
2756  // for(const auto& priorityChildRow : priorityChildRowVector.second)
2757  // retVector.push_back(tableView_->getDataView()[priorityChildRow][tableView_->getColUID()]);
2758  //
2759  // __COUT__ << "Returning priority children list." << __E__;
2760  // return retVector;
2761  // }
2762  // catch(std::runtime_error& e)
2763  // {
2764  // __COUT_WARN__ << "Priority configuration not found. Assuming all "
2765  // "children have equal priority. "
2766  // << __E__;
2767  // retVector.clear();
2768  // }
2769  // }
2770  // // else not by priority
2771  //
2772  // for(unsigned int r = 0; r < tableView_->getNumberOfRows(); ++r)
2773  // if(groupId_ == "" || tableView_->isEntryInGroup(r, childLinkIndex_, groupId_))
2774  // {
2775  // // check status if needed
2776  // if(onlyStatusTrue)
2777  // {
2778  // tableView_->getValue(tmpStatus, r, tableView_->getColStatus());
2779  //
2780  // if(!tmpStatus)
2781  // continue; // skip those with status false
2782  // }
2783  //
2784  // retVector.push_back(tableView_->getDataView()[r][tableView_->getColUID()]);
2785  // }
2786  }
2787  else if(row_ == TableView::INVALID)
2788  {
2789  __SS__ << "Malformed ConfigurationTree" << __E__;
2790 
2791  ss << nodeDump() << __E__;
2792  __SS_THROW__;
2793  }
2794  else if(col_ == TableView::INVALID)
2795  {
2796  // this node is uid node
2797  // so return all link and value nodes
2798 
2799  for(unsigned int c = 0; c < tableView_->getNumberOfColumns(); ++c)
2800  if(c == tableView_->getColUID() || // skip UID and linkID columns (only show
2801  // link column, to avoid duplicates)
2802  tableView_->getColumnInfo(c).isChildLinkGroupID() ||
2803  tableView_->getColumnInfo(c).isChildLinkUID())
2804  continue;
2805  else
2806  retVector.push_back(tableView_->getColumnInfo(c).getName());
2807  }
2808  else // this node is value node, so has no node to choose from
2809  {
2810  // this node is value node, cant go any deeper!
2811  __SS__ << "\n\nError occurred looking for children of nodeName=" << getValueName()
2812  << "\n\n"
2813  << "Invalid depth! getChildrenValues() called from a value point in the "
2814  "Configuration Tree."
2815  << __E__;
2816 
2817  ss << nodeDump() << __E__;
2818  __SS_THROW__;
2819  }
2820 
2821  return retVector;
2822 } // end getChildrenNames()
2823 
2824 //==============================================================================
2828 ConfigurationTree ConfigurationTree::getValueAsTreeNode(void) const
2829 {
2830  // check if first character is a /, .. if so try to get value in tree
2831  // if exception, just take value
2832  // note: this call will throw an error, in effect, if not a "value" node
2833  if(!tableView_)
2834  {
2835  __SS__ << "Invalid node for get value." << __E__;
2836  __SS_THROW__;
2837  }
2838 
2839  std::string valueString =
2840  tableView_->getValueAsString(row_, col_, true /* convertEnvironmentVariables */);
2841  //__COUT__ << valueString << __E__;
2842  if(valueString.size() && valueString[0] == '/')
2843  {
2844  //__COUT__ << "Starts with '/' - check if valid tree path: " << valueString <<
2845  // __E__;
2846  try
2847  {
2848  ConfigurationTree retNode = configMgr_->getNode(valueString);
2849  __COUT__ << "Found a valid tree path in value!" << __E__;
2850  return retNode;
2851  }
2852  catch(...)
2853  {
2854  __SS__ << "Invalid tree path." << __E__;
2855  __SS_ONLY_THROW__;
2856  }
2857  }
2858 
2859  {
2860  __SS__ << "Invalid value string '" << valueString
2861  << "' - must start with a '/' character." << __E__;
2862  __SS_ONLY_THROW__;
2863  }
2864 } // 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
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
const std::string & getDefaultValue(void) const
const time_t & getTableCreationTime(void) const
getTableCreationTime
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
std::string nodeDump(void) const
used for debugging (when throwing exception)
~ConfigurationTree(void)
destructor
bool isGroupLinkNode(void) const
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
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
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
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:1837
bool getChildLink(const unsigned int &col, bool &isGroup, std::pair< unsigned int, unsigned int > &linkPair) const
Definition: TableView.cc:3632
std::string getValueAsString(unsigned int row, unsigned int col, bool convertEnvironmentVariables=true) const
Definition: TableView.cc:975
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:1736
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:1952
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
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:19
static std::string decodeURIComponent(const std::string &data)
static std::string stackTrace(void)