otsdaq  v2_04_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  << " UID '"
445  << linkParentConfig_->getView().getValueAsString(
446  linkBackRow_, linkParentConfig_->getView().getColUID())
447  << "' at row " << linkBackRow_ << " col '"
448  << linkParentConfig_->getView().getColumnInfo(linkBackCol_).getName()
449  << ".'" << __E__;
450 
451  ss << StringMacros::stackTrace() << __E__;
452  }
453 
454  __SS_ONLY_THROW__;
455  }
456  return table_->getTableName();
457 } // end getTableName()
458 
459 //==============================================================================
460 // getFieldTableName
461 // returns the configuration name for the node's field.
462 // Note: for link nodes versus value nodes this has different functionality than
463 // getTableName()
464 const std::string& ConfigurationTree::getFieldTableName(void) const
465 {
466  // if link node, need config name from parent
467  if(isLinkNode())
468  {
469  if(!linkParentConfig_)
470  {
471  __SS__ << "Can not get configuration name of link node field with no parent "
472  "configuration pointer!"
473  << __E__;
474  ss << nodeDump() << __E__;
475  __SS_ONLY_THROW__;
476  }
477  return linkParentConfig_->getTableName();
478  }
479  else
480  return getTableName();
481 } // end getFieldTableName()
482 
483 //==============================================================================
484 // getDisconnectedTableName
485 const std::string& ConfigurationTree::getDisconnectedTableName(void) const
486 {
487  if(isLinkNode() && isDisconnected())
488  return disconnectedTargetName_;
489 
490  __SS__ << "Can not get disconnected target name of node unless it is a disconnected "
491  "link node!"
492  << __E__;
493 
494  ss << nodeDump() << __E__;
495  __SS_ONLY_THROW__;
496 } // end getDisconnectedTableName()
497 
498 //==============================================================================
499 // getDisconnectedLinkID
500 const std::string& ConfigurationTree::getDisconnectedLinkID(void) const
501 {
502  if(isLinkNode() && isDisconnected())
503  return disconnectedLinkID_;
504 
505  __SS__ << "Can not get disconnected target name of node unless it is a disconnected "
506  "link node!"
507  << __E__;
508 
509  ss << nodeDump() << __E__;
510  __SS_ONLY_THROW__;
511 } // end getDisconnectedLinkID()
512 
513 //==============================================================================
514 // getTableVersion
515 const TableVersion& ConfigurationTree::getTableVersion(void) const
516 {
517  if(!tableView_)
518  {
519  __SS__ << "Can not get configuration version of node with no config view pointer!"
520  << __E__;
521 
522  ss << nodeDump() << __E__;
523  __SS_ONLY_THROW__;
524  }
525  return tableView_->getVersion();
526 } // end getTableVersion()
527 
528 //==============================================================================
529 // getTableCreationTime
530 const time_t& ConfigurationTree::getTableCreationTime(void) const
531 {
532  if(!tableView_)
533  {
534  __SS__ << "Can not get configuration creation time of node with no config view "
535  "pointer!"
536  << __E__;
537 
538  ss << nodeDump() << __E__;
539  __SS_ONLY_THROW__;
540  }
541  return tableView_->getCreationTime();
542 } // end getTableCreationTime()
543 
544 //==============================================================================
545 // getFixedChoices
546 // returns vector of default + data choices
547 // Used as choices for tree-view, for example.
548 std::vector<std::string> ConfigurationTree::getFixedChoices(void) const
549 {
550  if(getValueType() != TableViewColumnInfo::TYPE_FIXED_CHOICE_DATA &&
551  getValueType() != TableViewColumnInfo::TYPE_BITMAP_DATA && !isLinkNode())
552  {
553  __SS__ << "Can not get fixed choices of node with value type of '"
554  << getValueType() << ".' Node must be a link or a value node with type '"
555  << TableViewColumnInfo::TYPE_BITMAP_DATA << "' or '"
556  << TableViewColumnInfo::TYPE_FIXED_CHOICE_DATA << ".'" << __E__;
557 
558  ss << nodeDump() << __E__;
559  __SS_ONLY_THROW__;
560  }
561 
562  std::vector<std::string> retVec;
563 
564  if(isLinkNode())
565  {
566  if(!linkParentConfig_)
567  {
568  __SS__
569  << "Can not get fixed choices of node with no parent config view pointer!"
570  << __E__;
571 
572  ss << nodeDump() << __E__;
573  __SS_ONLY_THROW__;
574  }
575 
576  //__COUT__ << getChildLinkIndex() << __E__;
577  //__COUT__ << linkColName_ << __E__;
578 
579  // for links, col_ = -1, column c needs to change (to ChildLink column of pair)
580  // get column from parent config pointer
581 
582  const TableView* parentView = &(linkParentConfig_->getView());
583  int c = parentView->findCol(linkColName_);
584 
585  std::pair<unsigned int /*link col*/, unsigned int /*link id col*/> linkPair;
586  bool isGroupLink;
587  parentView->getChildLink(c, isGroupLink, linkPair);
588  c = linkPair.first;
589 
590  std::vector<std::string> choices = parentView->getColumnInfo(c).getDataChoices();
591  for(const auto& choice : choices)
592  retVec.push_back(choice);
593 
594  return retVec;
595  }
596 
597  if(!tableView_)
598  {
599  __SS__ << "Can not get fixed choices of node with no config view pointer!"
600  << __E__;
601 
602  ss << nodeDump() << __E__;
603  __SS_ONLY_THROW__;
604  }
605 
606  // return vector of default + data choices
607  retVec.push_back(tableView_->getColumnInfo(col_).getDefaultValue());
608  std::vector<std::string> choices = tableView_->getColumnInfo(col_).getDataChoices();
609  for(const auto& choice : choices)
610  retVec.push_back(choice);
611 
612  return retVec;
613 } // end getFixedChoices()
614 
615 //==============================================================================
616 // getValueAsString
617 // NOTE: getValueAsString() method should be preferred if getting the Link UID
618 // because when disconnected will return "X". getValue() would return the
619 // column name of the link when disconnected.
620 //
621 // returnLinkTableValue returns the value in the source table as though link was
622 // not followed to destination table.
623 const std::string& ConfigurationTree::getValueAsString(bool returnLinkTableValue) const
624 {
625  if(isLinkNode())
626  {
627  if(returnLinkTableValue)
628  return linkColValue_;
629  else if(isDisconnected())
630  return ConfigurationTree::DISCONNECTED_VALUE;
631  else if(row_ == TableView::INVALID &&
632  col_ == TableView::INVALID) // this link is groupId node
633  return (groupId_ == "") ? table_->getTableName() : groupId_;
634  else if(col_ == TableView::INVALID) // this link is uid node
635  return tableView_->getDataView()[row_][tableView_->getColUID()];
636  else
637  {
638  __SS__ << "Impossible Link." << __E__;
639 
640  ss << nodeDump() << __E__;
641  __SS_THROW__;
642  }
643  }
644  else if(row_ != TableView::INVALID &&
645  col_ != TableView::INVALID) // this node is a value node
646  return tableView_->getDataView()[row_][col_];
647  else if(row_ == TableView::INVALID &&
648  col_ == TableView::INVALID) // this node is config node maybe with groupId
649  return (groupId_ == "") ? table_->getTableName() : groupId_;
650  else if(row_ == TableView::INVALID)
651  {
652  __SS__ << "Malformed ConfigurationTree" << __E__;
653 
654  ss << nodeDump() << __E__;
655  __SS_THROW__;
656  }
657  else if(col_ == TableView::INVALID) // this node is uid node
658  return tableView_->getDataView()[row_][tableView_->getColUID()];
659  else
660  {
661  __SS__ << "Impossible." << __E__;
662 
663  ss << nodeDump() << __E__;
664  __SS_THROW__;
665  }
666 } // end getValueAsString()
667 
668 //==============================================================================
669 // getUIDAsString
670 // returns UID associated with current value node or UID-Link node
671 //
672 const std::string& ConfigurationTree::getUIDAsString(void) const
673 {
674  if(isValueNode() || isUIDLinkNode())
675  return tableView_->getDataView()[row_][tableView_->getColUID()];
676 
677  {
678  __SS__ << "Can not get UID of node with type '" << getNodeType()
679  << ".' Node type must be '" << ConfigurationTree::NODE_TYPE_VALUE
680  << "' or '" << ConfigurationTree::NODE_TYPE_UID_LINK << ".'" << __E__;
681 
682  ss << nodeDump() << __E__;
683  __SS_ONLY_THROW__;
684  }
685 } // end getUIDAsString()
686 
687 //==============================================================================
688 // getValueDataType
689 // e.g. used to determine if node is type NUMBER
690 const std::string& ConfigurationTree::getValueDataType(void) const
691 {
692  if(isValueNode())
693  return tableView_->getColumnInfo(col_).getDataType();
694  else // must be std::string
695  return TableViewColumnInfo::DATATYPE_STRING;
696 } // end getValueDataType()
697 
698 //==============================================================================
699 // isDefaultValue
700 // returns true if is a value node and value is the default for the type
701 bool ConfigurationTree::isDefaultValue(void) const
702 {
703  if(!isValueNode())
704  return false;
705 
706  if(getValueDataType() == TableViewColumnInfo::DATATYPE_STRING)
707  {
708  if(getValueType() == TableViewColumnInfo::TYPE_ON_OFF ||
709  getValueType() == TableViewColumnInfo::TYPE_TRUE_FALSE ||
710  getValueType() == TableViewColumnInfo::TYPE_YES_NO)
711  return getValueAsString() ==
712  TableViewColumnInfo::DATATYPE_BOOL_DEFAULT; // default to OFF, NO,
713  // FALSE
714  else if(getValueType() == TableViewColumnInfo::TYPE_COMMENT)
715  return getValueAsString() == TableViewColumnInfo::DATATYPE_COMMENT_DEFAULT ||
716  getValueAsString() ==
717  ""; // in case people delete default comment, allow blank also
718  else
719  return getValueAsString() == TableViewColumnInfo::DATATYPE_STRING_DEFAULT;
720  }
721  else if(getValueDataType() == TableViewColumnInfo::DATATYPE_NUMBER)
722  return getValueAsString() == TableViewColumnInfo::DATATYPE_NUMBER_DEFAULT;
723  else if(getValueDataType() == TableViewColumnInfo::DATATYPE_TIME)
724  return getValueAsString() == TableViewColumnInfo::DATATYPE_TIME_DEFAULT;
725  else
726  return false;
727 } // end isDefaultValue()
728 
729 //==============================================================================
730 // getValueType
731 // e.g. used to determine if node is type TYPE_DATA, TYPE_ON_OFF, etc.
732 const std::string& ConfigurationTree::getValueType(void) const
733 {
734  if(isValueNode())
735  return tableView_->getColumnInfo(col_).getType();
736  else if(isLinkNode() && isDisconnected())
737  return ConfigurationTree::VALUE_TYPE_DISCONNECTED;
738  else // just call all non-value nodes data
739  return ConfigurationTree::VALUE_TYPE_NODE;
740 } // end getValueType()
741 
742 //==============================================================================
743 // getColumnInfo
744 // only sensible for value node
745 const TableViewColumnInfo& ConfigurationTree::getColumnInfo(void) const
746 {
747  if(isValueNode())
748  return tableView_->getColumnInfo(col_);
749  else
750  {
751  __SS__ << "Can only get column info from a value node! "
752  << "The node type is " << getNodeType() << __E__;
753 
754  ss << nodeDump() << __E__;
755  __SS_THROW__;
756  }
757 } // end getColumnInfo()
758 
759 //==============================================================================
760 // getRow
761 const unsigned int& ConfigurationTree::getRow(void) const { return row_; }
762 
763 //==============================================================================
764 // getColumn
765 const unsigned int& ConfigurationTree::getColumn(void) const { return col_; }
766 
767 //==============================================================================
768 // getFieldRow
769 // return field's row (different handling for value vs. link node)
770 const unsigned int& ConfigurationTree::getFieldRow(void) const
771 {
772  if(isLinkNode())
773  {
774  // for links, need to use parent info to determine
775  return linkBackRow_;
776  }
777  else
778  return row_;
779 } // end getFieldRow()
780 
781 //==============================================================================
782 // getFieldColumn
783 // return field's column (different handling for value vs. link node)
784 const unsigned int& ConfigurationTree::getFieldColumn(void) const
785 {
786  if(isLinkNode())
787  {
788  // for links, need to use parent info to determine
789  return linkBackCol_;
790  }
791  else
792  return col_;
793 } // end getFieldColumn()
794 
795 //==============================================================================
796 // getChildLinkIndex
797 const std::string& ConfigurationTree::getChildLinkIndex(void) const
798 {
799  if(!isLinkNode())
800  {
801  __SS__ << "Can only get link ID from a link! "
802  << "The node type is " << getNodeType() << __E__;
803 
804  ss << nodeDump() << __E__;
805  __SS_THROW__;
806  }
807  return childLinkIndex_;
808 } // end getChildLinkIndex()
809 
810 //==============================================================================
811 // getValueName
812 // e.g. used to determine column name of value node
813 const std::string& ConfigurationTree::getValueName(void) const
814 {
815  if(isValueNode())
816  return tableView_->getColumnInfo(col_).getName();
817  else if(isLinkNode())
818  return linkColName_;
819  else
820  {
821  __SS__ << "Can only get value name of a value node!" << __E__;
822 
823  ss << nodeDump() << __E__;
824  __SS_THROW__;
825  }
826 } // end getValueName()
827 
828 //==============================================================================
829 // recurse
830 // Used by ConfigurationTree to handle / syntax of getNode
831 ConfigurationTree ConfigurationTree::recurse(const ConfigurationTree& tree,
832  const std::string& childPath,
833  bool doNotThrowOnBrokenUIDLinks,
834  const std::string& originalNodeString)
835 {
836  //__COUT__ << tree.row_ << " " << tree.col_ << __E__;
837  //__COUT__ << "childPath=" << childPath << " " << childPath.length() << __E__;
838  if(childPath.length() <= 1) // only "/" or ""
839  return tree;
840  return tree.recursiveGetNode(
841  childPath, doNotThrowOnBrokenUIDLinks, originalNodeString);
842 } // end recurse()
843 
854 // std::string ConfigurationTree::getRecordFieldValueAsString(std::string fieldName) const
855 //{
856 // //enforce that starting point is a table node
857 // if(!isUIDNode())
858 // {
859 // __SS__ << "Can only get getRecordFieldValueAsString from a uid node! " <<
860 // "The node type is " << getNodeType() << __E__;
861 // __COUT__ << "\n" << ss.str() << __E__;
862 // __SS_THROW__;
863 // }
864 //
865 // unsigned int c = tableView_->findCol(fieldName);
866 // return tableView_->getDataView()[row_][c];
867 //}
868 
869 //==============================================================================
870 // getNode
871 // Connected to recursiveGetNode()
872 //
873 // nodeString can be a multi-part path using / delimiter
874 // use:
875 // getNode(/uid/col) or getNode(uid)->getNode(col)
876 //
877 // if doNotThrowOnBrokenUIDLinks
878 // then catch exceptions on UID links and call disconnected
879 ConfigurationTree ConfigurationTree::getNode(const std::string& nodeString,
880  bool doNotThrowOnBrokenUIDLinks) const
881 {
882  return recursiveGetNode(
883  nodeString, doNotThrowOnBrokenUIDLinks, "" /*originalNodeString*/);
884 } // end getNode() connected to recursiveGetNode()
885 ConfigurationTree ConfigurationTree::recursiveGetNode(
886  const std::string& nodeString,
887  bool doNotThrowOnBrokenUIDLinks,
888  const std::string& originalNodeString) const
889 {
890  //__COUT__ << "nodeString=" << nodeString << " " << nodeString.length() << __E__;
891  //__COUT__ << "doNotThrowOnBrokenUIDLinks=" << doNotThrowOnBrokenUIDLinks <<
892  // __E__;
893 
894  // get nodeName (in case of / syntax)
895  if(nodeString.length() < 1)
896  {
897  __SS__ << "Invalid empty node name! Looking for child node from node '"
898  << getValue() << "'..." << __E__;
899 
900  ss << nodeDump() << __E__;
901  __SS_THROW__;
902  }
903 
904  bool startingSlash = nodeString[0] == '/';
905 
906  std::string nodeName = nodeString.substr(
907  startingSlash ? 1 : 0, nodeString.find('/', 1) - (startingSlash ? 1 : 0));
908  //__COUT__ << "nodeName=" << nodeName << " " << nodeName.length() << __E__;
909 
910  std::string childPath =
911  nodeString.substr(nodeName.length() + (startingSlash ? 1 : 0));
912  //__COUT__ << "childPath=" << childPath << " " << childPath.length() << __E__;
913 
914  // if this tree is beginning at a configuration.. then go to uid, and vice versa
915 
916  try
917  {
918  //__COUT__ << row_ << " " << col_ << " " << groupId_ << " " << tableView_ <<
919  // __E__;
920  if(!table_)
921  {
922  // root node
923  // so return config node
924  return recurse(configMgr_->getNode(nodeName),
925  childPath,
926  doNotThrowOnBrokenUIDLinks,
927  originalNodeString);
928  }
929  else if(row_ == TableView::INVALID && col_ == TableView::INVALID)
930  {
931  // config node
932 
933  if(!tableView_)
934  {
935  __SS__ << "Missing configView pointer! Likely attempting to access a "
936  "child node through a disconnected link node."
937  << __E__;
938 
939  ss << nodeDump() << __E__;
940  __SS_THROW__;
941  }
942 
943  // this node is config node, so return uid node considering groupid
944  return recurse(
946  configMgr_,
947  table_,
948  "", // no new groupId string, not a link
949  0 /*linkParentConfig_*/,
950  "", // link node name, not a link
951  "", // link node value, not a link
952  TableView::INVALID /*linkBackRow_*/,
953  TableView::INVALID /*linkBackCol_*/,
954  "", // ignored disconnected target name, not a link
955  "", // ignored disconnected link id, not a link
956  "",
957  // if this node is group config node, consider that when getting rows
958  (groupId_ == "")
959  ? tableView_->findRow(tableView_->getColUID(), nodeName)
960  : tableView_->findRowInGroup(tableView_->getColUID(),
961  nodeName,
962  groupId_,
963  childLinkIndex_)),
964  childPath,
965  doNotThrowOnBrokenUIDLinks,
966  originalNodeString);
967  }
968  else if(row_ == TableView::INVALID)
969  {
970  __SS__ << "Malformed ConfigurationTree" << __E__;
971 
972  ss << nodeDump() << __E__;
973  __SS_THROW__;
974  }
975  else if(col_ == TableView::INVALID)
976  {
977  // this node is uid node, so return link, group link, disconnected, or value
978  // node
979 
980  //__COUT__ << "nodeName=" << nodeName << " " << nodeName.length() <<
981  // __E__;
982 
983  // if the value is a unique link ..
984  // return a uid node!
985  // if the value is a group link
986  // return a config node with group string
987  // else.. return value node
988 
989  if(!tableView_)
990  {
991  __SS__ << "Missing configView pointer! Likely attempting to access a "
992  "child node through a disconnected link node."
993  << __E__;
994 
995  ss << nodeDump() << __E__;
996  __SS_THROW__;
997  }
998 
999  unsigned int c = tableView_->findCol(nodeName);
1000  std::pair<unsigned int /*link col*/, unsigned int /*link id col*/> linkPair;
1001  bool isGroupLink, isLink;
1002  if((isLink = tableView_->getChildLink(c, isGroupLink, linkPair)) &&
1003  !isGroupLink)
1004  {
1005  //__COUT__ << "nodeName=" << nodeName << " " << nodeName.length() <<
1006  // __E__; is a unique link, return uid node in new configuration
1007  // need new configuration pointer
1008  // and row of linkUID in new configuration
1009 
1010  const TableBase* childConfig;
1011  try
1012  {
1013  childConfig = configMgr_->getTableByName(
1014  tableView_->getDataView()[row_][linkPair.first]);
1015  childConfig->getView(); // get view as a test for an active view
1016 
1017  if(doNotThrowOnBrokenUIDLinks) // try a test of getting row
1018  {
1019  childConfig->getView().findRow(
1020  childConfig->getView().getColUID(),
1021  tableView_->getDataView()[row_][linkPair.second]);
1022  }
1023  }
1024  catch(...)
1025  {
1026  // __COUT_WARN__ << "Found disconnected node! (" <<
1027  // nodeName
1028  //<<
1029  // ":" <<
1030  // tableView_->getDataView()[row_][linkPair.first]
1031  //<< ")" << " at entry with UID " <<
1032  // tableView_->getDataView()[row_][tableView_->getColUID()]
1033  //<< __E__; do not recurse further
1034  return ConfigurationTree(
1035  configMgr_,
1036  0,
1037  "",
1038  table_, // linkParentConfig_
1039  nodeName,
1040  tableView_->getDataView()[row_][c], // this the link node field
1041  // associated value (matches
1042  // targeted column)
1043  row_ /*linkBackRow_*/,
1044  c /*linkBackCol_*/,
1045  tableView_->getDataView()[row_][linkPair.first], // give
1046  // disconnected
1047  // target name
1048  tableView_->getDataView()[row_][linkPair.second], // give
1049  // disconnected
1050  // link ID
1051  tableView_->getColumnInfo(c).getChildLinkIndex());
1052  }
1053 
1054  return recurse(
1055  ConfigurationTree( // this is a link node
1056  configMgr_,
1057  childConfig,
1058  "", // no new groupId string
1059  table_, // linkParentConfig_
1060  nodeName, // this is a link node
1061  tableView_->getDataView()[row_][c], // this the link node field
1062  // associated value (matches
1063  // targeted column)
1064  row_ /*linkBackRow_*/,
1065  c /*linkBackCol_*/,
1066  "", // ignore since is connected
1067  "", // ignore since is connected
1068  tableView_->getColumnInfo(c).getChildLinkIndex(),
1069  childConfig->getView().findRow(
1070  childConfig->getView().getColUID(),
1071  tableView_->getDataView()[row_][linkPair.second])),
1072  childPath,
1073  doNotThrowOnBrokenUIDLinks,
1074  originalNodeString);
1075  }
1076  else if(isLink)
1077  {
1078  //__COUT__ << "nodeName=" << nodeName << " " << nodeName.length() <<
1079  // __E__; is a group link, return new configuration with group string
1080  // need new configuration pointer
1081  // and group string
1082 
1083  const TableBase* childConfig;
1084  try
1085  {
1086  childConfig = configMgr_->getTableByName(
1087  tableView_->getDataView()[row_][linkPair.first]);
1088  childConfig->getView(); // get view as a test for an active view
1089  }
1090  catch(...)
1091  {
1092  if(tableView_->getDataView()[row_][linkPair.first] !=
1093  TableViewColumnInfo::DATATYPE_LINK_DEFAULT)
1094  __COUT_WARN__
1095  << "Found disconnected node! Failed link target "
1096  "from nodeName="
1097  << nodeName << " to table:id="
1098  << tableView_->getDataView()[row_][linkPair.first] << ":"
1099  << tableView_->getDataView()[row_][linkPair.second] << __E__;
1100 
1101  // do not recurse further
1102  return ConfigurationTree(
1103  configMgr_,
1104  0,
1105  tableView_->getDataView()[row_][linkPair.second], // groupID
1106  table_, // linkParentConfig_
1107  nodeName,
1108  tableView_->getDataView()[row_][c], // this the link node field
1109  // associated value (matches
1110  // targeted column)
1111  row_ /*linkBackRow_*/,
1112  c /*linkBackCol_*/,
1113  tableView_->getDataView()[row_][linkPair.first], // give
1114  // disconnected
1115  // target name
1116  tableView_->getDataView()[row_][linkPair.second], // give
1117  // disconnected
1118  // target name
1119  tableView_->getColumnInfo(c).getChildLinkIndex());
1120  }
1121 
1122  return recurse(
1123  ConfigurationTree( // this is a link node
1124  configMgr_,
1125  childConfig,
1126  tableView_
1127  ->getDataView()[row_][linkPair.second], // groupId string
1128  table_, // linkParentConfig_
1129  nodeName, // this is a link node
1130  tableView_->getDataView()[row_][c], // this the link node field
1131  // associated value (matches
1132  // targeted column)
1133  row_ /*linkBackRow_*/,
1134  c /*linkBackCol_*/,
1135  "", // ignore since is connected
1136  "", // ignore since is connected
1137  tableView_->getColumnInfo(c).getChildLinkIndex()),
1138  childPath,
1139  doNotThrowOnBrokenUIDLinks,
1140  originalNodeString);
1141  }
1142  else
1143  {
1144  //__COUT__ << "nodeName=" << nodeName << " " << nodeName.length() <<
1145  // __E__; return value node
1146  return ConfigurationTree(configMgr_,
1147  table_,
1148  "",
1149  0 /*linkParentConfig_*/,
1150  "",
1151  "",
1152  TableView::INVALID /*linkBackRow_*/,
1153  TableView::INVALID /*linkBackCol_*/,
1154  "",
1155  "" /*disconnectedLinkID*/,
1156  "",
1157  row_,
1158  c);
1159  }
1160  }
1161  }
1162  catch(std::runtime_error& e)
1163  {
1164  __SS__ << "\n\nError occurred descending from node '" << getValue()
1165  << "' in table '" << getTableName() << "' looking for child '" << nodeName
1166  << "'\n\n"
1167  << __E__;
1168  ss << "The original node search string was '" << originalNodeString << ".'"
1169  << __E__;
1170  ss << "--- Additional error detail: \n\n" << e.what() << __E__;
1171 
1172  ss << nodeDump() << __E__;
1173  __SS_ONLY_THROW__;
1174  }
1175  catch(...)
1176  {
1177  __SS__ << "\n\nError occurred descending from node '" << getValue()
1178  << "' in table '" << getTableName() << "' looking for child '" << nodeName
1179  << "'\n\n"
1180  << __E__;
1181  ss << "The original node search string was '" << originalNodeString << ".'"
1182  << __E__;
1183 
1184  ss << nodeDump() << __E__;
1185  __SS_ONLY_THROW__;
1186  }
1187 
1188  // this node is value node, so has no node to choose from
1189  __SS__
1190  << "\n\nError occurred descending from node '" << getValue() << "' in table '"
1191  << getTableName() << "' looking for child '" << nodeName << "'\n\n"
1192  << "Invalid depth! getNode() called from a value point in the Configuration Tree."
1193  << __E__;
1194  ss << "The original node search string was '" << originalNodeString << ".'" << __E__;
1195 
1196  ss << nodeDump() << __E__;
1197  __SS_ONLY_THROW__; // this node is value node, cant go any deeper!
1198 } // end recursiveGetNode()
1199 
1200 //==============================================================================
1201 // nodeDump
1202 // Useful for debugging a node failure, like when throwing an exception
1203 std::string ConfigurationTree::nodeDump(void) const
1204 {
1205  __SS__ << "ConfigurationTree::nodeDump() "
1206  "=====================================\nConfigurationTree::nodeDump():"
1207  << __E__;
1208 
1209  // try each level of debug.. and ignore errors
1210  try
1211  {
1212  ss << "\t"
1213  << "Error occurred from node '" << getValueAsString() << "'..." << __E__;
1214  }
1215  catch(...)
1216  {
1217  } // ignore errors
1218  try
1219  {
1220  ss << "\t"
1221  << "Error occurred from node '" << getValue() << "' in table '"
1222  << getTableName() << ".'" << __E__;
1223  }
1224  catch(...)
1225  {
1226  } // ignore errors
1227  try
1228  {
1229  auto children = getChildrenNames();
1230  ss << "\t"
1231  << "Here is the list of possible children (count = " << children.size()
1232  << "):" << __E__;
1233  for(auto& child : children)
1234  ss << "\t\t" << child << __E__;
1235  if(tableView_)
1236  {
1237  ss << "\n\nHere is the culprit table printout:\n\n";
1238  tableView_->print(ss);
1239  }
1240  }
1241  catch(...)
1242  {
1243  } // ignore errors trying to show children
1244 
1245  try
1246  {
1247  ss << "\n\n" << StringMacros::stackTrace() << __E__;
1248  }
1249  catch(...)
1250  {
1251  } // ignore errors
1252 
1253  ss << "end ConfigurationTree::nodeDump() ====================================="
1254  << __E__;
1255 
1256  return ss.str();
1257 } // end nodeDump()
1258 
1259 //==============================================================================
1260 ConfigurationTree ConfigurationTree::getBackNode(std::string nodeName,
1261  unsigned int backSteps) const
1262 {
1263  for(unsigned int i = 0; i < backSteps; i++)
1264  nodeName = nodeName.substr(0, nodeName.find_last_of('/'));
1265 
1266  return getNode(nodeName);
1267 } // end getBackNode()
1268 
1269 //==============================================================================
1270 ConfigurationTree ConfigurationTree::getForwardNode(std::string nodeName,
1271  unsigned int forwardSteps) const
1272 {
1273  unsigned int s = 0;
1274 
1275  // skip all leading /'s
1276  while(s < nodeName.length() && nodeName[s] == '/')
1277  ++s;
1278 
1279  for(unsigned int i = 0; i < forwardSteps; i++)
1280  s = nodeName.find('/', s) + 1;
1281 
1282  return getNode(nodeName.substr(0, s));
1283 } // end getForwardNode()
1284 
1285 //==============================================================================
1286 // isValueNode
1287 // if true, then this is a leaf node, i.e. there can be no children, only a value
1288 bool ConfigurationTree::isValueNode(void) const
1289 {
1290  return (row_ != TableView::INVALID && col_ != TableView::INVALID);
1291 } // end isValueNode()
1292 
1293 //==============================================================================
1294 // isValueBoolType
1295 // if true, then this is a leaf node with BOOL type
1296 bool ConfigurationTree::isValueBoolType(void) const
1297 {
1298  return isValueNode() && tableView_->getColumnInfo(col_).isBoolType();
1299 } // end isValueBoolType()
1300 
1301 //==============================================================================
1302 // isValueNumberDataType
1303 // if true, then this is a leaf node with NUMBER data type
1304 bool ConfigurationTree::isValueNumberDataType(void) const
1305 {
1306  return isValueNode() && tableView_->getColumnInfo(col_).isNumberDataType();
1307 } // end isValueBoolType()
1308 
1309 //==============================================================================
1310 // isDisconnected
1311 // if true, then this is a disconnected node, i.e. there is a configuration link missing
1312 // (this is possible when the configuration is loaded in stages and the complete tree
1313 // may not be available, yet)
1314 bool ConfigurationTree::isDisconnected(void) const
1315 {
1316  if(!isLinkNode())
1317  {
1318  __SS__ << "\n\nError occurred testing link connection at node with value '"
1319  << getValue() << "' in table '" << getTableName() << "'\n\n"
1320  << __E__;
1321  ss << "This is not a Link node! It is node type '" << getNodeType()
1322  << ".' Only a Link node can be disconnected." << __E__;
1323 
1324  ss << nodeDump() << __E__;
1325  __SS_ONLY_THROW__;
1326  }
1327 
1328  return !table_ || !tableView_;
1329 } // end isDisconnected()
1330 
1331 //==============================================================================
1332 // isLinkNode
1333 // if true, then this is a link node
1334 bool ConfigurationTree::isLinkNode(void) const { return linkColName_ != ""; }
1335 
1336 //==============================================================================
1337 // getNodeType
1338 // return node type as string
1339 const std::string ConfigurationTree::NODE_TYPE_GROUP_TABLE = "GroupTableNode";
1340 const std::string ConfigurationTree::NODE_TYPE_TABLE = "TableNode";
1341 const std::string ConfigurationTree::NODE_TYPE_GROUP_LINK = "GroupLinkNode";
1342 const std::string ConfigurationTree::NODE_TYPE_UID_LINK = "UIDLinkNode";
1343 const std::string ConfigurationTree::NODE_TYPE_VALUE = "ValueNode";
1344 const std::string ConfigurationTree::NODE_TYPE_UID = "UIDNode";
1345 const std::string ConfigurationTree::NODE_TYPE_ROOT = "RootNode";
1346 
1347 std::string ConfigurationTree::getNodeType(void) const
1348 {
1349  if(!table_)
1350  return ConfigurationTree::NODE_TYPE_ROOT;
1351  if(isConfigurationNode() && groupId_ != "")
1352  return ConfigurationTree::NODE_TYPE_GROUP_TABLE;
1353  if(isConfigurationNode())
1354  return ConfigurationTree::NODE_TYPE_TABLE;
1355  if(isGroupLinkNode())
1356  return ConfigurationTree::NODE_TYPE_GROUP_LINK;
1357  if(isLinkNode())
1358  return ConfigurationTree::NODE_TYPE_UID_LINK;
1359  if(isValueNode())
1360  return ConfigurationTree::NODE_TYPE_VALUE;
1361  return ConfigurationTree::NODE_TYPE_UID;
1362 } // end getNodeType()
1363 
1364 //==============================================================================
1365 // isGroupLinkNode
1366 // if true, then this is a group link node
1367 bool ConfigurationTree::isGroupLinkNode(void) const
1368 {
1369  return (isLinkNode() && groupId_ != "");
1370 }
1371 
1372 //==============================================================================
1373 // isUIDLinkNode
1374 // if true, then this is a uid link node
1375 bool ConfigurationTree::isUIDLinkNode(void) const
1376 {
1377  return (isLinkNode() && groupId_ == "");
1378 }
1379 
1380 //==============================================================================
1381 // isUIDNode
1382 // if true, then this is a uid node
1383 bool ConfigurationTree::isUIDNode(void) const
1384 {
1385  return (row_ != TableView::INVALID && col_ == TableView::INVALID);
1386 }
1387 
1388 //==============================================================================
1389 // getCommonFields
1390 // wrapper for ...recursiveGetCommonFields
1391 //
1392 // returns common fields in order encountered
1393 // including through UID links depending on depth specified
1394 //
1395 // Field := {Table, UID, Column Name, Relative Path, TableViewColumnInfo}
1396 //
1397 // if fieldAcceptList or fieldRejectList are not empty,
1398 // then reject any that are not in field accept filter list
1399 // and reject any that are in field reject filter list
1400 //
1401 // will only go to specified depth looking for fields
1402 // (careful to prevent infinite loops in tree navigation)
1403 //
1404 std::vector<ConfigurationTree::RecordField> ConfigurationTree::getCommonFields(
1405  const std::vector<std::string /*uid*/>& recordList,
1406  const std::vector<std::string /*relative-path*/>& fieldAcceptList,
1407  const std::vector<std::string /*relative-path*/>& fieldRejectList,
1408  unsigned int depth,
1409  bool autoSelectFilterFields) const
1410 {
1411  // enforce that starting point is a table node
1412  if(!isRootNode() && !isConfigurationNode())
1413  {
1414  __SS__ << "Can only get getCommonFields from a root or table node! "
1415  << "The node type is " << getNodeType() << __E__;
1416 
1417  ss << nodeDump() << __E__;
1418  __SS_THROW__;
1419  }
1420 
1421  std::vector<ConfigurationTree::RecordField> fieldCandidateList;
1422  std::vector<int> fieldCount; //-1 := guaranteed, else count must match num of records
1423 
1424  --depth; // decrement for recursion
1425 
1426  // for each record in <record list>
1427  // loop through all record's children
1428  // if isValueNode (value nodes are possible field candidates!)
1429  // if first uid record
1430  // add field to <field candidates list> if in <field filter list>
1431  // mark <field count> as guaranteed -1 (all these fields must be common
1432  // for UIDs in same table)
1433  // else not first uid record, do not need to check, must be same as first
1434  // record! else if depth > 0 and UID-Link Node recursively (call
1435  // recursiveGetCommonFields())
1436  // =====================
1437  // Start recursiveGetCommonFields()
1438  // --depth;
1439  // loop through all children
1440  // if isValueNode (value nodes are possible field candidates!)
1441  // if first uid record
1442  // add field to <field candidates list> if in <field
1443  // filter list> initial mark <field count> as 1
1444  // else
1445  // if field is in <field candidates list>,
1446  // increment <field count> for field candidate
1447  // else if field is not in list, ignore field
1448  // else if depth > 0 and is UID-Link
1449  // if Link Table/UID pair is not found in <field candidates
1450  // list> (avoid endless loops through tree)
1451  // recursiveGetCommonFields()
1452  // =====================
1453  //
1454  //
1455  // loop through all field candidates
1456  // remove those with <field count> != num of records
1457  //
1458  //
1459  // return result
1460 
1461  bool found; // used in loops
1462  // auto tableName = isRootNode()?"/":getTableName(); //all records will share this
1463  // table name
1464 
1465  for(unsigned int i = 0; i < recordList.size(); ++i)
1466  {
1467  //__COUT__ << "Checking " << recordList[i] << __E__;
1468 
1469  auto recordChildren = getNode(recordList[i]).getChildren();
1470  for(const auto& fieldNode : recordChildren)
1471  {
1472  // __COUT__ << "All... " << fieldNode.second.getNodeType() <<
1473  // " -- " << fieldNode.first << __E__;
1474 
1475  if(fieldNode.second.isValueNode() || fieldNode.second.isGroupLinkNode())
1476  {
1477  // skip author and record insertion time
1478  if(fieldNode.second.isValueNode())
1479  {
1480  if(fieldNode.second.getColumnInfo().getType() ==
1481  TableViewColumnInfo::TYPE_AUTHOR ||
1482  fieldNode.second.getColumnInfo().getType() ==
1483  TableViewColumnInfo::TYPE_TIMESTAMP)
1484  continue;
1485 
1486  //__COUT__ << "isValueNode " << fieldNode.first << __E__;
1487  }
1488 
1489  if(!i) // first uid record
1490  {
1491  // check field accept filter list
1492  found = fieldAcceptList.size() ? false
1493  : true; // accept if no filter list
1494  for(const auto& fieldFilter : fieldAcceptList)
1495  if(StringMacros::wildCardMatch(fieldFilter, fieldNode.first))
1496  {
1497  found = true;
1498  break;
1499  }
1500 
1501  if(found)
1502  {
1503  // check field reject filter list
1504 
1505  found = true; // accept if no filter list
1506  for(const auto& fieldFilter : fieldRejectList)
1507  if(StringMacros::wildCardMatch(fieldFilter, fieldNode.first))
1508  {
1509  found = false; // reject if match
1510  break;
1511  }
1512  }
1513 
1514  // if found, guaranteed field (all these fields must be common for
1515  // UIDs in same table)
1516  if(found)
1517  {
1518  if(fieldNode.second.isGroupLinkNode())
1519  {
1520  __COUT__ << "isGroupLinkNode " << fieldNode.first << __E__;
1521 
1522  // must get column info differently for group link column
1523 
1524  std::pair<unsigned int /*link col*/,
1525  unsigned int /*link id col*/>
1526  linkPair;
1527  bool isGroupLink;
1528  tableView_->getChildLink(tableView_->findCol(fieldNode.first),
1529  isGroupLink,
1530  linkPair);
1531 
1532  // add both link columns
1533 
1534  fieldCandidateList.push_back(ConfigurationTree::RecordField(
1535  table_->getTableName(),
1536  recordList[i],
1537  tableView_->getColumnInfo(linkPair.first).getName(),
1538  "", // relative path, not including columnName_
1539  &tableView_->getColumnInfo(linkPair.first)));
1540  fieldCount.push_back(-1); // mark guaranteed field
1541 
1542  fieldCandidateList.push_back(ConfigurationTree::RecordField(
1543  table_->getTableName(),
1544  recordList[i],
1545  tableView_->getColumnInfo(linkPair.second).getName(),
1546  "", // relative path, not including columnName_
1547  &tableView_->getColumnInfo(linkPair.second)));
1548  fieldCount.push_back(-1); // mark guaranteed field
1549  }
1550  else // value node
1551  {
1552  fieldCandidateList.push_back(ConfigurationTree::RecordField(
1553  fieldNode.second.getTableName(),
1554  recordList[i],
1555  fieldNode.first,
1556  "", // relative path, not including columnName_
1557  &fieldNode.second.getColumnInfo()));
1558  fieldCount.push_back(-1); // mark guaranteed field
1559  }
1560  }
1561  }
1562  // else // not first uid record, do not need to check, must be same as
1563  // first record!
1564  } // end value field handling
1565  else if(depth > 0 && fieldNode.second.isUIDLinkNode() &&
1566  !fieldNode.second.isDisconnected())
1567  {
1568  //__COUT__ << "isUIDLinkNode " << fieldNode.first << __E__;
1569  fieldNode.second.recursiveGetCommonFields(
1570  fieldCandidateList,
1571  fieldCount,
1572  fieldAcceptList,
1573  fieldRejectList,
1574  depth,
1575  fieldNode.first + "/", // relativePathBase
1576  !i // launch inFirstRecord (or not) depth search
1577  );
1578  } // end unique link handling
1579  }
1580  } // end record loop
1581 
1582  //__COUT__ << "======================= check for count = " <<
1583  // (int)recordList.size() << __E__;
1584 
1585  // loop through all field candidates
1586  // remove those with <field count> != num of records
1587  for(unsigned int i = 0; i < fieldCandidateList.size(); ++i)
1588  {
1589  //__COUT__ << "Checking " << fieldCandidateList[i].relativePath_ <<
1590  // fieldCandidateList[i].columnName_ << " = " <<
1591  // fieldCount[i] << __E__;
1592  if(fieldCount[i] != -1 && fieldCount[i] != (int)recordList.size())
1593  {
1594  //__COUT__ << "Erasing " << fieldCandidateList[i].relativePath_ <<
1595  // fieldCandidateList[i].columnName_ << __E__;
1596 
1597  fieldCount.erase(fieldCount.begin() + i);
1598  fieldCandidateList.erase(fieldCandidateList.begin() + i);
1599  --i; // rewind to look at next after deleted
1600  }
1601  }
1602 
1603  // for(unsigned int i=0;i<fieldCandidateList.size();++i)
1604  // __COUT__ << "Pre-Final " << fieldCandidateList[i].relativePath_ <<
1605  // fieldCandidateList[i].columnName_ << __E__;
1606 
1607  if(autoSelectFilterFields)
1608  {
1609  // filter for just 3 of the best filter fields
1610  // i.e. preference for GroupID, On/Off, and FixedChoice fields.
1611  std::set<std::pair<unsigned int /*fieldPriority*/, unsigned int /*fieldIndex*/>>
1612  prioritySet;
1613 
1614  unsigned int highestPriority = 0;
1615  for(unsigned int i = 0; i < fieldCandidateList.size(); ++i)
1616  {
1617  // __COUT__ << "Option " << fieldCandidateList[i].relativePath_
1618  //<< fieldCandidateList[i].columnName_ << " : " <<
1619  // fieldCandidateList[i].columnInfo_->getType() << ":" <<
1620  // fieldCandidateList[i].columnInfo_->getDataType() <<
1621  //__E__;
1622 
1623  if(fieldCandidateList[i].columnInfo_->isBoolType())
1624  prioritySet.emplace(
1625  std::make_pair(0 /*fieldPriority*/, i /*fieldIndex*/));
1626  else if(fieldCandidateList[i].columnInfo_->isGroupID())
1627  {
1628  prioritySet.emplace(
1629  std::make_pair(1 /*fieldPriority*/, i /*fieldIndex*/));
1630  if(highestPriority < 1)
1631  highestPriority = 1;
1632  }
1633  else if(fieldCandidateList[i].columnInfo_->getType() ==
1634  TableViewColumnInfo::TYPE_FIXED_CHOICE_DATA)
1635  {
1636  prioritySet.emplace(
1637  std::make_pair(3 /*fieldPriority*/, i /*fieldIndex*/));
1638  if(highestPriority < 3)
1639  highestPriority = 3;
1640  }
1641  else if(fieldCandidateList[i].columnInfo_->getType() ==
1642  TableViewColumnInfo::TYPE_DATA)
1643  {
1644  prioritySet.emplace(
1645  std::make_pair(10 /*fieldPriority*/, i /*fieldIndex*/));
1646  if(highestPriority < 10)
1647  highestPriority = 10;
1648  }
1649  else // skip other fields and mark for erasing
1650  {
1651  fieldCandidateList[i].tableName_ =
1652  ""; // clear table name as indicator for erase
1653  continue;
1654  }
1655 
1656  } // done ranking fields
1657 
1658  __COUTV__(StringMacros::setToString(prioritySet));
1659 
1660  // now choose the top 3, and delete the rest
1661  // clear table name to indicate field should be erased
1662  {
1663  unsigned int cnt = 0;
1664  for(const auto& priorityFieldIndex : prioritySet)
1665  if(++cnt > 3) // then mark for erasing
1666  {
1667  // __COUT__ << cnt << " marking " <<
1668  //fieldCandidateList[
1669  // priorityFieldIndex.second].relativePath_
1670  //<<
1671  // fieldCandidateList[priorityFieldIndex.second].columnName_
1672  //<<
1673  // __E__;
1674  fieldCandidateList[priorityFieldIndex.second].tableName_ =
1675  ""; // clear table name as indicator for erase
1676  }
1677  }
1678 
1679  for(unsigned int i = 0; i < fieldCandidateList.size(); ++i)
1680  {
1681  if(fieldCandidateList[i].tableName_ == "") // then erase
1682  {
1683  // __COUT__ << "Erasing " << fieldCandidateList[i].relativePath_
1684  //<< fieldCandidateList[i].columnName_ << __E__;
1685  fieldCandidateList.erase(fieldCandidateList.begin() + i);
1686  --i; // rewind to look at next after deleted
1687  }
1688  }
1689  } // end AUTO filter field selection
1690 
1691  for(unsigned int i = 0; i < fieldCandidateList.size(); ++i)
1692  __COUT__ << "Final " << fieldCandidateList[i].relativePath_
1693  << fieldCandidateList[i].columnName_ << __E__;
1694 
1695  return fieldCandidateList;
1696 } // end getCommonFields()
1697 
1698 //==============================================================================
1699 // getUniqueValuesForField
1700 //
1701 // returns sorted unique values for the specified records and field
1702 //
1703 std::set<std::string /*unique-value*/> ConfigurationTree::getUniqueValuesForField(
1704  const std::vector<std::string /*relative-path*/>& recordList,
1705  const std::string& fieldName) const
1706 {
1707  // enforce that starting point is a table node
1708  if(!isConfigurationNode())
1709  {
1710  __SS__ << "Can only get getCommonFields from a table node! "
1711  << "The node type is " << getNodeType() << __E__;
1712 
1713  ss << nodeDump() << __E__;
1714  __SS_THROW__;
1715  }
1716 
1717  std::set<std::string /*unique-value*/> uniqueValues;
1718 
1719  // for each record in <record list>
1720  // emplace value at field into set
1721  //
1722  // return result
1723 
1724  for(unsigned int i = 0; i < recordList.size(); ++i)
1725  {
1726  __COUT__ << "Checking " << recordList[i] << __E__;
1727 
1728  // Note: that ConfigurationTree maps both fields associated with a link
1729  // to the same node instance.
1730  // The behavior is likely not expected as response for this function..
1731  // so for links return actual value for field name specified
1732  // i.e. if Table of link is requested give that; if linkID is requested give
1733  // that. use TRUE in getValueAsString for proper behavior
1734  uniqueValues.emplace(
1735  getNode(recordList[i]).getNode(fieldName).getValueAsString(true));
1736  }
1737 
1738  return uniqueValues;
1739 } // end getUniqueValuesForField()
1740 
1741 //==============================================================================
1742 // recursiveGetCommonFields
1743 // wrapper is ...getCommonFields
1744 void ConfigurationTree::recursiveGetCommonFields(
1745  std::vector<ConfigurationTree::RecordField>& fieldCandidateList,
1746  std::vector<int>& fieldCount,
1747  const std::vector<std::string /*relative-path*/>& fieldAcceptList,
1748  const std::vector<std::string /*relative-path*/>& fieldRejectList,
1749  unsigned int depth,
1750  const std::string& relativePathBase,
1751  bool inFirstRecord) const
1752 {
1753  --depth;
1754  //__COUT__ << "relativePathBase " << relativePathBase << __E__;
1755 
1756  // =====================
1757  // Start recursiveGetCommonFields()
1758  // --depth;
1759  // loop through all children
1760  // if isValueNode (value nodes are possible field candidates!)
1761  // if first uid record
1762  // add field to <field candidates list> if in <field filter list>
1763  // initial mark <field count> as 1
1764  // else
1765  // if field is in list,
1766  // increment count for field candidate
1767  // //?increment fields in list count for record
1768  // else if field is not in list, discard field
1769  // else if depth > 0 and is UID-Link
1770  // if Link Table/UID pair is not found in <field candidates list> (avoid
1771  // endless loops through tree) recursiveGetCommonFields()
1772  // =====================
1773 
1774  bool found; // used in loops
1775  auto tableName = getTableName(); // all fields will share this table name
1776  auto uid = getUIDAsString(); // all fields will share this uid
1777  unsigned int j;
1778 
1779  auto recordChildren = getChildren();
1780  for(const auto& fieldNode : recordChildren)
1781  {
1782  if(fieldNode.second.isValueNode())
1783  {
1784  // skip author and record insertion time
1785  if(fieldNode.second.getColumnInfo().getType() ==
1786  TableViewColumnInfo::TYPE_AUTHOR ||
1787  fieldNode.second.getColumnInfo().getType() ==
1788  TableViewColumnInfo::TYPE_TIMESTAMP)
1789  continue;
1790 
1791  //__COUT__ << "isValueNode " << fieldNode.first << __E__;
1792  if(inFirstRecord) // first uid record
1793  {
1794  // check field accept filter list
1795  found = fieldAcceptList.size() ? false : true; // accept if no filter
1796  // list
1797  for(const auto& fieldFilter : fieldAcceptList)
1798  if(StringMacros::wildCardMatch(fieldFilter, fieldNode.first))
1799  {
1800  found = true;
1801  break;
1802  }
1803 
1804  if(found)
1805  {
1806  // check field reject filter list
1807 
1808  found = true; // accept if no filter list
1809  for(const auto& fieldFilter : fieldRejectList)
1810  if(StringMacros::wildCardMatch(fieldFilter, fieldNode.first))
1811  {
1812  found = false; // reject if match
1813  break;
1814  }
1815  }
1816 
1817  // if found, new field (since this is first record)
1818  if(found)
1819  {
1820  //__COUT__ << "FOUND field " <<
1821  // (relativePathBase + fieldNode.first) << __E__;
1822  fieldCandidateList.push_back(ConfigurationTree::RecordField(
1823  tableName,
1824  uid,
1825  fieldNode.first,
1826  relativePathBase, // relative path, not including columnName_
1827  &fieldNode.second.getColumnInfo()));
1828  fieldCount.push_back(1); // init count to 1
1829  }
1830  }
1831  else // not first record
1832  {
1833  // if field is in <field candidates list>, increment <field count>
1834  // else ignore
1835  for(j = 0; j < fieldCandidateList.size(); ++j)
1836  {
1837  if((relativePathBase + fieldNode.first) ==
1838  (fieldCandidateList[j].relativePath_ +
1839  fieldCandidateList[j].columnName_))
1840  {
1841  //__COUT__ << "incrementing " << j <<
1842  // " " << fieldCandidateList[j].relativePath_ << __E__;
1843  // found, so increment <field count>
1844  ++fieldCount[j];
1845  break;
1846  }
1847  }
1848  }
1849  }
1850  else if(depth > 0 && fieldNode.second.isUIDLinkNode() &&
1851  !fieldNode.second.isDisconnected())
1852  {
1853  //__COUT__ << "isUIDLinkNode " << (relativePathBase + fieldNode.first) <<
1854  // __E__;
1855  fieldNode.second.recursiveGetCommonFields(
1856  fieldCandidateList,
1857  fieldCount,
1858  fieldAcceptList,
1859  fieldRejectList,
1860  depth,
1861  (relativePathBase + fieldNode.first) + "/", // relativePathBase
1862  inFirstRecord // continue inFirstRecord (or not) depth search
1863  );
1864  }
1865  }
1866 } // end recursiveGetCommonFields()
1867 
1868 //==============================================================================
1869 // getChildrenByPriority
1870 // returns them in order encountered in the table
1871 // if filterMap criteria, then rejects any that do not meet all criteria
1872 //
1873 // value can be comma-separated for OR of multiple values
1874 std::vector<std::vector<std::pair<std::string, ConfigurationTree>>>
1875 ConfigurationTree::getChildrenByPriority(
1876  std::map<std::string /*relative-path*/, std::string /*value*/> filterMap,
1877  bool onlyStatusTrue) const
1878 {
1879  std::vector<std::vector<std::pair<std::string, ConfigurationTree>>> retVector;
1880 
1881  //__COUT__ << "Children of node: " << getValueAsString() << __E__;
1882 
1883  bool filtering = filterMap.size();
1884  bool skip;
1885  std::string fieldValue;
1886 
1887  bool createContainer;
1888 
1889  std::vector<std::vector<std::string>> childrenNamesByPriority =
1890  getChildrenNamesByPriority(onlyStatusTrue);
1891 
1892  for(auto& childNamesAtPriority : childrenNamesByPriority)
1893  {
1894  createContainer = true;
1895 
1896  for(auto& childName : childNamesAtPriority)
1897  {
1898  //__COUT__ << "\tChild: " << childName << __E__;
1899 
1900  if(filtering)
1901  {
1902  // if all criteria are not met, then skip
1903  skip = false;
1904 
1905  // for each filter, check value
1906  for(const auto& filterPair : filterMap)
1907  {
1908  std::string filterPath = childName + "/" + filterPair.first;
1909  __COUTV__(filterPath);
1910  try
1911  {
1912  // extract field value list
1913  std::vector<std::string> fieldValues;
1914  StringMacros::getVectorFromString(
1915  filterPair.second,
1916  fieldValues,
1917  std::set<char>({','}) /*delimiters*/);
1918 
1919  __COUTV__(fieldValues.size());
1920 
1921  skip = true;
1922  // for each field check if any match
1923  for(const auto& fieldValue : fieldValues)
1924  {
1925  // Note: that ConfigurationTree maps both fields associated
1926  // with a link to the same node instance. The behavior is
1927  // likely not expected as response for this function.. so for
1928  // links return actual value for field name specified i.e.
1929  // if Table of link is requested give that; if linkID is
1930  // requested give that. use TRUE in getValueAsString for
1931  // proper
1932  // behavior
1933 
1934  __COUT__ << "\t\tCheck: " << filterPair.first
1935  << " == " << fieldValue << " => "
1936  << StringMacros::decodeURIComponent(fieldValue)
1937  << " ??? "
1938  << this->getNode(filterPath).getValueAsString(true)
1939  << __E__;
1940 
1941  if(StringMacros::wildCardMatch(
1942  StringMacros::decodeURIComponent(fieldValue),
1943  this->getNode(filterPath).getValueAsString(true)))
1944  {
1945  // found a match for the field/value pair
1946  skip = false;
1947  break;
1948  }
1949  }
1950  }
1951  catch(...)
1952  {
1953  __SS__ << "Failed to access filter path '" << filterPath
1954  << "' - aborting." << __E__;
1955 
1956  ss << nodeDump() << __E__;
1957  __SS_THROW__;
1958  }
1959 
1960  if(skip)
1961  break; // no match for this field, so stop checking and skip this
1962  // record
1963  }
1964 
1965  if(skip)
1966  continue; // skip this record
1967 
1968  //__COUT__ << "\tChild accepted: " << childName << __E__;
1969  }
1970 
1971  if(createContainer)
1972  {
1973  retVector.push_back(
1974  std::vector<std::pair<std::string, ConfigurationTree>>());
1975  createContainer = false;
1976  }
1977 
1978  retVector[retVector.size() - 1].push_back(
1979  std::pair<std::string, ConfigurationTree>(
1980  childName, this->getNode(childName, true)));
1981  } // end children within priority loop
1982  } // end children by priority loop
1983 
1984  //__COUT__ << "Done w/Children of node: " << getValueAsString() << __E__;
1985  return retVector;
1986 } // end getChildrenByPriority()
1987 
1988 //==============================================================================
1989 // getChildren
1990 // returns them in order encountered in the table
1991 // if filterMap criteria, then rejects any that do not meet all criteria
1992 //
1993 // value can be comma-separated for OR of multiple values
1994 std::vector<std::pair<std::string, ConfigurationTree>> ConfigurationTree::getChildren(
1995  std::map<std::string /*relative-path*/, std::string /*value*/> filterMap,
1996  bool byPriority,
1997  bool onlyStatusTrue) const
1998 {
1999  std::vector<std::pair<std::string, ConfigurationTree>> retVector;
2000 
2001  //__COUT__ << "Children of node: " << getValueAsString() << __E__;
2002 
2003  bool filtering = filterMap.size();
2004  bool skip;
2005  std::string fieldValue;
2006 
2007  std::vector<std::string> childrenNames = getChildrenNames(byPriority, onlyStatusTrue);
2008  for(auto& childName : childrenNames)
2009  {
2010  //__COUT__ << "\tChild: " << childName << __E__;
2011 
2012  if(filtering)
2013  {
2014  // if all criteria are not met, then skip
2015  skip = false;
2016 
2017  // for each filter, check value
2018  for(const auto& filterPair : filterMap)
2019  {
2020  std::string filterPath = childName + "/" + filterPair.first;
2021  __COUTV__(filterPath);
2022  try
2023  {
2024  // extract field value list
2025  std::vector<std::string> fieldValues;
2026  StringMacros::getVectorFromString(
2027  filterPair.second,
2028  fieldValues,
2029  std::set<char>({','}) /*delimiters*/);
2030 
2031  __COUTV__(fieldValues.size());
2032 
2033  skip = true;
2034  // for each field check if any match
2035  for(const auto& fieldValue : fieldValues)
2036  {
2037  // Note: that ConfigurationTree maps both fields associated with a
2038  // link to the same node instance. The behavior is likely not
2039  // expected as response for this function.. so for links
2040  // return
2041  // actual value for field name specified i.e. if Table of link
2042  // is requested give that; if linkID is requested give that. use
2043  // TRUE in getValueAsString for proper behavior
2044 
2045  __COUT__
2046  << "\t\tCheck: " << filterPair.first << " == " << fieldValue
2047  << " => " << StringMacros::decodeURIComponent(fieldValue)
2048  << " ??? " << this->getNode(filterPath).getValueAsString(true)
2049  << __E__;
2050 
2051  if(StringMacros::wildCardMatch(
2052  StringMacros::decodeURIComponent(fieldValue),
2053  this->getNode(filterPath).getValueAsString(true)))
2054  {
2055  // found a match for the field/value pair
2056  skip = false;
2057  break;
2058  }
2059  }
2060  }
2061  catch(...)
2062  {
2063  __SS__ << "Failed to access filter path '" << filterPath
2064  << "' - aborting." << __E__;
2065 
2066  ss << nodeDump() << __E__;
2067  __SS_THROW__;
2068  }
2069 
2070  if(skip)
2071  break; // no match for this field, so stop checking and skip this
2072  // record
2073  }
2074 
2075  if(skip)
2076  continue; // skip this record
2077 
2078  //__COUT__ << "\tChild accepted: " << childName << __E__;
2079  }
2080 
2081  retVector.push_back(std::pair<std::string, ConfigurationTree>(
2082  childName, this->getNode(childName, true)));
2083  }
2084 
2085  //__COUT__ << "Done w/Children of node: " << getValueAsString() << __E__;
2086  return retVector;
2087 } // end getChildren()
2088 
2089 //==============================================================================
2090 // getChildren
2091 // returns them in order encountered in the table
2092 std::map<std::string, ConfigurationTree> ConfigurationTree::getChildrenMap(void) const
2093 {
2094  std::map<std::string, ConfigurationTree> retMap;
2095 
2096  //__COUT__ << "Children of node: " << getValueAsString() << __E__;
2097 
2098  std::vector<std::string> childrenNames = getChildrenNames();
2099  for(auto& childName : childrenNames)
2100  {
2101  //__COUT__ << "\tChild: " << childName << __E__;
2102  retMap.insert(std::pair<std::string, ConfigurationTree>(
2103  childName, this->getNode(childName)));
2104  }
2105 
2106  //__COUT__ << "Done w/Children of node: " << getValueAsString() << __E__;
2107  return retMap;
2108 } // end getChildrenMap()
2109 
2110 //==============================================================================
2111 bool ConfigurationTree::isRootNode(void) const { return (!table_); }
2112 
2113 //==============================================================================
2114 bool ConfigurationTree::isConfigurationNode(void) const
2115 {
2116  return (table_ && row_ == TableView::INVALID && col_ == TableView::INVALID);
2117 }
2118 
2119 //==============================================================================
2120 // getChildrenNamesByPriority
2121 // returns them in priority order encountered in the table
2122 std::vector<std::vector<std::string>> ConfigurationTree::getChildrenNamesByPriority(
2123  bool onlyStatusTrue) const
2124 {
2125  std::vector<std::vector<std::string /*child name*/>> retVector;
2126 
2127  if(!tableView_)
2128  {
2129  __SS__ << "Can not get children names of '" << getValueAsString()
2130  << "' with null configuration view pointer!" << __E__;
2131  if(isLinkNode() && isDisconnected())
2132  ss << " This node is a disconnected link to " << getDisconnectedTableName()
2133  << __E__;
2134 
2135  ss << nodeDump() << __E__;
2136  __SS_ONLY_THROW__;
2137  }
2138 
2139  if(row_ == TableView::INVALID && col_ == TableView::INVALID)
2140  {
2141  // this node is config node
2142  // so return all uid node strings that match groupId
2143 
2144  bool tmpStatus;
2145 
2146  if(1) // reshuffle by priority
2147  {
2148  try
2149  {
2150  std::map<uint64_t /*priority*/, std::vector<unsigned int /*child row*/>>
2151  orderedByPriority;
2152  std::vector<std::string /*child name*/> retPrioritySet;
2153 
2154  unsigned int col = tableView_->getColPriority();
2155 
2156  uint64_t tmpPriority;
2157 
2158  for(unsigned int r = 0; r < tableView_->getNumberOfRows(); ++r)
2159  if(groupId_ == "" ||
2160  tableView_->isEntryInGroup(r, childLinkIndex_, groupId_))
2161  {
2162  // check status if needed
2163  if(onlyStatusTrue)
2164  {
2165  tableView_->getValue(
2166  tmpStatus, r, tableView_->getColStatus());
2167  if(!tmpStatus)
2168  continue; // skip those with status false
2169  }
2170 
2171  tableView_->getValue(tmpPriority, r, col);
2172  // do not accept DEFAULT value of 0.. convert to 100
2173  orderedByPriority[tmpPriority ? tmpPriority : 100].push_back(r);
2174  }
2175 
2176  // at this point have priority map
2177  // now build return vector
2178 
2179  for(const auto& priorityChildRowVector : orderedByPriority)
2180  {
2181  retVector.push_back(std::vector<std::string /*child name*/>());
2182  for(const auto& priorityChildRow : priorityChildRowVector.second)
2183  retVector[retVector.size() - 1].push_back(
2184  tableView_->getDataView()[priorityChildRow]
2185  [tableView_->getColUID()]);
2186  }
2187 
2188  __COUT__ << "Returning priority children list." << __E__;
2189  return retVector;
2190  }
2191  catch(std::runtime_error& e)
2192  {
2193  __COUT_WARN__ << "Error identifying priority. Assuming all children have "
2194  "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(std::vector<std::string /*child name*/>());
2213  retVector[retVector.size() - 1].push_back(
2214  tableView_->getDataView()[r][tableView_->getColUID()]);
2215  }
2216  }
2217  else if(row_ == TableView::INVALID)
2218  {
2219  __SS__ << "Malformed ConfigurationTree" << __E__;
2220 
2221  ss << nodeDump() << __E__;
2222  __SS_THROW__;
2223  }
2224  else if(col_ == TableView::INVALID)
2225  {
2226  // this node is uid node
2227  // so return all link and value nodes
2228 
2229  for(unsigned int c = 0; c < tableView_->getNumberOfColumns(); ++c)
2230  if(c == tableView_->getColUID() || // skip UID and linkID columns (only show
2231  // link column, to avoid duplicates)
2232  tableView_->getColumnInfo(c).isChildLinkGroupID() ||
2233  tableView_->getColumnInfo(c).isChildLinkUID())
2234  continue;
2235  else
2236  {
2237  retVector.push_back(std::vector<std::string /*child name*/>());
2238  retVector[retVector.size() - 1].push_back(
2239  tableView_->getColumnInfo(c).getName());
2240  }
2241  }
2242  else // this node is value node, so has no node to choose from
2243  {
2244  // this node is value node, cant go any deeper!
2245  __SS__ << "\n\nError occurred looking for children of nodeName=" << getValueName()
2246  << "\n\n"
2247  << "Invalid depth! getChildrenValues() called from a value point in the "
2248  "Configuration Tree."
2249  << __E__;
2250 
2251  ss << nodeDump() << __E__;
2252  __SS_THROW__;
2253  }
2254 
2255  return retVector;
2256 } // end getChildrenNamesByPriority()
2257 
2258 //==============================================================================
2259 // getChildrenNames
2260 // returns them in order encountered in the table
2261 std::vector<std::string> ConfigurationTree::getChildrenNames(bool byPriority,
2262  bool onlyStatusTrue) const
2263 {
2264  std::vector<std::string /*child name*/> retVector;
2265 
2266  if(!tableView_)
2267  {
2268  __SS__ << "Can not get children names of '" << getValueAsString()
2269  << "' with null configuration view pointer!" << __E__;
2270  if(isLinkNode() && isDisconnected())
2271  ss << " This node is a disconnected link to " << getDisconnectedTableName()
2272  << __E__;
2273  __SS_ONLY_THROW__;
2274  }
2275 
2276  if(row_ == TableView::INVALID && col_ == TableView::INVALID)
2277  {
2278  // this node is config node
2279  // so return all uid node strings that match groupId
2280 
2281  bool tmpStatus;
2282 
2283  if(byPriority) // reshuffle by priority
2284  {
2285  try
2286  {
2287  std::map<uint64_t /*priority*/, std::vector<unsigned int /*child row*/>>
2288  orderedByPriority;
2289  std::vector<std::string /*child name*/> retPrioritySet;
2290 
2291  unsigned int col = tableView_->getColPriority();
2292 
2293  uint64_t tmpPriority;
2294 
2295  for(unsigned int r = 0; r < tableView_->getNumberOfRows(); ++r)
2296  if(groupId_ == "" ||
2297  tableView_->isEntryInGroup(r, childLinkIndex_, groupId_))
2298  {
2299  // check status if needed
2300  if(onlyStatusTrue)
2301  {
2302  tableView_->getValue(
2303  tmpStatus, r, tableView_->getColStatus());
2304 
2305  if(!tmpStatus)
2306  continue; // skip those with status false
2307  }
2308 
2309  tableView_->getValue(tmpPriority, r, col);
2310  // do not accept DEFAULT value of 0.. convert to 100
2311  orderedByPriority[tmpPriority ? tmpPriority : 100].push_back(r);
2312  }
2313 
2314  // at this point have priority map
2315  // now build return vector
2316 
2317  for(const auto& priorityChildRowVector : orderedByPriority)
2318  for(const auto& priorityChildRow : priorityChildRowVector.second)
2319  retVector.push_back(
2320  tableView_->getDataView()[priorityChildRow]
2321  [tableView_->getColUID()]);
2322 
2323  __COUT__ << "Returning priority children list." << __E__;
2324  return retVector;
2325  }
2326  catch(std::runtime_error& e)
2327  {
2328  __COUT_WARN__ << "Priority configuration not found. Assuming all "
2329  "children have equal priority (Error: "
2330  << e.what() << __E__;
2331  retVector.clear();
2332  }
2333  }
2334  // else not by priority
2335 
2336  for(unsigned int r = 0; r < tableView_->getNumberOfRows(); ++r)
2337  if(groupId_ == "" || tableView_->isEntryInGroup(r, childLinkIndex_, groupId_))
2338  {
2339  // check status if needed
2340  if(onlyStatusTrue)
2341  {
2342  tableView_->getValue(tmpStatus, r, tableView_->getColStatus());
2343 
2344  if(!tmpStatus)
2345  continue; // skip those with status false
2346  }
2347 
2348  retVector.push_back(
2349  tableView_->getDataView()[r][tableView_->getColUID()]);
2350  }
2351  }
2352  else if(row_ == TableView::INVALID)
2353  {
2354  __SS__ << "Malformed ConfigurationTree" << __E__;
2355 
2356  ss << nodeDump() << __E__;
2357  __SS_THROW__;
2358  }
2359  else if(col_ == TableView::INVALID)
2360  {
2361  // this node is uid node
2362  // so return all link and value nodes
2363 
2364  for(unsigned int c = 0; c < tableView_->getNumberOfColumns(); ++c)
2365  if(c == tableView_->getColUID() || // skip UID and linkID columns (only show
2366  // link column, to avoid duplicates)
2367  tableView_->getColumnInfo(c).isChildLinkGroupID() ||
2368  tableView_->getColumnInfo(c).isChildLinkUID())
2369  continue;
2370  else
2371  retVector.push_back(tableView_->getColumnInfo(c).getName());
2372  }
2373  else // this node is value node, so has no node to choose from
2374  {
2375  // this node is value node, cant go any deeper!
2376  __SS__ << "\n\nError occurred looking for children of nodeName=" << getValueName()
2377  << "\n\n"
2378  << "Invalid depth! getChildrenValues() called from a value point in the "
2379  "Configuration Tree."
2380  << __E__;
2381 
2382  ss << nodeDump() << __E__;
2383  __SS_THROW__;
2384  }
2385 
2386  return retVector;
2387 } // end getChildrenNames()
2388 
2389 //==============================================================================
2390 // getValueAsTreeNode
2391 // returns tree node for value of this node, treating the value
2392 // as a string for the absolute path string from root of tree
2393 ConfigurationTree ConfigurationTree::getValueAsTreeNode(void) const
2394 {
2395  // check if first character is a /, .. if so try to get value in tree
2396  // if exception, just take value
2397  // note: this call will throw an error, in effect, if not a "value" node
2398  if(!tableView_)
2399  {
2400  __SS__ << "Invalid node for get value." << __E__;
2401  __SS_THROW__;
2402  }
2403 
2404  std::string valueString =
2405  tableView_->getValueAsString(row_, col_, true /* convertEnvironmentVariables */);
2406  //__COUT__ << valueString << __E__;
2407  if(valueString.size() && valueString[0] == '/')
2408  {
2409  //__COUT__ << "Starts with '/' - check if valid tree path: " << valueString <<
2410  // __E__;
2411  try
2412  {
2413  ConfigurationTree retNode = configMgr_->getNode(valueString);
2414  __COUT__ << "Found a valid tree path in value!" << __E__;
2415  return retNode;
2416  }
2417  catch(...)
2418  {
2419  __SS__ << "Invalid tree path." << __E__;
2420  __SS_ONLY_THROW__;
2421  }
2422  }
2423 
2424  {
2425  __SS__ << "Invalid value string '" << valueString
2426  << "' - must start with a '/' character." << __E__;
2427  __SS_ONLY_THROW__;
2428  }
2429 } // end getValueAsTreeNode()