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