otsdaq  v2_04_01
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  {
1567  //__COUT__ << "isUIDLinkNode " << fieldNode.first << __E__;
1568 
1569  // must get column info differently for UID link column
1570 
1571  // add link fields and recursively traverse for fields
1572  if(!i) // first uid record
1573  {
1574  // check field accept filter list
1575  found = fieldAcceptList.size() ? false
1576  : true; // accept if no filter list
1577  for(const auto& fieldFilter : fieldAcceptList)
1578  if(StringMacros::wildCardMatch(fieldFilter, fieldNode.first))
1579  {
1580  found = true;
1581  break;
1582  }
1583 
1584  if(found)
1585  {
1586  // check field reject filter list
1587 
1588  found = true; // accept if no filter list
1589  for(const auto& fieldFilter : fieldRejectList)
1590  if(StringMacros::wildCardMatch(fieldFilter, fieldNode.first))
1591  {
1592  found = false; // reject if match
1593  break;
1594  }
1595  }
1596 
1597  // if found, guaranteed field (all UID link fields must be common for
1598  // UIDs in same table)
1599  if(found)
1600  {
1601  std::pair<unsigned int /*link col*/, unsigned int /*link id col*/>
1602  linkPair;
1603  bool isGroupLink;
1604  tableView_->getChildLink(
1605  tableView_->findCol(fieldNode.first), isGroupLink, linkPair);
1606 
1607  // add both link columns
1608 
1609  fieldCandidateList.push_back(ConfigurationTree::RecordField(
1610  table_->getTableName(),
1611  recordList[i],
1612  tableView_->getColumnInfo(linkPair.first).getName(),
1613  "", // relative path, not including columnName_
1614  &tableView_->getColumnInfo(linkPair.first)));
1615  fieldCount.push_back(-1); // mark guaranteed field
1616 
1617  fieldCandidateList.push_back(ConfigurationTree::RecordField(
1618  table_->getTableName(),
1619  recordList[i],
1620  tableView_->getColumnInfo(linkPair.second).getName(),
1621  "", // relative path, not including columnName_
1622  &tableView_->getColumnInfo(linkPair.second)));
1623  fieldCount.push_back(-1); // mark guaranteed field
1624  }
1625  }
1626 
1627  // recursive field traversal is (currently) a bit different from first
1628  // level in that it does not add the link fields
1629  if(!fieldNode.second.isDisconnected())
1630  fieldNode.second.recursiveGetCommonFields(
1631  fieldCandidateList,
1632  fieldCount,
1633  fieldAcceptList,
1634  fieldRejectList,
1635  depth,
1636  fieldNode.first + "/", // relativePathBase
1637  !i // launch inFirstRecord (or not) depth search
1638  );
1639  } // end unique link handling
1640  }
1641  } // end record loop
1642 
1643  //__COUT__ << "======================= check for count = " <<
1644  // (int)recordList.size() << __E__;
1645 
1646  // loop through all field candidates
1647  // remove those with <field count> != num of records
1648  for(unsigned int i = 0; i < fieldCandidateList.size(); ++i)
1649  {
1650  //__COUT__ << "Checking " << fieldCandidateList[i].relativePath_ <<
1651  // fieldCandidateList[i].columnName_ << " = " <<
1652  // fieldCount[i] << __E__;
1653  if(fieldCount[i] != -1 && fieldCount[i] != (int)recordList.size())
1654  {
1655  //__COUT__ << "Erasing " << fieldCandidateList[i].relativePath_ <<
1656  // fieldCandidateList[i].columnName_ << __E__;
1657 
1658  fieldCount.erase(fieldCount.begin() + i);
1659  fieldCandidateList.erase(fieldCandidateList.begin() + i);
1660  --i; // rewind to look at next after deleted
1661  }
1662  }
1663 
1664  // for(unsigned int i=0;i<fieldCandidateList.size();++i)
1665  // __COUT__ << "Pre-Final " << fieldCandidateList[i].relativePath_ <<
1666  // fieldCandidateList[i].columnName_ << __E__;
1667 
1668  if(autoSelectFilterFields)
1669  {
1670  // filter for just 3 of the best filter fields
1671  // i.e. preference for GroupID, On/Off, and FixedChoice fields.
1672  std::set<std::pair<unsigned int /*fieldPriority*/, unsigned int /*fieldIndex*/>>
1673  prioritySet;
1674 
1675  unsigned int highestPriority = 0;
1676  for(unsigned int i = 0; i < fieldCandidateList.size(); ++i)
1677  {
1678  // __COUT__ << "Option " << fieldCandidateList[i].relativePath_
1679  //<< fieldCandidateList[i].columnName_ << " : " <<
1680  // fieldCandidateList[i].columnInfo_->getType() << ":" <<
1681  // fieldCandidateList[i].columnInfo_->getDataType() <<
1682  //__E__;
1683 
1684  if(fieldCandidateList[i].columnInfo_->isBoolType())
1685  prioritySet.emplace(
1686  std::make_pair(0 /*fieldPriority*/, i /*fieldIndex*/));
1687  else if(fieldCandidateList[i].columnInfo_->isGroupID())
1688  {
1689  prioritySet.emplace(
1690  std::make_pair(1 /*fieldPriority*/, i /*fieldIndex*/));
1691  if(highestPriority < 1)
1692  highestPriority = 1;
1693  }
1694  else if(fieldCandidateList[i].columnInfo_->getType() ==
1695  TableViewColumnInfo::TYPE_FIXED_CHOICE_DATA)
1696  {
1697  prioritySet.emplace(
1698  std::make_pair(3 /*fieldPriority*/, i /*fieldIndex*/));
1699  if(highestPriority < 3)
1700  highestPriority = 3;
1701  }
1702  else if(fieldCandidateList[i].columnInfo_->getType() ==
1703  TableViewColumnInfo::TYPE_DATA)
1704  {
1705  prioritySet.emplace(
1706  std::make_pair(10 /*fieldPriority*/, i /*fieldIndex*/));
1707  if(highestPriority < 10)
1708  highestPriority = 10;
1709  }
1710  else // skip other fields and mark for erasing
1711  {
1712  fieldCandidateList[i].tableName_ =
1713  ""; // clear table name as indicator for erase
1714  continue;
1715  }
1716 
1717  } // done ranking fields
1718 
1719  __COUTV__(StringMacros::setToString(prioritySet));
1720 
1721  // now choose the top 3, and delete the rest
1722  // clear table name to indicate field should be erased
1723  {
1724  unsigned int cnt = 0;
1725  for(const auto& priorityFieldIndex : prioritySet)
1726  if(++cnt > 3) // then mark for erasing
1727  {
1728  // __COUT__ << cnt << " marking " <<
1729  // fieldCandidateList[
1730  // priorityFieldIndex.second].relativePath_
1731  //<<
1732  // fieldCandidateList[priorityFieldIndex.second].columnName_
1733  //<<
1734  // __E__;
1735  fieldCandidateList[priorityFieldIndex.second].tableName_ =
1736  ""; // clear table name as indicator for erase
1737  }
1738  }
1739 
1740  for(unsigned int i = 0; i < fieldCandidateList.size(); ++i)
1741  {
1742  if(fieldCandidateList[i].tableName_ == "") // then erase
1743  {
1744  // __COUT__ << "Erasing " <<
1745  // fieldCandidateList[i].relativePath_
1746  //<< fieldCandidateList[i].columnName_ << __E__;
1747  fieldCandidateList.erase(fieldCandidateList.begin() + i);
1748  --i; // rewind to look at next after deleted
1749  }
1750  }
1751  } // end AUTO filter field selection
1752 
1753  for(unsigned int i = 0; i < fieldCandidateList.size(); ++i)
1754  __COUT__ << "Final " << fieldCandidateList[i].relativePath_
1755  << fieldCandidateList[i].columnName_ << __E__;
1756 
1757  return fieldCandidateList;
1758 } // end getCommonFields()
1759 
1760 //==============================================================================
1761 // getUniqueValuesForField
1762 //
1763 // returns sorted unique values for the specified records and field
1764 //
1765 std::set<std::string /*unique-value*/> ConfigurationTree::getUniqueValuesForField(
1766  const std::vector<std::string /*relative-path*/>& recordList,
1767  const std::string& fieldName) const
1768 {
1769  // enforce that starting point is a table node
1770  if(!isConfigurationNode())
1771  {
1772  __SS__ << "Can only get getCommonFields from a table node! "
1773  << "The node type is " << getNodeType() << __E__;
1774 
1775  ss << nodeDump() << __E__;
1776  __SS_THROW__;
1777  }
1778 
1779  std::set<std::string /*unique-value*/> uniqueValues;
1780 
1781  // for each record in <record list>
1782  // emplace value at field into set
1783  //
1784  // return result
1785 
1786  for(unsigned int i = 0; i < recordList.size(); ++i)
1787  {
1788  __COUT__ << "Checking " << recordList[i] << __E__;
1789 
1790  // Note: that ConfigurationTree maps both fields associated with a link
1791  // to the same node instance.
1792  // The behavior is likely not expected as response for this function..
1793  // so for links return actual value for field name specified
1794  // i.e. if Table of link is requested give that; if linkID is requested give
1795  // that. use TRUE in getValueAsString for proper behavior
1796  uniqueValues.emplace(
1797  getNode(recordList[i]).getNode(fieldName).getValueAsString(true));
1798  }
1799 
1800  return uniqueValues;
1801 } // end getUniqueValuesForField()
1802 
1803 //==============================================================================
1804 // recursiveGetCommonFields
1805 // wrapper is ...getCommonFields
1806 void ConfigurationTree::recursiveGetCommonFields(
1807  std::vector<ConfigurationTree::RecordField>& fieldCandidateList,
1808  std::vector<int>& fieldCount,
1809  const std::vector<std::string /*relative-path*/>& fieldAcceptList,
1810  const std::vector<std::string /*relative-path*/>& fieldRejectList,
1811  unsigned int depth,
1812  const std::string& relativePathBase,
1813  bool inFirstRecord) const
1814 {
1815  --depth;
1816  //__COUT__ << "relativePathBase " << relativePathBase << __E__;
1817 
1818  // =====================
1819  // Start recursiveGetCommonFields()
1820  // --depth;
1821  // loop through all children
1822  // if isValueNode (value nodes are possible field candidates!)
1823  // if first uid record
1824  // add field to <field candidates list> if in <field filter list>
1825  // initial mark <field count> as 1
1826  // else
1827  // if field is in list,
1828  // increment count for field candidate
1829  // //?increment fields in list count for record
1830  // else if field is not in list, discard field
1831  // else if depth > 0 and is UID-Link
1832  // if Link Table/UID pair is not found in <field candidates list> (avoid
1833  // endless loops through tree) recursiveGetCommonFields()
1834  // =====================
1835 
1836  bool found; // used in loops
1837  auto tableName = getTableName(); // all fields will share this table name
1838  auto uid = getUIDAsString(); // all fields will share this uid
1839  unsigned int j;
1840 
1841  auto recordChildren = getChildren();
1842  for(const auto& fieldNode : recordChildren)
1843  {
1844  // __COUT__ << "All... " << fieldNode.second.getNodeType() <<
1845  // " -- " << (relativePathBase + fieldNode.first) << __E__;
1846 
1847  if(fieldNode.second.isValueNode())
1848  {
1849  // skip author and record insertion time
1850  if(fieldNode.second.getColumnInfo().getType() ==
1851  TableViewColumnInfo::TYPE_AUTHOR ||
1852  fieldNode.second.getColumnInfo().getType() ==
1853  TableViewColumnInfo::TYPE_TIMESTAMP)
1854  continue;
1855 
1856  //__COUT__ << "isValueNode " << fieldNode.first << __E__;
1857  if(inFirstRecord) // first uid record
1858  {
1859  // check field accept filter list
1860  found = fieldAcceptList.size() ? false : true; // accept if no filter
1861  // list
1862  for(const auto& fieldFilter : fieldAcceptList)
1863  if(StringMacros::wildCardMatch(fieldFilter, fieldNode.first))
1864  {
1865  found = true;
1866  break;
1867  }
1868 
1869  if(found)
1870  {
1871  // check field reject filter list
1872 
1873  found = true; // accept if no filter list
1874  for(const auto& fieldFilter : fieldRejectList)
1875  if(StringMacros::wildCardMatch(fieldFilter, fieldNode.first))
1876  {
1877  found = false; // reject if match
1878  break;
1879  }
1880  }
1881 
1882  // if found, new field (since this is first record)
1883  if(found)
1884  {
1885  //__COUT__ << "FOUND field " <<
1886  // (relativePathBase + fieldNode.first) << __E__;
1887  fieldCandidateList.push_back(ConfigurationTree::RecordField(
1888  tableName,
1889  uid,
1890  fieldNode.first,
1891  relativePathBase, // relative path, not including columnName_
1892  &fieldNode.second.getColumnInfo()));
1893  fieldCount.push_back(1); // init count to 1
1894  }
1895  }
1896  else // not first record
1897  {
1898  // if field is in <field candidates list>, increment <field count>
1899  // else ignore
1900  for(j = 0; j < fieldCandidateList.size(); ++j)
1901  {
1902  if((relativePathBase + fieldNode.first) ==
1903  (fieldCandidateList[j].relativePath_ +
1904  fieldCandidateList[j].columnName_))
1905  {
1906  //__COUT__ << "incrementing " << j <<
1907  // " " << fieldCandidateList[j].relativePath_ << __E__;
1908  // found, so increment <field count>
1909  ++fieldCount[j];
1910  break;
1911  }
1912  }
1913  }
1914  }
1915  else if(depth > 0 && fieldNode.second.isUIDLinkNode() &&
1916  !fieldNode.second.isDisconnected())
1917  {
1918  //__COUT__ << "isUIDLinkNode " << (relativePathBase + fieldNode.first) <<
1919  // __E__;
1920  fieldNode.second.recursiveGetCommonFields(
1921  fieldCandidateList,
1922  fieldCount,
1923  fieldAcceptList,
1924  fieldRejectList,
1925  depth,
1926  (relativePathBase + fieldNode.first) + "/", // relativePathBase
1927  inFirstRecord // continue inFirstRecord (or not) depth search
1928  );
1929  }
1930  }
1931 } // end recursiveGetCommonFields()
1932 
1933 //==============================================================================
1934 // getChildrenByPriority
1935 // returns them in order encountered in the table
1936 // if filterMap criteria, then rejects any that do not meet all criteria
1937 //
1938 // value can be comma-separated for OR of multiple values
1939 std::vector<std::vector<std::pair<std::string, ConfigurationTree>>>
1940 ConfigurationTree::getChildrenByPriority(
1941  std::map<std::string /*relative-path*/, std::string /*value*/> filterMap,
1942  bool onlyStatusTrue) const
1943 {
1944  std::vector<std::vector<std::pair<std::string, ConfigurationTree>>> retVector;
1945 
1946  //__COUT__ << "Children of node: " << getValueAsString() << __E__;
1947 
1948  bool filtering = filterMap.size();
1949  bool skip;
1950  std::string fieldValue;
1951 
1952  bool createContainer;
1953 
1954  std::vector<std::vector<std::string>> childrenNamesByPriority =
1955  getChildrenNamesByPriority(onlyStatusTrue);
1956 
1957  for(auto& childNamesAtPriority : childrenNamesByPriority)
1958  {
1959  createContainer = true;
1960 
1961  for(auto& childName : childNamesAtPriority)
1962  {
1963  //__COUT__ << "\tChild: " << childName << __E__;
1964 
1965  if(filtering)
1966  {
1967  // if all criteria are not met, then skip
1968  skip = false;
1969 
1970  // for each filter, check value
1971  for(const auto& filterPair : filterMap)
1972  {
1973  std::string filterPath = childName + "/" + filterPair.first;
1974  __COUTV__(filterPath);
1975  try
1976  {
1977  // extract field value list
1978  std::vector<std::string> fieldValues;
1979  StringMacros::getVectorFromString(
1980  filterPair.second,
1981  fieldValues,
1982  std::set<char>({','}) /*delimiters*/);
1983 
1984  __COUTV__(fieldValues.size());
1985 
1986  skip = true;
1987  // for each field check if any match
1988  for(const auto& fieldValue : fieldValues)
1989  {
1990  // Note: that ConfigurationTree maps both fields associated
1991  // with a link to the same node instance. The behavior is
1992  // likely not expected as response for this function.. so for
1993  // links return actual value for field name specified i.e.
1994  // if Table of link is requested give that; if linkID is
1995  // requested give that. use TRUE in getValueAsString for
1996  // proper
1997  // behavior
1998 
1999  __COUT__ << "\t\tCheck: " << filterPair.first
2000  << " == " << fieldValue << " => "
2001  << StringMacros::decodeURIComponent(fieldValue)
2002  << " ??? "
2003  << this->getNode(filterPath).getValueAsString(true)
2004  << __E__;
2005 
2006  if(StringMacros::wildCardMatch(
2007  StringMacros::decodeURIComponent(fieldValue),
2008  this->getNode(filterPath).getValueAsString(true)))
2009  {
2010  // found a match for the field/value pair
2011  skip = false;
2012  break;
2013  }
2014  }
2015  }
2016  catch(...)
2017  {
2018  __SS__ << "Failed to access filter path '" << filterPath
2019  << "' - aborting." << __E__;
2020 
2021  ss << nodeDump() << __E__;
2022  __SS_THROW__;
2023  }
2024 
2025  if(skip)
2026  break; // no match for this field, so stop checking and skip this
2027  // record
2028  }
2029 
2030  if(skip)
2031  continue; // skip this record
2032 
2033  //__COUT__ << "\tChild accepted: " << childName << __E__;
2034  }
2035 
2036  if(createContainer)
2037  {
2038  retVector.push_back(
2039  std::vector<std::pair<std::string, ConfigurationTree>>());
2040  createContainer = false;
2041  }
2042 
2043  retVector[retVector.size() - 1].push_back(
2044  std::pair<std::string, ConfigurationTree>(
2045  childName, this->getNode(childName, true)));
2046  } // end children within priority loop
2047  } // end children by priority loop
2048 
2049  //__COUT__ << "Done w/Children of node: " << getValueAsString() << __E__;
2050  return retVector;
2051 } // end getChildrenByPriority()
2052 
2053 //==============================================================================
2054 // getChildren
2055 // returns them in order encountered in the table
2056 // if filterMap criteria, then rejects any that do not meet all criteria
2057 //
2058 // value can be comma-separated for OR of multiple values
2059 std::vector<std::pair<std::string, ConfigurationTree>> ConfigurationTree::getChildren(
2060  std::map<std::string /*relative-path*/, std::string /*value*/> filterMap,
2061  bool byPriority,
2062  bool onlyStatusTrue) const
2063 {
2064  std::vector<std::pair<std::string, ConfigurationTree>> retVector;
2065 
2066  //__COUT__ << "Children of node: " << getValueAsString() << __E__;
2067 
2068  bool filtering = filterMap.size();
2069  bool skip;
2070  std::string fieldValue;
2071 
2072  std::vector<std::string> childrenNames = getChildrenNames(byPriority, onlyStatusTrue);
2073  for(auto& childName : childrenNames)
2074  {
2075  //__COUT__ << "\tChild: " << childName << __E__;
2076 
2077  if(filtering)
2078  {
2079  // if all criteria are not met, then skip
2080  skip = false;
2081 
2082  // for each filter, check value
2083  for(const auto& filterPair : filterMap)
2084  {
2085  std::string filterPath = childName + "/" + filterPair.first;
2086  __COUTV__(filterPath);
2087  try
2088  {
2089  // extract field value list
2090  std::vector<std::string> fieldValues;
2091  StringMacros::getVectorFromString(
2092  filterPair.second,
2093  fieldValues,
2094  std::set<char>({','}) /*delimiters*/);
2095 
2096  __COUTV__(fieldValues.size());
2097 
2098  skip = true;
2099  // for each field check if any match
2100  for(const auto& fieldValue : fieldValues)
2101  {
2102  // Note: that ConfigurationTree maps both fields associated with a
2103  // link to the same node instance. The behavior is likely not
2104  // expected as response for this function.. so for links
2105  // return
2106  // actual value for field name specified i.e. if Table of link
2107  // is requested give that; if linkID is requested give that. use
2108  // TRUE in getValueAsString for proper behavior
2109 
2110  __COUT__
2111  << "\t\tCheck: " << filterPair.first << " == " << fieldValue
2112  << " => " << StringMacros::decodeURIComponent(fieldValue)
2113  << " ??? " << this->getNode(filterPath).getValueAsString(true)
2114  << __E__;
2115 
2116  if(StringMacros::wildCardMatch(
2117  StringMacros::decodeURIComponent(fieldValue),
2118  this->getNode(filterPath).getValueAsString(true)))
2119  {
2120  // found a match for the field/value pair
2121  skip = false;
2122  break;
2123  }
2124  }
2125  }
2126  catch(...)
2127  {
2128  __SS__ << "Failed to access filter path '" << filterPath
2129  << "' - aborting." << __E__;
2130 
2131  ss << nodeDump() << __E__;
2132  __SS_THROW__;
2133  }
2134 
2135  if(skip)
2136  break; // no match for this field, so stop checking and skip this
2137  // record
2138  }
2139 
2140  if(skip)
2141  continue; // skip this record
2142 
2143  //__COUT__ << "\tChild accepted: " << childName << __E__;
2144  }
2145 
2146  retVector.push_back(std::pair<std::string, ConfigurationTree>(
2147  childName, this->getNode(childName, true)));
2148  }
2149 
2150  //__COUT__ << "Done w/Children of node: " << getValueAsString() << __E__;
2151  return retVector;
2152 } // end getChildren()
2153 
2154 //==============================================================================
2155 // getChildren
2156 // returns them in order encountered in the table
2157 std::map<std::string, ConfigurationTree> ConfigurationTree::getChildrenMap(void) const
2158 {
2159  std::map<std::string, ConfigurationTree> retMap;
2160 
2161  //__COUT__ << "Children of node: " << getValueAsString() << __E__;
2162 
2163  std::vector<std::string> childrenNames = getChildrenNames();
2164  for(auto& childName : childrenNames)
2165  {
2166  //__COUT__ << "\tChild: " << childName << __E__;
2167  retMap.insert(std::pair<std::string, ConfigurationTree>(
2168  childName, this->getNode(childName)));
2169  }
2170 
2171  //__COUT__ << "Done w/Children of node: " << getValueAsString() << __E__;
2172  return retMap;
2173 } // end getChildrenMap()
2174 
2175 //==============================================================================
2176 bool ConfigurationTree::isRootNode(void) const { return (!table_); }
2177 
2178 //==============================================================================
2179 bool ConfigurationTree::isConfigurationNode(void) const
2180 {
2181  return (table_ && row_ == TableView::INVALID && col_ == TableView::INVALID);
2182 }
2183 
2184 //==============================================================================
2185 // getChildrenNamesByPriority
2186 // returns them in priority order encountered in the table
2187 std::vector<std::vector<std::string>> ConfigurationTree::getChildrenNamesByPriority(
2188  bool onlyStatusTrue) const
2189 {
2190  std::vector<std::vector<std::string /*child name*/>> retVector;
2191 
2192  if(!tableView_)
2193  {
2194  __SS__ << "Can not get children names of '" << getValueAsString()
2195  << "' with null configuration view pointer!" << __E__;
2196  if(isLinkNode() && isDisconnected())
2197  ss << " This node is a disconnected link to " << getDisconnectedTableName()
2198  << __E__;
2199 
2200  ss << nodeDump() << __E__;
2201  __SS_ONLY_THROW__;
2202  }
2203 
2204  if(row_ == TableView::INVALID && col_ == TableView::INVALID)
2205  {
2206  // this node is config node
2207  // so return all uid node strings that match groupId
2208 
2209  bool tmpStatus;
2210 
2211  if(1) // reshuffle by priority
2212  {
2213  try
2214  {
2215  std::map<uint64_t /*priority*/, std::vector<unsigned int /*child row*/>>
2216  orderedByPriority;
2217  std::vector<std::string /*child name*/> retPrioritySet;
2218 
2219  unsigned int col = tableView_->getColPriority();
2220 
2221  uint64_t tmpPriority;
2222 
2223  for(unsigned int r = 0; r < tableView_->getNumberOfRows(); ++r)
2224  if(groupId_ == "" ||
2225  tableView_->isEntryInGroup(r, childLinkIndex_, groupId_))
2226  {
2227  // check status if needed
2228  if(onlyStatusTrue)
2229  {
2230  tableView_->getValue(
2231  tmpStatus, r, tableView_->getColStatus());
2232  if(!tmpStatus)
2233  continue; // skip those with status false
2234  }
2235 
2236  tableView_->getValue(tmpPriority, r, col);
2237  // do not accept DEFAULT value of 0.. convert to 100
2238  orderedByPriority[tmpPriority ? tmpPriority : 100].push_back(r);
2239  }
2240 
2241  // at this point have priority map
2242  // now build return vector
2243 
2244  for(const auto& priorityChildRowVector : orderedByPriority)
2245  {
2246  retVector.push_back(std::vector<std::string /*child name*/>());
2247  for(const auto& priorityChildRow : priorityChildRowVector.second)
2248  retVector[retVector.size() - 1].push_back(
2249  tableView_->getDataView()[priorityChildRow]
2250  [tableView_->getColUID()]);
2251  }
2252 
2253  __COUT__ << "Returning priority children list." << __E__;
2254  return retVector;
2255  }
2256  catch(std::runtime_error& e)
2257  {
2258  __COUT_WARN__ << "Error identifying priority. Assuming all children have "
2259  "equal priority (Error: "
2260  << e.what() << __E__;
2261  retVector.clear();
2262  }
2263  }
2264  // else not by priority
2265 
2266  for(unsigned int r = 0; r < tableView_->getNumberOfRows(); ++r)
2267  if(groupId_ == "" || tableView_->isEntryInGroup(r, childLinkIndex_, groupId_))
2268  {
2269  // check status if needed
2270  if(onlyStatusTrue)
2271  {
2272  tableView_->getValue(tmpStatus, r, tableView_->getColStatus());
2273  if(!tmpStatus)
2274  continue; // skip those with status false
2275  }
2276 
2277  retVector.push_back(std::vector<std::string /*child name*/>());
2278  retVector[retVector.size() - 1].push_back(
2279  tableView_->getDataView()[r][tableView_->getColUID()]);
2280  }
2281  }
2282  else if(row_ == TableView::INVALID)
2283  {
2284  __SS__ << "Malformed ConfigurationTree" << __E__;
2285 
2286  ss << nodeDump() << __E__;
2287  __SS_THROW__;
2288  }
2289  else if(col_ == TableView::INVALID)
2290  {
2291  // this node is uid node
2292  // so return all link and value nodes
2293 
2294  for(unsigned int c = 0; c < tableView_->getNumberOfColumns(); ++c)
2295  if(c == tableView_->getColUID() || // skip UID and linkID columns (only show
2296  // link column, to avoid duplicates)
2297  tableView_->getColumnInfo(c).isChildLinkGroupID() ||
2298  tableView_->getColumnInfo(c).isChildLinkUID())
2299  continue;
2300  else
2301  {
2302  retVector.push_back(std::vector<std::string /*child name*/>());
2303  retVector[retVector.size() - 1].push_back(
2304  tableView_->getColumnInfo(c).getName());
2305  }
2306  }
2307  else // this node is value node, so has no node to choose from
2308  {
2309  // this node is value node, cant go any deeper!
2310  __SS__ << "\n\nError occurred looking for children of nodeName=" << getValueName()
2311  << "\n\n"
2312  << "Invalid depth! getChildrenValues() called from a value point in the "
2313  "Configuration Tree."
2314  << __E__;
2315 
2316  ss << nodeDump() << __E__;
2317  __SS_THROW__;
2318  }
2319 
2320  return retVector;
2321 } // end getChildrenNamesByPriority()
2322 
2323 //==============================================================================
2324 // getChildrenNames
2325 // returns them in order encountered in the table
2326 std::vector<std::string> ConfigurationTree::getChildrenNames(bool byPriority,
2327  bool onlyStatusTrue) const
2328 {
2329  std::vector<std::string /*child name*/> retVector;
2330 
2331  if(!tableView_)
2332  {
2333  __SS__ << "Can not get children names of '" << getValueAsString()
2334  << "' with null configuration view pointer!" << __E__;
2335  if(isLinkNode() && isDisconnected())
2336  ss << " This node is a disconnected link to " << getDisconnectedTableName()
2337  << __E__;
2338  __SS_ONLY_THROW__;
2339  }
2340 
2341  if(row_ == TableView::INVALID && col_ == TableView::INVALID)
2342  {
2343  // this node is config node
2344  // so return all uid node strings that match groupId
2345 
2346  bool tmpStatus;
2347 
2348  if(byPriority) // reshuffle by priority
2349  {
2350  try
2351  {
2352  std::map<uint64_t /*priority*/, std::vector<unsigned int /*child row*/>>
2353  orderedByPriority;
2354  std::vector<std::string /*child name*/> retPrioritySet;
2355 
2356  unsigned int col = tableView_->getColPriority();
2357 
2358  uint64_t tmpPriority;
2359 
2360  for(unsigned int r = 0; r < tableView_->getNumberOfRows(); ++r)
2361  if(groupId_ == "" ||
2362  tableView_->isEntryInGroup(r, childLinkIndex_, groupId_))
2363  {
2364  // check status if needed
2365  if(onlyStatusTrue)
2366  {
2367  tableView_->getValue(
2368  tmpStatus, r, tableView_->getColStatus());
2369 
2370  if(!tmpStatus)
2371  continue; // skip those with status false
2372  }
2373 
2374  tableView_->getValue(tmpPriority, r, col);
2375  // do not accept DEFAULT value of 0.. convert to 100
2376  orderedByPriority[tmpPriority ? tmpPriority : 100].push_back(r);
2377  }
2378 
2379  // at this point have priority map
2380  // now build return vector
2381 
2382  for(const auto& priorityChildRowVector : orderedByPriority)
2383  for(const auto& priorityChildRow : priorityChildRowVector.second)
2384  retVector.push_back(
2385  tableView_->getDataView()[priorityChildRow]
2386  [tableView_->getColUID()]);
2387 
2388  __COUT__ << "Returning priority children list." << __E__;
2389  return retVector;
2390  }
2391  catch(std::runtime_error& e)
2392  {
2393  __COUT_WARN__ << "Priority configuration not found. Assuming all "
2394  "children have equal priority (Error: "
2395  << e.what() << __E__;
2396  retVector.clear();
2397  }
2398  }
2399  // else not by priority
2400 
2401  for(unsigned int r = 0; r < tableView_->getNumberOfRows(); ++r)
2402  if(groupId_ == "" || tableView_->isEntryInGroup(r, childLinkIndex_, groupId_))
2403  {
2404  // check status if needed
2405  if(onlyStatusTrue)
2406  {
2407  tableView_->getValue(tmpStatus, r, tableView_->getColStatus());
2408 
2409  if(!tmpStatus)
2410  continue; // skip those with status false
2411  }
2412 
2413  retVector.push_back(
2414  tableView_->getDataView()[r][tableView_->getColUID()]);
2415  }
2416  }
2417  else if(row_ == TableView::INVALID)
2418  {
2419  __SS__ << "Malformed ConfigurationTree" << __E__;
2420 
2421  ss << nodeDump() << __E__;
2422  __SS_THROW__;
2423  }
2424  else if(col_ == TableView::INVALID)
2425  {
2426  // this node is uid node
2427  // so return all link and value nodes
2428 
2429  for(unsigned int c = 0; c < tableView_->getNumberOfColumns(); ++c)
2430  if(c == tableView_->getColUID() || // skip UID and linkID columns (only show
2431  // link column, to avoid duplicates)
2432  tableView_->getColumnInfo(c).isChildLinkGroupID() ||
2433  tableView_->getColumnInfo(c).isChildLinkUID())
2434  continue;
2435  else
2436  retVector.push_back(tableView_->getColumnInfo(c).getName());
2437  }
2438  else // this node is value node, so has no node to choose from
2439  {
2440  // this node is value node, cant go any deeper!
2441  __SS__ << "\n\nError occurred looking for children of nodeName=" << getValueName()
2442  << "\n\n"
2443  << "Invalid depth! getChildrenValues() called from a value point in the "
2444  "Configuration Tree."
2445  << __E__;
2446 
2447  ss << nodeDump() << __E__;
2448  __SS_THROW__;
2449  }
2450 
2451  return retVector;
2452 } // end getChildrenNames()
2453 
2454 //==============================================================================
2455 // getValueAsTreeNode
2456 // returns tree node for value of this node, treating the value
2457 // as a string for the absolute path string from root of tree
2458 ConfigurationTree ConfigurationTree::getValueAsTreeNode(void) const
2459 {
2460  // check if first character is a /, .. if so try to get value in tree
2461  // if exception, just take value
2462  // note: this call will throw an error, in effect, if not a "value" node
2463  if(!tableView_)
2464  {
2465  __SS__ << "Invalid node for get value." << __E__;
2466  __SS_THROW__;
2467  }
2468 
2469  std::string valueString =
2470  tableView_->getValueAsString(row_, col_, true /* convertEnvironmentVariables */);
2471  //__COUT__ << valueString << __E__;
2472  if(valueString.size() && valueString[0] == '/')
2473  {
2474  //__COUT__ << "Starts with '/' - check if valid tree path: " << valueString <<
2475  // __E__;
2476  try
2477  {
2478  ConfigurationTree retNode = configMgr_->getNode(valueString);
2479  __COUT__ << "Found a valid tree path in value!" << __E__;
2480  return retNode;
2481  }
2482  catch(...)
2483  {
2484  __SS__ << "Invalid tree path." << __E__;
2485  __SS_ONLY_THROW__;
2486  }
2487  }
2488 
2489  {
2490  __SS__ << "Invalid value string '" << valueString
2491  << "' - must start with a '/' character." << __E__;
2492  __SS_ONLY_THROW__;
2493  }
2494 } // end getValueAsTreeNode()