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