otsdaq  3.06.00
TableView.cc
1 #include "otsdaq/TableCore/TableView.h"
2 #include "otsdaq/Macros/StringMacros.h"
3 #include "otsdaq/TableCore/TableBase.h"
4 
5 #include <cstdlib>
6 #include <iostream>
7 #include <regex>
8 #include <sstream>
9 
10 using namespace ots;
11 
12 #undef __MF_SUBJECT__
13 #define __MF_SUBJECT__ "TableView"
14 #undef __COUT_HDR__
15 #define __COUT_HDR__ (tableName_ + "v" + version_.toString() + "\t<> ")
16 
17 const unsigned int TableView::INVALID = -1;
18 
19 //==============================================================================
20 TableView::TableView(const std::string& tableName)
21  : storageData_(tableName) // hijack momentarily for convert to caps
22  , tableName_(TableBase::convertToCaps(storageData_))
23  , version_(TableVersion::INVALID)
24  , comment_("")
25  , author_("")
26  , creationTime_(time(0))
27  , lastAccessTime_(0)
28  , colUID_(INVALID)
29  , colStatus_(INVALID)
30  , colPriority_(INVALID)
31  , fillWithLooseColumnMatching_(false)
32  , getSourceRawData_(false)
33  , sourceColumnMismatchCount_(0)
34  , sourceColumnMissingCount_(0)
35 {
36  storageData_ = ""; // unhijack
37 
38  if(tableName == "")
39  {
40  __SS__ << "Do not allow anonymous table view construction!" << __E__;
41  ss << StringMacros::stackTrace() << __E__;
42  __SS_THROW__;
43  }
44 
45 } // end constructor
46 
47 //==============================================================================
48 TableView::~TableView(void) {}
49 
50 //==============================================================================
54 TableView& TableView::operator=(const TableView /*src*/)
55 {
56  __SS__ << "Invalid use of operator=... Should not directly copy a TableView. Please "
57  "use TableView::copy(sourceView,author,comment)";
58  ss << StringMacros::stackTrace() << __E__;
59 
60  __COUT__ << ss.str() << __E__;
61  exit(0);
62  __SS_THROW__;
63 }
64 
65 //==============================================================================
66 TableView& TableView::copy(const TableView& src,
67  TableVersion destinationVersion,
68  const std::string& author)
69 {
70  // tableName_ = src.tableName_;
71  version_ = destinationVersion;
72  comment_ = src.comment_;
73  author_ = author; // take new author
74  // creationTime_ = time(0); //don't change creation time
75  lastAccessTime_ = time(0);
76 
77  // can not use operator= for TableViewColumn (it is a const class)
78  // columnsInfo_ = src.columnsInfo_;
79  columnsInfo_.clear();
80  for(auto& c : src.columnsInfo_)
81  columnsInfo_.push_back(c);
82 
83  theDataView_ = src.theDataView_;
84  sourceColumnNames_ = src.sourceColumnNames_;
85 
86  // RAR remove init() check, because usually copy() is only the first step
87  // in a series of changes that result in another call to init()
88  // init(); // verify consistency
89 
90  std::string tmpCachePrepend = TableBase::GROUP_CACHE_PREPEND;
91  tmpCachePrepend = TableBase::convertToCaps(tmpCachePrepend);
92  std::string tmpJsonDocPrepend = TableBase::JSON_DOC_PREPEND;
93  tmpJsonDocPrepend = TableBase::convertToCaps(tmpJsonDocPrepend);
94 
95  //if special GROUP CACHE table, handle construction in a special way
96  if(tableName_.substr(0, tmpCachePrepend.length()) == tmpCachePrepend ||
97  tableName_.substr(0, tmpJsonDocPrepend.length()) == tmpJsonDocPrepend)
98  {
99  __COUTT__ << "TableView copy for '" << tableName_ << "' done." << __E__;
100  return *this;
101  } //end special GROUP CACHE table construction
102 
103  initColUID(); // setup UID column
104  initRowDefaults();
105  try
106  {
107  initColStatus(); // setup Status column
108  }
109  catch(...)
110  {
111  } // ignore no Status column
112  try
113  {
114  initColPriority(); // setup Priority column
115  }
116  catch(...)
117  {
118  } // ignore no Priority column
119 
120  return *this;
121 } // end copy()
122 
123 //==============================================================================
126 unsigned int TableView::copyRows(const std::string& author,
127  const TableView& src,
128  unsigned int srcOffsetRow /* = 0 */,
129  unsigned int srcRowsToCopy /* = -1 */,
130  unsigned int destOffsetRow /* = -1 */,
131  unsigned char generateUniqueDataColumns /* = false */,
132  const std::string& baseNameAutoUID /*= "" */)
133 {
134  __COUTTV__(destOffsetRow);
135  __COUTTV__(srcOffsetRow);
136  __COUTTV__(srcRowsToCopy);
137 
138  unsigned int retRow = (unsigned int)-1;
139 
140  // check that column sizes match
141  if(src.getNumberOfColumns() != getNumberOfColumns())
142  {
143  __SS__ << "Error! Number of Columns of source view must match destination view."
144  << "Dimension of source is [" << src.getNumberOfColumns()
145  << "] and of destination is [" << getNumberOfColumns() << "]." << __E__;
146  __SS_THROW__;
147  }
148 
149  unsigned int srcRows = src.getNumberOfRows();
150 
151  for(unsigned int r = 0; r < srcRowsToCopy; ++r)
152  {
153  if(r + srcOffsetRow >= srcRows)
154  break; // end when no more source rows to copy (past bounds)
155 
156  destOffsetRow = addRow(author,
157  generateUniqueDataColumns /*incrementUniqueData*/,
158  baseNameAutoUID /*baseNameAutoUID*/,
159  destOffsetRow); // add and get row created
160 
161  if(retRow == (unsigned int)-1)
162  retRow = destOffsetRow; // save row of first copied entry
163 
164  // copy data
165  for(unsigned int col = 0; col < getNumberOfColumns(); ++col)
166  if(generateUniqueDataColumns &&
167  (columnsInfo_[col].getType() == TableViewColumnInfo::TYPE_UID ||
168  columnsInfo_[col].getType() == TableViewColumnInfo::TYPE_UNIQUE_DATA ||
169  columnsInfo_[col].getType() ==
170  TableViewColumnInfo::TYPE_UNIQUE_GROUP_DATA))
171  continue; // if leaving unique data, then skip copy
172  else
173  {
174  __COUTT__ << "Copying [" << r + srcOffsetRow << "][" << col << "] to ["
175  << destOffsetRow << "][" << col
176  << "] = " << src.theDataView_[r + srcOffsetRow][col] << __E__;
177  theDataView_[destOffsetRow][col] =
178  src.theDataView_[r + srcOffsetRow][col];
179  }
180 
181  // prepare for next row
182  ++destOffsetRow;
183  }
184 
185  return retRow;
186 } // end copyRows()
187 
188 //==============================================================================
195 void TableView::init(void)
196 {
197  //__COUT__ << "Starting table verification..." << StringMacros::stackTrace() << __E__;
198 
199  try
200  {
201  // verify column names are unique
202  // make set of names,.. and CommentDescription == COMMENT
203  std::set<std::string> colNameSet;
204  std::string capsColName, colName;
205  for(auto& colInfo : columnsInfo_)
206  {
207  colName = colInfo.getStorageName();
208  if(colName == "COMMENT_DESCRIPTION")
209  colName = "COMMENT";
210  capsColName = "";
211  for(unsigned int i = 0; i < colName.size(); ++i)
212  {
213  if(colName[i] == '_')
214  continue;
215  capsColName += colName[i];
216  }
217 
218  colNameSet.emplace(capsColName);
219  }
220 
221  if(colNameSet.size() != columnsInfo_.size())
222  {
223  __SS__ << "Table Error:\t"
224  << " Columns names must be unique! There are " << columnsInfo_.size()
225  << " columns and the unique name count is " << colNameSet.size()
226  << __E__;
227  __SS_THROW__;
228  }
229 
230  initColUID(); // setup UID column
231  try
232  {
233  initColStatus(); // setup Status column
234  }
235  catch(...)
236  {
237  } // ignore no Status column
238  try
239  {
240  initColPriority(); // setup Priority column
241  }
242  catch(...)
243  {
244  } // ignore no Priority column
245 
246  // fix source columns if not already populated
247  if(sourceColumnNames_.size() == 0) // setup sourceColumnNames_ to be correct
248  for(unsigned int i = 0; i < getNumberOfColumns(); ++i)
249  sourceColumnNames_.emplace(getColumnsInfo()[i].getStorageName());
250 
251  // require one comment column
252  unsigned int colPos;
253  if((colPos = findColByType(TableViewColumnInfo::TYPE_COMMENT)) != INVALID)
254  {
255  if(columnsInfo_[colPos].getName() != TableViewColumnInfo::COL_NAME_COMMENT)
256  {
257  __SS__ << "Table Error:\t" << TableViewColumnInfo::TYPE_COMMENT
258  << " data type column must have name="
259  << TableViewColumnInfo::COL_NAME_COMMENT << __E__;
260  __SS_THROW__;
261  }
262 
263  if(findColByType(TableViewColumnInfo::TYPE_COMMENT, colPos + 1) !=
264  INVALID) // found two!
265  {
266  __SS__ << "Table Error:\t" << TableViewColumnInfo::TYPE_COMMENT
267  << " data type in column " << columnsInfo_[colPos].getName()
268  << " is repeated. This is not allowed." << __E__;
269  __SS_THROW__;
270  }
271 
272  if(colPos != getNumberOfColumns() - 3)
273  {
274  __SS__ << "Table Error:\t" << TableViewColumnInfo::TYPE_COMMENT
275  << " data type column must be 3rd to last (in column "
276  << getNumberOfColumns() - 3 << ")." << __E__;
277  __SS_THROW__;
278  }
279  }
280  else
281  {
282  __SS__ << "Table Error:\t" << TableViewColumnInfo::TYPE_COMMENT
283  << " data type column "
284  << " is missing. This is not allowed." << __E__;
285  __SS_THROW__;
286  }
287 
288  // require one author column
289  if((colPos = findColByType(TableViewColumnInfo::TYPE_AUTHOR)) != INVALID)
290  {
291  if(findColByType(TableViewColumnInfo::TYPE_AUTHOR, colPos + 1) !=
292  INVALID) // found two!
293  {
294  __SS__ << "Table Error:\t" << TableViewColumnInfo::TYPE_AUTHOR
295  << " data type in column " << columnsInfo_[colPos].getName()
296  << " is repeated. This is not allowed." << __E__;
297  __SS_THROW__;
298  }
299 
300  if(colPos != getNumberOfColumns() - 2)
301  {
302  __SS__ << "Table Error:\t" << TableViewColumnInfo::TYPE_AUTHOR
303  << " data type column must be 2nd to last (in column "
304  << getNumberOfColumns() - 2 << ")." << __E__;
305  __SS_THROW__;
306  }
307  }
308  else
309  {
310  __SS__ << "Table Error:\t" << TableViewColumnInfo::TYPE_AUTHOR
311  << " data type column "
312  << " is missing. This is not allowed." << __E__;
313  __SS_THROW__;
314  }
315 
316  // require one timestamp column
317  if((colPos = findColByType(TableViewColumnInfo::TYPE_TIMESTAMP)) != INVALID)
318  {
319  if(findColByType(TableViewColumnInfo::TYPE_TIMESTAMP, colPos + 1) !=
320  INVALID) // found two!
321  {
322  __SS__ << "Table Error:\t" << TableViewColumnInfo::TYPE_TIMESTAMP
323  << " data type in column " << columnsInfo_[colPos].getName()
324  << " is repeated. This is not allowed." << __E__;
325  __SS_THROW__;
326  }
327 
328  if(colPos != getNumberOfColumns() - 1)
329  {
330  __SS__ << "Table Error:\t" << TableViewColumnInfo::TYPE_TIMESTAMP
331  << " data type column must be last (in column "
332  << getNumberOfColumns() - 1 << ")." << __E__;
333  __COUT_ERR__ << "\n" << ss.str();
334  __SS_THROW__;
335  }
336  }
337  else
338  {
339  __SS__ << "Table Error:\t" << TableViewColumnInfo::TYPE_TIMESTAMP
340  << " data type column "
341  << " is missing. This is not allowed." << __E__;
342  __SS_THROW__;
343  }
344 
345  // check that UID is really unique ID (no repeats)
346  // and ... allow letters, numbers, dash, underscore
347  // and ... force size 1
348  std::set<std::string /*uid*/> uidSet;
349  for(unsigned int row = 0; row < getNumberOfRows(); ++row)
350  {
351  if(uidSet.find(theDataView_[row][colUID_]) != uidSet.end())
352  {
353  __SS__ << ("Entries in UID are not unique. Specifically at row=" +
354  std::to_string(row) + " value=" + theDataView_[row][colUID_])
355  << __E__;
356  __SS_THROW__;
357  }
358 
359  if(theDataView_[row][colUID_].size() == 0)
360  {
361  __SS__ << "An invalid UID '" << theDataView_[row][colUID_] << "' "
362  << " was identified. UIDs must contain at least 1 character."
363  << __E__;
364  __SS_THROW__;
365  }
366 
367  for(unsigned int i = 0; i < theDataView_[row][colUID_].size(); ++i)
368  if(!((theDataView_[row][colUID_][i] >= 'A' &&
369  theDataView_[row][colUID_][i] <= 'Z') ||
370  (theDataView_[row][colUID_][i] >= 'a' &&
371  theDataView_[row][colUID_][i] <= 'z') ||
372  (theDataView_[row][colUID_][i] >= '0' &&
373  theDataView_[row][colUID_][i] <= '9') ||
374  (theDataView_[row][colUID_][i] == '-' ||
375  theDataView_[row][colUID_][i] == '_')))
376  {
377  __SS__ << "An invalid UID '" << theDataView_[row][colUID_] << "' "
378  << " was identified. UIDs must contain only letters, numbers, "
379  << "dashes, and underscores." << __E__;
380  __SS_THROW__;
381  }
382 
383  uidSet.insert(theDataView_[row][colUID_]);
384  }
385  if(uidSet.size() != getNumberOfRows())
386  {
387  __SS__ << "Entries in UID are not unique!"
388  << "There are " << getNumberOfRows()
389  << " records and the unique UID count is " << uidSet.size() << __E__;
390  __SS_THROW__;
391  }
392 
393  // check that any TYPE_UNIQUE_DATA columns are really unique (no repeats)
394  colPos = (unsigned int)-1;
395  while((colPos = findColByType(TableViewColumnInfo::TYPE_UNIQUE_DATA,
396  colPos + 1)) != INVALID)
397  {
398  std::set<std::string /*unique data*/> uDataSet;
399  for(unsigned int row = 0; row < getNumberOfRows(); ++row)
400  {
401  if(uDataSet.find(theDataView_[row][colPos]) != uDataSet.end())
402  {
403  __SS__ << "Entries in Unique Data column "
404  << columnsInfo_[colPos].getName()
405  << (" are not unique. Specifically at row=" +
406  std::to_string(row) +
407  " value=" + theDataView_[row][colPos])
408  << __E__;
409  __SS_THROW__;
410  }
411  uDataSet.insert(theDataView_[row][colPos]);
412  }
413  if(uDataSet.size() != getNumberOfRows())
414  {
415  __SS__ << "Entries in Unique Data column "
416  << columnsInfo_[colPos].getName() << " are not unique!"
417  << "There are " << getNumberOfRows()
418  << " records and the unique data count is " << uDataSet.size()
419  << __E__;
420  __SS_THROW__;
421  }
422  }
423 
424  // check that any TYPE_UNIQUE_GROUP_DATA columns are really unique fpr groups (no
425  // repeats)
426  colPos = (unsigned int)-1;
427  while((colPos = findColByType(TableViewColumnInfo::TYPE_UNIQUE_GROUP_DATA,
428  colPos + 1)) != INVALID)
429  {
430  // colPos is a unique group data column
431  // now, for each groupId column
432  // check that data is unique for all groups
433  for(unsigned int groupIdColPos = 0; groupIdColPos < columnsInfo_.size();
434  ++groupIdColPos)
435  if(columnsInfo_[groupIdColPos].isGroupID())
436  {
437  std::map<std::string /*group name*/,
438  std::pair<unsigned int /*memberCount*/,
439  std::set<std::string /*unique data*/>>>
440  uGroupDataSets;
441 
442  for(unsigned int row = 0; row < getNumberOfRows(); ++row)
443  {
444  auto groupIds = getSetOfGroupIDs(groupIdColPos, row);
445 
446  for(const auto& groupId : groupIds)
447  {
448  uGroupDataSets[groupId].first++; // add to member count
449 
450  if(uGroupDataSets[groupId].second.find(
451  theDataView_[row][colPos]) !=
452  uGroupDataSets[groupId].second.end())
453  {
454  __SS__ << "Entries in Unique Group Data column " << colPos
455  << ":" << columnsInfo_[colPos].getName()
456  << " are not unique for group ID '" << groupId
457  << ".' Specifically at row=" << std::to_string(row)
458  << " value=" << theDataView_[row][colPos] << __E__;
459  __SS_THROW__;
460  }
461  uGroupDataSets[groupId].second.insert(
462  theDataView_[row][colPos]);
463  }
464  }
465 
466  for(const auto& groupPair : uGroupDataSets)
467  if(uGroupDataSets[groupPair.first].second.size() !=
468  uGroupDataSets[groupPair.first].first)
469  {
470  __SS__
471  << "Entries in Unique Data column "
472  << columnsInfo_[colPos].getName()
473  << " are not unique for group '" << groupPair.first
474  << "!'"
475  << "There are " << uGroupDataSets[groupPair.first].first
476  << " records and the unique data count is "
477  << uGroupDataSets[groupPair.first].second.size() << __E__;
478  __SS_THROW__;
479  }
480  }
481  } // end TYPE_UNIQUE_GROUP_DATA check
482 
483  auto rowDefaults = initRowDefaults(); // getDefaultRowValues();
484 
485  // check that column types are well behaved
486  // - check that fixed choice data is one of choices
487  // - sanitize booleans
488  // - check that child link I are unique
489  // note: childLinkId refers to childLinkGroupIDs AND childLinkUIDs
490  std::set<std::string> groupIdIndexes, childLinkIndexes, childLinkIdLabels;
491  unsigned int groupIdIndexesCount = 0, childLinkIndexesCount = 0,
492  childLinkIdLabelsCount = 0;
493  bool tmpIsGroup;
494  std::pair<unsigned int /*link col*/, unsigned int /*link id col*/> tmpLinkPair;
495 
496  // check sanity of data view rows x cols (have seen weird out-of-range crashes)
497  if(getNumberOfRows() != theDataView_.size())
498  {
499  __SS__ << "Impossible row mismatch " << getNumberOfRows() << " vs "
500  << theDataView_.size() << "! How did you get here?" << __E__;
501  __SS_THROW__;
502  }
503  for(unsigned int row = 0; row < getNumberOfRows(); ++row)
504  if(getNumberOfColumns() != theDataView_[row].size())
505  {
506  __SS__ << "Impossible col mismatch " << getNumberOfColumns() << " vs ["
507  << row << "]" << theDataView_[row].size()
508  << "! How did you get here?" << __E__;
509  __SS_THROW__;
510  }
511  if(getNumberOfColumns() != columnsInfo_.size())
512  {
513  __SS__ << "Impossible col info mismatch " << getNumberOfColumns() << " vs "
514  << columnsInfo_.size() << "! How did you get here?" << __E__;
515  __SS_THROW__;
516  }
517  if(getNumberOfColumns() != rowDefaults.size())
518  {
519  __SS__ << "Impossible col default mismatch " << getNumberOfColumns() << " vs "
520  << rowDefaults.size() << "! How did you get here?" << __E__;
521  __SS_THROW__;
522  }
523 
524  for(unsigned int col = 0; col < getNumberOfColumns(); ++col)
525  {
526  if(columnsInfo_[col].getType() == TableViewColumnInfo::TYPE_FIXED_CHOICE_DATA)
527  {
528  const std::vector<std::string>& theDataChoices =
529  columnsInfo_[col].getDataChoices();
530 
531  // check if arbitrary values allowed
532  if(theDataChoices.size() && theDataChoices[0] == "arbitraryBool=1")
533  continue; // arbitrary values allowed
534 
535  bool found;
536  for(unsigned int row = 0; row < getNumberOfRows(); ++row)
537  {
538  found = false;
539  // check against default value first
540  if(theDataView_[row][col] == rowDefaults[col])
541  continue; // default is always ok
542 
543  for(const auto& choice : theDataChoices)
544  {
545  if(theDataView_[row][col] == choice)
546  {
547  found = true;
548  break;
549  }
550  }
551  if(!found)
552  {
553  __SS__ << getTableName() << " Error:\t'" << theDataView_[row][col]
554  << "' in column " << columnsInfo_[col].getName()
555  << " is not a valid Fixed Choice option. "
556  << "Possible values are as follows: ";
557 
558  ss << columnsInfo_[col].getDefaultValue()
559  << (columnsInfo_[col].getDataChoices().size() ? ", " : "");
560  for(unsigned int i = 0;
561  i < columnsInfo_[col].getDataChoices().size();
562  ++i)
563  {
564  if(columnsInfo_[col].getDataChoices()[i] == "arbitraryBool=0")
565  continue; //skip printout of arbitrary bool field first
566 
567  if(i && (i != 1 || columnsInfo_[col].getDataChoices()[0] !=
568  "arbitraryBool=0"))
569  ss << ", ";
570  ss << columnsInfo_[col].getDataChoices()[i];
571  }
572  ss << "." << __E__;
573  __SS_THROW__;
574  }
575  }
576  }
577  else if(columnsInfo_[col].isChildLink())
578  {
579  // check if forcing fixed choices
580 
581  const std::vector<std::string>& theDataChoices =
582  columnsInfo_[col].getDataChoices();
583 
584  // check if arbitrary values allowed
585  if(!theDataChoices.size() || theDataChoices[0] == "arbitraryBool=1")
586  continue; // arbitrary values allowed
587 
588  // skip one if arbitrary setting is embedded as first value
589  bool skipOne =
590  (theDataChoices.size() && theDataChoices[0] == "arbitraryBool=0");
591  bool hasSkipped;
592 
593  bool found;
594  for(unsigned int row = 0; row < getNumberOfRows(); ++row)
595  {
596  found = false;
597 
598  hasSkipped = false;
599  for(const auto& choice : theDataChoices)
600  {
601  if(skipOne && !hasSkipped)
602  {
603  hasSkipped = true;
604  continue;
605  }
606 
607  if(theDataView_[row][col] == choice)
608  {
609  found = true;
610  break;
611  }
612  }
613  if(!found)
614  {
615  __SS__ << getTableName() << " Error:\t the value '"
616  << theDataView_[row][col] << "' in column "
617  << columnsInfo_[col].getName()
618  << " is not a valid Fixed Choice option. "
619  << "Possible values are as follows: ";
620 
621  // ss <<
622  // StringMacros::vectorToString(columnsInfo_[col].getDataChoices())
623  // << __E__;
624  for(unsigned int i = skipOne ? 1 : 0;
625  i < columnsInfo_[col].getDataChoices().size();
626  ++i)
627  {
628  if(i > (skipOne ? 1 : 0))
629  ss << ", ";
630  ss << columnsInfo_[col].getDataChoices()[i];
631  }
632  ss << "." << __E__;
633  __SS_THROW__;
634  }
635  }
636  }
637  else if(columnsInfo_[col].getType() == TableViewColumnInfo::TYPE_ON_OFF)
638  for(unsigned int row = 0; row < getNumberOfRows(); ++row)
639  {
640  if(theDataView_[row][col] == "1" || theDataView_[row][col] == "on" ||
641  theDataView_[row][col] == "On" || theDataView_[row][col] == "ON")
642  theDataView_[row][col] = TableViewColumnInfo::TYPE_VALUE_ON;
643  else if(theDataView_[row][col] == "0" ||
644  theDataView_[row][col] == "off" ||
645  theDataView_[row][col] == "Off" ||
646  theDataView_[row][col] == "OFF")
647  theDataView_[row][col] = TableViewColumnInfo::TYPE_VALUE_OFF;
648  else
649  {
650  __SS__ << getTableName() << " Error:\t the value '"
651  << theDataView_[row][col] << "' in column "
652  << columnsInfo_[col].getName()
653  << " is not a valid Type (On/Off) std::string. Possible "
654  "values are 1, on, On, ON, 0, off, Off, OFF."
655  << __E__;
656  __SS_THROW__;
657  }
658  }
659  else if(columnsInfo_[col].getType() == TableViewColumnInfo::TYPE_TRUE_FALSE)
660  for(unsigned int row = 0; row < getNumberOfRows(); ++row)
661  {
662  if(theDataView_[row][col] == "1" ||
663  theDataView_[row][col] == "true" ||
664  theDataView_[row][col] == "True" ||
665  theDataView_[row][col] == "TRUE")
666  theDataView_[row][col] = TableViewColumnInfo::TYPE_VALUE_TRUE;
667  else if(theDataView_[row][col] == "0" ||
668  theDataView_[row][col] == "false" ||
669  theDataView_[row][col] == "False" ||
670  theDataView_[row][col] == "FALSE")
671  theDataView_[row][col] = TableViewColumnInfo::TYPE_VALUE_FALSE;
672  else
673  {
674  __SS__ << getTableName() << " Error:\t the value '"
675  << theDataView_[row][col] << "' in column "
676  << columnsInfo_[col].getName()
677  << " is not a valid Type (True/False) std::string. "
678  "Possible values are 1, true, True, TRUE, 0, false, "
679  "False, FALSE."
680  << __E__;
681  __SS_THROW__;
682  }
683  }
684  else if(columnsInfo_[col].getType() == TableViewColumnInfo::TYPE_YES_NO)
685  for(unsigned int row = 0; row < getNumberOfRows(); ++row)
686  {
687  if(theDataView_[row][col] == "1" || theDataView_[row][col] == "yes" ||
688  theDataView_[row][col] == "Yes" || theDataView_[row][col] == "YES")
689  theDataView_[row][col] = TableViewColumnInfo::TYPE_VALUE_YES;
690  else if(theDataView_[row][col] == "0" ||
691  theDataView_[row][col] == "no" ||
692  theDataView_[row][col] == "No" ||
693  theDataView_[row][col] == "NO")
694  theDataView_[row][col] = TableViewColumnInfo::TYPE_VALUE_NO;
695  else
696  {
697  __SS__ << getTableName() << " Error:\t the value '"
698  << theDataView_[row][col] << "' in column "
699  << columnsInfo_[col].getName()
700  << " is not a valid Type (Yes/No) std::string. Possible "
701  "values are 1, yes, Yes, YES, 0, no, No, NO."
702  << __E__;
703  __SS_THROW__;
704  }
705  }
706  else if(columnsInfo_[col].isGroupID()) // GroupID type
707  {
708  colLinkGroupIDs_[columnsInfo_[col].getChildLinkIndex()] =
709  col; // add to groupid map
710  // check uniqueness
711  groupIdIndexes.emplace(columnsInfo_[col].getChildLinkIndex());
712  ++groupIdIndexesCount;
713  }
714  else if(columnsInfo_[col].isChildLink()) // Child Link type
715  {
716  // sanitize no link to default
717  for(unsigned int row = 0; row < getNumberOfRows(); ++row)
718  if(theDataView_[row][col] == "NoLink" ||
719  theDataView_[row][col] == "No_Link" ||
720  theDataView_[row][col] == "NOLINK" ||
721  theDataView_[row][col] == "NO_LINK" ||
722  theDataView_[row][col] == "Nolink" ||
723  theDataView_[row][col] == "nolink" ||
724  theDataView_[row][col] == "noLink")
725  theDataView_[row][col] =
726  TableViewColumnInfo::DATATYPE_LINK_DEFAULT;
727 
728  // check uniqueness
729  childLinkIndexes.emplace(columnsInfo_[col].getChildLinkIndex());
730  ++childLinkIndexesCount;
731 
732  // force data type to TableViewColumnInfo::DATATYPE_STRING
733  if(columnsInfo_[col].getDataType() !=
734  TableViewColumnInfo::DATATYPE_STRING)
735  {
736  __SS__ << getTableName() << " Error:\t"
737  << "Column " << col << " with name '"
738  << columnsInfo_[col].getName()
739  << "' is a Child Link column and has an illegal data type of '"
740  << columnsInfo_[col].getDataType()
741  << "'. The data type for Child Link columns must be "
742  << TableViewColumnInfo::DATATYPE_STRING << __E__;
743  __SS_THROW__;
744  }
745 
746  // check for link mate (i.e. every child link needs link ID)
747  getChildLink(col, tmpIsGroup, tmpLinkPair);
748  }
749  else if(columnsInfo_[col].isChildLinkUID() || // Child Link ID type
750  columnsInfo_[col].isChildLinkGroupID())
751  {
752  // check uniqueness
753  childLinkIdLabels.emplace(columnsInfo_[col].getChildLinkIndex());
754  ++childLinkIdLabelsCount;
755 
756  // check that the Link ID is not empty, and force to default
757  for(unsigned int row = 0; row < getNumberOfRows(); ++row)
758  if(theDataView_[row][col] == "")
759  theDataView_[row][col] = rowDefaults[col];
760 
761  // check for link mate (i.e. every child link needs link ID)
762  getChildLink(col, tmpIsGroup, tmpLinkPair);
763  }
764 
765  // check if number exist and then if it is limited by min and max, use functions in here to get values different than stof
766  if(columnsInfo_[col].isNumberDataType())
767  {
768  std::string minimumValueString = columnsInfo_[col].getMinValue();
769  std::string maximumValueString = columnsInfo_[col].getMaxValue();
770  double minimumValue, maximumValue, valueFromTable;
771  bool minExists = false, maxExists = false;
772 
773  if(!minimumValueString.empty())
774  {
775  minExists = StringMacros::getNumber(
776  StringMacros::convertEnvironmentVariables(minimumValueString),
777  minimumValue);
778  if(!minExists)
779  {
780  __SS__ << "Inavlid user spec'd min value '" << minimumValueString
781  << "' which is not a valid number. The minimum value must "
782  "be a number (environment variables and math "
783  "operations are allowed)."
784  << __E__;
785  __SS_THROW__;
786  }
787  }
788 
789  if(!maximumValueString.empty())
790  {
791  maxExists = StringMacros::getNumber(
792  StringMacros::convertEnvironmentVariables(maximumValueString),
793  maximumValue);
794  if(!maxExists)
795  {
796  __SS__ << "Inavlid user spec'd max value '" << maximumValueString
797  << "' which is not a valid number. The maximum value must "
798  "be a number (environment variables and math "
799  "operations are allowed)."
800  << __E__;
801  __SS_THROW__;
802  }
803  }
804 
805  if(minExists && maxExists && minimumValue > maximumValue)
806  {
807  __SS__ << "Minimum value is greater than maximum, check table editor "
808  "to change this"
809  << __E__;
810  __SS_THROW__;
811  }
812 
813  if(minExists || maxExists)
814  for(unsigned int row = 0; row < getNumberOfRows(); ++row)
815  {
816  getValue(valueFromTable, row, col);
817  if(minExists && valueFromTable < minimumValue)
818  {
819  __SS__
820  << "The value '" << valueFromTable << "'("
821  << getValueAsString(
822  row, col, false /* convertEnvironmentVariables */)
823  << ") at [row,col]=[" << row << "," << col
824  << "] is outside the established limits: "
825  << valueFromTable
826  << " is lower than the specified minimum " << minimumValue
827  << "." << __E__;
828  __SS_THROW__;
829  }
830  if(maxExists && valueFromTable > maximumValue)
831  {
832  __SS__
833  << "This value '" << valueFromTable << "'("
834  << getValueAsString(
835  row, col, false /* convertEnvironmentVariables */)
836  << ") at [row,col]=[" << row << "," << col
837  << "] is outside the established limits: "
838  << valueFromTable
839  << " is greater than the specified maximum "
840  << maximumValue << "." << __E__;
841  __SS_THROW__;
842  }
843  }
844  } // end handling NUMBER data types
845  } // end column loop
846 
847  // verify child link index uniqueness
848  if(groupIdIndexes.size() != groupIdIndexesCount)
849  {
850  __SS__ << ("GroupId Labels are not unique!") << "There are "
851  << groupIdIndexesCount << " GroupId Labels and the unique count is "
852  << groupIdIndexes.size() << __E__;
853  __SS_THROW__;
854  }
855  if(childLinkIndexes.size() != childLinkIndexesCount)
856  {
857  __SS__ << ("Child Link Labels are not unique!") << "There are "
858  << childLinkIndexesCount
859  << " Child Link Labels and the unique count is "
860  << childLinkIndexes.size() << __E__;
861  __SS_THROW__;
862  }
863  if(childLinkIdLabels.size() != childLinkIdLabelsCount)
864  {
865  __SS__ << ("Child Link ID Labels are not unique!") << "There are "
866  << childLinkIdLabelsCount
867  << " Child Link ID Labels and the unique count is "
868  << childLinkIdLabels.size() << __E__;
869  __SS_THROW__;
870  }
871  }
872  catch(...)
873  {
874  __COUT__ << "Error occured in TableView::init() for version=" << version_
875  << __E__;
876  throw;
877  }
878 } // end init()
879 
880 //==============================================================================
885 void TableView::getValue(std::string& value,
886  unsigned int row,
887  unsigned int col,
888  bool doConvertEnvironmentVariables) const
889 {
890  if(!(row < getNumberOfRows() && col < theDataView_[row].size()))
891  {
892  __SS__ << "Invalid row col requested " << row << "," << col << " vs "
893  << getNumberOfRows() << "," << columnsInfo_.size() << "/"
894  << theDataView_[row].size() << __E__;
895  __SS_THROW__;
896  }
897 
898  value = validateValueForColumn(
899  theDataView_[row][col], col, doConvertEnvironmentVariables);
900 } // end getValue()
901 
902 //==============================================================================
907 std::string TableView::validateValueForColumn(const std::string& value,
908  unsigned int col,
909  bool doConvertEnvironmentVariables) const
910 {
911  if(col >= columnsInfo_.size())
912  {
913  __SS__ << "Invalid col requested" << __E__;
914  __SS_THROW__;
915  }
916 
917  if(columnsInfo_[col].getType() == TableViewColumnInfo::TYPE_FIXED_CHOICE_DATA &&
918  // value == columnsInfo_[col].getDefaultValue())
919  value == columnsInfo_[col].getDefaultDefaultValue(columnsInfo_[col].getType(),
920  columnsInfo_[col].getDataType()))
921  {
922  // if type string, fixed choice and DEFAULT, then return string of first choice
923 
924  std::vector<std::string> choices = columnsInfo_[col].getDataChoices();
925 
926  // consider arbitrary bool
927  bool skipOne = (choices.size() && choices[0].find("arbitraryBool=") == 0);
928  size_t index = (skipOne ? 1 : 0);
929  if(choices.size() > index)
930  {
931  return doConvertEnvironmentVariables
933  : choices[index]; // handled value from fixed choices
934  }
935  } // end handling default to fixed choice conversion
936 
937  if(columnsInfo_[col].getDataType() == TableViewColumnInfo::DATATYPE_STRING)
938  return doConvertEnvironmentVariables
940  : value;
941  else if(columnsInfo_[col].getDataType() == TableViewColumnInfo::DATATYPE_TIME)
942  {
944  doConvertEnvironmentVariables
946  : value);
947  }
948  else
949  {
950  __SS__ << "\tUnrecognized column data type: " << columnsInfo_[col].getDataType()
951  << " in configuration " << tableName_
952  << " at column=" << columnsInfo_[col].getName()
953  << " for getValue with type '"
954  << StringMacros::demangleTypeName(typeid(std::string).name()) << "'"
955  << __E__;
956  __SS_THROW__;
957  }
958 
959  // return retValue;
960 } // end validateValueForColumn()
961 
962 //==============================================================================
966 std::string TableView::getValueAsString(unsigned int row,
967  unsigned int col,
968  bool doConvertEnvironmentVariables) const
969 {
970  if(!(col < columnsInfo_.size() && row < getNumberOfRows()))
971  {
972  __SS__ << ("Invalid row col requested") << __E__;
973  __SS_THROW__;
974  }
975 
976  __COUTS__(30) << columnsInfo_[col].getType() << " " << col << __E__;
977 
978  if(columnsInfo_[col].getType() == TableViewColumnInfo::TYPE_ON_OFF)
979  {
980  if(theDataView_[row][col] == "1" || theDataView_[row][col] == "on" ||
981  theDataView_[row][col] == "On" || theDataView_[row][col] == "ON")
982  return TableViewColumnInfo::TYPE_VALUE_ON;
983  else
984  return TableViewColumnInfo::TYPE_VALUE_OFF;
985  }
986  else if(columnsInfo_[col].getType() == TableViewColumnInfo::TYPE_TRUE_FALSE)
987  {
988  if(theDataView_[row][col] == "1" || theDataView_[row][col] == "true" ||
989  theDataView_[row][col] == "True" || theDataView_[row][col] == "TRUE")
990  return TableViewColumnInfo::TYPE_VALUE_TRUE;
991  else
992  return TableViewColumnInfo::TYPE_VALUE_FALSE;
993  }
994  else if(columnsInfo_[col].getType() == TableViewColumnInfo::TYPE_YES_NO)
995  {
996  if(theDataView_[row][col] == "1" || theDataView_[row][col] == "yes" ||
997  theDataView_[row][col] == "Yes" || theDataView_[row][col] == "YES")
998  return TableViewColumnInfo::TYPE_VALUE_YES;
999  else
1000  return TableViewColumnInfo::TYPE_VALUE_NO;
1001  }
1002 
1003  return doConvertEnvironmentVariables
1004  ? StringMacros::convertEnvironmentVariables(theDataView_[row][col])
1005  : theDataView_[row][col];
1006 } //end getValueAsString()
1007 
1008 //==============================================================================
1016  unsigned int row,
1017  unsigned int col,
1018  bool doConvertEnvironmentVariables /* = true */,
1019  bool quotesToDoubleQuotes /* = false */) const
1020 {
1021  std::string val = getValueAsString(row, col, doConvertEnvironmentVariables);
1022  std::string retVal = "";
1023  retVal.reserve(val.size()); // reserve roughly right size
1024  for(unsigned int i = 0; i < val.size(); ++i)
1025  {
1026  if(val[i] == '\n')
1027  retVal += "\\n";
1028  else if(val[i] == '\t')
1029  retVal += "\\t";
1030  else if(val[i] == '\r')
1031  retVal += "\\r";
1032  else
1033  {
1034  // escaped characters need a
1035  if(val[i] == '\\')
1036  retVal += '\\';
1037  if(quotesToDoubleQuotes && val[i] == '"')
1038  retVal += '"'; //convert " to "" for excel style CSV
1039  else if(!quotesToDoubleQuotes && val[i] == '"')
1040  retVal += '\\';
1041  retVal += val[i];
1042  }
1043  }
1044  return retVal;
1045 } //end getEscapedValueAsString()
1046 
1047 //==============================================================================
1050 void TableView::setValue(const std::string& value, unsigned int row, unsigned int col)
1051 {
1052  if(!(col < columnsInfo_.size() && row < getNumberOfRows()))
1053  {
1054  __SS__ << "Invalid row (" << row << ") col (" << col << ") requested!" << __E__;
1055  __SS_THROW__;
1056  }
1057 
1058  if(columnsInfo_[col].getDataType() == TableViewColumnInfo::DATATYPE_STRING)
1059  theDataView_[row][col] = value;
1060  else // dont allow TableViewColumnInfo::DATATYPE_TIME to be set as string.. force use
1061  // as time_t to standardize string result
1062  {
1063  __SS__ << "\tUnrecognized column data type: " << columnsInfo_[col].getDataType()
1064  << " in configuration " << tableName_
1065  << " at column=" << columnsInfo_[col].getName()
1066  << " for setValue with type '"
1067  << StringMacros::demangleTypeName(typeid(value).name()) << "'" << __E__;
1068  __SS_THROW__;
1069  }
1070 } // end setValue()
1071 
1072 //==============================================================================
1073 void TableView::setValue(const char* value, unsigned int row, unsigned int col)
1074 {
1075  setValue(std::string(value), row, col);
1076 } // end setValue()
1077 
1078 //==============================================================================
1081 void TableView::setValueAsString(const std::string& value,
1082  unsigned int row,
1083  unsigned int col)
1084 {
1085  if(!(col < columnsInfo_.size() && row < getNumberOfRows()))
1086  {
1087  __SS__ << "Invalid row (" << row << ") col (" << col << ") requested!" << __E__;
1088  __SS_THROW__;
1089  }
1090 
1091  theDataView_[row][col] = value;
1092 } // end setValueAsString()
1093 
1094 //==============================================================================
1102  unsigned int row,
1103  unsigned int col,
1104  std::string baseValueAsString /*= "" */,
1105  bool doMathAppendStrategy /*= false*/,
1106  std::string childLinkIndex /* = "" */,
1107  std::string groupId /* = "" */)
1108 {
1109  if(!(col < columnsInfo_.size() && row < getNumberOfRows()))
1110  {
1111  __SS__ << "Invalid row (" << row << ") col (" << col << ") requested!" << __E__;
1112  __SS_THROW__;
1113  }
1114 
1115  bool isUniqueGroupCol =
1116  (columnsInfo_[col].getType() == TableViewColumnInfo::TYPE_UNIQUE_GROUP_DATA);
1117  unsigned int childLinkIndexCol = -1;
1118  if(isUniqueGroupCol)
1119  {
1120  __COUTVS__(12, childLinkIndex); //set TRACE level to TLVL_DEBUG + 12
1121  __COUTVS__(12, groupId); //set TRACE level to TLVL_DEBUG + 12
1122  childLinkIndexCol = getLinkGroupIDColumn(childLinkIndex); // column in question
1123  __COUTVS__(12, childLinkIndexCol); //set TRACE level to TLVL_DEBUG + 12
1124  }
1125 
1126  __COUTT__ << "Current '" << columnsInfo_[col].getName() << "' "
1127  << (isUniqueGroupCol ? "(Unique in Group) " : "")
1128  << "unique data entry is data[" << row << "][" << col << "] = '"
1129  << theDataView_[row][col] << "' baseValueAsString = " << baseValueAsString
1130  << " doMathAppendStrategy = " << doMathAppendStrategy << __E__;
1131 
1132  bool firstConflict = true;
1133  int maxUniqueData = -1;
1134  std::string tmpString = "";
1135  bool foundAny;
1136  unsigned int index;
1137  std::string numString;
1138  std::string opString; // for doMathAppendStrategy
1139 
1140  // find max in rows
1141 
1142  // this->print();
1143 
1144  for(unsigned int r = 0; r < getNumberOfRows(); ++r)
1145  {
1146  if(r == row)
1147  continue; // skip row to add
1148 
1149  if(isUniqueGroupCol && !isEntryInGroupCol(r, childLinkIndexCol, groupId))
1150  continue; // skip rows not in group
1151 
1152  // find last non numeric character
1153 
1154  foundAny = false;
1155  tmpString = theDataView_[r][col];
1156 
1157  __COUTS__(3) << "row[" << r << "] tmpString " << tmpString << __E__;
1158 
1159  for(index = tmpString.length() - 1; index < tmpString.length(); --index)
1160  {
1161  __COUTS__(3) << index << " tmpString[index] " << tmpString[index] << __E__;
1162  if(!(tmpString[index] >= '0' && tmpString[index] <= '9'))
1163  break; // if not numeric, break
1164  foundAny = true;
1165  }
1166 
1167  __COUTS__(3) << "index " << index << " foundAny " << foundAny << __E__;
1168 
1169  if(tmpString.length() && foundAny) // then found a numeric substring
1170  {
1171  // create numeric substring
1172  numString = tmpString.substr(index + 1);
1173 
1174  // and alpha basestring
1175  tmpString = tmpString.substr(0, index + 1);
1176 
1177  if(doMathAppendStrategy && tmpString.size())
1178  {
1179  // look for op string
1180  foundAny = false;
1181  for(index = tmpString.length() - 1; index < tmpString.length(); --index)
1182  {
1183  __COUTS__(4)
1184  << index << " tmpString[index] " << tmpString[index] << __E__;
1185  if(!(tmpString[index] == '+' || tmpString[index] == ' '))
1186  break; // if not plus op, break
1187  foundAny = true;
1188  }
1189 
1190  if(foundAny)
1191  {
1192  // create numeric substring
1193  opString = tmpString.substr(index + 1);
1194 
1195  // and alpha basestring
1196  tmpString = tmpString.substr(0, index + 1);
1197  }
1198  }
1199 
1200  __COUTS__(3) << tmpString << " vs " << baseValueAsString << __E__;
1201 
1202  if(baseValueAsString != "" && tmpString != baseValueAsString)
1203  continue; // skip max unique number if basestring does not match
1204 
1205  __COUTS__(3) << "Found unique data base string '" << tmpString
1206  << "' and number string '" << numString << "' in last record '"
1207  << theDataView_[r][col] << "'" << __E__;
1208 
1209  if(firstConflict)
1210  {
1211  // if baseValueAsString ends in number, then add _ to keep naming similar
1212  if(baseValueAsString.size() &&
1213  baseValueAsString[baseValueAsString.size() - 1] >= '0' &&
1214  baseValueAsString[baseValueAsString.size() - 1] <= '9')
1215  baseValueAsString += '_';
1216 
1217  firstConflict = false;
1218  }
1219 
1220  // extract number
1221  sscanf(numString.c_str(), "%u", &index);
1222 
1223  if((int)index > maxUniqueData)
1224  {
1225  maxUniqueData = (int)index;
1226 
1227  if(baseValueAsString == "")
1228  baseValueAsString = tmpString; // assume a value for base string
1229  }
1230  }
1231  else if(maxUniqueData < 0 &&
1232  (baseValueAsString == "" || tmpString == baseValueAsString))
1233  {
1234  if(firstConflict)
1235  {
1236  // if baseValueAsString ends in number, then add _ to keep naming similar
1237  if(baseValueAsString.size() &&
1238  baseValueAsString[baseValueAsString.size() - 1] >= '0' &&
1239  baseValueAsString[baseValueAsString.size() - 1] <= '9')
1240  baseValueAsString += '_';
1241 
1242  firstConflict = false;
1243  }
1244 
1245  maxUniqueData = 0; // start a number if basestring conflict
1246  }
1247  } //end loop finding max unique data (potentially for group)
1248 
1249  __COUTVS__(12, maxUniqueData); //set TRACE level to TLVL_DEBUG + 12
1250  __COUTVS__(12, baseValueAsString); //set TRACE level to TLVL_DEBUG + 12
1251 
1252  if(maxUniqueData == -1) // if no conflicts, then do not add number
1253  {
1254  if(baseValueAsString != "")
1255  theDataView_[row][col] = baseValueAsString;
1256  else
1257  theDataView_[row][col] = columnsInfo_[col].getDefaultValue();
1258  }
1259  else
1260  {
1261  ++maxUniqueData; // increment
1262 
1263  char indexString[1000];
1264  sprintf(indexString, "%u", maxUniqueData);
1265 
1266  __COUTVS__(12, indexString); //set TRACE level to TLVL_DEBUG + 12
1267  __COUTVS__(12, baseValueAsString); //set TRACE level to TLVL_DEBUG + 12
1268 
1269  if(doMathAppendStrategy)
1270  theDataView_[row][col] = baseValueAsString + " + " + indexString;
1271  else
1272  theDataView_[row][col] = baseValueAsString + indexString;
1273  }
1274 
1275  __COUTT__ << "New unique data entry is data[" << row << "][" << col << "] = '"
1276  << theDataView_[row][col] << "'" << __E__;
1277 
1278  if(TTEST(13))
1279  {
1280  std::stringstream ss;
1281  this->print(ss);
1282  __COUT_MULTI__(13, ss.str());
1283  }
1284 
1285  return theDataView_[row][col];
1286 } // end setUniqueColumnValue()
1287 
1288 //==============================================================================
1291 unsigned int TableView::initColUID(void)
1292 {
1293  if(colUID_ != INVALID)
1294  return colUID_;
1295 
1296  // if doesn't exist throw error! each view must have a UID column
1298  if(colUID_ == INVALID)
1299  {
1300  __COUT__ << "Column Types: " << __E__;
1301  for(unsigned int col = 0; col < columnsInfo_.size(); ++col)
1302  std::cout << columnsInfo_[col].getType() << "() "
1303  << columnsInfo_[col].getName() << __E__;
1304  __SS__ << "\tMissing UID Column in table named '" << tableName_ << "'" << __E__;
1305  __SS_THROW__;
1306  }
1307  return colUID_;
1308 }
1309 //==============================================================================
1313 unsigned int TableView::getColUID(void) const
1314 {
1315  if(colUID_ != INVALID)
1316  return colUID_;
1317 
1318  __COUT__ << "Column Types: " << __E__;
1319  for(unsigned int col = 0; col < columnsInfo_.size(); ++col)
1320  std::cout << columnsInfo_[col].getType() << "() " << columnsInfo_[col].getName()
1321  << __E__;
1322 
1323  __SS__ << ("Missing UID Column in config named " + tableName_ +
1324  ". (Possibly TableView was just not initialized?" +
1325  " This is the const call so can not alter class members)")
1326  << __E__;
1327 
1328  ss << StringMacros::stackTrace() << __E__;
1329 
1330  __SS_THROW__;
1331 }
1332 
1333 //==============================================================================
1336 unsigned int TableView::initColStatus(void)
1337 {
1338  if(colStatus_ != INVALID)
1339  return colStatus_;
1340 
1341  // if doesn't exist throw error! each view must have a UID column
1342  for(unsigned int col = 0; col < columnsInfo_.size(); ++col)
1343  if(columnsInfo_[col].getName() == TableViewColumnInfo::COL_NAME_STATUS)
1344  {
1345  colStatus_ = col;
1346  return colStatus_;
1347  }
1348  for(unsigned int col = 0; col < columnsInfo_.size(); ++col)
1349  if(columnsInfo_[col].getName() == TableViewColumnInfo::COL_NAME_ENABLED)
1350  {
1351  colStatus_ = col;
1352  return colStatus_;
1353  }
1354 
1355  // at this point not found!
1356 
1357  __SS__ << "\tMissing column named '" << TableViewColumnInfo::COL_NAME_STATUS
1358  << "' or '" << TableViewColumnInfo::COL_NAME_ENABLED << "' in table '"
1359  << tableName_ << ".'" << __E__;
1360  ss << "\n\nTable '" << tableName_ << "' Columns: " << __E__;
1361  for(unsigned int col = 0; col < columnsInfo_.size(); ++col)
1362  ss << columnsInfo_[col].getType() << "() " << columnsInfo_[col].getName()
1363  << __E__;
1364 
1365  __SS_ONLY_THROW__;
1366 
1367 } // end initColStatus()
1368 
1369 //==============================================================================
1372 unsigned int TableView::initColPriority(void)
1373 {
1374  if(colPriority_ != INVALID)
1375  return colPriority_;
1376 
1377  // if doesn't exist throw error! each view must have a UID column
1378  colPriority_ =
1379  findCol("*" + TableViewColumnInfo::COL_NAME_PRIORITY); // wild card search
1380  if(colPriority_ == INVALID)
1381  {
1382  __SS__ << "\tMissing column named '" << TableViewColumnInfo::COL_NAME_PRIORITY
1383  << "' in table '" << tableName_ << ".'" << __E__;
1384  ss << "\n\nTable '" << tableName_ << "' Columns: " << __E__;
1385  for(unsigned int col = 0; col < columnsInfo_.size(); ++col)
1386  ss << columnsInfo_[col].getType() << "() " << columnsInfo_[col].getName()
1387  << __E__;
1388 
1389  __SS_THROW__;
1390  }
1391  return colPriority_;
1392 }
1393 
1394 //==============================================================================
1398 unsigned int TableView::getColStatus(void) const
1399 {
1400  if(colStatus_ != INVALID)
1401  return colStatus_;
1402 
1403  __SS__ << "\tMissing column named '" << TableViewColumnInfo::COL_NAME_STATUS
1404  << "' or '" << TableViewColumnInfo::COL_NAME_ENABLED << "' in table '"
1405  << tableName_ << ".'"
1406  << " (The Status column is identified when the TableView is initialized)"
1407  << __E__;
1408 
1409  ss << "\n\nTable '" << tableName_ << "' Columns: " << __E__;
1410  for(unsigned int col = 0; col < columnsInfo_.size(); ++col)
1411  ss << "\t" << columnsInfo_[col].getType() << "() " << columnsInfo_[col].getName()
1412  << __E__;
1413 
1414  ss << __E__;
1415 
1416  ss << StringMacros::stackTrace() << __E__;
1417 
1418  __COUT_WARN__ << ss.str();
1419  __SS_ONLY_THROW__;
1420 } // end getColStatus()
1421 
1422 //==============================================================================
1429 unsigned int TableView::getColPriority(void) const
1430 {
1431  if(colPriority_ != INVALID)
1432  return colPriority_;
1433 
1434  __SS__ << "Priority column was not found... \nColumn Types: " << __E__;
1435 
1436  ss << "Missing " << TableViewColumnInfo::COL_NAME_PRIORITY
1437  << " Column in table named '" << tableName_
1438  << ".' (The Priority column is identified when the TableView is initialized)"
1439  << __E__; // this is the const call, so can not identify the column and
1440  // set colPriority_ here
1441 
1442  ss << "\n\nTable '" << tableName_ << "' Columns: " << __E__;
1443  for(unsigned int col = 0; col < columnsInfo_.size(); ++col)
1444  ss << "\t" << columnsInfo_[col].getType() << "() " << columnsInfo_[col].getName()
1445  << __E__;
1446  ss << __E__;
1447 
1448  ss << StringMacros::stackTrace() << __E__;
1449 
1450  __SS_ONLY_THROW__; // keep it quiet
1451 } // end getColPriority()
1452 
1453 //==============================================================================
1456 void TableView::addRowToGroup(const unsigned int& row,
1457  const unsigned int& col,
1458  const std::string& groupID) //,
1459 // const std::string &colDefault)
1460 {
1461  if(isEntryInGroupCol(row, col, groupID))
1462  {
1463  __SS__ << "GroupID (" << groupID << ") added to row (" << row
1464  << " is already present!" << __E__;
1465  __SS_THROW__;
1466  }
1467 
1468  // not in group, so
1469  // if no groups
1470  // set groupid
1471  // if other groups
1472  // prepend groupId |
1473  if(getDataView()[row][col] == "" ||
1474  getDataView()[row][col] == getDefaultRowValues()[col]) // colDefault)
1475  setValue(groupID, row, col);
1476  else
1477  setValue(groupID + " | " + getDataView()[row][col], row, col);
1478 
1479  //__COUT__ << getDataView()[row][col] << __E__;
1480 } // end addRowToGroup()
1481 
1482 //==============================================================================
1488 std::vector<unsigned int /*group row*/> TableView::getGroupRows(
1489  const unsigned int groupIdCol,
1490  const std::string& groupID,
1491  bool onlyStatusTrue /*=false*/,
1492  bool orderedByPriority /*=false*/) const
1493 {
1494  std::vector<unsigned int /*group row*/> retVector;
1495  std::vector<std::vector<unsigned int /*group row*/>> groupRowVectors =
1496  getGroupRowsInVectors(groupIdCol, groupID, onlyStatusTrue, orderedByPriority);
1497 
1498  for(const auto& groupRowVector : groupRowVectors)
1499  for(const auto& groupRow : groupRowVector)
1500  retVector.push_back(groupRow);
1501 
1502  return retVector;
1503 } // end getGroupRows()
1504 
1505 //==============================================================================
1511 std::vector<std::vector<unsigned int /*group row*/>> TableView::getGroupRowsByPriority(
1512  const unsigned int groupIdCol,
1513  const std::string& groupID,
1514  bool onlyStatusTrue /*=false*/) const
1515 {
1516  return getGroupRowsInVectors(
1517  groupIdCol, groupID, onlyStatusTrue, true /*orderedByPriority*/);
1518 } // end getGroupRowsByPriority()
1519 
1520 //==============================================================================
1528 std::vector<std::vector<unsigned int /*group row*/>> TableView::getGroupRowsInVectors(
1529  const unsigned int groupIdCol,
1530  const std::string& groupID,
1531  bool onlyStatusTrue,
1532  bool orderedByPriority) const
1533 {
1534  std::map<uint64_t /*priority*/, std::vector<unsigned int /*child row*/>>
1535  mapByPriority;
1536  std::vector<std::vector<unsigned int /*group row*/>> retVector;
1537  uint64_t tmpPriority;
1538  bool tmpStatus;
1539 
1540  if(!(orderedByPriority &&
1541  colPriority_ != INVALID)) // if no priority column, all at same priorty [0]
1542  retVector.push_back(std::vector<unsigned int /*group row*/>());
1543 
1544  __COUTS__(2) << "getGroupRowsInVectors: " << groupID << " at col " << groupIdCol
1545  << __E__;
1546  for(unsigned int r = 0; r < getNumberOfRows(); ++r)
1547  if(groupID == "" || groupID == "*" || groupIdCol == INVALID ||
1548  isEntryInGroupCol(r, groupIdCol, groupID))
1549  {
1550  if(groupIdCol != INVALID)
1551  __COUTS__(2) << "Row " << r << " '" << getDataView()[r][groupIdCol]
1552  << "' is in group " << groupID << __E__;
1553  // check status if needed
1554  if(onlyStatusTrue && colStatus_ != INVALID)
1555  {
1556  getValue(tmpStatus, r, colStatus_);
1557 
1558  if(!tmpStatus)
1559  continue; // skip those with status false
1560  }
1561 
1562  if(orderedByPriority && colPriority_ != INVALID)
1563  {
1564  getValue(tmpPriority, r, colPriority_);
1565  // do not accept DEFAULT value of 0.. convert to 100
1566  mapByPriority[tmpPriority ? tmpPriority : 100].push_back(r);
1567  }
1568  else // assume equal priority
1569  retVector[0].push_back(r);
1570  }
1571  else // already true that... if(groupIdCol != INVALID)
1572  __COUTS__(2) << "Row " << r << " '" << getDataView()[r][groupIdCol]
1573  << "' is NOT in group " << groupID << __E__;
1574 
1575  if(orderedByPriority && colPriority_ != INVALID)
1576  {
1577  // at this point have priority map (which automatically sorts by priority)
1578  // now build return vector
1579  for(const auto& priorityChildRowVector : mapByPriority)
1580  {
1581  retVector.push_back(std::vector<unsigned int /*group row*/>());
1582  for(const auto& priorityChildRow : priorityChildRowVector.second)
1583  retVector[retVector.size() - 1].push_back(priorityChildRow);
1584  }
1585 
1586  __COUT__ << "Returning priority children list." << __E__;
1587  }
1588  // else equal priority vector already constructed
1589 
1590  return retVector;
1591 } // end getGroupRowsInVectors()
1592 
1593 //==============================================================================
1598 bool TableView::removeRowFromGroup(const unsigned int& row,
1599  const unsigned int& col,
1600  const std::string& groupNeedle,
1601  bool deleteRowIfNoGroupLeft)
1602 {
1603  __COUT__ << "removeRowFromGroup groupNeedle " << groupNeedle << __E__;
1604  std::set<std::string> groupIDList;
1605  if(!isEntryInGroupCol(row, col, groupNeedle, &groupIDList))
1606  {
1607  __SS__ << "GroupID (" << groupNeedle << ") removed from row (" << row
1608  << ") was already removed!" << __E__;
1609  print();
1610  __SS_THROW__;
1611  }
1612 
1613  // is in group, so
1614  // create new string based on set of groupids
1615  // but skip groupNeedle
1616 
1617  std::string newValue = "";
1618  unsigned int cnt = 0;
1619  for(const auto& groupID : groupIDList)
1620  {
1621  __COUTT__ << groupID << " " << groupNeedle << " " << newValue << __E__;
1622  if(groupID == groupNeedle)
1623  continue; // skip group to be removed
1624 
1625  if(cnt)
1626  newValue += " | ";
1627  newValue += groupID;
1628  }
1629 
1630  bool wasDeleted = false;
1631  if(deleteRowIfNoGroupLeft && newValue == "")
1632  {
1633  __COUTT__ << "Delete row since it no longer part of any group." << __E__;
1634  deleteRow(row);
1635  wasDeleted = true;
1636  }
1637  else
1638  {
1639  setValue(newValue, row, col);
1640  __COUTT__ << getDataView()[row][col] << __E__;
1641  }
1642 
1643  return wasDeleted;
1644 } // end removeRowFromGroup()
1645 
1646 //==============================================================================
1652 bool TableView::isEntryInGroup(const unsigned int& r,
1653  const std::string& childLinkIndex,
1654  const std::string& groupNeedle) const
1655 {
1656  unsigned int c = getLinkGroupIDColumn(childLinkIndex); // column in question
1657 
1658  return isEntryInGroupCol(r, c, groupNeedle);
1659 } // end isEntryInGroup()
1660 
1661 //==============================================================================
1670 bool TableView::isEntryInGroupCol(const unsigned int& r,
1671  const unsigned int& c,
1672  const std::string& groupNeedle,
1673  std::set<std::string>* groupIDList) const
1674 {
1675  if(r >= getNumberOfRows() || c >= getNumberOfColumns())
1676  {
1677  __SS__ << "Invalid row/col requested!" << __E__;
1678  ss << StringMacros::stackTrace() << __E__;
1679  __SS_THROW__;
1680  }
1681 
1682  unsigned int i = 0;
1683  unsigned int j = 0;
1684  bool found = false;
1685 
1686  __COUTT__ << "groupNeedle " << groupNeedle << __E__;
1687 
1688  // go through the full groupString extracting groups and comparing to groupNeedle
1689  for(; j < theDataView_[r][c].size(); ++j)
1690  if((theDataView_[r][c][j] == ' ' || // ignore leading white space or |
1691  theDataView_[r][c][j] == '|') &&
1692  i == j)
1693  ++i;
1694  else if((theDataView_[r][c][j] ==
1695  ' ' || // trailing white space or | indicates group
1696  theDataView_[r][c][j] == '|') &&
1697  i != j) // assume end of group name
1698  {
1699  if(groupIDList)
1700  groupIDList->emplace(theDataView_[r][c].substr(i, j - i));
1701 
1702  __COUTT__ << "Group found to compare: " << theDataView_[r][c].substr(i, j - i)
1703  << __E__;
1704  if(groupIDList ? groupNeedle == theDataView_[r][c].substr(i, j - i)
1706  theDataView_[r][c].substr(i, j - i), groupNeedle))
1707  {
1708  __COUTT__ << "'" << theDataView_[r][c].substr(i, j - i)
1709  << "' is in group '" << groupNeedle << "'!" << __E__;
1710  if(!groupIDList) // dont return if caller is trying to get group list
1711  return true;
1712  found = true;
1713  }
1714  // if no match, setup i and j for next find
1715  i = j + 1;
1716  }
1717 
1718  if(i != j) // last group check (for case when no ' ' or '|')
1719  {
1720  if(groupIDList)
1721  groupIDList->emplace(theDataView_[r][c].substr(i, j - i));
1722 
1723  __COUTT__ << "Group found to compare: " << theDataView_[r][c].substr(i, j - i)
1724  << __E__;
1725  if(groupIDList ? groupNeedle == theDataView_[r][c].substr(i, j - i)
1726  : StringMacros::wildCardMatch(theDataView_[r][c].substr(i, j - i),
1727  groupNeedle))
1728  {
1729  __COUTT__ << "'" << theDataView_[r][c].substr(i, j - i) << "' is in group '"
1730  << groupNeedle << "'!" << __E__;
1731  return true;
1732  }
1733  }
1734 
1735  return found;
1736 } // end isEntryInGroupCol()
1737 
1738 //==============================================================================
1746 std::set<std::string> TableView::getSetOfGroupIDs(const std::string& childLinkIndex,
1747  unsigned int r) const
1748 {
1749  return getSetOfGroupIDs(getLinkGroupIDColumn(childLinkIndex), r);
1750 }
1751 std::set<std::string> TableView::getSetOfGroupIDs(const unsigned int& c,
1752  unsigned int r) const
1753 {
1754  //__COUT__ << "GroupID col=" << (int)c << __E__;
1755 
1756  std::set<std::string> retSet;
1757 
1758  // unsigned int i = 0;
1759  // unsigned int j = 0;
1760 
1761  if(r != (unsigned int)-1)
1762  {
1763  if(r >= getNumberOfRows())
1764  {
1765  __SS__ << "Invalid row requested!" << __E__;
1766  __SS_THROW__;
1767  }
1768 
1769  StringMacros::getSetFromString(theDataView_[r][c], retSet);
1770  // //go through the full groupString extracting groups
1771  // //add each found groupId to set
1772  // for(;j<theDataView_[r][c].size();++j)
1773  // if((theDataView_[r][c][j] == ' ' || //ignore leading white space or |
1774  // theDataView_[r][c][j] == '|')
1775  // && i == j)
1776  // ++i;
1777  // else if((theDataView_[r][c][j] == ' ' || //trailing white space or |
1778  // indicates group theDataView_[r][c][j] == '|')
1779  // && i != j) // assume end of group name
1780  // {
1781  // //__COUT__ << "Group found: " <<
1782  // // theDataView_[r][c].substr(i,j-i) << __E__;
1783  //
1784  //
1785  // retSet.emplace(theDataView_[r][c].substr(i,j-i));
1786  //
1787  // //setup i and j for next find
1788  // i = j+1;
1789  // }
1790  //
1791  // if(i != j) //last group check (for case when no ' ' or '|')
1792  // retSet.emplace(theDataView_[r][c].substr(i,j-i));
1793  }
1794  else
1795  {
1796  // do all rows
1797  for(r = 0; r < getNumberOfRows(); ++r)
1798  {
1799  StringMacros::getSetFromString(theDataView_[r][c], retSet);
1800 
1801  // i=0;
1802  // j=0;
1803  //
1804  // //__COUT__ << (int)r << ": " << theDataView_[r][c] << __E__;
1805  //
1806  // //go through the full groupString extracting groups
1807  // //add each found groupId to set
1808  // for(;j<theDataView_[r][c].size();++j)
1809  // {
1810  // //__COUT__ << "i:" << i << " j:" << j << __E__;
1811  //
1812  // if((theDataView_[r][c][j] == ' ' || //ignore leading white
1813  // space or | theDataView_[r][c][j] == '|')
1814  // && i == j)
1815  // ++i;
1816  // else if((theDataView_[r][c][j] == ' ' || //trailing white
1817  // space or | indicates group theDataView_[r][c][j]
1818  // ==
1819  // '|')
1820  // && i != j) // assume end of group name
1821  // {
1822  // //__COUT__ << "Group found: " <<
1823  // // theDataView_[r][c].substr(i,j-i) << __E__;
1824  //
1825  // retSet.emplace(theDataView_[r][c].substr(i,j-i));
1826  //
1827  // //setup i and j for next find
1828  // i = j+1;
1829  // }
1830  // }
1831  //
1832  // if(i != j) //last group (for case when no ' ' or '|')
1833  // {
1834  // //__COUT__ << "Group found: " <<
1835  // // theDataView_[r][c].substr(i,j-i) << __E__;
1836  // retSet.emplace(theDataView_[r][c].substr(i,j-i));
1837  // }
1838  }
1839  }
1840 
1841  return retSet;
1842 }
1843 
1844 //==============================================================================
1847 unsigned int TableView::getLinkGroupIDColumn(const std::string& childLinkIndex) const
1848 {
1849  if(!childLinkIndex.size())
1850  {
1851  __SS__ << "Empty childLinkIndex string parameter!" << __E__;
1852  ss << StringMacros::stackTrace() << __E__;
1853  __SS_THROW__;
1854  }
1855 
1856  const char* needleChildLinkIndex = &childLinkIndex[0];
1857 
1858  // allow space syntax to target a childLinkIndex from a different parentLinkIndex
1859  // e.g. "parentLinkIndex childLinkIndex"
1860  size_t spacePos = childLinkIndex.find(' ');
1861  if(spacePos != std::string::npos &&
1862  spacePos + 1 < childLinkIndex.size()) // make sure there are more characters
1863  {
1864  // found space syntax for targeting childLinkIndex
1865  needleChildLinkIndex = &childLinkIndex[spacePos + 1];
1866  }
1867 
1868  std::map<std::string, unsigned int>::const_iterator it =
1869  colLinkGroupIDs_.find(needleChildLinkIndex);
1870  if(it != // if already known, return it
1871  colLinkGroupIDs_.end())
1872  return it->second;
1873 
1874  // otherwise search (perhaps init() was not called)
1875  for(unsigned int col = 0; col < columnsInfo_.size(); ++col)
1876  {
1877  // only check columns with link index associated...
1878  if(columnsInfo_[col].isChildLink() || columnsInfo_[col].isChildLinkUID() ||
1879  columnsInfo_[col].isChildLinkGroupID() || columnsInfo_[col].isGroupID())
1880  {
1881  if(needleChildLinkIndex == columnsInfo_[col].getChildLinkIndex())
1882  return col;
1883  }
1884  }
1885 
1886  __SS__
1887  << "Error! Incompatible table for this group link! Table '" << tableName_
1888  << "' is missing a GroupID column with data type '"
1889  << TableViewColumnInfo::TYPE_START_GROUP_ID << "-" << needleChildLinkIndex
1890  << "'.\n\n"
1891  << "Note: you can separate the child GroupID column data type from "
1892  << "the parent GroupLink column data type; this is accomplished by using a space "
1893  << "character at the parent level - the string after the space will be treated "
1894  "as the "
1895  << "child GroupID column data type." << __E__;
1896  ss << "Existing Column GroupIDs: " << __E__;
1897  for(auto& groupIdColPair : colLinkGroupIDs_)
1898  ss << "\t" << groupIdColPair.first << " : col-" << groupIdColPair.second << __E__;
1899 
1900  ss << "Existing Column Types: " << __E__;
1901  for(unsigned int col = 0; col < columnsInfo_.size(); ++col)
1902  ss << "\t" << columnsInfo_[col].getType() << "() " << columnsInfo_[col].getName()
1903  << __E__;
1904 
1905  ss << StringMacros::stackTrace() << __E__;
1906 
1907  __SS_THROW__;
1908 } // end getLinkGroupIDColumn()
1909 
1910 //==============================================================================
1911 unsigned int TableView::findRow(unsigned int col,
1912  const std::string& value,
1913  unsigned int offsetRow,
1914  bool doNotThrow /*= false*/) const
1915 {
1916  for(unsigned int row = offsetRow; row < theDataView_.size(); ++row)
1917  {
1918  if(theDataView_[row][col] == value)
1919  return row;
1920  }
1921  if(doNotThrow)
1922  return TableView::INVALID;
1923 
1924  __SS__ << "\tIn view: " << tableName_ << ", Can't find value=" << value
1925  << " in column named " << columnsInfo_[col].getName()
1926  << " with type=" << columnsInfo_[col].getType() << __E__ << __E__
1927  << StringMacros::stackTrace() << __E__;
1928 
1929  // Note: findRow gets purposely called by configuration GUI a lot looking for
1930  // exceptions so may not want to print out
1931  //__COUT__ << "\n" << ss.str();
1932  __SS_ONLY_THROW__;
1933 } // end findRow()
1934 
1935 //==============================================================================
1936 unsigned int TableView::findRowInGroup(unsigned int col,
1937  const std::string& value,
1938  const std::string& groupId,
1939  const std::string& childLinkIndex,
1940  unsigned int offsetRow) const
1941 {
1942  unsigned int groupIdCol = getLinkGroupIDColumn(childLinkIndex);
1943  for(unsigned int row = offsetRow; row < theDataView_.size(); ++row)
1944  {
1945  if(theDataView_[row][col] == value && isEntryInGroupCol(row, groupIdCol, groupId))
1946  return row;
1947  }
1948 
1949  __SS__ << "\tIn view: " << tableName_ << ", Can't find in group the value=" << value
1950  << " in column named '" << columnsInfo_[col].getName()
1951  << "' with type=" << columnsInfo_[col].getType() << " and GroupID: '"
1952  << groupId << "' in column '" << groupIdCol
1953  << "' with GroupID child link index '" << childLinkIndex << "'" << __E__;
1954  // Note: findRowInGroup gets purposely called by configuration GUI a lot looking for
1955  // exceptions so may not want to print out
1956  __SS_ONLY_THROW__;
1957 } // end findRowInGroup()
1958 
1959 //==============================================================================
1962 unsigned int TableView::findCol(const std::string& wildCardName) const
1963 {
1964  for(unsigned int col = 0; col < columnsInfo_.size(); ++col)
1965  if(StringMacros::wildCardMatch(wildCardName /*needle*/,
1966  columnsInfo_[col].getName() /*haystack*/))
1967  return col;
1968 
1969  __SS__ << "\tIn view: " << tableName_ << ", Can't find column named '" << wildCardName
1970  << "'" << __E__;
1971  ss << "Existing columns:\n";
1972  for(unsigned int col = 0; col < columnsInfo_.size(); ++col)
1973  ss << "\t" << columnsInfo_[col].getName() << "\n";
1974 
1975  ss << StringMacros::stackTrace() << __E__;
1976 
1977  // Note: findCol gets purposely called by configuration GUI a lot looking for
1978  // exceptions so may not want to print out
1979  __SS_ONLY_THROW__;
1980 } // end findCol()
1981 
1982 //==============================================================================
1985 unsigned int TableView::findColByType(const std::string& type,
1986  unsigned int startingCol) const
1987 {
1988  for(unsigned int col = startingCol; col < columnsInfo_.size(); ++col)
1989  {
1990  __COUTS__(40) << columnsInfo_[col].getType() << __E__;
1991  if(columnsInfo_[col].getType() == type)
1992  return col;
1993  }
1994 
1995  return INVALID;
1996 } // end findColByType()
1997 
2000 //==============================================================================
2002 unsigned int TableView::getDataColumnSize(void) const
2003 {
2004  // if no data, give benefit of the doubt that phantom data has mockup column size
2005  if(!getNumberOfRows())
2006  return getNumberOfColumns();
2007  return theDataView_[0].size(); // number of columns in first row of data
2008 }
2009 
2010 //==============================================================================
2011 std::set<std::string> TableView::getColumnNames(void) const
2012 {
2013  std::set<std::string> retSet;
2014  for(auto& colInfo : columnsInfo_)
2015  retSet.emplace(colInfo.getName());
2016  return retSet;
2017 } // end getColumnNames()
2018 
2019 //==============================================================================
2020 std::map<std::string, unsigned int /*col*/> TableView::getColumnNamesMap(void) const
2021 {
2022  std::map<std::string, unsigned int /*col*/> retMap;
2023  unsigned int c = 0;
2024  for(auto& colInfo : columnsInfo_)
2025  retMap.emplace(std::make_pair(colInfo.getName(), c++));
2026  return retMap;
2027 } // end getColumnNamesMap()
2028 
2029 //==============================================================================
2030 std::set<std::string> TableView::getColumnStorageNames(void) const
2031 {
2032  std::set<std::string> retSet;
2033  for(auto& colInfo : columnsInfo_)
2034  retSet.emplace(colInfo.getStorageName());
2035  return retSet;
2036 }
2037 
2038 //==============================================================================
2039 const std::vector<std::string>& TableView::initRowDefaults(void)
2040 {
2041  std::vector<std::string>& retVec = rowDefaultValues_;
2042  retVec.clear();
2043 
2044  // fill each col of new row with default values
2045  for(unsigned int col = 0; col < getNumberOfColumns(); ++col)
2046  {
2047  // if this is a fixed choice Link, and NO_LINK is not in list,
2048  // take first in list to avoid creating illegal rows.
2049  // NOTE: this is not a problem for standard fixed choice fields
2050  // because the default value is always required.
2051 
2052  if(columnsInfo_[col].isChildLink())
2053  {
2054  const std::vector<std::string>& theDataChoices =
2055  columnsInfo_[col].getDataChoices();
2056 
2057  // check if arbitrary values allowed
2058  if(!theDataChoices.size() || // if so, use default
2059  theDataChoices[0] == "arbitraryBool=1")
2060  retVec.push_back(columnsInfo_[col].getDefaultValue());
2061  else
2062  {
2063  bool skipOne =
2064  (theDataChoices.size() && theDataChoices[0] == "arbitraryBool=0");
2065  bool hasSkipped;
2066 
2067  // look for default value in list
2068 
2069  bool foundDefault = false;
2070  hasSkipped = false;
2071  for(const auto& choice : theDataChoices)
2072  if(skipOne && !hasSkipped)
2073  {
2074  hasSkipped = true;
2075  continue;
2076  }
2077  else if(choice == columnsInfo_[col].getDefaultValue())
2078  {
2079  foundDefault = true;
2080  break;
2081  }
2082 
2083  // use first choice if possible
2084  if(!foundDefault && theDataChoices.size() > (skipOne ? 1 : 0))
2085  retVec.push_back(theDataChoices[(skipOne ? 1 : 0)]);
2086  else // else stick with default
2087  retVec.push_back(columnsInfo_[col].getDefaultValue());
2088  }
2089  }
2090  else
2091  retVec.push_back(columnsInfo_[col].getDefaultValue());
2092  }
2093 
2094  //__COUT__ << StringMacros::stackTrace() << __E__;
2095  //__COUTV__(StringMacros::vectorToString(rowDefaultValues_));
2096  return rowDefaultValues_;
2097 } // end getDefaultRowValues()
2098 
2099 //==============================================================================
2100 const TableViewColumnInfo& TableView::getColumnInfo(unsigned int column) const
2101 {
2102  if(column >= columnsInfo_.size())
2103  {
2104  __SS__ << "\nCan't find column " << column
2105  << "\n\n\n\nThe column info is likely missing due to incomplete "
2106  "Configuration View filling.\n\n"
2107  << __E__;
2108  ss << StringMacros::stackTrace() << __E__;
2109  __SS_THROW__;
2110  }
2111  return columnsInfo_[column];
2112 } // end getColumnInfo()
2113 
2116 //==============================================================================
2117 void TableView::setURIEncodedComment(const std::string& uriComment)
2118 {
2119  comment_ = StringMacros::decodeURIComponent(uriComment);
2120 }
2121 
2122 //==============================================================================
2123 void TableView::setAuthor(const std::string& author) { author_ = author; }
2124 
2125 //==============================================================================
2126 void TableView::setCreationTime(time_t t) { creationTime_ = t; }
2127 
2128 //==============================================================================
2129 void TableView::setLastAccessTime(time_t t) { lastAccessTime_ = t; }
2130 
2131 //==============================================================================
2132 void TableView::setLooseColumnMatching(bool setValue)
2133 {
2134  fillWithLooseColumnMatching_ = setValue;
2135 }
2136 
2137 //==============================================================================
2138 void TableView::doGetSourceRawData(bool setValue) { getSourceRawData_ = setValue; }
2139 
2140 //==============================================================================
2141 void TableView::reset(void)
2142 {
2143  version_ = -1;
2144  comment_ = "";
2145  author_ = "";
2146  columnsInfo_.clear();
2147  theDataView_.clear();
2148 } // end reset()
2149 
2150 //==============================================================================
2151 void TableView::print(std::ostream& out /* = std::cout */) const
2152 {
2153  out << "============================================================================="
2154  "="
2155  << __E__;
2156  out << "Print: " << tableName_ << " Version: " << version_ << " Comment: " << comment_
2157  << " Author: " << author_ << " Creation Time: " << ctime(&creationTime_) << __E__;
2158  out << "\t\tNumber of Cols " << getNumberOfColumns() << __E__;
2159  out << "\t\tNumber of Rows " << getNumberOfRows() << __E__;
2160 
2161  out << "Columns:\t";
2162  for(int i = 0; i < (int)columnsInfo_.size(); ++i)
2163  out << i << ":" << columnsInfo_[i].getName() << ":"
2164  << columnsInfo_[i].getStorageName() << ":" << columnsInfo_[i].getType() << ":"
2165  << columnsInfo_[i].getDataType() << "\t ";
2166  out << __E__;
2167 
2168  out << "Rows:" << __E__;
2169  // int num;
2170  std::string val;
2171  for(int r = 0; r < (int)getNumberOfRows(); ++r)
2172  {
2173  out << (int)r << ":\t";
2174  for(int c = 0; c < (int)getNumberOfColumns(); ++c)
2175  {
2176  out << (int)c << ":";
2177 
2178  // if fixed choice type, print index in choice
2179  if(columnsInfo_[c].getType() == TableViewColumnInfo::TYPE_FIXED_CHOICE_DATA)
2180  {
2181  int choiceIndex = -1;
2182  std::vector<std::string> choices = columnsInfo_[c].getDataChoices();
2183  val = StringMacros::convertEnvironmentVariables(theDataView_[r][c]);
2184 
2185  if(val == columnsInfo_[c].getDefaultValue())
2186  choiceIndex = 0;
2187  else
2188  {
2189  for(int i = 0; i < (int)choices.size(); ++i)
2190  if(val == choices[i])
2191  choiceIndex = i + 1;
2192  }
2193 
2194  out << "ChoiceIndex=" << choiceIndex << ":";
2195  }
2196 
2197  out << theDataView_[r][c];
2198  // stopped using below, because it is called sometimes during debugging when
2199  // numbers are set to environment variables:
2200  // if(columnsInfo_[c].getDataType() == "NUMBER")
2201  // {
2202  // getValue(num,r,c,false);
2203  // out << num;
2204  // }
2205  // else
2206  // {
2207  // getValue(val,r,c,false);
2208  // out << val;
2209  // }
2210  out << "\t\t";
2211  }
2212  out << __E__;
2213  }
2214 } // end print()
2215 
2216 //==============================================================================
2217 void TableView::printJSON(std::ostream& out /* = std::cout */) const
2218 {
2219  { //handle special GROUP CACHE table
2220  std::string tmpCachePrepend = TableBase::GROUP_CACHE_PREPEND;
2221  tmpCachePrepend = TableBase::convertToCaps(tmpCachePrepend);
2222  std::string tmpJsonDocPrepend = TableBase::JSON_DOC_PREPEND;
2223  tmpJsonDocPrepend = TableBase::convertToCaps(tmpJsonDocPrepend);
2224  __COUTS__(32) << " '" << tableName_ << "' vs " << tmpCachePrepend << " or "
2225  << tmpJsonDocPrepend << __E__;
2226  //if special GROUP CACHE table, handle construction in a special way
2227  if(tableName_.substr(0, tmpCachePrepend.length()) == tmpCachePrepend ||
2228  tableName_.substr(0, tmpJsonDocPrepend.length()) == tmpJsonDocPrepend)
2229  {
2230  out << getCustomStorageData();
2231  return;
2232  } //end special GROUP CACHE table construction
2233  } //end handle special GROUP CACHE table
2234 
2235  out << "{\n";
2236  out << "\"NAME\" : \"" << tableName_ << "\",\n";
2237 
2238  // out << "\"VERSION\": \"" << version_ << "\",\n";
2239 
2240  out << "\"COMMENT\" : ";
2241 
2242  // output escaped comment
2243  std::string val;
2244  val = comment_;
2245  out << "\"";
2246  for(unsigned int i = 0; i < val.size(); ++i)
2247  {
2248  if(val[i] == '\n')
2249  out << "\\n";
2250  else if(val[i] == '\t')
2251  out << "\\t";
2252  else if(val[i] == '\r')
2253  out << "\\r";
2254  else
2255  {
2256  // escaped characters need a
2257  if(val[i] == '"' || val[i] == '\\')
2258  out << '\\';
2259  out << val[i];
2260  }
2261  }
2262  out << "\",\n";
2263 
2264  out << "\"AUTHOR\" : \"" << author_ << "\",\n";
2265  out << "\"CREATION_TIME\" : " << creationTime_ << ",\n";
2266 
2267  // USELESS... out << "\"NUM_OF_COLS\" : " << getNumberOfColumns() << ",\n";
2268  // USELESS... out << "\"NUM_OF_ROWS\" : " << getNumberOfRows() << ",\n";
2269 
2270  out << "\"COL_TYPES\" : {\n";
2271  for(int c = 0; c < (int)getNumberOfColumns(); ++c)
2272  {
2273  out << "\t\t\"" << columnsInfo_[c].getStorageName() << "\" : ";
2274  out << "\"" << columnsInfo_[c].getDataType() << "\"";
2275  if(c + 1 < (int)getNumberOfColumns())
2276  out << ",";
2277  out << "\n";
2278  }
2279  out << "},\n"; // close COL_TYPES
2280 
2281  out << "\"DATA_SET\" : [\n";
2282  // int num;
2283  for(int r = 0; r < (int)getNumberOfRows(); ++r)
2284  {
2285  out << "\t{\n";
2286  for(int c = 0; c < (int)getNumberOfColumns(); ++c)
2287  {
2288  out << "\t\t\"" << columnsInfo_[c].getStorageName() << "\" : ";
2289 
2290  out << "\"" << getEscapedValueAsString(r, c, false)
2291  << "\""; // do not convert env variables
2292 
2293  if(c + 1 < (int)getNumberOfColumns())
2294  out << ",";
2295  out << "\n";
2296  }
2297  out << "\t}";
2298  if(r + 1 < (int)getNumberOfRows())
2299  out << ",";
2300  out << "\n";
2301  }
2302  out << "]\n"; // close DATA_SET
2303 
2304  out << "}";
2305 } // end printJSON()
2306 
2307 //==============================================================================
2308 void TableView::printCSV(std::ostream& out /* = std::cout */,
2309  const std::string& valueDelimeter /* = "," */,
2310  const std::string& recordDelimeter /* = "\n" */,
2311  bool includeColumnNames /* = false */) const
2312 {
2313  { //handle special GROUP CACHE table
2314  std::string tmpCachePrepend = TableBase::GROUP_CACHE_PREPEND;
2315  tmpCachePrepend = TableBase::convertToCaps(tmpCachePrepend);
2316  std::string tmpJsonDocPrepend = TableBase::JSON_DOC_PREPEND;
2317  tmpJsonDocPrepend = TableBase::convertToCaps(tmpJsonDocPrepend);
2318  __COUTS__(32) << " '" << tableName_ << "' vs " << tmpCachePrepend << " or "
2319  << tmpJsonDocPrepend << __E__;
2320  //if special GROUP CACHE table, handle construction in a special way
2321  if(tableName_.substr(0, tmpCachePrepend.length()) == tmpCachePrepend ||
2322  tableName_.substr(0, tmpJsonDocPrepend.length()) == tmpJsonDocPrepend)
2323  {
2324  __SS__ << "Cannot convert custom storage data to CSV!" << __E__;
2325  __SS_THROW__;
2326  // out << getCustomStorageData();
2327  // return;
2328  } //end special GROUP CACHE table construction
2329  } //end handle special GROUP CACHE table
2330 
2331  for(int c = 0; includeColumnNames && c < (int)getNumberOfColumns(); ++c)
2332  {
2333  if(c)
2334  out << valueDelimeter;
2335  out << "\"" << columnsInfo_[c].getStorageName() << "\"";
2336  }
2337  if(includeColumnNames)
2338  out << recordDelimeter;
2339 
2340  for(int r = 0; r < (int)getNumberOfRows(); ++r)
2341  {
2342  for(int c = 0; c < (int)getNumberOfColumns(); ++c)
2343  {
2344  if(c)
2345  out << valueDelimeter;
2346  out << "\""
2347  << getEscapedValueAsString(r, c, false, true /* quotesToDoubleQuotes*/)
2348  << "\""; // do not convert env variables, convert " to "" for excel style
2349  }
2350  out << recordDelimeter;
2351  }
2352 
2353 } // end printCSV()
2354 
2355 //==============================================================================
2363 int TableView::fillFromJSON(const std::string& json)
2364 {
2365  { //handle special GROUP CACHE table
2366  std::string tmpCachePrepend = TableBase::GROUP_CACHE_PREPEND;
2367  tmpCachePrepend = TableBase::convertToCaps(tmpCachePrepend);
2368  std::string tmpJsonDocPrepend = TableBase::JSON_DOC_PREPEND;
2369  tmpJsonDocPrepend = TableBase::convertToCaps(tmpJsonDocPrepend);
2370 
2371  //if special JSON DOC table, handle construction in a special way
2372  if(tableName_.substr(0, tmpJsonDocPrepend.length()) == tmpJsonDocPrepend ||
2373  tableName_.substr(0, tmpCachePrepend.length()) == tmpCachePrepend)
2374  {
2375  __COUTS__(3) << "Special JSON doc: " << json << __E__;
2376  setCustomStorageData(json);
2377  return 0; //success
2378  } //end special JSON DOC table construction or special GROUP CACHE table construction
2379  } //end handle special GROUP CACHE table
2380 
2381  bool dbg = false; //tableName_ == "TABLE_GROUP_METADATA";
2382  bool rawData = getSourceRawData_;
2383  if(getSourceRawData_)
2384  { // only get source raw data once, then revert member variable
2385  __COUTV__(getSourceRawData_);
2386  getSourceRawData_ = false;
2387  sourceRawData_ = ""; // clear for this fill
2388  }
2389 
2390  std::map<std::string /*key*/, unsigned int /*entries/rows*/> keyEntryCountMap;
2391  std::vector<std::string> keys;
2392  keys.push_back("NAME");
2393  keys.push_back("COMMENT");
2394  keys.push_back("AUTHOR");
2395  keys.push_back("CREATION_TIME");
2396  // keys.push_back ("COL_TYPES");
2397  keys.push_back("DATA_SET");
2398  enum
2399  {
2400  CV_JSON_FILL_NAME,
2401  CV_JSON_FILL_COMMENT,
2402  CV_JSON_FILL_AUTHOR,
2403  CV_JSON_FILL_CREATION_TIME,
2404  // CV_JSON_FILL_COL_TYPES,
2405  CV_JSON_FILL_DATA_SET
2406  };
2407 
2408  if(dbg)
2409  {
2410  __COUTV__(tableName_);
2411  __COUTTV__(getNumberOfRows());
2412  __COUTV__(json);
2413  }
2414 
2415  sourceColumnMismatchCount_ = 0;
2416  sourceColumnMissingCount_ = 0;
2417  sourceColumnNames_.clear(); // reset
2418  unsigned int colFoundCount = 0;
2419  unsigned int i = 0;
2420  unsigned int row = -1;
2421  unsigned int colSpeedup = 0;
2422  unsigned int startString, startNumber = 0, endNumber = -1;
2423  unsigned int bracketCount = 0;
2424  unsigned int sqBracketCount = 0;
2425  bool inQuotes = 0;
2426  bool newString = 0;
2427  bool newValue = 0;
2428  // bool isDataArray = 0;
2429  bool keyIsMatch, keyIsComment;
2430  unsigned int keyIsMatchIndex, keyIsMatchStorageIndex, keyIsMatchCommentIndex;
2431  const std::string COMMENT_ALT_KEY = "COMMENT";
2432 
2433  std::string extractedString = "", currKey = "", currVal = "";
2434  unsigned int currDepth = 0;
2435 
2436  std::vector<std::string> jsonPath;
2437  std::vector<char> jsonPathType; // indicator of type in jsonPath: { [ K
2438  char lastPopType = '_'; // either: _ { [ K
2439  // _ indicates reset pop (this happens when a new {obj} starts)
2440  unsigned int matchedKey = -1;
2441  unsigned int lastCol = -1;
2442 
2443  // find all depth 1 matching keys
2444  for(; i < json.size(); ++i)
2445  {
2446  switch(json[i])
2447  {
2448  case '"':
2449  if(i - 1 < json.size() && // ignore if escaped
2450  json[i - 1] == '\\')
2451  break;
2452 
2453  inQuotes = !inQuotes; // toggle in quotes if not escaped
2454  if(inQuotes)
2455  startString = i;
2456  else
2457  {
2458  extractedString = StringMacros::restoreJSONStringEntities(
2459  json.substr(startString + 1, i - startString - 1));
2460  newString = 1; // have new string!
2461  }
2462  break;
2463  case ':':
2464  if(inQuotes)
2465  break; // skip if in quote
2466 
2467  // must be a json object level to have a key
2468  if(jsonPathType[jsonPathType.size() - 1] != '{' ||
2469  !newString) // and must have a string for key
2470  {
2471  __COUT__ << "Invalid ':' position" << __E__;
2472  return -1;
2473  }
2474 
2475  // valid, so take key
2476  jsonPathType.push_back('K');
2477  jsonPath.push_back(extractedString);
2478  startNumber = i;
2479  newString = 0; // clear flag
2480  endNumber = -1; // reset end number index
2481  break;
2482 
2483  // if(isKey ||
2484  // isDataArray)
2485  // {
2486  // std::cout << "Invalid ':' position" << __E__;
2487  // return -1;
2488  // }
2489  // isKey = 1; //new value is a key
2490  // newValue = 1;
2491  // startNumber = i;
2492  // break;
2493  case ',':
2494  if(inQuotes)
2495  break; // skip if in quote
2496  if(lastPopType == '{') // don't need value again of nested object
2497  {
2498  // check if the nested object was the value to a key, if so, pop key
2499  if(jsonPathType[jsonPathType.size() - 1] == 'K')
2500  {
2501  lastPopType = 'K';
2502  jsonPath.pop_back();
2503  jsonPathType.pop_back();
2504  }
2505  break; // skip , handling if {obj} just ended
2506  }
2507 
2508  if(newString)
2509  currVal = extractedString;
2510  else // number value
2511  {
2512  if(endNumber == (unsigned int)-1 || // take i as end number if needed
2513  endNumber <= startNumber)
2514  endNumber = i;
2515  // extract number value
2516  if(endNumber <= startNumber) // empty data, could be {}
2517  currVal = "";
2518  else
2519  currVal = json.substr(startNumber + 1, endNumber - startNumber - 1);
2520  }
2521 
2522  currDepth = bracketCount;
2523 
2524  if(jsonPathType[jsonPathType.size() - 1] == 'K') // this is the value to key
2525  {
2526  currKey = jsonPath[jsonPathType.size() - 1];
2527  newValue = 1; // new value to consider!
2528 
2529  // pop key
2530  lastPopType = 'K';
2531  jsonPath.pop_back();
2532  jsonPathType.pop_back();
2533  }
2534  else if(jsonPathType[jsonPathType.size() - 1] ==
2535  '[') // this is a value in array
2536  {
2537  // key is last key
2538  for(unsigned int k = jsonPathType.size() - 2; k < jsonPathType.size();
2539  --k)
2540  if(jsonPathType[k] == 'K')
2541  {
2542  currKey = jsonPath[k];
2543  break;
2544  }
2545  else if(k == 0)
2546  {
2547  __COUT__ << "Invalid array position" << __E__;
2548  return -1;
2549  }
2550 
2551  newValue = 1; // new value to consider!
2552  // isDataArray = 1;
2553  }
2554  else // { is an error
2555  {
2556  __COUT__ << "Invalid ',' position" << __E__;
2557  return -1;
2558  }
2559 
2560  startNumber = i;
2561  break;
2562 
2563  case '{':
2564  if(inQuotes)
2565  break; // skip if in quote
2566  lastPopType = '_'; // reset because of new object
2567  jsonPathType.push_back('{');
2568  jsonPath.push_back("{");
2569  ++bracketCount;
2570  break;
2571 
2572  // ++bracketCount;
2573  // isDataArray = 0;
2574  // isKey = 0;
2575  // endingObject = 0;
2576  // break;
2577  case '}':
2578  if(inQuotes)
2579  break; // skip if in quote
2580 
2581  if(lastPopType != '{' && // don't need value again of nested object
2582  jsonPathType[jsonPathType.size() - 1] == 'K') // this is the value to key
2583  {
2584  currDepth = bracketCount;
2585  currKey = jsonPath[jsonPathType.size() - 1];
2586  if(newString)
2587  currVal = extractedString;
2588  else // number value
2589  {
2590  if(endNumber == (unsigned int)-1 || // take i as end number if needed
2591  endNumber <= startNumber)
2592  endNumber = i;
2593  // extract val
2594  if(endNumber <= startNumber) // empty data, could be {}
2595  currVal = "";
2596  else
2597  currVal =
2598  json.substr(startNumber + 1, endNumber - startNumber - 1);
2599  }
2600  newValue = 1; // new value to consider!
2601  // pop key
2602  jsonPath.pop_back();
2603  jsonPathType.pop_back();
2604  }
2605  // pop {
2606  if(jsonPathType[jsonPathType.size() - 1] != '{')
2607  {
2608  __COUT__ << "Invalid '}' position" << __E__;
2609  return -1;
2610  }
2611  lastPopType = '{';
2612  jsonPath.pop_back();
2613  jsonPathType.pop_back();
2614  --bracketCount;
2615  break;
2616  case '[':
2617  if(inQuotes)
2618  break; // skip if in quote
2619  jsonPathType.push_back('[');
2620  jsonPath.push_back("[");
2621  ++sqBracketCount;
2622  startNumber = i;
2623  break;
2624  case ']':
2625  if(inQuotes)
2626  break; // skip if in quote
2627 
2628  // must be an array at this level (in order to close it)
2629  if(jsonPathType[jsonPathType.size() - 1] != '[')
2630  {
2631  __COUT__ << "Invalid ']' position" << __E__;
2632  return -1;
2633  }
2634 
2635  currDepth = bracketCount;
2636 
2637  // This is an array value
2638  if(newString)
2639  currVal = extractedString;
2640  else // number value
2641  {
2642  if(endNumber == (unsigned int)-1 || // take i as end number if needed
2643  endNumber <= startNumber)
2644  endNumber = i;
2645  // extract val
2646  if(endNumber <= startNumber) // empty data, could be {}
2647  currVal = "";
2648  else
2649  currVal = json.substr(startNumber + 1, endNumber - startNumber - 1);
2650  }
2651  // isDataArray = 1;
2652 
2653  // key is last key
2654  for(unsigned int k = jsonPathType.size() - 2; k < jsonPathType.size(); --k)
2655  if(jsonPathType[k] == 'K')
2656  {
2657  currKey = jsonPath[k];
2658  break;
2659  }
2660  else if(k == 0)
2661  {
2662  __COUT__ << "Invalid array position" << __E__;
2663  return -1;
2664  }
2665 
2666  // pop [
2667  if(jsonPathType[jsonPathType.size() - 1] != '[')
2668  {
2669  __COUT__ << "Invalid ']' position" << __E__;
2670  return -1;
2671  }
2672  lastPopType = '[';
2673  jsonPath.pop_back();
2674  jsonPathType.pop_back();
2675  --sqBracketCount;
2676  break;
2677  case ' ': // white space handling for numbers
2678  case '\t':
2679  case '\n':
2680  case '\r':
2681  if(inQuotes)
2682  break; // skip if in quote
2683  if(startNumber != (unsigned int)-1 && endNumber == (unsigned int)-1)
2684  endNumber = i;
2685  startNumber = i;
2686  break;
2687  default:;
2688  }
2689 
2690  // continue;
2691 
2692  // handle a new completed value
2693  if(newValue)
2694  {
2695  if(dbg) // for debugging
2696  {
2697  std::cout << i << ":\t" << json[i] << " - ";
2698 
2699  // if(isDataArray)
2700  // std::cout << "Array:: ";
2701  // if(newString)
2702  // std::cout << "New String:: ";
2703  // else
2704  // std::cout << "New Number:: ";
2705  //
2706 
2707  std::cout << "ExtKey=";
2708  for(unsigned int k = 0; k < jsonPath.size(); ++k)
2709  std::cout << jsonPath[k] << "/";
2710  std::cout << " - ";
2711  std::cout << lastPopType << " ";
2712  std::cout << bracketCount << " ";
2713  std::cout << sqBracketCount << " ";
2714  std::cout << inQuotes << " ";
2715  std::cout << newValue << "-";
2716  std::cout << currKey << "-{" << currDepth << "}:";
2717  std::cout << currVal << " ";
2718  std::cout << startNumber << "-";
2719  std::cout << endNumber << " ";
2720  std::cout << "\n";
2721  __COUTTV__(fillWithLooseColumnMatching_);
2722  __COUTTV__(getNumberOfRows());
2723  }
2724 
2725  // extract only what we care about
2726  // for TableView only care about matching depth 1
2727 
2728  // handle matching depth 1 keys
2729 
2730  matchedKey = -1; // init to unfound
2731  for(unsigned int k = 0; k < keys.size(); ++k)
2732  if((currDepth == 1 && keys[k] == currKey) ||
2733  (currDepth > 1 && keys[k] == jsonPath[1]))
2734  matchedKey = k;
2735 
2736  if(rawData)
2737  {
2738  // raw data handling fills raw data string with row/col values
2739 
2740  if(currDepth == 1)
2741  {
2742  if(matchedKey == CV_JSON_FILL_COMMENT)
2743  setComment(currVal);
2744  else if(matchedKey == CV_JSON_FILL_AUTHOR)
2745  setAuthor(currVal);
2746  else if(matchedKey == CV_JSON_FILL_CREATION_TIME)
2747  setCreationTime(strtol(currVal.c_str(), 0, 10));
2748  }
2749  else if(currDepth == 2)
2750  {
2751  // encode URI component so commas are surviving delimiter
2752  sourceRawData_ += StringMacros::encodeURIComponent(currKey) + "," +
2753  StringMacros::encodeURIComponent(currVal) + ",";
2754  sourceColumnNames_.emplace(currKey);
2755  }
2756  }
2757  else if(matchedKey != (unsigned int)-1)
2758  {
2759  if(dbg)
2760  __COUTT__ << "New Data for:: key[" << matchedKey << "]-"
2761  << keys[matchedKey] << "\n";
2762 
2763  switch(matchedKey)
2764  {
2765  case CV_JSON_FILL_NAME:
2766  // table name is now constant, set by parent TableBase
2767  if(currDepth == 1)
2768  {
2769  // setTableName(currVal);
2770  // check for consistency, and show warning
2771  if(currVal != getTableName() &&
2772  getTableName() !=
2773  "TABLE_GROUP_METADATA") // allow metadata table to be illegal, since it is created by ConfigurationManager.cc
2774  __COUT_WARN__ << "JSON-fill Table name mismatch: " << currVal
2775  << " vs " << getTableName() << __E__;
2776  }
2777  break;
2778  case CV_JSON_FILL_COMMENT:
2779  if(currDepth == 1)
2780  setComment(currVal);
2781  break;
2782  case CV_JSON_FILL_AUTHOR:
2783  if(currDepth == 1)
2784  setAuthor(currVal);
2785  break;
2786  case CV_JSON_FILL_CREATION_TIME:
2787  if(currDepth == 1)
2788  setCreationTime(strtol(currVal.c_str(), 0, 10));
2789  break;
2790  // case CV_JSON_FILL_COL_TYPES:
2791  //
2792  // break;
2793  case CV_JSON_FILL_DATA_SET:
2794  if(dbg)
2795  __COUTT__ << "CV_JSON_FILL_DATA_SET New Data for::" << matchedKey
2796  << "]-" << keys[matchedKey] << "/" << currDepth
2797  << ".../" << currKey << "\n";
2798 
2799  if(currDepth == 2) // second level depth
2800  {
2801  // if matches first column name.. then add new row
2802  // else add to current row
2803  unsigned int col, ccnt = 0;
2804  unsigned int noc = getNumberOfColumns();
2805  for(; ccnt < noc; ++ccnt)
2806  {
2807  // use colSpeedup to change the first column we search
2808  // for each iteration.. since we expect the data to
2809  // be arranged in column order
2810 
2811  if(fillWithLooseColumnMatching_)
2812  {
2813  // loose column matching makes no attempt to
2814  // match the column names
2815  // just assumes the data is in the correct order
2816 
2817  col = colSpeedup;
2818 
2819  // auto matched
2820  if(col <= lastCol) // add row (use lastCol in case new
2821  // column-0 was added
2822  row = addRow();
2823  lastCol = col;
2824  if(getNumberOfRows() == 1) // only for first row
2825  sourceColumnNames_.emplace(currKey);
2826 
2827  // add value to row and column
2828 
2829  if(row >= getNumberOfRows())
2830  {
2831  __SS__ << "Invalid row"
2832  << __E__; // should be impossible?
2833  std::cout << ss.str();
2834  __SS_THROW__;
2835  return -1;
2836  }
2837 
2838  theDataView_[row][col] =
2839  currVal; // THERE IS NO CHECK FOR WHAT IS READ FROM
2840  // THE DATABASE. IT SHOULD BE ALREADY
2841  // CONSISTENT
2842  break;
2843  }
2844  else
2845  {
2846  col = (ccnt + colSpeedup) % noc;
2847 
2848  // match key by ignoring '_'
2849  // also accept COMMENT == COMMENT_DESCRIPTION
2850  // (this is for backwards compatibility..)
2851  keyIsMatch = true;
2852  keyIsComment = true;
2853  for(keyIsMatchIndex = 0,
2854  keyIsMatchStorageIndex = 0,
2855  keyIsMatchCommentIndex = 0;
2856  keyIsMatchIndex < currKey.size();
2857  ++keyIsMatchIndex)
2858  {
2859  if(columnsInfo_[col]
2860  .getStorageName()[keyIsMatchStorageIndex] ==
2861  '_')
2862  ++keyIsMatchStorageIndex; // skip to next storage
2863  // character
2864  if(currKey[keyIsMatchIndex] == '_')
2865  continue; // skip to next character
2866 
2867  // match to storage name
2868  if(keyIsMatchStorageIndex >=
2869  columnsInfo_[col].getStorageName().size() ||
2870  currKey[keyIsMatchIndex] !=
2871  columnsInfo_[col]
2872  .getStorageName()[keyIsMatchStorageIndex])
2873  {
2874  // size mismatch or character mismatch
2875  keyIsMatch = false;
2876  if(!keyIsComment)
2877  break;
2878  }
2879 
2880  // check also if alternate comment is matched
2881  if(keyIsComment &&
2882  keyIsMatchCommentIndex < COMMENT_ALT_KEY.size())
2883  {
2884  if(currKey[keyIsMatchIndex] !=
2885  COMMENT_ALT_KEY[keyIsMatchCommentIndex])
2886  {
2887  // character mismatch with COMMENT
2888  keyIsComment = false;
2889  }
2890  }
2891 
2892  ++keyIsMatchStorageIndex; // go to next character
2893  }
2894 
2895  if(dbg)
2896  {
2897  __COUTTV__(keyIsMatch);
2898  __COUTTV__(keyIsComment);
2899  __COUTTV__(currKey);
2900  __COUTTV__(columnsInfo_[col].getStorageName());
2901  __COUTTV__(getNumberOfRows());
2902  }
2903 
2904  if(keyIsMatch || keyIsComment) // currKey ==
2905  // columnsInfo_[c].getStorageName())
2906  {
2907  if(keyEntryCountMap.find(currKey) ==
2908  keyEntryCountMap.end())
2909  keyEntryCountMap[currKey] =
2910  0; // show follow row count
2911  else
2912  ++keyEntryCountMap.at(currKey);
2913 
2914  // add row (based on entry counts)
2915  if(keyEntryCountMap.size() == 1 ||
2916  (keyEntryCountMap.at(currKey) &&
2917  keyEntryCountMap.at(currKey) >
2918  row)) // if(col <= lastCol)
2919  {
2920  if(getNumberOfRows()) // skip first time
2921  sourceColumnMissingCount_ +=
2922  getNumberOfColumns() - colFoundCount;
2923 
2924  colFoundCount = 0; // reset column found count
2925  row = addRow();
2926  }
2927  lastCol = col;
2928  ++colFoundCount;
2929 
2930  if(getNumberOfRows() == 1) // only for first row
2931  sourceColumnNames_.emplace(currKey);
2932 
2933  // add value to row and column
2934 
2935  if(row >= getNumberOfRows())
2936  {
2937  __SS__ << "Invalid row"
2938  << __E__; // should be impossible?!
2939  __COUT__ << "\n" << ss.str();
2940  __SS_THROW__;
2941  return -1; // never gets here
2942  }
2943 
2944  theDataView_[row][col] = currVal;
2945  break;
2946  }
2947  }
2948  }
2949 
2950  if(ccnt >= getNumberOfColumns())
2951  {
2952  __COUT__
2953  << "Invalid column in JSON source data: " << currKey
2954  << " not found in column names of table named "
2955  << getTableName() << "."
2956  << __E__; // input data doesn't match config description
2957 
2958  // CHANGED on 11/10/2016
2959  // to.. try just not populating data instead of error
2960  ++sourceColumnMismatchCount_; // but count errors
2961  if(getNumberOfRows() ==
2962  1) // only for first row, track source column names
2963  sourceColumnNames_.emplace(currKey);
2964 
2965  //__SS_THROW__;
2966  __COUT_WARN__ << "Trying to ignore error, and not populating "
2967  "missing column."
2968  << __E__;
2969  }
2970  else // short cut to proper column hopefully in next search
2971  colSpeedup = (colSpeedup + 1) % noc;
2972  }
2973  break;
2974  default:; // unknown match?
2975  } // end switch statement to match json key
2976  } // end matched key if statement
2977 
2978  // clean up handling of new value
2979 
2980  newString = 0; // toggle flag
2981  newValue = 0; // toggle flag
2982  // isDataArray = 0;
2983  endNumber = -1; // reset end number index
2984  }
2985 
2986  // if(i>200) break; //185
2987  }
2988 
2989  //__COUT__ << "Done!" << __E__;
2990  __COUTTV__(fillWithLooseColumnMatching_);
2991  __COUTTV__(sourceColumnNames_.size());
2992  //__COUTV__(tableName_); // << "tableName_ = " << tableName_
2993 
2994  if(!fillWithLooseColumnMatching_ && sourceColumnMissingCount_ > 0)
2995  {
2996  __COUTV__(sourceColumnMissingCount_);
2997  __SS__ << "Can not ignore errors because not every column was found in the "
2998  "source data!"
2999  << ". Please see the details below:\n\n"
3000  << getMismatchColumnInfo() << StringMacros::stackTrace();
3001  __SS_ONLY_THROW__;
3002  }
3003 
3004  if(sourceColumnNames_.size() ==
3005  0) //if not populated by data (i.e. zero records), then use default column names
3006  {
3007  for(unsigned int i = 0; i < getNumberOfColumns(); ++i)
3008  sourceColumnNames_.emplace(getColumnsInfo()[i].getStorageName());
3009  }
3010 
3011  // print();
3012 
3013  return 0; // success
3014 } // end fillFromJSON()
3015 
3016 //==============================================================================
3017 std::string TableView::getMismatchColumnInfo(void) const
3018 {
3019  const std::set<std::string>& srcColNames = getSourceColumnNames();
3020  std::set<std::string> destColNames = getColumnStorageNames();
3021 
3022  __SS__ << "The source column size was found to be " << srcColNames.size()
3023  << ", and the current number of columns for this table is "
3024  << getNumberOfColumns() << ". This resulted in a count of "
3025  << getSourceColumnMismatch() << " source column mismatches, and a count of "
3026  << getSourceColumnMissing() << " table entries missing in "
3027  << getNumberOfRows() << " row(s) of data." << __E__;
3028 
3029  ss << "\n\n"
3030  << srcColNames.size()
3031  << " Source column names in ALPHABETICAL order were as follows:\n";
3032  char index = 'a';
3033  std::string preIndexStr = "";
3034  for(auto& srcColName : srcColNames)
3035  {
3036  if(destColNames.find(srcColName) == destColNames.end())
3037  ss << "\n\t*** " << preIndexStr << index << ". " << srcColName << " ***";
3038  else
3039  ss << "\n\t" << preIndexStr << index << ". " << srcColName;
3040 
3041  if(index == 'z') // wrap-around
3042  {
3043  preIndexStr += 'a'; // keep adding index 'digits' for wrap-around
3044  index = 'a';
3045  }
3046  else
3047  ++index;
3048  }
3049  ss << __E__;
3050 
3051  ss << "\n\n"
3052  << destColNames.size()
3053  << " Current table column names in ALPHABETICAL order are as follows:\n";
3054  index = 'a';
3055  preIndexStr = "";
3056  for(auto& destColName : destColNames)
3057  {
3058  if(srcColNames.find(destColName) == srcColNames.end())
3059  ss << "\n\t*** " << preIndexStr << index << ". " << destColName << " ***";
3060  else
3061  ss << "\n\t" << preIndexStr << index << ". " << destColName;
3062 
3063  if(index == 'z') // wrap-around
3064  {
3065  preIndexStr += 'a'; // keep adding index 'digits' for wrap-around
3066  index = 'a';
3067  }
3068  else
3069  ++index;
3070  }
3071  ss << __E__;
3072  return ss.str();
3073 } // end getMismatchColumnInfo()
3074 
3075 //==============================================================================
3076 bool TableView::isURIEncodedCommentTheSame(const std::string& comment) const
3077 {
3078  std::string compareStr = StringMacros::decodeURIComponent(comment);
3079  return comment_ == compareStr;
3080 }
3085 //{
3086 // __COUT__ << "valueStr " << valueStr << __E__;
3087 //
3088 // if(!(c < columnsInfo_.size() && r < getNumberOfRows()))
3089 // {
3090 // __SS__ << "Invalid row (" << (int)r << ") col (" << (int)c << ") requested!" <<
3091 //__E__;
3092 // __SS_THROW__;
3093 // }
3094 //
3095 // __COUT__ << "originalValueStr " << theDataView_[r][c] << __E__;
3096 //
3097 // if(columnsInfo_[c].getDataType() == TableViewColumnInfo::DATATYPE_TIME)
3098 // {
3099 // time_t valueTime(strtol(valueStr.c_str(),0,10));
3100 // time_t originalValueTime;
3101 // getValue(originalValueTime,r,c);
3102 // __COUT__ << "time_t valueStr " << valueTime << __E__;
3103 // __COUT__ << "time_t originalValueStr " << originalValueTime << __E__;
3104 // return valueTime == originalValueTime;
3105 // }
3106 // else
3107 // {
3108 // return valueStr == theDataView_[r][c];
3109 // }
3110 //}
3111 
3112 //==============================================================================
3123 void TableView::fillFromCSV(const std::string& data,
3124  const int& dataOffset /* = 0 */,
3125  const std::string& author /* = "" */,
3126  const char rowDelimter /* = ',' */,
3127  const char colDelimter /* = '\n' */)
3128 {
3129  int row = dataOffset;
3130  int col = 0;
3131  std::string currentValue = "";
3132  bool insideQuotes = false;
3133  int authorCol = findColByType(TableViewColumnInfo::TYPE_AUTHOR);
3134  int timestampCol = findColByType(TableViewColumnInfo::TYPE_TIMESTAMP);
3135 
3136  for(size_t i = 0; i < data.size(); ++i)
3137  {
3138  char c = data[i];
3139  const char nextChar = (i + 1 < data.size() ? data[i + 1] : ' ');
3140 
3141  if(c == '"')
3142  {
3143  if(insideQuotes && nextChar == '"') // "" will escape a double-quote in CSV
3144  {
3145  // Escaped double-quote
3146  currentValue += '"';
3147  ++i; //skip next quote
3148  }
3149  else
3150  {
3151  // Toggle quote mode
3152  insideQuotes = !insideQuotes;
3153  }
3154  }
3155  else if(c == rowDelimter && !insideQuotes)
3156  {
3157  if(col == 0 && row >= (int)getNumberOfRows())
3158  addRow(author);
3159  setValueAsString(StringMacros::trim(currentValue), row, col);
3160  ++col;
3161  currentValue = "";
3162  }
3163  else if((c == colDelimter || c == '\r') && !insideQuotes)
3164  {
3165  if(col > 0)
3166  {
3167  setValueAsString(StringMacros::trim(currentValue), row, col);
3168  __COUTV__(getValueAsString(row, col));
3169 
3170  //if row is actually column names, then delete the row
3171  if(getValueAsString(row, col) == getColumnsInfo()[col].getStorageName())
3172  {
3173  __COUT__ << "First row detected as column names." << __E__;
3174  deleteRow(row);
3175  --row; //rewind
3176  }
3177  else
3178  {
3179  //enforce author and timestamp not from CSV data
3180  setValue(author, row, authorCol);
3181  setValue(time(0), row, timestampCol);
3182  }
3183 
3184  col = 0;
3185  ++row; //prepare for next row
3186  currentValue = "";
3187  }
3188  }
3189  else
3190  {
3191  currentValue += c;
3192  }
3193  } //end text loop
3194 
3195  // Add last value if any
3196  if(col > 0)
3197  {
3198  setValueAsString(StringMacros::trim(currentValue), row, col);
3199  __COUTV__(getValueAsString(row, col));
3200  __COUTV__(getValueAsString(row, timestampCol));
3201 
3202  //if row is actually column names, then delete the row
3203  if(getValueAsString(row, col) == getColumnsInfo()[col].getStorageName())
3204  {
3205  __COUT__ << "First row detected as column names." << __E__;
3206  deleteRow(row);
3207  --row; //rewind
3208  }
3209  else
3210  {
3211  //enforce author and timestamp not from CSV data
3212  setValue(author, row, authorCol);
3213  setValue(time(0), row, timestampCol);
3214  }
3215 
3216  col = 0;
3217  ++row; //prepare for next row
3218  currentValue = "";
3219  }
3220 
3221  init(); // verify new table (throws runtime_errors)
3222 
3223 } //end fillFromCSV
3224 
3225 //==============================================================================
3251 int TableView::fillFromEncodedCSV(const std::string& data,
3252  const int& dataOffset,
3253  const std::string& author)
3254 {
3255  int retVal = 0;
3256 
3257  int r = dataOffset;
3258  int c = 0;
3259 
3260  int i = 0; // use to parse data std::string
3261  int j = data.find(',', i); // find next cell delimiter
3262  int k = data.find(';', i); // find next row delimiter
3263 
3264  bool rowWasModified;
3265  unsigned int countRowsModified = 0;
3266  int authorCol = findColByType(TableViewColumnInfo::TYPE_AUTHOR);
3267  int timestampCol = findColByType(TableViewColumnInfo::TYPE_TIMESTAMP);
3268  // std::string valueStr, tmpTimeStr, originalValueStr;
3269 
3270  while(k != (int)(std::string::npos))
3271  {
3272  rowWasModified = false;
3273  if(r >= (int)getNumberOfRows())
3274  {
3275  addRow();
3276  //__COUT__ << "Row added" << __E__;
3277  rowWasModified = true;
3278  }
3279 
3280  while(j < k && j != (int)(std::string::npos))
3281  {
3282  //__COUT__ << "Col " << (int)c << __E__;
3283 
3284  // skip last 2 columns
3285  if(c >= (int)getNumberOfColumns() - 2)
3286  {
3287  i = j + 1;
3288  j = data.find(',', i); // find next cell delimiter
3289  ++c;
3290  continue;
3291  }
3292 
3293  if(setURIEncodedValue(data.substr(i, j - i), r, c))
3294  rowWasModified = true;
3295 
3296  i = j + 1;
3297  j = data.find(',', i); // find next cell delimiter
3298  ++c;
3299  }
3300 
3301  // if row was modified, assign author and timestamp
3302  if(author != "" && rowWasModified)
3303  {
3304  __COUTT__ << "Row=" << (int)r << " was modified!" << __E__;
3305  setValue(author, r, authorCol);
3306  setValue(time(0), r, timestampCol);
3307  }
3308 
3309  if(rowWasModified)
3310  ++countRowsModified;
3311 
3312  ++r;
3313  c = 0;
3314 
3315  i = k + 1;
3316  j = data.find(',', i); // find next cell delimiter
3317  k = data.find(';', i); // find new row delimiter
3318  }
3319 
3320  // delete excess rows
3321  while(r < (int)getNumberOfRows())
3322  {
3323  deleteRow(r);
3324  __COUT__ << "Row deleted: " << (int)r << __E__;
3325  ++countRowsModified;
3326  }
3327 
3328  __COUT_INFO__ << "countRowsModified=" << countRowsModified << __E__;
3329 
3330  if(!countRowsModified)
3331  {
3332  // check that source columns match storage name
3333  // otherwise allow same data...
3334 
3335  bool match = getColumnStorageNames().size() == getSourceColumnNames().size();
3336  if(match)
3337  {
3338  for(auto& destColName : getColumnStorageNames())
3339  if(getSourceColumnNames().find(destColName) ==
3340  getSourceColumnNames().end())
3341  {
3342  __COUT__ << "Found column name mismach for '" << destColName
3343  << "'... So allowing same data!" << __E__;
3344 
3345  match = false;
3346  break;
3347  }
3348  }
3349  // if still a match, do not allow!
3350  if(match)
3351  {
3352  __SS__ << "No rows were modified! No reason to fill a view with same content."
3353  << __E__;
3354  __COUT__ << "\n" << ss.str();
3355  return -1;
3356  }
3357  // else mark with retVal
3358  retVal = 1;
3359  } // end same check
3360 
3361  // print(); //for debugging
3362 
3363  // setup sourceColumnNames_ to be correct
3364  sourceColumnNames_.clear();
3365  for(unsigned int i = 0; i < getNumberOfColumns(); ++i)
3366  sourceColumnNames_.emplace(getColumnsInfo()[i].getStorageName());
3367 
3368  init(); // verify new table (throws runtime_errors)
3369 
3370  // printout for debugging
3371  // __SS__ << "\n";
3372  // print(ss);
3373  // __COUT__ << "\n" << ss.str() << __E__;
3374 
3375  return retVal;
3376 } // end fillFromEncodedCSV()
3377 
3378 //==============================================================================
3387 bool TableView::setURIEncodedValue(const std::string& value,
3388  const unsigned int& r,
3389  const unsigned int& c,
3390  const std::string& author)
3391 {
3392  if(!(c < columnsInfo_.size() && r < getNumberOfRows()))
3393  {
3394  __SS__ << "Invalid row (" << (int)r << ") col (" << (int)c << ") requested!"
3395  << "Number of Rows = " << getNumberOfRows()
3396  << "Number of Columns = " << columnsInfo_.size() << __E__;
3397  print(ss);
3398  __SS_THROW__;
3399  }
3400 
3401  std::string valueStr = StringMacros::decodeURIComponent(value);
3402  std::string originalValueStr =
3403  getValueAsString(r, c, false); // do not convert env variables
3404 
3405  //__COUT__ << "valueStr " << valueStr << __E__;
3406  //__COUT__ << "originalValueStr " << originalValueStr << __E__;
3407 
3408  if(columnsInfo_[c].getDataType() == TableViewColumnInfo::DATATYPE_NUMBER)
3409  {
3410  // check if valid number
3411  std::string convertedString = StringMacros::convertEnvironmentVariables(valueStr);
3412  // do not check here, let init check
3413  // if this is a link to valid number, then this is an improper check.
3414  // if(!StringMacros::isNumber(convertedString))
3415  // {
3416  // __SS__ << "\tIn configuration " << tableName_
3417  // << " at column=" << columnsInfo_[c].getName() << " the value
3418  // set
3419  //("
3420  // << convertedString << ")"
3421  // << " is not a number! Please fix it or change the column
3422  // type..."
3423  // << __E__;
3424  // __SS_THROW__;
3425  // }
3426  theDataView_[r][c] = valueStr;
3427 
3428  // is it here that a new exception should be added to enforce min and max, given that they only appear with number type?
3429  }
3430  else if(columnsInfo_[c].getDataType() == TableViewColumnInfo::DATATYPE_TIME)
3431  {
3432  // valueStr = StringMacros::decodeURIComponent(data.substr(i,j-i));
3433  //
3434  // getValue(tmpTimeStr,r,c);
3435  // if(valueStr != tmpTimeStr)//theDataView_[r][c])
3436  // {
3437  // __COUT__ << "valueStr=" << valueStr <<
3438  // " theDataView_[r][c]=" << tmpTimeStr << __E__;
3439  // rowWasModified = true;
3440  // }
3441 
3442  setValue(time_t(strtol(valueStr.c_str(), 0, 10)), r, c);
3443  }
3444  else
3445  theDataView_[r][c] = valueStr;
3446 
3447  bool rowWasModified =
3448  (originalValueStr !=
3449  getValueAsString(r, c, false)); // do not convert env variables
3450 
3451  // if row was modified, assign author and timestamp
3452  if(author != "" && rowWasModified)
3453  {
3454  __COUT__ << "Row=" << (int)r << " was modified!" << __E__;
3455  int authorCol = findColByType(TableViewColumnInfo::TYPE_AUTHOR);
3456  int timestampCol = findColByType(TableViewColumnInfo::TYPE_TIMESTAMP);
3457  setValue(author, r, authorCol);
3458  setValue(time(0), r, timestampCol);
3459  }
3460 
3461  return rowWasModified;
3462 } // end setURIEncodedValue()
3463 
3464 //==============================================================================
3465 void TableView::resizeDataView(unsigned int nRows, unsigned int nCols)
3466 {
3467  // FIXME This maybe should disappear but I am using it in ConfigurationHandler
3468  // still...
3469  theDataView_.resize(nRows, std::vector<std::string>(nCols));
3470 }
3471 
3472 //==============================================================================
3479 unsigned int TableView::addRow(
3480  const std::string& author,
3481  unsigned char
3482  incrementUniqueData /* = false */, // leave as unsigned char rather than
3483  // bool, too many things (e.g. strings)
3484  // evaluate successfully to bool values
3485  const std::string& baseNameAutoUID /* = "" */,
3486  unsigned int rowToAdd /* = -1 */,
3487  std::string childLinkIndex /* = "" */,
3488  std::string groupId /* = "" */)
3489 {
3490  // default to last row
3491  if(rowToAdd == (unsigned int)-1)
3492  rowToAdd = getNumberOfRows();
3493 
3494  theDataView_.resize(getNumberOfRows() + 1,
3495  std::vector<std::string>(getNumberOfColumns()));
3496 
3497  // shift data down the table if necessary
3498  for(unsigned int r = getNumberOfRows() - 2; r >= rowToAdd; --r)
3499  {
3500  if(r == (unsigned int)-1)
3501  break; // quit wrap around case
3502  for(unsigned int col = 0; col < getNumberOfColumns(); ++col)
3503  theDataView_[r + 1][col] = theDataView_[r][col];
3504  }
3505 
3506  std::vector<std::string> defaultRowValues = getDefaultRowValues();
3507 
3508  // char indexString[1000];
3509  std::string tmpString, baseString;
3510  // bool foundAny;
3511  // unsigned int index;
3512  // unsigned int maxUniqueData;
3513  std::string numString;
3514 
3515  // fill each col of new row with default values
3516  // if a row is a unique data row, increment last row in attempt to make a legal
3517  // column
3518  for(unsigned int col = 0; col < getNumberOfColumns(); ++col)
3519  {
3520  // if(incrementUniqueData)
3521  // __COUT__ << col << " " << columnsInfo_[col].getType() << " basename= " <<
3522  // baseNameAutoUID << __E__;
3523 
3524  // baseNameAutoUID indicates to attempt to make row unique
3525  // add index to max number
3526  if(incrementUniqueData &&
3527  (col == getColUID() || columnsInfo_[col].isChildLinkGroupID() ||
3528  (getNumberOfRows() > 1 &&
3529  (columnsInfo_[col].getType() == TableViewColumnInfo::TYPE_UNIQUE_DATA ||
3530  columnsInfo_[col].getType() ==
3531  TableViewColumnInfo::TYPE_UNIQUE_GROUP_DATA))))
3532  {
3533  if(col == getColUID() || columnsInfo_[col].isChildLinkGroupID())
3535  rowToAdd, col, baseNameAutoUID /*baseValueAsString*/);
3536  else
3537  setUniqueColumnValue(rowToAdd,
3538  col,
3539  "" /* baseValueAsString */,
3540  false /* doMathAppendStrategy */,
3541  childLinkIndex,
3542  groupId);
3543  }
3544  else
3545  theDataView_[rowToAdd][col] = defaultRowValues[col];
3546  }
3547 
3548  if(author != "")
3549  {
3550  __COUT__ << "Row=" << rowToAdd << " was created!" << __E__;
3551 
3552  int authorCol = findColByType(TableViewColumnInfo::TYPE_AUTHOR);
3553  int timestampCol = findColByType(TableViewColumnInfo::TYPE_TIMESTAMP);
3554  setValue(author, rowToAdd, authorCol);
3555  setValue(time(0), rowToAdd, timestampCol);
3556  }
3557 
3558  return rowToAdd;
3559 } // end addRow()
3560 
3561 //==============================================================================
3565 {
3566  if(r >= (int)getNumberOfRows())
3567  {
3568  // out of bounds
3569  __SS__ << "Row " << (int)r
3570  << " is out of bounds (Row Count = " << getNumberOfRows()
3571  << ") and can not be deleted." << __E__;
3572  __SS_THROW__;
3573  }
3574 
3575  theDataView_.erase(theDataView_.begin() + r);
3576 } // end deleteRow()
3577 
3578 //==============================================================================
3595  const unsigned int& c,
3596  bool& isGroup,
3597  std::pair<unsigned int /*link col*/, unsigned int /*link id col*/>& linkPair) const
3598 {
3599  if(!(c < columnsInfo_.size()))
3600  {
3601  __SS__ << "Invalid col (" << (int)c << ") requested for child link!" << __E__;
3602  __SS_THROW__;
3603  }
3604 
3605  //__COUT__ << "getChildLink for col: " << (int)c << "-" <<
3606  // columnsInfo_[c].getType() << "-" << columnsInfo_[c].getName() << __E__;
3607 
3608  // check if column is a child link UID
3609  if((isGroup = columnsInfo_[c].isChildLinkGroupID()) ||
3610  columnsInfo_[c].isChildLinkUID())
3611  {
3612  // must be part of unique link, (or invalid table?)
3613  //__COUT__ << "col: " << (int)c << __E__;
3614  linkPair.second = c;
3615  std::string index = columnsInfo_[c].getChildLinkIndex();
3616 
3617  //__COUT__ << "index: " << index << __E__;
3618 
3619  // find pair link
3620  for(unsigned int col = 0; col < columnsInfo_.size(); ++col)
3621  {
3622  //__COUT__ << "try: " << col << "-" << columnsInfo_[col].getType() << "-" <<
3623  // columnsInfo_[col].getName() << __E__;
3624  if(col == c)
3625  continue; // skip column c that we know
3626  else if(columnsInfo_[col].isChildLink() &&
3627  index == columnsInfo_[col].getChildLinkIndex())
3628  {
3629  // found match!
3630  //__COUT__ << "getChildLink Found match for col: " << (int)c << " at " <<
3631  // col << __E__;
3632  linkPair.first = col;
3633  return true;
3634  }
3635  }
3636 
3637  // if here then invalid table!
3638  __SS__ << "\tIn view: " << tableName_
3639  << ", Can't find complete child link for column name "
3640  << columnsInfo_[c].getName() << __E__;
3641  __SS_THROW__;
3642  }
3643 
3644  if(!columnsInfo_[c].isChildLink())
3645  return false; // cant be unique link
3646 
3647  // this is child link, so find pair link uid or gid column
3648  linkPair.first = c;
3649  std::string index = columnsInfo_[c].getChildLinkIndex();
3650 
3651  //__COUT__ << "index: " << index << __E__;
3652 
3653  // find pair link
3654  for(unsigned int col = 0; col < columnsInfo_.size(); ++col)
3655  {
3656  //__COUT__ << "try: " << col << "-" << columnsInfo_[col].getType() << "-" <<
3657  // columnsInfo_[col].getName() << __E__;
3658  if(col == c)
3659  continue; // skip column c that we know
3660  // __COUT__ << "try: " << col << "-" << columnsInfo_[col].getType() <<
3661  // "-" << columnsInfo_[col].getName() <<
3662  // "-u" << columnsInfo_[col].isChildLinkUID() <<
3663  // "-g" << columnsInfo_[col].isChildLinkGroupID() << __E__;
3664  //
3665  // if(columnsInfo_[col].isChildLinkUID())
3666  // __COUT__ << "-L" << columnsInfo_[col].getChildLinkIndex() << __E__;
3667  //
3668  // if(columnsInfo_[col].isChildLinkGroupID())
3669  // __COUT__ << "-L" << columnsInfo_[col].getChildLinkIndex() << __E__;
3670 
3671  if(((columnsInfo_[col].isChildLinkUID() && !(isGroup = false)) ||
3672  (columnsInfo_[col].isChildLinkGroupID() && (isGroup = true))) &&
3673  index == columnsInfo_[col].getChildLinkIndex())
3674  {
3675  // found match!
3676  //__COUT__ << "getChildLink Found match for col: " << (int)c << " at " << col
3677  //<< __E__;
3678  linkPair.second = col;
3679  return true;
3680  }
3681  }
3682 
3683  // if here then invalid table!
3684  __SS__ << "\tIn view: " << tableName_
3685  << ", Can't find complete child link id for column name "
3686  << columnsInfo_[c].getName() << __E__;
3687  __SS_THROW__;
3688 } // end getChildLink()
static std::string convertToCaps(std::string &str, bool isConfigName=false)
Definition: TableBase.cc:1894
static const std::string DATATYPE_NUMBER
static const std::string TYPE_UID
NOTE: Do NOT put '-' in static const TYPEs because it will mess up javascript handling in the web gui...
unsigned int findRow(unsigned int col, const T &value, unsigned int offsetRow=0, bool doNotThrow=false) const
< in included .icc source
std::string getEscapedValueAsString(unsigned int row, unsigned int col, bool convertEnvironmentVariables=true, bool quotesToDoubleQuotes=false) const
Definition: TableView.cc:1015
bool isEntryInGroup(const unsigned int &row, const std::string &childLinkIndex, const std::string &groupNeedle) const
Definition: TableView.cc:1652
void setValueAsString(const std::string &value, unsigned int row, unsigned int col)
Definition: TableView.cc:1081
void deleteRow(int r)
Definition: TableView.cc:3564
std::vector< std::vector< unsigned int > > getGroupRowsByPriority(const unsigned int groupIdCol, const std::string &groupID, bool onlyStatusTrue=false) const
Definition: TableView.cc:1511
T validateValueForColumn(const std::string &value, unsigned int col, bool doConvertEnvironmentVariables=true) const
< in included .icc source
TableView(const std::string &tableName)
= "");
Definition: TableView.cc:20
unsigned int getColStatus(void) const
Definition: TableView.cc:1398
unsigned int getLinkGroupIDColumn(const std::string &childLinkIndex) const
Definition: TableView.cc:1847
bool removeRowFromGroup(const unsigned int &row, const unsigned int &col, const std::string &groupID, bool deleteRowIfNoGroupLeft=false)
Definition: TableView.cc:1598
unsigned int findColByType(const std::string &type, unsigned int startingCol=0) const
Definition: TableView.cc:1985
bool getChildLink(const unsigned int &col, bool &isGroup, std::pair< unsigned int, unsigned int > &linkPair) const
Definition: TableView.cc:3594
void addRowToGroup(const unsigned int &row, const unsigned int &col, const std::string &groupID)
, const std::string& colDefault);
Definition: TableView.cc:1456
unsigned int copyRows(const std::string &author, const TableView &src, unsigned int srcOffsetRow=0, unsigned int srcRowsToCopy=(unsigned int) -1, unsigned int destOffsetRow=(unsigned int) -1, unsigned char generateUniqueDataColumns=false, const std::string &baseNameAutoUID="")
Definition: TableView.cc:126
unsigned int getColPriority(void) const
Definition: TableView.cc:1429
const std::string & setUniqueColumnValue(unsigned int row, unsigned int col, std::string baseValueAsString="", bool doMathAppendStrategy=false, std::string childLinkIndex="", std::string groupId="")
Definition: TableView.cc:1101
void init(void)
Definition: TableView.cc:195
std::string getValueAsString(unsigned int row, unsigned int col, bool convertEnvironmentVariables=true) const
Definition: TableView.cc:966
std::vector< unsigned int > getGroupRows(const unsigned int groupIdCol, const std::string &groupID, bool onlyStatusTrue=false, bool orderedByPriority=false) const
Definition: TableView.cc:1488
const std::string & getCustomStorageData(void) const
Getters.
Definition: TableView.h:71
std::set< std::string > getSetOfGroupIDs(const std::string &childLinkIndex, unsigned int row=-1) const
Definition: TableView.cc:1746
void getValue(T &value, unsigned int row, unsigned int col, bool doConvertEnvironmentVariables=true) const
< in included .icc source
unsigned int getDataColumnSize(void) const
getDataColumnSize
Definition: TableView.cc:2002
int fillFromJSON(const std::string &json)
Definition: TableView.cc:2363
unsigned int getColUID(void) const
Definition: TableView.cc:1313
bool setURIEncodedValue(const std::string &value, const unsigned int &row, const unsigned int &col, const std::string &author="")
Definition: TableView.cc:3387
void fillFromCSV(const std::string &data, const int &dataOffset=0, const std::string &author="", const char rowDelimter=',', const char colDelimter='\n')
Definition: TableView.cc:3123
int fillFromEncodedCSV(const std::string &data, const int &dataOffset=0, const std::string &author="")
Definition: TableView.cc:3251
unsigned int findCol(const std::string &name) const
Definition: TableView.cc:1962
void setValue(const T &value, unsigned int row, unsigned int col)
< in included .icc source
void setURIEncodedComment(const std::string &uriComment)
Definition: TableView.cc:2117
unsigned int addRow(const std::string &author="", unsigned char incrementUniqueData=false, const std::string &baseNameAutoUID="", unsigned int rowToAdd=(unsigned int) -1, std::string childLinkIndex="", std::string groupId="")
Definition: TableView.cc:3479
unsigned int findRowInGroup(unsigned int col, const T &value, const std::string &groupId, const std::string &childLinkIndex, unsigned int offsetRow=0) const
< in included .icc source
void setCustomStorageData(const std::string &storageData)
Definition: TableView.h:168
defines used also by OtsConfigurationWizardSupervisor
static std::string getTimestampString(const std::string &linuxTimeInSeconds)
static const std::string & trim(std::string &s)
static void getSetFromString(const std::string &inputString, std::set< std::string > &setToReturn, const std::set< char > &delimiter={',', '|', '&'}, const std::set< char > &whitespace={' ', '\t', '\n', '\r'})
static std::string convertEnvironmentVariables(const std::string &data)
static std::string demangleTypeName(const char *name)
static std::string restoreJSONStringEntities(const std::string &str)
static bool wildCardMatch(const std::string &needle, const std::string &haystack, unsigned int *priorityIndex=0)
Definition: StringMacros.cc:30
static std::string decodeURIComponent(const std::string &data)
static std::string stackTrace(void)
static bool getNumber(const std::string &s, T &retValue)