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