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