otsdaq  v1_01_02
 All Classes Namespaces Functions
ConfigurationTree.cc
1 #include "otsdaq-core/ConfigurationInterface/ConfigurationTree.h"
2 
3 #include "otsdaq-core/ConfigurationInterface/ConfigurationManager.h"
4 #include "otsdaq-core/ConfigurationDataFormats/ConfigurationBase.h"
5 
6 
7 #include <typeinfo>
8 
9 
10 using namespace ots;
11 
12 
13 #undef __MF_SUBJECT__
14 #define __MF_SUBJECT__ "ConfigurationTree"
15 
16 const std::string ConfigurationTree::DISCONNECTED_VALUE = "X";
17 const std::string ConfigurationTree::VALUE_TYPE_DISCONNECTED = "Disconnected";
18 const std::string ConfigurationTree::VALUE_TYPE_NODE = "Node";
19 
20 //==============================================================================
21 ConfigurationTree::ConfigurationTree()
22 : configMgr_ (0),
23  configuration_ (0),
24  groupId_ (""),
25  linkColName_ (""),
26  linkColValue_ (""),
27  disconnectedTargetName_ (""),
28  disconnectedLinkID_ (""),
29  childLinkIndex_ (""),
30  row_ (0),
31  col_ (0),
32  configView_ (0)
33 {
34  //std::cout << __PRETTY_FUNCTION__ << std::endl;
35  //std::cout << __PRETTY_FUNCTION__ << "EMPTY CONSTRUCTOR ConfigManager: " << configMgr_ << " configuration: " << configuration_ << std::endl;
36 }
37 //==============================================================================
38 ConfigurationTree::ConfigurationTree(const ConfigurationManager* const &configMgr, const ConfigurationBase* const &config)
39 : ConfigurationTree(configMgr, config, "", "", "", "", "" /*disconnectedLinkID_*/, "", ConfigurationView::INVALID, ConfigurationView::INVALID)
40 {
41  //std::cout << __PRETTY_FUNCTION__ << std::endl;
42  //std::cout << __PRETTY_FUNCTION__ << "SHORT CONTRUCTOR ConfigManager: " << configMgr_ << " configuration: " << configuration_ << std::endl;
43 }
44 
45 //==============================================================================
46 ConfigurationTree::ConfigurationTree(
47  const ConfigurationManager* const& configMgr,
48  const ConfigurationBase* const& config,
49  const std::string& groupId,
50  const std::string& linkColName,
51  const std::string& linkColValue,
52  const std::string& disconnectedTargetName,
53  const std::string& disconnectedLinkID,
54  const std::string& childLinkIndex,
55  const unsigned int row,
56  const unsigned int col)
57 : configMgr_ (configMgr),
58  configuration_ (config),
59  groupId_ (groupId),
60  linkColName_ (linkColName),
61  linkColValue_ (linkColValue),
62  disconnectedTargetName_ (disconnectedTargetName),
63  disconnectedLinkID_ (disconnectedLinkID),
64  childLinkIndex_ (childLinkIndex),
65  row_ (row),
66  col_ (col),
67  configView_ (0)
68 {
69  //std::cout << __PRETTY_FUNCTION__ << std::endl;
70  //std::cout << __PRETTY_FUNCTION__ << "FULL CONTRUCTOR ConfigManager: " << configMgr_ << " configuration: " << configuration_ << std::endl;
71  if(!configMgr_)// || !configuration_ || !configView_)
72  {
73  std::stringstream ss;
74  ss << __COUT_HDR_FL__ << "Invalid empty pointer given to tree!\n" <<
75  "\n\tconfigMgr_=" << configMgr_ <<
76  "\n\tconfiguration_=" << configuration_ <<
77  "\n\tconfigView_=" << configView_ <<
78  std::endl;
79  __MOUT__ << ss.str();
80  throw std::runtime_error(ss.str());
81  }
82 
83  if(configuration_)
84  configView_ = &(configuration_->getView());
85 
86  //verify UID column exists
87  if(configView_ &&
88  configView_->getColumnInfo(configView_->getColUID()).getType() != ViewColumnInfo::TYPE_UID)
89  {
90  __SS__ << "Missing UID column (must column of type " << ViewColumnInfo::TYPE_UID <<
91  ") in config view : " << configView_->getTableName() << std::endl;
92  __MOUT__ << ss.str();
93  throw std::runtime_error(ss.str());
94  }
95 }
96 
97 //==============================================================================
98 //destructor
99 ConfigurationTree::~ConfigurationTree(void)
100 {
101  //std::cout << __PRETTY_FUNCTION__ << std::endl;
102 }
103 
104 //==============================================================================
105 //print
106 // print out tree from this node for desired depth
107 // depth of 0 means print out only this node's value
108 // depth of 1 means include this node's children's values, etc..
109 // depth of -1 means print full tree
110 void ConfigurationTree::print(const unsigned int &depth, std::ostream &out) const
111 {
112  recursivePrint(*this,depth,out,"\t");
113 }
114 
115 //==============================================================================
116 void ConfigurationTree::recursivePrint(const ConfigurationTree &t, unsigned int depth, std::ostream &out, std::string space)
117 {
118  if(t.isValueNode())
119  out << space << t.getValueName() << " :\t" << t.getValueAsString() << std::endl;
120  else
121  {
122  if(t.isLinkNode())
123  {
124  out << space << t.getValueName();
125  if(t.isDisconnected())
126  {
127  out << " :\t" << t.getValueAsString() << std::endl;
128  return;
129  }
130  out << " (" <<
131  (t.isGroupLinkNode()?"Group":"U") <<
132  "ID=" << t.getValueAsString() <<
133  ") : " << std::endl;
134  }
135  else
136  out << space << t.getValueAsString() << " : " << std::endl;
137 
138  //if depth>=1 print all children
139  // child.print(depth-1)
140  if(depth >= 1)
141  {
142  auto C = t.getChildren();
143  if(!C.empty())
144  out << space << "{" << std::endl;
145  for(auto &c:C)
146  recursivePrint(c.second,depth-1,out,space + " ");
147  if(!C.empty())
148  out << space << "}" << std::endl;
149  }
150  }
151 }
152 
153 
154 //==============================================================================
155 //getValue (only std::string value)
156 void ConfigurationTree::getValue(std::string& value) const
157 {
158  //__MOUT__ << row_ << " " << col_ << " p: " << configView_<< std::endl;
159 
160  if(row_ != ConfigurationView::INVALID && col_ != ConfigurationView::INVALID) //this node is a value node
161  configView_->getValue(value,row_,col_);
162  else if(row_ == ConfigurationView::INVALID && col_ == ConfigurationView::INVALID) //this node is config node maybe with groupId
163  {
164  if(isLinkNode() && isDisconnected())
165  value = (groupId_ == "") ? getValueName():groupId_; //a disconnected link still knows its table name or groupId
166  else
167  value = (groupId_ == "") ? configuration_->getConfigurationName():groupId_;
168  }
169  else if(row_ == ConfigurationView::INVALID)
170  {
171  __MOUT__ << std::endl;
172  throw std::runtime_error("Malformed ConfigurationTree");
173  }
174  else if(col_ == ConfigurationView::INVALID) //this node is uid node
175  configView_->getValue(value,row_,configView_->getColUID());
176  else
177  {
178  __MOUT__ << std::endl;
179  throw std::runtime_error("Impossible.");
180  }
181 
182 }
183 
184 //==============================================================================
185 //getValue
186 // Only std::string value will work.
187 // If this is a value node, and not type string, configView->getValue should
188 // throw exception.
189 //
190 // NOTE: getValueAsString() method should be preferred if getting the Link UID
191 // because when disconnected will return "X". getValue() would return the
192 // column name of the link when disconnected.
193 std::string ConfigurationTree::getValue() const
194 {
195  //__MOUT__ << row_ << " " << col_ << " p: " << configView_<< std::endl;
196 
197  std::string value = "";
198  if(row_ != ConfigurationView::INVALID && col_ != ConfigurationView::INVALID) //this node is a value node
199  configView_->getValue(value,row_,col_);
200  else if(row_ == ConfigurationView::INVALID && col_ == ConfigurationView::INVALID) //this node is config node maybe with groupId
201  {
202  if(isLinkNode() && isDisconnected())
203  value = (groupId_ == "") ? getValueName():groupId_; //a disconnected link still knows its table name or groupId
204  else
205  value = (groupId_ == "") ? configuration_->getConfigurationName():groupId_;
206  }
207  else if(row_ == ConfigurationView::INVALID)
208  {
209  __MOUT__ << std::endl;
210  throw std::runtime_error("Malformed ConfigurationTree");
211  }
212  else if(col_ == ConfigurationView::INVALID) //this node is uid node
213  configView_->getValue(value,row_,configView_->getColUID());
214  else
215  {
216  __MOUT__ << std::endl;
217  throw std::runtime_error("Impossible.");
218  }
219  return value;
220 
221 }
222 
223 //==============================================================================
224 //getEscapedValue
225 // Only works if a value node, other exception thrown
226 std::string ConfigurationTree::getEscapedValue() const
227 {
228  if(row_ != ConfigurationView::INVALID && col_ != ConfigurationView::INVALID) //this node is a value node
229  return configView_->getEscapedValueAsString(row_,col_);
230 
231  __SS__ << "Can't get escaped value except from a value node!" <<
232  " This node is type '" << getNodeType() << "." << std::endl;
233  __MOUT_ERR__ << "\n" << ss.str();
234  throw std::runtime_error(ss.str());
235 }
236 
237 //==============================================================================
238 //getConfigurationName
239 const std::string& ConfigurationTree::getConfigurationName(void) const
240 {
241  if(!configuration_)
242  {
243  __SS__ << "Can't get configuration name of node with no configuration pointer!" << std::endl;
244  throw std::runtime_error(ss.str());
245  }
246  return configuration_->getConfigurationName();
247 }
248 
249 //==============================================================================
250 //getDisconnectedTableName
251 const std::string& ConfigurationTree::getDisconnectedTableName(void) const
252 {
253  if(isLinkNode() && isDisconnected()) return disconnectedTargetName_;
254 
255  __SS__ << "Can't get disconnected target name of node unless it is a disconnected link node!" << std::endl;
256  throw std::runtime_error(ss.str());
257 }
258 
259 //==============================================================================
260 //getDisconnectedLinkID
261 const std::string& ConfigurationTree::getDisconnectedLinkID(void) const
262 {
263  if(isLinkNode() && isDisconnected()) return disconnectedLinkID_;
264 
265  __SS__ << "Can't get disconnected target name of node unless it is a disconnected link node!" << std::endl;
266  throw std::runtime_error(ss.str());
267 }
268 
269 //==============================================================================
270 //getConfigurationVersion
271 const ConfigurationVersion& ConfigurationTree::getConfigurationVersion(void) const
272 {
273  if(!configView_)
274  {
275  __SS__ << "Can't get configuration version of node with no config view pointer!" << std::endl;
276  throw std::runtime_error(ss.str());
277  }
278  return configView_->getVersion();
279 }
280 
281 //==============================================================================
282 //getConfigurationCreationTime
283 const time_t& ConfigurationTree::getConfigurationCreationTime(void) const
284 {
285  if(!configView_)
286  {
287  __SS__ << "Can't get configuration creation time of node with no config view pointer!" << std::endl;
288  throw std::runtime_error(ss.str());
289  }
290  return configView_->getCreationTime();
291 }
292 
293 
294 //==============================================================================
295 //getFixedChoices
296 // returns vector of default + data choices
297 // Used as choices for tree-view, for example.
298 std::vector<std::string> ConfigurationTree::getFixedChoices(void) const
299 {
300  if(!configView_)
301  {
302  __SS__ << "Can't get fixed choices of node with no config view pointer!" << std::endl;
303  throw std::runtime_error(ss.str());
304  }
305 
306  if(getValueType() != ViewColumnInfo::TYPE_FIXED_CHOICE_DATA &&
307  getValueType() != ViewColumnInfo::TYPE_BITMAP_DATA)
308  {
309  __SS__ << "Can't get fixed choices of node with value type of '" <<
310  getValueType() << ".' Value type must be '" <<
311  ViewColumnInfo::TYPE_BITMAP_DATA << "' or '" <<
312  ViewColumnInfo::TYPE_FIXED_CHOICE_DATA << ".'" << std::endl;
313  throw std::runtime_error(ss.str());
314  }
315 
316  //return vector of default + data choices
317  std::vector<std::string> retVec;
318  retVec.push_back(configView_->getColumnInfo(col_).getDefaultValue());
319  std::vector<std::string> choices = configView_->getColumnInfo(col_).getDataChoices();
320  for(const auto &choice:choices)
321  retVec.push_back(choice);
322 
323  return retVec;
324 }
325 
326 //==============================================================================
327 //getValueAsString
328 // NOTE: getValueAsString() method should be preferred if getting the Link UID
329 // because when disconnected will return "X". getValue() would return the
330 // column name of the link when disconnected.
331 //
332 // returnLinkTableValue returns the value in the source table as though link was
333 // not followed to destination table.
334 const std::string& ConfigurationTree::getValueAsString(bool returnLinkTableValue) const
335 {
336  if(isLinkNode())
337  {
338  if(returnLinkTableValue)
339  return linkColValue_;
340  else if(isDisconnected())
341  return ConfigurationTree::DISCONNECTED_VALUE;
342  else if(row_ == ConfigurationView::INVALID && col_ == ConfigurationView::INVALID) //this link is groupId node
343  return (groupId_ == "")?configuration_->getConfigurationName():groupId_;
344  else if(col_ == ConfigurationView::INVALID) //this link is uid node
345  return configView_->getDataView()[row_][configView_->getColUID()];
346  else
347  {
348  __MOUT__ << std::endl;
349  throw std::runtime_error("Impossible Link.");
350  }
351  }
352  else if(row_ != ConfigurationView::INVALID && col_ != ConfigurationView::INVALID) //this node is a value node
353  return configView_->getDataView()[row_][col_];
354  else if(row_ == ConfigurationView::INVALID && col_ == ConfigurationView::INVALID) //this node is config node maybe with groupId
355  return (groupId_ == "")?configuration_->getConfigurationName():groupId_;
356  else if(row_ == ConfigurationView::INVALID)
357  {
358  __MOUT__ << std::endl;
359  throw std::runtime_error("Malformed ConfigurationTree");
360  }
361  else if(col_ == ConfigurationView::INVALID) //this node is uid node
362  return configView_->getDataView()[row_][configView_->getColUID()];
363  else
364  {
365  __MOUT__ << std::endl;
366  throw std::runtime_error("Impossible.");
367  }
368 }
369 
370 //==============================================================================
371 //getUIDAsString
372 // returns UID associated with current value node or UID-Link node
373 //
374 const std::string& ConfigurationTree::getUIDAsString(void) const
375 {
376  if(isValueNode() || isUIDLinkNode())
377  return configView_->getDataView()[row_][configView_->getColUID()];
378 
379  {
380  __SS__ << "Can't get UID of node with type '" <<
381  getNodeType() << ".' Node type must be '" <<
382  ConfigurationTree::NODE_TYPE_VALUE << "' or '" <<
383  ConfigurationTree::NODE_TYPE_UID_LINK << ".'" << std::endl;
384  throw std::runtime_error(ss.str());
385  }
386 }
387 
388 //==============================================================================
389 //getValueDataType
390 // e.g. used to determine if node is type NUMBER
391 const std::string& ConfigurationTree::getValueDataType(void) const
392 {
393  if(isValueNode())
394  return configView_->getColumnInfo(col_).getDataType();
395  else //must be std::string
396  return ViewColumnInfo::DATATYPE_STRING;
397 }
398 
399 //==============================================================================
400 //isDefaultValue
401 // returns true if is a value node and value is the default for the type
402 bool ConfigurationTree::isDefaultValue(void) const
403 {
404  if(!isValueNode()) return false;
405 
406  if(getValueDataType() == ViewColumnInfo::DATATYPE_STRING)
407  {
408  if(getValueType() == ViewColumnInfo::TYPE_ON_OFF ||
409  getValueType() == ViewColumnInfo::TYPE_TRUE_FALSE ||
410  getValueType() == ViewColumnInfo::TYPE_YES_NO)
411  return getValueAsString() == ViewColumnInfo::DATATYPE_BOOL_DEFAULT; //default to OFF, NO, FALSE
412  else if(getValueType() == ViewColumnInfo::TYPE_COMMENT)
413  return getValueAsString() == ViewColumnInfo::DATATYPE_COMMENT_DEFAULT ||
414  getValueAsString() == ""; //in case people delete default comment, allow blank also
415  else
416  return getValueAsString() == ViewColumnInfo::DATATYPE_STRING_DEFAULT;
417  }
418  else if(getValueDataType() == ViewColumnInfo::DATATYPE_NUMBER)
419  return getValueAsString() == ViewColumnInfo::DATATYPE_NUMBER_DEFAULT;
420  else if(getValueDataType() == ViewColumnInfo::DATATYPE_TIME)
421  return getValueAsString() == ViewColumnInfo::DATATYPE_TIME_DEFAULT;
422  else
423  return false;
424 }
425 
426 //==============================================================================
427 //getValueType
428 // e.g. used to determine if node is type TYPE_DATA, TYPE_ON_OFF, etc.
429 const std::string& ConfigurationTree::getValueType(void) const
430 {
431  if(isValueNode())
432  return configView_->getColumnInfo(col_).getType();
433  else if(isLinkNode() && isDisconnected())
434  return ConfigurationTree::VALUE_TYPE_DISCONNECTED;
435  else //just call all non-value nodes data
436  return ConfigurationTree::VALUE_TYPE_NODE;
437 }
438 
439 //==============================================================================
440 //getColumnInfo
441 // only sensible for value node
442 const ViewColumnInfo& ConfigurationTree::getColumnInfo(void) const
443 {
444  if(isValueNode())
445  return configView_->getColumnInfo(col_);
446  else
447  {
448  __SS__ << "Can only get column info from a value node! " <<
449  "The node type is " << getNodeType() << std::endl;
450  __MOUT__ << "\n" << ss.str() << std::endl;
451  throw std::runtime_error(ss.str());
452  }
453 
454 }
455 
456 //==============================================================================
457 //getRow
458 const unsigned int& ConfigurationTree::getRow(void) const
459 { return row_; }
460 
461 //==============================================================================
462 //getColumn
463 const unsigned int& ConfigurationTree::getColumn(void) const
464 { return col_; }
465 
466 //==============================================================================
467 //getChildLinkIndex
468 const std::string& ConfigurationTree::getChildLinkIndex(void) const
469 {
470  if(!isLinkNode())
471  {
472  __SS__ << "Can only get link ID from a link! " <<
473  "The node type is " << getNodeType() << std::endl;
474  __MOUT__ << "\n" << ss.str() << std::endl;
475  throw std::runtime_error(ss.str());
476  }
477  return childLinkIndex_;
478 }
479 
480 //==============================================================================
481 //getValueName
482 // e.g. used to determine column name of value node
483 const std::string& ConfigurationTree::getValueName(void) const
484 {
485  if(isValueNode())
486  return configView_->getColumnInfo(col_).getName();
487  else if(isLinkNode())
488  return linkColName_;
489  else
490  {
491  __SS__ << "Can only get value name of a value node!" << std::endl;
492  __MOUT__ << "\n" << ss.str() << std::endl;
493  throw std::runtime_error(ss.str());
494  }
495 
496 }
497 
498 //==============================================================================
499 //recurse
500 // Used by ConfigurationTree to handle / syntax of getNode
501 ConfigurationTree ConfigurationTree::recurse(const ConfigurationTree& tree, const std::string& childPath)
502 {
503  //__MOUT__ << tree.row_ << " " << tree.col_ << std::endl;
504  //__MOUT__ << "childPath=" << childPath << " " << childPath.length() << std::endl;
505  if(childPath.length() <= 1) //only "/" or ""
506  return tree;
507  return tree.getNode(childPath);
508 }
509 
520 //std::string ConfigurationTree::getRecordFieldValueAsString(std::string fieldName) const
521 //{
522 // //enforce that starting point is a table node
523 // if(!isUIDNode())
524 // {
525 // __SS__ << "Can only get getRecordFieldValueAsString from a uid node! " <<
526 // "The node type is " << getNodeType() << std::endl;
527 // __MOUT__ << "\n" << ss.str() << std::endl;
528 // throw std::runtime_error(ss.str());
529 // }
530 //
531 // unsigned int c = configView_->findCol(fieldName);
532 // return configView_->getDataView()[row_][c];
533 //}
534 
535 //==============================================================================
536 //getNode
537 // nodeString can be a multi-part path using / delimiter
538 // use:
539 // getNode(/uid/col) or getNode(uid)->getNode(col)
540 //
541 // if doNotThrowOnBrokenUIDLinks
542 // then catch exceptions on UID links and call disconnected
543 ConfigurationTree ConfigurationTree::getNode(const std::string &nodeString,
544  bool doNotThrowOnBrokenUIDLinks) const
545 {
546  //__MOUT__ << "nodeString=" << nodeString << " " << nodeString.length() << std::endl;
547 
548  //get nodeName (in case of / syntax)
549  if(nodeString.length() < 1) throw std::runtime_error("Invalid node name!");
550 
551  bool startingSlash = nodeString[0] == '/';
552 
553  std::string nodeName = nodeString.substr(startingSlash?1:0, nodeString.find('/',1)-(startingSlash?1:0));
554  //__MOUT__ << "nodeName=" << nodeName << " " << nodeName.length() << std::endl;
555 
556  std::string childPath = nodeString.substr(nodeName.length() + (startingSlash?1:0));
557  //__MOUT__ << "childPath=" << childPath << " " << childPath.length() << std::endl;
558 
559  //if this tree is beginning at a configuration.. then go to uid, and vice versa
560 
561  try
562  {
563 
564  //__MOUT__ << row_ << " " << col_ << " " << groupId_ << " " << configView_ << std::endl;
565  if(row_ == ConfigurationView::INVALID && col_ == ConfigurationView::INVALID)
566  {
567  //this node is config node, so return uid node considering groupid
568  return recurse(ConfigurationTree(
569  configMgr_,
570  configuration_,
571  "", //no new groupId string, not a link
572  "", //link node name, not a link
573  "", //link node value, not a link
574  "", //ignored disconnected target name, not a link
575  "", //ignored disconnected link id, not a link
576  "",
577  //if this node is group config node, consider that when getting rows
578  (groupId_ == "")?
579  configView_->findRow(configView_->getColUID(),nodeName)
580  : configView_->findRowInGroup(configView_->getColUID(),
581  nodeName,groupId_,childLinkIndex_) ),
582  childPath);
583  }
584  else if(row_ == ConfigurationView::INVALID)
585  {
586  //__MOUT__ << std::endl;
587  throw std::runtime_error("Malformed ConfigurationTree");
588  }
589  else if(col_ == ConfigurationView::INVALID)
590  {
591  //this node is uid node, so return link, group link, disconnected, or value node
592 
593  //__MOUT__ << "nodeName=" << nodeName << " " << nodeName.length() << std::endl;
594 
595  //if the value is a unique link ..
596  //return a uid node!
597  //if the value is a group link
598  //return a config node with group string
599  //else.. return value node
600 
601  unsigned int c = configView_->findCol(nodeName);
602  std::pair<unsigned int /*link col*/, unsigned int /*link id col*/> linkPair;
603  bool isGroupLink, isLink;
604  if((isLink = configView_->getChildLink(c, &isGroupLink, &linkPair)) &&
605  !isGroupLink)
606  {
607  //__MOUT__ << "nodeName=" << nodeName << " " << nodeName.length() << std::endl;
608  //is a unique link, return uid node in new configuration
609  // need new configuration pointer
610  // and row of linkUID in new configuration
611 
612  const ConfigurationBase* childConfig;
613  try
614  {
615  childConfig = configMgr_->getConfigurationByName(configView_->getDataView()[row_][linkPair.first]);
616  childConfig->getView(); //get view as a test for an active view
617 
618  if(doNotThrowOnBrokenUIDLinks) //try a test of getting row
619  {
620  childConfig->getView().findRow(childConfig->getView().getColUID(),
621  configView_->getDataView()[row_][linkPair.second]);
622  }
623  }
624  catch(...)
625  {
626  // __MOUT_WARN__ << "Found disconnected node! (" << nodeName <<
627  // ":" << configView_->getDataView()[row_][linkPair.first] << ")" <<
628  // " at entry with UID " <<
629  // configView_->getDataView()[row_][configView_->getColUID()] <<
630  // std::endl;
631  //do not recurse further
632  return ConfigurationTree(
633  configMgr_,
634  0,
635  "",
636  nodeName,
637  configView_->getDataView()[row_][c], //this the link node field associated value (matches targeted column)
638  configView_->getDataView()[row_][linkPair.first], //give disconnected target name
639  configView_->getDataView()[row_][linkPair.second], //give disconnected link ID
640  configView_->getColumnInfo(c).getChildLinkIndex());
641  }
642 
643  return recurse(
644  ConfigurationTree( //this is a link node
645  configMgr_,
646  childConfig,
647  "", //no new groupId string
648  nodeName, //this is a link node
649  configView_->getDataView()[row_][c], //this the link node field associated value (matches targeted column)
650  "", //ignore since is connected
651  "", //ignore since is connected
652  configView_->getColumnInfo(c).getChildLinkIndex(),
653  childConfig->getView().findRow(childConfig->getView().getColUID(),
654  configView_->getDataView()[row_][linkPair.second])
655  ),
656  childPath);
657  }
658  else if(isLink)
659  {
660  //__MOUT__ << "nodeName=" << nodeName << " " << nodeName.length() << std::endl;
661  //is a group link, return new configuration with group string
662  // need new configuration pointer
663  // and group string
664 
665  const ConfigurationBase* childConfig;
666  try
667  {
668  childConfig = configMgr_->getConfigurationByName(
669  configView_->getDataView()[row_][linkPair.first]);
670  childConfig->getView(); //get view as a test for an active view
671  }
672  catch(...)
673  {
674  if(configView_->getDataView()[row_][linkPair.first] !=
675  ViewColumnInfo::DATATYPE_LINK_DEFAULT)
676  __MOUT_WARN__ << "Found disconnected node! Failed link target from nodeName=" <<
677  nodeName << " to table:id=" <<
678  configView_->getDataView()[row_][linkPair.first] << ":" <<
679  configView_->getDataView()[row_][linkPair.second] <<
680  std::endl;
681 
682  //do not recurse further
683  return ConfigurationTree(configMgr_,0,
684  configView_->getDataView()[row_][linkPair.second],
685  nodeName,
686  configView_->getDataView()[row_][c], //this the link node field associated value (matches targeted column)
687  configView_->getDataView()[row_][linkPair.first], //give disconnected target name
688  configView_->getDataView()[row_][linkPair.second], //give disconnected target name
689  configView_->getColumnInfo(c).getChildLinkIndex()
690  );
691  }
692 
693  return recurse(
694  ConfigurationTree( //this is a link node
695  configMgr_,
696  childConfig,
697  configView_->getDataView()[row_][linkPair.second], //groupId string
698  nodeName, //this is a link node
699  configView_->getDataView()[row_][c], //this the link node field associated value (matches targeted column)
700  "", //ignore since is connected
701  "", //ignore since is connected
702  configView_->getColumnInfo(c).getChildLinkIndex()
703  ),
704  childPath);
705  }
706  else
707  {
708  //__MOUT__ << "nodeName=" << nodeName << " " << nodeName.length() << std::endl;
709  //return value node
710  return ConfigurationTree(
711  configMgr_,
712  configuration_,"","","","",""/*disconnectedLinkID*/,"",
713  row_,c);
714  }
715  }
716 
717  }
718  catch(std::runtime_error &e)
719  {
720  __SS__ << "\n\nError occurred descending from node '" << getValue() <<
721  "' looking for child '" << nodeName << "'\n\n" << std::endl;
722  ss << "--- Additional error detail: \n\n" << e.what() << std::endl;
723  throw std::runtime_error(ss.str());
724  }
725  catch(...)
726  {
727  __SS__ << "\n\nError occurred descending from node '" << getValue() <<
728  "' looking for child '" << nodeName << "'\n\n" << std::endl;
729  throw std::runtime_error(ss.str());
730  }
731 
732  //this node is value node, so has no node to choose from
733  __SS__ << "\n\nError occurred looking for nodeName=" << nodeName << "\n\n" <<
734  "Invalid depth! getNode() called from a value point in the Configuration Tree." << std::endl;
735  throw std::runtime_error(ss.str()); // this node is value node, cant go any deeper!
736 }
737 
738 
739 //==============================================================================
740 ConfigurationTree ConfigurationTree::getBackNode(std::string nodeName, unsigned int backSteps) const
741 {
742  for(unsigned int i=0; i<backSteps; i++)
743  nodeName = nodeName.substr(0, nodeName.find_last_of('/'));
744 
745  return getNode(nodeName);
746 }
747 
748 //==============================================================================
749 //isValueNode
750 // if true, then this is a leaf node, i.e. there can be no children, only a value
751 bool ConfigurationTree::isValueNode(void) const
752 {
753  return (row_ != ConfigurationView::INVALID && col_ != ConfigurationView::INVALID);
754 }
755 
756 //==============================================================================
757 //isDisconnected
758 // if true, then this is a disconnected node, i.e. there is a configuration link missing
759 // (this is possible when the configuration is loaded in stages and the complete tree
760 // may not be available, yet)
761 bool ConfigurationTree::isDisconnected(void) const
762 {
763  if(!isLinkNode())
764  {
765  __SS__ << "This is not a Link node! Only a Link node can be disconnected." << std::endl;
766  __MOUT__ << ss.str();
767  throw std::runtime_error(ss.str());
768  }
769 
770  return !configuration_ || !configView_;
771 }
772 
773 //==============================================================================
774 //isLinkNode
775 // if true, then this is a link node
776 bool ConfigurationTree::isLinkNode(void) const
777 {
778  return linkColName_ != "";
779 }
780 //
781 //==============================================================================
782 //getNodeType
783 // return node type as string
784 const std::string ConfigurationTree::NODE_TYPE_GROUP_TABLE = "GroupConfigurationNode";
785 const std::string ConfigurationTree::NODE_TYPE_TABLE = "ConfigurationNode";
786 const std::string ConfigurationTree::NODE_TYPE_GROUP_LINK = "GroupLinkNode";
787 const std::string ConfigurationTree::NODE_TYPE_UID_LINK = "UIDLinkNode";
788 const std::string ConfigurationTree::NODE_TYPE_VALUE = "ValueNode";
789 const std::string ConfigurationTree::NODE_TYPE_UID = "UIDNode";
790 std::string ConfigurationTree::getNodeType(void) const
791 {
792  if(isConfigurationNode() && groupId_ != "") return ConfigurationTree::NODE_TYPE_GROUP_TABLE;
793  if(isConfigurationNode()) return ConfigurationTree::NODE_TYPE_TABLE;
794  if(isGroupLinkNode()) return ConfigurationTree::NODE_TYPE_GROUP_LINK;
795  if(isLinkNode()) return ConfigurationTree::NODE_TYPE_UID_LINK;
796  if(isValueNode()) return ConfigurationTree::NODE_TYPE_VALUE;
797  return ConfigurationTree::NODE_TYPE_UID;
798 }
799 
800 //==============================================================================
801 //isGroupLinkNode
802 // if true, then this is a group link node
803 bool ConfigurationTree::isGroupLinkNode(void) const
804 {
805  return (isLinkNode() && groupId_ != "");
806 }
807 
808 //==============================================================================
809 //isUIDLinkNode
810 // if true, then this is a uid link node
811 bool ConfigurationTree::isUIDLinkNode(void) const
812 {
813  return (isLinkNode() && groupId_ == "");
814 }
815 
816 //==============================================================================
817 //isUIDNode
818 // if true, then this is a uid node
819 bool ConfigurationTree::isUIDNode(void) const
820 {
821  return (row_ != ConfigurationView::INVALID && col_ == ConfigurationView::INVALID);
822 }
823 
824 
825 //==============================================================================
826 //getCommonFields
827 // wrapper for ...recursiveGetCommonFields
828 //
829 // returns common fields in order encountered
830 // including through UID links depending on depth specified
831 //
832 // Field := {Table, UID, Column Name, Relative Path, ViewColumnInfo}
833 //
834 // if fieldAcceptList or fieldRejectList are not empty,
835 // then reject any that are not in field accept filter list
836 // and reject any that are in field reject filter list
837 //
838 // will only go to specified depth looking for fields
839 // (careful to prevent infinite loops in tree navigation)
840 //
841 std::vector<ConfigurationTree::RecordField> ConfigurationTree::getCommonFields(
842  const std::vector<std::string /*uid*/> &recordList,
843  const std::vector<std::string /*relative-path*/> &fieldAcceptList,
844  const std::vector<std::string /*relative-path*/> &fieldRejectList,
845  unsigned int depth) const
846 {
847  //enforce that starting point is a table node
848  if(!isConfigurationNode())
849  {
850  __SS__ << "Can only get getCommonFields from a table node! " <<
851  "The node type is " << getNodeType() << std::endl;
852  __MOUT__ << "\n" << ss.str() << std::endl;
853  throw std::runtime_error(ss.str());
854  }
855 
856  std::vector<ConfigurationTree::RecordField> fieldCandidateList;
857  std::vector<int> fieldCount; //-1 := guaranteed, else count must match num of records
858 
859  --depth; //decrement for recursion
860 
861  //for each record in <record list>
862  // loop through all record's children
863  // if isValueNode (value nodes are possible field candidates!)
864  // if first uid record
865  // add field to <field candidates list> if in <field filter list>
866  // mark <field count> as guaranteed -1 (all these fields must be common for UIDs in same table)
867  // else not first uid record, do not need to check, must be same as first record!
868  // else if depth > 0 and UID-Link Node
869  // recursively (call recursiveGetCommonFields())
870  // =====================
871  // Start recursiveGetCommonFields()
872  // --depth;
873  // loop through all children
874  // if isValueNode (value nodes are possible field candidates!)
875  // if first uid record
876  // add field to <field candidates list> if in <field filter list>
877  // initial mark <field count> as 1
878  // else
879  // if field is in <field candidates list>,
880  // increment <field count> for field candidate
881  // else if field is not in list, ignore field
882  // else if depth > 0 and is UID-Link
883  // if Link Table/UID pair is not found in <field candidates list> (avoid endless loops through tree)
884  // recursiveGetCommonFields()
885  // =====================
886  //
887  //
888  //loop through all field candidates
889  // remove those with <field count> != num of records
890  //
891  //
892  //return result
893 
894  bool found; //used in loops
895  auto tableName = getConfigurationName(); //all records will share this table name
896 
897  for(unsigned int i=0;i<recordList.size();++i)
898  {
899  //__MOUT__ << "Checking " << recordList[i] << std::endl;
900 
901  auto recordChildren = getNode(recordList[i]).getChildren();
902  for(const auto &fieldNode : recordChildren)
903  {
904  //__MOUT__ << "All... " << fieldNode.second.getNodeType() <<
905  // " -- " << fieldNode.first << std::endl;
906  if(fieldNode.second.isValueNode())
907  {
908  //skip author and record insertion time
909  if(fieldNode.second.getColumnInfo().getType() ==
910  ViewColumnInfo::TYPE_AUTHOR ||
911  fieldNode.second.getColumnInfo().getType() ==
912  ViewColumnInfo::TYPE_TIMESTAMP)
913  continue;
914 
915  //__MOUT__ << "isValueNode " << fieldNode.first << std::endl;
916  if(!i) //first uid record
917  {
918  //check field accept filter list
919  found = fieldAcceptList.size()?false:true; //accept if no filter list
920  for(const auto &fieldFilter : fieldAcceptList)
921  if(fieldFilter[0] == '*') //leading wildcard
922  {
923  if(fieldNode.first ==
924  fieldFilter.substr(1))
925  {
926  found = true;
927  break;
928  }
929  }
930  else if(fieldFilter.size() &&
931  fieldFilter[fieldFilter.size()-1] == '*') //trailing wildcard
932  {
933  if(fieldNode.first.substr(0,fieldFilter.size()-1) ==
934  fieldFilter.substr(0,fieldFilter.size()-1))
935  {
936  found = true;
937  break;
938  }
939  }
940  else //no leading wildcard
941  {
942  if(fieldNode.first == fieldFilter)
943  {
944  found = true;
945  break;
946  }
947  }
948 
949 
950  if(found)
951  {
952  //check field reject filter list
953 
954  found = true; //accept if no filter list
955  for(const auto &fieldFilter : fieldRejectList)
956  if(fieldFilter[0] == '*') //leading wildcard
957  {
958  if(fieldNode.first ==
959  fieldFilter.substr(1))
960  {
961  found = false; //reject if match
962  break;
963  }
964  }
965  else if(fieldFilter.size() &&
966  fieldFilter[fieldFilter.size()-1] == '*') //trailing wildcard
967  {
968  if(fieldNode.first.substr(0,fieldFilter.size()-1) ==
969  fieldFilter.substr(0,fieldFilter.size()-1))
970  {
971  found = false; //reject if match
972  break;
973  }
974  }
975  else //no leading wildcard
976  {
977  if(fieldNode.first == fieldFilter)
978  {
979  found = false; //reject if match
980  break;
981  }
982  }
983 
984  }
985 
986  //if found, guaranteed field (all these fields must be common for UIDs in same table)
987  if(found)
988  {
989  fieldCandidateList.push_back(
991  tableName,
992  recordList[i],
993  fieldNode.first,
994  "", //relative path, not including columnName_
995  &fieldNode.second.getColumnInfo()
996  ));
997  fieldCount.push_back(-1); //mark guaranteed field
998  }
999  }
1000  //else // not first uid record, do not need to check, must be same as first record!
1001 
1002  }
1003  else if(depth > 0 &&
1004  fieldNode.second.isUIDLinkNode() &&
1005  !fieldNode.second.isDisconnected())
1006  {
1007  //__MOUT__ << "isUIDLinkNode " << fieldNode.first << std::endl;
1008  fieldNode.second.recursiveGetCommonFields(
1009  fieldCandidateList,
1010  fieldCount,
1011  fieldAcceptList,
1012  fieldRejectList,
1013  depth,
1014  fieldNode.first + "/", //relativePathBase
1015  !i //launch inFirstRecord (or not) depth search
1016  );
1017  }
1018  }
1019 
1020  }
1021 
1022  //__MOUT__ << "======================= check for count = " <<
1023  // (int)recordList.size() << std::endl;
1024 
1025  //loop through all field candidates
1026  // remove those with <field count> != num of records
1027  for(unsigned int i=0;i<fieldCandidateList.size();++i)
1028  {
1029  //__MOUT__ << "Checking " << fieldCandidateList[i].relativePath_ <<
1030  // fieldCandidateList[i].columnName_ << " = " <<
1031  // fieldCount[i] << std::endl;
1032  if(fieldCount[i] != -1 &&
1033  fieldCount[i] != (int)recordList.size())
1034  {
1035  //__MOUT__ << "Erasing " << fieldCandidateList[i].relativePath_ <<
1036  // fieldCandidateList[i].columnName_ << std::endl;
1037 
1038  fieldCount.erase(fieldCount.begin() + i);
1039  fieldCandidateList.erase(fieldCandidateList.begin() + i);
1040  --i; //rewind to look at next after deleted
1041  }
1042  }
1043 
1044  for(unsigned int i=0;i<fieldCandidateList.size();++i)
1045  __MOUT__ << "Final " << fieldCandidateList[i].relativePath_ <<
1046  fieldCandidateList[i].columnName_ << std::endl;
1047 
1048  return fieldCandidateList;
1049 }
1050 
1051 //==============================================================================
1052 //getUniqueValuesForField
1053 //
1054 // returns sorted unique values for the specified records and field
1055 //
1056 std::set<std::string /*unique-value*/> ConfigurationTree::getUniqueValuesForField(
1057  const std::vector<std::string /*relative-path*/> &recordList,
1058  const std::string &fieldName) const
1059 {
1060  //enforce that starting point is a table node
1061  if(!isConfigurationNode())
1062  {
1063  __SS__ << "Can only get getCommonFields from a table node! " <<
1064  "The node type is " << getNodeType() << std::endl;
1065  __MOUT__ << "\n" << ss.str() << std::endl;
1066  throw std::runtime_error(ss.str());
1067  }
1068 
1069  std::set<std::string /*unique-value*/> uniqueValues;
1070 
1071  //for each record in <record list>
1072  // emplace value at field into set
1073  //
1074  //return result
1075 
1076  for(unsigned int i=0;i<recordList.size();++i)
1077  {
1078  __MOUT__ << "Checking " << recordList[i] << std::endl;
1079 
1080  //Note: that ConfigurationTree maps both fields associated with a link
1081  // to the same node instance.
1082  //The behavior is likely not expected as response for this function..
1083  // so for links return actual value for field name specified
1084  // i.e. if Table of link is requested give that; if linkID is requested give that.
1085  //use TRUE in getValueAsString for proper behavior
1086  uniqueValues.emplace(getNode(recordList[i]).getNode(fieldName).getValueAsString(true));
1087  }
1088 
1089  return uniqueValues;
1090 }
1091 
1092 //==============================================================================
1093 //recursiveGetCommonFields
1094 // wrapper is ...getCommonFields
1095 void ConfigurationTree::recursiveGetCommonFields(
1096  std::vector<ConfigurationTree::RecordField> &fieldCandidateList,
1097  std::vector<int> &fieldCount,
1098  const std::vector<std::string /*relative-path*/> &fieldAcceptList,
1099  const std::vector<std::string /*relative-path*/> &fieldRejectList,
1100  unsigned int depth,
1101  const std::string &relativePathBase,
1102  bool inFirstRecord
1103  ) const
1104 {
1105  --depth;
1106  //__MOUT__ << "relativePathBase " << relativePathBase << std::endl;
1107 
1108  // =====================
1109  // Start recursiveGetCommonFields()
1110  // --depth;
1111  // loop through all children
1112  // if isValueNode (value nodes are possible field candidates!)
1113  // if first uid record
1114  // add field to <field candidates list> if in <field filter list>
1115  // initial mark <field count> as 1
1116  // else
1117  // if field is in list,
1118  // increment count for field candidate
1119  // //?increment fields in list count for record
1120  // else if field is not in list, discard field
1121  // else if depth > 0 and is UID-Link
1122  // if Link Table/UID pair is not found in <field candidates list> (avoid endless loops through tree)
1123  // recursiveGetCommonFields()
1124  // =====================
1125 
1126 
1127  bool found; //used in loops
1128  auto tableName = getConfigurationName(); //all fields will share this table name
1129  auto uid = getUIDAsString(); //all fields will share this uid
1130  unsigned int j;
1131 
1132  auto recordChildren = getChildren();
1133  for(const auto &fieldNode : recordChildren)
1134  {
1135  if(fieldNode.second.isValueNode())
1136  {
1137  //skip author and record insertion time
1138  if(fieldNode.second.getColumnInfo().getType() ==
1139  ViewColumnInfo::TYPE_AUTHOR ||
1140  fieldNode.second.getColumnInfo().getType() ==
1141  ViewColumnInfo::TYPE_TIMESTAMP)
1142  continue;
1143 
1144  //__MOUT__ << "isValueNode " << fieldNode.first << std::endl;
1145  if(inFirstRecord) //first uid record
1146  {
1147  //check field accept filter list
1148  found = fieldAcceptList.size()?false:true; //accept if no filter list
1149  for(const auto &fieldFilter : fieldAcceptList)
1150  if(fieldFilter[0] == '*') //leading wildcard
1151  {
1152  if(fieldNode.first ==
1153  fieldFilter.substr(1))
1154  {
1155  found = true;
1156  break;
1157  }
1158  }
1159  else if(fieldFilter.size() &&
1160  fieldFilter[fieldFilter.size()-1] == '*') //trailing wildcard
1161  {
1162  if((relativePathBase + fieldNode.first).substr(
1163  0,fieldFilter.size()-1) ==
1164  fieldFilter.substr(0,fieldFilter.size()-1))
1165  {
1166  found = true;
1167  break;
1168  }
1169  }
1170  else //no leading wildcard
1171  {
1172  if((relativePathBase + fieldNode.first) ==
1173  fieldFilter)
1174  {
1175  found = true;
1176  break;
1177  }
1178  }
1179 
1180  if(found)
1181  {
1182  //check field reject filter list
1183 
1184  found = true; //accept if no filter list
1185  for(const auto &fieldFilter : fieldRejectList)
1186  if(fieldFilter[0] == '*') //leading wildcard
1187  {
1188  if(fieldNode.first ==
1189  fieldFilter.substr(1))
1190  {
1191  found = false; //reject if match
1192  break;
1193  }
1194  }
1195  else if(fieldFilter.size() &&
1196  fieldFilter[fieldFilter.size()-1] == '*') //trailing wildcard
1197  {
1198  if((relativePathBase + fieldNode.first).substr(
1199  0,fieldFilter.size()-1) ==
1200  fieldFilter.substr(0,fieldFilter.size()-1))
1201  {
1202  found = false; //reject if match
1203  break;
1204  }
1205  }
1206  else //no leading wildcard
1207  {
1208  if((relativePathBase + fieldNode.first) ==
1209  fieldFilter)
1210  {
1211  found = false; //reject if match
1212  break;
1213  }
1214  }
1215  }
1216 
1217  //if found, new field (since this is first record)
1218  if(found)
1219  {
1220  //__MOUT__ << "FOUND field " <<
1221  // (relativePathBase + fieldNode.first) << std::endl;
1222  fieldCandidateList.push_back(
1224  tableName,
1225  uid,
1226  fieldNode.first,
1227  relativePathBase, //relative path, not including columnName_
1228  &fieldNode.second.getColumnInfo()
1229  ));
1230  fieldCount.push_back(1); //init count to 1
1231  }
1232  }
1233  else //not first record
1234  {
1235  //if field is in <field candidates list>, increment <field count>
1236  // else ignore
1237  for(j=0;j<fieldCandidateList.size();++j)
1238  {
1239  if((relativePathBase + fieldNode.first) ==
1240  (fieldCandidateList[j].relativePath_ +
1241  fieldCandidateList[j].columnName_))
1242  {
1243  //__MOUT__ << "incrementing " << j <<
1244  // " " << fieldCandidateList[j].relativePath_ << std::endl;
1245  //found, so increment <field count>
1246  ++fieldCount[j];
1247  break;
1248  }
1249  }
1250  }
1251  }
1252  else if(depth > 0 &&
1253  fieldNode.second.isUIDLinkNode() &&
1254  !fieldNode.second.isDisconnected())
1255  {
1256  //__MOUT__ << "isUIDLinkNode " << (relativePathBase + fieldNode.first) << std::endl;
1257  fieldNode.second.recursiveGetCommonFields(
1258  fieldCandidateList,
1259  fieldCount,
1260  fieldAcceptList,
1261  fieldRejectList,
1262  depth,
1263  (relativePathBase + fieldNode.first) + "/", //relativePathBase
1264  inFirstRecord //continue inFirstRecord (or not) depth search
1265  );
1266  }
1267  }
1268 }
1269 
1270 //==============================================================================
1271 //getChildren
1272 // returns them in order encountered in the table
1273 // if filterMap criteria, then rejects any that do not meet all criteria
1274 std::vector<std::pair<std::string,ConfigurationTree> > ConfigurationTree::getChildren(
1275  std::map<std::string /*relative-path*/, std::string /*value*/> filterMap) const
1276 {
1277  std::vector<std::pair<std::string,ConfigurationTree> > retMap;
1278 
1279  //__MOUT__ << "Children of node: " << getValueAsString() << std::endl;
1280 
1281  bool filtering = filterMap.size();
1282  bool skip;
1283  std::string fieldValue;
1284 
1285  std::vector<std::string> childrenNames = getChildrenNames();
1286  for(auto &childName : childrenNames)
1287  {
1288  //__MOUT__ << "\tChild: " << childName << std::endl;
1289 
1290  if(filtering)
1291  {
1292  //if all criteria are not met, then skip
1293  skip = false;
1294 
1295  //for each filter, check value
1296  for(const auto &filterPair:filterMap)
1297  {
1298  std::string filterPath = childName + "/" + filterPair.first;
1299  try
1300  {
1301 
1302 
1303  //extract field value list
1304  std::istringstream f(filterPair.second);
1305 
1306  skip = true;
1307  //for each field check if any match
1308  while (getline(f, fieldValue, ','))
1309  {
1310  //Note: that ConfigurationTree maps both fields associated with a link
1311  // to the same node instance.
1312  //The behavior is likely not expected as response for this function..
1313  // so for links return actual value for field name specified
1314  // i.e. if Table of link is requested give that; if linkID is requested give that.
1315  //use TRUE in getValueAsString for proper behavior
1316  __MOUT__ << "\t\tCheck: " << filterPair.first <<
1317  " == " << fieldValue << " ??? " <<
1318  this->getNode(filterPath).getValueAsString(true) <<
1319  std::endl;
1320  if(this->getNode(filterPath).getValueAsString(true) ==
1321  ConfigurationView::decodeURIComponent(fieldValue))
1322  {
1323  //found a match for the field/value pair
1324  skip = false;
1325  break;
1326  }
1327  }
1328  }
1329  catch(...)
1330  {
1331  __SS__ << "Failed to access filter path '" <<
1332  filterPath << "' - aborting." << std::endl;
1333  __MOUT_ERR__ << "\n" << ss.str();
1334  throw std::runtime_error(ss.str());
1335  }
1336 
1337  if(skip) break; //no match for this field, so stop checking and skip this record
1338  }
1339 
1340  if(skip) continue; //skip this record
1341 
1342  __MOUT__ << "\tChild accepted: " << childName << std::endl;
1343  }
1344 
1345  retMap.push_back(std::pair<std::string,ConfigurationTree>(childName,
1346  this->getNode(childName, true)));
1347  }
1348 
1349  //__MOUT__ << "Done w/Children of node: " << getValueAsString() << std::endl;
1350  return retMap;
1351 }
1352 
1353 //==============================================================================
1354 //getChildren
1355 // returns them in order encountered in the table
1356 std::map<std::string,ConfigurationTree> ConfigurationTree::getChildrenMap(void) const
1357 {
1358  std::map<std::string,ConfigurationTree> retMap;
1359 
1360  //__MOUT__ << "Children of node: " << getValueAsString() << std::endl;
1361 
1362  std::vector<std::string> childrenNames = getChildrenNames();
1363  for(auto& childName : childrenNames)
1364  {
1365  //__MOUT__ << "\tChild: " << childName << std::endl;
1366  retMap.insert(std::pair<std::string,ConfigurationTree>(childName, this->getNode(childName)));
1367  }
1368 
1369  //__MOUT__ << "Done w/Children of node: " << getValueAsString() << std::endl;
1370  return retMap;
1371 }
1372 
1373 //==============================================================================
1374 bool ConfigurationTree::isConfigurationNode(void) const
1375 {
1376  return (row_ == ConfigurationView::INVALID && col_ == ConfigurationView::INVALID);
1377 }
1378 
1379 //==============================================================================
1380 //getChildrenNames
1381 // returns them in order encountered in the table
1382 std::vector<std::string> ConfigurationTree::getChildrenNames(void) const
1383 {
1384  std::vector<std::string> retSet;
1385 
1386  if(!configView_)
1387  {
1388  __SS__ << "Can not get children names of '" <<
1389  getValueAsString() <<
1390  "' with null configuration view pointer!" << std::endl;
1391  if(isLinkNode() && isDisconnected())
1392  ss << " This node is a disconnected link to " <<
1393  getDisconnectedTableName() << std::endl;
1394  __MOUT_ERR__ << "\n" << ss.str();
1395  throw std::runtime_error(ss.str());
1396  }
1397 
1398  if(row_ == ConfigurationView::INVALID && col_ == ConfigurationView::INVALID)
1399  {
1400  //this node is config node
1401  //so return all uid node strings that match groupId
1402 
1403  for(unsigned int r = 0; r<configView_->getNumberOfRows(); ++r)
1404  if(groupId_ == "" ||
1405  configView_->isEntryInGroup(r,childLinkIndex_,groupId_))
1406 // groupId_ == configView_->getDataView()[r][configView_->getColLinkGroupID(
1407 // childLinkIndex_)])
1408  retSet.push_back(configView_->getDataView()[r][configView_->getColUID()]);
1409  }
1410  else if(row_ == ConfigurationView::INVALID)
1411  throw std::runtime_error("Malformed ConfigurationTree");
1412  else if(col_ == ConfigurationView::INVALID)
1413  {
1414  //this node is uid node
1415  //so return all link and value nodes
1416 
1417  for(unsigned int c = 0; c<configView_->getNumberOfColumns(); ++c)
1418  if(c == configView_->getColUID() || //skip UID and linkID columns (only show link column, to avoid duplicates)
1419  configView_->getColumnInfo(c).isChildLinkGroupID() ||
1420  configView_->getColumnInfo(c).isChildLinkUID())
1421  continue;
1422  else
1423  retSet.push_back(configView_->getColumnInfo(c).getName());
1424  }
1425  else //this node is value node, so has no node to choose from
1426  {
1427  __MOUT__ << "\n\nError occurred looking for children of nodeName=" << getValueName() << "\n\n" << std::endl;
1428  throw std::runtime_error("Invalid depth! getChildrenValues() called from a value point in the Configuration Tree."); // this node is value node, cant go any deeper!
1429  }
1430 
1431  return retSet;
1432 }
1433 
1434 
1435 
1436 
1437 
1438