otsdaq  v2_03_00
ConfigurationTree.cc
1 #include "otsdaq-core/ConfigurationInterface/ConfigurationTree.h"
2 
3 #include <typeinfo>
4 
5 #include "otsdaq-core/ConfigurationInterface/ConfigurationManager.h"
6 #include "otsdaq-core/Macros/StringMacros.h"
7 #include "otsdaq-core/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 
18 //==============================================================================
19 ConfigurationTree::ConfigurationTree()
20  : configMgr_(0)
21  , table_(0)
22  , groupId_("")
23  , linkParentConfig_(0)
24  , linkColName_("")
25  , linkColValue_("")
26  , linkBackRow_(0)
27  , linkBackCol_(0)
28  , disconnectedTargetName_("")
29  , disconnectedLinkID_("")
30  , childLinkIndex_("")
31  , row_(0)
32  , col_(0)
33  , tableView_(0)
34 {
35  //__COUT__ << __E__;
36  //__COUT__ << "EMPTY CONSTRUCTOR ConfigManager: " << configMgr_ << " configuration: "
37  //<< table_ << __E__;
38 } //end empty constructor
39 
40 //==============================================================================
41 ConfigurationTree::ConfigurationTree(const ConfigurationManager* const& configMgr,
42  const TableBase* const& config)
43  : ConfigurationTree(configMgr,
44  config,
45  "" /*groupId_*/,
46  0 /*linkParentConfig_*/,
47  "" /*linkColName_*/,
48  "" /*linkColValue_*/,
49  TableView::INVALID /*linkBackRow_*/,
50  TableView::INVALID /*linkBackCol_*/,
51  "" /*disconnectedTargetName_*/,
52  "" /*disconnectedLinkID_*/,
53  "" /*childLinkIndex_*/,
54  TableView::INVALID /*row_*/,
55  TableView::INVALID /*col_*/)
56 {
57  //__COUT__ << __E__;
58  //__COUT__ << "SHORT CONTRUCTOR ConfigManager: " << configMgr_ << " configuration: "
59  //<< table_ << __E__;
60 } //end short constructor
61 
62 //==============================================================================
63 ConfigurationTree::ConfigurationTree(const ConfigurationManager* const& configMgr,
64  const TableBase* const& config,
65  const std::string& groupId,
66  const TableBase* const& linkParentConfig,
67  const std::string& linkColName,
68  const std::string& linkColValue,
69  const unsigned int linkBackRow,
70  const unsigned int linkBackCol,
71  const std::string& disconnectedTargetName,
72  const std::string& disconnectedLinkID,
73  const std::string& childLinkIndex,
74  const unsigned int row,
75  const unsigned int col)
76  : configMgr_(configMgr)
77  , table_(config)
78  , groupId_(groupId)
79  , linkParentConfig_(linkParentConfig)
80  , linkColName_(linkColName)
81  , linkColValue_(linkColValue)
82  , linkBackRow_(linkBackRow)
83  , linkBackCol_(linkBackCol)
84  , disconnectedTargetName_(disconnectedTargetName)
85  , disconnectedLinkID_(disconnectedLinkID)
86  , childLinkIndex_(childLinkIndex)
87  , row_(row)
88  , col_(col)
89  , tableView_(0)
90 {
91  //__COUT__ << __E__;
92  //__COUT__ << "FULL CONTRUCTOR ConfigManager: " << configMgr_ << " configuration: " <<
93  // table_ << __E__;
94  if(!configMgr_) // || !table_ || !tableView_)
95  {
96  __SS__ << "Invalid empty pointer given to tree!\n"
97  << "\n\tconfigMgr_=" << configMgr_ << "\n\tconfiguration_=" << table_
98  << "\n\tconfigView_=" << tableView_ << __E__;
99 
100  ss << nodeDump() << __E__;
101  __SS_THROW__;
102  }
103 
104  if(table_)
105  tableView_ = &(table_->getView());
106 
107  // verify UID column exists
108  if(tableView_ && tableView_->getColumnInfo(tableView_->getColUID()).getType() !=
109  TableViewColumnInfo::TYPE_UID)
110  {
111  __SS__ << "Missing UID column (must column of type "
112  << TableViewColumnInfo::TYPE_UID
113  << ") in config view : " << tableView_->getTableName() << __E__;
114 
115  ss << nodeDump() << __E__;
116  __SS_THROW__;
117  }
118 } //end full constructor
119 
120 //==============================================================================
121 // destructor
122 ConfigurationTree::~ConfigurationTree(void)
123 {
124  //__COUT__ << __E__;
125 } //end destructor
126 
127 //==============================================================================
128 // print
129 // print out tree from this node for desired depth
130 // depth of 0 means print out only this node's value
131 // depth of 1 means include this node's children's values, etc..
132 // depth of -1 means print full tree
133 void ConfigurationTree::print(const unsigned int& depth, std::ostream& out) const
134 {
135  recursivePrint(*this, depth, out, "\t");
136 } //end print()
137 
138 //==============================================================================
139 void ConfigurationTree::recursivePrint(const ConfigurationTree& t,
140  unsigned int depth,
141  std::ostream& out,
142  std::string space)
143 {
144  if(t.isValueNode())
145  out << space << t.getValueName() << " :\t" << t.getValueAsString() << __E__;
146  else
147  {
148  if(t.isLinkNode())
149  {
150  out << space << t.getValueName();
151  if(t.isDisconnected())
152  {
153  out << " :\t" << t.getValueAsString() << __E__;
154  return;
155  }
156  out << " (" << (t.isGroupLinkNode() ? "Group" : "U")
157  << "ID=" << t.getValueAsString() << ") : " << __E__;
158  }
159  else
160  out << space << t.getValueAsString() << " : " << __E__;
161 
162  // if depth>=1 print all children
163  // child.print(depth-1)
164  if(depth >= 1)
165  {
166  auto C = t.getChildren();
167  if(!C.empty())
168  out << space << "{" << __E__;
169  for(auto& c : C)
170  recursivePrint(c.second, depth - 1, out, space + " ");
171  if(!C.empty())
172  out << space << "}" << __E__;
173  }
174  }
175 } //end recursivePrint()
176 
177 //==============================================================================
178 std::string ConfigurationTree::handleValidateValueForColumn(
179  const TableView* configView,
180  std::string value,
181  unsigned int col,
183 {
184  if(!configView)
185  {
186  __SS__ << "Null configView" << __E__;
187 
188  ss << nodeDump() << __E__;
189  __SS_THROW__;
190  }
191  __COUT__ << "handleValidateValueForColumn<string>" << __E__;
192  return configView->validateValueForColumn(value, col);
193 } // end std::string handleValidateValueForColumn()
194 
195 //==============================================================================
196 // getValue (only std::string value)
197 // special version of getValue for string type
198 // Note: necessary because types of std::basic_string<char> cause compiler problems if no
199 // string specific function
200 void ConfigurationTree::getValue(std::string& value) const
201 {
202  //__COUT__ << row_ << " " << col_ << " p: " << tableView_<< __E__;
203 
204  if(row_ != TableView::INVALID &&
205  col_ != TableView::INVALID) // this node is a value node
206  {
207  // attempt to interpret the value as a tree node path itself
208  try
209  {
210  ConfigurationTree valueAsTreeNode = getValueAsTreeNode();
211  // valueAsTreeNode.getValue<T>(value);
212  __COUT__ << "Success following path to tree node!" << __E__;
213  // value has been interpreted as a tree node value
214  // now verify result under the rules of this column
215  // if(typeid(std::string) == typeid(value))
216 
217  // Note: want to interpret table value as though it is in column of different
218  // table this allows a number to be read as a string, for example, without
219  // exceptions
220  value = tableView_->validateValueForColumn(valueAsTreeNode.getValueAsString(),
221  col_);
222 
223  __COUT__ << "Successful value!" << __E__;
224 
225  // else
226  // value = tableView_->validateValueForColumn<T>(
227  // valueAsTreeNode.getValueAsString(),col_);
228 
229  return;
230  }
231  catch(...) // tree node path interpretation failed
232  {
233  //__COUT__ << "Invalid path, just returning normal value." << __E__;
234  }
235 
236  // else normal return
237  tableView_->getValue(value, row_, col_);
238  }
239  else if(row_ == TableView::INVALID &&
240  col_ == TableView::INVALID) // this node is config node maybe with groupId
241  {
242  if(isLinkNode() && isDisconnected())
243  value =
244  (groupId_ == "") ? getValueName() : groupId_; // a disconnected link
245  // still knows its table
246  // name or groupId
247  else
248  value = (groupId_ == "") ? table_->getTableName() : groupId_;
249  }
250  else if(row_ == TableView::INVALID)
251  {
252  __SS__ << "Malformed ConfigurationTree" << __E__;
253  __SS_THROW__;
254  }
255  else if(col_ == TableView::INVALID) // this node is uid node
256  tableView_->getValue(value, row_, tableView_->getColUID());
257  else
258  {
259  __SS__ << "Impossible." << __E__;
260  __SS_THROW__;
261  }
262 } //end getValue()
263 
264 //==============================================================================
265 // getValue
266 // Only std::string value will work.
267 // If this is a value node, and not type string, configView->getValue should
268 // throw exception.
269 //
270 // NOTE: getValueAsString() method should be preferred if getting the Link UID
271 // because when disconnected will return "X". getValue() would return the
272 // column name of the link when disconnected.
273 //
275 // Note: if called without template, necessary because types of std::basic_string<char>
276 // cause compiler problems if no string specific function
277 std::string ConfigurationTree::getValue() const
278 {
279  std::string value;
280  ConfigurationTree::getValue(value);
281  return value;
282 } //end getValue()
283 
284 //==============================================================================
285 // getValue (only ConfigurationTree::BitMap value)
286 // special version of getValue for string type
287 // Note: necessary because types of std::basic_string<char> cause compiler problems if no
288 // string specific function
289 void ConfigurationTree::getValueAsBitMap(ConfigurationTree::BitMap& bitmap) const
290 {
291  //__COUT__ << row_ << " " << col_ << " p: " << tableView_<< __E__;
292 
293  if(row_ != TableView::INVALID &&
294  col_ != TableView::INVALID) // this node is a value node
295  {
296  std::string bitmapString;
297  tableView_->getValue(bitmapString, row_, col_);
298 
299  __COUTV__(bitmapString);
300  if(bitmapString == TableViewColumnInfo::DATATYPE_STRING_DEFAULT)
301  {
302  bitmap.isDefault_ = true;
303  return;
304  }
305  else
306  bitmap.isDefault_ = false;
307 
308  // extract bit map
309  {
310  bitmap.bitmap_.clear();
311  int row = -1;
312  bool openRow = false;
313  unsigned int startInt = -1;
314  for(unsigned int i = 0; i < bitmapString.length(); i++)
315  {
316  __COUTV__(bitmapString[i]);
317  __COUTV__(row);
318  __COUTV__(openRow);
319  __COUTV__(startInt);
320  __COUTV__(i);
321 
322  if(!openRow) // need start of row
323  {
324  if(bitmapString[i] == '[')
325  { // open a new row
326  openRow = true;
327  ++row;
328  bitmap.bitmap_.push_back(std::vector<uint64_t>());
329  }
330  else if(bitmapString[i] == ']')
331  {
332  break; // ending bracket, done with string
333  }
334  else if(bitmapString[i] == ',') // end characters found not within
335  // row
336  {
337  __SS__
338  << "Too many ']' or ',' characters in bit map configuration"
339  << __E__;
340 
341  ss << nodeDump() << __E__;
342  __SS_ONLY_THROW__;
343  }
344  }
345  else if(startInt == (unsigned int)-1) // need to find start of number
346  {
347  if(bitmapString[i] == ']') // found end of row, instead of start of
348  // number, assume row ended
349  {
350  openRow = false;
351  }
352  else if(bitmapString[i] >= '0' &&
353  bitmapString[i] <= '9') // found start of number
354  {
355  startInt = i;
356  }
357  else if(bitmapString[i] == ',') // comma found without number
358  {
359  __SS__ << "Too many ',' characters in bit map configuration"
360  << __E__;
361 
362  ss << nodeDump() << __E__;
363  __SS_ONLY_THROW__;
364  }
365  }
366  else
367  {
368  // looking for end of number
369 
370  if(bitmapString[i] ==
371  ']') // found end of row, assume row and number ended
372  {
373  openRow = false;
374  bitmap.bitmap_[row].push_back(strtoul(
375  bitmapString.substr(startInt, i - startInt).c_str(), 0, 0));
376  startInt = -1;
377  }
378  else if(bitmapString[i] == ',') // comma found, assume end of number
379  {
380  bitmap.bitmap_[row].push_back(strtoul(
381  bitmapString.substr(startInt, i - startInt).c_str(), 0, 0));
382  startInt = -1;
383  }
384  }
385  }
386 
387  for(unsigned int r = 0; r < bitmap.bitmap_.size(); ++r)
388  {
389  for(unsigned int c = 0; c < bitmap.bitmap_[r].size(); ++c)
390  {
391  __COUT__ << r << "," << c << " = " << bitmap.bitmap_[r][c] << __E__;
392  }
393  __COUT__ << "================" << __E__;
394  }
395  }
396  }
397  else
398  {
399  __SS__ << "Requesting getValue must be on a value node." << __E__;
400 
401  ss << nodeDump() << __E__;
402  __SS_THROW__;
403  }
404 } //end getValueAsBitMap()
405 
406 //==============================================================================
407 // getValue
408 //
409 // special version of getValue for ConfigurationTree::BitMap type
410 ConfigurationTree::BitMap ConfigurationTree::getValueAsBitMap() const
411 {
413  ConfigurationTree::getValueAsBitMap(value);
414  return value;
415 } //end getValueAsBitMap()
416 
417 //==============================================================================
418 // getEscapedValue
419 // Only works if a value node, other exception thrown
420 std::string ConfigurationTree::getEscapedValue() const
421 {
422  if(row_ != TableView::INVALID &&
423  col_ != TableView::INVALID) // this node is a value node
424  return tableView_->getEscapedValueAsString(row_, col_);
425 
426  __SS__ << "Can not get escaped value except from a value node!"
427  << " This node is type '" << getNodeType() << "." << __E__;
428 
429  ss << nodeDump() << __E__;
430  __SS_THROW__;
431 } //end getEscapedValue()
432 
433 //==============================================================================
434 // getTableName
435 const std::string& ConfigurationTree::getTableName(void) const
436 {
437  if(!table_)
438  {
439  __SS__ << "Can not get configuration name of node with no configuration pointer! "
440  << "Is there a broken link? " << __E__;
441  if(linkParentConfig_)
442  {
443  ss << "Error occurred traversing from " << linkParentConfig_->getTableName()
444  << " row " << linkBackRow_ << " col '"
445  << linkParentConfig_->getView().getColumnInfo(linkBackCol_).getName()
446  << ".'" << __E__;
447  }
448 
449  __SS_ONLY_THROW__;
450  }
451  return table_->getTableName();
452 } //end getTableName()
453 
454 //==============================================================================
455 // getFieldTableName
456 // returns the configuration name for the node's field.
457 // Note: for link nodes versus value nodes this has different functionality than
458 // getTableName()
459 const std::string& ConfigurationTree::getFieldTableName(void) const
460 {
461  // if link node, need config name from parent
462  if(isLinkNode())
463  {
464  if(!linkParentConfig_)
465  {
466  __SS__ << "Can not get configuration name of link node field with no parent "
467  "configuration pointer!"
468  << __E__;
469  ss << nodeDump() << __E__;
470  __SS_ONLY_THROW__;
471  }
472  return linkParentConfig_->getTableName();
473  }
474  else
475  return getTableName();
476 } //end getFieldTableName()
477 
478 //==============================================================================
479 // getDisconnectedTableName
480 const std::string& ConfigurationTree::getDisconnectedTableName(void) const
481 {
482  if(isLinkNode() && isDisconnected())
483  return disconnectedTargetName_;
484 
485  __SS__ << "Can not get disconnected target name of node unless it is a disconnected "
486  "link node!"
487  << __E__;
488 
489  ss << nodeDump() << __E__;
490  __SS_ONLY_THROW__;
491 } //end getDisconnectedTableName()
492 
493 //==============================================================================
494 // getDisconnectedLinkID
495 const std::string& ConfigurationTree::getDisconnectedLinkID(void) const
496 {
497  if(isLinkNode() && isDisconnected())
498  return disconnectedLinkID_;
499 
500  __SS__ << "Can not get disconnected target name of node unless it is a disconnected "
501  "link node!"
502  << __E__;
503 
504  ss << nodeDump() << __E__;
505  __SS_ONLY_THROW__;
506 } //end getDisconnectedLinkID()
507 
508 //==============================================================================
509 // getTableVersion
510 const TableVersion& ConfigurationTree::getTableVersion(void) const
511 {
512  if(!tableView_)
513  {
514  __SS__ << "Can not get configuration version of node with no config view pointer!"
515  << __E__;
516 
517  ss << nodeDump() << __E__;
518  __SS_ONLY_THROW__;
519  }
520  return tableView_->getVersion();
521 } //end getTableVersion()
522 
523 //==============================================================================
524 // getTableCreationTime
525 const time_t& ConfigurationTree::getTableCreationTime(void) const
526 {
527  if(!tableView_)
528  {
529  __SS__ << "Can not get configuration creation time of node with no config view "
530  "pointer!"
531  << __E__;
532 
533  ss << nodeDump() << __E__;
534  __SS_ONLY_THROW__;
535  }
536  return tableView_->getCreationTime();
537 } //end getTableCreationTime()
538 
539 //==============================================================================
540 // getFixedChoices
541 // returns vector of default + data choices
542 // Used as choices for tree-view, for example.
543 std::vector<std::string> ConfigurationTree::getFixedChoices(void) const
544 {
545  if(getValueType() != TableViewColumnInfo::TYPE_FIXED_CHOICE_DATA &&
546  getValueType() != TableViewColumnInfo::TYPE_BITMAP_DATA && !isLinkNode())
547  {
548  __SS__ << "Can not get fixed choices of node with value type of '"
549  << getValueType() << ".' Node must be a link or a value node with type '"
550  << TableViewColumnInfo::TYPE_BITMAP_DATA << "' or '"
551  << TableViewColumnInfo::TYPE_FIXED_CHOICE_DATA << ".'" << __E__;
552 
553  ss << nodeDump() << __E__;
554  __SS_ONLY_THROW__;
555  }
556 
557  std::vector<std::string> retVec;
558 
559  if(isLinkNode())
560  {
561  if(!linkParentConfig_)
562  {
563  __SS__
564  << "Can not get fixed choices of node with no parent config view pointer!"
565  << __E__;
566 
567  ss << nodeDump() << __E__;
568  __SS_ONLY_THROW__;
569  }
570 
571  //__COUT__ << getChildLinkIndex() << __E__;
572  //__COUT__ << linkColName_ << __E__;
573 
574  // for links, col_ = -1, column c needs to change (to ChildLink column of pair)
575  // get column from parent config pointer
576 
577  const TableView* parentView = &(linkParentConfig_->getView());
578  int c = parentView->findCol(linkColName_);
579 
580  std::pair<unsigned int /*link col*/, unsigned int /*link id col*/> linkPair;
581  bool isGroupLink;
582  parentView->getChildLink(c, isGroupLink, linkPair);
583  c = linkPair.first;
584 
585  std::vector<std::string> choices = parentView->getColumnInfo(c).getDataChoices();
586  for(const auto& choice : choices)
587  retVec.push_back(choice);
588 
589  return retVec;
590  }
591 
592  if(!tableView_)
593  {
594  __SS__ << "Can not get fixed choices of node with no config view pointer!"
595  << __E__;
596 
597  ss << nodeDump() << __E__;
598  __SS_ONLY_THROW__;
599  }
600 
601  // return vector of default + data choices
602  retVec.push_back(tableView_->getColumnInfo(col_).getDefaultValue());
603  std::vector<std::string> choices = tableView_->getColumnInfo(col_).getDataChoices();
604  for(const auto& choice : choices)
605  retVec.push_back(choice);
606 
607  return retVec;
608 } //end getFixedChoices()
609 
610 //==============================================================================
611 // getValueAsString
612 // NOTE: getValueAsString() method should be preferred if getting the Link UID
613 // because when disconnected will return "X". getValue() would return the
614 // column name of the link when disconnected.
615 //
616 // returnLinkTableValue returns the value in the source table as though link was
617 // not followed to destination table.
618 const std::string& ConfigurationTree::getValueAsString(bool returnLinkTableValue) const
619 {
620  if(isLinkNode())
621  {
622  if(returnLinkTableValue)
623  return linkColValue_;
624  else if(isDisconnected())
625  return ConfigurationTree::DISCONNECTED_VALUE;
626  else if(row_ == TableView::INVALID &&
627  col_ == TableView::INVALID) // this link is groupId node
628  return (groupId_ == "") ? table_->getTableName() : groupId_;
629  else if(col_ == TableView::INVALID) // this link is uid node
630  return tableView_->getDataView()[row_][tableView_->getColUID()];
631  else
632  {
633  __SS__ << "Impossible Link." << __E__;
634 
635  ss << nodeDump() << __E__;
636  __SS_THROW__;
637  }
638  }
639  else if(row_ != TableView::INVALID &&
640  col_ != TableView::INVALID) // this node is a value node
641  return tableView_->getDataView()[row_][col_];
642  else if(row_ == TableView::INVALID &&
643  col_ == TableView::INVALID) // this node is config node maybe with groupId
644  return (groupId_ == "") ? table_->getTableName() : groupId_;
645  else if(row_ == TableView::INVALID)
646  {
647  __SS__ << "Malformed ConfigurationTree" << __E__;
648 
649  ss << nodeDump() << __E__;
650  __SS_THROW__;
651  }
652  else if(col_ == TableView::INVALID) // this node is uid node
653  return tableView_->getDataView()[row_][tableView_->getColUID()];
654  else
655  {
656  __SS__ << "Impossible." << __E__;
657 
658  ss << nodeDump() << __E__;
659  __SS_THROW__;
660  }
661 } //end getValueAsString()
662 
663 //==============================================================================
664 // getUIDAsString
665 // returns UID associated with current value node or UID-Link node
666 //
667 const std::string& ConfigurationTree::getUIDAsString(void) const
668 {
669  if(isValueNode() || isUIDLinkNode())
670  return tableView_->getDataView()[row_][tableView_->getColUID()];
671 
672  {
673  __SS__ << "Can not get UID of node with type '" << getNodeType()
674  << ".' Node type must be '" << ConfigurationTree::NODE_TYPE_VALUE
675  << "' or '" << ConfigurationTree::NODE_TYPE_UID_LINK << ".'" << __E__;
676 
677  ss << nodeDump() << __E__;
678  __SS_ONLY_THROW__;
679  }
680 } //end getUIDAsString()
681 
682 //==============================================================================
683 // getValueDataType
684 // e.g. used to determine if node is type NUMBER
685 const std::string& ConfigurationTree::getValueDataType(void) const
686 {
687  if(isValueNode())
688  return tableView_->getColumnInfo(col_).getDataType();
689  else // must be std::string
690  return TableViewColumnInfo::DATATYPE_STRING;
691 } //end getValueDataType()
692 
693 //==============================================================================
694 // isDefaultValue
695 // returns true if is a value node and value is the default for the type
696 bool ConfigurationTree::isDefaultValue(void) const
697 {
698  if(!isValueNode())
699  return false;
700 
701  if(getValueDataType() == TableViewColumnInfo::DATATYPE_STRING)
702  {
703  if(getValueType() == TableViewColumnInfo::TYPE_ON_OFF ||
704  getValueType() == TableViewColumnInfo::TYPE_TRUE_FALSE ||
705  getValueType() == TableViewColumnInfo::TYPE_YES_NO)
706  return getValueAsString() ==
707  TableViewColumnInfo::DATATYPE_BOOL_DEFAULT; // default to OFF, NO,
708  // FALSE
709  else if(getValueType() == TableViewColumnInfo::TYPE_COMMENT)
710  return getValueAsString() == TableViewColumnInfo::DATATYPE_COMMENT_DEFAULT ||
711  getValueAsString() ==
712  ""; // in case people delete default comment, allow blank also
713  else
714  return getValueAsString() == TableViewColumnInfo::DATATYPE_STRING_DEFAULT;
715  }
716  else if(getValueDataType() == TableViewColumnInfo::DATATYPE_NUMBER)
717  return getValueAsString() == TableViewColumnInfo::DATATYPE_NUMBER_DEFAULT;
718  else if(getValueDataType() == TableViewColumnInfo::DATATYPE_TIME)
719  return getValueAsString() == TableViewColumnInfo::DATATYPE_TIME_DEFAULT;
720  else
721  return false;
722 } //end isDefaultValue()
723 
724 //==============================================================================
725 // getValueType
726 // e.g. used to determine if node is type TYPE_DATA, TYPE_ON_OFF, etc.
727 const std::string& ConfigurationTree::getValueType(void) const
728 {
729  if(isValueNode())
730  return tableView_->getColumnInfo(col_).getType();
731  else if(isLinkNode() && isDisconnected())
732  return ConfigurationTree::VALUE_TYPE_DISCONNECTED;
733  else // just call all non-value nodes data
734  return ConfigurationTree::VALUE_TYPE_NODE;
735 } //end getValueType()
736 
737 //==============================================================================
738 // getColumnInfo
739 // only sensible for value node
740 const TableViewColumnInfo& ConfigurationTree::getColumnInfo(void) const
741 {
742  if(isValueNode())
743  return tableView_->getColumnInfo(col_);
744  else
745  {
746  __SS__ << "Can only get column info from a value node! "
747  << "The node type is " << getNodeType() << __E__;
748 
749  ss << nodeDump() << __E__;
750  __SS_THROW__;
751  }
752 } //end getColumnInfo()
753 
754 //==============================================================================
755 // getRow
756 const unsigned int& ConfigurationTree::getRow(void) const { return row_; }
757 
758 //==============================================================================
759 // getColumn
760 const unsigned int& ConfigurationTree::getColumn(void) const { return col_; }
761 
762 //==============================================================================
763 // getFieldRow
764 // return field's row (different handling for value vs. link node)
765 const unsigned int& ConfigurationTree::getFieldRow(void) const
766 {
767  if(isLinkNode())
768  {
769  // for links, need to use parent info to determine
770  return linkBackRow_;
771  }
772  else
773  return row_;
774 } //end getFieldRow()
775 
776 //==============================================================================
777 // getFieldColumn
778 // return field's column (different handling for value vs. link node)
779 const unsigned int& ConfigurationTree::getFieldColumn(void) const
780 {
781  if(isLinkNode())
782  {
783  // for links, need to use parent info to determine
784  return linkBackCol_;
785  }
786  else
787  return col_;
788 } //end getFieldColumn()
789 
790 //==============================================================================
791 // getChildLinkIndex
792 const std::string& ConfigurationTree::getChildLinkIndex(void) const
793 {
794  if(!isLinkNode())
795  {
796  __SS__ << "Can only get link ID from a link! "
797  << "The node type is " << getNodeType() << __E__;
798 
799  ss << nodeDump() << __E__;
800  __SS_THROW__;
801  }
802  return childLinkIndex_;
803 } //end getChildLinkIndex()
804 
805 //==============================================================================
806 // getValueName
807 // e.g. used to determine column name of value node
808 const std::string& ConfigurationTree::getValueName(void) const
809 {
810  if(isValueNode())
811  return tableView_->getColumnInfo(col_).getName();
812  else if(isLinkNode())
813  return linkColName_;
814  else
815  {
816  __SS__ << "Can only get value name of a value node!" << __E__;
817 
818  ss << nodeDump() << __E__;
819  __SS_THROW__;
820  }
821 } //end getValueName()
822 
823 //==============================================================================
824 // recurse
825 // Used by ConfigurationTree to handle / syntax of getNode
826 ConfigurationTree ConfigurationTree::recurse(const ConfigurationTree& tree,
827  const std::string& childPath,
828  bool doNotThrowOnBrokenUIDLinks,
829  const std::string& originalNodeString)
830 {
831  //__COUT__ << tree.row_ << " " << tree.col_ << __E__;
832  //__COUT__ << "childPath=" << childPath << " " << childPath.length() << __E__;
833  if(childPath.length() <= 1) // only "/" or ""
834  return tree;
835  return tree.recursiveGetNode(
836  childPath, doNotThrowOnBrokenUIDLinks, originalNodeString);
837 } //end recurse()
838 
849 // std::string ConfigurationTree::getRecordFieldValueAsString(std::string fieldName) const
850 //{
851 // //enforce that starting point is a table node
852 // if(!isUIDNode())
853 // {
854 // __SS__ << "Can only get getRecordFieldValueAsString from a uid node! " <<
855 // "The node type is " << getNodeType() << __E__;
856 // __COUT__ << "\n" << ss.str() << __E__;
857 // __SS_THROW__;
858 // }
859 //
860 // unsigned int c = tableView_->findCol(fieldName);
861 // return tableView_->getDataView()[row_][c];
862 //}
863 
864 //==============================================================================
865 // getNode
866 // Connected to recursiveGetNode()
867 //
868 // nodeString can be a multi-part path using / delimiter
869 // use:
870 // getNode(/uid/col) or getNode(uid)->getNode(col)
871 //
872 // if doNotThrowOnBrokenUIDLinks
873 // then catch exceptions on UID links and call disconnected
874 ConfigurationTree ConfigurationTree::getNode(const std::string& nodeString,
875  bool doNotThrowOnBrokenUIDLinks) const
876 {
877  return recursiveGetNode(
878  nodeString, doNotThrowOnBrokenUIDLinks, "" /*originalNodeString*/);
879 } //end getNode() connected to recursiveGetNode()
880 ConfigurationTree ConfigurationTree::recursiveGetNode(
881  const std::string& nodeString,
882  bool doNotThrowOnBrokenUIDLinks,
883  const std::string& originalNodeString) const
884 {
885  //__COUT__ << "nodeString=" << nodeString << " " << nodeString.length() << __E__;
886  //__COUT__ << "doNotThrowOnBrokenUIDLinks=" << doNotThrowOnBrokenUIDLinks <<
887  // __E__;
888 
889  // get nodeName (in case of / syntax)
890  if(nodeString.length() < 1)
891  {
892  __SS__ << "Invalid empty node name! Looking for child node from node '"
893  << getValue() << "'..." << __E__;
894 
895  ss << nodeDump() << __E__;
896  __SS_THROW__;
897  }
898 
899  bool startingSlash = nodeString[0] == '/';
900 
901  std::string nodeName = nodeString.substr(
902  startingSlash ? 1 : 0, nodeString.find('/', 1) - (startingSlash ? 1 : 0));
903  //__COUT__ << "nodeName=" << nodeName << " " << nodeName.length() << __E__;
904 
905  std::string childPath =
906  nodeString.substr(nodeName.length() + (startingSlash ? 1 : 0));
907  //__COUT__ << "childPath=" << childPath << " " << childPath.length() << __E__;
908 
909  // if this tree is beginning at a configuration.. then go to uid, and vice versa
910 
911  try
912  {
913  //__COUT__ << row_ << " " << col_ << " " << groupId_ << " " << tableView_ <<
914  // __E__;
915  if(!table_)
916  {
917  // root node
918  // so return config node
919  return recurse(configMgr_->getNode(nodeName),
920  childPath,
921  doNotThrowOnBrokenUIDLinks,
922  originalNodeString);
923  }
924  else if(row_ == TableView::INVALID && col_ == TableView::INVALID)
925  {
926  // config node
927 
928  if(!tableView_)
929  {
930  __SS__ << "Missing configView pointer! Likely attempting to access a "
931  "child node through a disconnected link node."
932  << __E__;
933 
934  ss << nodeDump() << __E__;
935  __SS_THROW__;
936  }
937 
938  // this node is config node, so return uid node considering groupid
939  return recurse(
941  configMgr_,
942  table_,
943  "", // no new groupId string, not a link
944  0 /*linkParentConfig_*/,
945  "", // link node name, not a link
946  "", // link node value, not a link
947  TableView::INVALID /*linkBackRow_*/,
948  TableView::INVALID /*linkBackCol_*/,
949  "", // ignored disconnected target name, not a link
950  "", // ignored disconnected link id, not a link
951  "",
952  // if this node is group config node, consider that when getting rows
953  (groupId_ == "")
954  ? tableView_->findRow(tableView_->getColUID(), nodeName)
955  : tableView_->findRowInGroup(tableView_->getColUID(),
956  nodeName,
957  groupId_,
958  childLinkIndex_)),
959  childPath,
960  doNotThrowOnBrokenUIDLinks,
961  originalNodeString);
962  }
963  else if(row_ == TableView::INVALID)
964  {
965  __SS__ << "Malformed ConfigurationTree" << __E__;
966 
967  ss << nodeDump() << __E__;
968  __SS_THROW__;
969  }
970  else if(col_ == TableView::INVALID)
971  {
972  // this node is uid node, so return link, group link, disconnected, or value
973  // node
974 
975  //__COUT__ << "nodeName=" << nodeName << " " << nodeName.length() <<
976  // __E__;
977 
978  // if the value is a unique link ..
979  // return a uid node!
980  // if the value is a group link
981  // return a config node with group string
982  // else.. return value node
983 
984  if(!tableView_)
985  {
986  __SS__ << "Missing configView pointer! Likely attempting to access a "
987  "child node through a disconnected link node."
988  << __E__;
989 
990  ss << nodeDump() << __E__;
991  __SS_THROW__;
992  }
993 
994  unsigned int c = tableView_->findCol(nodeName);
995  std::pair<unsigned int /*link col*/, unsigned int /*link id col*/> linkPair;
996  bool isGroupLink, isLink;
997  if((isLink = tableView_->getChildLink(c, isGroupLink, linkPair)) &&
998  !isGroupLink)
999  {
1000  //__COUT__ << "nodeName=" << nodeName << " " << nodeName.length() <<
1001  // __E__; is a unique link, return uid node in new configuration
1002  // need new configuration pointer
1003  // and row of linkUID in new configuration
1004 
1005  const TableBase* childConfig;
1006  try
1007  {
1008  childConfig = configMgr_->getTableByName(
1009  tableView_->getDataView()[row_][linkPair.first]);
1010  childConfig->getView(); // get view as a test for an active view
1011 
1012  if(doNotThrowOnBrokenUIDLinks) // try a test of getting row
1013  {
1014  childConfig->getView().findRow(
1015  childConfig->getView().getColUID(),
1016  tableView_->getDataView()[row_][linkPair.second]);
1017  }
1018  }
1019  catch(...)
1020  {
1021  // __COUT_WARN__ << "Found disconnected node! (" <<
1022  // nodeName
1023  //<<
1024  // ":" <<
1025  // tableView_->getDataView()[row_][linkPair.first]
1026  //<< ")" << " at entry with UID " <<
1027  // tableView_->getDataView()[row_][tableView_->getColUID()]
1028  //<< __E__; do not recurse further
1029  return ConfigurationTree(
1030  configMgr_,
1031  0,
1032  "",
1033  table_, // linkParentConfig_
1034  nodeName,
1035  tableView_->getDataView()[row_][c], // this the link node field
1036  // associated value (matches
1037  // targeted column)
1038  row_ /*linkBackRow_*/,
1039  c /*linkBackCol_*/,
1040  tableView_->getDataView()[row_][linkPair.first], // give
1041  // disconnected
1042  // target name
1043  tableView_->getDataView()[row_][linkPair.second], // give
1044  // disconnected
1045  // link ID
1046  tableView_->getColumnInfo(c).getChildLinkIndex());
1047  }
1048 
1049  return recurse(
1050  ConfigurationTree( // this is a link node
1051  configMgr_,
1052  childConfig,
1053  "", // no new groupId string
1054  table_, // linkParentConfig_
1055  nodeName, // this is a link node
1056  tableView_->getDataView()[row_][c], // this the link node field
1057  // associated value (matches
1058  // targeted column)
1059  row_ /*linkBackRow_*/,
1060  c /*linkBackCol_*/,
1061  "", // ignore since is connected
1062  "", // ignore since is connected
1063  tableView_->getColumnInfo(c).getChildLinkIndex(),
1064  childConfig->getView().findRow(
1065  childConfig->getView().getColUID(),
1066  tableView_->getDataView()[row_][linkPair.second])),
1067  childPath,
1068  doNotThrowOnBrokenUIDLinks,
1069  originalNodeString);
1070  }
1071  else if(isLink)
1072  {
1073  //__COUT__ << "nodeName=" << nodeName << " " << nodeName.length() <<
1074  // __E__; is a group link, return new configuration with group string
1075  // need new configuration pointer
1076  // and group string
1077 
1078  const TableBase* childConfig;
1079  try
1080  {
1081  childConfig = configMgr_->getTableByName(
1082  tableView_->getDataView()[row_][linkPair.first]);
1083  childConfig->getView(); // get view as a test for an active view
1084  }
1085  catch(...)
1086  {
1087  if(tableView_->getDataView()[row_][linkPair.first] !=
1088  TableViewColumnInfo::DATATYPE_LINK_DEFAULT)
1089  __COUT_WARN__
1090  << "Found disconnected node! Failed link target "
1091  "from nodeName="
1092  << nodeName << " to table:id="
1093  << tableView_->getDataView()[row_][linkPair.first] << ":"
1094  << tableView_->getDataView()[row_][linkPair.second] << __E__;
1095 
1096  // do not recurse further
1097  return ConfigurationTree(
1098  configMgr_,
1099  0,
1100  tableView_->getDataView()[row_][linkPair.second], // groupID
1101  table_, // linkParentConfig_
1102  nodeName,
1103  tableView_->getDataView()[row_][c], // this the link node field
1104  // associated value (matches
1105  // targeted column)
1106  row_ /*linkBackRow_*/,
1107  c /*linkBackCol_*/,
1108  tableView_->getDataView()[row_][linkPair.first], // give
1109  // disconnected
1110  // target name
1111  tableView_->getDataView()[row_][linkPair.second], // give
1112  // disconnected
1113  // target name
1114  tableView_->getColumnInfo(c).getChildLinkIndex());
1115  }
1116 
1117  return recurse(
1118  ConfigurationTree( // this is a link node
1119  configMgr_,
1120  childConfig,
1121  tableView_
1122  ->getDataView()[row_][linkPair.second], // groupId string
1123  table_, // linkParentConfig_
1124  nodeName, // this is a link node
1125  tableView_->getDataView()[row_][c], // this the link node field
1126  // associated value (matches
1127  // targeted column)
1128  row_ /*linkBackRow_*/,
1129  c /*linkBackCol_*/,
1130  "", // ignore since is connected
1131  "", // ignore since is connected
1132  tableView_->getColumnInfo(c).getChildLinkIndex()),
1133  childPath,
1134  doNotThrowOnBrokenUIDLinks,
1135  originalNodeString);
1136  }
1137  else
1138  {
1139  //__COUT__ << "nodeName=" << nodeName << " " << nodeName.length() <<
1140  // __E__; return value node
1141  return ConfigurationTree(configMgr_,
1142  table_,
1143  "",
1144  0 /*linkParentConfig_*/,
1145  "",
1146  "",
1147  TableView::INVALID /*linkBackRow_*/,
1148  TableView::INVALID /*linkBackCol_*/,
1149  "",
1150  "" /*disconnectedLinkID*/,
1151  "",
1152  row_,
1153  c);
1154  }
1155  }
1156  }
1157  catch(std::runtime_error& e)
1158  {
1159  __SS__ << "\n\nError occurred descending from node '" << getValue()
1160  << "' in table '" << getTableName() << "' looking for child '" << nodeName
1161  << "'\n\n"
1162  << __E__;
1163  ss << "The original node search string was '" << originalNodeString << ".'"
1164  << __E__;
1165  ss << "--- Additional error detail: \n\n" << e.what() << __E__;
1166 
1167  ss << nodeDump() << __E__;
1168  __SS_ONLY_THROW__;
1169  }
1170  catch(...)
1171  {
1172  __SS__ << "\n\nError occurred descending from node '" << getValue()
1173  << "' in table '" << getTableName() << "' looking for child '" << nodeName
1174  << "'\n\n"
1175  << __E__;
1176  ss << "The original node search string was '" << originalNodeString << ".'"
1177  << __E__;
1178 
1179  ss << nodeDump() << __E__;
1180  __SS_ONLY_THROW__;
1181  }
1182 
1183  // this node is value node, so has no node to choose from
1184  __SS__
1185  << "\n\nError occurred descending from node '" << getValue() << "' in table '"
1186  << getTableName() << "' looking for child '" << nodeName << "'\n\n"
1187  << "Invalid depth! getNode() called from a value point in the Configuration Tree."
1188  << __E__;
1189  ss << "The original node search string was '" << originalNodeString << ".'" << __E__;
1190 
1191  ss << nodeDump() << __E__;
1192  __SS_ONLY_THROW__; // this node is value node, cant go any deeper!
1193 } //end recursiveGetNode()
1194 
1195 //==============================================================================
1196 // nodeDump
1197 // Useful for debugging a node failure, like when throwing an exception
1198 std::string ConfigurationTree::nodeDump(void) const
1199 {
1200  __SS__ << "ConfigurationTree::nodeDump() "
1201  "=====================================\nConfigurationTree::nodeDump():"
1202  << __E__;
1203 
1204  // try each level of debug.. and ignore errors
1205  try
1206  {
1207  ss << "\t"
1208  << "Error occurred from node '" << getValueAsString() << "'..." << __E__;
1209  }
1210  catch(...)
1211  {
1212  } // ignore errors
1213  try
1214  {
1215  ss << "\t"
1216  << "Error occurred from node '" << getValue() << "' in table '"
1217  << getTableName() << ".'" << __E__;
1218  }
1219  catch(...)
1220  {
1221  } // ignore errors
1222  try
1223  {
1224  auto children = getChildrenNames();
1225  ss << "\t"
1226  << "Here is the list of possible children (count = " << children.size()
1227  << "):" << __E__;
1228  for(auto& child : children)
1229  ss << "\t\t" << child << __E__;
1230  if(tableView_)
1231  {
1232  ss << "\n\nHere is the culprit table printout:\n\n";
1233  tableView_->print(ss);
1234  }
1235  }
1236  catch(...)
1237  {
1238  } // ignore errors trying to show children
1239 
1240  try
1241  {
1242  ss << "\n\n" << StringMacros::stackTrace() << __E__;
1243  }
1244  catch(...)
1245  {
1246  } // ignore errors
1247 
1248  ss << "end ConfigurationTree::nodeDump() ====================================="
1249  << __E__;
1250 
1251  return ss.str();
1252 } //end nodeDump()
1253 
1254 //==============================================================================
1255 ConfigurationTree ConfigurationTree::getBackNode(std::string nodeName,
1256  unsigned int backSteps) const
1257 {
1258  for(unsigned int i = 0; i < backSteps; i++)
1259  nodeName = nodeName.substr(0, nodeName.find_last_of('/'));
1260 
1261  return getNode(nodeName);
1262 } //end getBackNode()
1263 
1264 //==============================================================================
1265 ConfigurationTree ConfigurationTree::getForwardNode(std::string nodeName,
1266  unsigned int forwardSteps) const
1267 {
1268  unsigned int s = 0;
1269 
1270  // skip all leading /'s
1271  while(s < nodeName.length() && nodeName[s] == '/')
1272  ++s;
1273 
1274  for(unsigned int i = 0; i < forwardSteps; i++)
1275  s = nodeName.find('/', s) + 1;
1276 
1277  return getNode(nodeName.substr(0, s));
1278 } //end getForwardNode()
1279 
1280 //==============================================================================
1281 // isValueNode
1282 // if true, then this is a leaf node, i.e. there can be no children, only a value
1283 bool ConfigurationTree::isValueNode(void) const
1284 {
1285  return (row_ != TableView::INVALID && col_ != TableView::INVALID);
1286 } //end isValueNode()
1287 
1288 //==============================================================================
1289 // isValueBoolType
1290 // if true, then this is a leaf node with BOOL type
1291 bool ConfigurationTree::isValueBoolType(void) const
1292 {
1293  return isValueNode() && tableView_->getColumnInfo(col_).isBoolType();
1294 } // end isValueBoolType()
1295 
1296 //==============================================================================
1297 // isValueNumberDataType
1298 // if true, then this is a leaf node with NUMBER data type
1299 bool ConfigurationTree::isValueNumberDataType(void) const
1300 {
1301  return isValueNode() && tableView_->getColumnInfo(col_).isNumberDataType();
1302 } // end isValueBoolType()
1303 
1304 //==============================================================================
1305 // isDisconnected
1306 // if true, then this is a disconnected node, i.e. there is a configuration link missing
1307 // (this is possible when the configuration is loaded in stages and the complete tree
1308 // may not be available, yet)
1309 bool ConfigurationTree::isDisconnected(void) const
1310 {
1311  if(!isLinkNode())
1312  {
1313  __SS__ << "\n\nError occurred testing link connection at node with value '"
1314  << getValue() << "' in table '" << getTableName() << "'\n\n"
1315  << __E__;
1316  ss << "This is not a Link node! It is node type '" << getNodeType()
1317  << ".' Only a Link node can be disconnected." << __E__;
1318 
1319  ss << nodeDump() << __E__;
1320  __SS_ONLY_THROW__;
1321  }
1322 
1323  return !table_ || !tableView_;
1324 } //end isDisconnected()
1325 
1326 //==============================================================================
1327 // isLinkNode
1328 // if true, then this is a link node
1329 bool ConfigurationTree::isLinkNode(void) const { return linkColName_ != ""; }
1330 
1331 //==============================================================================
1332 // getNodeType
1333 // return node type as string
1334 const std::string ConfigurationTree::NODE_TYPE_GROUP_TABLE = "GroupTableNode";
1335 const std::string ConfigurationTree::NODE_TYPE_TABLE = "TableNode";
1336 const std::string ConfigurationTree::NODE_TYPE_GROUP_LINK = "GroupLinkNode";
1337 const std::string ConfigurationTree::NODE_TYPE_UID_LINK = "UIDLinkNode";
1338 const std::string ConfigurationTree::NODE_TYPE_VALUE = "ValueNode";
1339 const std::string ConfigurationTree::NODE_TYPE_UID = "UIDNode";
1340 const std::string ConfigurationTree::NODE_TYPE_ROOT = "RootNode";
1341 
1342 std::string ConfigurationTree::getNodeType(void) const
1343 {
1344  if(!table_)
1345  return ConfigurationTree::NODE_TYPE_ROOT;
1346  if(isConfigurationNode() && groupId_ != "")
1347  return ConfigurationTree::NODE_TYPE_GROUP_TABLE;
1348  if(isConfigurationNode())
1349  return ConfigurationTree::NODE_TYPE_TABLE;
1350  if(isGroupLinkNode())
1351  return ConfigurationTree::NODE_TYPE_GROUP_LINK;
1352  if(isLinkNode())
1353  return ConfigurationTree::NODE_TYPE_UID_LINK;
1354  if(isValueNode())
1355  return ConfigurationTree::NODE_TYPE_VALUE;
1356  return ConfigurationTree::NODE_TYPE_UID;
1357 } //end getNodeType()
1358 
1359 //==============================================================================
1360 // isGroupLinkNode
1361 // if true, then this is a group link node
1362 bool ConfigurationTree::isGroupLinkNode(void) const
1363 {
1364  return (isLinkNode() && groupId_ != "");
1365 }
1366 
1367 //==============================================================================
1368 // isUIDLinkNode
1369 // if true, then this is a uid link node
1370 bool ConfigurationTree::isUIDLinkNode(void) const
1371 {
1372  return (isLinkNode() && groupId_ == "");
1373 }
1374 
1375 //==============================================================================
1376 // isUIDNode
1377 // if true, then this is a uid node
1378 bool ConfigurationTree::isUIDNode(void) const
1379 {
1380  return (row_ != TableView::INVALID && col_ == TableView::INVALID);
1381 }
1382 
1383 //==============================================================================
1384 // getCommonFields
1385 // wrapper for ...recursiveGetCommonFields
1386 //
1387 // returns common fields in order encountered
1388 // including through UID links depending on depth specified
1389 //
1390 // Field := {Table, UID, Column Name, Relative Path, TableViewColumnInfo}
1391 //
1392 // if fieldAcceptList or fieldRejectList are not empty,
1393 // then reject any that are not in field accept filter list
1394 // and reject any that are in field reject filter list
1395 //
1396 // will only go to specified depth looking for fields
1397 // (careful to prevent infinite loops in tree navigation)
1398 //
1399 std::vector<ConfigurationTree::RecordField> ConfigurationTree::getCommonFields(
1400  const std::vector<std::string /*uid*/>& recordList,
1401  const std::vector<std::string /*relative-path*/>& fieldAcceptList,
1402  const std::vector<std::string /*relative-path*/>& fieldRejectList,
1403  unsigned int depth) const
1404 {
1405  // enforce that starting point is a table node
1406  if(!isRootNode() && !isConfigurationNode())
1407  {
1408  __SS__ << "Can only get getCommonFields from a root or table node! "
1409  << "The node type is " << getNodeType() << __E__;
1410 
1411  ss << nodeDump() << __E__;
1412  __SS_THROW__;
1413  }
1414 
1415  std::vector<ConfigurationTree::RecordField> fieldCandidateList;
1416  std::vector<int> fieldCount; //-1 := guaranteed, else count must match num of records
1417 
1418  --depth; // decrement for recursion
1419 
1420  // for each record in <record list>
1421  // loop through all record's children
1422  // if isValueNode (value nodes are possible field candidates!)
1423  // if first uid record
1424  // add field to <field candidates list> if in <field filter list>
1425  // mark <field count> as guaranteed -1 (all these fields must be common
1426  // for UIDs in same table)
1427  // else not first uid record, do not need to check, must be same as first
1428  // record! else if depth > 0 and UID-Link Node recursively (call
1429  // recursiveGetCommonFields())
1430  // =====================
1431  // Start recursiveGetCommonFields()
1432  // --depth;
1433  // loop through all children
1434  // if isValueNode (value nodes are possible field candidates!)
1435  // if first uid record
1436  // add field to <field candidates list> if in <field
1437  // filter list> initial mark <field count> as 1
1438  // else
1439  // if field is in <field candidates list>,
1440  // increment <field count> for field candidate
1441  // else if field is not in list, ignore field
1442  // else if depth > 0 and is UID-Link
1443  // if Link Table/UID pair is not found in <field candidates
1444  // list> (avoid endless loops through tree)
1445  // recursiveGetCommonFields()
1446  // =====================
1447  //
1448  //
1449  // loop through all field candidates
1450  // remove those with <field count> != num of records
1451  //
1452  //
1453  // return result
1454 
1455  bool found; // used in loops
1456  // auto tableName = isRootNode()?"/":getTableName(); //all records will share this
1457  // table name
1458 
1459  for(unsigned int i = 0; i < recordList.size(); ++i)
1460  {
1461  //__COUT__ << "Checking " << recordList[i] << __E__;
1462 
1463  auto recordChildren = getNode(recordList[i]).getChildren();
1464  for(const auto& fieldNode : recordChildren)
1465  {
1466  // __COUT__ << "All... " << fieldNode.second.getNodeType() <<
1467  // " -- " << fieldNode.first << __E__;
1468  if(fieldNode.second.isValueNode())
1469  {
1470  // skip author and record insertion time
1471  if(fieldNode.second.getColumnInfo().getType() ==
1472  TableViewColumnInfo::TYPE_AUTHOR ||
1473  fieldNode.second.getColumnInfo().getType() ==
1474  TableViewColumnInfo::TYPE_TIMESTAMP)
1475  continue;
1476 
1477  //__COUT__ << "isValueNode " << fieldNode.first << __E__;
1478  if(!i) // first uid record
1479  {
1480  // check field accept filter list
1481  found = fieldAcceptList.size() ? false
1482  : true; // accept if no filter list
1483  for(const auto& fieldFilter : fieldAcceptList)
1484  if(StringMacros::wildCardMatch(fieldFilter, fieldNode.first))
1485  {
1486  found = true;
1487  break;
1488  }
1489 
1490  if(found)
1491  {
1492  // check field reject filter list
1493 
1494  found = true; // accept if no filter list
1495  for(const auto& fieldFilter : fieldRejectList)
1496  if(StringMacros::wildCardMatch(fieldFilter, fieldNode.first))
1497  {
1498  found = false; // reject if match
1499  break;
1500  }
1501  }
1502 
1503  // if found, guaranteed field (all these fields must be common for
1504  // UIDs in same table)
1505  if(found)
1506  {
1507  fieldCandidateList.push_back(ConfigurationTree::RecordField(
1508  fieldNode.second.getTableName(),
1509  recordList[i],
1510  fieldNode.first,
1511  "", // relative path, not including columnName_
1512  &fieldNode.second.getColumnInfo()));
1513  fieldCount.push_back(-1); // mark guaranteed field
1514  }
1515  }
1516  // else // not first uid record, do not need to check, must be same as
1517  // first record!
1518  }
1519  else if(depth > 0 && fieldNode.second.isUIDLinkNode() &&
1520  !fieldNode.second.isDisconnected())
1521  {
1522  //__COUT__ << "isUIDLinkNode " << fieldNode.first << __E__;
1523  fieldNode.second.recursiveGetCommonFields(
1524  fieldCandidateList,
1525  fieldCount,
1526  fieldAcceptList,
1527  fieldRejectList,
1528  depth,
1529  fieldNode.first + "/", // relativePathBase
1530  !i // launch inFirstRecord (or not) depth search
1531  );
1532  }
1533  }
1534  }
1535 
1536  //__COUT__ << "======================= check for count = " <<
1537  // (int)recordList.size() << __E__;
1538 
1539  // loop through all field candidates
1540  // remove those with <field count> != num of records
1541  for(unsigned int i = 0; i < fieldCandidateList.size(); ++i)
1542  {
1543  //__COUT__ << "Checking " << fieldCandidateList[i].relativePath_ <<
1544  // fieldCandidateList[i].columnName_ << " = " <<
1545  // fieldCount[i] << __E__;
1546  if(fieldCount[i] != -1 && fieldCount[i] != (int)recordList.size())
1547  {
1548  //__COUT__ << "Erasing " << fieldCandidateList[i].relativePath_ <<
1549  // fieldCandidateList[i].columnName_ << __E__;
1550 
1551  fieldCount.erase(fieldCount.begin() + i);
1552  fieldCandidateList.erase(fieldCandidateList.begin() + i);
1553  --i; // rewind to look at next after deleted
1554  }
1555  }
1556 
1557  // for(unsigned int i=0;i<fieldCandidateList.size();++i)
1558  // __COUT__ << "Final " << fieldCandidateList[i].relativePath_ <<
1559  // fieldCandidateList[i].columnName_ << __E__;
1560 
1561  return fieldCandidateList;
1562 } //end getCommonFields()
1563 
1564 //==============================================================================
1565 // getUniqueValuesForField
1566 //
1567 // returns sorted unique values for the specified records and field
1568 //
1569 std::set<std::string /*unique-value*/> ConfigurationTree::getUniqueValuesForField(
1570  const std::vector<std::string /*relative-path*/>& recordList,
1571  const std::string& fieldName) const
1572 {
1573  // enforce that starting point is a table node
1574  if(!isConfigurationNode())
1575  {
1576  __SS__ << "Can only get getCommonFields from a table node! "
1577  << "The node type is " << getNodeType() << __E__;
1578 
1579  ss << nodeDump() << __E__;
1580  __SS_THROW__;
1581  }
1582 
1583  std::set<std::string /*unique-value*/> uniqueValues;
1584 
1585  // for each record in <record list>
1586  // emplace value at field into set
1587  //
1588  // return result
1589 
1590  for(unsigned int i = 0; i < recordList.size(); ++i)
1591  {
1592  __COUT__ << "Checking " << recordList[i] << __E__;
1593 
1594  // Note: that ConfigurationTree maps both fields associated with a link
1595  // to the same node instance.
1596  // The behavior is likely not expected as response for this function..
1597  // so for links return actual value for field name specified
1598  // i.e. if Table of link is requested give that; if linkID is requested give
1599  // that. use TRUE in getValueAsString for proper behavior
1600  uniqueValues.emplace(
1601  getNode(recordList[i]).getNode(fieldName).getValueAsString(true));
1602  }
1603 
1604  return uniqueValues;
1605 } //end getUniqueValuesForField()
1606 
1607 //==============================================================================
1608 // recursiveGetCommonFields
1609 // wrapper is ...getCommonFields
1610 void ConfigurationTree::recursiveGetCommonFields(
1611  std::vector<ConfigurationTree::RecordField>& fieldCandidateList,
1612  std::vector<int>& fieldCount,
1613  const std::vector<std::string /*relative-path*/>& fieldAcceptList,
1614  const std::vector<std::string /*relative-path*/>& fieldRejectList,
1615  unsigned int depth,
1616  const std::string& relativePathBase,
1617  bool inFirstRecord) const
1618 {
1619  --depth;
1620  //__COUT__ << "relativePathBase " << relativePathBase << __E__;
1621 
1622  // =====================
1623  // Start recursiveGetCommonFields()
1624  // --depth;
1625  // loop through all children
1626  // if isValueNode (value nodes are possible field candidates!)
1627  // if first uid record
1628  // add field to <field candidates list> if in <field filter list>
1629  // initial mark <field count> as 1
1630  // else
1631  // if field is in list,
1632  // increment count for field candidate
1633  // //?increment fields in list count for record
1634  // else if field is not in list, discard field
1635  // else if depth > 0 and is UID-Link
1636  // if Link Table/UID pair is not found in <field candidates list> (avoid
1637  // endless loops through tree) recursiveGetCommonFields()
1638  // =====================
1639 
1640  bool found; // used in loops
1641  auto tableName = getTableName(); // all fields will share this table name
1642  auto uid = getUIDAsString(); // all fields will share this uid
1643  unsigned int j;
1644 
1645  auto recordChildren = getChildren();
1646  for(const auto& fieldNode : recordChildren)
1647  {
1648  if(fieldNode.second.isValueNode())
1649  {
1650  // skip author and record insertion time
1651  if(fieldNode.second.getColumnInfo().getType() ==
1652  TableViewColumnInfo::TYPE_AUTHOR ||
1653  fieldNode.second.getColumnInfo().getType() ==
1654  TableViewColumnInfo::TYPE_TIMESTAMP)
1655  continue;
1656 
1657  //__COUT__ << "isValueNode " << fieldNode.first << __E__;
1658  if(inFirstRecord) // first uid record
1659  {
1660  // check field accept filter list
1661  found = fieldAcceptList.size() ? false : true; // accept if no filter
1662  // list
1663  for(const auto& fieldFilter : fieldAcceptList)
1664  if(StringMacros::wildCardMatch(fieldFilter, fieldNode.first))
1665  {
1666  found = true;
1667  break;
1668  }
1669 
1670  if(found)
1671  {
1672  // check field reject filter list
1673 
1674  found = true; // accept if no filter list
1675  for(const auto& fieldFilter : fieldRejectList)
1676  if(StringMacros::wildCardMatch(fieldFilter, fieldNode.first))
1677  {
1678  found = false; // reject if match
1679  break;
1680  }
1681  }
1682 
1683  // if found, new field (since this is first record)
1684  if(found)
1685  {
1686  //__COUT__ << "FOUND field " <<
1687  // (relativePathBase + fieldNode.first) << __E__;
1688  fieldCandidateList.push_back(ConfigurationTree::RecordField(
1689  tableName,
1690  uid,
1691  fieldNode.first,
1692  relativePathBase, // relative path, not including columnName_
1693  &fieldNode.second.getColumnInfo()));
1694  fieldCount.push_back(1); // init count to 1
1695  }
1696  }
1697  else // not first record
1698  {
1699  // if field is in <field candidates list>, increment <field count>
1700  // else ignore
1701  for(j = 0; j < fieldCandidateList.size(); ++j)
1702  {
1703  if((relativePathBase + fieldNode.first) ==
1704  (fieldCandidateList[j].relativePath_ +
1705  fieldCandidateList[j].columnName_))
1706  {
1707  //__COUT__ << "incrementing " << j <<
1708  // " " << fieldCandidateList[j].relativePath_ << __E__;
1709  // found, so increment <field count>
1710  ++fieldCount[j];
1711  break;
1712  }
1713  }
1714  }
1715  }
1716  else if(depth > 0 && fieldNode.second.isUIDLinkNode() &&
1717  !fieldNode.second.isDisconnected())
1718  {
1719  //__COUT__ << "isUIDLinkNode " << (relativePathBase + fieldNode.first) <<
1720  // __E__;
1721  fieldNode.second.recursiveGetCommonFields(
1722  fieldCandidateList,
1723  fieldCount,
1724  fieldAcceptList,
1725  fieldRejectList,
1726  depth,
1727  (relativePathBase + fieldNode.first) + "/", // relativePathBase
1728  inFirstRecord // continue inFirstRecord (or not) depth search
1729  );
1730  }
1731  }
1732 } //end recursiveGetCommonFields()
1733 
1734 //==============================================================================
1735 // getChildrenByPriority
1736 // returns them in order encountered in the table
1737 // if filterMap criteria, then rejects any that do not meet all criteria
1738 //
1739 // value can be comma-separated for OR of multiple values
1740 std::vector<std::vector<std::pair<std::string, ConfigurationTree>>>
1741 ConfigurationTree::getChildrenByPriority(
1742  std::map<std::string /*relative-path*/, std::string /*value*/> filterMap,
1743  bool onlyStatusTrue) const
1744 {
1745  std::vector<std::vector<std::pair<std::string, ConfigurationTree>>> retVector;
1746 
1747  //__COUT__ << "Children of node: " << getValueAsString() << __E__;
1748 
1749  bool filtering = filterMap.size();
1750  bool skip;
1751  std::string fieldValue;
1752 
1753  bool createContainer;
1754 
1755  std::vector<std::vector<std::string>> childrenNamesByPriority =
1756  getChildrenNamesByPriority(onlyStatusTrue);
1757 
1758  for(auto& childNamesAtPriority : childrenNamesByPriority)
1759  {
1760  createContainer = true;
1761 
1762  for(auto& childName : childNamesAtPriority)
1763  {
1764  //__COUT__ << "\tChild: " << childName << __E__;
1765 
1766  if(filtering)
1767  {
1768  // if all criteria are not met, then skip
1769  skip = false;
1770 
1771  // for each filter, check value
1772  for(const auto& filterPair : filterMap)
1773  {
1774  std::string filterPath = childName + "/" + filterPair.first;
1775  __COUTV__(filterPath);
1776  try
1777  {
1778  // extract field value list
1779  std::vector<std::string> fieldValues;
1780  StringMacros::getVectorFromString(
1781  filterPair.second,
1782  fieldValues,
1783  std::set<char>({','}) /*delimiters*/);
1784 
1785  __COUTV__(fieldValues.size());
1786 
1787  skip = true;
1788  // for each field check if any match
1789  for(const auto& fieldValue : fieldValues)
1790  {
1791  // Note: that ConfigurationTree maps both fields associated
1792  // with a link to the same node instance. The behavior is
1793  // likely not expected as response for this function.. so for
1794  // links return actual value for field name specified i.e.
1795  // if Table of link is requested give that; if linkID is
1796  // requested give that. use TRUE in getValueAsString for
1797  // proper
1798  // behavior
1799 
1800  __COUT__ << "\t\tCheck: " << filterPair.first
1801  << " == " << fieldValue << " => "
1802  << StringMacros::decodeURIComponent(fieldValue)
1803  << " ??? "
1804  << this->getNode(filterPath).getValueAsString(true)
1805  << __E__;
1806 
1807  if(StringMacros::wildCardMatch(
1808  StringMacros::decodeURIComponent(fieldValue),
1809  this->getNode(filterPath).getValueAsString(true)))
1810  {
1811  // found a match for the field/value pair
1812  skip = false;
1813  break;
1814  }
1815  }
1816  }
1817  catch(...)
1818  {
1819  __SS__ << "Failed to access filter path '" << filterPath
1820  << "' - aborting." << __E__;
1821 
1822  ss << nodeDump() << __E__;
1823  __SS_THROW__;
1824  }
1825 
1826  if(skip)
1827  break; // no match for this field, so stop checking and skip this
1828  // record
1829  }
1830 
1831  if(skip)
1832  continue; // skip this record
1833 
1834  //__COUT__ << "\tChild accepted: " << childName << __E__;
1835  }
1836 
1837  if(createContainer)
1838  {
1839  retVector.push_back(
1840  std::vector<std::pair<std::string, ConfigurationTree>>());
1841  createContainer = false;
1842  }
1843 
1844  retVector[retVector.size() - 1].push_back(
1845  std::pair<std::string, ConfigurationTree>(
1846  childName, this->getNode(childName, true)));
1847  } // end children within priority loop
1848  } // end children by priority loop
1849 
1850  //__COUT__ << "Done w/Children of node: " << getValueAsString() << __E__;
1851  return retVector;
1852 } // end getChildrenByPriority()
1853 
1854 //==============================================================================
1855 // getChildren
1856 // returns them in order encountered in the table
1857 // if filterMap criteria, then rejects any that do not meet all criteria
1858 //
1859 // value can be comma-separated for OR of multiple values
1860 std::vector<std::pair<std::string, ConfigurationTree>> ConfigurationTree::getChildren(
1861  std::map<std::string /*relative-path*/, std::string /*value*/> filterMap,
1862  bool byPriority,
1863  bool onlyStatusTrue) const
1864 {
1865  std::vector<std::pair<std::string, ConfigurationTree>> retVector;
1866 
1867  //__COUT__ << "Children of node: " << getValueAsString() << __E__;
1868 
1869  bool filtering = filterMap.size();
1870  bool skip;
1871  std::string fieldValue;
1872 
1873  std::vector<std::string> childrenNames = getChildrenNames(byPriority, onlyStatusTrue);
1874  for(auto& childName : childrenNames)
1875  {
1876  //__COUT__ << "\tChild: " << childName << __E__;
1877 
1878  if(filtering)
1879  {
1880  // if all criteria are not met, then skip
1881  skip = false;
1882 
1883  // for each filter, check value
1884  for(const auto& filterPair : filterMap)
1885  {
1886  std::string filterPath = childName + "/" + filterPair.first;
1887  __COUTV__(filterPath);
1888  try
1889  {
1890  // extract field value list
1891  std::vector<std::string> fieldValues;
1892  StringMacros::getVectorFromString(
1893  filterPair.second,
1894  fieldValues,
1895  std::set<char>({','}) /*delimiters*/);
1896 
1897  __COUTV__(fieldValues.size());
1898 
1899  skip = true;
1900  // for each field check if any match
1901  for(const auto& fieldValue : fieldValues)
1902  {
1903  // Note: that ConfigurationTree maps both fields associated with a
1904  // link to the same node instance. The behavior is likely not
1905  // expected as response for this function.. so for links
1906  // return
1907  // actual value for field name specified i.e. if Table of link
1908  // is requested give that; if linkID is requested give that. use
1909  // TRUE in getValueAsString for proper behavior
1910 
1911  __COUT__
1912  << "\t\tCheck: " << filterPair.first << " == " << fieldValue
1913  << " => " << StringMacros::decodeURIComponent(fieldValue)
1914  << " ??? " << this->getNode(filterPath).getValueAsString(true)
1915  << __E__;
1916 
1917  if(StringMacros::wildCardMatch(
1918  StringMacros::decodeURIComponent(fieldValue),
1919  this->getNode(filterPath).getValueAsString(true)))
1920  {
1921  // found a match for the field/value pair
1922  skip = false;
1923  break;
1924  }
1925  }
1926  }
1927  catch(...)
1928  {
1929  __SS__ << "Failed to access filter path '" << filterPath
1930  << "' - aborting." << __E__;
1931 
1932  ss << nodeDump() << __E__;
1933  __SS_THROW__;
1934  }
1935 
1936  if(skip)
1937  break; // no match for this field, so stop checking and skip this
1938  // record
1939  }
1940 
1941  if(skip)
1942  continue; // skip this record
1943 
1944  //__COUT__ << "\tChild accepted: " << childName << __E__;
1945  }
1946 
1947  retVector.push_back(std::pair<std::string, ConfigurationTree>(
1948  childName, this->getNode(childName, true)));
1949  }
1950 
1951  //__COUT__ << "Done w/Children of node: " << getValueAsString() << __E__;
1952  return retVector;
1953 } //end getChildren()
1954 
1955 //==============================================================================
1956 // getChildren
1957 // returns them in order encountered in the table
1958 std::map<std::string, ConfigurationTree> ConfigurationTree::getChildrenMap(void) const
1959 {
1960  std::map<std::string, ConfigurationTree> retMap;
1961 
1962  //__COUT__ << "Children of node: " << getValueAsString() << __E__;
1963 
1964  std::vector<std::string> childrenNames = getChildrenNames();
1965  for(auto& childName : childrenNames)
1966  {
1967  //__COUT__ << "\tChild: " << childName << __E__;
1968  retMap.insert(std::pair<std::string, ConfigurationTree>(
1969  childName, this->getNode(childName)));
1970  }
1971 
1972  //__COUT__ << "Done w/Children of node: " << getValueAsString() << __E__;
1973  return retMap;
1974 } //end getChildrenMap()
1975 
1976 //==============================================================================
1977 bool ConfigurationTree::isRootNode(void) const { return (!table_); }
1978 
1979 //==============================================================================
1980 bool ConfigurationTree::isConfigurationNode(void) const
1981 {
1982  return (table_ && row_ == TableView::INVALID && col_ == TableView::INVALID);
1983 }
1984 
1985 //==============================================================================
1986 // getChildrenNamesByPriority
1987 // returns them in priority order encountered in the table
1988 std::vector<std::vector<std::string>> ConfigurationTree::getChildrenNamesByPriority(
1989  bool onlyStatusTrue) const
1990 {
1991  std::vector<std::vector<std::string /*child name*/>> retVector;
1992 
1993  if(!tableView_)
1994  {
1995  __SS__ << "Can not get children names of '" << getValueAsString()
1996  << "' with null configuration view pointer!" << __E__;
1997  if(isLinkNode() && isDisconnected())
1998  ss << " This node is a disconnected link to " << getDisconnectedTableName()
1999  << __E__;
2000 
2001  ss << nodeDump() << __E__;
2002  __SS_ONLY_THROW__;
2003  }
2004 
2005  if(row_ == TableView::INVALID && col_ == TableView::INVALID)
2006  {
2007  // this node is config node
2008  // so return all uid node strings that match groupId
2009 
2010  bool tmpStatus;
2011 
2012  if(1) // reshuffle by priority
2013  {
2014  try
2015  {
2016  std::map<uint64_t /*priority*/, std::vector<unsigned int /*child row*/>>
2017  orderedByPriority;
2018  std::vector<std::string /*child name*/> retPrioritySet;
2019 
2020  unsigned int col = tableView_->getColPriority();
2021 
2022  uint64_t tmpPriority;
2023 
2024  for(unsigned int r = 0; r < tableView_->getNumberOfRows(); ++r)
2025  if(groupId_ == "" ||
2026  tableView_->isEntryInGroup(r, childLinkIndex_, groupId_))
2027  {
2028  // check status if needed
2029  if(onlyStatusTrue)
2030  {
2031  tableView_->getValue(
2032  tmpStatus, r, tableView_->getColStatus());
2033  if(!tmpStatus)
2034  continue; // skip those with status false
2035  }
2036 
2037  tableView_->getValue(tmpPriority, r, col);
2038  // do not accept DEFAULT value of 0.. convert to 100
2039  orderedByPriority[tmpPriority ? tmpPriority : 100].push_back(r);
2040  }
2041 
2042  // at this point have priority map
2043  // now build return vector
2044 
2045  for(const auto& priorityChildRowVector : orderedByPriority)
2046  {
2047  retVector.push_back(std::vector<std::string /*child name*/>());
2048  for(const auto& priorityChildRow : priorityChildRowVector.second)
2049  retVector[retVector.size() - 1].push_back(
2050  tableView_->getDataView()[priorityChildRow]
2051  [tableView_->getColUID()]);
2052  }
2053 
2054  __COUT__ << "Returning priority children list." << __E__;
2055  return retVector;
2056  }
2057  catch(std::runtime_error& e)
2058  {
2059  __COUT_WARN__ << "Error identifying priority. Assuming all children have "
2060  "equal priority (Error: "
2061  << e.what() << __E__;
2062  retVector.clear();
2063  }
2064  }
2065  // else not by priority
2066 
2067  for(unsigned int r = 0; r < tableView_->getNumberOfRows(); ++r)
2068  if(groupId_ == "" || tableView_->isEntryInGroup(r, childLinkIndex_, groupId_))
2069  {
2070  // check status if needed
2071  if(onlyStatusTrue)
2072  {
2073  tableView_->getValue(tmpStatus, r, tableView_->getColStatus());
2074  if(!tmpStatus)
2075  continue; // skip those with status false
2076  }
2077 
2078  retVector.push_back(std::vector<std::string /*child name*/>());
2079  retVector[retVector.size() - 1].push_back(
2080  tableView_->getDataView()[r][tableView_->getColUID()]);
2081  }
2082  }
2083  else if(row_ == TableView::INVALID)
2084  {
2085  __SS__ << "Malformed ConfigurationTree" << __E__;
2086 
2087  ss << nodeDump() << __E__;
2088  __SS_THROW__;
2089  }
2090  else if(col_ == TableView::INVALID)
2091  {
2092  // this node is uid node
2093  // so return all link and value nodes
2094 
2095  for(unsigned int c = 0; c < tableView_->getNumberOfColumns(); ++c)
2096  if(c == tableView_->getColUID() || // skip UID and linkID columns (only show
2097  // link column, to avoid duplicates)
2098  tableView_->getColumnInfo(c).isChildLinkGroupID() ||
2099  tableView_->getColumnInfo(c).isChildLinkUID())
2100  continue;
2101  else
2102  {
2103  retVector.push_back(std::vector<std::string /*child name*/>());
2104  retVector[retVector.size() - 1].push_back(
2105  tableView_->getColumnInfo(c).getName());
2106  }
2107  }
2108  else // this node is value node, so has no node to choose from
2109  {
2110  // this node is value node, cant go any deeper!
2111  __SS__ << "\n\nError occurred looking for children of nodeName=" << getValueName()
2112  << "\n\n"
2113  << "Invalid depth! getChildrenValues() called from a value point in the "
2114  "Configuration Tree."
2115  << __E__;
2116 
2117  ss << nodeDump() << __E__;
2118  __SS_THROW__;
2119  }
2120 
2121  return retVector;
2122 } //end getChildrenNamesByPriority()
2123 
2124 //==============================================================================
2125 // getChildrenNames
2126 // returns them in order encountered in the table
2127 std::vector<std::string> ConfigurationTree::getChildrenNames(bool byPriority,
2128  bool onlyStatusTrue) const
2129 {
2130  std::vector<std::string /*child name*/> retVector;
2131 
2132  if(!tableView_)
2133  {
2134  __SS__ << "Can not get children names of '" << getValueAsString()
2135  << "' with null configuration view pointer!" << __E__;
2136  if(isLinkNode() && isDisconnected())
2137  ss << " This node is a disconnected link to " << getDisconnectedTableName()
2138  << __E__;
2139  __SS_ONLY_THROW__;
2140  }
2141 
2142  if(row_ == TableView::INVALID && col_ == TableView::INVALID)
2143  {
2144  // this node is config node
2145  // so return all uid node strings that match groupId
2146 
2147  bool tmpStatus;
2148 
2149  if(byPriority) // reshuffle by priority
2150  {
2151  try
2152  {
2153  std::map<uint64_t /*priority*/, std::vector<unsigned int /*child row*/>>
2154  orderedByPriority;
2155  std::vector<std::string /*child name*/> retPrioritySet;
2156 
2157  unsigned int col = tableView_->getColPriority();
2158 
2159  uint64_t tmpPriority;
2160 
2161  for(unsigned int r = 0; r < tableView_->getNumberOfRows(); ++r)
2162  if(groupId_ == "" ||
2163  tableView_->isEntryInGroup(r, childLinkIndex_, groupId_))
2164  {
2165  // check status if needed
2166  if(onlyStatusTrue)
2167  {
2168  tableView_->getValue(
2169  tmpStatus, r, tableView_->getColStatus());
2170  if(!tmpStatus)
2171  continue; // skip those with status false
2172  }
2173 
2174  tableView_->getValue(tmpPriority, r, col);
2175  // do not accept DEFAULT value of 0.. convert to 100
2176  orderedByPriority[tmpPriority ? tmpPriority : 100].push_back(r);
2177  }
2178 
2179  // at this point have priority map
2180  // now build return vector
2181 
2182  for(const auto& priorityChildRowVector : orderedByPriority)
2183  for(const auto& priorityChildRow : priorityChildRowVector.second)
2184  retVector.push_back(
2185  tableView_->getDataView()[priorityChildRow]
2186  [tableView_->getColUID()]);
2187 
2188  __COUT__ << "Returning priority children list." << __E__;
2189  return retVector;
2190  }
2191  catch(std::runtime_error& e)
2192  {
2193  __COUT_WARN__ << "Priority configuration not found. Assuming all "
2194  "children have equal priority (Error: "
2195  << e.what() << __E__;
2196  retVector.clear();
2197  }
2198  }
2199  // else not by priority
2200 
2201  for(unsigned int r = 0; r < tableView_->getNumberOfRows(); ++r)
2202  if(groupId_ == "" || tableView_->isEntryInGroup(r, childLinkIndex_, groupId_))
2203  {
2204  // check status if needed
2205  if(onlyStatusTrue)
2206  {
2207  tableView_->getValue(tmpStatus, r, tableView_->getColStatus());
2208  if(!tmpStatus)
2209  continue; // skip those with status false
2210  }
2211 
2212  retVector.push_back(
2213  tableView_->getDataView()[r][tableView_->getColUID()]);
2214  }
2215  }
2216  else if(row_ == TableView::INVALID)
2217  {
2218  __SS__ << "Malformed ConfigurationTree" << __E__;
2219 
2220  ss << nodeDump() << __E__;
2221  __SS_THROW__;
2222  }
2223  else if(col_ == TableView::INVALID)
2224  {
2225  // this node is uid node
2226  // so return all link and value nodes
2227 
2228  for(unsigned int c = 0; c < tableView_->getNumberOfColumns(); ++c)
2229  if(c == tableView_->getColUID() || // skip UID and linkID columns (only show
2230  // link column, to avoid duplicates)
2231  tableView_->getColumnInfo(c).isChildLinkGroupID() ||
2232  tableView_->getColumnInfo(c).isChildLinkUID())
2233  continue;
2234  else
2235  retVector.push_back(tableView_->getColumnInfo(c).getName());
2236  }
2237  else // this node is value node, so has no node to choose from
2238  {
2239  // this node is value node, cant go any deeper!
2240  __SS__ << "\n\nError occurred looking for children of nodeName=" << getValueName()
2241  << "\n\n"
2242  << "Invalid depth! getChildrenValues() called from a value point in the "
2243  "Configuration Tree."
2244  << __E__;
2245 
2246  ss << nodeDump() << __E__;
2247  __SS_THROW__;
2248  }
2249 
2250  return retVector;
2251 } //end getChildrenNames()
2252 
2253 //==============================================================================
2254 // getValueAsTreeNode
2255 // returns tree node for value of this node, treating the value
2256 // as a string for the absolute path string from root of tree
2257 ConfigurationTree ConfigurationTree::getValueAsTreeNode(void) const
2258 {
2259  // check if first character is a /, .. if so try to get value in tree
2260  // if exception, just take value
2261  // note: this call will throw an error, in effect, if not a "value" node
2262  if(!tableView_)
2263  {
2264  __SS__ << "Invalid node for get value." << __E__;
2265  __SS_THROW__;
2266  }
2267 
2268  std::string valueString =
2269  tableView_->getValueAsString(row_, col_, true /* convertEnvironmentVariables */);
2270  //__COUT__ << valueString << __E__;
2271  if(valueString.size() && valueString[0] == '/')
2272  {
2273  //__COUT__ << "Starts with '/' - check if valid tree path: " << valueString <<
2274  // __E__;
2275  try
2276  {
2277  ConfigurationTree retNode = configMgr_->getNode(valueString);
2278  __COUT__ << "Found a valid tree path in value!" << __E__;
2279  return retNode;
2280  }
2281  catch(...)
2282  {
2283  __SS__ << "Invalid tree path." << __E__;
2284  __SS_ONLY_THROW__;
2285  }
2286  }
2287 
2288  {
2289  __SS__ << "Invalid value string '" << valueString
2290  << "' - must start with a '/' character." << __E__;
2291  __SS_ONLY_THROW__;
2292  }
2293 } //end getValueAsTreeNode()