otsdaq  v2_01_00
ConfigurationView.cc
1 #include "otsdaq-core/ConfigurationDataFormats/ConfigurationView.h"
2 
3 #include <iostream>
4 #include <sstream>
5 #include <cstdlib>
6 #include <regex>
7 
8 using namespace ots;
9 
10 #undef __MF_SUBJECT__
11 #define __MF_SUBJECT__ "ConfigurationView"
12 #undef __MF_HDR__
13 #define __MF_HDR__ tableName_ << ":v" << version_ << ":" << __COUT_HDR_FL__
14 
15 const unsigned int ConfigurationView::INVALID = -1;
16 
17 //==============================================================================
18 ConfigurationView::ConfigurationView(const std::string &name)
19 : uniqueStorageIdentifier_ (""),
20  tableName_ (name),
21  version_ (ConfigurationVersion::INVALID),
22  comment_ (""),
23  author_ (""),
24  creationTime_ (time(0)),
25  lastAccessTime_ (0),
26  colUID_ (INVALID),
27  colStatus_ (INVALID),
28  colPriority_ (INVALID),
29  fillWithLooseColumnMatching_(false),
30  sourceColumnMismatchCount_ (0),
31  sourceColumnMissingCount_ (0)
32 {}
33 
34 //==============================================================================
35 ConfigurationView::~ConfigurationView(void)
36 {}
37 
38 //==============================================================================
39 //operator=
40 // Do NOT allow!... use ConfigurationView::copy
41 // copy is used to maintain consistency with version, creationTime, lastAccessTime, etc)
42 ConfigurationView& ConfigurationView::operator=(const ConfigurationView src)
43 {
44  __SS__ << "Invalid use of operator=... Should not directly copy a ConfigurationView. Please use ConfigurationView::copy(sourceView,author,comment)";
45  throw std::runtime_error(ss.str());
46 }
47 
48 //==============================================================================
49 ConfigurationView& ConfigurationView::copy(const ConfigurationView &src,
50  ConfigurationVersion destinationVersion,
51  const std::string &author)
52 {
53  tableName_ = src.tableName_;
54  version_ = destinationVersion;
55  comment_ = src.comment_;
56  author_ = author; //take new author
57  //creationTime_ = time(0); //don't change creation time
58  lastAccessTime_ = time(0);
59  columnsInfo_ = src.columnsInfo_;
60  theDataView_ = src.theDataView_;
61  sourceColumnNames_ = src.sourceColumnNames_;
62  init(); // verify consistency
63  return *this;
64 }
65 
66 //==============================================================================
67 //init
68 // Should be called after being filled to setup special members
69 // and verify consistency.
70 // e.g. identifying the UID column
71 //
72 // Note: this function also sanitizes yes/no, on/off, and true/false types
73 void ConfigurationView::init(void)
74 {
75  try
76  {
77  //verify column names are unique
78  // make set of names,.. and CommentDescription == COMMENT
79  std::set<std::string> colNameSet;
80  std::string capsColName, colName;
81  for(auto &colInfo: columnsInfo_)
82  {
83  colName = colInfo.getStorageName();
84  if(colName == "COMMENT_DESCRIPTION")
85  colName = "COMMENT";
86  capsColName = "";
87  for(unsigned int i=0;i<colName.size(); ++i)
88  {
89  if(colName[i] == '_') continue;
90  capsColName += colName[i];
91  }
92 
93  colNameSet.emplace(capsColName);
94  }
95 
96  if(colNameSet.size() != columnsInfo_.size())
97  {
98  __SS__ << "Configuration Error:\t" <<
99  " Columns names must be unique! There are " << columnsInfo_.size() <<
100  " columns and the unique name count is " << colNameSet.size() << std::endl;
101  __COUT_ERR__ << "\n" << ss.str();
102 
103  throw std::runtime_error(ss.str());
104  }
105 
106  getOrInitColUID(); //setup UID column
107  try
108  {
109  getOrInitColStatus(); //setup Status column
110  }
111  catch(...){} //ignore no Status column
112  try
113  {
114  getOrInitColPriority(); //setup Priority column
115  }
116  catch(...){} //ignore no Priority column
117 
118 
119  //require one comment column
120  unsigned int colPos;
121  if((colPos = findColByType(ViewColumnInfo::TYPE_COMMENT)) != INVALID)
122  {
123  if(columnsInfo_[colPos].getName() != "CommentDescription")
124  {
125  __SS__ << "Configuration Error:\t" << ViewColumnInfo::TYPE_COMMENT <<
126  " data type column must have name=" <<
127  "CommentDescription" << std::endl;
128  __COUT_ERR__ << "\n" << ss.str();
129  throw std::runtime_error(ss.str());
130  }
131 
132  if(findColByType(ViewColumnInfo::TYPE_COMMENT,colPos+1) != INVALID) //found two!
133  {
134  __SS__ << "Configuration Error:\t" << ViewColumnInfo::TYPE_COMMENT <<
135  " data type in column " <<
136  columnsInfo_[colPos].getName() <<
137  " is repeated. This is not allowed." << std::endl;
138  __COUT_ERR__ << "\n" << ss.str();
139  throw std::runtime_error(ss.str());
140  }
141 
142  if(colPos != getNumberOfColumns()-3)
143  {
144  __SS__ << "Configuration Error:\t" << ViewColumnInfo::TYPE_COMMENT <<
145  " data type column must be 3rd to last (in column " <<
146  getNumberOfColumns()-3 << ")." << std::endl;
147  __COUT_ERR__ << "\n" << ss.str();
148  throw std::runtime_error(ss.str());
149  }
150  }
151  else
152  {
153  __SS__ << "Configuration Error:\t" << ViewColumnInfo::TYPE_COMMENT <<
154  " data type column " <<" is missing. This is not allowed." << std::endl;
155  __COUT_ERR__ << "\n" << ss.str();
156  throw std::runtime_error(ss.str());
157  }
158 
159  //require one author column
160  if((colPos = findColByType(ViewColumnInfo::TYPE_AUTHOR)) != INVALID)
161  {
162  if(findColByType(ViewColumnInfo::TYPE_AUTHOR,colPos+1) != INVALID) //found two!
163  {
164  __SS__ << "Configuration Error:\t" << ViewColumnInfo::TYPE_AUTHOR <<
165  " data type in column " <<
166  columnsInfo_[colPos].getName() <<
167  " is repeated. This is not allowed." << std::endl;
168  __COUT_ERR__ << "\n" << ss.str();
169  throw std::runtime_error(ss.str());
170  }
171 
172  if(colPos != getNumberOfColumns()-2)
173  {
174  __SS__ << "Configuration Error:\t" << ViewColumnInfo::TYPE_AUTHOR <<
175  " data type column must be 2nd to last (in column " <<
176  getNumberOfColumns()-2 << ")." << std::endl;
177  __COUT_ERR__ << "\n" << ss.str();
178  throw std::runtime_error(ss.str());
179  }
180  }
181  else
182  {
183  __SS__ << "Configuration Error:\t" << ViewColumnInfo::TYPE_AUTHOR <<
184  " data type column " <<" is missing. This is not allowed." << std::endl;
185  __COUT_ERR__ << "\n" << ss.str();
186  throw std::runtime_error(ss.str());
187  }
188 
189  //require one timestamp column
190  if((colPos = findColByType(ViewColumnInfo::TYPE_TIMESTAMP)) != INVALID)
191  {
192  if(findColByType(ViewColumnInfo::TYPE_TIMESTAMP,colPos+1) != INVALID) //found two!
193  {
194  __SS__ << "Configuration Error:\t" << ViewColumnInfo::TYPE_TIMESTAMP <<
195  " data type in column " <<
196  columnsInfo_[colPos].getName() << " is repeated. This is not allowed." <<
197  std::endl;
198  __COUT_ERR__ << "\n" << ss.str();
199  throw std::runtime_error(ss.str());
200  }
201 
202  if(colPos != getNumberOfColumns()-1)
203  {
204  __SS__ << "Configuration Error:\t" << ViewColumnInfo::TYPE_TIMESTAMP <<
205  " data type column must be last (in column " <<
206  getNumberOfColumns()-1 << ")." << std::endl;
207  __COUT_ERR__ << "\n" << ss.str();
208  throw std::runtime_error(ss.str());
209  }
210  }
211  else
212  {
213  __SS__ << "Configuration Error:\t" << ViewColumnInfo::TYPE_TIMESTAMP <<
214  " data type column " <<" is missing. This is not allowed." << std::endl;
215  __COUT_ERR__ << "\n" << ss.str();
216  throw std::runtime_error(ss.str());
217  }
218 
219  //check that UID is really unique ID (no repeats)
220  // and ... allow letters, numbers, dash, underscore
221  // and ... force size 1
222  std::set<std::string /*uid*/> uidSet;
223  for(unsigned int row = 0; row < getNumberOfRows(); ++row)
224  {
225  if(uidSet.find(theDataView_[row][colUID_]) != uidSet.end())
226  {
227  __SS__ << ("Entries in UID are not unique. Specifically at row=" +
228  std::to_string(row) + " value=" + theDataView_[row][colUID_])<< std::endl;
229  __COUT_ERR__ << "\n" << ss.str();
230  throw std::runtime_error(ss.str());
231  }
232 
233  if(theDataView_[row][colUID_].size() == 0)
234  {
235  __SS__ << "An invalid UID '" << theDataView_[row][colUID_] << "' " <<
236  " was identified. UIDs must contain at least 1 character." <<
237  std::endl;
238  throw std::runtime_error(ss.str());
239  }
240 
241  for(unsigned int i=0;i<theDataView_[row][colUID_].size();++i)
242  if(!(
243  (theDataView_[row][colUID_][i] >= 'A' && theDataView_[row][colUID_][i] <= 'Z') ||
244  (theDataView_[row][colUID_][i] >= 'a' && theDataView_[row][colUID_][i] <= 'z') ||
245  (theDataView_[row][colUID_][i] >= '0' && theDataView_[row][colUID_][i] <= '9') ||
246  (theDataView_[row][colUID_][i] == '-' ||
247  theDataView_[row][colUID_][i] <= '_')
248  ))
249  {
250  __SS__ << "An invalid UID '" << theDataView_[row][colUID_] << "' " <<
251  " was identified. UIDs must contain only letters, numbers," <<
252  "dashes, and underscores." << std::endl;
253  throw std::runtime_error(ss.str());
254  }
255 
256  uidSet.insert(theDataView_[row][colUID_]);
257  }
258  if(uidSet.size() != getNumberOfRows())
259  {
260  __SS__ << "Entries in UID are not unique!" <<
261  "There are " << getNumberOfRows() <<
262  " rows and the unique UID count is " << uidSet.size() << std::endl;
263  __COUT_ERR__ << "\n" << ss.str();
264  throw std::runtime_error(ss.str());
265  }
266 
267  //check that any TYPE_UNIQUE_DATA columns are really unique (no repeats)
268  colPos = (unsigned int)-1;
269  while((colPos = findColByType(ViewColumnInfo::TYPE_UNIQUE_DATA,colPos+1)) != INVALID)
270  {
271  std::set<std::string /*unique data*/> uDataSet;
272  for(unsigned int row = 0; row < getNumberOfRows(); ++row)
273  {
274  if(uDataSet.find(theDataView_[row][colPos]) != uDataSet.end())
275  {
276  __SS__ << "Entries in Unique Data column " <<
277  columnsInfo_[colPos].getName() <<
278  (" are not unique. Specifically at row=" +
279  std::to_string(row) + " value=" + theDataView_[row][colPos]) <<
280  std::endl;
281  __COUT_ERR__ << "\n" << ss.str();
282  throw std::runtime_error(ss.str());
283  }
284  uDataSet.insert(theDataView_[row][colPos]);
285  }
286  if(uDataSet.size() != getNumberOfRows())
287  {
288  __SS__ << "Entries in Unique Data column " <<
289  columnsInfo_[colPos].getName() << " are not unique!" <<
290  "There are " << getNumberOfRows() <<
291  " rows and the unique data count is " << uDataSet.size() << std::endl;
292  __COUT_ERR__ << "\n" << ss.str();
293  throw std::runtime_error(ss.str());
294  }
295  }
296 
297  auto rowDefaults = getDefaultRowValues();
298 
299  //check that column types are well behaved
300  // - check that fixed choice data is one of choices
301  // - sanitize booleans
302  // - check that child link I are unique
303  // note: childLinkId refers to childLinkGroupIDs AND childLinkUIDs
304  std::set<std::string> groupIdIndexes, childLinkIndexes, childLinkIdLabels;
305  unsigned int groupIdIndexesCount = 0, childLinkIndexesCount = 0, childLinkIdLabelsCount = 0;
306  for(unsigned int col = 0; col < getNumberOfColumns(); ++col)
307  {
308  if(columnsInfo_[col].getType() == ViewColumnInfo::TYPE_FIXED_CHOICE_DATA)
309  {
310  const std::vector<std::string>& theDataChoices =
311  columnsInfo_[col].getDataChoices();
312 
313  //check if arbitrary values allowed
314  if(theDataChoices.size() && theDataChoices[0] ==
315  "arbitraryBool=1")
316  continue; //arbitrary values allowed
317 
318  bool found;
319  for(unsigned int row = 0; row < getNumberOfRows(); ++row)
320  {
321  found = false;
322  //check against default value first
323  if(theDataView_[row][col] == rowDefaults[col])
324  continue; //default is always ok
325 
326  for(const auto &choice:theDataChoices)
327  {
328  if(theDataView_[row][col] == choice)
329  {
330  found = true;
331  break;
332  }
333  }
334  if(!found)
335  {
336  __SS__ << "Configuration Error:\t'" << theDataView_[row][col] << "' in column " <<
337  columnsInfo_[col].getName() << " is not a valid Fixed Choice option. " <<
338  "Possible values are as follows: ";
339 
340  for(unsigned int i = 0; i < columnsInfo_[col].getDataChoices().size(); ++i)
341  {
342  if(i) ss << ", ";
343  ss << columnsInfo_[col].getDataChoices()[i];
344  }
345  ss << "." << std::endl;
346  __COUT_ERR__ << "\n" << ss.str();
347  throw std::runtime_error(ss.str());
348  }
349  }
350  }
351  if(columnsInfo_[col].isChildLink())
352  {
353  //check if forcing fixed choices
354 
355  const std::vector<std::string>& theDataChoices =
356  columnsInfo_[col].getDataChoices();
357 
358  //check if arbitrary values allowed
359  if(!theDataChoices.size() ||
360  theDataChoices[0] == "arbitraryBool=1")
361  continue; //arbitrary values allowed
362 
363  //skip one if arbitrary setting is embedded as first value
364  bool skipOne = (theDataChoices.size() &&
365  theDataChoices[0] == "arbitraryBool=0");
366  bool hasSkipped;
367 
368  bool found;
369  for(unsigned int row = 0; row < getNumberOfRows(); ++row)
370  {
371  found = false;
372 
373  hasSkipped = false;
374  for(const auto &choice:theDataChoices)
375  {
376  if(skipOne && !hasSkipped) {hasSkipped = true; continue;}
377 
378  if(theDataView_[row][col] == choice)
379  {
380  found = true;
381  break;
382  }
383  }
384  if(!found)
385  {
386  __SS__ << "Configuration Error:\t'" << theDataView_[row][col] << "' in column " <<
387  columnsInfo_[col].getName() << " is not a valid Fixed Choice option. " <<
388  "Possible values are as follows: ";
389 
390  for(unsigned int i = skipOne?1:0; i < columnsInfo_[col].getDataChoices().size(); ++i)
391  {
392  if(i == 1 + (skipOne?1:0)) ss << ", ";
393  ss << columnsInfo_[col].getDataChoices()[i];
394  }
395  ss << "." << std::endl;
396  __COUT_ERR__ << "\n" << ss.str();
397  throw std::runtime_error(ss.str());
398  }
399  }
400  }
401  else if(columnsInfo_[col].getType() == ViewColumnInfo::TYPE_ON_OFF)
402  for(unsigned int row = 0; row < getNumberOfRows(); ++row)
403  {
404  if (theDataView_[row][col] == "1" || theDataView_[row][col] == "on" || theDataView_[row][col] == "On" || theDataView_[row][col] == "ON")
405  theDataView_[row][col] = ViewColumnInfo::TYPE_VALUE_ON;
406  else if (theDataView_[row][col] == "0" || theDataView_[row][col] == "off" || theDataView_[row][col] == "Off" || theDataView_[row][col] == "OFF")
407  theDataView_[row][col] = ViewColumnInfo::TYPE_VALUE_OFF;
408  else
409  {
410  __SS__ << "Configuration Error:\t" << theDataView_[row][col] << " in column " <<
411  columnsInfo_[col].getName() << " is not a valid Type (On/Off) std::string. Possible values are 1, on, On, ON, 0, off, Off, OFF." << std::endl;
412  __COUT_ERR__ << "\n" << ss.str();
413  throw std::runtime_error(ss.str());
414  }
415  }
416  else if(columnsInfo_[col].getType() == ViewColumnInfo::TYPE_TRUE_FALSE)
417  for(unsigned int row = 0; row < getNumberOfRows(); ++row)
418  {
419  if (theDataView_[row][col] == "1" || theDataView_[row][col] == "true" || theDataView_[row][col] == "True" || theDataView_[row][col] == "TRUE")
420  theDataView_[row][col] = ViewColumnInfo::TYPE_VALUE_TRUE;
421  else if (theDataView_[row][col] == "0" || theDataView_[row][col] == "false" || theDataView_[row][col] == "False" || theDataView_[row][col] == "FALSE")
422  theDataView_[row][col] = ViewColumnInfo::TYPE_VALUE_FALSE;
423  else
424  {
425  __SS__ << "Configuration Error:\t" << theDataView_[row][col] << " in column " <<
426  columnsInfo_[col].getName() << " is not a valid Type (True/False) std::string. Possible values are 1, true, True, TRUE, 0, false, False, FALSE." << std::endl;
427  __COUT_ERR__ << "\n" << ss.str();
428  throw std::runtime_error(ss.str());
429  }
430  }
431  else if(columnsInfo_[col].getType() == ViewColumnInfo::TYPE_YES_NO)
432  for(unsigned int row = 0; row < getNumberOfRows(); ++row)
433  {
434  if (theDataView_[row][col] == "1" || theDataView_[row][col] == "yes" || theDataView_[row][col] == "Yes" || theDataView_[row][col] == "YES")
435  theDataView_[row][col] = ViewColumnInfo::TYPE_VALUE_YES;
436  else if (theDataView_[row][col] == "0" || theDataView_[row][col] == "no" || theDataView_[row][col] == "No" || theDataView_[row][col] == "NO")
437  theDataView_[row][col] = ViewColumnInfo::TYPE_VALUE_NO;
438  else
439  {
440  __SS__ << "Configuration Error:\t" << theDataView_[row][col] << " in column " <<
441  columnsInfo_[col].getName() << " is not a valid Type (Yes/No) std::string. Possible values are 1, yes, Yes, YES, 0, no, No, NO." << std::endl;
442  __COUT_ERR__ << "\n" << ss.str();
443  throw std::runtime_error(ss.str());
444  }
445  }
446  else if(columnsInfo_[col].isGroupID()) //GroupID type
447  {
448  colLinkGroupIDs_[columnsInfo_[col].getChildLinkIndex()] = col; //add to groupid map
449  //check uniqueness
450  groupIdIndexes.emplace(columnsInfo_[col].getChildLinkIndex());
451  ++groupIdIndexesCount;
452  }
453  else if(columnsInfo_[col].isChildLink()) //Child Link type
454  {
455  //sanitize no link to default
456  for(unsigned int row = 0; row < getNumberOfRows(); ++row)
457  if(theDataView_[row][col] == "NoLink" ||
458  theDataView_[row][col] == "No_Link" ||
459  theDataView_[row][col] == "NOLINK" ||
460  theDataView_[row][col] == "NO_LINK" ||
461  theDataView_[row][col] == "Nolink" ||
462  theDataView_[row][col] == "nolink" ||
463  theDataView_[row][col] == "noLink")
464  theDataView_[row][col] = ViewColumnInfo::DATATYPE_LINK_DEFAULT;
465 
466  //check uniqueness
467  childLinkIndexes.emplace(columnsInfo_[col].getChildLinkIndex());
468  ++childLinkIndexesCount;
469 
470  //force data type to ViewColumnInfo::DATATYPE_STRING
471  if(columnsInfo_[col].getDataType() != ViewColumnInfo::DATATYPE_STRING)
472  {
473  __SS__ << "Configuration Error:\t" << "Column " << col <<
474  " with name " << columnsInfo_[col].getName() <<
475  " is a Child Link column and has an illegal data type of '" <<
476  columnsInfo_[col].getDataType() <<
477  "'. The data type for Child Link columns must be " <<
478  ViewColumnInfo::DATATYPE_STRING << std::endl;
479  __COUT_ERR__ << "\n" << ss.str();
480  throw std::runtime_error(ss.str());
481  }
482 
483  }
484  else if(columnsInfo_[col].isChildLinkUID() || //Child Link ID type
485  columnsInfo_[col].isChildLinkGroupID())
486  {
487  //check uniqueness
488  childLinkIdLabels.emplace(columnsInfo_[col].getChildLinkIndex());
489  ++childLinkIdLabelsCount;
490 
491  //check that the Link ID is not empty, and force to default
492  for(unsigned int row = 0; row < getNumberOfRows(); ++row)
493  if(theDataView_[row][col] == "")
494  theDataView_[row][col] = rowDefaults[col];
495 
496  }
497  }
498 
499  //verify child link index uniqueness
500  if(groupIdIndexes.size() != groupIdIndexesCount)
501  {
502  __SS__ << ("GroupId Labels are not unique!") <<
503  "There are " << groupIdIndexesCount <<
504  " GroupId Labels and the unique count is " << groupIdIndexes.size() << std::endl;
505  __COUT_ERR__ << "\n" << ss.str();
506  throw std::runtime_error(ss.str());
507  }
508  if(childLinkIndexes.size() != childLinkIndexesCount)
509  {
510  __SS__ << ("Child Link Labels are not unique!") <<
511  "There are " << childLinkIndexesCount <<
512  " Child Link Labels and the unique count is " << childLinkIndexes.size() << std::endl;
513  __COUT_ERR__ << "\n" << ss.str();
514  throw std::runtime_error(ss.str());
515  }
516  if(childLinkIdLabels.size() != childLinkIdLabelsCount)
517  {
518  __SS__ << ("Child Link ID Labels are not unique!") <<
519  "There are " << childLinkIdLabelsCount <<
520  " Child Link ID Labels and the unique count is " << childLinkIdLabels.size() << std::endl;
521  __COUT_ERR__ << "\n" << ss.str();
522  throw std::runtime_error(ss.str());
523  }
524 
525  }
526  catch(...)
527  {
528  __COUT__ << "Error occured in ConfigurationView::init() for version=" << version_ << std::endl;
529  throw;
530  }
531 }
532 
533 //==============================================================================
534 //getValue
535 // string version
536 // Note: necessary because types of std::basic_string<char> cause compiler problems if no string specific function
537 void ConfigurationView::getValue(std::string& value, unsigned int row, unsigned int col, bool doConvertEnvironmentVariables) const
538 {
539  if(!(col < columnsInfo_.size() && row < getNumberOfRows()))
540  {
541  __SS__ << "Invalid row col requested" << std::endl;
542  __SS_THROW__;
543  }
544 
545  value = validateValueForColumn(theDataView_[row][col],col,doConvertEnvironmentVariables);
546 }
547 
548 
549 //==============================================================================
550 //validateValueForColumn
551 // string version
552 // Note: necessary because types of std::basic_string<char>
553 // cause compiler problems if no string specific function
554 std::string ConfigurationView::validateValueForColumn(const std::string& value,
555  unsigned int col, bool doConvertEnvironmentVariables) const
556 {
557  if(col >= columnsInfo_.size())
558  {
559  __SS__ << "Invalid col requested" << std::endl;
560  __SS_THROW__;
561  }
562 
563  std::string retValue;
564 
565  if(columnsInfo_[col].getDataType() == ViewColumnInfo::DATATYPE_STRING)
566  retValue = doConvertEnvironmentVariables?StringMacros::convertEnvironmentVariables(value):value;
567  else if(columnsInfo_[col].getDataType() == ViewColumnInfo::DATATYPE_TIME)
568  {
569  retValue.resize(30); //known fixed size: Thu Aug 23 14:55:02 2001 CST
570  time_t timestamp(
571  strtol((doConvertEnvironmentVariables?StringMacros::convertEnvironmentVariables(value):value).c_str(),
572  0,10));
573  struct tm tmstruct;
574  ::localtime_r(&timestamp, &tmstruct);
575  ::strftime(&retValue[0], 30, "%c %Z", &tmstruct);
576  retValue.resize(strlen(retValue.c_str()));
577  }
578  else
579  {
580  __SS__ << "\tUnrecognized column data type: " << columnsInfo_[col].getDataType()
581  << " in configuration " << tableName_
582  << " at column=" << columnsInfo_[col].getName()
583  << " for getValue with type '" << StringMacros::demangleTypeName(typeid(retValue).name())
584  << "'" << std::endl;
585  __SS_THROW__;
586  }
587 
588  return retValue;
589 }
590 
591 
592 //==============================================================================
593 //getValueAsString
594 // gets the value with the proper data type and converts to string
595 // as though getValue was called.
596 std::string ConfigurationView::getValueAsString(unsigned int row, unsigned int col,
597  bool doConvertEnvironmentVariables) const
598 {
599  if(!(col < columnsInfo_.size() && row < getNumberOfRows()))
600  {
601  __SS__ << ("Invalid row col requested") << std::endl;
602  __COUT_ERR__ << ss.str();
603  throw std::runtime_error(ss.str());
604  }
605 
606  //__COUT__ << columnsInfo_[col].getType() << " " << col << std::endl;
607 
608  if(columnsInfo_[col].getType() == ViewColumnInfo::TYPE_ON_OFF)
609  {
610  if (theDataView_[row][col] == "1" || theDataView_[row][col] == "on" || theDataView_[row][col] == "On" || theDataView_[row][col] == "ON")
611  return ViewColumnInfo::TYPE_VALUE_ON;
612  else
613  return ViewColumnInfo::TYPE_VALUE_OFF;
614  }
615  else if(columnsInfo_[col].getType() == ViewColumnInfo::TYPE_TRUE_FALSE)
616  {
617  if (theDataView_[row][col] == "1" || theDataView_[row][col] == "true" || theDataView_[row][col] == "True" || theDataView_[row][col] == "TRUE")
618  return ViewColumnInfo::TYPE_VALUE_TRUE;
619  else
620  return ViewColumnInfo::TYPE_VALUE_FALSE;
621  }
622  else if(columnsInfo_[col].getType() == ViewColumnInfo::TYPE_YES_NO)
623  {
624  if (theDataView_[row][col] == "1" || theDataView_[row][col] == "yes" || theDataView_[row][col] == "Yes" || theDataView_[row][col] == "YES")
625  return ViewColumnInfo::TYPE_VALUE_YES;
626  else
627  return ViewColumnInfo::TYPE_VALUE_NO;
628  }
629 
630  //__COUT__ << std::endl;
631  return doConvertEnvironmentVariables?StringMacros::convertEnvironmentVariables(theDataView_[row][col]):
632  theDataView_[row][col];
633 }
634 
635 
636 //==============================================================================
637 //getEscapedValueAsString
638 // gets the value with the proper data type and converts to string
639 // as though getValue was called.
640 // then escapes all special characters with slash.
641 // Note: this should be useful for values placed in double quotes, i.e. JSON.
642 std::string ConfigurationView::getEscapedValueAsString(unsigned int row, unsigned int col,
643  bool doConvertEnvironmentVariables) const
644 {
645  std::string val = getValueAsString(row,col,doConvertEnvironmentVariables);
646  std::string retVal = "";
647  retVal.reserve(val.size()); //reserve roughly right size
648  for(unsigned int i=0;i<val.size();++i)
649  {
650  if(val[i] == '\n')
651  retVal += "\\n";
652  else if(val[i] == '\t')
653  retVal += "\\t";
654  else if(val[i] == '\r')
655  retVal += "\\r";
656  else
657  {
658  //escaped characters need a
659  if(val[i] == '"' ||
660  val[i] == '\\')
661  retVal += '\\';
662  retVal += val[i];
663  }
664  }
665  return retVal;
666 }
667 
668 //==============================================================================
669 //setValue
670 // string version
671 void ConfigurationView::setValue(const std::string &value, unsigned int row, unsigned int col)
672 {
673  if(!(col < columnsInfo_.size() && row < getNumberOfRows()))
674  {
675  __SS__ << "Invalid row (" << row << ") col (" << col << ") requested!" << std::endl;
676  throw std::runtime_error(ss.str());
677  }
678 
679  if(columnsInfo_[col].getDataType() == ViewColumnInfo::DATATYPE_STRING)
680  theDataView_[row][col] = value;
681  else // dont allow ViewColumnInfo::DATATYPE_TIME to be set as string.. force use as time_t to standardize string result
682  {
683 
684  __SS__ << "\tUnrecognized column data type: " << columnsInfo_[col].getDataType()
685  << " in configuration " << tableName_
686  << " at column=" << columnsInfo_[col].getName()
687  << " for setValue with type '" << StringMacros::demangleTypeName(typeid(value).name())
688  << "'" << std::endl;
689  throw std::runtime_error(ss.str());
690  }
691 }
692 void ConfigurationView::setValue(const char *value, unsigned int row, unsigned int col)
693 { setValue(std::string(value),row,col); }
694 
695 //==============================================================================
696 //setValue
697 // string version
698 void ConfigurationView::setValueAsString(const std::string &value, unsigned int row, unsigned int col)
699 {
700  if(!(col < columnsInfo_.size() && row < getNumberOfRows()))
701  {
702  __SS__ << "Invalid row (" << row << ") col (" << col << ") requested!" << std::endl;
703  throw std::runtime_error(ss.str());
704  }
705 
706  theDataView_[row][col] = value;
707 }
708 
709 //==============================================================================
710 //getOrInitColUID
711 // if column not found throw error
712 const unsigned int ConfigurationView::getOrInitColUID(void)
713 {
714  if(colUID_ != INVALID) return colUID_;
715 
716  //if doesn't exist throw error! each view must have a UID column
717  colUID_ = findColByType(ViewColumnInfo::TYPE_UID);
718  if(colUID_ == INVALID)
719  {
720  __COUT__ << "Column Types: " << std::endl;
721  for(unsigned int col=0; col<columnsInfo_.size(); ++col)
722  std::cout << columnsInfo_[col].getType() << "() " << columnsInfo_[col].getName() << std::endl;
723  __SS__ << "\tMissing UID Column in table named '" << tableName_ << "'" << std::endl;
724  __COUT_ERR__ << "\n" << ss.str() << std::endl;
725  throw std::runtime_error(ss.str());
726  }
727  return colUID_;
728 }
729 //==============================================================================
730 //getColOfUID
731 // const version, so don't attempt to lookup
732 // if column not found throw error
733 const unsigned int ConfigurationView::getColUID(void) const
734 {
735  if(colUID_ != INVALID) return colUID_;
736 
737  __COUT__ << "Column Types: " << std::endl;
738  for(unsigned int col=0; col<columnsInfo_.size(); ++col)
739  std::cout << columnsInfo_[col].getType() << "() " << columnsInfo_[col].getName() << std::endl;
740 
741  __SS__ << ("Missing UID Column in config named " + tableName_ +
742  ". (Possibly ConfigurationView was just not initialized?" +
743  "This is the const call so can not alter class members)") << std::endl;
744  __COUT_ERR__ << "\n" << ss.str() << std::endl;
745  throw std::runtime_error(ss.str());
746 }
747 
748 //==============================================================================
749 //getOrInitColStatus
750 // if column not found throw error
751 const unsigned int ConfigurationView::getOrInitColStatus(void)
752 {
753  if(colStatus_ != INVALID) return colStatus_;
754 
755  //if doesn't exist throw error! each view must have a UID column
756  colStatus_ = findCol(ViewColumnInfo::COL_NAME_STATUS);
757  if(colStatus_ == INVALID)
758  {
759  __SS__ << "\tMissing " << ViewColumnInfo::COL_NAME_STATUS << " Column in table named '" << tableName_ << "'" << std::endl;
760  ss << "Column Types: " << std::endl;
761  for(unsigned int col=0; col<columnsInfo_.size(); ++col)
762  ss << columnsInfo_[col].getType() << "() " << columnsInfo_[col].getName() << std::endl;
763 
764  //__COUT_ERR__ << "\n" << ss.str() << std::endl;
765  throw std::runtime_error(ss.str());
766  }
767  return colStatus_;
768 }
769 
770 //==============================================================================
771 //getOrInitColPriority
772 // if column not found throw error
773 const unsigned int ConfigurationView::getOrInitColPriority(void)
774 {
775  if(colPriority_ != INVALID) return colPriority_;
776 
777  //if doesn't exist throw error! each view must have a UID column
778  colPriority_ = findCol(ViewColumnInfo::COL_NAME_PRIORITY);
779  if(colPriority_ == INVALID)
780  {
781  __SS__ << "\tMissing " << ViewColumnInfo::COL_NAME_PRIORITY <<
782  " Column in table named '" << tableName_ << "'" << std::endl;
783  ss << "Column Types: " << std::endl;
784  for(unsigned int col=0; col<columnsInfo_.size(); ++col)
785  ss << columnsInfo_[col].getType() << "() " << columnsInfo_[col].getName() << std::endl;
786 
787  //__COUT_ERR__ << "\n" << ss.str() << std::endl;
788  throw std::runtime_error(ss.str());
789  }
790  return colPriority_;
791 }
792 
793 //==============================================================================
794 //getColStatus
795 // const version, so don't attempt to lookup
796 // if column not found throw error
797 const unsigned int ConfigurationView::getColStatus(void) const
798 {
799  if(colStatus_ != INVALID) return colStatus_;
800 
801  __COUT__ << "Column Types: " << std::endl;
802  for(unsigned int col=0; col<columnsInfo_.size(); ++col)
803  std::cout << columnsInfo_[col].getType() << "() " << columnsInfo_[col].getName() << std::endl;
804 
805  __SS__ << "Missing " << ViewColumnInfo::COL_NAME_STATUS << " Column in config named " << tableName_ <<
806  ". (Possibly ConfigurationView was just not initialized?" <<
807  "This is the const call so can not alter class members)" << std::endl;
808  __COUT_ERR__ << "\n" << ss.str() << std::endl;
809  throw std::runtime_error(ss.str());
810 }
811 
812 //==============================================================================
813 //getColPriority
814 // const version, so don't attempt to lookup
815 // if column not found throw error
816 const unsigned int ConfigurationView::getColPriority(void) const
817 {
818  if(colPriority_ != INVALID) return colPriority_;
819 
820  __COUT__ << "Column Types: " << std::endl;
821  for(unsigned int col=0; col<columnsInfo_.size(); ++col)
822  std::cout << columnsInfo_[col].getType() << "() " << columnsInfo_[col].getName() << std::endl;
823 
824  __SS__ << "Missing " << ViewColumnInfo::COL_NAME_PRIORITY << " Column in config named " << tableName_ <<
825  ". (Possibly ConfigurationView was just not initialized?" <<
826  "This is the const call so can not alter class members)" << std::endl;
827  __COUT_ERR__ << "\n" << ss.str() << std::endl;
828  throw std::runtime_error(ss.str());
829 }
830 
831 //==============================================================================
832 //addRowToGroup
833 // Group entry can include | to place a record in multiple groups
834 void ConfigurationView::addRowToGroup(const unsigned int &row,
835  const unsigned int &col,
836  const std::string &groupID)//,
837  //const std::string &colDefault)
838 {
839  if(isEntryInGroupCol(row,col,groupID))
840  {
841  __SS__ << "GroupID (" << groupID <<
842  ") added to row (" << row
843  << " is already present!" << std::endl;
844  throw std::runtime_error(ss.str());
845  }
846 
847  //not in group, so
848  // if no groups
849  // set groupid
850  // if other groups
851  // prepend groupId |
852  if(getDataView()[row][col] == "" ||
853  getDataView()[row][col] == getDefaultRowValues()[col]) //colDefault)
854  setValue(
855  groupID,
856  row,col);
857  else
858  setValue(
859  groupID + " | " + getDataView()[row][col],
860  row,col);
861 
862  //__COUT__ << getDataView()[row][col] << std::endl;
863 }
864 
865 //==============================================================================
866 //removeRowFromGroup
867 // Group entry can include | to place a record in multiple groups
868 //
869 // returns true if row was deleted because it had no group left
870 bool ConfigurationView::removeRowFromGroup(const unsigned int &row,
871  const unsigned int &col,
872  const std::string &groupNeedle,
873  bool deleteRowIfNoGroupLeft)
874 {
875  __COUT__ << "groupNeedle " << groupNeedle << std::endl;
876  std::set<std::string> groupIDList;
877  if(!isEntryInGroupCol(row,col,groupNeedle,&groupIDList))
878  {
879  __SS__ << "GroupID (" << groupNeedle <<
880  ") removed from row (" << row
881  << ") was already removed!" << std::endl;
882  throw std::runtime_error(ss.str());
883  }
884 
885  //is in group, so
886  // create new string based on set of groupids
887  // but skip groupNeedle
888 
889  std::string newValue = "";
890  unsigned int cnt = 0;
891  for(const auto & groupID : groupIDList)
892  {
893  //__COUT__ << groupID << " " << groupNeedle << " " << newValue << std::endl;
894  if(groupID == groupNeedle) continue; //skip group to be removed
895 
896  if(cnt) newValue += " | ";
897  newValue += groupID;
898  }
899 
900  bool wasDeleted = false;
901  if(deleteRowIfNoGroupLeft && newValue == "")
902  {
903  __COUT__ << "Delete row since it no longer part of any group." << std::endl;
904  deleteRow(row);
905  wasDeleted = true;
906  }
907  else
908  setValue(newValue,row,col);
909 
910  //__COUT__ << getDataView()[row][col] << std::endl;
911 
912  return wasDeleted;
913 }
914 
915 //==============================================================================
916 //isEntryInGroup
917 // All group link checking should use this function
918 // so that handling is consistent
919 //
920 // Group entry can include | to place a record in multiple groups
921 bool ConfigurationView::isEntryInGroup(const unsigned int &r,
922  const std::string &childLinkIndex,
923  const std::string &groupNeedle) const
924 {
925  unsigned int c = getColLinkGroupID(childLinkIndex); //column in question
926 
927  return isEntryInGroupCol(r,c,groupNeedle);
928 }
929 
930 //==============================================================================
931 // isEntryInGroupCol
932 // private function to prevent users from treating any column like a GroupID column
933 //
934 // if *groupIDList != 0 return set of groupIDs found
935 // useful for removing groupIDs.
936 //
937 // Group entry can include | to place a record in multiple groups
938 //
939 // Note: should mirror what happens in ConfigurationView::getSetOfGroupIDs
940 bool ConfigurationView::isEntryInGroupCol(const unsigned int &r,
941  const unsigned int &c, const std::string &groupNeedle,
942  std::set<std::string> *groupIDList) const
943 {
944  unsigned int i=0;
945  unsigned int j=0;
946  bool found = false;
947 
948  //__COUT__ << "groupNeedle " << groupNeedle << std::endl;
949 
950  //go through the full groupString extracting groups and comparing to groupNeedle
951  for(;j<theDataView_[r][c].size();++j)
952  if((theDataView_[r][c][j] == ' ' || //ignore leading white space or |
953  theDataView_[r][c][j] == '|')
954  && i == j)
955  ++i;
956  else if((theDataView_[r][c][j] == ' ' || //trailing white space or | indicates group
957  theDataView_[r][c][j] == '|')
958  && i != j) // assume end of group name
959  {
960  if(groupIDList) groupIDList->emplace(theDataView_[r][c].substr(i,j-i));
961 
962  //__COUT__ << "Group found to compare: " <<
963  // theDataView_[r][c].substr(i,j-i) << std::endl;
964  if(groupNeedle == theDataView_[r][c].substr(i,j-i))
965  {
966  if(!groupIDList) //dont return if caller is trying to get group list
967  return true;
968  found = true;
969  }
970  //if no match, setup i and j for next find
971  i = j+1;
972  }
973 
974  if(i != j) //last group check (for case when no ' ' or '|')
975  {
976  if(groupIDList) groupIDList->emplace(theDataView_[r][c].substr(i,j-i));
977 
978  //__COUT__ << "Group found to compare: " <<
979  // theDataView_[r][c].substr(i,j-i) << std::endl;
980  if(groupNeedle == theDataView_[r][c].substr(i,j-i))
981  return true;
982  }
983 
984  return found;
985 }
986 
987 
988 //==============================================================================
989 //getSetOfGroupIDs
990 // if row == -1, then considers all rows
991 // else just that row
992 // returns unique set of groupIds in GroupID column
993 // associate with childLinkIndex
994 //
995 // Note: should mirror what happens in ConfigurationView::isEntryInGroupCol
996 std::set<std::string> ConfigurationView::getSetOfGroupIDs(const std::string &childLinkIndex,
997  unsigned int r) const
998 {
999  unsigned int c = getColLinkGroupID(childLinkIndex);
1000 
1001  //__COUT__ << "GroupID col=" << (int)c << std::endl;
1002 
1003  std::set<std::string> retSet;
1004 
1005  unsigned int i=0;
1006  unsigned int j=0;
1007 
1008  if(r != (unsigned int)-1)
1009  {
1010  if(r >= getNumberOfRows())
1011  {
1012  __SS__ << "Invalid row requested!" << std::endl;
1013  throw std::runtime_error(ss.str());
1014  }
1015 
1016  StringMacros::getSetFromString(theDataView_[r][c],retSet);
1017 // //go through the full groupString extracting groups
1018 // //add each found groupId to set
1019 // for(;j<theDataView_[r][c].size();++j)
1020 // if((theDataView_[r][c][j] == ' ' || //ignore leading white space or |
1021 // theDataView_[r][c][j] == '|')
1022 // && i == j)
1023 // ++i;
1024 // else if((theDataView_[r][c][j] == ' ' || //trailing white space or | indicates group
1025 // theDataView_[r][c][j] == '|')
1026 // && i != j) // assume end of group name
1027 // {
1028 // //__COUT__ << "Group found: " <<
1029 // // theDataView_[r][c].substr(i,j-i) << std::endl;
1030 //
1031 //
1032 // retSet.emplace(theDataView_[r][c].substr(i,j-i));
1033 //
1034 // //setup i and j for next find
1035 // i = j+1;
1036 // }
1037 //
1038 // if(i != j) //last group check (for case when no ' ' or '|')
1039 // retSet.emplace(theDataView_[r][c].substr(i,j-i));
1040  }
1041  else
1042  {
1043  //do all rows
1044  for(r=0;r<getNumberOfRows();++r)
1045  {
1046  StringMacros::getSetFromString(theDataView_[r][c],retSet);
1047 
1048 // i=0;
1049 // j=0;
1050 //
1051 // //__COUT__ << (int)r << ": " << theDataView_[r][c] << std::endl;
1052 //
1053 // //go through the full groupString extracting groups
1054 // //add each found groupId to set
1055 // for(;j<theDataView_[r][c].size();++j)
1056 // {
1057 // //__COUT__ << "i:" << i << " j:" << j << std::endl;
1058 //
1059 // if((theDataView_[r][c][j] == ' ' || //ignore leading white space or |
1060 // theDataView_[r][c][j] == '|')
1061 // && i == j)
1062 // ++i;
1063 // else if((theDataView_[r][c][j] == ' ' || //trailing white space or | indicates group
1064 // theDataView_[r][c][j] == '|')
1065 // && i != j) // assume end of group name
1066 // {
1067 // //__COUT__ << "Group found: " <<
1068 // // theDataView_[r][c].substr(i,j-i) << std::endl;
1069 //
1070 // retSet.emplace(theDataView_[r][c].substr(i,j-i));
1071 //
1072 // //setup i and j for next find
1073 // i = j+1;
1074 // }
1075 // }
1076 //
1077 // if(i != j) //last group (for case when no ' ' or '|')
1078 // {
1079 // //__COUT__ << "Group found: " <<
1080 // // theDataView_[r][c].substr(i,j-i) << std::endl;
1081 // retSet.emplace(theDataView_[r][c].substr(i,j-i));
1082 // }
1083  }
1084  }
1085 
1086  return retSet;
1087 }
1088 
1089 //==============================================================================
1090 //getColOfLinkGroupID
1091 // const version, if column not found throw error
1092 const unsigned int ConfigurationView::getColLinkGroupID(const std::string &childLinkIndex) const
1093 {
1094  std::map<std::string, unsigned int>::const_iterator it = colLinkGroupIDs_.find(childLinkIndex);
1095  if(it != //if already known, return it
1096  colLinkGroupIDs_.end())
1097  return it->second;
1098 
1099  __COUT__ << "Existing Column Types: " << std::endl;
1100  for(unsigned int col=0; col<columnsInfo_.size(); ++col)
1101  std::cout << columnsInfo_[col].getType() << "() " << columnsInfo_[col].getName() << std::endl;
1102 
1103  __SS__ << ("Incompatible table for this group link. Table '" +
1104  tableName_ + "' is missing a GroupID column with data type '" +
1105  (ViewColumnInfo::TYPE_START_GROUP_ID + "-" + childLinkIndex) +
1106  "'." ) << std::endl;
1107  __COUT_ERR__ << "\n" << ss.str();
1108  throw std::runtime_error(ss.str());
1109 }
1110 
1111 //==============================================================================
1112 unsigned int ConfigurationView::findRow(unsigned int col, const std::string& value, unsigned int offsetRow) const
1113 {
1114  for(unsigned int row=offsetRow; row<theDataView_.size(); ++row)
1115  {
1116  if(theDataView_[row][col] == value)
1117  return row;
1118  }
1119 
1120  __SS__ << "\tIn view: " << tableName_
1121  << ", Can't find value=" << value
1122  << " in column named " << columnsInfo_[col].getName()
1123  << " with type=" << columnsInfo_[col].getType()
1124  << std::endl;
1125  //Note: findRow gets purposely called by configuration GUI a lot looking for exceptions
1126  // so may not want to print out
1127  //__COUT__ << "\n" << ss.str();
1128  throw std::runtime_error(ss.str());
1129 }
1130 
1131 //==============================================================================
1132 unsigned int ConfigurationView::findRowInGroup(unsigned int col, const std::string& value,
1133  const std::string &groupId, const std::string &childLinkIndex, unsigned int offsetRow) const
1134 {
1135  unsigned int groupIdCol = getColLinkGroupID(childLinkIndex);
1136  for(unsigned int row=offsetRow; row<theDataView_.size(); ++row)
1137  {
1138  if(theDataView_[row][col] == value &&
1139  isEntryInGroupCol(row,groupIdCol,groupId))
1140  return row;
1141  }
1142 
1143  __SS__ << "\tIn view: " << tableName_ << ", Can't find in group the value=" <<
1144  value << " in column named '" <<
1145  columnsInfo_[col].getName() << "' with type=" <<
1146  columnsInfo_[col].getType() << " and GroupID: '" <<
1147  groupId << "' in column '" << groupIdCol <<
1148  "' with GroupID child link index '" << childLinkIndex << "'" << std::endl;
1149  //Note: findRowInGroup gets purposely called by configuration GUI a lot looking for exceptions
1150  // so may not want to print out
1151  //__COUT__ << "\n" << ss.str();
1152  throw std::runtime_error(ss.str());
1153 }
1154 
1155 //==============================================================================
1156 //findCol
1157 // throws exception if column not found by name
1158 unsigned int ConfigurationView::findCol(const std::string& name) const
1159 {
1160  for(unsigned int col=0; col<columnsInfo_.size(); ++col)
1161  if(columnsInfo_[col].getName() == name)
1162  return col;
1163 
1164  __SS__ << "\tIn view: " << tableName_ <<
1165  ", Can't find column named '" << name << "'" << std::endl;
1166  ss << "Existing columns:\n";
1167  for(unsigned int col=0; col<columnsInfo_.size(); ++col)
1168  ss << "\t" << columnsInfo_[col].getName() << "\n";
1169  throw std::runtime_error(ss.str());
1170 }
1171 
1172 //==============================================================================
1173 //findColByType
1174 // return invalid if type not found
1175 unsigned int ConfigurationView::findColByType(const std::string& type, int startingCol) const
1176 {
1177  for(unsigned int col=startingCol; col<columnsInfo_.size(); ++col)
1178  if(columnsInfo_[col].getType() == type)
1179  return col;
1180 
1181  return INVALID;
1182 }
1183 
1184 //Getters
1185 //==============================================================================
1186 const std::string& ConfigurationView::getUniqueStorageIdentifier(void) const
1187 {
1188  return uniqueStorageIdentifier_;
1189 }
1190 
1191 //==============================================================================
1192 const std::string& ConfigurationView::getTableName(void) const
1193 {
1194  return tableName_;
1195 }
1196 
1197 //==============================================================================
1198 const ConfigurationVersion& ConfigurationView::getVersion(void) const
1199 {
1200  return version_;
1201 }
1202 
1203 //==============================================================================
1204 const std::string& ConfigurationView::getComment(void) const
1205 {
1206  return comment_;
1207 }
1208 
1209 //==============================================================================
1210 const std::string& ConfigurationView::getAuthor(void) const
1211 {
1212  return author_;
1213 }
1214 
1215 //==============================================================================
1216 const time_t& ConfigurationView::getCreationTime(void) const
1217 {
1218  return creationTime_;
1219 }
1220 
1221 //==============================================================================
1222 const time_t& ConfigurationView::getLastAccessTime(void) const
1223 {
1224  return lastAccessTime_;
1225 }
1226 
1227 //==============================================================================
1228 const bool& ConfigurationView::getLooseColumnMatching(void) const
1229 {
1230  return fillWithLooseColumnMatching_;
1231 }
1232 
1233 //==============================================================================
1234 //getDataColumnSize
1235 const unsigned int ConfigurationView::getDataColumnSize(void) const
1236 {
1237  //if no data, give benefit of the doubt that phantom data has mockup column size
1238  if(!getNumberOfRows()) return getNumberOfColumns();
1239  return theDataView_[0].size(); //number of columns in first row of data
1240 }
1241 
1242 //==============================================================================
1243 //getSourceColumnMismatch
1244 // The source information is only valid after modifying the table with ::fillFromJSON
1245 const unsigned int& ConfigurationView::getSourceColumnMismatch(void) const
1246 {
1247  return sourceColumnMismatchCount_;
1248 }
1249 
1250 //==============================================================================
1251 //getSourceColumnMissing
1252 // The source information is only valid after modifying the table with ::fillFromJSON
1253 const unsigned int& ConfigurationView::getSourceColumnMissing(void) const
1254 {
1255  return sourceColumnMissingCount_;
1256 }
1257 
1258 //==============================================================================
1259 //getSourceColumnNames
1260 // The source information is only valid after modifying the table with ::fillFromJSON
1261 const std::set<std::string>& ConfigurationView::getSourceColumnNames(void) const
1262 {
1263  return sourceColumnNames_;
1264 }
1265 
1266 //==============================================================================
1267 std::set<std::string> ConfigurationView::getColumnNames(void) const
1268 {
1269  std::set<std::string> retSet;
1270  for(auto &colInfo: columnsInfo_)
1271  retSet.emplace(colInfo.getName());
1272  return retSet;
1273 }
1274 
1275 //==============================================================================
1276 std::set<std::string> ConfigurationView::getColumnStorageNames(void) const
1277 {
1278  std::set<std::string> retSet;
1279  for(auto &colInfo: columnsInfo_)
1280  retSet.emplace(colInfo.getStorageName());
1281  return retSet;
1282 }
1283 
1284 //==============================================================================
1285 std::vector<std::string> ConfigurationView::getDefaultRowValues(void) const
1286 {
1287  std::vector<std::string> retVec;
1288 
1289  //fill each col of new row with default values
1290  for(unsigned int col=0;col<getNumberOfColumns();++col)
1291  {
1292  //if this is a fixed choice Link, and NO_LINK is not in list,
1293  // take first in list to avoid creating illegal rows.
1294  //NOTE: this is not a problem for standard fixed choice fields
1295  // because the default value is always required.
1296 
1297  if(columnsInfo_[col].isChildLink())
1298  {
1299  const std::vector<std::string>& theDataChoices =
1300  columnsInfo_[col].getDataChoices();
1301 
1302  //check if arbitrary values allowed
1303  if(!theDataChoices.size() || //if so, use default
1304  theDataChoices[0] == "arbitraryBool=1")
1305  retVec.push_back(columnsInfo_[col].getDefaultValue());
1306  else
1307  {
1308  bool skipOne = (theDataChoices.size() &&
1309  theDataChoices[0] == "arbitraryBool=0");
1310  bool hasSkipped;
1311 
1312  //look for default value in list
1313 
1314  bool foundDefault = false;
1315  hasSkipped = false;
1316  for(const auto &choice:theDataChoices)
1317  if(skipOne && !hasSkipped) {hasSkipped = true; continue;}
1318  else if(choice == columnsInfo_[col].getDefaultValue())
1319  {
1320  foundDefault = true;
1321  break;
1322  }
1323 
1324  //use first choice if possible
1325  if(!foundDefault && theDataChoices.size() > (skipOne?1:0))
1326  retVec.push_back(theDataChoices[(skipOne?1:0)]);
1327  else //else stick with default
1328  retVec.push_back(columnsInfo_[col].getDefaultValue());
1329  }
1330  }
1331  else
1332  retVec.push_back(columnsInfo_[col].getDefaultValue());
1333  }
1334 
1335  return retVec;
1336 }
1337 
1338 //==============================================================================
1339 unsigned int ConfigurationView::getNumberOfRows(void) const
1340 {
1341  return theDataView_.size();
1342 }
1343 
1344 //==============================================================================
1345 unsigned int ConfigurationView::getNumberOfColumns(void) const
1346 {
1347  return columnsInfo_.size();
1348 }
1349 
1350 //==============================================================================
1351 const ConfigurationView::DataView& ConfigurationView::getDataView(void) const
1352 {
1353  return theDataView_;
1354 }
1355 
1356 //==============================================================================
1357 //ConfigurationView::DataView* ConfigurationView::getDataViewP(void)
1358 //{
1359 // return &theDataView_;
1360 //}
1361 
1362 //==============================================================================
1363 const std::vector<ViewColumnInfo>& ConfigurationView::getColumnsInfo (void) const
1364 {
1365  return columnsInfo_;
1366 }
1367 
1368 //==============================================================================
1369 std::vector<ViewColumnInfo>* ConfigurationView::getColumnsInfoP(void)
1370 {
1371  return &columnsInfo_;
1372 }
1373 //==============================================================================
1374 const ViewColumnInfo& ConfigurationView::getColumnInfo(unsigned int column) const
1375 {
1376  if(column >= columnsInfo_.size())
1377  {
1378  std::stringstream errMsg;
1379  errMsg << __COUT_HDR_FL__ << "\nCan't find column " << column <<
1380  "\n\n\n\nThe column info is likely missing due to incomplete Configuration View filling.\n\n"
1381  << std::endl;
1382  throw std::runtime_error(errMsg.str().c_str());
1383  }
1384  return columnsInfo_[column];
1385 }
1386 
1387 //Setters
1388 //==============================================================================
1389 void ConfigurationView::setUniqueStorageIdentifier(const std::string &storageUID)
1390 {
1391  uniqueStorageIdentifier_ = storageUID;
1392 }
1393 
1394 //==============================================================================
1395 void ConfigurationView::setTableName(const std::string &name)
1396 {
1397  tableName_ = name;
1398 }
1399 
1400 //==============================================================================
1401 void ConfigurationView::setComment(const std::string &comment)
1402 {
1403  comment_ = comment;
1404 }
1405 
1406 //==============================================================================
1407 void ConfigurationView::setURIEncodedComment(const std::string &uriComment)
1408 {
1409  comment_ = StringMacros::decodeURIComponent(uriComment);
1410 }
1411 
1412 //==============================================================================
1413 void ConfigurationView::setAuthor(const std::string &author)
1414 {
1415  author_ = author;
1416 }
1417 
1418 //==============================================================================
1419 void ConfigurationView::setCreationTime(time_t t)
1420 {
1421  creationTime_ = t;
1422 }
1423 
1424 //==============================================================================
1425 void ConfigurationView::setLastAccessTime(time_t t)
1426 {
1427  lastAccessTime_ = t;
1428 }
1429 
1430 //==============================================================================
1431 void ConfigurationView::setLooseColumnMatching(bool setValue)
1432 {
1433  fillWithLooseColumnMatching_ = setValue;
1434 }
1435 
1436 //==============================================================================
1437 void ConfigurationView::reset (void)
1438 {
1439  version_ = -1;
1440  comment_ = "";
1441  author_ + "";
1442  columnsInfo_.clear();
1443  theDataView_.clear();
1444 }
1445 
1446 //==============================================================================
1447 void ConfigurationView::print (std::ostream &out) const
1448 {
1449  out << "==============================================================================" << std::endl;
1450  out << "Print: " << tableName_ << " Version: " << version_ << " Comment: " << comment_ <<
1451  " Author: " << author_ << " Creation Time: " << ctime(&creationTime_) << std::endl;
1452  out << "\t\tNumber of Cols " << getNumberOfColumns() << std::endl;
1453  out << "\t\tNumber of Rows " << getNumberOfRows() << std::endl;
1454 
1455  out << "Columns:\t";
1456  for(int i=0;i<(int)columnsInfo_.size();++i)
1457  out << i << ":" << columnsInfo_[i].getName() << ":" << columnsInfo_[i].getStorageName() << ":" << columnsInfo_[i].getDataType() << "\t ";
1458  out << std::endl;
1459 
1460  out << "Rows:" << std::endl;
1461  int num;
1462  std::string val;
1463  for(int r=0;r<(int)getNumberOfRows();++r)
1464  {
1465  out << (int)r << ":\t";
1466  for(int c=0;c<(int)getNumberOfColumns();++c)
1467  {
1468  out << (int)c << ":";
1469 
1470  //if fixed choice type, print index in choice
1471  if(columnsInfo_[c].getType() == ViewColumnInfo::TYPE_FIXED_CHOICE_DATA)
1472  {
1473  int choiceIndex = -1;
1474  std::vector<std::string> choices = columnsInfo_[c].getDataChoices();
1475  val = StringMacros::convertEnvironmentVariables(theDataView_[r][c]);
1476 
1477  if(val == columnsInfo_[c].getDefaultValue())
1478  choiceIndex = 0;
1479  else
1480  {
1481  for(int i=0;i<(int)choices.size();++i)
1482  if(val == choices[i])
1483  choiceIndex = i+1;
1484  }
1485 
1486  out << "ChoiceIndex=" << choiceIndex << ":";
1487  }
1488 
1489 
1490  out << theDataView_[r][c];
1491  //stopped using below, because it is called sometimes during debugging when
1492  // numbers are set to environment variables:
1493  // if(columnsInfo_[c].getDataType() == "NUMBER")
1494  // {
1495  // getValue(num,r,c,false);
1496  // out << num;
1497  // }
1498  // else
1499  // {
1500  // getValue(val,r,c,false);
1501  // out << val;
1502  // }
1503  out << "\t\t";
1504  }
1505  out << std::endl;
1506  }
1507 }
1508 
1509 
1510 //==============================================================================
1511 void ConfigurationView::printJSON (std::ostream &out) const
1512 {
1513  out << "{\n";
1514  out << "\"NAME\" : \"" << tableName_ << "\",\n";
1515 
1516  //out << "\"VERSION\" : \"" << version_ << "\"\n";
1517 
1518  out << "\"COMMENT\" : ";
1519 
1520  //output escaped comment
1521  std::string val;
1522  val = comment_;
1523  out << "\"";
1524  for(unsigned int i=0;i<val.size();++i)
1525  {
1526 
1527  if(val[i] == '\n')
1528  out << "\\n";
1529  else if(val[i] == '\t')
1530  out << "\\t";
1531  else if(val[i] == '\r')
1532  out << "\\r";
1533  else
1534  {
1535  //escaped characters need a
1536  if(val[i] == '"' ||
1537  val[i] == '\\')
1538  out << '\\';
1539  out << val[i];
1540  }
1541  }
1542  out << "\",\n";
1543 
1544  out << "\"AUTHOR\" : \"" << author_ << "\",\n";
1545  out << "\"CREATION_TIME\" : " << creationTime_ << ",\n";
1546 
1547  //USELESS... out << "\"NUM_OF_COLS\" : " << getNumberOfColumns() << ",\n";
1548  //USELESS... out << "\"NUM_OF_ROWS\" : " << getNumberOfRows() << ",\n";
1549 
1550 
1551 
1552  out << "\"COL_TYPES\" : {\n";
1553  for(int c=0;c<(int)getNumberOfColumns();++c)
1554  {
1555  out << "\t\t\"" << columnsInfo_[c].getStorageName() << "\" : ";
1556  out << "\"" << columnsInfo_[c].getDataType() << "\"";
1557  if(c+1 < (int)getNumberOfColumns())
1558  out << ",";
1559  out << "\n";
1560  }
1561  out << "},\n"; //close COL_TYPES
1562 
1563  out << "\"DATA_SET\" : [\n";
1564  int num;
1565  for(int r=0;r<(int)getNumberOfRows();++r)
1566  {
1567  out << "\t{\n";
1568  for(int c=0;c<(int)getNumberOfColumns();++c)
1569  {
1570  out << "\t\t\"" << columnsInfo_[c].getStorageName() << "\" : ";
1571 
1572  out << "\"" << getEscapedValueAsString(r,c,false) << "\""; //do not convert env variables
1573 
1574  if(c+1 < (int)getNumberOfColumns())
1575  out << ",";
1576  out << "\n";
1577  }
1578  out << "\t}";
1579  if(r+1 < (int)getNumberOfRows())
1580  out << ",";
1581  out << "\n";
1582  }
1583  out << "]\n"; //close DATA_SET
1584 
1585  out << "}";
1586 }
1587 
1588 //==============================================================================
1589 //restoreJSONStringEntities
1590 // returns string with literals \n \t \" \r \\ replaced with char
1591 std::string restoreJSONStringEntities(const std::string &str)
1592 {
1593  unsigned int sz = str.size();
1594  if(!sz) return ""; //empty string, returns empty string
1595 
1596  std::stringstream retStr;
1597  unsigned int i=0;
1598  for(;i<sz-1;++i)
1599  {
1600  if(str[i] == '\\') //if 2 char escape sequence, replace with char
1601  switch(str[i+1])
1602  {
1603  case 'n':
1604  retStr << '\n'; ++i; break;
1605  case '"':
1606  retStr << '"'; ++i; break;
1607  case 't':
1608  retStr << '\t'; ++i; break;
1609  case 'r':
1610  retStr << '\r'; ++i; break;
1611  case '\\':
1612  retStr << '\\'; ++i; break;
1613  default:
1614  retStr << str[i];
1615  }
1616  else
1617  retStr << str[i];
1618  }
1619  if(i == sz-1)
1620  retStr << str[sz-1]; //output last character (which can't escape anything)
1621 
1622  return retStr.str();
1623 }
1624 
1625 //==============================================================================
1626 //fillFromJSON
1627 // Clears and fills the view from the JSON string.
1628 // Returns -1 on failure
1629 //
1630 // first level keys:
1631 // NAME
1632 // DATA_SET
1633 
1634 int ConfigurationView::fillFromJSON(const std::string &json)
1635 {
1636  std::vector<std::string> keys;
1637  keys.push_back ("NAME");
1638  keys.push_back ("COMMENT");
1639  keys.push_back ("AUTHOR");
1640  keys.push_back ("CREATION_TIME");
1641  //keys.push_back ("COL_TYPES");
1642  keys.push_back ("DATA_SET");
1643  enum {
1644  CV_JSON_FILL_NAME,
1645  CV_JSON_FILL_COMMENT,
1646  CV_JSON_FILL_AUTHOR,
1647  CV_JSON_FILL_CREATION_TIME,
1648  // CV_JSON_FILL_COL_TYPES,
1649  CV_JSON_FILL_DATA_SET
1650  };
1651 
1652  //__COUT__ << json << std::endl;
1653 
1654  sourceColumnMismatchCount_ = 0;
1655  sourceColumnMissingCount_ = 0;
1656  sourceColumnNames_.clear(); //reset
1657  unsigned int colFoundCount = 0;
1658  unsigned int i = 0;
1659  unsigned int row = -1;
1660  unsigned int colSpeedup = 0;
1661  unsigned int startString, startNumber, endNumber = -1;
1662  unsigned int bracketCount = 0;
1663  unsigned int sqBracketCount = 0;
1664  bool inQuotes = 0;
1665  bool newString = 0;
1666  bool newValue = 0;
1667  bool isDataArray = 0;
1668  bool keyIsMatch, keyIsComment;
1669  unsigned int keyIsMatchIndex, keyIsMatchStorageIndex, keyIsMatchCommentIndex;
1670  const std::string COMMENT_ALT_KEY = "COMMENT";
1671 
1672  std::string extractedString = "", currKey = "", currVal = "";
1673  unsigned int currDepth;
1674 
1675  std::vector<std::string> jsonPath;
1676  std::vector<char> jsonPathType; // indicator of type in jsonPath: { [ K
1677  char lastPopType = '_'; // either: _ { [ K
1678  // _ indicates reset pop (this happens when a new {obj} starts)
1679  unsigned int matchedKey = -1;
1680  unsigned int lastCol = -1;
1681 
1682 
1683  //find all depth 1 matching keys
1684  for(;i<json.size();++i)
1685  {
1686  switch(json[i])
1687  {
1688  case '"':
1689  if(i-1 < json.size() && //ignore if escaped
1690  json[i-1] == '\\') break;
1691 
1692  inQuotes = !inQuotes; //toggle in quotes if not escaped
1693  if(inQuotes)
1694  startString = i;
1695  else
1696  {
1697  extractedString = restoreJSONStringEntities(
1698  json.substr(startString+1,i-startString-1));
1699  newString = 1; //have new string!
1700  }
1701  break;
1702  case ':':
1703  if(inQuotes) break; //skip if in quote
1704 
1705  //must be a json object level to have a key
1706  if(jsonPathType[jsonPathType.size()-1] != '{'
1707  || !newString) //and must have a string for key
1708  {
1709  __COUT__ << "Invalid ':' position" << std::endl;
1710  return -1;
1711  }
1712 
1713  //valid, so take key
1714  jsonPathType.push_back('K');
1715  jsonPath.push_back(extractedString);
1716  startNumber = i;
1717  newString = 0; //clear flag
1718  endNumber = -1; //reset end number index
1719  break;
1720 
1721  // if(isKey ||
1722  // isDataArray)
1723  // {
1724  // std::cout << "Invalid ':' position" << std::endl;
1725  // return -1;
1726  // }
1727  // isKey = 1; //new value is a key
1728  // newValue = 1;
1729  // startNumber = i;
1730  // break;
1731  case ',':
1732  if(inQuotes) break; //skip if in quote
1733  if(lastPopType == '{') //don't need value again of nested object
1734  {
1735  //check if the nested object was the value to a key, if so, pop key
1736  if(jsonPathType[jsonPathType.size()-1] == 'K')
1737  {
1738  lastPopType = 'K';
1739  jsonPath.pop_back();
1740  jsonPathType.pop_back();
1741  }
1742  break; //skip , handling if {obj} just ended
1743  }
1744 
1745  if(newString)
1746  currVal = extractedString;
1747  else //number value
1748  {
1749  if(endNumber == (unsigned int)-1 || //take i as end number if needed
1750  endNumber <= startNumber)
1751  endNumber = i;
1752  //extract number value
1753  if(endNumber <= startNumber) //empty data, could be {}
1754  currVal = "";
1755  else
1756  currVal = json.substr(startNumber+1,endNumber-startNumber-1);
1757  }
1758 
1759  currDepth = bracketCount;
1760 
1761  if(jsonPathType[jsonPathType.size()-1] == 'K') //this is the value to key
1762  {
1763  currKey = jsonPath[jsonPathType.size()-1];
1764  newValue = 1; //new value to consider!
1765 
1766  //pop key
1767  lastPopType = 'K';
1768  jsonPath.pop_back();
1769  jsonPathType.pop_back();
1770  }
1771  else if(jsonPathType[jsonPathType.size()-1] == '[') //this is a value in array
1772  {
1773  //key is last key
1774  for(unsigned int k=jsonPathType.size()-2; k<jsonPathType.size(); --k)
1775  if(jsonPathType[k] == 'K')
1776  {
1777  currKey = jsonPath[k];
1778  break;
1779  }
1780  else if(k == 0)
1781  {
1782  __COUT__ << "Invalid array position" << std::endl;
1783  return -1;
1784  }
1785 
1786  newValue = 1; //new value to consider!
1787  isDataArray = 1;
1788  }
1789  else // { is an error
1790  {
1791  __COUT__ << "Invalid ',' position" << std::endl;
1792  return -1;
1793  }
1794 
1795  startNumber = i;
1796  break;
1797 
1798  case '{':
1799  if(inQuotes) break; //skip if in quote
1800  lastPopType = '_'; //reset because of new object
1801  jsonPathType.push_back('{');
1802  jsonPath.push_back("{");
1803  ++bracketCount;
1804  break;
1805 
1806  // ++bracketCount;
1807  // isDataArray = 0;
1808  // isKey = 0;
1809  // endingObject = 0;
1810  // break;
1811  case '}':
1812  if(inQuotes) break; //skip if in quote
1813 
1814  if(lastPopType != '{' && //don't need value again of nested object
1815  jsonPathType[jsonPathType.size()-1] == 'K') //this is the value to key
1816  {
1817  currDepth = bracketCount;
1818  currKey = jsonPath[jsonPathType.size()-1];
1819  if(newString)
1820  currVal = extractedString;
1821  else //number value
1822  {
1823  if(endNumber == (unsigned int)-1 || //take i as end number if needed
1824  endNumber <= startNumber)
1825  endNumber = i;
1826  //extract val
1827  if(endNumber <= startNumber) //empty data, could be {}
1828  currVal = "";
1829  else
1830  currVal = json.substr(startNumber+1,endNumber-startNumber-1);
1831  }
1832  newValue = 1; //new value to consider!
1833  //pop key
1834  jsonPath.pop_back();
1835  jsonPathType.pop_back();
1836  }
1837  //pop {
1838  if(jsonPathType[jsonPathType.size()-1] != '{')
1839  {
1840  __COUT__ << "Invalid '}' position" << std::endl;
1841  return -1;
1842  }
1843  lastPopType = '{';
1844  jsonPath.pop_back();
1845  jsonPathType.pop_back();
1846  --bracketCount;
1847  break;
1848  case '[':
1849  if(inQuotes) break; //skip if in quote
1850  jsonPathType.push_back('[');
1851  jsonPath.push_back("[");
1852  ++sqBracketCount;
1853  startNumber = i;
1854  break;
1855  case ']':
1856  if(inQuotes) break; //skip if in quote
1857 
1858  //must be an array at this level (in order to close it)
1859  if(jsonPathType[jsonPathType.size()-1] != '[')
1860  {
1861  __COUT__ << "Invalid ']' position" << std::endl;
1862  return -1;
1863  }
1864 
1865  currDepth = bracketCount;
1866 
1867  //This is an array value
1868  if(newString)
1869  currVal = extractedString;
1870  else //number value
1871  {
1872  if(endNumber == (unsigned int)-1 || //take i as end number if needed
1873  endNumber <= startNumber)
1874  endNumber = i;
1875  //extract val
1876  if(endNumber <= startNumber) //empty data, could be {}
1877  currVal = "";
1878  else
1879  currVal = json.substr(startNumber+1,endNumber-startNumber-1);
1880  }
1881  isDataArray = 1;
1882 
1883  //key is last key
1884  for(unsigned int k=jsonPathType.size()-2; k<jsonPathType.size(); --k)
1885  if(jsonPathType[k] == 'K')
1886  {
1887  currKey = jsonPath[k];
1888  break;
1889  }
1890  else if(k == 0)
1891  {
1892  __COUT__ << "Invalid array position" << std::endl;
1893  return -1;
1894  }
1895 
1896  //pop [
1897  if(jsonPathType[jsonPathType.size()-1] != '[')
1898  {
1899  __COUT__ << "Invalid ']' position" << std::endl;
1900  return -1;
1901  }
1902  lastPopType = '[';
1903  jsonPath.pop_back();
1904  jsonPathType.pop_back();
1905  --sqBracketCount;
1906  break;
1907  case ' ': //white space handling for numbers
1908  case '\t':
1909  case '\n':
1910  case '\r':
1911  if(inQuotes) break; //skip if in quote
1912  if(startNumber != (unsigned int)-1 &&
1913  endNumber == (unsigned int)-1)
1914  endNumber = i;
1915  startNumber = i;
1916  break;
1917  default:;
1918  }
1919 
1920  if(0) //for debugging
1921  {
1922  std::cout << i << ":\t" << json[i] << " - ";
1923 
1924  std::cout << "ExtKey=";
1925  for(unsigned int k=0;k<jsonPath.size();++k)
1926  std::cout << jsonPath[k] << "/";
1927  std::cout << " - ";
1928  std::cout << lastPopType << " ";
1929  std::cout << bracketCount << " ";
1930  std::cout << sqBracketCount << " ";
1931  std::cout << inQuotes << " ";
1932  std::cout << newValue << "-";
1933  std::cout << currKey << "-{" << currDepth << "}:" ;
1934  std::cout << currVal << " ";
1935  std::cout << startNumber << "-";
1936  std::cout << endNumber << " ";
1937  std::cout << "\n";
1938  }
1939 
1940  //continue;
1941 
1942  //handle a new completed value
1943  if(newValue)
1944  {
1945 
1946  // std::cout << "ExtKey=";
1947  // for(unsigned int k=0;k<jsonPath.size();++k)
1948  // std::cout << jsonPath[k] << "/";
1949  //
1950  // std::cout << currKey << "-{" << currDepth << "}: - ";
1951  //
1952  // if(isDataArray)
1953  // std::cout << "Array:: ";
1954  // if(newString)
1955  // std::cout << "New String:: ";
1956  // else
1957  // std::cout << "New Number:: ";
1958  //
1959  // std::cout << currVal << "\n";
1960 
1961 
1962  //extract only what we care about
1963  // for ConfigurationView only care about matching depth 1
1964 
1965  //handle matching depth 1 keys
1966 
1967  matchedKey = -1; //init to unfound
1968  for(unsigned int k=0;k<keys.size();++k)
1969  if((currDepth == 1 && keys[k] == currKey) ||
1970  (currDepth > 1 && keys[k] == jsonPath[1]))
1971  matchedKey = k;
1972 
1973  if(matchedKey != (unsigned int)-1)
1974  {
1975  // std::cout << "New Data for:: key[" << matchedKey << "]-" <<
1976  // keys[matchedKey] << "\n";
1977 
1978  switch(matchedKey)
1979  {
1980  case CV_JSON_FILL_NAME:
1981  if(currDepth == 1) setTableName(currVal);
1982  break;
1983  case CV_JSON_FILL_COMMENT:
1984  if(currDepth == 1) setComment(currVal);
1985  break;
1986  case CV_JSON_FILL_AUTHOR:
1987  if(currDepth == 1) setAuthor(currVal);
1988  break;
1989  case CV_JSON_FILL_CREATION_TIME:
1990  if(currDepth == 1) setCreationTime(strtol(currVal.c_str(),0,10));
1991  break;
1992  //case CV_JSON_FILL_COL_TYPES:
1993  //
1994  //break;
1995  case CV_JSON_FILL_DATA_SET:
1996  // std::cout << "CV_JSON_FILL_DATA_SET New Data for:: " << matchedKey << "]-" <<
1997  // keys[matchedKey] << "/" << currDepth << ".../" <<
1998  // currKey << "\n";
1999 
2000  if(currDepth == 2) //second level depth
2001  {
2002  //if matches first column name.. then add new row
2003  //else add to current row
2004  unsigned int col, ccnt = 0;
2005  unsigned int noc = getNumberOfColumns();
2006  for(;ccnt<noc;++ccnt)
2007  {
2008  //use colSpeedup to change the first column we search
2009  //for each iteration.. since we expect the data to
2010  //be arranged in column order
2011 
2012  if(fillWithLooseColumnMatching_)
2013  {
2014  //loose column matching makes no attempt to
2015  // match the column names
2016  //just assumes the data is in the correct order
2017 
2018  col = colSpeedup;
2019 
2020  //auto matched
2021  if(col <= lastCol) //add row (use lastCol in case new column-0 was added
2022  row = addRow();
2023  lastCol = col;
2024  if(getNumberOfRows() == 1) //only for first row
2025  sourceColumnNames_.emplace(currKey);
2026 
2027  //add value to row and column
2028 
2029  if(row >= getNumberOfRows())
2030  {
2031  __SS__ << "Invalid row" << std::endl; //should be impossible?
2032  std::cout << ss.str();
2033  throw std::runtime_error(ss.str());
2034  return -1;
2035  }
2036 
2037  theDataView_[row][col] = currVal;//THERE IS NO CHECK FOR WHAT IS READ FROM THE DATABASE. IT SHOULD BE ALREADY CONSISTENT
2038  break;
2039  }
2040  else
2041  {
2042  col = (ccnt + colSpeedup) % noc;
2043 
2044  //match key by ignoring '_'
2045  // also accept COMMENT == COMMENT_DESCRIPTION
2046  // (this is for backwards compatibility..)
2047  keyIsMatch = true;
2048  keyIsComment = true;
2049  for(keyIsMatchIndex=0, keyIsMatchStorageIndex=0, keyIsMatchCommentIndex=0;
2050  keyIsMatchIndex<currKey.size();++keyIsMatchIndex)
2051  {
2052  if(columnsInfo_[col].getStorageName()[keyIsMatchStorageIndex] == '_')
2053  ++keyIsMatchStorageIndex; //skip to next storage character
2054  if(currKey[keyIsMatchIndex] == '_')
2055  continue; //skip to next character
2056 
2057  //match to storage name
2058  if(keyIsMatchStorageIndex >= columnsInfo_[col].getStorageName().size() ||
2059  currKey[keyIsMatchIndex] !=
2060  columnsInfo_[col].getStorageName()[keyIsMatchStorageIndex])
2061  {
2062  //size mismatch or character mismatch
2063  keyIsMatch = false;
2064  if(!keyIsComment)
2065  break;
2066  }
2067 
2068  //check also if alternate comment is matched
2069  if(keyIsComment && keyIsMatchCommentIndex < COMMENT_ALT_KEY.size())
2070  {
2071  if(currKey[keyIsMatchIndex] != COMMENT_ALT_KEY[keyIsMatchCommentIndex])
2072  {
2073  //character mismatch with COMMENT
2074  keyIsComment = false;
2075  }
2076  }
2077 
2078  ++keyIsMatchStorageIndex; //go to next character
2079  }
2080 
2081  if(keyIsMatch || keyIsComment) //currKey == columnsInfo_[c].getStorageName())
2082  {
2083  //matched
2084  if(col <= lastCol) //add row (use lastCol in case new column-0 was added
2085  {
2086  if(getNumberOfRows()) //skip first time
2087  sourceColumnMissingCount_ += getNumberOfColumns() - colFoundCount;
2088 
2089  colFoundCount = 0; //reset column found count
2090  row = addRow();
2091  }
2092  lastCol = col;
2093  ++colFoundCount;
2094 
2095  if(getNumberOfRows() == 1) //only for first row
2096  sourceColumnNames_.emplace(currKey);
2097 
2098  //add value to row and column
2099 
2100  if(row >= getNumberOfRows())
2101  {
2102  __SS__ << "Invalid row" << std::endl; //should be impossible?!
2103  __COUT__ << "\n" << ss.str();
2104  throw std::runtime_error(ss.str());
2105  return -1; //never gets here
2106  }
2107 
2108  theDataView_[row][col] = currVal;
2109  break;
2110  }
2111  }
2112  }
2113 
2114  colSpeedup = (colSpeedup + 1) % noc; //short cut to proper column hopefully in next search
2115 
2116  if(ccnt >= getNumberOfColumns())
2117  {
2118  __SS__ << "\n\nInvalid column in JSON source data: " <<
2119  currKey << " not found in column names of table named " <<
2120  getTableName() << "." <<
2121  std::endl; //input data doesn't match config description
2122  __COUT__ << "\n" << ss.str();
2123  //CHANGED on 11/10/2016
2124  // to.. try just not populating data instead of error
2125  ++sourceColumnMismatchCount_; //but count errors
2126  if(getNumberOfRows() == 1) //only for first row
2127  sourceColumnNames_.emplace(currKey);
2128 
2129  //throw std::runtime_error(ss.str());
2130  }
2131 
2132  }
2133  break;
2134  default:; //unknown match?
2135  }
2136 
2137  }
2138 
2139  //clean up handling of new value
2140 
2141  newString = 0; //toggle flag
2142  newValue = 0; //toggle flag
2143  isDataArray = 0;
2144  endNumber = -1; //reset end number index
2145  }
2146 
2147  //if(i>200) break; //185
2148  }
2149 
2150  //__COUT__ << "Done!" << std::endl;
2151 
2152  //print();
2153 
2154  return 0; //success
2155 }
2156 
2157 
2158 
2159 //==============================================================================
2160 bool ConfigurationView::isURIEncodedCommentTheSame(const std::string &comment) const
2161 {
2162  std::string compareStr = StringMacros::decodeURIComponent(comment);
2163  return comment_ == compareStr;
2164 }
2165 //
2167 //bool ConfigurationView::isValueTheSame(const std::string &valueStr,
2168 // unsigned int r, unsigned int c) const
2169 //{
2170 // __COUT__ << "valueStr " << valueStr << std::endl;
2171 //
2172 // if(!(c < columnsInfo_.size() && r < getNumberOfRows()))
2173 // {
2174 // __SS__ << "Invalid row (" << (int)r << ") col (" << (int)c << ") requested!" << std::endl;
2175 // throw std::runtime_error(ss.str());
2176 // }
2177 //
2178 // __COUT__ << "originalValueStr " << theDataView_[r][c] << std::endl;
2179 //
2180 // if(columnsInfo_[c].getDataType() == ViewColumnInfo::DATATYPE_TIME)
2181 // {
2182 // time_t valueTime(strtol(valueStr.c_str(),0,10));
2183 // time_t originalValueTime;
2184 // getValue(originalValueTime,r,c);
2185 // __COUT__ << "time_t valueStr " << valueTime << std::endl;
2186 // __COUT__ << "time_t originalValueStr " << originalValueTime << std::endl;
2187 // return valueTime == originalValueTime;
2188 // }
2189 // else
2190 // {
2191 // return valueStr == theDataView_[r][c];
2192 // }
2193 //}
2194 
2195 //==============================================================================
2196 //fillFromCSV
2197 // Fills the view from the CSV string.
2198 //
2199 // Note: converts all %## to the ascii character, # is hex nibble
2200 // (e.g. '%' must be represented as "%25")
2201 //
2202 // dataOffset := starting destination row
2203 //
2204 // while there are row entries in the data.. replace
2205 // data range from [dataOffset, dataOffset+chunkSize-1]
2206 // ... note if less rows, this means rows were deleted
2207 // ... if more, then rows were added.
2208 //
2209 // ',' next cell delimiter
2210 // ';' next row delimiter
2211 //
2212 //
2213 // if author == "", do nothing special for author and timestamp column
2214 // if author != "", assign author for any row that has been modified, and assign now as timestamp
2215 //
2216 // Returns -1 if data was same and pre-existing content
2217 // otherwise 0
2218 int ConfigurationView::fillFromCSV(const std::string &data, const int &dataOffset,
2219  const std::string &author)
2220 throw(std::runtime_error)
2221 {
2222  int r = dataOffset;
2223  int c = 0;
2224 
2225  int i = 0; //use to parse data std::string
2226  int j = data.find(',',i); //find next cell delimiter
2227  int k = data.find(';',i); //find next row delimiter
2228 
2229  bool rowWasModified;
2230  unsigned int countRowsModified = 0;
2231  int authorCol = findColByType(ViewColumnInfo::TYPE_AUTHOR);
2232  int timestampCol = findColByType(ViewColumnInfo::TYPE_TIMESTAMP);
2233  //std::string valueStr, tmpTimeStr, originalValueStr;
2234 
2235  while(k != (int)(std::string::npos))
2236  {
2237  rowWasModified = false;
2238  if(r >= (int)getNumberOfRows())
2239  {
2240  addRow();
2241  //__COUT__ << "Row added" << std::endl;
2242  rowWasModified = true;
2243  }
2244 
2245  while(j < k && j != (int)(std::string::npos))
2246  {
2247  //__COUT__ << "Col " << (int)c << std::endl;
2248 
2249  //skip last 2 columns
2250  if(c >= (int)getNumberOfColumns()-2)
2251  {
2252  i=j+1;
2253  j = data.find(',',i); //find next cell delimiter
2254  ++c;
2255  continue;
2256  }
2257 
2258  if(setURIEncodedValue(data.substr(i,j-i),r,c))
2259  rowWasModified = true;
2260 
2261  i=j+1;
2262  j = data.find(',',i); //find next cell delimiter
2263  ++c;
2264  }
2265 
2266  //if row was modified, assign author and timestamp
2267  if(author != "" && rowWasModified)
2268  {
2269  __COUT__ << "Row=" << (int)r << " was modified!" << std::endl;
2270  setValue(author,r,authorCol);
2271  setValue(time(0),r,timestampCol);
2272  }
2273 
2274  if(rowWasModified) ++countRowsModified;
2275 
2276  ++r;
2277  c = 0;
2278 
2279 
2280  i = k+1;
2281  j = data.find(',',i); //find next cell delimiter
2282  k = data.find(';',i); //find new row delimiter
2283  }
2284 
2285  //delete excess rows
2286  while(r < (int)getNumberOfRows())
2287  {
2288  deleteRow(r);
2289  __COUT__ << "Row deleted: " << (int)r << std::endl;
2290  ++countRowsModified;
2291  }
2292 
2293  __COUT_INFO__ << "countRowsModified=" <<
2294  countRowsModified << std::endl;
2295 
2296  if(!countRowsModified)
2297  {
2298  __SS__ << "No rows were modified! No reason to fill a view with same content." << std::endl;
2299  __COUT__ << "\n" << ss.str();
2300  return -1;
2301  }
2302 
2303  //print(); //for debugging
2304 
2305  //setup sourceColumnNames_ to be correct
2306  sourceColumnNames_.clear();
2307  for(unsigned int i=0;i<getNumberOfColumns();++i)
2308  sourceColumnNames_.emplace(getColumnsInfo()[i].getStorageName());
2309 
2310  init(); //verify new table (throws runtime_errors)
2311 
2312  //printout for debugging
2313  // __SS__ << "\n";
2314  // print(ss);
2315  // __COUT__ << "\n" << ss.str() << std::endl;
2316 
2317  return 0;
2318 }
2319 
2320 //==============================================================================
2321 //setURIEncodedValue
2322 // converts all %## to the ascii character
2323 // returns true if value was different than original value
2324 //
2325 //
2326 // if author == "", do nothing special for author and timestamp column
2327 // if author != "", assign author for any row that has been modified, and assign now as timestamp
2328 bool ConfigurationView::setURIEncodedValue(const std::string &value, const unsigned int &r,
2329  const unsigned int &c, const std::string &author)
2330 {
2331  if(!(c < columnsInfo_.size() && r < getNumberOfRows()))
2332  {
2333  __SS__ << "Invalid row (" << (int)r << ") col (" << (int)c << ") requested!" <<
2334  "Number of Rows = " << getNumberOfRows() <<
2335  "Number of Columns = " << columnsInfo_.size() << std::endl;
2336  print(ss);
2337  throw std::runtime_error(ss.str());
2338  }
2339 
2340  std::string valueStr = StringMacros::decodeURIComponent(value);
2341  std::string originalValueStr = getValueAsString(r,c,false); //do not convert env variables
2342 
2343 
2344  //__COUT__ << "valueStr " << valueStr << std::endl;
2345  //__COUT__ << "originalValueStr " << originalValueStr << std::endl;
2346 
2347  if(columnsInfo_[c].getDataType() == ViewColumnInfo::DATATYPE_NUMBER)
2348  {
2349  //check if valid number
2350  std::string convertedString = StringMacros::convertEnvironmentVariables(valueStr);
2351  if(!StringMacros::isNumber(convertedString))
2352  {
2353  __SS__ << "\tIn configuration " << tableName_
2354  << " at column=" << columnsInfo_[c].getName()
2355  << " the value set (" << convertedString << ")"
2356  << " is not a number! Please fix it or change the column type..." << std::endl;
2357  throw std::runtime_error(ss.str());
2358  }
2359  theDataView_[r][c] = valueStr;
2360  }
2361  else if(columnsInfo_[c].getDataType() == ViewColumnInfo::DATATYPE_TIME)
2362  {
2363  // valueStr = StringMacros::decodeURIComponent(data.substr(i,j-i));
2364  //
2365  // getValue(tmpTimeStr,r,c);
2366  // if(valueStr != tmpTimeStr)//theDataView_[r][c])
2367  // {
2368  // __COUT__ << "valueStr=" << valueStr <<
2369  // " theDataView_[r][c]=" << tmpTimeStr << std::endl;
2370  // rowWasModified = true;
2371  // }
2372 
2373  setValue(time_t(strtol(valueStr.c_str(),0,10)),
2374  r,c);
2375  }
2376  else
2377  theDataView_[r][c] = valueStr;
2378 
2379  bool rowWasModified = (originalValueStr != getValueAsString(r,c,false)); //do not convert env variables
2380 
2381  //if row was modified, assign author and timestamp
2382  if(author != "" && rowWasModified)
2383  {
2384  __COUT__ << "Row=" << (int)r << " was modified!" << std::endl;
2385  int authorCol = findColByType(ViewColumnInfo::TYPE_AUTHOR);
2386  int timestampCol = findColByType(ViewColumnInfo::TYPE_TIMESTAMP);
2387  setValue(author,r,authorCol);
2388  setValue(time(0),r,timestampCol);
2389  }
2390 
2391  return rowWasModified;
2392 }
2393 
2394 //==============================================================================
2395 void ConfigurationView::resizeDataView(unsigned int nRows, unsigned int nCols)
2396 {
2397  //FIXME This maybe should disappear but I am using it in ConfigurationHandler still...
2398  theDataView_.resize(nRows, std::vector<std::string>(nCols));
2399 }
2400 
2401 //==============================================================================
2402 //addRow
2403 // returns index of added row, always is last row
2404 // return -1 on failure (throw error)
2405 //
2406 // if baseNameAutoUID != "", creates a UID based on this base name
2407 // and increments and appends an integer relative to the previous last row
2408 int ConfigurationView::addRow(const std::string &author,
2409  bool incrementUniqueData,
2410  std::string baseNameAutoUID)
2411 {
2412  int row = getNumberOfRows();
2413  theDataView_.resize(getNumberOfRows()+1,std::vector<std::string>(getNumberOfColumns()));
2414 
2415  std::vector<std::string> defaultRowValues =
2416  getDefaultRowValues();
2417 
2418  char indexString[1000];
2419  std::string tmpString, baseString;
2420  bool foundAny;
2421  unsigned int index;
2422  unsigned int maxUniqueData;
2423  std::string numString;
2424 
2425  //fill each col of new row with default values
2426  // if a row is a unique data row, increment last row in attempt to make a legal column
2427  for(unsigned int col=0;col<getNumberOfColumns();++col)
2428  {
2429  //__COUT__ << col << " " << columnsInfo_[col].getType() << " == " <<
2430  // ViewColumnInfo::TYPE_UNIQUE_DATA << __E__;
2431 
2432  //baseNameAutoUID indicates to attempt to make row unique
2433  // add index to max number
2434  if(incrementUniqueData &&
2435  (col == getColUID() || (row && columnsInfo_[col].getType() ==
2436  ViewColumnInfo::TYPE_UNIQUE_DATA)))
2437  {
2438 
2439  //__COUT__ << "New unique data entry is '" << theDataView_[row][col] << "'" << __E__;
2440 
2441  maxUniqueData = 0;
2442  tmpString = "";
2443  baseString = "";
2444 
2445  //find max in rows
2446 
2447  //this->print();
2448 
2449  for(unsigned int r=0;r<getNumberOfRows()-1;++r)
2450  {
2451  //find last non numeric character
2452 
2453  foundAny = false;
2454  tmpString = theDataView_[r][col];
2455 
2456  //__COUT__ << "tmpString " << tmpString << __E__;
2457 
2458  for(index = tmpString.length()-1;index < tmpString.length(); --index)
2459  {
2460  //__COUT__ << index << " tmpString[index] " << tmpString[index] << __E__;
2461  if(!(tmpString[index] >= '0' && tmpString[index] <= '9')) break; //if not numeric, break
2462  foundAny = true;
2463  }
2464 
2465  //__COUT__ << "index " << index << __E__;
2466 
2467  if(tmpString.length() &&
2468  foundAny) //then found a numeric substring
2469  {
2470  //create numeric substring
2471  numString = tmpString.substr(index+1);
2472  tmpString = tmpString.substr(0,index+1);
2473 
2474  //__COUT__ << "Found unique data base string '" <<
2475  // tmpString << "' and number string '" << numString <<
2476  // "' in last record '" << theDataView_[r][col] << "'" << __E__;
2477 
2478  //extract number
2479  sscanf(numString.c_str(),"%u",&index);
2480 
2481  if(index > maxUniqueData)
2482  {
2483  maxUniqueData = index;
2484  baseString = tmpString;
2485  }
2486  }
2487  }
2488 
2489  ++maxUniqueData; //increment
2490 
2491  sprintf(indexString,"%u",maxUniqueData);
2492  //__COUT__ << "indexString " << indexString << __E__;
2493 
2494  //__COUT__ << "baseNameAutoUID " << baseNameAutoUID << __E__;
2495  if(col == getColUID())
2496  {
2497  //handle UID case
2498  if(baseNameAutoUID != "")
2499  theDataView_[row][col] = baseNameAutoUID + indexString;
2500  else
2501  theDataView_[row][col] = baseString + indexString;
2502  }
2503  else
2504  theDataView_[row][col] = baseString + indexString;
2505 
2506  __COUT__ << "New unique data entry is '" << theDataView_[row][col] << "'" << __E__;
2507 
2508  //this->print();
2509  }
2510  else
2511  theDataView_[row][col] = defaultRowValues[col];
2512  }
2513 
2514  if(author != "")
2515  {
2516  __COUT__ << "Row=" << row << " was created!" << std::endl;
2517  int authorCol = findColByType(ViewColumnInfo::TYPE_AUTHOR);
2518  int timestampCol = findColByType(ViewColumnInfo::TYPE_TIMESTAMP);
2519  setValue(author,row,authorCol);
2520  setValue(time(0),row,timestampCol);
2521  }
2522 
2523 // if(baseNameAutoUID != "")
2524 // {
2525 // std::string indexSubstring = "0";
2526 // //if there is a last row with baseName in it
2527 // if(theDataView_.size() > 1 &&
2528 // 0 ==
2529 // theDataView_[theDataView_.size()-2][getColUID()].find(baseNameAutoUID))
2530 // //extract last index
2531 // indexSubstring = theDataView_[theDataView_.size()-2][getColUID()].substr(
2532 // baseNameAutoUID.size());
2533 //
2534 // unsigned int index;
2535 // sscanf(indexSubstring.c_str(),"%u",&index);
2536 // ++index; //increment
2537 // char indexString[100];
2538 // sprintf(indexString,"%u",index);
2539 //
2540 // baseNameAutoUID += indexString;
2541 // setValue(baseNameAutoUID,row,getColUID());
2542 // }
2543 
2544  return row;
2545 }
2546 
2547 //==============================================================================
2548 //deleteRow
2549 // throws exception on failure
2550 void ConfigurationView::deleteRow(int r)
2551 {
2552  if(r >= (int)getNumberOfRows())
2553  {
2554  //out of bounds
2555  __SS__ << "Row " << (int)r << " is out of bounds (Row Count = " <<
2556  getNumberOfRows() << ") and can not be deleted." <<
2557  std::endl;
2558  throw std::runtime_error(ss.str());
2559  }
2560 
2561  theDataView_.erase(theDataView_.begin()+r);
2562 }
2563 
2564 //==============================================================================
2565 //isColChildLinkUnique ~
2566 // find the pair of columns associated with a child link.
2567 //
2568 // c := a member column of the pair
2569 //
2570 // returns:
2571 // isGroup := indicates pair found is a group link
2572 // linkPair := pair of columns that are part of the link
2573 //
2574 // a unique link is defined by two columns: TYPE_START_CHILD_LINK, TYPE_START_CHILD_LINK_UID
2575 // a group link is defined by two columns: TYPE_START_CHILD_LINK, TYPE_START_CHILD_LINK_GROUP_ID
2576 //
2577 // returns true if column is member of a group or unique link.
2578 const bool ConfigurationView::getChildLink(const unsigned int& c, bool& isGroup,
2579  std::pair<unsigned int /*link col*/, unsigned int /*link id col*/>& linkPair) const
2580 {
2581 
2582  if(!(c < columnsInfo_.size()))
2583  {
2584  __SS__ << "Invalid col (" << (int)c << ") requested!" << std::endl;
2585  throw std::runtime_error(ss.str());
2586  }
2587 
2588  //__COUT__ << "getChildLink for col: " << (int)c << "-" <<
2589  // columnsInfo_[c].getType() << "-" << columnsInfo_[c].getName() << std::endl;
2590 
2591  //check if column is a child link UID
2592  if((isGroup = columnsInfo_[c].isChildLinkGroupID()) ||
2593  columnsInfo_[c].isChildLinkUID())
2594  {
2595  //must be part of unique link, (or invalid table?)
2596  //__COUT__ << "col: " << (int)c << std::endl;
2597  linkPair.second = c;
2598  std::string index = columnsInfo_[c].getChildLinkIndex();
2599 
2600  //__COUT__ << "index: " << index << std::endl;
2601 
2602  //find pair link
2603  for(unsigned int col=0; col<columnsInfo_.size(); ++col)
2604  {
2605  //__COUT__ << "try: " << col << "-" << columnsInfo_[col].getType() << "-" << columnsInfo_[col].getName() << std::endl;
2606  if(col == c) continue; //skip column c that we know
2607  else if(columnsInfo_[col].isChildLink() &&
2608  index == columnsInfo_[col].getChildLinkIndex())
2609  {
2610  //found match!
2611  //__COUT__ << "getChildLink Found match for col: " << (int)c << " at " << col << std::endl;
2612  linkPair.first = col;
2613  return true;
2614  }
2615  }
2616 
2617  //if here then invalid table!
2618  __SS__ << "\tIn view: " << tableName_ <<
2619  ", Can't find complete child link for column name " << columnsInfo_[c].getName() << std::endl;
2620  throw std::runtime_error(ss.str());
2621  }
2622 
2623  if(!columnsInfo_[c].isChildLink())
2624  return false; //cant be unique link
2625 
2626  //this is child link, so find pair link uid or gid column
2627  linkPair.first = c;
2628  std::string index = columnsInfo_[c].getChildLinkIndex();
2629 
2630  //__COUT__ << "index: " << index << std::endl;
2631 
2632  //find pair link
2633  for(unsigned int col=0; col<columnsInfo_.size(); ++col)
2634  {
2635  //__COUT__ << "try: " << col << "-" << columnsInfo_[col].getType() << "-" << columnsInfo_[col].getName() << std::endl;
2636  if(col == c) continue; //skip column c that we know
2637  // __COUT__ << "try: " << col << "-" << columnsInfo_[col].getType() <<
2638  // "-" << columnsInfo_[col].getName() <<
2639  // "-u" << columnsInfo_[col].isChildLinkUID() <<
2640  // "-g" << columnsInfo_[col].isChildLinkGroupID() << std::endl;
2641  //
2642  // if(columnsInfo_[col].isChildLinkUID())
2643  // __COUT__ << "-L" << columnsInfo_[col].getChildLinkIndex() << std::endl;
2644  //
2645  // if(columnsInfo_[col].isChildLinkGroupID())
2646  // __COUT__ << "-L" << columnsInfo_[col].getChildLinkIndex() << std::endl;
2647 
2648  if(((columnsInfo_[col].isChildLinkUID() && !(isGroup = false)) ||
2649  (columnsInfo_[col].isChildLinkGroupID() && (isGroup = true)))
2650  && index == columnsInfo_[col].getChildLinkIndex())
2651  {
2652  //found match!
2653  //__COUT__ << "getChildLink Found match for col: " << (int)c << " at " << col << std::endl;
2654  linkPair.second = col;
2655  return true;
2656  }
2657  }
2658 
2659  //if here then invalid table!
2660  __SS__ << "\tIn view: " << tableName_ <<
2661  ", Can't find complete child link id for column name " << columnsInfo_[c].getName() << std::endl;
2662  throw std::runtime_error(ss.str());
2663 }
2664 
2665 
2666 
2667