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