tdaq-develop-2025-02-12
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 
19 //==============================================================================
21  : configMgr_(0)
22  , table_(0)
23  , groupId_("")
24  , linkParentTable_(0)
25  , linkColName_("")
26  , linkColValue_("")
27  , linkBackRow_(0)
28  , linkBackCol_(0)
29  , disconnectedTargetName_("")
30  , disconnectedLinkID_("")
31  , childLinkIndex_("")
32  , row_(0)
33  , col_(0)
34  , tableView_(0)
35 {
36  //__COUT__ << __E__;
37  //__COUT__ << "EMPTY CONSTRUCTOR ConfigManager: " << configMgr_ << " configuration: "
38  //<< table_ << __E__;
39 } // end empty constructor
40 
41 //==============================================================================
43  const TableBase* const& table)
44  : ConfigurationTree(configMgr,
45  table,
46  "" /*groupId_*/,
47  0 /*linkParentTable_*/,
48  "" /*linkColName_*/,
49  "" /*linkColValue_*/,
50  TableView::INVALID /*linkBackRow_*/,
51  TableView::INVALID /*linkBackCol_*/,
52  "" /*disconnectedTargetName_*/,
53  "" /*disconnectedLinkID_*/,
54  "" /*childLinkIndex_*/,
55  TableView::INVALID /*row_*/,
56  TableView::INVALID /*col_*/)
57 {
58  //__COUT__ << __E__;
59  //__COUT__ << "SHORT CONTRUCTOR ConfigManager: " << configMgr_ << " configuration: "
60  //<< table_ << __E__;
61 } // end short constructor
62 
63 //==============================================================================
65  const TableBase* const& table,
66  const std::string& groupId,
67  const TableBase* const& linkParentConfig,
68  const std::string& linkColName,
69  const std::string& linkColValue,
70  const unsigned int linkBackRow,
71  const unsigned int linkBackCol,
72  const std::string& disconnectedTargetName,
73  const std::string& disconnectedLinkID,
74  const std::string& childLinkIndex,
75  const unsigned int row,
76  const unsigned int col)
77  : configMgr_(configMgr)
78  , table_(table)
79  , groupId_(groupId)
80  , linkParentTable_(linkParentConfig)
81  , linkColName_(linkColName)
82  , linkColValue_(linkColValue)
83  , linkBackRow_(linkBackRow)
84  , linkBackCol_(linkBackCol)
85  , disconnectedTargetName_(disconnectedTargetName)
86  , disconnectedLinkID_(disconnectedLinkID)
87  , childLinkIndex_(childLinkIndex)
88  , row_(row)
89  , col_(col)
90  , tableView_(0)
91 {
92  if(!configMgr_)
93  {
94  __SS__ << "Invalid empty pointer given to tree!\n"
95  << "\n\tconfigMgr_=" << configMgr_ << "\n\tconfiguration_=" << table_
96  << "\n\tconfigView_=" << tableView_ << __E__;
97 
98  ss << nodeDump() << __E__;
99  __SS_THROW__;
100  }
101 
102  if(table_)
103  tableView_ = &(table_->getView());
104 
105  // verify UID column exists
106  if(tableView_ && tableView_->getColumnInfo(tableView_->getColUID()).getType() !=
108  {
109  __SS__ << "Missing UID column (must column of type "
111  << ") in config view : " << tableView_->getTableName() << __E__;
112 
113  ss << nodeDump() << __E__;
114  __SS_THROW__;
115  }
116 } // end full constructor
117 
118 //==============================================================================
121 {
122  //__COUT__ << __E__;
123 } // end destructor
124 
125 //==============================================================================
131 void ConfigurationTree::print(const unsigned int& depth, std::ostream& out) const
132 {
133  recursivePrint(*this, depth, out, "\t");
134 } // end print()
135 
136 //==============================================================================
137 void ConfigurationTree::recursivePrint(const ConfigurationTree& t,
138  unsigned int depth,
139  std::ostream& out,
140  std::string space)
141 {
142  if(t.isValueNode())
143  out << space << t.getValueName() << " :\t" << t.getValueAsString() << __E__;
144  else
145  {
146  if(t.isLinkNode())
147  {
148  out << space << t.getValueName();
149  if(t.isDisconnected())
150  {
151  out << " :\t" << t.getValueAsString() << __E__;
152  return;
153  }
154  out << " (" << (t.isGroupLinkNode() ? "Group" : "U")
155  << "ID=" << t.getValueAsString() << ") : " << __E__;
156  }
157  else
158  out << space << t.getValueAsString() << " : " << __E__;
159 
160  // if depth>=1 print all children
161  // child.print(depth-1)
162  if(depth >= 1)
163  {
164  auto C = t.getChildren();
165  if(!C.empty())
166  out << space << "{" << __E__;
167  for(auto& c : C)
168  recursivePrint(c.second, depth - 1, out, space + " ");
169  if(!C.empty())
170  out << space << "}" << __E__;
171  }
172  }
173 } // end recursivePrint()
174 
175 //==============================================================================
176 std::string ConfigurationTree::handleValidateValueForColumn(
177  const TableView* configView,
178  std::string value,
179  unsigned int col,
181 {
182  if(!configView)
183  {
184  __SS__ << "Null configView" << __E__;
185 
186  ss << nodeDump() << __E__;
187  __SS_THROW__;
188  }
189  __COUT__ << "handleValidateValueForColumn<string>" << __E__;
190  return configView->validateValueForColumn(value, col);
191 } // end std::string handleValidateValueForColumn()
192 
193 //==============================================================================
198 void ConfigurationTree::getValue(std::string& value) const
199 {
200  //__COUT__ << row_ << " " << col_ << " p: " << tableView_<< __E__;
201 
202  if(row_ != TableView::INVALID &&
203  col_ != TableView::INVALID) // this node is a value node
204  {
205  // attempt to interpret the value as a tree node path itself
206  try
207  {
208  ConfigurationTree valueAsTreeNode = getValueAsTreeNode();
209  // valueAsTreeNode.getValue<T>(value);
210  __COUT__ << "Success following path to tree node!" << __E__;
211  // value has been interpreted as a tree node value
212  // now verify result under the rules of this column
213  // if(typeid(std::string) == typeid(value))
214 
215  // Note: want to interpret table value as though it is in column of different
216  // table this allows a number to be read as a string, for example, without
217  // exceptions
218  value = tableView_->validateValueForColumn(valueAsTreeNode.getValueAsString(),
219  col_);
220 
221  __COUT__ << "Successful value!" << __E__;
222 
223  // else
224  // value = tableView_->validateValueForColumn<T>(
225  // valueAsTreeNode.getValueAsString(),col_);
226 
227  return;
228  }
229  catch(...) // tree node path interpretation failed
230  {
231  //__COUT__ << "Invalid path, just returning normal value." << __E__;
232  }
233 
234  // else normal return
235  tableView_->getValue(value, row_, col_);
236  }
237  else if(row_ == TableView::INVALID &&
238  col_ == TableView::INVALID) // this node is table node maybe with groupId
239  {
240  if(isLinkNode() && isDisconnected())
241  value = (groupId_ == "") ? getValueName() : groupId_; // a disconnected link
242  // still knows its table
243  // name or groupId
244  else
245  value = (groupId_ == "") ? table_->getTableName() : groupId_;
246  }
247  else if(row_ == TableView::INVALID)
248  {
249  __SS__ << "Malformed ConfigurationTree" << __E__;
250  __SS_THROW__;
251  }
252  else if(col_ == TableView::INVALID) // this node is uid node
253  tableView_->getValue(value, row_, tableView_->getColUID());
254  else
255  {
256  __SS__ << "Impossible." << __E__;
257  __SS_THROW__;
258  }
259 } // end getValue()
260 
261 //==============================================================================
274 std::string ConfigurationTree::getValue() const
275 {
276  std::string value;
278  return value;
279 } // end getValue()
280 //==============================================================================
293 std::string ConfigurationTree::getValueWithDefault(const std::string& defaultValue) const
294 {
295  if(isDefaultValue())
296  return defaultValue;
297  else
299 } // end getValueWithDefault()
300 
301 //==============================================================================
307 {
308  //__COUT__ << row_ << " " << col_ << " p: " << tableView_<< __E__;
309 
310  if(row_ != TableView::INVALID &&
311  col_ != TableView::INVALID) // this node is a value node
312  {
313  std::string bitmapString;
314  tableView_->getValue(bitmapString, row_, col_);
315 
316  __COUTV__(bitmapString);
317  if(bitmapString == TableViewColumnInfo::DATATYPE_STRING_DEFAULT)
318  {
319  bitmap.isDefault_ = true;
320  return;
321  }
322  else
323  bitmap.isDefault_ = false;
324 
325  // extract bit map
326  {
327  bitmap.bitmap_.clear();
328  int row = -1;
329  bool openRow = false;
330  unsigned int startInt = -1;
331  for(unsigned int i = 0; i < bitmapString.length(); i++)
332  {
333  __COUTV__(bitmapString[i]);
334  __COUTV__(row);
335  __COUTV__(openRow);
336  __COUTV__(startInt);
337  __COUTV__(i);
338 
339  if(!openRow) // need start of row
340  {
341  if(bitmapString[i] == '[')
342  { // open a new row
343  openRow = true;
344  ++row;
345  bitmap.bitmap_.push_back(std::vector<uint64_t>());
346  }
347  else if(bitmapString[i] == ']')
348  {
349  break; // ending bracket, done with string
350  }
351  else if(bitmapString[i] == ',') // end characters found not within
352  // row
353  {
354  __SS__
355  << "Too many ']' or ',' characters in bit map configuration"
356  << __E__;
357 
358  ss << nodeDump() << __E__;
359  __SS_ONLY_THROW__;
360  }
361  }
362  else if(startInt == (unsigned int)-1) // need to find start of number
363  {
364  if(bitmapString[i] == ']') // found end of row, instead of start of
365  // number, assume row ended
366  {
367  openRow = false;
368  }
369  else if(bitmapString[i] >= '0' &&
370  bitmapString[i] <= '9') // found start of number
371  {
372  startInt = i;
373  }
374  else if(bitmapString[i] == ',') // comma found without number
375  {
376  __SS__ << "Too many ',' characters in bit map configuration"
377  << __E__;
378 
379  ss << nodeDump() << __E__;
380  __SS_ONLY_THROW__;
381  }
382  }
383  else
384  {
385  // looking for end of number
386 
387  if(bitmapString[i] ==
388  ']') // found end of row, assume row and number ended
389  {
390  openRow = false;
391  bitmap.bitmap_[row].push_back(strtoul(
392  bitmapString.substr(startInt, i - startInt).c_str(), 0, 0));
393  startInt = -1;
394  }
395  else if(bitmapString[i] == ',') // comma found, assume end of number
396  {
397  bitmap.bitmap_[row].push_back(strtoul(
398  bitmapString.substr(startInt, i - startInt).c_str(), 0, 0));
399  startInt = -1;
400  }
401  }
402  }
403 
404  for(unsigned int r = 0; r < bitmap.bitmap_.size(); ++r)
405  {
406  for(unsigned int c = 0; c < bitmap.bitmap_[r].size(); ++c)
407  {
408  __COUT__ << r << "," << c << " = " << bitmap.bitmap_[r][c] << __E__;
409  }
410  __COUT__ << "================" << __E__;
411  }
412  }
413  }
414  else
415  {
416  __SS__ << "Requesting getValue must be on a value node." << __E__;
417 
418  ss << nodeDump() << __E__;
419  __SS_THROW__;
420  }
421 } // end getValueAsBitMap()
422 
423 //==============================================================================
428 {
431  return value;
432 } // end getValueAsBitMap()
433 
434 //==============================================================================
438 {
439  if(row_ != TableView::INVALID &&
440  col_ != TableView::INVALID) // this node is a value node
441  return tableView_->getEscapedValueAsString(row_, col_);
442 
443  __SS__ << "Can not get escaped value except from a value node!"
444  << " This node is type '" << getNodeType() << "." << __E__;
445 
446  ss << nodeDump() << __E__;
447  __SS_THROW__;
448 } // end getEscapedValue()
449 
450 //==============================================================================
452 const std::string& ConfigurationTree::getTableName(void) const
453 {
454  if(!table_)
455  {
456  __SS__ << "Can not get configuration name of node with no configuration pointer! "
457  << "Is there a broken link? " << __E__;
458  if(linkParentTable_)
459  {
460  ss << "Error occurred traversing from " << linkParentTable_->getTableName()
461  << " UID '"
462  << linkParentTable_->getView().getValueAsString(
463  linkBackRow_, linkParentTable_->getView().getColUID())
464  << "' at row " << linkBackRow_ << " col '"
465  << linkParentTable_->getView().getColumnInfo(linkBackCol_).getName()
466  << ".'" << __E__;
467 
468  ss << StringMacros::stackTrace() << __E__;
469  }
470 
471  __SS_ONLY_THROW__;
472  }
473  return table_->getTableName();
474 } // end getTableName()
475 
476 //==============================================================================
478 const std::string& ConfigurationTree::getParentTableName(void) const
479 {
480  if(linkParentTable_)
481  return linkParentTable_->getTableName();
482 
483  __SS__ << "Can not get parent table name of node with no parent table pointer! "
484  << "Was this node initialized correctly? " << __E__;
485  __SS_ONLY_THROW__;
486 } // end getParentTableName()
487 
488 //==============================================================================
490 const unsigned int& ConfigurationTree::getNodeRow(void) const
491 {
492  if(isUIDNode() || isValueNode())
493  return row_;
494 
495  __SS__ << "Can only get row from a UID or value node!" << __E__;
496  if(linkParentTable_)
497  {
498  ss << "Error occurred traversing from " << linkParentTable_->getTableName()
499  << " UID '"
500  << linkParentTable_->getView().getValueAsString(
501  linkBackRow_, linkParentTable_->getView().getColUID())
502  << "' at row " << linkBackRow_ << " col '"
503  << linkParentTable_->getView().getColumnInfo(linkBackCol_).getName() << ".'"
504  << __E__;
505 
506  ss << StringMacros::stackTrace() << __E__;
507  }
508 
509  __SS_ONLY_THROW__;
510 
511 } // end getNodeRow()
512 
513 //==============================================================================
518 const std::string& ConfigurationTree::getFieldTableName(void) const
519 {
520  // if link node, need config name from parent
521  if(isLinkNode())
522  {
523  if(!linkParentTable_)
524  {
525  __SS__ << "Can not get configuration name of link node field with no parent "
526  "configuration pointer!"
527  << __E__;
528  ss << nodeDump() << __E__;
529  __SS_ONLY_THROW__;
530  }
531  return linkParentTable_->getTableName();
532  }
533  else
534  return getTableName();
535 } // end getFieldTableName()
536 
537 //==============================================================================
539 const std::string& ConfigurationTree::getDisconnectedTableName(void) const
540 {
541  if(isLinkNode() && isDisconnected())
542  return disconnectedTargetName_;
543 
544  __SS__ << "Can not get disconnected target name of node unless it is a disconnected "
545  "link node!"
546  << __E__;
547 
548  ss << nodeDump() << __E__;
549  __SS_ONLY_THROW__;
550 } // end getDisconnectedTableName()
551 
552 //==============================================================================
554 const std::string& ConfigurationTree::getDisconnectedLinkID(void) const
555 {
556  if(isLinkNode() && isDisconnected())
557  return disconnectedLinkID_;
558 
559  __SS__ << "Can not get disconnected target name of node unless it is a disconnected "
560  "link node!"
561  << __E__;
562 
563  ss << nodeDump() << __E__;
564  __SS_ONLY_THROW__;
565 } // end getDisconnectedLinkID()
566 
567 //==============================================================================
570 {
571  if(!tableView_)
572  {
573  __SS__ << "Can not get configuration version of node with no config view pointer!"
574  << __E__;
575 
576  ss << nodeDump() << __E__;
577  __SS_ONLY_THROW__;
578  }
579  return tableView_->getVersion();
580 } // end getTableVersion()
581 
582 //==============================================================================
585 {
586  if(!tableView_)
587  {
588  __SS__ << "Can not get configuration creation time of node with no config view "
589  "pointer!"
590  << __E__;
591 
592  ss << nodeDump() << __E__;
593  __SS_ONLY_THROW__;
594  }
595  return tableView_->getCreationTime();
596 } // end getTableCreationTime()
597 
598 //==============================================================================
601 std::set<std::string> ConfigurationTree::getSetOfGroupIDs(void) const
602 {
603  if(!isGroupIDNode())
604  {
605  __SS__ << "Can not get set of group IDs of node with value type of '"
606  << getNodeType() << ".' Node must be a GroupID node." << __E__;
607 
608  ss << nodeDump() << __E__;
609  __SS_ONLY_THROW__;
610  }
611 
612  return tableView_->getSetOfGroupIDs(col_, row_);
613 
614 } // end getSetOfGroupIDs()
615 
616 //==============================================================================
620 std::vector<std::string> ConfigurationTree::getFixedChoices(void) const
621 {
622  if(getValueType() != TableViewColumnInfo::TYPE_FIXED_CHOICE_DATA &&
623  getValueType() != TableViewColumnInfo::TYPE_BITMAP_DATA && !isLinkNode())
624  {
625  __SS__ << "Can not get fixed choices of node with value type of '"
626  << getValueType() << ".' Node must be a link or a value node with type '"
627  << TableViewColumnInfo::TYPE_BITMAP_DATA << "' or '"
628  << TableViewColumnInfo::TYPE_FIXED_CHOICE_DATA << ".'" << __E__;
629 
630  ss << nodeDump() << __E__;
631  __SS_ONLY_THROW__;
632  }
633 
634  std::vector<std::string> retVec;
635 
636  if(isLinkNode())
637  {
638  if(!linkParentTable_)
639  {
640  __SS__
641  << "Can not get fixed choices of node with no parent config view pointer!"
642  << __E__;
643 
644  ss << nodeDump() << __E__;
645  __SS_ONLY_THROW__;
646  }
647 
648  //__COUT__ << getChildLinkIndex() << __E__;
649  //__COUT__ << linkColName_ << __E__;
650 
651  // for links, col_ = -1, column c needs to change (to ChildLink column of pair)
652  // get column from parent config pointer
653 
654  const TableView* parentView = &(linkParentTable_->getView());
655  int c = parentView->findCol(linkColName_);
656 
657  std::pair<unsigned int /*link col*/, unsigned int /*link id col*/> linkPair;
658  bool isGroupLink;
659  parentView->getChildLink(c, isGroupLink, linkPair);
660  c = linkPair.first;
661 
662  std::vector<std::string> choices = parentView->getColumnInfo(c).getDataChoices();
663  for(const auto& choice : choices)
664  retVec.push_back(choice);
665 
666  return retVec;
667  }
668 
669  if(!tableView_)
670  {
671  __SS__ << "Can not get fixed choices of node with no config view pointer!"
672  << __E__;
673 
674  ss << nodeDump() << __E__;
675  __SS_ONLY_THROW__;
676  }
677 
678  // return vector of default + data choices
679  retVec.push_back(tableView_->getColumnInfo(col_).getDefaultValue());
680  std::vector<std::string> choices = tableView_->getColumnInfo(col_).getDataChoices();
681  for(const auto& choice : choices)
682  retVec.push_back(choice);
683 
684  return retVec;
685 } // end getFixedChoices()
686 
687 //==============================================================================
689 const std::string& ConfigurationTree::getComment(void) const
690 {
691  return getNode(TableViewColumnInfo::COL_NAME_COMMENT).getValueAsString() == ""
692  ? TableViewColumnInfo::DATATYPE_COMMENT_DEFAULT
693  : getNode(TableViewColumnInfo::COL_NAME_COMMENT).getValueAsString();
694 } // end getComment()
695 
696 //==============================================================================
698 const std::string& ConfigurationTree::getAuthor(void) const
699 {
700  return getNode(TableViewColumnInfo::COL_NAME_AUTHOR).getValueAsString();
701 } // end getAuthor()
702 
703 //==============================================================================
711 const std::string& ConfigurationTree::getValueAsString(bool returnLinkTableValue) const
712 {
713  //__COUTV__(col_);__COUTV__(row_);__COUTV__(table_);__COUTV__(tableView_);
714 
715  if(isLinkNode())
716  {
717  if(returnLinkTableValue)
718  return linkColValue_;
719  else if(isDisconnected())
720  return ConfigurationTree::DISCONNECTED_VALUE;
721  else if(row_ == TableView::INVALID &&
722  col_ == TableView::INVALID) // this link is groupId node
723  return (groupId_ == "") ? table_->getTableName() : groupId_;
724  else if(col_ == TableView::INVALID) // this link is uid node
725  return tableView_->getDataView()[row_][tableView_->getColUID()];
726  else
727  {
728  __SS__ << "Impossible Link." << __E__;
729 
730  ss << nodeDump() << __E__;
731  __SS_THROW__;
732  }
733  }
734  else if(row_ != TableView::INVALID &&
735  col_ != TableView::INVALID) // this node is a value node
736  return tableView_->getDataView()[row_][col_];
737  else if(row_ == TableView::INVALID &&
738  col_ == TableView::INVALID) // this node is table node maybe with groupId
739  {
740  // if root node, then no table defined
741  if(isRootNode())
742  return ConfigurationTree::ROOT_NAME;
743 
744  return (groupId_ == "") ? table_->getTableName() : groupId_;
745  }
746  else if(row_ == TableView::INVALID)
747  {
748  __SS__ << "Malformed ConfigurationTree" << __E__;
749 
750  ss << nodeDump() << __E__;
751  __SS_THROW__;
752  }
753  else if(col_ == TableView::INVALID) // this node is uid node
754  return tableView_->getDataView()[row_][tableView_->getColUID()];
755  else
756  {
757  __SS__ << "Impossible." << __E__;
758 
759  ss << nodeDump() << __E__;
760  __SS_THROW__;
761  }
762 } // end getValueAsString()
763 
764 //==============================================================================
768 const std::string& ConfigurationTree::getUIDAsString(void) const
769 {
770  if(isValueNode() || isUIDLinkNode() || isUIDNode())
771  return tableView_->getDataView()[row_][tableView_->getColUID()];
772 
773  {
774  __SS__ << "Can not get UID of node with type '" << getNodeType()
775  << ".' Node type must be '" << ConfigurationTree::NODE_TYPE_VALUE
776  << "' or '" << ConfigurationTree::NODE_TYPE_UID_LINK << ".'" << __E__;
777 
778  ss << nodeDump() << __E__;
779  __SS_ONLY_THROW__;
780  }
781 } // end getUIDAsString()
782 
783 //==============================================================================
786 const std::string& ConfigurationTree::getValueDataType(void) const
787 {
788  if(isValueNode())
789  return tableView_->getColumnInfo(col_).getDataType();
790  else // must be std::string
791  return TableViewColumnInfo::DATATYPE_STRING;
792 } // end getValueDataType()
793 
794 //==============================================================================
798 {
799  if(!isValueNode())
800  return false;
801 
802  if(getValueDataType() == TableViewColumnInfo::DATATYPE_STRING)
803  {
804  if(getValueType() == TableViewColumnInfo::TYPE_ON_OFF ||
805  getValueType() == TableViewColumnInfo::TYPE_TRUE_FALSE ||
806  getValueType() == TableViewColumnInfo::TYPE_YES_NO)
807  return getValueAsString() ==
808  TableViewColumnInfo::DATATYPE_BOOL_DEFAULT; // default to OFF, NO,
809  // FALSE
810  else if(getValueType() == TableViewColumnInfo::TYPE_COMMENT)
811  return getValueAsString() == TableViewColumnInfo::DATATYPE_COMMENT_DEFAULT ||
812  getValueAsString() ==
813  ""; // in case people delete default comment, allow blank also
814  else
815  return getValueAsString() == TableViewColumnInfo::DATATYPE_STRING_DEFAULT;
816  }
818  return getValueAsString() == TableViewColumnInfo::DATATYPE_NUMBER_DEFAULT;
819  else if(getValueDataType() == TableViewColumnInfo::DATATYPE_TIME)
820  return getValueAsString() == TableViewColumnInfo::DATATYPE_TIME_DEFAULT;
821  else
822  return false;
823 } // end isDefaultValue()
824 
825 //==============================================================================
829 const std::string& ConfigurationTree::getDefaultValue(void) const
830 {
831  if(!isValueNode())
832  {
833  __SS__ << "Can only get default value from a value node! "
834  << "The node type is " << getNodeType() << __E__;
835 
836  ss << nodeDump() << __E__;
837  __SS_THROW__;
838  }
839 
840  if(getValueDataType() == TableViewColumnInfo::DATATYPE_STRING)
841  {
842  if(getValueType() == TableViewColumnInfo::TYPE_ON_OFF ||
843  getValueType() == TableViewColumnInfo::TYPE_TRUE_FALSE ||
844  getValueType() == TableViewColumnInfo::TYPE_YES_NO)
845  return TableViewColumnInfo::DATATYPE_BOOL_DEFAULT; // default to OFF, NO,
846  // FALSE
847  else if(getValueType() == TableViewColumnInfo::TYPE_COMMENT)
848  return TableViewColumnInfo::
849  DATATYPE_COMMENT_DEFAULT; // in case people delete default comment, allow blank also
850  else
851  return TableViewColumnInfo::DATATYPE_STRING_DEFAULT;
852  }
854  return TableViewColumnInfo::DATATYPE_NUMBER_DEFAULT;
855  else if(getValueDataType() == TableViewColumnInfo::DATATYPE_TIME)
856  return TableViewColumnInfo::DATATYPE_TIME_DEFAULT;
857 
858  {
859  __SS__ << "Can only get default value from a value node! "
860  << "The node type is " << getNodeType() << __E__;
861 
862  ss << nodeDump() << __E__;
863  __SS_THROW__;
864  }
865 } // end isDefaultValue()
866 
867 //==============================================================================
870 const std::string& ConfigurationTree::getValueType(void) const
871 {
872  if(isValueNode())
873  return tableView_->getColumnInfo(col_).getType();
874  else if(isLinkNode() && isDisconnected())
875  return ConfigurationTree::VALUE_TYPE_DISCONNECTED;
876  else // just call all non-value nodes data
877  return ConfigurationTree::VALUE_TYPE_NODE;
878 } // end getValueType()
879 
880 //==============================================================================
884 {
885  if(isValueNode())
886  return tableView_->getColumnInfo(col_);
887  else
888  {
889  __SS__ << "Can only get column info from a value node! "
890  << "The node type is " << getNodeType() << __E__;
891 
892  ss << nodeDump() << __E__;
893  __SS_THROW__;
894  }
895 } // end getColumnInfo()
896 
897 //==============================================================================
899 const unsigned int& ConfigurationTree::getRow(void) const { return row_; }
900 
901 //==============================================================================
903 const unsigned int& ConfigurationTree::getColumn(void) const { return col_; }
904 
905 //==============================================================================
908 const unsigned int& ConfigurationTree::getFieldRow(void) const
909 {
910  if(isLinkNode())
911  {
912  // for links, need to use parent info to determine
913  return linkBackRow_;
914  }
915  else
916  return row_;
917 } // end getFieldRow()
918 
919 //==============================================================================
922 const unsigned int& ConfigurationTree::getFieldColumn(void) const
923 {
924  if(isLinkNode())
925  {
926  // for links, need to use parent info to determine
927  return linkBackCol_;
928  }
929  else
930  return col_;
931 } // end getFieldColumn()
932 
933 //==============================================================================
935 const std::string& ConfigurationTree::getChildLinkIndex(void) const
936 {
937  if(!isLinkNode())
938  {
939  __SS__ << "Can only get link ID from a link! "
940  << "The node type is " << getNodeType() << __E__;
941 
942  ss << nodeDump() << __E__;
943  __SS_THROW__;
944  }
945  return childLinkIndex_;
946 } // end getChildLinkIndex()
947 
948 //==============================================================================
951 const std::string& ConfigurationTree::getValueName(void) const
952 {
953  if(isValueNode())
954  return tableView_->getColumnInfo(col_).getName();
955  else if(isLinkNode())
956  return linkColName_;
957  else
958  {
959  __SS__ << "Can only get value name of a value node!" << __E__;
960 
961  ss << nodeDump() << __E__;
962  __SS_THROW__;
963  }
964 } // end getValueName()
965 
966 //==============================================================================
969 ConfigurationTree ConfigurationTree::recurse(const ConfigurationTree& tree,
970  const std::string& childPath,
971  bool doNotThrowOnBrokenUIDLinks,
972  const std::string& originalNodeString)
973 {
974  __COUT_TYPE__(TLVL_DEBUG + 50)
975  << __COUT_HDR__ << tree.row_ << " " << tree.col_ << __E__;
976  __COUT_TYPE__(TLVL_DEBUG + 51) << __COUT_HDR__ << "childPath=" << childPath << " "
977  << childPath.length() << __E__;
978  if(childPath.length() <= 1) // only "/" or ""
979  return tree;
980  return tree.recursiveGetNode(
981  childPath, doNotThrowOnBrokenUIDLinks, originalNodeString);
982 } // end recurse()
983 
984 //==============================================================================
994 ConfigurationTree ConfigurationTree::getNode(const std::string& nodeString,
995  bool doNotThrowOnBrokenUIDLinks) const
996 {
997  // __COUT__ << "nodeString=" << nodeString << " len=" << nodeString.length() << __E__;
998  return recursiveGetNode(
999  nodeString, doNotThrowOnBrokenUIDLinks, "" /*originalNodeString*/);
1000 } // end getNode() connected to recursiveGetNode()
1001 ConfigurationTree ConfigurationTree::recursiveGetNode(
1002  const std::string& nodeString,
1003  bool doNotThrowOnBrokenUIDLinks,
1004  const std::string& originalNodeString) const
1005 {
1006  __COUT_TYPE__(TLVL_DEBUG + 51) << __COUT_HDR__ << "nodeString=" << nodeString
1007  << " len=" << nodeString.length() << __E__;
1008  __COUT_TYPE__(TLVL_DEBUG + 52)
1009  << __COUT_HDR__ << "doNotThrowOnBrokenUIDLinks=" << doNotThrowOnBrokenUIDLinks
1010  << __E__;
1011 
1012  // get nodeName (in case of / syntax)
1013  if(nodeString.length() < 1)
1014  {
1015  __SS__ << "Invalid empty node name! Looking for child node '" << nodeString
1016  << "' from node '" << getValue() << "'..." << __E__;
1017 
1018  ss << nodeDump() << __E__;
1019  __SS_THROW__;
1020  }
1021 
1022  // ignore multiple starting slashes
1023  size_t startingIndex = 0;
1024  while(startingIndex < nodeString.length() && nodeString[startingIndex] == '/')
1025  ++startingIndex;
1026  size_t endingIndex = nodeString.find('/', startingIndex);
1027  if(endingIndex == std::string::npos)
1028  endingIndex = nodeString.length();
1029 
1030  std::string nodeName = nodeString.substr(startingIndex, endingIndex - startingIndex);
1031  __COUT_TYPE__(TLVL_DEBUG + 51) << __COUT_HDR__ << "nodeName=" << nodeName
1032  << " len=" << nodeName.length() << __E__;
1033 
1034  ++endingIndex;
1035  std::string childPath =
1036  (endingIndex >= nodeString.length() ? "" : nodeString.substr(endingIndex));
1037  __COUT_TYPE__(TLVL_DEBUG + 51)
1038  << __COUT_HDR__ << "childPath=" << childPath << " len=" << childPath.length()
1039  << " endingIndex=" << endingIndex
1040  << " nodeString.length()=" << nodeString.length() << __E__;
1041 
1042  // if this tree is beginning at a configuration.. then go to uid, and vice versa
1043 
1044  try
1045  {
1046  __COUT_TYPE__(TLVL_DEBUG + 50) << __COUT_HDR__ << row_ << " " << col_ << " "
1047  << groupId_ << " " << tableView_ << __E__;
1048  if(isRootNode())
1049  {
1050  // root node
1051  // so return table node
1052  return recurse(configMgr_->getNode(nodeName),
1053  childPath,
1054  doNotThrowOnBrokenUIDLinks,
1055  originalNodeString);
1056  }
1057  else if(row_ == TableView::INVALID && col_ == TableView::INVALID)
1058  {
1059  // table node
1060 
1061  if(!tableView_)
1062  {
1063  __SS__ << "Missing configView pointer! Likely attempting to access a "
1064  "child node through a disconnected link node."
1065  << __E__;
1066 
1067  ss << nodeDump() << __E__;
1068  __SS_THROW__;
1069  }
1070 
1071  // this node is table node, so return uid node considering groupid
1072  return recurse(
1074  configMgr_,
1075  table_,
1076  "", // no new groupId string, not a link
1077  0 /*linkParentTable_*/,
1078  "", // link node name, not a link
1079  "", // link node value, not a link
1080  TableView::INVALID /*linkBackRow_*/,
1081  TableView::INVALID /*linkBackCol_*/,
1082  "", // ignored disconnected target name, not a link
1083  "", // ignored disconnected link id, not a link
1084  "",
1085  // if this node is group table node, consider that when getting rows
1086  (groupId_ == "")
1087  ? tableView_->findRow(tableView_->getColUID(), nodeName)
1088  : tableView_->findRowInGroup(tableView_->getColUID(),
1089  nodeName,
1090  groupId_,
1091  childLinkIndex_)),
1092  childPath,
1093  doNotThrowOnBrokenUIDLinks,
1094  originalNodeString);
1095  }
1096  else if(row_ == TableView::INVALID)
1097  {
1098  __SS__ << "Malformed ConfigurationTree" << __E__;
1099 
1100  ss << nodeDump() << __E__;
1101  __SS_THROW__;
1102  }
1103  else if(col_ == TableView::INVALID)
1104  {
1105  // this node is uid node, so return link, group link, disconnected, or value
1106  // node
1107 
1108  __COUT_TYPE__(TLVL_DEBUG + 51) << __COUT_HDR__ << "nodeName=" << nodeName
1109  << " " << nodeName.length() << __E__;
1110 
1111  // if the value is a unique link ..
1112  // return a uid node!
1113  // if the value is a group link
1114  // return a table node with group string
1115  // else.. return value node
1116 
1117  if(!tableView_)
1118  {
1119  __SS__ << "Missing configView pointer! Likely attempting to access a "
1120  "child node through a disconnected link node."
1121  << __E__;
1122 
1123  ss << nodeDump() << __E__;
1124  __SS_THROW__;
1125  }
1126 
1127  unsigned int c = tableView_->findCol(nodeName);
1128  std::pair<unsigned int /*link col*/, unsigned int /*link id col*/> linkPair;
1129  bool isGroupLink, isLink;
1130  if((isLink = tableView_->getChildLink(c, isGroupLink, linkPair)) &&
1131  !isGroupLink)
1132  {
1133  __COUT_TYPE__(TLVL_DEBUG + 50) << __COUT_HDR__ << "nodeName=" << nodeName
1134  << " " << nodeName.length() << __E__;
1135  //is a unique link, return uid node in new configuration
1136  // need new configuration pointer
1137  // and row of linkUID in new configuration
1138 
1139  const TableBase* childConfig;
1140  try
1141  {
1142  childConfig = configMgr_->getTableByName(
1143  tableView_->getDataView()[row_][linkPair.first]);
1144  childConfig->getView(); // get view as a test for an active view
1145 
1146  if(doNotThrowOnBrokenUIDLinks) // try a test of getting row
1147  {
1148  childConfig->getView().findRow(
1149  childConfig->getView().getColUID(),
1150  tableView_->getDataView()[row_][linkPair.second]);
1151  }
1152  }
1153  catch(...)
1154  {
1155  __COUT_TYPE__(TLVL_DEBUG + 50)
1156  << __COUT_HDR__ << "Found disconnected node! (" << nodeName << ":"
1157  << tableView_->getDataView()[row_][linkPair.first] << ")"
1158  << " at entry with UID "
1159  << tableView_->getDataView()[row_][tableView_->getColUID()]
1160  << __E__;
1161  //do not recurse further
1162  return ConfigurationTree(
1163  configMgr_,
1164  0,
1165  "",
1166  table_, // linkParentTable_
1167  nodeName,
1168  tableView_->getDataView()[row_][c], // this the link node field
1169  // associated value (matches
1170  // targeted column)
1171  row_ /*linkBackRow_*/,
1172  c /*linkBackCol_*/,
1173  tableView_->getDataView()[row_][linkPair.first], // give
1174  // disconnected
1175  // target name
1176  tableView_->getDataView()[row_][linkPair.second], // give
1177  // disconnected
1178  // link ID
1179  tableView_->getColumnInfo(c).getChildLinkIndex());
1180  }
1181 
1182  return recurse(
1183  ConfigurationTree( // this is a link node
1184  configMgr_,
1185  childConfig,
1186  "", // no new groupId string
1187  table_, // linkParentTable_
1188  nodeName, // this is a link node
1189  tableView_->getDataView()[row_][c], // this the link node field
1190  // associated value (matches
1191  // targeted column)
1192  row_ /*linkBackRow_*/,
1193  c /*linkBackCol_*/,
1194  "", // ignore since is connected
1195  "", // ignore since is connected
1196  tableView_->getColumnInfo(c).getChildLinkIndex(),
1197  childConfig->getView().findRow(
1198  childConfig->getView().getColUID(),
1199  tableView_->getDataView()[row_][linkPair.second])),
1200  childPath,
1201  doNotThrowOnBrokenUIDLinks,
1202  originalNodeString);
1203  }
1204  else if(isLink)
1205  {
1206  __COUT_TYPE__(TLVL_DEBUG + 50) << __COUT_HDR__ << "nodeName=" << nodeName
1207  << " " << nodeName.length() << __E__;
1208  // is a group link, return new configuration with group string
1209  // need new configuration pointer
1210  // and group string
1211 
1212  const TableBase* childConfig;
1213  try
1214  {
1215  childConfig = configMgr_->getTableByName(
1216  tableView_->getDataView()[row_][linkPair.first]);
1217  childConfig->getView(); // get view as a test for an active view
1218  }
1219  catch(...)
1220  {
1221  if(tableView_->getDataView()[row_][linkPair.first] !=
1222  TableViewColumnInfo::DATATYPE_LINK_DEFAULT)
1223  __COUT_WARN__
1224  << "Found disconnected node! Failed link target "
1225  "from nodeName="
1226  << nodeName << " to table:id="
1227  << tableView_->getDataView()[row_][linkPair.first] << ":"
1228  << tableView_->getDataView()[row_][linkPair.second] << __E__;
1229 
1230  // do not recurse further
1231  return ConfigurationTree(
1232  configMgr_,
1233  0,
1234  tableView_->getDataView()[row_][linkPair.second], // groupID
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  // target name
1248  tableView_->getColumnInfo(c).getChildLinkIndex());
1249  }
1250 
1251  return recurse(
1252  ConfigurationTree( // this is a link node
1253  configMgr_,
1254  childConfig,
1255  tableView_
1256  ->getDataView()[row_][linkPair.second], // groupId string
1257  table_, // linkParentTable_
1258  nodeName, // this is a link node
1259  tableView_->getDataView()[row_][c], // this the link node field
1260  // associated value (matches
1261  // targeted column)
1262  row_ /*linkBackRow_*/,
1263  c /*linkBackCol_*/,
1264  "", // ignore since is connected
1265  "", // ignore since is connected
1266  tableView_->getColumnInfo(c).getChildLinkIndex()),
1267  childPath,
1268  doNotThrowOnBrokenUIDLinks,
1269  originalNodeString);
1270  }
1271  else
1272  {
1273  __COUT_TYPE__(TLVL_DEBUG + 50) << __COUT_HDR__ << "nodeName=" << nodeName
1274  << " " << nodeName.length() << __E__;
1275  //return value node
1276  return ConfigurationTree(configMgr_,
1277  table_,
1278  "",
1279  0 /*linkParentTable_*/,
1280  "",
1281  "",
1282  TableView::INVALID /*linkBackRow_*/,
1283  TableView::INVALID /*linkBackCol_*/,
1284  "",
1285  "" /*disconnectedLinkID*/,
1286  "",
1287  row_,
1288  c);
1289  }
1290  }
1291  }
1292  catch(std::runtime_error& e)
1293  {
1294  __SS__ << "\n\nError occurred descending from node '" << getValue()
1295  << "' in table '" << getTableName() << "' looking for child '" << nodeName
1296  << "'\n\n"
1297  << __E__;
1298  ss << "The original node search string was '" << originalNodeString << ".'"
1299  << __E__;
1300  ss << "--- Additional error detail: \n\n" << e.what() << __E__;
1301 
1302  ss << nodeDump() << __E__;
1303  __SS_ONLY_THROW__;
1304  }
1305  catch(...)
1306  {
1307  __SS__ << "\n\nError occurred descending from node '" << getValue()
1308  << "' in table '" << getTableName() << "' looking for child '" << nodeName
1309  << "'\n\n"
1310  << __E__;
1311  ss << "The original node search string was '" << originalNodeString << ".'"
1312  << __E__;
1313  try
1314  {
1315  throw;
1316  } //one more try to printout extra info
1317  catch(const std::exception& e)
1318  {
1319  ss << "Exception message: " << e.what();
1320  }
1321  catch(...)
1322  {
1323  }
1324  ss << nodeDump() << __E__;
1325  __SS_ONLY_THROW__;
1326  }
1327 
1328  // this node is value node, so has no node to choose from
1329  __SS__
1330  << "\n\nError occurred descending from node '" << getValue() << "' in table '"
1331  << getTableName() << "' looking for child '" << nodeName << "'\n\n"
1332  << "Invalid depth! getNode() called from a value point in the Configuration Tree."
1333  << __E__;
1334  ss << "The original node search string was '" << originalNodeString << ".'" << __E__;
1335 
1336  ss << nodeDump() << __E__;
1337  __SS_ONLY_THROW__; // this node is value node, cant go any deeper!
1338 } // end recursiveGetNode()
1339 
1340 //==============================================================================
1342 std::map<std::string, ConfigurationTree> ConfigurationTree::getNodes(
1343  const std::string& nodeString) const
1344 {
1345  if(nodeString.length() < 1)
1346  {
1347  return getChildrenMap();
1348  }
1349 
1350  return getNode(nodeString).getChildrenMap();
1351 }
1352 //==============================================================================
1355 std::string ConfigurationTree::nodeDump(void) const
1356 {
1357  __SS__ << __E__ << __E__;
1358 
1359  ss << "Row=" << (int)row_ << ", Col=" << (int)col_ << ", TablePointer=" << table_
1360  << __E__;
1361 
1362  // stack trace can seg fault on demangle call!... ?
1363  try
1364  {
1365  ss << "\n\n" << StringMacros::stackTrace() << __E__ << __E__;
1366  }
1367  catch(...)
1368  {
1369  } // ignore errors
1370 
1371  ss << "ConfigurationTree::nodeDump() "
1372  "=====================================\nConfigurationTree::nodeDump():"
1373  << __E__;
1374 
1375  // try each level of debug.. and ignore errors
1376  try
1377  {
1378  ss << "\t"
1379  << "Node dump initiated from node '" << getValueAsString() << "'..." << __E__;
1380  }
1381  catch(...)
1382  {
1383  } // ignore errors
1384  try
1385  {
1386  ss << "\t"
1387  << "Node dump initiated from node '" << getValue() << "' in table '"
1388  << getTableName() << ".'" << __E__;
1389  }
1390  catch(...)
1391  {
1392  } // ignore errors
1393 
1394  try
1395  {
1396  //try to avoid recursive throwing of getChildrenNames() until death spiral
1397  if(isTableNode() || isGroupLinkNode())
1398  {
1399  auto children = getChildrenNames();
1400  ss << "\t"
1401  << "Here is the list of possible children (count = " << children.size()
1402  << "):" << __E__;
1403  for(auto& child : children)
1404  ss << "\t\t" << child << __E__;
1405  if(tableView_)
1406  {
1407  ss << "\n\nHere is the culprit table printout:\n\n";
1408  tableView_->print(ss);
1409  }
1410  }
1411  }
1412  catch(...)
1413  {
1414  } // ignore errors trying to show children
1415 
1416  ss << "\n\nend ConfigurationTree::nodeDump() ====================================="
1417  << __E__;
1418 
1419  return ss.str();
1420 } // end nodeDump()
1421 
1422 //==============================================================================
1423 ConfigurationTree ConfigurationTree::getBackNode(std::string nodeName,
1424  unsigned int backSteps) const
1425 {
1426  for(unsigned int i = 0; i < backSteps; i++)
1427  nodeName = nodeName.substr(0, nodeName.find_last_of('/'));
1428 
1429  return getNode(nodeName);
1430 } // end getBackNode()
1431 
1432 //==============================================================================
1433 ConfigurationTree ConfigurationTree::getForwardNode(std::string nodeName,
1434  unsigned int forwardSteps) const
1435 {
1436  unsigned int s = 0;
1437 
1438  // skip all leading /'s
1439  while(s < nodeName.length() && nodeName[s] == '/')
1440  ++s;
1441 
1442  for(unsigned int i = 0; i < forwardSteps; i++)
1443  s = nodeName.find('/', s) + 1;
1444 
1445  return getNode(nodeName.substr(0, s));
1446 } // end getForwardNode()
1447 
1448 //==============================================================================
1452 {
1453  return (row_ != TableView::INVALID && col_ != TableView::INVALID);
1454 } // end isValueNode()
1455 
1456 //==============================================================================
1460 {
1461  return isValueNode() && tableView_->getColumnInfo(col_).isBoolType();
1462 } // end isValueBoolType()
1463 
1464 //==============================================================================
1468 {
1469  return isValueNode() && tableView_->getColumnInfo(col_).isNumberDataType();
1470 } // end isValueBoolType()
1471 
1472 //==============================================================================
1478 {
1479  if(!isLinkNode())
1480  {
1481  __SS__ << "\n\nError occurred testing link connection at node with value '"
1482  << getValue() << "' in table '" << getTableName() << "'\n\n"
1483  << __E__;
1484  ss << "This is not a Link node! It is node type '" << getNodeType()
1485  << ".' Only a Link node can be disconnected." << __E__;
1486 
1487  ss << nodeDump() << __E__;
1488  __SS_ONLY_THROW__;
1489  }
1490 
1491  return !table_ || !tableView_;
1492 } // end isDisconnected()
1493 
1494 //==============================================================================
1497 bool ConfigurationTree::isLinkNode(void) const { return linkColName_ != ""; }
1498 
1499 //==============================================================================
1502 const std::string ConfigurationTree::NODE_TYPE_GROUP_TABLE = "GroupTableNode";
1503 const std::string ConfigurationTree::NODE_TYPE_TABLE = "TableNode";
1504 const std::string ConfigurationTree::NODE_TYPE_GROUP_LINK = "GroupLinkNode";
1505 const std::string ConfigurationTree::NODE_TYPE_UID_LINK = "UIDLinkNode";
1506 const std::string ConfigurationTree::NODE_TYPE_VALUE = "ValueNode";
1507 const std::string ConfigurationTree::NODE_TYPE_UID = "UIDNode";
1508 const std::string ConfigurationTree::NODE_TYPE_ROOT = "RootNode";
1509 
1510 std::string ConfigurationTree::getNodeType(void) const
1511 {
1512  if(isRootNode())
1513  return ConfigurationTree::NODE_TYPE_ROOT;
1514  if(isTableNode() && groupId_ != "")
1516  if(isTableNode())
1517  return ConfigurationTree::NODE_TYPE_TABLE;
1518  if(isGroupLinkNode())
1519  return ConfigurationTree::NODE_TYPE_GROUP_LINK;
1520  if(isLinkNode())
1521  return ConfigurationTree::NODE_TYPE_UID_LINK;
1522  if(isValueNode())
1523  return ConfigurationTree::NODE_TYPE_VALUE;
1524  return ConfigurationTree::NODE_TYPE_UID;
1525 } // end getNodeType()
1526 
1527 //==============================================================================
1531 {
1532  return (isLinkNode() && groupId_ != "");
1533 }
1534 
1535 //==============================================================================
1539 {
1540  return (isLinkNode() && groupId_ == "");
1541 } // end isUIDLinkNode()
1542 
1543 //==============================================================================
1547 {
1548  return (isValueNode() && tableView_->getColumnInfo(col_).isGroupID());
1549 } // end isGroupIDNode()
1550 
1551 //==============================================================================
1555 {
1556  return (row_ != TableView::INVALID && col_ == TableView::INVALID);
1557 }
1558 
1559 //==============================================================================
1575 std::vector<ConfigurationTree::RecordField> ConfigurationTree::getCommonFields(
1576  const std::vector<std::string /*uid*/>& recordList,
1577  const std::vector<std::string /*relative-path*/>& fieldAcceptList,
1578  const std::vector<std::string /*relative-path*/>& fieldRejectList,
1579  unsigned int depth,
1580  bool autoSelectFilterFields) const
1581 {
1582  // enforce that starting point is a table node
1583  if(!isRootNode() && !isTableNode())
1584  {
1585  __SS__ << "Can only get getCommonFields from a root or table node! "
1586  << "The node type is " << getNodeType() << __E__;
1587 
1588  ss << nodeDump() << __E__;
1589  __SS_THROW__;
1590  }
1591 
1592  std::vector<ConfigurationTree::RecordField> fieldCandidateList;
1593  std::vector<int> fieldCount; //-1 := guaranteed, else count must match num of records
1594 
1595  --depth; // decrement for recursion
1596 
1597  // for each record in <record list>
1598  // loop through all record's children
1599  // if isValueNode (value nodes are possible field candidates!)
1600  // if first uid record
1601  // add field to <field candidates list> if in <field filter list>
1602  // mark <field count> as guaranteed -1 (all these fields must be common
1603  // for UIDs in same table)
1604  // else not first uid record, do not need to check, must be same as first
1605  // record! else if depth > 0 and UID-Link Node recursively (call
1606  // recursiveGetCommonFields())
1607  // =====================
1608  // Start recursiveGetCommonFields()
1609  // --depth;
1610  // loop through all children
1611  // if isValueNode (value nodes are possible field candidates!)
1612  // if first uid record
1613  // add field to <field candidates list> if in <field
1614  // filter list> initial mark <field count> as 1
1615  // else
1616  // if field is in <field candidates list>,
1617  // increment <field count> for field candidate
1618  // else if field is not in list, ignore field
1619  // else if depth > 0 and is UID-Link
1620  // if Link Table/UID pair is not found in <field candidates
1621  // list> (avoid endless loops through tree)
1622  // recursiveGetCommonFields()
1623  // =====================
1624  //
1625  //
1626  // loop through all field candidates
1627  // remove those with <field count> != num of records
1628  //
1629  //
1630  // return result
1631 
1632  bool found; // used in loops
1633  // auto tableName = isRootNode()?"/":getTableName(); //all records will share this
1634  // table name
1635 
1636  // if no records, just return table fields
1637  if(!recordList.size() && tableView_)
1638  {
1639  const std::vector<TableViewColumnInfo>& colInfo = tableView_->getColumnsInfo();
1640 
1641  for(unsigned int col = 0; col < colInfo.size(); ++col)
1642  {
1643  __COUT_TYPE__(TLVL_DEBUG + 11) << __COUT_HDR__ << "Considering field "
1644  << colInfo[col].getName() << __E__;
1645 
1646  // check field accept filter list
1647  found = fieldAcceptList.size() ? false : true; // accept if no filter
1648  // list
1649  for(const auto& fieldFilter : fieldAcceptList)
1650  if(StringMacros::wildCardMatch(fieldFilter, colInfo[col].getName()))
1651  {
1652  found = true;
1653  break;
1654  }
1655 
1656  if(found)
1657  {
1658  // check field reject filter list
1659 
1660  found = true; // accept if no filter list
1661  for(const auto& fieldFilter : fieldRejectList)
1662  if(StringMacros::wildCardMatch(fieldFilter, colInfo[col].getName()))
1663  {
1664  found = false; // reject if match
1665  break;
1666  }
1667  }
1668 
1669  // if found, new field (since this is first record)
1670  if(found)
1671  {
1672  __COUT_TYPE__(TLVL_DEBUG + 11)
1673  << __COUT_HDR__ << "FOUND field " << colInfo[col].getName() << __E__;
1674 
1675  if(colInfo[col].isChildLink())
1676  {
1677  __COUT_TYPE__(TLVL_DEBUG + 11) << __COUT_HDR__ << "isGroupLinkNode "
1678  << colInfo[col].getName() << __E__;
1679 
1680  // must get column info differently for group link column
1681 
1682  std::pair<unsigned int /*link col*/, unsigned int /*link id col*/>
1683  linkPair;
1684  bool isGroupLink;
1685  tableView_->getChildLink(col, isGroupLink, linkPair);
1686 
1687  // add both link columns
1688 
1689  fieldCandidateList.push_back(ConfigurationTree::RecordField(
1690  table_->getTableName(),
1691  "", // uid
1692  tableView_->getColumnInfo(linkPair.first).getName(),
1693  "", // relative path, not including columnName_
1694  &tableView_->getColumnInfo(linkPair.first)));
1695  fieldCount.push_back(-1); // mark guaranteed field
1696 
1697  fieldCandidateList.push_back(ConfigurationTree::RecordField(
1698  table_->getTableName(),
1699  "", // uid
1700  tableView_->getColumnInfo(linkPair.second).getName(),
1701  "", // relative path, not including columnName_
1702  &tableView_->getColumnInfo(linkPair.second)));
1703  fieldCount.push_back(-1); // mark guaranteed field
1704  }
1705  else // value node
1706  {
1707  fieldCandidateList.push_back(ConfigurationTree::RecordField(
1708  table_->getTableName(),
1709  "", // uid
1710  colInfo[col].getName(),
1711  "", // relative path, not including columnName_
1712  &colInfo[col]));
1713  fieldCount.push_back(1); // init count to 1
1714  }
1715  }
1716  } // end table column loop
1717  } // end no record handling
1718 
1719  for(unsigned int i = 0; i < recordList.size(); ++i)
1720  {
1721  __COUT_TYPE__(TLVL_DEBUG + 11)
1722  << __COUT_HDR__ << "Checking " << recordList[i] << __E__;
1723  ConfigurationTree node = getNode(recordList[i]);
1724 
1725  node.recursiveGetCommonFields(fieldCandidateList,
1726  fieldCount,
1727  fieldAcceptList,
1728  fieldRejectList,
1729  depth,
1730  "", // relativePathBase
1731  !i // continue inFirstRecord (or not) depth search
1732  );
1733 
1734  } // end record loop
1735 
1736  __COUT__ << "======================= check for count = " << (int)recordList.size()
1737  << __E__;
1738 
1739  // loop through all field candidates
1740  // remove those with <field count> != num of records
1741  for(unsigned int i = 0; i < fieldCandidateList.size(); ++i)
1742  {
1743  __COUT_TYPE__(TLVL_DEBUG + 11)
1744  << __COUT_HDR__ << "Checking " << fieldCandidateList[i].relativePath_
1745  << fieldCandidateList[i].columnName_ << " = " << fieldCount[i] << __E__;
1746  if(recordList.size() != 0 && fieldCount[i] != -1 &&
1747  fieldCount[i] != (int)recordList.size())
1748  {
1749  __COUT_TYPE__(TLVL_DEBUG + 11)
1750  << __COUT_HDR__ << "Erasing " << fieldCandidateList[i].relativePath_
1751  << fieldCandidateList[i].columnName_ << __E__;
1752 
1753  fieldCount.erase(fieldCount.begin() + i);
1754  fieldCandidateList.erase(fieldCandidateList.begin() + i);
1755  --i; // rewind to look at next after deleted
1756  }
1757  }
1758 
1759  for(unsigned int i = 0; i < fieldCandidateList.size(); ++i)
1760  __COUT_TYPE__(TLVL_DEBUG + 11)
1761  << __COUT_HDR__ << "Pre-Final " << fieldCandidateList[i].relativePath_
1762  << fieldCandidateList[i].columnName_ << __E__;
1763 
1764  if(autoSelectFilterFields)
1765  {
1766  // filter for just 3 of the best filter fields
1767  // i.e. preference for GroupID, On/Off, and FixedChoice fields.
1768  std::set<std::pair<unsigned int /*fieldPriority*/, unsigned int /*fieldIndex*/>>
1769  prioritySet;
1770 
1771  unsigned int priorityPenalty;
1772  for(unsigned int i = 0; i < fieldCandidateList.size(); ++i)
1773  {
1774  __COUT_TYPE__(TLVL_DEBUG + 11)
1775  << __COUT_HDR__ << "Option [" << i << "] "
1776  << fieldCandidateList[i].relativePath_
1777  << fieldCandidateList[i].columnName_ << " : "
1778  << fieldCandidateList[i].columnInfo_->getType() << ":"
1779  << fieldCandidateList[i].columnInfo_->getDataType() << __E__;
1780 
1781  priorityPenalty = std::count(fieldCandidateList[i].relativePath_.begin(),
1782  fieldCandidateList[i].relativePath_.end(),
1783  '/') *
1784  20; // penalize if not top level
1785 
1786  if(fieldCandidateList[i].columnInfo_->isBoolType() &&
1787  (fieldCandidateList[i].columnName_ ==
1788  TableViewColumnInfo::COL_NAME_STATUS ||
1789  fieldCandidateList[i].columnName_ ==
1790  TableViewColumnInfo::COL_NAME_ENABLED))
1791  {
1792  priorityPenalty += 0;
1793  }
1794  else if(fieldCandidateList[i].columnInfo_->isGroupID())
1795  {
1796  priorityPenalty += 1;
1797  }
1798  else if(fieldCandidateList[i].columnInfo_->isBoolType())
1799  {
1800  priorityPenalty += 3;
1801  }
1802  else if(fieldCandidateList[i].columnInfo_->getType() ==
1803  TableViewColumnInfo::TYPE_FIXED_CHOICE_DATA)
1804  {
1805  priorityPenalty += 3;
1806  }
1807  else if(fieldCandidateList[i].columnInfo_->getType() ==
1808  TableViewColumnInfo::TYPE_DATA)
1809  {
1810  priorityPenalty += 10;
1811  }
1812  else // skip other fields and mark for erasing
1813  {
1814  fieldCandidateList[i].tableName_ =
1815  ""; // clear table name as indicator for erase
1816  continue;
1817  }
1818  prioritySet.emplace(
1819  std::make_pair(priorityPenalty /*fieldPriority*/, i /*fieldIndex*/));
1820  __COUT_TYPE__(TLVL_DEBUG + 11)
1821  << __COUT_HDR__ << "Option [" << i << "] "
1822  << fieldCandidateList[i].relativePath_
1823  << fieldCandidateList[i].columnName_ << " : "
1824  << fieldCandidateList[i].columnInfo_->getType() << ":"
1825  << fieldCandidateList[i].columnInfo_->getDataType()
1826  << "... priority = " << priorityPenalty << __E__;
1827 
1828  } // done ranking fields
1829 
1830  __COUTV__(StringMacros::setToString(prioritySet));
1831 
1832  // now choose the top 3, and delete the rest
1833  // clear table name to indicate field should be erased
1834  {
1835  unsigned int cnt = 0;
1836  for(const auto& priorityFieldIndex : prioritySet)
1837  if(++cnt > 3) // then mark for erasing
1838  {
1839  __COUT_TYPE__(TLVL_DEBUG + 11)
1840  << __COUT_HDR__ << cnt << " marking "
1841  << fieldCandidateList[priorityFieldIndex.second].relativePath_
1842  << fieldCandidateList[priorityFieldIndex.second].columnName_
1843  << __E__;
1844  fieldCandidateList[priorityFieldIndex.second].tableName_ =
1845  ""; // clear table name as indicator for erase
1846  }
1847  }
1848 
1849  for(unsigned int i = 0; i < fieldCandidateList.size(); ++i)
1850  {
1851  if(fieldCandidateList[i].tableName_ == "") // then erase
1852  {
1853  __COUT_TYPE__(TLVL_DEBUG + 11)
1854  << __COUT_HDR__ << "Erasing " << fieldCandidateList[i].relativePath_
1855  << fieldCandidateList[i].columnName_ << __E__;
1856  fieldCandidateList.erase(fieldCandidateList.begin() + i);
1857  --i; // rewind to look at next after deleted
1858  }
1859  }
1860  } // end AUTO filter field selection
1861 
1862  for(unsigned int i = 0; i < fieldCandidateList.size(); ++i)
1863  __COUT__ << "Final " << fieldCandidateList[i].relativePath_
1864  << fieldCandidateList[i].columnName_ << __E__;
1865 
1866  return fieldCandidateList;
1867 } // end getCommonFields()
1868 
1869 //==============================================================================
1875 std::set<std::string /*unique-value*/> ConfigurationTree::getUniqueValuesForField(
1876  const std::vector<std::string /*relative-path*/>& recordList,
1877  const std::string& fieldName,
1878  std::string* fieldGroupIDChildLinkIndex /* =0 */) const
1879 {
1880  if(fieldGroupIDChildLinkIndex)
1881  *fieldGroupIDChildLinkIndex = "";
1882 
1883  // enforce that starting point is a table node
1884  if(!isTableNode())
1885  {
1886  __SS__ << "Can only get getCommonFields from a table node! "
1887  << "The node type is " << getNodeType() << __E__;
1888 
1889  ss << nodeDump() << __E__;
1890  __SS_THROW__;
1891  }
1892 
1893  std::set<std::string /*unique-value*/> uniqueValues;
1894 
1895  // for each record in <record list>
1896  // emplace value at field into set
1897  //
1898  // return result
1899 
1900  // if no records, just return fieldGroupIDChildLinkIndex
1901  if(!recordList.size() && tableView_ && fieldGroupIDChildLinkIndex)
1902  {
1903  const TableViewColumnInfo& colInfo =
1904  tableView_->getColumnInfo(tableView_->findCol(fieldName));
1905 
1906  if(colInfo.isGroupID())
1907  *fieldGroupIDChildLinkIndex = colInfo.getChildLinkIndex();
1908 
1909  } // end no records
1910 
1911  for(unsigned int i = 0; i < recordList.size(); ++i)
1912  {
1913  //__COUT__ << "Checking " << recordList[i] << __E__;
1914 
1915  // Note: that ConfigurationTree maps both fields associated with a link
1916  // to the same node instance.
1917  // The behavior is likely not expected as response for this function..
1918  // so for links return actual value for field name specified
1919  // i.e. if Table of link is requested give that; if linkID is requested give
1920  // that. use TRUE in getValueAsString for proper behavior
1921 
1922  ConfigurationTree node = getNode(recordList[i]).getNode(fieldName);
1923 
1924  if(node.isGroupIDNode())
1925  {
1926  // handle groupID node special
1927 
1928  //__COUT__ << "GroupID field " << fieldName << __E__;
1929 
1930  // first time, get field's GroupID Child Link Index, if applicable
1931  if(i == 0 && fieldGroupIDChildLinkIndex)
1932  *fieldGroupIDChildLinkIndex = node.getColumnInfo().getChildLinkIndex();
1933 
1934  // return set of groupIDs individually
1935 
1936  std::set<std::string> setOfGroupIDs = node.getSetOfGroupIDs();
1937  for(auto& groupID : setOfGroupIDs)
1938  uniqueValues.emplace(groupID);
1939  }
1940  else // normal record, return value as string
1941  uniqueValues.emplace(node.getValueAsString(true));
1942 
1943  } // end record loop
1944 
1945  return uniqueValues;
1946 } // end getUniqueValuesForField()
1947 
1948 //==============================================================================
1951 void ConfigurationTree::recursiveGetCommonFields(
1952  std::vector<ConfigurationTree::RecordField>& fieldCandidateList,
1953  std::vector<int>& fieldCount,
1954  const std::vector<std::string /*relative-path*/>& fieldAcceptList,
1955  const std::vector<std::string /*relative-path*/>& fieldRejectList,
1956  unsigned int depth,
1957  const std::string& relativePathBase,
1958  bool inFirstRecord) const
1959 {
1960  //__COUT__ << depth << ":relativePathBase " << relativePathBase <<
1961  // " + " << inFirstRecord <<__E__;
1962  --depth;
1963 
1964  // clang-format off
1965  // =====================
1966  // Start recursiveGetCommonFields()
1967  // --depth;
1968  // loop through all children
1969  // if isValueNode (value nodes are possible field candidates!)
1970  // if first uid record
1971  // add field to <field candidates list> if in <field filter list>
1972  // initial mark <field count> as 1
1973  // else
1974  // if field is in list,
1975  // increment count for field candidate
1976  // //?increment fields in list count for record
1977  // else if field is not in list, discard field
1978  // else if depth > 0 and is UID-Link
1979  // if Link Table/UID pair is not found in <field candidates list>
1980  // (avoid endless loops through tree)
1981  // recursiveGetCommonFields()
1982  // =====================
1983  // clang-format on
1984 
1985  bool found; // used in loops
1986  auto tableName = getTableName(); // all fields will share this table name
1987  auto uid = getUIDAsString(); // all fields will share this uid
1988  unsigned int j;
1989 
1990  auto recordChildren = getChildren();
1991  for(const auto& fieldNode : recordChildren)
1992  {
1993  //__COUT__ << "All... " << fieldNode.second.getNodeType() <<
1994  // " -- " << (relativePathBase + fieldNode.first) <<
1995  // " + " << inFirstRecord <<__E__;
1996 
1997  if(fieldNode.second.isValueNode() || fieldNode.second.isGroupLinkNode())
1998  {
1999  // skip author and record insertion time
2000  if(fieldNode.second.isValueNode())
2001  {
2002  if(fieldNode.second.getColumnInfo().getType() ==
2003  TableViewColumnInfo::TYPE_AUTHOR ||
2004  fieldNode.second.getColumnInfo().getType() ==
2005  TableViewColumnInfo::TYPE_TIMESTAMP)
2006  continue;
2007 
2008  //__COUT__ << "isValueNode " << fieldNode.first << __E__;
2009  }
2010 
2011  if(inFirstRecord) // first uid record
2012  {
2013  //__COUT__ << "Checking... " << fieldNode.second.getNodeType() <<
2014  // " -- " << (relativePathBase + fieldNode.first) <<
2015  // "-- depth=" << depth << __E__;
2016 
2017  // check field accept filter list
2018  found = fieldAcceptList.size() ? false : true; // accept if no filter
2019  // list
2020  for(const auto& fieldFilter : fieldAcceptList)
2021  if(fieldFilter.find('/') != std::string::npos)
2022  {
2023  // filter is for full path, so add relative path base
2025  fieldFilter, relativePathBase + fieldNode.first))
2026  {
2027  found = true;
2028  break;
2029  }
2030  }
2031  else if(StringMacros::wildCardMatch(fieldFilter, fieldNode.first))
2032  {
2033  found = true;
2034  break;
2035  }
2036 
2037  if(found)
2038  {
2039  // check field reject filter list
2040 
2041  found = true; // accept if no filter list
2042  for(const auto& fieldFilter : fieldRejectList)
2043  if(fieldFilter.find('/') != std::string::npos)
2044  {
2045  // filter is for full path, so add relative path base
2047  fieldFilter, relativePathBase + fieldNode.first))
2048  {
2049  found = false; // reject if match
2050  break;
2051  }
2052  }
2053  else if(StringMacros::wildCardMatch(fieldFilter, fieldNode.first))
2054  {
2055  found = false; // reject if match
2056  break;
2057  }
2058  }
2059 
2060  // if found, new field (since this is first record)
2061  if(found)
2062  {
2063  //__COUT__ << "FOUND field " <<
2064  // (relativePathBase + fieldNode.first) << __E__;
2065 
2066  if(fieldNode.second.isGroupLinkNode())
2067  {
2068  //__COUT__ << "isGroupLinkNode " << fieldNode.first << __E__;
2069 
2070  // must get column info differently for group link column
2071 
2072  std::pair<unsigned int /*link col*/, unsigned int /*link id col*/>
2073  linkPair;
2074  bool isGroupLink;
2075  tableView_->getChildLink(
2076  tableView_->findCol(fieldNode.first), isGroupLink, linkPair);
2077 
2078  // add both link columns
2079 
2080  fieldCandidateList.push_back(ConfigurationTree::RecordField(
2081  table_->getTableName(),
2082  uid,
2083  tableView_->getColumnInfo(linkPair.first).getName(),
2084  relativePathBase, // relative path, not including columnName_
2085  &tableView_->getColumnInfo(linkPair.first)));
2086  fieldCount.push_back(1); // init count to 1
2087 
2088  fieldCandidateList.push_back(ConfigurationTree::RecordField(
2089  table_->getTableName(),
2090  uid,
2091  tableView_->getColumnInfo(linkPair.second).getName(),
2092  relativePathBase, // relative path, not including columnName_
2093  &tableView_->getColumnInfo(linkPair.second)));
2094  fieldCount.push_back(1); // init count to 1
2095  }
2096  else // value node
2097  {
2098  fieldCandidateList.push_back(ConfigurationTree::RecordField(
2099  tableName,
2100  uid,
2101  fieldNode.first,
2102  relativePathBase, // relative path, not including columnName_
2103  &fieldNode.second.getColumnInfo()));
2104  fieldCount.push_back(1); // init count to 1
2105  }
2106  }
2107  }
2108  else // not first record
2109  {
2110  // if field is in <field candidates list>, increment <field count>
2111  // else ignore
2112  for(j = 0; j < fieldCandidateList.size(); ++j)
2113  {
2114  if((relativePathBase + fieldNode.first) ==
2115  (fieldCandidateList[j].relativePath_ +
2116  fieldCandidateList[j].columnName_))
2117  {
2118  //__COUT__ << "incrementing " << j <<
2119  // " " << fieldCandidateList[j].relativePath_ << __E__;
2120  // found, so increment <field count>
2121  ++fieldCount[j];
2122  if(fieldNode.second.isGroupLinkNode() &&
2123  j + 1 < fieldCandidateList.size())
2124  ++fieldCount[j + 1]; // increment associated link index too!
2125  break;
2126  }
2127  }
2128  }
2129  } // end value and group link node handling
2130  else if(fieldNode.second.isUIDLinkNode())
2131  {
2132  //__COUT__ << "isUIDLinkNode " << (relativePathBase + fieldNode.first) <<
2133  // " + " << inFirstRecord << __E__;
2134 
2135  if(inFirstRecord) // first uid record
2136  {
2137  // check field accept filter list
2138  found =
2139  fieldAcceptList.size() ? false : true; // accept if no filter list
2140  for(const auto& fieldFilter : fieldAcceptList)
2141  if(fieldFilter.find('/') != std::string::npos)
2142  {
2143  // filter is for full path, so add relative path base
2145  fieldFilter, relativePathBase + fieldNode.first))
2146  {
2147  found = true;
2148  break;
2149  }
2150  }
2151  else if(StringMacros::wildCardMatch(fieldFilter, fieldNode.first))
2152  {
2153  found = true;
2154  break;
2155  }
2156 
2157  if(found)
2158  {
2159  // check field reject filter list
2160 
2161  found = true; // accept if no filter list
2162  for(const auto& fieldFilter : fieldRejectList)
2163  if(fieldFilter.find('/') != std::string::npos)
2164  {
2165  // filter is for full path, so add relative path base
2167  fieldFilter, relativePathBase + fieldNode.first))
2168  {
2169  found = false; // reject if match
2170  break;
2171  }
2172  }
2173  else if(StringMacros::wildCardMatch(fieldFilter, fieldNode.first))
2174  {
2175  found = false; // reject if match
2176  break;
2177  }
2178  }
2179 
2180  //__COUTV__(found);
2181 
2182  // if found, new field (since this is first record)
2183  if(found)
2184  {
2185  std::pair<unsigned int /*link col*/, unsigned int /*link id col*/>
2186  linkPair;
2187  bool isGroupLink;
2188 
2189  //__COUTV__(fieldNode.first);
2190  tableView_->getChildLink(
2191  tableView_->findCol(fieldNode.first), isGroupLink, linkPair);
2192 
2193  // add both link columns
2194 
2195  fieldCandidateList.push_back(ConfigurationTree::RecordField(
2196  table_->getTableName(),
2197  uid,
2198  tableView_->getColumnInfo(linkPair.first).getName(),
2199  relativePathBase, // relative path, not including columnName_
2200  &tableView_->getColumnInfo(linkPair.first)));
2201  fieldCount.push_back(1); // init count to 1
2202 
2203  fieldCandidateList.push_back(ConfigurationTree::RecordField(
2204  table_->getTableName(),
2205  uid,
2206  tableView_->getColumnInfo(linkPair.second).getName(),
2207  relativePathBase, // relative path, not including columnName_
2208  &tableView_->getColumnInfo(linkPair.second)));
2209  fieldCount.push_back(1); // init count to 1
2210  }
2211  }
2212  else // not first record
2213  {
2214  // if link fields (MUST BE 2) is in <field candidates list>, increment <field count>
2215  // else ignore
2216  for(j = 0; j < fieldCandidateList.size() - 1; ++j)
2217  {
2218  if((relativePathBase + fieldNode.first) ==
2219  (fieldCandidateList[j].relativePath_ +
2220  fieldCandidateList[j].columnName_))
2221  {
2222  //__COUT__ << "incrementing " << j <<
2223  // " " << fieldCandidateList[j].relativePath_ << __E__;
2224  // found, so increment <field count>
2225  ++fieldCount[j];
2226  ++fieldCount[j + 1]; // increment associated link index too!
2227  break;
2228  }
2229  }
2230  }
2231 
2232  // if depth remaining, then follow link, recursively!
2233  if(depth > 0 && !fieldNode.second.isDisconnected())
2234  fieldNode.second.recursiveGetCommonFields(
2235  fieldCandidateList,
2236  fieldCount,
2237  fieldAcceptList,
2238  fieldRejectList,
2239  depth,
2240  (relativePathBase + fieldNode.first) + "/", // relativePathBase
2241  inFirstRecord // continue inFirstRecord (or not) depth search
2242  );
2243  } // end handle unique link node
2244  } // end field node loop
2245 } // end recursiveGetCommonFields()
2246 
2247 //==============================================================================
2253 std::vector<std::vector<std::pair<std::string, ConfigurationTree>>>
2255  std::map<std::string /*relative-path*/, std::string /*value*/> filterMap,
2256  bool onlyStatusTrue) const
2257 {
2258  std::vector<std::vector<std::pair<std::string, ConfigurationTree>>> retVector;
2259 
2260  //__COUT__ << "Children of node: " << getValueAsString() << __E__;
2261 
2262  bool filtering = filterMap.size();
2263  std::string fieldValue;
2264 
2265  bool createContainer;
2266 
2267  std::vector<std::vector<std::string>> childrenNamesByPriority =
2268  getChildrenNamesByPriority(onlyStatusTrue);
2269 
2270  for(auto& childNamesAtPriority : childrenNamesByPriority)
2271  {
2272  createContainer = true;
2273 
2274  for(auto& childName : childNamesAtPriority)
2275  {
2276  //__COUT__ << "\tChild: " << childName << __E__;
2277 
2278  if(filtering) // if all criteria are not met, then skip
2279  if(!passFilterMap(childName, filterMap))
2280  continue;
2281 
2282  if(createContainer)
2283  {
2284  retVector.push_back(
2285  std::vector<std::pair<std::string, ConfigurationTree>>());
2286  createContainer = false;
2287  }
2288 
2289  retVector[retVector.size() - 1].push_back(
2290  std::pair<std::string, ConfigurationTree>(
2291  childName, this->getNode(childName, true)));
2292  } // end children within priority loop
2293  } // end children by priority loop
2294 
2295  //__COUT__ << "Done w/Children of node: " << getValueAsString() << __E__;
2296  return retVector;
2297 } // end getChildrenByPriority()
2298 
2299 //==============================================================================
2303  const std::string& childName,
2304  std::map<std::string /*relative-path*/, std::string /*value*/> filterMap) const
2305 {
2306  // if all criteria are not met, then skip
2307  bool skip = false;
2308 
2309  // for each filter, check value
2310  for(const auto& filterPair : filterMap)
2311  {
2312  std::string filterPath = childName + "/" + filterPair.first;
2313  __COUTV__(filterPath);
2314 
2315  ConfigurationTree childNode = this->getNode(filterPath);
2316  try
2317  {
2318  // extract field value list
2319  std::vector<std::string> fieldValues;
2321  filterPair.second, fieldValues, std::set<char>({','}) /*delimiters*/);
2322 
2323  __COUTV__(fieldValues.size());
2324 
2325  skip = true;
2326  // for each field check if any match
2327  for(const auto& fieldValue : fieldValues)
2328  {
2329  // Note: that ConfigurationTree maps both fields associated with a
2330  // link to the same node instance. The behavior is likely not
2331  // expected as response for this function.. so for links
2332  // return
2333  // actual value for field name specified i.e. if Table of link
2334  // is requested give that; if linkID is requested give that. use
2335  // TRUE in getValueAsString for proper behavior
2336 
2337  if(childNode.isGroupIDNode())
2338  {
2339  // handle groupID node special, check against set of groupIDs
2340 
2341  bool groupIdFound = false;
2342  std::set<std::string> setOfGroupIDs = childNode.getSetOfGroupIDs();
2343 
2344  for(auto& groupID : setOfGroupIDs)
2345  {
2346  __COUT__ << "\t\tGroupID Check: " << filterPair.first
2347  << " == " << fieldValue << " => "
2348  << StringMacros::decodeURIComponent(fieldValue)
2349  << " ??? " << groupID << __E__;
2350 
2352  StringMacros::decodeURIComponent(fieldValue), groupID))
2353  {
2354  // found a match for the field/groupId pair
2355  __COUT__ << "Found match" << __E__;
2356  groupIdFound = true;
2357  break;
2358  }
2359  } // end groupID search
2360 
2361  if(groupIdFound)
2362  {
2363  // found a match for the field/groupId-set pair
2364  __COUT__ << "Found break match" << __E__;
2365  skip = false;
2366  break;
2367  }
2368  }
2369  else // normal child node, check against value
2370  {
2371  __COUT__ << "\t\tCheck: " << filterPair.first << " == " << fieldValue
2372  << " => " << StringMacros::decodeURIComponent(fieldValue)
2373  << " ??? " << childNode.getValueAsString(true) << __E__;
2374 
2377  childNode.getValueAsString(true)))
2378  {
2379  // found a match for the field/value pair
2380  skip = false;
2381  break;
2382  }
2383  }
2384  }
2385  }
2386  catch(...)
2387  {
2388  __SS__ << "Failed to access filter path '" << filterPath << "' - aborting."
2389  << __E__;
2390 
2391  ss << nodeDump() << __E__;
2392  __SS_THROW__;
2393  }
2394 
2395  if(skip)
2396  break; // no match for this field, so stop checking and skip this
2397  // record
2398  }
2399  return !skip;
2400 } //end passFilterMap()
2401 
2402 //==============================================================================
2411 std::vector<std::pair<std::string, ConfigurationTree>> ConfigurationTree::getChildren(
2412  std::map<std::string /*relative-path*/, std::string /*value*/> filterMap,
2413  bool byPriority,
2414  bool onlyStatusTrue) const
2415 {
2416  std::vector<std::pair<std::string, ConfigurationTree>> retVector;
2417 
2418  //__COUT__ << "Children of node: " << getValueAsString() << __E__;
2419 
2420  bool filtering = filterMap.size();
2421  // bool skip;
2422  std::string fieldValue;
2423 
2424  std::vector<std::string> childrenNames = getChildrenNames(byPriority, onlyStatusTrue);
2425  for(auto& childName : childrenNames)
2426  {
2427  if(filtering && // if all criteria are not met, then skip
2428  !passFilterMap(childName, filterMap))
2429  continue;
2430 
2431  retVector.push_back(std::pair<std::string, ConfigurationTree>(
2432  childName, this->getNode(childName, true)));
2433  }
2434 
2435  //__COUT__ << "Done w/Children of node: " << getValueAsString() << __E__;
2436  return retVector;
2437 } // end getChildren()
2438 
2439 //==============================================================================
2443 std::map<std::string, ConfigurationTree> ConfigurationTree::getChildrenMap(
2444  std::map<std::string /*relative-path*/, std::string /*value*/> filterMap,
2445  bool onlyStatusTrue) const
2446 {
2447  std::map<std::string, ConfigurationTree> retMap;
2448 
2449  bool filtering = filterMap.size();
2450 
2451  //__COUT__ << "Children of node: " << getValueAsString() << __E__;
2452  std::vector<std::string> childrenNames =
2453  getChildrenNames(false /* byPriority */, onlyStatusTrue);
2454  for(auto& childName : childrenNames)
2455  {
2456  //__COUT__ << "\tChild: " << childName << __E__;
2457 
2458  // if all criteria are not met, then skip
2459  if(filtering && !passFilterMap(childName, filterMap))
2460  continue;
2461 
2462  retMap.insert(std::pair<std::string, ConfigurationTree>(
2463  childName, this->getNode(childName)));
2464  }
2465 
2466  //__COUT__ << "Done w/Children of node: " << getValueAsString() << __E__;
2467  return retMap;
2468 } // end getChildrenMap()
2469 
2470 //==============================================================================
2473 {
2474  if(!isUIDNode())
2475  {
2476  __SS__ << "Can not get status of '" << getValueAsString()
2477  << ".' Can only check the status of a UID/Record node!" << __E__;
2478  ss << nodeDump() << __E__;
2479  __SS_THROW__;
2480  }
2481 
2482  bool tmpStatus = true;
2483  try
2484  {
2485  tableView_->getValue(tmpStatus, row_, tableView_->getColStatus());
2486  }
2487  catch(const std::runtime_error& e)
2488  {
2489  //ignore error, assuming does not have a status column
2490  //default to enabled if no status
2491  }
2492  return tmpStatus;
2493 } // end isEnabled()
2494 
2495 //==============================================================================
2496 bool ConfigurationTree::isStatusNode(void) const
2497 {
2498  if(!isValueNode())
2499  return false;
2500 
2501  return col_ == tableView_->getColStatus();
2502 } // end isStatusNode()
2503 
2504 //==============================================================================
2507 std::vector<std::vector<std::string>> ConfigurationTree::getChildrenNamesByPriority(
2508  bool onlyStatusTrue) const
2509 {
2510  std::vector<std::vector<std::string /*child name*/>> retVector;
2511 
2512  if(!tableView_)
2513  {
2514  __SS__ << "Can not get children names of '" << getValueAsString()
2515  << "' with null configuration view pointer!" << __E__;
2516  if(isLinkNode() && isDisconnected())
2517  ss << " This node is a disconnected link to " << getDisconnectedTableName()
2518  << __E__;
2519 
2520  ss << nodeDump() << __E__;
2521  __SS_ONLY_THROW__;
2522  }
2523 
2524  if(row_ == TableView::INVALID && col_ == TableView::INVALID)
2525  {
2526  // this node is table node
2527  // so return all uid node strings that match groupId
2528 
2529  // bool tmpStatus;
2530 
2531  std::vector<std::vector<unsigned int /*group row*/>> groupRowsByPriority =
2532  tableView_->getGroupRowsByPriority(
2533  groupId_ == ""
2534  ? TableView::INVALID
2535  : // if no group ID, take all rows and ignore column, do not attempt link lookup
2536  tableView_->getLinkGroupIDColumn(childLinkIndex_),
2537  groupId_,
2538  onlyStatusTrue);
2539 
2540  // now build vector of vector names by priority
2541  for(const auto& priorityChildRowVector : groupRowsByPriority)
2542  {
2543  retVector.push_back(std::vector<std::string /*child name*/>());
2544  for(const auto& priorityChildRow : priorityChildRowVector)
2545  retVector[retVector.size() - 1].push_back(
2546  tableView_->getDataView()[priorityChildRow][tableView_->getColUID()]);
2547  }
2548  }
2549  else if(row_ == TableView::INVALID)
2550  {
2551  __SS__ << "Malformed ConfigurationTree" << __E__;
2552 
2553  ss << nodeDump() << __E__;
2554  __SS_THROW__;
2555  }
2556  else if(col_ == TableView::INVALID)
2557  {
2558  // this node is uid node
2559  // so return all link and value nodes
2560 
2561  for(unsigned int c = 0; c < tableView_->getNumberOfColumns(); ++c)
2562  if(c == tableView_->getColUID() || // skip UID and linkID columns (only show
2563  // link column, to avoid duplicates)
2564  tableView_->getColumnInfo(c).isChildLinkGroupID() ||
2565  tableView_->getColumnInfo(c).isChildLinkUID())
2566  continue;
2567  else
2568  {
2569  retVector.push_back(std::vector<std::string /*child name*/>());
2570  retVector[retVector.size() - 1].push_back(
2571  tableView_->getColumnInfo(c).getName());
2572  }
2573  }
2574  else // this node is value node, so has no node to choose from
2575  {
2576  // this node is value node, cant go any deeper!
2577  __SS__ << "\n\nError occurred looking for children of nodeName=" << getValueName()
2578  << "\n\n"
2579  << "Invalid depth! getChildrenValues() called from a value point in the "
2580  "Configuration Tree."
2581  << __E__;
2582 
2583  ss << nodeDump() << __E__;
2584  __SS_THROW__;
2585  }
2586 
2587  return retVector;
2588 } // end getChildrenNamesByPriority()
2589 
2590 //==============================================================================
2593 std::vector<std::string> ConfigurationTree::getChildrenNames(bool byPriority,
2594  bool onlyStatusTrue) const
2595 {
2596  std::vector<std::string /*child name*/> retVector;
2597 
2598  if(isRootNode())
2599  {
2600  for(auto& configPair : configMgr_->getActiveVersions())
2601  {
2602  //__GEN_COUT__ << configPair.first << " " << (int)(configPair.second?1:0) <<
2603  // __E__;
2604  retVector.push_back(configPair.first);
2605  }
2606  return retVector;
2607  }
2608 
2609  if(!tableView_)
2610  {
2611  __SS__ << "Can not get children names of '" << getFieldName() << ":"
2612  << getValueAsString() << "' with null configuration view pointer!"
2613  << __E__;
2614  if(isLinkNode() && isDisconnected())
2615  ss << " This node is a disconnected link to " << getDisconnectedTableName()
2616  << "(" << getDisconnectedLinkID() << ")" << __E__;
2617  __SS_ONLY_THROW__;
2618  }
2619 
2620  if(row_ == TableView::INVALID && col_ == TableView::INVALID)
2621  {
2622  // this node is table node
2623  // so return all uid node strings that match groupId
2624  std::vector<unsigned int /*group row*/> groupRows = tableView_->getGroupRows(
2625  (groupId_ == ""
2626  ? TableView::INVALID
2627  : // if no group ID, take all rows, do not attempt link lookup
2628  tableView_->getLinkGroupIDColumn(childLinkIndex_)),
2629  groupId_,
2630  onlyStatusTrue,
2631  byPriority);
2632 
2633  // now build vector of vector names by priority
2634  for(const auto& groupRow : groupRows)
2635  retVector.push_back(
2636  tableView_->getDataView()[groupRow][tableView_->getColUID()]);
2637 
2638  // bool tmpStatus;
2639  //
2640  // if(byPriority) // reshuffle by priority
2641  // {
2642  // try
2643  // {
2644  // std::map<uint64_t /*priority*/, std::vector<unsigned int /*child row*/>> orderedByPriority;
2645  // std::vector<std::string /*child name*/> retPrioritySet;
2646  //
2647  // unsigned int col = tableView_->getColPriority();
2648  //
2649  // uint64_t tmpPriority;
2650  //
2651  // for(unsigned int r = 0; r < tableView_->getNumberOfRows(); ++r)
2652  // if(groupId_ == "" || tableView_->isEntryInGroup(r, childLinkIndex_, groupId_))
2653  // {
2654  // // check status if needed
2655  // if(onlyStatusTrue)
2656  // {
2657  // tableView_->getValue(tmpStatus, r, tableView_->getColStatus());
2658  //
2659  // if(!tmpStatus)
2660  // continue; // skip those with status false
2661  // }
2662  //
2663  // tableView_->getValue(tmpPriority, r, col);
2664  // // do not accept DEFAULT value of 0.. convert to 100
2665  // orderedByPriority[tmpPriority ? tmpPriority : 100].push_back(r);
2666  // }
2667  //
2668  // // at this point have priority map
2669  // // now build return vector
2670  //
2671  // for(const auto& priorityChildRowVector : orderedByPriority)
2672  // for(const auto& priorityChildRow : priorityChildRowVector.second)
2673  // retVector.push_back(tableView_->getDataView()[priorityChildRow][tableView_->getColUID()]);
2674  //
2675  // __COUT__ << "Returning priority children list." << __E__;
2676  // return retVector;
2677  // }
2678  // catch(std::runtime_error& e)
2679  // {
2680  // __COUT_WARN__ << "Priority configuration not found. Assuming all "
2681  // "children have equal priority. "
2682  // << __E__;
2683  // retVector.clear();
2684  // }
2685  // }
2686  // // else not by priority
2687  //
2688  // for(unsigned int r = 0; r < tableView_->getNumberOfRows(); ++r)
2689  // if(groupId_ == "" || tableView_->isEntryInGroup(r, childLinkIndex_, groupId_))
2690  // {
2691  // // check status if needed
2692  // if(onlyStatusTrue)
2693  // {
2694  // tableView_->getValue(tmpStatus, r, tableView_->getColStatus());
2695  //
2696  // if(!tmpStatus)
2697  // continue; // skip those with status false
2698  // }
2699  //
2700  // retVector.push_back(tableView_->getDataView()[r][tableView_->getColUID()]);
2701  // }
2702  }
2703  else if(row_ == TableView::INVALID)
2704  {
2705  __SS__ << "Malformed ConfigurationTree" << __E__;
2706 
2707  ss << nodeDump() << __E__;
2708  __SS_THROW__;
2709  }
2710  else if(col_ == TableView::INVALID)
2711  {
2712  // this node is uid node
2713  // so return all link and value nodes
2714 
2715  for(unsigned int c = 0; c < tableView_->getNumberOfColumns(); ++c)
2716  if(c == tableView_->getColUID() || // skip UID and linkID columns (only show
2717  // link column, to avoid duplicates)
2718  tableView_->getColumnInfo(c).isChildLinkGroupID() ||
2719  tableView_->getColumnInfo(c).isChildLinkUID())
2720  continue;
2721  else
2722  retVector.push_back(tableView_->getColumnInfo(c).getName());
2723  }
2724  else // this node is value node, so has no node to choose from
2725  {
2726  // this node is value node, cant go any deeper!
2727  __SS__ << "\n\nError occurred looking for children of nodeName=" << getValueName()
2728  << "\n\n"
2729  << "Invalid depth! getChildrenValues() called from a value point in the "
2730  "Configuration Tree."
2731  << __E__;
2732 
2733  ss << nodeDump() << __E__;
2734  __SS_THROW__;
2735  }
2736 
2737  return retVector;
2738 } // end getChildrenNames()
2739 
2740 //==============================================================================
2744 ConfigurationTree ConfigurationTree::getValueAsTreeNode(void) const
2745 {
2746  // check if first character is a /, .. if so try to get value in tree
2747  // if exception, just take value
2748  // note: this call will throw an error, in effect, if not a "value" node
2749  if(!tableView_)
2750  {
2751  __SS__ << "Invalid node for get value." << __E__;
2752  __SS_THROW__;
2753  }
2754 
2755  std::string valueString =
2756  tableView_->getValueAsString(row_, col_, true /* convertEnvironmentVariables */);
2757  //__COUT__ << valueString << __E__;
2758  if(valueString.size() && valueString[0] == '/')
2759  {
2760  //__COUT__ << "Starts with '/' - check if valid tree path: " << valueString <<
2761  // __E__;
2762  try
2763  {
2764  ConfigurationTree retNode = configMgr_->getNode(valueString);
2765  __COUT__ << "Found a valid tree path in value!" << __E__;
2766  return retNode;
2767  }
2768  catch(...)
2769  {
2770  __SS__ << "Invalid tree path." << __E__;
2771  __SS_ONLY_THROW__;
2772  }
2773  }
2774 
2775  {
2776  __SS__ << "Invalid value string '" << valueString
2777  << "' - must start with a '/' character." << __E__;
2778  __SS_ONLY_THROW__;
2779  }
2780 } // 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
ConfigurationTree::BitMap getValueAsBitMap(void) const
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 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:681
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...
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::vector< std::vector< unsigned int > > getGroupRowsByPriority(const unsigned int groupIdCol, const std::string &groupID, bool onlyStatusTrue=false) const
Definition: TableView.cc:1503
T validateValueForColumn(const std::string &value, unsigned int col, bool doConvertEnvironmentVariables=true) const
< in included .icc source
std::string getEscapedValueAsString(unsigned int row, unsigned int col, bool convertEnvironmentVariables=true) const
Definition: TableView.cc:1018
unsigned int getColStatus(void) const
Definition: TableView.cc:1390
unsigned int getLinkGroupIDColumn(const std::string &childLinkIndex) const
Definition: TableView.cc:1820
bool getChildLink(const unsigned int &col, bool &isGroup, std::pair< unsigned int, unsigned int > &linkPair) const
Definition: TableView.cc:3454
std::string getValueAsString(unsigned int row, unsigned int col, bool convertEnvironmentVariables=true) const
Definition: TableView.cc:969
std::vector< unsigned int > getGroupRows(const unsigned int groupIdCol, const std::string &groupID, bool onlyStatusTrue=false, bool orderedByPriority=false) const
Definition: TableView.cc:1480
std::set< std::string > getSetOfGroupIDs(const std::string &childLinkIndex, unsigned int row=-1) const
Definition: TableView.cc:1719
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:1305
unsigned int findCol(const std::string &name) const
Definition: TableView.cc:1935
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 bool wildCardMatch(const std::string &needle, const std::string &haystack, unsigned int *priorityIndex=0)
Definition: StringMacros.cc:18
static std::string decodeURIComponent(const std::string &data)
static std::string stackTrace(void)