otsdaq  v1_01_04
 All Classes Namespaces Functions
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't 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't 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't 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't 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't 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't 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't 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(!configView_)
351  {
352  __SS__ << "Can't get fixed choices of node with no config view pointer!" << std::endl;
353  throw std::runtime_error(ss.str());
354  }
355 
356  if(getValueType() != ViewColumnInfo::TYPE_FIXED_CHOICE_DATA &&
357  getValueType() != ViewColumnInfo::TYPE_BITMAP_DATA &&
358  !isLinkNode())
359  {
360  __SS__ << "Can't get fixed choices of node with value type of '" <<
361  getValueType() << ".' Node must be a link or a value node with type '" <<
362  ViewColumnInfo::TYPE_BITMAP_DATA << "' or '" <<
363  ViewColumnInfo::TYPE_FIXED_CHOICE_DATA << ".'" << std::endl;
364  throw std::runtime_error(ss.str());
365  }
366 
367  std::vector<std::string> retVec;
368 
369  if(isLinkNode())
370  {
371  if(!linkParentConfig_)
372  {
373  __SS__ << "Can't get fixed choices of node with no parent config view pointer!" << std::endl;
374  throw std::runtime_error(ss.str());
375  }
376 
377  //__COUT__ << getChildLinkIndex() << std::endl;
378  //__COUT__ << linkColName_ << std::endl;
379 
380  //for links, col_ = -1, column c needs to change (to ChildLink column of pair)
381  // get column from parent config pointer
382 
383  const ConfigurationView* parentView = &(linkParentConfig_->getView());
384  int c = parentView->findCol(linkColName_);
385 
386  std::pair<unsigned int /*link col*/, unsigned int /*link id col*/> linkPair;
387  bool isGroupLink;
388  parentView->getChildLink(c, isGroupLink, linkPair);
389  c = linkPair.first;
390 
391  std::vector<std::string> choices = parentView->getColumnInfo(c).getDataChoices();
392  for(const auto &choice:choices)
393  retVec.push_back(choice);
394 
395  return retVec;
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't 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(row_ == ConfigurationView::INVALID && col_ == ConfigurationView::INVALID)
690  {
691  if(!configView_)
692  {
693  __SS__ << "Missing configView pointer! Likely attempting to access a child node through a disconnected link node." << std::endl;
694  __COUT_ERR__ << "\n" << ss.str();
695  throw std::runtime_error(ss.str());
696  }
697 
698  //this node is config node, so return uid node considering groupid
699  return recurse(ConfigurationTree(
700  configMgr_,
701  configuration_,
702  "", //no new groupId string, not a link
703  0 /*linkParentConfig_*/,
704  "", //link node name, not a link
705  "", //link node value, not a link
706  ConfigurationView::INVALID /*linkBackRow_*/, ConfigurationView::INVALID /*linkBackCol_*/,
707  "", //ignored disconnected target name, not a link
708  "", //ignored disconnected link id, not a link
709  "",
710  //if this node is group config node, consider that when getting rows
711  (groupId_ == "")?
712  configView_->findRow(configView_->getColUID(),nodeName)
713  : configView_->findRowInGroup(configView_->getColUID(),
714  nodeName,groupId_,childLinkIndex_) ),
715  childPath, doNotThrowOnBrokenUIDLinks);
716  }
717  else if(row_ == ConfigurationView::INVALID)
718  {
719  __SS__ << "Malformed ConfigurationTree" << std::endl;
720  __COUT_ERR__ << "\n" << ss.str();
721  throw std::runtime_error(ss.str());
722  }
723  else if(col_ == ConfigurationView::INVALID)
724  {
725  //this node is uid node, so return link, group link, disconnected, or value node
726 
727  //__COUT__ << "nodeName=" << nodeName << " " << nodeName.length() << std::endl;
728 
729  //if the value is a unique link ..
730  //return a uid node!
731  //if the value is a group link
732  //return a config node with group string
733  //else.. return value node
734 
735  if(!configView_)
736  {
737  __SS__ << "Missing configView pointer! Likely attempting to access a child node through a disconnected link node." << std::endl;
738  __COUT_ERR__ << "\n" << ss.str();
739  throw std::runtime_error(ss.str());
740  }
741 
742  unsigned int c = configView_->findCol(nodeName);
743  std::pair<unsigned int /*link col*/, unsigned int /*link id col*/> linkPair;
744  bool isGroupLink, isLink;
745  if((isLink = configView_->getChildLink(c, isGroupLink, linkPair)) &&
746  !isGroupLink)
747  {
748  //__COUT__ << "nodeName=" << nodeName << " " << nodeName.length() << std::endl;
749  //is a unique link, return uid node in new configuration
750  // need new configuration pointer
751  // and row of linkUID in new configuration
752 
753  const ConfigurationBase* childConfig;
754  try
755  {
756  childConfig = configMgr_->getConfigurationByName(configView_->getDataView()[row_][linkPair.first]);
757  childConfig->getView(); //get view as a test for an active view
758 
759  if(doNotThrowOnBrokenUIDLinks) //try a test of getting row
760  {
761  childConfig->getView().findRow(childConfig->getView().getColUID(),
762  configView_->getDataView()[row_][linkPair.second]);
763  }
764  }
765  catch(...)
766  {
767  // __COUT_WARN__ << "Found disconnected node! (" << nodeName <<
768  // ":" << configView_->getDataView()[row_][linkPair.first] << ")" <<
769  // " at entry with UID " <<
770  // configView_->getDataView()[row_][configView_->getColUID()] <<
771  // std::endl;
772  //do not recurse further
773  return ConfigurationTree(
774  configMgr_,
775  0,
776  "",
777  configuration_, //linkParentConfig_
778  nodeName,
779  configView_->getDataView()[row_][c], //this the link node field associated value (matches targeted column)
780  row_ /*linkBackRow_*/, c /*linkBackCol_*/,
781  configView_->getDataView()[row_][linkPair.first], //give disconnected target name
782  configView_->getDataView()[row_][linkPair.second], //give disconnected link ID
783  configView_->getColumnInfo(c).getChildLinkIndex());
784  }
785 
786  return recurse(
787  ConfigurationTree( //this is a link node
788  configMgr_,
789  childConfig,
790  "", //no new groupId string
791  configuration_, //linkParentConfig_
792  nodeName, //this is a link node
793  configView_->getDataView()[row_][c], //this the link node field associated value (matches targeted column)
794  row_ /*linkBackRow_*/, c /*linkBackCol_*/,
795  "", //ignore since is connected
796  "", //ignore since is connected
797  configView_->getColumnInfo(c).getChildLinkIndex(),
798  childConfig->getView().findRow(childConfig->getView().getColUID(),
799  configView_->getDataView()[row_][linkPair.second])
800  ),
801  childPath, doNotThrowOnBrokenUIDLinks);
802  }
803  else if(isLink)
804  {
805  //__COUT__ << "nodeName=" << nodeName << " " << nodeName.length() << std::endl;
806  //is a group link, return new configuration with group string
807  // need new configuration pointer
808  // and group string
809 
810  const ConfigurationBase* childConfig;
811  try
812  {
813  childConfig = configMgr_->getConfigurationByName(
814  configView_->getDataView()[row_][linkPair.first]);
815  childConfig->getView(); //get view as a test for an active view
816  }
817  catch(...)
818  {
819  if(configView_->getDataView()[row_][linkPair.first] !=
820  ViewColumnInfo::DATATYPE_LINK_DEFAULT)
821  __COUT_WARN__ << "Found disconnected node! Failed link target from nodeName=" <<
822  nodeName << " to table:id=" <<
823  configView_->getDataView()[row_][linkPair.first] << ":" <<
824  configView_->getDataView()[row_][linkPair.second] <<
825  std::endl;
826 
827  //do not recurse further
828  return ConfigurationTree(configMgr_,0,
829  configView_->getDataView()[row_][linkPair.second], //groupID
830  configuration_, //linkParentConfig_
831  nodeName,
832  configView_->getDataView()[row_][c], //this the link node field associated value (matches targeted column)
833  row_ /*linkBackRow_*/, c /*linkBackCol_*/,
834  configView_->getDataView()[row_][linkPair.first], //give disconnected target name
835  configView_->getDataView()[row_][linkPair.second], //give disconnected target name
836  configView_->getColumnInfo(c).getChildLinkIndex()
837  );
838  }
839 
840  return recurse(
841  ConfigurationTree( //this is a link node
842  configMgr_,
843  childConfig,
844  configView_->getDataView()[row_][linkPair.second], //groupId string
845  configuration_, //linkParentConfig_
846  nodeName, //this is a link node
847  configView_->getDataView()[row_][c], //this the link node field associated value (matches targeted column)
848  row_ /*linkBackRow_*/, c /*linkBackCol_*/,
849  "", //ignore since is connected
850  "", //ignore since is connected
851  configView_->getColumnInfo(c).getChildLinkIndex()
852  ),
853  childPath, doNotThrowOnBrokenUIDLinks);
854  }
855  else
856  {
857  //__COUT__ << "nodeName=" << nodeName << " " << nodeName.length() << std::endl;
858  //return value node
859  return ConfigurationTree(
860  configMgr_,
861  configuration_,"",
862  0 /*linkParentConfig_*/,
863  "","",
864  ConfigurationView::INVALID /*linkBackRow_*/, ConfigurationView::INVALID /*linkBackCol_*/,
865  "",""/*disconnectedLinkID*/,"",
866  row_,c);
867  }
868  }
869 
870  }
871  catch(std::runtime_error &e)
872  {
873  __SS__ << "\n\nError occurred descending from node '" << getValue() <<
874  "' in table '" << getConfigurationName() <<
875  "' looking for child '" << nodeName << "'\n\n" << std::endl;
876  ss << "--- Additional error detail: \n\n" << e.what() << std::endl;
877  throw std::runtime_error(ss.str());
878  }
879  catch(...)
880  {
881  __SS__ << "\n\nError occurred descending from node '" << getValue() <<
882  "' in table '" << getConfigurationName() <<
883  "' looking for child '" << nodeName << "'\n\n" << std::endl;
884  throw std::runtime_error(ss.str());
885  }
886 
887  //this node is value node, so has no node to choose from
888  __SS__ << "\n\nError occurred descending from node '" << getValue() <<
889  "' in table '" << getConfigurationName() <<
890  "' looking for child '" << nodeName << "'\n\n" <<
891  "Invalid depth! getNode() called from a value point in the Configuration Tree." << std::endl;
892  throw std::runtime_error(ss.str()); // this node is value node, cant go any deeper!
893 }
894 
895 
896 //==============================================================================
897 ConfigurationTree ConfigurationTree::getBackNode(std::string nodeName, unsigned int backSteps) const
898 {
899  for(unsigned int i=0; i<backSteps; i++)
900  nodeName = nodeName.substr(0, nodeName.find_last_of('/'));
901 
902  return getNode(nodeName);
903 }
904 
905 //==============================================================================
906 //isValueNode
907 // if true, then this is a leaf node, i.e. there can be no children, only a value
908 bool ConfigurationTree::isValueNode(void) const
909 {
910  return (row_ != ConfigurationView::INVALID && col_ != ConfigurationView::INVALID);
911 }
912 
913 //==============================================================================
914 //isDisconnected
915 // if true, then this is a disconnected node, i.e. there is a configuration link missing
916 // (this is possible when the configuration is loaded in stages and the complete tree
917 // may not be available, yet)
918 bool ConfigurationTree::isDisconnected(void) const
919 {
920  if(!isLinkNode())
921  {
922  __SS__ << "\n\nError occurred testing link connection at node with value '" <<
923  getValue() <<
924  "' in table '" << getConfigurationName() <<
925  "'\n\n" << std::endl;
926  ss << "This is not a Link node! It is node type '" <<
927  getNodeType() << ".' Only a Link node can be disconnected." << std::endl;
928  //__COUT__ << "\n" << ss.str();
929  throw std::runtime_error(ss.str());
930  }
931 
932  return !configuration_ || !configView_;
933 }
934 
935 //==============================================================================
936 //isLinkNode
937 // if true, then this is a link node
938 bool ConfigurationTree::isLinkNode(void) const
939 {
940  return linkColName_ != "";
941 }
942 //
943 //==============================================================================
944 //getNodeType
945 // return node type as string
946 const std::string ConfigurationTree::NODE_TYPE_GROUP_TABLE = "GroupConfigurationNode";
947 const std::string ConfigurationTree::NODE_TYPE_TABLE = "ConfigurationNode";
948 const std::string ConfigurationTree::NODE_TYPE_GROUP_LINK = "GroupLinkNode";
949 const std::string ConfigurationTree::NODE_TYPE_UID_LINK = "UIDLinkNode";
950 const std::string ConfigurationTree::NODE_TYPE_VALUE = "ValueNode";
951 const std::string ConfigurationTree::NODE_TYPE_UID = "UIDNode";
952 std::string ConfigurationTree::getNodeType(void) const
953 {
954  if(isConfigurationNode() && groupId_ != "") return ConfigurationTree::NODE_TYPE_GROUP_TABLE;
955  if(isConfigurationNode()) return ConfigurationTree::NODE_TYPE_TABLE;
956  if(isGroupLinkNode()) return ConfigurationTree::NODE_TYPE_GROUP_LINK;
957  if(isLinkNode()) return ConfigurationTree::NODE_TYPE_UID_LINK;
958  if(isValueNode()) return ConfigurationTree::NODE_TYPE_VALUE;
959  return ConfigurationTree::NODE_TYPE_UID;
960 }
961 
962 //==============================================================================
963 //isGroupLinkNode
964 // if true, then this is a group link node
965 bool ConfigurationTree::isGroupLinkNode(void) const
966 {
967  return (isLinkNode() && groupId_ != "");
968 }
969 
970 //==============================================================================
971 //isUIDLinkNode
972 // if true, then this is a uid link node
973 bool ConfigurationTree::isUIDLinkNode(void) const
974 {
975  return (isLinkNode() && groupId_ == "");
976 }
977 
978 //==============================================================================
979 //isUIDNode
980 // if true, then this is a uid node
981 bool ConfigurationTree::isUIDNode(void) const
982 {
983  return (row_ != ConfigurationView::INVALID && col_ == ConfigurationView::INVALID);
984 }
985 
986 
987 //==============================================================================
988 //getCommonFields
989 // wrapper for ...recursiveGetCommonFields
990 //
991 // returns common fields in order encountered
992 // including through UID links depending on depth specified
993 //
994 // Field := {Table, UID, Column Name, Relative Path, ViewColumnInfo}
995 //
996 // if fieldAcceptList or fieldRejectList are not empty,
997 // then reject any that are not in field accept filter list
998 // and reject any that are in field reject filter list
999 //
1000 // will only go to specified depth looking for fields
1001 // (careful to prevent infinite loops in tree navigation)
1002 //
1003 std::vector<ConfigurationTree::RecordField> ConfigurationTree::getCommonFields(
1004  const std::vector<std::string /*uid*/> &recordList,
1005  const std::vector<std::string /*relative-path*/> &fieldAcceptList,
1006  const std::vector<std::string /*relative-path*/> &fieldRejectList,
1007  unsigned int depth) const
1008 {
1009  //enforce that starting point is a table node
1010  if(!isConfigurationNode())
1011  {
1012  __SS__ << "Can only get getCommonFields from a table node! " <<
1013  "The node type is " << getNodeType() << std::endl;
1014  __COUT__ << "\n" << ss.str() << std::endl;
1015  throw std::runtime_error(ss.str());
1016  }
1017 
1018  std::vector<ConfigurationTree::RecordField> fieldCandidateList;
1019  std::vector<int> fieldCount; //-1 := guaranteed, else count must match num of records
1020 
1021  --depth; //decrement for recursion
1022 
1023  //for each record in <record list>
1024  // loop through all record's children
1025  // if isValueNode (value nodes are possible field candidates!)
1026  // if first uid record
1027  // add field to <field candidates list> if in <field filter list>
1028  // mark <field count> as guaranteed -1 (all these fields must be common for UIDs in same table)
1029  // else not first uid record, do not need to check, must be same as first record!
1030  // else if depth > 0 and UID-Link Node
1031  // recursively (call recursiveGetCommonFields())
1032  // =====================
1033  // Start recursiveGetCommonFields()
1034  // --depth;
1035  // loop through all children
1036  // if isValueNode (value nodes are possible field candidates!)
1037  // if first uid record
1038  // add field to <field candidates list> if in <field filter list>
1039  // initial mark <field count> as 1
1040  // else
1041  // if field is in <field candidates list>,
1042  // increment <field count> for field candidate
1043  // else if field is not in list, ignore field
1044  // else if depth > 0 and is UID-Link
1045  // if Link Table/UID pair is not found in <field candidates list> (avoid endless loops through tree)
1046  // recursiveGetCommonFields()
1047  // =====================
1048  //
1049  //
1050  //loop through all field candidates
1051  // remove those with <field count> != num of records
1052  //
1053  //
1054  //return result
1055 
1056  bool found; //used in loops
1057  auto tableName = getConfigurationName(); //all records will share this table name
1058 
1059  for(unsigned int i=0;i<recordList.size();++i)
1060  {
1061  //__COUT__ << "Checking " << recordList[i] << std::endl;
1062 
1063  auto recordChildren = getNode(recordList[i]).getChildren();
1064  for(const auto &fieldNode : recordChildren)
1065  {
1066  // __COUT__ << "All... " << fieldNode.second.getNodeType() <<
1067  // " -- " << fieldNode.first << std::endl;
1068  if(fieldNode.second.isValueNode())
1069  {
1070  //skip author and record insertion time
1071  if(fieldNode.second.getColumnInfo().getType() ==
1072  ViewColumnInfo::TYPE_AUTHOR ||
1073  fieldNode.second.getColumnInfo().getType() ==
1074  ViewColumnInfo::TYPE_TIMESTAMP)
1075  continue;
1076 
1077  //__COUT__ << "isValueNode " << fieldNode.first << std::endl;
1078  if(!i) //first uid record
1079  {
1080  //check field accept filter list
1081  found = fieldAcceptList.size()?false:true; //accept if no filter list
1082  for(const auto &fieldFilter : fieldAcceptList)
1083  if(ConfigurationTree::wildCardMatch(
1084  fieldFilter,fieldNode.first))
1085  {
1086  found = true;
1087  break;
1088  }
1089 // if(fieldFilter[0] == '*') //leading wildcard
1090 // {
1091 // if(fieldNode.first ==
1092 // fieldFilter.substr(1))
1093 // {
1094 // found = true;
1095 // break;
1096 // }
1097 // }
1098 // else if(fieldFilter.size() &&
1099 // fieldFilter[fieldFilter.size()-1] == '*') //trailing wildcard
1100 // {
1101 // if(fieldNode.first.substr(0,fieldFilter.size()-1) ==
1102 // fieldFilter.substr(0,fieldFilter.size()-1))
1103 // {
1104 // found = true;
1105 // break;
1106 // }
1107 // }
1108 // else //no leading wildcard
1109 // {
1110 // if(fieldNode.first == fieldFilter)
1111 // {
1112 // found = true;
1113 // break;
1114 // }
1115 // }
1116 
1117 
1118  if(found)
1119  {
1120  //check field reject filter list
1121 
1122  found = true; //accept if no filter list
1123  for(const auto &fieldFilter : fieldRejectList)
1124  if(ConfigurationTree::wildCardMatch(
1125  fieldFilter,fieldNode.first))
1126  {
1127  found = false; //reject if match
1128  break;
1129  }
1130 
1131 // if(fieldFilter[0] == '*') //leading wildcard
1132 // {
1133 // if(fieldNode.first ==
1134 // fieldFilter.substr(1))
1135 // {
1136 // found = false; //reject if match
1137 // break;
1138 // }
1139 // }
1140 // else if(fieldFilter.size() &&
1141 // fieldFilter[fieldFilter.size()-1] == '*') //trailing wildcard
1142 // {
1143 // if(fieldNode.first.substr(0,fieldFilter.size()-1) ==
1144 // fieldFilter.substr(0,fieldFilter.size()-1))
1145 // {
1146 // found = false; //reject if match
1147 // break;
1148 // }
1149 // }
1150 // else //no leading wildcard
1151 // {
1152 // if(fieldNode.first == fieldFilter)
1153 // {
1154 // found = false; //reject if match
1155 // break;
1156 // }
1157 // }
1158 
1159  }
1160 
1161  //if found, guaranteed field (all these fields must be common for UIDs in same table)
1162  if(found)
1163  {
1164  fieldCandidateList.push_back(
1166  tableName,
1167  recordList[i],
1168  fieldNode.first,
1169  "", //relative path, not including columnName_
1170  &fieldNode.second.getColumnInfo()
1171  ));
1172  fieldCount.push_back(-1); //mark guaranteed field
1173  }
1174  }
1175  //else // not first uid record, do not need to check, must be same as first record!
1176 
1177  }
1178  else if(depth > 0 &&
1179  fieldNode.second.isUIDLinkNode() &&
1180  !fieldNode.second.isDisconnected())
1181  {
1182  //__COUT__ << "isUIDLinkNode " << fieldNode.first << std::endl;
1183  fieldNode.second.recursiveGetCommonFields(
1184  fieldCandidateList,
1185  fieldCount,
1186  fieldAcceptList,
1187  fieldRejectList,
1188  depth,
1189  fieldNode.first + "/", //relativePathBase
1190  !i //launch inFirstRecord (or not) depth search
1191  );
1192  }
1193  }
1194 
1195  }
1196 
1197  //__COUT__ << "======================= check for count = " <<
1198  // (int)recordList.size() << std::endl;
1199 
1200  //loop through all field candidates
1201  // remove those with <field count> != num of records
1202  for(unsigned int i=0;i<fieldCandidateList.size();++i)
1203  {
1204  //__COUT__ << "Checking " << fieldCandidateList[i].relativePath_ <<
1205  // fieldCandidateList[i].columnName_ << " = " <<
1206  // fieldCount[i] << std::endl;
1207  if(fieldCount[i] != -1 &&
1208  fieldCount[i] != (int)recordList.size())
1209  {
1210  //__COUT__ << "Erasing " << fieldCandidateList[i].relativePath_ <<
1211  // fieldCandidateList[i].columnName_ << std::endl;
1212 
1213  fieldCount.erase(fieldCount.begin() + i);
1214  fieldCandidateList.erase(fieldCandidateList.begin() + i);
1215  --i; //rewind to look at next after deleted
1216  }
1217  }
1218 
1219 // for(unsigned int i=0;i<fieldCandidateList.size();++i)
1220 // __COUT__ << "Final " << fieldCandidateList[i].relativePath_ <<
1221 // fieldCandidateList[i].columnName_ << std::endl;
1222 
1223  return fieldCandidateList;
1224 }
1225 
1226 //==============================================================================
1227 //getUniqueValuesForField
1228 //
1229 // returns sorted unique values for the specified records and field
1230 //
1231 std::set<std::string /*unique-value*/> ConfigurationTree::getUniqueValuesForField(
1232  const std::vector<std::string /*relative-path*/> &recordList,
1233  const std::string &fieldName) const
1234 {
1235  //enforce that starting point is a table node
1236  if(!isConfigurationNode())
1237  {
1238  __SS__ << "Can only get getCommonFields from a table node! " <<
1239  "The node type is " << getNodeType() << std::endl;
1240  __COUT__ << "\n" << ss.str() << std::endl;
1241  throw std::runtime_error(ss.str());
1242  }
1243 
1244  std::set<std::string /*unique-value*/> uniqueValues;
1245 
1246  //for each record in <record list>
1247  // emplace value at field into set
1248  //
1249  //return result
1250 
1251  for(unsigned int i=0;i<recordList.size();++i)
1252  {
1253  __COUT__ << "Checking " << recordList[i] << std::endl;
1254 
1255  //Note: that ConfigurationTree maps both fields associated with a link
1256  // to the same node instance.
1257  //The behavior is likely not expected as response for this function..
1258  // so for links return actual value for field name specified
1259  // i.e. if Table of link is requested give that; if linkID is requested give that.
1260  //use TRUE in getValueAsString for proper behavior
1261  uniqueValues.emplace(getNode(recordList[i]).getNode(fieldName).getValueAsString(true));
1262  }
1263 
1264  return uniqueValues;
1265 }
1266 
1267 //==============================================================================
1268 //recursiveGetCommonFields
1269 // wrapper is ...getCommonFields
1270 void ConfigurationTree::recursiveGetCommonFields(
1271  std::vector<ConfigurationTree::RecordField> &fieldCandidateList,
1272  std::vector<int> &fieldCount,
1273  const std::vector<std::string /*relative-path*/> &fieldAcceptList,
1274  const std::vector<std::string /*relative-path*/> &fieldRejectList,
1275  unsigned int depth,
1276  const std::string &relativePathBase,
1277  bool inFirstRecord
1278  ) const
1279 {
1280  --depth;
1281  //__COUT__ << "relativePathBase " << relativePathBase << std::endl;
1282 
1283  // =====================
1284  // Start recursiveGetCommonFields()
1285  // --depth;
1286  // loop through all children
1287  // if isValueNode (value nodes are possible field candidates!)
1288  // if first uid record
1289  // add field to <field candidates list> if in <field filter list>
1290  // initial mark <field count> as 1
1291  // else
1292  // if field is in list,
1293  // increment count for field candidate
1294  // //?increment fields in list count for record
1295  // else if field is not in list, discard field
1296  // else if depth > 0 and is UID-Link
1297  // if Link Table/UID pair is not found in <field candidates list> (avoid endless loops through tree)
1298  // recursiveGetCommonFields()
1299  // =====================
1300 
1301 
1302  bool found; //used in loops
1303  auto tableName = getConfigurationName(); //all fields will share this table name
1304  auto uid = getUIDAsString(); //all fields will share this uid
1305  unsigned int j;
1306 
1307  auto recordChildren = getChildren();
1308  for(const auto &fieldNode : recordChildren)
1309  {
1310  if(fieldNode.second.isValueNode())
1311  {
1312  //skip author and record insertion time
1313  if(fieldNode.second.getColumnInfo().getType() ==
1314  ViewColumnInfo::TYPE_AUTHOR ||
1315  fieldNode.second.getColumnInfo().getType() ==
1316  ViewColumnInfo::TYPE_TIMESTAMP)
1317  continue;
1318 
1319  //__COUT__ << "isValueNode " << fieldNode.first << std::endl;
1320  if(inFirstRecord) //first uid record
1321  {
1322  //check field accept filter list
1323  found = fieldAcceptList.size()?false:true; //accept if no filter list
1324  for(const auto &fieldFilter : fieldAcceptList)
1325  if(ConfigurationTree::wildCardMatch(
1326  fieldFilter,fieldNode.first))
1327  {
1328  found = true;
1329  break;
1330  }
1331 // if(fieldFilter[0] == '*') //leading wildcard
1332 // {
1333 // if(fieldNode.first ==
1334 // fieldFilter.substr(1))
1335 // {
1336 // found = true;
1337 // break;
1338 // }
1339 // }
1340 // else if(fieldFilter.size() &&
1341 // fieldFilter[fieldFilter.size()-1] == '*') //trailing wildcard
1342 // {
1343 // if((relativePathBase + fieldNode.first).substr(
1344 // 0,fieldFilter.size()-1) ==
1345 // fieldFilter.substr(0,fieldFilter.size()-1))
1346 // {
1347 // found = true;
1348 // break;
1349 // }
1350 // }
1351 // else //no leading wildcard
1352 // {
1353 // if((relativePathBase + fieldNode.first) ==
1354 // fieldFilter)
1355 // {
1356 // found = true;
1357 // break;
1358 // }
1359 // }
1360 
1361  if(found)
1362  {
1363  //check field reject filter list
1364 
1365  found = true; //accept if no filter list
1366  for(const auto &fieldFilter : fieldRejectList)
1367  if(ConfigurationTree::wildCardMatch(
1368  fieldFilter,fieldNode.first))
1369  {
1370  found = false; //reject if match
1371  break;
1372  }
1373 // if(fieldFilter[0] == '*') //leading wildcard
1374 // {
1375 // if(fieldNode.first ==
1376 // fieldFilter.substr(1))
1377 // {
1378 // found = false; //reject if match
1379 // break;
1380 // }
1381 // }
1382 // else if(fieldFilter.size() &&
1383 // fieldFilter[fieldFilter.size()-1] == '*') //trailing wildcard
1384 // {
1385 // if((relativePathBase + fieldNode.first).substr(
1386 // 0,fieldFilter.size()-1) ==
1387 // fieldFilter.substr(0,fieldFilter.size()-1))
1388 // {
1389 // found = false; //reject if match
1390 // break;
1391 // }
1392 // }
1393 // else //no leading wildcard
1394 // {
1395 // if((relativePathBase + fieldNode.first) ==
1396 // fieldFilter)
1397 // {
1398 // found = false; //reject if match
1399 // break;
1400 // }
1401 // }
1402  }
1403 
1404  //if found, new field (since this is first record)
1405  if(found)
1406  {
1407  //__COUT__ << "FOUND field " <<
1408  // (relativePathBase + fieldNode.first) << std::endl;
1409  fieldCandidateList.push_back(
1411  tableName,
1412  uid,
1413  fieldNode.first,
1414  relativePathBase, //relative path, not including columnName_
1415  &fieldNode.second.getColumnInfo()
1416  ));
1417  fieldCount.push_back(1); //init count to 1
1418  }
1419  }
1420  else //not first record
1421  {
1422  //if field is in <field candidates list>, increment <field count>
1423  // else ignore
1424  for(j=0;j<fieldCandidateList.size();++j)
1425  {
1426  if((relativePathBase + fieldNode.first) ==
1427  (fieldCandidateList[j].relativePath_ +
1428  fieldCandidateList[j].columnName_))
1429  {
1430  //__COUT__ << "incrementing " << j <<
1431  // " " << fieldCandidateList[j].relativePath_ << std::endl;
1432  //found, so increment <field count>
1433  ++fieldCount[j];
1434  break;
1435  }
1436  }
1437  }
1438  }
1439  else if(depth > 0 &&
1440  fieldNode.second.isUIDLinkNode() &&
1441  !fieldNode.second.isDisconnected())
1442  {
1443  //__COUT__ << "isUIDLinkNode " << (relativePathBase + fieldNode.first) << std::endl;
1444  fieldNode.second.recursiveGetCommonFields(
1445  fieldCandidateList,
1446  fieldCount,
1447  fieldAcceptList,
1448  fieldRejectList,
1449  depth,
1450  (relativePathBase + fieldNode.first) + "/", //relativePathBase
1451  inFirstRecord //continue inFirstRecord (or not) depth search
1452  );
1453  }
1454  }
1455 }
1456 
1457 //==============================================================================
1458 //getChildren
1459 // returns them in order encountered in the table
1460 // if filterMap criteria, then rejects any that do not meet all criteria
1461 std::vector<std::pair<std::string,ConfigurationTree> > ConfigurationTree::getChildren(
1462  std::map<std::string /*relative-path*/, std::string /*value*/> filterMap) const
1463 {
1464  std::vector<std::pair<std::string,ConfigurationTree> > retMap;
1465 
1466  //__COUT__ << "Children of node: " << getValueAsString() << std::endl;
1467 
1468  bool filtering = filterMap.size();
1469  bool skip;
1470  std::string fieldValue;
1471 
1472  std::vector<std::string> childrenNames = getChildrenNames();
1473  for(auto &childName : childrenNames)
1474  {
1475  //__COUT__ << "\tChild: " << childName << std::endl;
1476 
1477  if(filtering)
1478  {
1479  //if all criteria are not met, then skip
1480  skip = false;
1481 
1482  //for each filter, check value
1483  for(const auto &filterPair:filterMap)
1484  {
1485  std::string filterPath = childName + "/" + filterPair.first;
1486  try
1487  {
1488 
1489  //extract field value list
1490  std::istringstream f(filterPair.second);
1491 
1492  skip = true;
1493  //for each field check if any match
1494  while (getline(f, fieldValue, ','))
1495  {
1496  //Note: that ConfigurationTree maps both fields associated with a link
1497  // to the same node instance.
1498  //The behavior is likely not expected as response for this function..
1499  // so for links return actual value for field name specified
1500  // i.e. if Table of link is requested give that; if linkID is requested give that.
1501  //use TRUE in getValueAsString for proper behavior
1502 
1503  __COUT__ << "\t\tCheck: " << filterPair.first <<
1504  " == " << fieldValue << " ??? " <<
1505  this->getNode(filterPath).getValueAsString(true) <<
1506  std::endl;
1507 
1508  if(ConfigurationTree::wildCardMatch(
1509  ConfigurationView::decodeURIComponent(fieldValue),
1510  this->getNode(filterPath).getValueAsString(true) ))
1511  {
1512  //found a match for the field/value pair
1513  skip = false;
1514  break;
1515  }
1516 
1517 // if(this->getNode(filterPath).getValueAsString(true) ==
1518 // ConfigurationView::decodeURIComponent(fieldValue))
1519 // {
1520 // //found a match for the field/value pair
1521 // skip = false;
1522 // break;
1523 // }
1524  }
1525  }
1526  catch(...)
1527  {
1528  __SS__ << "Failed to access filter path '" <<
1529  filterPath << "' - aborting." << std::endl;
1530  __COUT_ERR__ << "\n" << ss.str();
1531  throw std::runtime_error(ss.str());
1532  }
1533 
1534  if(skip) break; //no match for this field, so stop checking and skip this record
1535  }
1536 
1537  if(skip) continue; //skip this record
1538 
1539  __COUT__ << "\tChild accepted: " << childName << std::endl;
1540  }
1541 
1542  retMap.push_back(std::pair<std::string,ConfigurationTree>(childName,
1543  this->getNode(childName, true)));
1544  }
1545 
1546  //__COUT__ << "Done w/Children of node: " << getValueAsString() << std::endl;
1547  return retMap;
1548 }
1549 
1550 //==============================================================================
1551 //wildCardMatch
1552 // find needle in haystack
1553 // allow needle to have leading and/or trailing wildcard '*'
1554 bool ConfigurationTree::wildCardMatch(const std::string& needle, const std::string& haystack)
1555 try
1556 {
1557 // __COUT__ << "\t\t wildCardMatch: " << needle <<
1558 // " =in= " << haystack << " ??? " <<
1559 // std::endl;
1560 
1561  if(needle.size() == 0)
1562  return true; //if empty needle, always "found"
1563 
1564  if(needle[0] == '*' && //leading wildcard
1565  needle[needle.size()-1] == '*' ) //and trailing wildcard
1566  return std::string::npos != haystack.find(needle.substr(1,needle.size()-2));
1567 
1568  if(needle[0] == '*') //leading wildcard
1569  return needle.substr(1) ==
1570  haystack.substr(haystack.size() - (needle.size()-1));
1571 
1572  if(needle[needle.size()-1] == '*') //trailing wildcard
1573  return needle.substr(0,needle.size()-1) ==
1574  haystack.substr(0,needle.size()-1);
1575 
1576  //else //no wildcards
1577  return needle == haystack;
1578 }
1579 catch(...)
1580 {
1581  return false; //if out of range
1582 }
1583 
1584 
1585 //==============================================================================
1586 //getChildren
1587 // returns them in order encountered in the table
1588 std::map<std::string,ConfigurationTree> ConfigurationTree::getChildrenMap(void) const
1589 {
1590  std::map<std::string,ConfigurationTree> retMap;
1591 
1592  //__COUT__ << "Children of node: " << getValueAsString() << std::endl;
1593 
1594  std::vector<std::string> childrenNames = getChildrenNames();
1595  for(auto& childName : childrenNames)
1596  {
1597  //__COUT__ << "\tChild: " << childName << std::endl;
1598  retMap.insert(std::pair<std::string,ConfigurationTree>(childName, this->getNode(childName)));
1599  }
1600 
1601  //__COUT__ << "Done w/Children of node: " << getValueAsString() << std::endl;
1602  return retMap;
1603 }
1604 
1605 //==============================================================================
1606 bool ConfigurationTree::isConfigurationNode(void) const
1607 {
1608  return (row_ == ConfigurationView::INVALID && col_ == ConfigurationView::INVALID);
1609 }
1610 
1611 //==============================================================================
1612 //getChildrenNames
1613 // returns them in order encountered in the table
1614 std::vector<std::string> ConfigurationTree::getChildrenNames(void) const
1615 {
1616  std::vector<std::string> retSet;
1617 
1618  if(!configView_)
1619  {
1620  __SS__ << "Can not get children names of '" <<
1621  getValueAsString() <<
1622  "' with null configuration view pointer!" << std::endl;
1623  if(isLinkNode() && isDisconnected())
1624  ss << " This node is a disconnected link to " <<
1625  getDisconnectedTableName() << std::endl;
1626  __COUT_ERR__ << "\n" << ss.str();
1627  throw std::runtime_error(ss.str());
1628  }
1629 
1630  if(row_ == ConfigurationView::INVALID && col_ == ConfigurationView::INVALID)
1631  {
1632  //this node is config node
1633  //so return all uid node strings that match groupId
1634 
1635  for(unsigned int r = 0; r<configView_->getNumberOfRows(); ++r)
1636  if(groupId_ == "" ||
1637  configView_->isEntryInGroup(r,childLinkIndex_,groupId_))
1638 // groupId_ == configView_->getDataView()[r][configView_->getColLinkGroupID(
1639 // childLinkIndex_)])
1640  retSet.push_back(configView_->getDataView()[r][configView_->getColUID()]);
1641  }
1642  else if(row_ == ConfigurationView::INVALID)
1643  {
1644  __SS__ << "Malformed ConfigurationTree" << std::endl;
1645  __COUT_ERR__ << ss.str();
1646  throw std::runtime_error(ss.str());
1647  }
1648  else if(col_ == ConfigurationView::INVALID)
1649  {
1650  //this node is uid node
1651  //so return all link and value nodes
1652 
1653  for(unsigned int c = 0; c<configView_->getNumberOfColumns(); ++c)
1654  if(c == configView_->getColUID() || //skip UID and linkID columns (only show link column, to avoid duplicates)
1655  configView_->getColumnInfo(c).isChildLinkGroupID() ||
1656  configView_->getColumnInfo(c).isChildLinkUID())
1657  continue;
1658  else
1659  retSet.push_back(configView_->getColumnInfo(c).getName());
1660  }
1661  else //this node is value node, so has no node to choose from
1662  {
1663  // this node is value node, cant go any deeper!
1664  __SS__ << "\n\nError occurred looking for children of nodeName=" << getValueName() << "\n\n" <<
1665  "Invalid depth! getChildrenValues() called from a value point in the Configuration Tree." << std::endl;
1666  __COUT_ERR__ << ss.str();
1667  throw std::runtime_error(ss.str());
1668  }
1669 
1670  return retSet;
1671 }
1672 
1673 
1674 //==============================================================================
1675 //getValueAsTreeNode
1676 // returns tree node for value of this node, treating the value
1677 // as a string for the absolute path string from root of tree
1678 ConfigurationTree ConfigurationTree::getValueAsTreeNode(void) const
1679 {
1680  //check if first character is a /, .. if so try to get value in tree
1681  // if exception, just take value
1682  //note: this call will throw an error, in effect, if not a "value" node
1683  if(!configView_)
1684  {
1685  __SS__ << "Invalid node for get value." << std::endl;
1686  __COUT__ << ss.str();
1687  throw std::runtime_error(ss.str());
1688  }
1689 
1690  std::string valueString = configView_->getValueAsString(row_,col_,true /* convertEnvironmentVariables */);
1691  //__COUT__ << valueString << std::endl;
1692  if(valueString.size() && valueString[0] == '/')
1693  {
1694  //__COUT__ << "Starts with '/' - check if valid tree path: " << valueString << std::endl;
1695  try
1696  {
1697  ConfigurationTree retNode = configMgr_->getNode(valueString);
1698  __COUT__ << "Found a valid tree path in value!" << std::endl;
1699  return retNode;
1700  }
1701  catch(...)
1702  {
1703  __SS__ << "Invalid tree path." << std::endl;
1704  //__COUT__ << ss.str();
1705  throw std::runtime_error(ss.str());
1706  }
1707  }
1708 
1709 
1710  {
1711  __SS__ << "Invalid value string '" << valueString <<
1712  "' - must start with a '/' character." << std::endl;
1713  throw std::runtime_error(ss.str());
1714  }
1715 }
1716 
1717 
1718 
1719 
1720 
1721 
1722 
1723 
1724 
1725 
1726