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