otsdaq  3.04.02
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  for(unsigned int r = 0; r < getNumberOfRows(); ++r)
1545  if(groupID == "" || groupID == "*" || groupIdCol == INVALID ||
1546  isEntryInGroupCol(r, groupIdCol, groupID))
1547  {
1548  // check status if needed
1549  if(onlyStatusTrue && colStatus_ != INVALID)
1550  {
1551  getValue(tmpStatus, r, colStatus_);
1552 
1553  if(!tmpStatus)
1554  continue; // skip those with status false
1555  }
1556 
1557  if(orderedByPriority && colPriority_ != INVALID)
1558  {
1559  getValue(tmpPriority, r, colPriority_);
1560  // do not accept DEFAULT value of 0.. convert to 100
1561  mapByPriority[tmpPriority ? tmpPriority : 100].push_back(r);
1562  }
1563  else // assume equal priority
1564  retVector[0].push_back(r);
1565  }
1566 
1567  if(orderedByPriority && colPriority_ != INVALID)
1568  {
1569  // at this point have priority map (which automatically sorts by priority)
1570  // now build return vector
1571  for(const auto& priorityChildRowVector : mapByPriority)
1572  {
1573  retVector.push_back(std::vector<unsigned int /*group row*/>());
1574  for(const auto& priorityChildRow : priorityChildRowVector.second)
1575  retVector[retVector.size() - 1].push_back(priorityChildRow);
1576  }
1577 
1578  __COUT__ << "Returning priority children list." << __E__;
1579  }
1580  // else equal priority vector already constructed
1581 
1582  return retVector;
1583 } // end getGroupRowsInVectors()
1584 
1585 //==============================================================================
1590 bool TableView::removeRowFromGroup(const unsigned int& row,
1591  const unsigned int& col,
1592  const std::string& groupNeedle,
1593  bool deleteRowIfNoGroupLeft)
1594 {
1595  __COUT__ << "groupNeedle " << groupNeedle << __E__;
1596  std::set<std::string> groupIDList;
1597  if(!isEntryInGroupCol(row, col, groupNeedle, &groupIDList))
1598  {
1599  __SS__ << "GroupID (" << groupNeedle << ") removed from row (" << row
1600  << ") was already removed!" << __E__;
1601  print();
1602  __SS_THROW__;
1603  }
1604 
1605  // is in group, so
1606  // create new string based on set of groupids
1607  // but skip groupNeedle
1608 
1609  std::string newValue = "";
1610  unsigned int cnt = 0;
1611  for(const auto& groupID : groupIDList)
1612  {
1613  //__COUT__ << groupID << " " << groupNeedle << " " << newValue << __E__;
1614  if(groupID == groupNeedle)
1615  continue; // skip group to be removed
1616 
1617  if(cnt)
1618  newValue += " | ";
1619  newValue += groupID;
1620  }
1621 
1622  bool wasDeleted = false;
1623  if(deleteRowIfNoGroupLeft && newValue == "")
1624  {
1625  __COUT__ << "Delete row since it no longer part of any group." << __E__;
1626  deleteRow(row);
1627  wasDeleted = true;
1628  }
1629  else
1630  setValue(newValue, row, col);
1631 
1632  //__COUT__ << getDataView()[row][col] << __E__;
1633 
1634  return wasDeleted;
1635 } // end removeRowFromGroup()
1636 
1637 //==============================================================================
1643 bool TableView::isEntryInGroup(const unsigned int& r,
1644  const std::string& childLinkIndex,
1645  const std::string& groupNeedle) const
1646 {
1647  unsigned int c = getLinkGroupIDColumn(childLinkIndex); // column in question
1648 
1649  return isEntryInGroupCol(r, c, groupNeedle);
1650 } // end isEntryInGroup()
1651 
1652 //==============================================================================
1661 bool TableView::isEntryInGroupCol(const unsigned int& r,
1662  const unsigned int& c,
1663  const std::string& groupNeedle,
1664  std::set<std::string>* groupIDList) const
1665 {
1666  if(r >= getNumberOfRows() || c >= getNumberOfColumns())
1667  {
1668  __SS__ << "Invalid row/col requested!" << __E__;
1669  ss << StringMacros::stackTrace() << __E__;
1670  __SS_THROW__;
1671  }
1672 
1673  unsigned int i = 0;
1674  unsigned int j = 0;
1675  bool found = false;
1676 
1677  //__COUT__ << "groupNeedle " << groupNeedle << __E__;
1678 
1679  // go through the full groupString extracting groups and comparing to groupNeedle
1680  for(; j < theDataView_[r][c].size(); ++j)
1681  if((theDataView_[r][c][j] == ' ' || // ignore leading white space or |
1682  theDataView_[r][c][j] == '|') &&
1683  i == j)
1684  ++i;
1685  else if((theDataView_[r][c][j] ==
1686  ' ' || // trailing white space or | indicates group
1687  theDataView_[r][c][j] == '|') &&
1688  i != j) // assume end of group name
1689  {
1690  if(groupIDList)
1691  groupIDList->emplace(theDataView_[r][c].substr(i, j - i));
1692 
1693  //__COUT__ << "Group found to compare: " <<
1694  // theDataView_[r][c].substr(i,j-i) << __E__;
1695  if(groupNeedle == theDataView_[r][c].substr(i, j - i))
1696  {
1697  if(!groupIDList) // dont return if caller is trying to get group list
1698  return true;
1699  found = true;
1700  }
1701  // if no match, setup i and j for next find
1702  i = j + 1;
1703  }
1704 
1705  if(i != j) // last group check (for case when no ' ' or '|')
1706  {
1707  if(groupIDList)
1708  groupIDList->emplace(theDataView_[r][c].substr(i, j - i));
1709 
1710  //__COUT__ << "Group found to compare: " <<
1711  // theDataView_[r][c].substr(i,j-i) << __E__;
1712  if(groupNeedle == theDataView_[r][c].substr(i, j - i))
1713  return true;
1714  }
1715 
1716  return found;
1717 } // end isEntryInGroupCol()
1718 
1719 //==============================================================================
1727 std::set<std::string> TableView::getSetOfGroupIDs(const std::string& childLinkIndex,
1728  unsigned int r) const
1729 {
1730  return getSetOfGroupIDs(getLinkGroupIDColumn(childLinkIndex), r);
1731 }
1732 std::set<std::string> TableView::getSetOfGroupIDs(const unsigned int& c,
1733  unsigned int r) const
1734 {
1735  //__COUT__ << "GroupID col=" << (int)c << __E__;
1736 
1737  std::set<std::string> retSet;
1738 
1739  // unsigned int i = 0;
1740  // unsigned int j = 0;
1741 
1742  if(r != (unsigned int)-1)
1743  {
1744  if(r >= getNumberOfRows())
1745  {
1746  __SS__ << "Invalid row requested!" << __E__;
1747  __SS_THROW__;
1748  }
1749 
1750  StringMacros::getSetFromString(theDataView_[r][c], retSet);
1751  // //go through the full groupString extracting groups
1752  // //add each found groupId to set
1753  // for(;j<theDataView_[r][c].size();++j)
1754  // if((theDataView_[r][c][j] == ' ' || //ignore leading white space or |
1755  // theDataView_[r][c][j] == '|')
1756  // && i == j)
1757  // ++i;
1758  // else if((theDataView_[r][c][j] == ' ' || //trailing white space or |
1759  // indicates group theDataView_[r][c][j] == '|')
1760  // && i != j) // assume end of group name
1761  // {
1762  // //__COUT__ << "Group found: " <<
1763  // // theDataView_[r][c].substr(i,j-i) << __E__;
1764  //
1765  //
1766  // retSet.emplace(theDataView_[r][c].substr(i,j-i));
1767  //
1768  // //setup i and j for next find
1769  // i = j+1;
1770  // }
1771  //
1772  // if(i != j) //last group check (for case when no ' ' or '|')
1773  // retSet.emplace(theDataView_[r][c].substr(i,j-i));
1774  }
1775  else
1776  {
1777  // do all rows
1778  for(r = 0; r < getNumberOfRows(); ++r)
1779  {
1780  StringMacros::getSetFromString(theDataView_[r][c], retSet);
1781 
1782  // i=0;
1783  // j=0;
1784  //
1785  // //__COUT__ << (int)r << ": " << theDataView_[r][c] << __E__;
1786  //
1787  // //go through the full groupString extracting groups
1788  // //add each found groupId to set
1789  // for(;j<theDataView_[r][c].size();++j)
1790  // {
1791  // //__COUT__ << "i:" << i << " j:" << j << __E__;
1792  //
1793  // if((theDataView_[r][c][j] == ' ' || //ignore leading white
1794  // space or | theDataView_[r][c][j] == '|')
1795  // && i == j)
1796  // ++i;
1797  // else if((theDataView_[r][c][j] == ' ' || //trailing white
1798  // space or | indicates group theDataView_[r][c][j]
1799  // ==
1800  // '|')
1801  // && i != j) // assume end of group name
1802  // {
1803  // //__COUT__ << "Group found: " <<
1804  // // theDataView_[r][c].substr(i,j-i) << __E__;
1805  //
1806  // retSet.emplace(theDataView_[r][c].substr(i,j-i));
1807  //
1808  // //setup i and j for next find
1809  // i = j+1;
1810  // }
1811  // }
1812  //
1813  // if(i != j) //last group (for case when no ' ' or '|')
1814  // {
1815  // //__COUT__ << "Group found: " <<
1816  // // theDataView_[r][c].substr(i,j-i) << __E__;
1817  // retSet.emplace(theDataView_[r][c].substr(i,j-i));
1818  // }
1819  }
1820  }
1821 
1822  return retSet;
1823 }
1824 
1825 //==============================================================================
1828 unsigned int TableView::getLinkGroupIDColumn(const std::string& childLinkIndex) const
1829 {
1830  if(!childLinkIndex.size())
1831  {
1832  __SS__ << "Empty childLinkIndex string parameter!" << __E__;
1833  ss << StringMacros::stackTrace() << __E__;
1834  __SS_THROW__;
1835  }
1836 
1837  const char* needleChildLinkIndex = &childLinkIndex[0];
1838 
1839  // allow space syntax to target a childLinkIndex from a different parentLinkIndex
1840  // e.g. "parentLinkIndex childLinkIndex"
1841  size_t spacePos = childLinkIndex.find(' ');
1842  if(spacePos != std::string::npos &&
1843  spacePos + 1 < childLinkIndex.size()) // make sure there are more characters
1844  {
1845  // found space syntax for targeting childLinkIndex
1846  needleChildLinkIndex = &childLinkIndex[spacePos + 1];
1847  }
1848 
1849  std::map<std::string, unsigned int>::const_iterator it =
1850  colLinkGroupIDs_.find(needleChildLinkIndex);
1851  if(it != // if already known, return it
1852  colLinkGroupIDs_.end())
1853  return it->second;
1854 
1855  // otherwise search (perhaps init() was not called)
1856  for(unsigned int col = 0; col < columnsInfo_.size(); ++col)
1857  {
1858  // only check columns with link index associated...
1859  if(columnsInfo_[col].isChildLink() || columnsInfo_[col].isChildLinkUID() ||
1860  columnsInfo_[col].isChildLinkGroupID() || columnsInfo_[col].isGroupID())
1861  {
1862  if(needleChildLinkIndex == columnsInfo_[col].getChildLinkIndex())
1863  return col;
1864  }
1865  }
1866 
1867  __SS__
1868  << "Error! Incompatible table for this group link! Table '" << tableName_
1869  << "' is missing a GroupID column with data type '"
1870  << TableViewColumnInfo::TYPE_START_GROUP_ID << "-" << needleChildLinkIndex
1871  << "'.\n\n"
1872  << "Note: you can separate the child GroupID column data type from "
1873  << "the parent GroupLink column data type; this is accomplished by using a space "
1874  << "character at the parent level - the string after the space will be treated "
1875  "as the "
1876  << "child GroupID column data type." << __E__;
1877  ss << "Existing Column GroupIDs: " << __E__;
1878  for(auto& groupIdColPair : colLinkGroupIDs_)
1879  ss << "\t" << groupIdColPair.first << " : col-" << groupIdColPair.second << __E__;
1880 
1881  ss << "Existing Column Types: " << __E__;
1882  for(unsigned int col = 0; col < columnsInfo_.size(); ++col)
1883  ss << "\t" << columnsInfo_[col].getType() << "() " << columnsInfo_[col].getName()
1884  << __E__;
1885 
1886  ss << StringMacros::stackTrace() << __E__;
1887 
1888  __SS_THROW__;
1889 } // end getLinkGroupIDColumn()
1890 
1891 //==============================================================================
1892 unsigned int TableView::findRow(unsigned int col,
1893  const std::string& value,
1894  unsigned int offsetRow,
1895  bool doNotThrow /*= false*/) const
1896 {
1897  for(unsigned int row = offsetRow; row < theDataView_.size(); ++row)
1898  {
1899  if(theDataView_[row][col] == value)
1900  return row;
1901  }
1902  if(doNotThrow)
1903  return TableView::INVALID;
1904 
1905  __SS__ << "\tIn view: " << tableName_ << ", Can't find value=" << value
1906  << " in column named " << columnsInfo_[col].getName()
1907  << " with type=" << columnsInfo_[col].getType() << __E__ << __E__
1908  << StringMacros::stackTrace() << __E__;
1909 
1910  // Note: findRow gets purposely called by configuration GUI a lot looking for
1911  // exceptions so may not want to print out
1912  //__COUT__ << "\n" << ss.str();
1913  __SS_ONLY_THROW__;
1914 } // end findRow()
1915 
1916 //==============================================================================
1917 unsigned int TableView::findRowInGroup(unsigned int col,
1918  const std::string& value,
1919  const std::string& groupId,
1920  const std::string& childLinkIndex,
1921  unsigned int offsetRow) const
1922 {
1923  unsigned int groupIdCol = getLinkGroupIDColumn(childLinkIndex);
1924  for(unsigned int row = offsetRow; row < theDataView_.size(); ++row)
1925  {
1926  if(theDataView_[row][col] == value && isEntryInGroupCol(row, groupIdCol, groupId))
1927  return row;
1928  }
1929 
1930  __SS__ << "\tIn view: " << tableName_ << ", Can't find in group the value=" << value
1931  << " in column named '" << columnsInfo_[col].getName()
1932  << "' with type=" << columnsInfo_[col].getType() << " and GroupID: '"
1933  << groupId << "' in column '" << groupIdCol
1934  << "' with GroupID child link index '" << childLinkIndex << "'" << __E__;
1935  // Note: findRowInGroup gets purposely called by configuration GUI a lot looking for
1936  // exceptions so may not want to print out
1937  __SS_ONLY_THROW__;
1938 } // end findRowInGroup()
1939 
1940 //==============================================================================
1943 unsigned int TableView::findCol(const std::string& wildCardName) const
1944 {
1945  for(unsigned int col = 0; col < columnsInfo_.size(); ++col)
1946  if(StringMacros::wildCardMatch(wildCardName /*needle*/,
1947  columnsInfo_[col].getName() /*haystack*/))
1948  return col;
1949 
1950  __SS__ << "\tIn view: " << tableName_ << ", Can't find column named '" << wildCardName
1951  << "'" << __E__;
1952  ss << "Existing columns:\n";
1953  for(unsigned int col = 0; col < columnsInfo_.size(); ++col)
1954  ss << "\t" << columnsInfo_[col].getName() << "\n";
1955 
1956  ss << StringMacros::stackTrace() << __E__;
1957 
1958  // Note: findCol gets purposely called by configuration GUI a lot looking for
1959  // exceptions so may not want to print out
1960  __SS_ONLY_THROW__;
1961 } // end findCol()
1962 
1963 //==============================================================================
1966 unsigned int TableView::findColByType(const std::string& type,
1967  unsigned int startingCol) const
1968 {
1969  for(unsigned int col = startingCol; col < columnsInfo_.size(); ++col)
1970  {
1971  __COUTS__(40) << columnsInfo_[col].getType() << __E__;
1972  if(columnsInfo_[col].getType() == type)
1973  return col;
1974  }
1975 
1976  return INVALID;
1977 } // end findColByType()
1978 
1981 //==============================================================================
1983 unsigned int TableView::getDataColumnSize(void) const
1984 {
1985  // if no data, give benefit of the doubt that phantom data has mockup column size
1986  if(!getNumberOfRows())
1987  return getNumberOfColumns();
1988  return theDataView_[0].size(); // number of columns in first row of data
1989 }
1990 
1991 //==============================================================================
1992 std::set<std::string> TableView::getColumnNames(void) const
1993 {
1994  std::set<std::string> retSet;
1995  for(auto& colInfo : columnsInfo_)
1996  retSet.emplace(colInfo.getName());
1997  return retSet;
1998 } // end getColumnNames()
1999 
2000 //==============================================================================
2001 std::map<std::string, unsigned int /*col*/> TableView::getColumnNamesMap(void) const
2002 {
2003  std::map<std::string, unsigned int /*col*/> retMap;
2004  unsigned int c = 0;
2005  for(auto& colInfo : columnsInfo_)
2006  retMap.emplace(std::make_pair(colInfo.getName(), c++));
2007  return retMap;
2008 } // end getColumnNamesMap()
2009 
2010 //==============================================================================
2011 std::set<std::string> TableView::getColumnStorageNames(void) const
2012 {
2013  std::set<std::string> retSet;
2014  for(auto& colInfo : columnsInfo_)
2015  retSet.emplace(colInfo.getStorageName());
2016  return retSet;
2017 }
2018 
2019 //==============================================================================
2020 const std::vector<std::string>& TableView::initRowDefaults(void)
2021 {
2022  std::vector<std::string>& retVec = rowDefaultValues_;
2023  retVec.clear();
2024 
2025  // fill each col of new row with default values
2026  for(unsigned int col = 0; col < getNumberOfColumns(); ++col)
2027  {
2028  // if this is a fixed choice Link, and NO_LINK is not in list,
2029  // take first in list to avoid creating illegal rows.
2030  // NOTE: this is not a problem for standard fixed choice fields
2031  // because the default value is always required.
2032 
2033  if(columnsInfo_[col].isChildLink())
2034  {
2035  const std::vector<std::string>& theDataChoices =
2036  columnsInfo_[col].getDataChoices();
2037 
2038  // check if arbitrary values allowed
2039  if(!theDataChoices.size() || // if so, use default
2040  theDataChoices[0] == "arbitraryBool=1")
2041  retVec.push_back(columnsInfo_[col].getDefaultValue());
2042  else
2043  {
2044  bool skipOne =
2045  (theDataChoices.size() && theDataChoices[0] == "arbitraryBool=0");
2046  bool hasSkipped;
2047 
2048  // look for default value in list
2049 
2050  bool foundDefault = false;
2051  hasSkipped = false;
2052  for(const auto& choice : theDataChoices)
2053  if(skipOne && !hasSkipped)
2054  {
2055  hasSkipped = true;
2056  continue;
2057  }
2058  else if(choice == columnsInfo_[col].getDefaultValue())
2059  {
2060  foundDefault = true;
2061  break;
2062  }
2063 
2064  // use first choice if possible
2065  if(!foundDefault && theDataChoices.size() > (skipOne ? 1 : 0))
2066  retVec.push_back(theDataChoices[(skipOne ? 1 : 0)]);
2067  else // else stick with default
2068  retVec.push_back(columnsInfo_[col].getDefaultValue());
2069  }
2070  }
2071  else
2072  retVec.push_back(columnsInfo_[col].getDefaultValue());
2073  }
2074 
2075  //__COUT__ << StringMacros::stackTrace() << __E__;
2076  //__COUTV__(StringMacros::vectorToString(rowDefaultValues_));
2077  return rowDefaultValues_;
2078 } // end getDefaultRowValues()
2079 
2080 //==============================================================================
2081 const TableViewColumnInfo& TableView::getColumnInfo(unsigned int column) const
2082 {
2083  if(column >= columnsInfo_.size())
2084  {
2085  __SS__ << "\nCan't find column " << column
2086  << "\n\n\n\nThe column info is likely missing due to incomplete "
2087  "Configuration View filling.\n\n"
2088  << __E__;
2089  ss << StringMacros::stackTrace() << __E__;
2090  __SS_THROW__;
2091  }
2092  return columnsInfo_[column];
2093 } // end getColumnInfo()
2094 
2097 //==============================================================================
2098 void TableView::setURIEncodedComment(const std::string& uriComment)
2099 {
2100  comment_ = StringMacros::decodeURIComponent(uriComment);
2101 }
2102 
2103 //==============================================================================
2104 void TableView::setAuthor(const std::string& author) { author_ = author; }
2105 
2106 //==============================================================================
2107 void TableView::setCreationTime(time_t t) { creationTime_ = t; }
2108 
2109 //==============================================================================
2110 void TableView::setLastAccessTime(time_t t) { lastAccessTime_ = t; }
2111 
2112 //==============================================================================
2113 void TableView::setLooseColumnMatching(bool setValue)
2114 {
2115  fillWithLooseColumnMatching_ = setValue;
2116 }
2117 
2118 //==============================================================================
2119 void TableView::doGetSourceRawData(bool setValue) { getSourceRawData_ = setValue; }
2120 
2121 //==============================================================================
2122 void TableView::reset(void)
2123 {
2124  version_ = -1;
2125  comment_ = "";
2126  author_ = "";
2127  columnsInfo_.clear();
2128  theDataView_.clear();
2129 } // end reset()
2130 
2131 //==============================================================================
2132 void TableView::print(std::ostream& out /* = std::cout */) const
2133 {
2134  out << "============================================================================="
2135  "="
2136  << __E__;
2137  out << "Print: " << tableName_ << " Version: " << version_ << " Comment: " << comment_
2138  << " Author: " << author_ << " Creation Time: " << ctime(&creationTime_) << __E__;
2139  out << "\t\tNumber of Cols " << getNumberOfColumns() << __E__;
2140  out << "\t\tNumber of Rows " << getNumberOfRows() << __E__;
2141 
2142  out << "Columns:\t";
2143  for(int i = 0; i < (int)columnsInfo_.size(); ++i)
2144  out << i << ":" << columnsInfo_[i].getName() << ":"
2145  << columnsInfo_[i].getStorageName() << ":" << columnsInfo_[i].getType() << ":"
2146  << columnsInfo_[i].getDataType() << "\t ";
2147  out << __E__;
2148 
2149  out << "Rows:" << __E__;
2150  // int num;
2151  std::string val;
2152  for(int r = 0; r < (int)getNumberOfRows(); ++r)
2153  {
2154  out << (int)r << ":\t";
2155  for(int c = 0; c < (int)getNumberOfColumns(); ++c)
2156  {
2157  out << (int)c << ":";
2158 
2159  // if fixed choice type, print index in choice
2160  if(columnsInfo_[c].getType() == TableViewColumnInfo::TYPE_FIXED_CHOICE_DATA)
2161  {
2162  int choiceIndex = -1;
2163  std::vector<std::string> choices = columnsInfo_[c].getDataChoices();
2164  val = StringMacros::convertEnvironmentVariables(theDataView_[r][c]);
2165 
2166  if(val == columnsInfo_[c].getDefaultValue())
2167  choiceIndex = 0;
2168  else
2169  {
2170  for(int i = 0; i < (int)choices.size(); ++i)
2171  if(val == choices[i])
2172  choiceIndex = i + 1;
2173  }
2174 
2175  out << "ChoiceIndex=" << choiceIndex << ":";
2176  }
2177 
2178  out << theDataView_[r][c];
2179  // stopped using below, because it is called sometimes during debugging when
2180  // numbers are set to environment variables:
2181  // if(columnsInfo_[c].getDataType() == "NUMBER")
2182  // {
2183  // getValue(num,r,c,false);
2184  // out << num;
2185  // }
2186  // else
2187  // {
2188  // getValue(val,r,c,false);
2189  // out << val;
2190  // }
2191  out << "\t\t";
2192  }
2193  out << __E__;
2194  }
2195 } // end print()
2196 
2197 //==============================================================================
2198 void TableView::printJSON(std::ostream& out /* = std::cout */) const
2199 {
2200  { //handle special GROUP CACHE table
2201  std::string tmpCachePrepend = TableBase::GROUP_CACHE_PREPEND;
2202  tmpCachePrepend = TableBase::convertToCaps(tmpCachePrepend);
2203  std::string tmpJsonDocPrepend = TableBase::JSON_DOC_PREPEND;
2204  tmpJsonDocPrepend = TableBase::convertToCaps(tmpJsonDocPrepend);
2205  __COUTS__(32) << " '" << tableName_ << "' vs " << tmpCachePrepend << " or "
2206  << tmpJsonDocPrepend << __E__;
2207  //if special GROUP CACHE table, handle construction in a special way
2208  if(tableName_.substr(0, tmpCachePrepend.length()) == tmpCachePrepend ||
2209  tableName_.substr(0, tmpJsonDocPrepend.length()) == tmpJsonDocPrepend)
2210  {
2211  out << getCustomStorageData();
2212  return;
2213  } //end special GROUP CACHE table construction
2214  } //end handle special GROUP CACHE table
2215 
2216  out << "{\n";
2217  out << "\"NAME\" : \"" << tableName_ << "\",\n";
2218 
2219  // out << "\"VERSION\": \"" << version_ << "\",\n";
2220 
2221  out << "\"COMMENT\" : ";
2222 
2223  // output escaped comment
2224  std::string val;
2225  val = comment_;
2226  out << "\"";
2227  for(unsigned int i = 0; i < val.size(); ++i)
2228  {
2229  if(val[i] == '\n')
2230  out << "\\n";
2231  else if(val[i] == '\t')
2232  out << "\\t";
2233  else if(val[i] == '\r')
2234  out << "\\r";
2235  else
2236  {
2237  // escaped characters need a
2238  if(val[i] == '"' || val[i] == '\\')
2239  out << '\\';
2240  out << val[i];
2241  }
2242  }
2243  out << "\",\n";
2244 
2245  out << "\"AUTHOR\" : \"" << author_ << "\",\n";
2246  out << "\"CREATION_TIME\" : " << creationTime_ << ",\n";
2247 
2248  // USELESS... out << "\"NUM_OF_COLS\" : " << getNumberOfColumns() << ",\n";
2249  // USELESS... out << "\"NUM_OF_ROWS\" : " << getNumberOfRows() << ",\n";
2250 
2251  out << "\"COL_TYPES\" : {\n";
2252  for(int c = 0; c < (int)getNumberOfColumns(); ++c)
2253  {
2254  out << "\t\t\"" << columnsInfo_[c].getStorageName() << "\" : ";
2255  out << "\"" << columnsInfo_[c].getDataType() << "\"";
2256  if(c + 1 < (int)getNumberOfColumns())
2257  out << ",";
2258  out << "\n";
2259  }
2260  out << "},\n"; // close COL_TYPES
2261 
2262  out << "\"DATA_SET\" : [\n";
2263  // int num;
2264  for(int r = 0; r < (int)getNumberOfRows(); ++r)
2265  {
2266  out << "\t{\n";
2267  for(int c = 0; c < (int)getNumberOfColumns(); ++c)
2268  {
2269  out << "\t\t\"" << columnsInfo_[c].getStorageName() << "\" : ";
2270 
2271  out << "\"" << getEscapedValueAsString(r, c, false)
2272  << "\""; // do not convert env variables
2273 
2274  if(c + 1 < (int)getNumberOfColumns())
2275  out << ",";
2276  out << "\n";
2277  }
2278  out << "\t}";
2279  if(r + 1 < (int)getNumberOfRows())
2280  out << ",";
2281  out << "\n";
2282  }
2283  out << "]\n"; // close DATA_SET
2284 
2285  out << "}";
2286 } // end printJSON()
2287 
2288 //==============================================================================
2289 void TableView::printCSV(std::ostream& out /* = std::cout */,
2290  const std::string& valueDelimeter /* = "," */,
2291  const std::string& recordDelimeter /* = "\n" */,
2292  bool includeColumnNames /* = false */) const
2293 {
2294  { //handle special GROUP CACHE table
2295  std::string tmpCachePrepend = TableBase::GROUP_CACHE_PREPEND;
2296  tmpCachePrepend = TableBase::convertToCaps(tmpCachePrepend);
2297  std::string tmpJsonDocPrepend = TableBase::JSON_DOC_PREPEND;
2298  tmpJsonDocPrepend = TableBase::convertToCaps(tmpJsonDocPrepend);
2299  __COUTS__(32) << " '" << tableName_ << "' vs " << tmpCachePrepend << " or "
2300  << tmpJsonDocPrepend << __E__;
2301  //if special GROUP CACHE table, handle construction in a special way
2302  if(tableName_.substr(0, tmpCachePrepend.length()) == tmpCachePrepend ||
2303  tableName_.substr(0, tmpJsonDocPrepend.length()) == tmpJsonDocPrepend)
2304  {
2305  __SS__ << "Cannot convert custom storage data to CSV!" << __E__;
2306  __SS_THROW__;
2307  // out << getCustomStorageData();
2308  // return;
2309  } //end special GROUP CACHE table construction
2310  } //end handle special GROUP CACHE table
2311 
2312  for(int c = 0; includeColumnNames && c < (int)getNumberOfColumns(); ++c)
2313  {
2314  if(c)
2315  out << valueDelimeter;
2316  out << "\"" << columnsInfo_[c].getStorageName() << "\"";
2317  }
2318  if(includeColumnNames)
2319  out << recordDelimeter;
2320 
2321  for(int r = 0; r < (int)getNumberOfRows(); ++r)
2322  {
2323  for(int c = 0; c < (int)getNumberOfColumns(); ++c)
2324  {
2325  if(c)
2326  out << valueDelimeter;
2327  out << "\""
2328  << getEscapedValueAsString(r, c, false, true /* quotesToDoubleQuotes*/)
2329  << "\""; // do not convert env variables, convert " to "" for excel style
2330  }
2331  out << recordDelimeter;
2332  }
2333 
2334 } // end printCSV()
2335 
2336 //==============================================================================
2344 int TableView::fillFromJSON(const std::string& json)
2345 {
2346  { //handle special GROUP CACHE table
2347  std::string tmpCachePrepend = TableBase::GROUP_CACHE_PREPEND;
2348  tmpCachePrepend = TableBase::convertToCaps(tmpCachePrepend);
2349  std::string tmpJsonDocPrepend = TableBase::JSON_DOC_PREPEND;
2350  tmpJsonDocPrepend = TableBase::convertToCaps(tmpJsonDocPrepend);
2351 
2352  //if special JSON DOC table, handle construction in a special way
2353  if(tableName_.substr(0, tmpJsonDocPrepend.length()) == tmpJsonDocPrepend ||
2354  tableName_.substr(0, tmpCachePrepend.length()) == tmpCachePrepend)
2355  {
2356  __COUTS__(3) << "Special JSON doc: " << json << __E__;
2357  setCustomStorageData(json);
2358  return 0; //success
2359  } //end special JSON DOC table construction or special GROUP CACHE table construction
2360  } //end handle special GROUP CACHE table
2361 
2362  bool dbg = false; //tableName_ == "TABLE_GROUP_METADATA";
2363  bool rawData = getSourceRawData_;
2364  if(getSourceRawData_)
2365  { // only get source raw data once, then revert member variable
2366  __COUTV__(getSourceRawData_);
2367  getSourceRawData_ = false;
2368  sourceRawData_ = ""; // clear for this fill
2369  }
2370 
2371  std::map<std::string /*key*/, unsigned int /*entries/rows*/> keyEntryCountMap;
2372  std::vector<std::string> keys;
2373  keys.push_back("NAME");
2374  keys.push_back("COMMENT");
2375  keys.push_back("AUTHOR");
2376  keys.push_back("CREATION_TIME");
2377  // keys.push_back ("COL_TYPES");
2378  keys.push_back("DATA_SET");
2379  enum
2380  {
2381  CV_JSON_FILL_NAME,
2382  CV_JSON_FILL_COMMENT,
2383  CV_JSON_FILL_AUTHOR,
2384  CV_JSON_FILL_CREATION_TIME,
2385  // CV_JSON_FILL_COL_TYPES,
2386  CV_JSON_FILL_DATA_SET
2387  };
2388 
2389  if(dbg)
2390  {
2391  __COUTV__(tableName_);
2392  __COUTTV__(getNumberOfRows());
2393  __COUTV__(json);
2394  }
2395 
2396  sourceColumnMismatchCount_ = 0;
2397  sourceColumnMissingCount_ = 0;
2398  sourceColumnNames_.clear(); // reset
2399  unsigned int colFoundCount = 0;
2400  unsigned int i = 0;
2401  unsigned int row = -1;
2402  unsigned int colSpeedup = 0;
2403  unsigned int startString, startNumber = 0, endNumber = -1;
2404  unsigned int bracketCount = 0;
2405  unsigned int sqBracketCount = 0;
2406  bool inQuotes = 0;
2407  bool newString = 0;
2408  bool newValue = 0;
2409  // bool isDataArray = 0;
2410  bool keyIsMatch, keyIsComment;
2411  unsigned int keyIsMatchIndex, keyIsMatchStorageIndex, keyIsMatchCommentIndex;
2412  const std::string COMMENT_ALT_KEY = "COMMENT";
2413 
2414  std::string extractedString = "", currKey = "", currVal = "";
2415  unsigned int currDepth = 0;
2416 
2417  std::vector<std::string> jsonPath;
2418  std::vector<char> jsonPathType; // indicator of type in jsonPath: { [ K
2419  char lastPopType = '_'; // either: _ { [ K
2420  // _ indicates reset pop (this happens when a new {obj} starts)
2421  unsigned int matchedKey = -1;
2422  unsigned int lastCol = -1;
2423 
2424  // find all depth 1 matching keys
2425  for(; i < json.size(); ++i)
2426  {
2427  switch(json[i])
2428  {
2429  case '"':
2430  if(i - 1 < json.size() && // ignore if escaped
2431  json[i - 1] == '\\')
2432  break;
2433 
2434  inQuotes = !inQuotes; // toggle in quotes if not escaped
2435  if(inQuotes)
2436  startString = i;
2437  else
2438  {
2439  extractedString = StringMacros::restoreJSONStringEntities(
2440  json.substr(startString + 1, i - startString - 1));
2441  newString = 1; // have new string!
2442  }
2443  break;
2444  case ':':
2445  if(inQuotes)
2446  break; // skip if in quote
2447 
2448  // must be a json object level to have a key
2449  if(jsonPathType[jsonPathType.size() - 1] != '{' ||
2450  !newString) // and must have a string for key
2451  {
2452  __COUT__ << "Invalid ':' position" << __E__;
2453  return -1;
2454  }
2455 
2456  // valid, so take key
2457  jsonPathType.push_back('K');
2458  jsonPath.push_back(extractedString);
2459  startNumber = i;
2460  newString = 0; // clear flag
2461  endNumber = -1; // reset end number index
2462  break;
2463 
2464  // if(isKey ||
2465  // isDataArray)
2466  // {
2467  // std::cout << "Invalid ':' position" << __E__;
2468  // return -1;
2469  // }
2470  // isKey = 1; //new value is a key
2471  // newValue = 1;
2472  // startNumber = i;
2473  // break;
2474  case ',':
2475  if(inQuotes)
2476  break; // skip if in quote
2477  if(lastPopType == '{') // don't need value again of nested object
2478  {
2479  // check if the nested object was the value to a key, if so, pop key
2480  if(jsonPathType[jsonPathType.size() - 1] == 'K')
2481  {
2482  lastPopType = 'K';
2483  jsonPath.pop_back();
2484  jsonPathType.pop_back();
2485  }
2486  break; // skip , handling if {obj} just ended
2487  }
2488 
2489  if(newString)
2490  currVal = extractedString;
2491  else // number value
2492  {
2493  if(endNumber == (unsigned int)-1 || // take i as end number if needed
2494  endNumber <= startNumber)
2495  endNumber = i;
2496  // extract number value
2497  if(endNumber <= startNumber) // empty data, could be {}
2498  currVal = "";
2499  else
2500  currVal = json.substr(startNumber + 1, endNumber - startNumber - 1);
2501  }
2502 
2503  currDepth = bracketCount;
2504 
2505  if(jsonPathType[jsonPathType.size() - 1] == 'K') // this is the value to key
2506  {
2507  currKey = jsonPath[jsonPathType.size() - 1];
2508  newValue = 1; // new value to consider!
2509 
2510  // pop key
2511  lastPopType = 'K';
2512  jsonPath.pop_back();
2513  jsonPathType.pop_back();
2514  }
2515  else if(jsonPathType[jsonPathType.size() - 1] ==
2516  '[') // this is a value in array
2517  {
2518  // key is last key
2519  for(unsigned int k = jsonPathType.size() - 2; k < jsonPathType.size();
2520  --k)
2521  if(jsonPathType[k] == 'K')
2522  {
2523  currKey = jsonPath[k];
2524  break;
2525  }
2526  else if(k == 0)
2527  {
2528  __COUT__ << "Invalid array position" << __E__;
2529  return -1;
2530  }
2531 
2532  newValue = 1; // new value to consider!
2533  // isDataArray = 1;
2534  }
2535  else // { is an error
2536  {
2537  __COUT__ << "Invalid ',' position" << __E__;
2538  return -1;
2539  }
2540 
2541  startNumber = i;
2542  break;
2543 
2544  case '{':
2545  if(inQuotes)
2546  break; // skip if in quote
2547  lastPopType = '_'; // reset because of new object
2548  jsonPathType.push_back('{');
2549  jsonPath.push_back("{");
2550  ++bracketCount;
2551  break;
2552 
2553  // ++bracketCount;
2554  // isDataArray = 0;
2555  // isKey = 0;
2556  // endingObject = 0;
2557  // break;
2558  case '}':
2559  if(inQuotes)
2560  break; // skip if in quote
2561 
2562  if(lastPopType != '{' && // don't need value again of nested object
2563  jsonPathType[jsonPathType.size() - 1] == 'K') // this is the value to key
2564  {
2565  currDepth = bracketCount;
2566  currKey = jsonPath[jsonPathType.size() - 1];
2567  if(newString)
2568  currVal = extractedString;
2569  else // number value
2570  {
2571  if(endNumber == (unsigned int)-1 || // take i as end number if needed
2572  endNumber <= startNumber)
2573  endNumber = i;
2574  // extract val
2575  if(endNumber <= startNumber) // empty data, could be {}
2576  currVal = "";
2577  else
2578  currVal =
2579  json.substr(startNumber + 1, endNumber - startNumber - 1);
2580  }
2581  newValue = 1; // new value to consider!
2582  // pop key
2583  jsonPath.pop_back();
2584  jsonPathType.pop_back();
2585  }
2586  // pop {
2587  if(jsonPathType[jsonPathType.size() - 1] != '{')
2588  {
2589  __COUT__ << "Invalid '}' position" << __E__;
2590  return -1;
2591  }
2592  lastPopType = '{';
2593  jsonPath.pop_back();
2594  jsonPathType.pop_back();
2595  --bracketCount;
2596  break;
2597  case '[':
2598  if(inQuotes)
2599  break; // skip if in quote
2600  jsonPathType.push_back('[');
2601  jsonPath.push_back("[");
2602  ++sqBracketCount;
2603  startNumber = i;
2604  break;
2605  case ']':
2606  if(inQuotes)
2607  break; // skip if in quote
2608 
2609  // must be an array at this level (in order to close it)
2610  if(jsonPathType[jsonPathType.size() - 1] != '[')
2611  {
2612  __COUT__ << "Invalid ']' position" << __E__;
2613  return -1;
2614  }
2615 
2616  currDepth = bracketCount;
2617 
2618  // This is an array value
2619  if(newString)
2620  currVal = extractedString;
2621  else // number value
2622  {
2623  if(endNumber == (unsigned int)-1 || // take i as end number if needed
2624  endNumber <= startNumber)
2625  endNumber = i;
2626  // extract val
2627  if(endNumber <= startNumber) // empty data, could be {}
2628  currVal = "";
2629  else
2630  currVal = json.substr(startNumber + 1, endNumber - startNumber - 1);
2631  }
2632  // isDataArray = 1;
2633 
2634  // key is last key
2635  for(unsigned int k = jsonPathType.size() - 2; k < jsonPathType.size(); --k)
2636  if(jsonPathType[k] == 'K')
2637  {
2638  currKey = jsonPath[k];
2639  break;
2640  }
2641  else if(k == 0)
2642  {
2643  __COUT__ << "Invalid array position" << __E__;
2644  return -1;
2645  }
2646 
2647  // pop [
2648  if(jsonPathType[jsonPathType.size() - 1] != '[')
2649  {
2650  __COUT__ << "Invalid ']' position" << __E__;
2651  return -1;
2652  }
2653  lastPopType = '[';
2654  jsonPath.pop_back();
2655  jsonPathType.pop_back();
2656  --sqBracketCount;
2657  break;
2658  case ' ': // white space handling for numbers
2659  case '\t':
2660  case '\n':
2661  case '\r':
2662  if(inQuotes)
2663  break; // skip if in quote
2664  if(startNumber != (unsigned int)-1 && endNumber == (unsigned int)-1)
2665  endNumber = i;
2666  startNumber = i;
2667  break;
2668  default:;
2669  }
2670 
2671  // continue;
2672 
2673  // handle a new completed value
2674  if(newValue)
2675  {
2676  if(dbg) // for debugging
2677  {
2678  std::cout << i << ":\t" << json[i] << " - ";
2679 
2680  // if(isDataArray)
2681  // std::cout << "Array:: ";
2682  // if(newString)
2683  // std::cout << "New String:: ";
2684  // else
2685  // std::cout << "New Number:: ";
2686  //
2687 
2688  std::cout << "ExtKey=";
2689  for(unsigned int k = 0; k < jsonPath.size(); ++k)
2690  std::cout << jsonPath[k] << "/";
2691  std::cout << " - ";
2692  std::cout << lastPopType << " ";
2693  std::cout << bracketCount << " ";
2694  std::cout << sqBracketCount << " ";
2695  std::cout << inQuotes << " ";
2696  std::cout << newValue << "-";
2697  std::cout << currKey << "-{" << currDepth << "}:";
2698  std::cout << currVal << " ";
2699  std::cout << startNumber << "-";
2700  std::cout << endNumber << " ";
2701  std::cout << "\n";
2702  __COUTTV__(fillWithLooseColumnMatching_);
2703  __COUTTV__(getNumberOfRows());
2704  }
2705 
2706  // extract only what we care about
2707  // for TableView only care about matching depth 1
2708 
2709  // handle matching depth 1 keys
2710 
2711  matchedKey = -1; // init to unfound
2712  for(unsigned int k = 0; k < keys.size(); ++k)
2713  if((currDepth == 1 && keys[k] == currKey) ||
2714  (currDepth > 1 && keys[k] == jsonPath[1]))
2715  matchedKey = k;
2716 
2717  if(rawData)
2718  {
2719  // raw data handling fills raw data string with row/col values
2720 
2721  if(currDepth == 1)
2722  {
2723  if(matchedKey == CV_JSON_FILL_COMMENT)
2724  setComment(currVal);
2725  else if(matchedKey == CV_JSON_FILL_AUTHOR)
2726  setAuthor(currVal);
2727  else if(matchedKey == CV_JSON_FILL_CREATION_TIME)
2728  setCreationTime(strtol(currVal.c_str(), 0, 10));
2729  }
2730  else if(currDepth == 2)
2731  {
2732  // encode URI component so commas are surviving delimiter
2733  sourceRawData_ += StringMacros::encodeURIComponent(currKey) + "," +
2734  StringMacros::encodeURIComponent(currVal) + ",";
2735  sourceColumnNames_.emplace(currKey);
2736  }
2737  }
2738  else if(matchedKey != (unsigned int)-1)
2739  {
2740  if(dbg)
2741  __COUTT__ << "New Data for:: key[" << matchedKey << "]-"
2742  << keys[matchedKey] << "\n";
2743 
2744  switch(matchedKey)
2745  {
2746  case CV_JSON_FILL_NAME:
2747  // table name is now constant, set by parent TableBase
2748  if(currDepth == 1)
2749  {
2750  // setTableName(currVal);
2751  // check for consistency, and show warning
2752  if(currVal != getTableName() &&
2753  getTableName() !=
2754  "TABLE_GROUP_METADATA") // allow metadata table to be illegal, since it is created by ConfigurationManager.cc
2755  __COUT_WARN__ << "JSON-fill Table name mismatch: " << currVal
2756  << " vs " << getTableName() << __E__;
2757  }
2758  break;
2759  case CV_JSON_FILL_COMMENT:
2760  if(currDepth == 1)
2761  setComment(currVal);
2762  break;
2763  case CV_JSON_FILL_AUTHOR:
2764  if(currDepth == 1)
2765  setAuthor(currVal);
2766  break;
2767  case CV_JSON_FILL_CREATION_TIME:
2768  if(currDepth == 1)
2769  setCreationTime(strtol(currVal.c_str(), 0, 10));
2770  break;
2771  // case CV_JSON_FILL_COL_TYPES:
2772  //
2773  // break;
2774  case CV_JSON_FILL_DATA_SET:
2775  if(dbg)
2776  __COUTT__ << "CV_JSON_FILL_DATA_SET New Data for::" << matchedKey
2777  << "]-" << keys[matchedKey] << "/" << currDepth
2778  << ".../" << currKey << "\n";
2779 
2780  if(currDepth == 2) // second level depth
2781  {
2782  // if matches first column name.. then add new row
2783  // else add to current row
2784  unsigned int col, ccnt = 0;
2785  unsigned int noc = getNumberOfColumns();
2786  for(; ccnt < noc; ++ccnt)
2787  {
2788  // use colSpeedup to change the first column we search
2789  // for each iteration.. since we expect the data to
2790  // be arranged in column order
2791 
2792  if(fillWithLooseColumnMatching_)
2793  {
2794  // loose column matching makes no attempt to
2795  // match the column names
2796  // just assumes the data is in the correct order
2797 
2798  col = colSpeedup;
2799 
2800  // auto matched
2801  if(col <= lastCol) // add row (use lastCol in case new
2802  // column-0 was added
2803  row = addRow();
2804  lastCol = col;
2805  if(getNumberOfRows() == 1) // only for first row
2806  sourceColumnNames_.emplace(currKey);
2807 
2808  // add value to row and column
2809 
2810  if(row >= getNumberOfRows())
2811  {
2812  __SS__ << "Invalid row"
2813  << __E__; // should be impossible?
2814  std::cout << ss.str();
2815  __SS_THROW__;
2816  return -1;
2817  }
2818 
2819  theDataView_[row][col] =
2820  currVal; // THERE IS NO CHECK FOR WHAT IS READ FROM
2821  // THE DATABASE. IT SHOULD BE ALREADY
2822  // CONSISTENT
2823  break;
2824  }
2825  else
2826  {
2827  col = (ccnt + colSpeedup) % noc;
2828 
2829  // match key by ignoring '_'
2830  // also accept COMMENT == COMMENT_DESCRIPTION
2831  // (this is for backwards compatibility..)
2832  keyIsMatch = true;
2833  keyIsComment = true;
2834  for(keyIsMatchIndex = 0,
2835  keyIsMatchStorageIndex = 0,
2836  keyIsMatchCommentIndex = 0;
2837  keyIsMatchIndex < currKey.size();
2838  ++keyIsMatchIndex)
2839  {
2840  if(columnsInfo_[col]
2841  .getStorageName()[keyIsMatchStorageIndex] ==
2842  '_')
2843  ++keyIsMatchStorageIndex; // skip to next storage
2844  // character
2845  if(currKey[keyIsMatchIndex] == '_')
2846  continue; // skip to next character
2847 
2848  // match to storage name
2849  if(keyIsMatchStorageIndex >=
2850  columnsInfo_[col].getStorageName().size() ||
2851  currKey[keyIsMatchIndex] !=
2852  columnsInfo_[col]
2853  .getStorageName()[keyIsMatchStorageIndex])
2854  {
2855  // size mismatch or character mismatch
2856  keyIsMatch = false;
2857  if(!keyIsComment)
2858  break;
2859  }
2860 
2861  // check also if alternate comment is matched
2862  if(keyIsComment &&
2863  keyIsMatchCommentIndex < COMMENT_ALT_KEY.size())
2864  {
2865  if(currKey[keyIsMatchIndex] !=
2866  COMMENT_ALT_KEY[keyIsMatchCommentIndex])
2867  {
2868  // character mismatch with COMMENT
2869  keyIsComment = false;
2870  }
2871  }
2872 
2873  ++keyIsMatchStorageIndex; // go to next character
2874  }
2875 
2876  if(dbg)
2877  {
2878  __COUTTV__(keyIsMatch);
2879  __COUTTV__(keyIsComment);
2880  __COUTTV__(currKey);
2881  __COUTTV__(columnsInfo_[col].getStorageName());
2882  __COUTTV__(getNumberOfRows());
2883  }
2884 
2885  if(keyIsMatch || keyIsComment) // currKey ==
2886  // columnsInfo_[c].getStorageName())
2887  {
2888  if(keyEntryCountMap.find(currKey) ==
2889  keyEntryCountMap.end())
2890  keyEntryCountMap[currKey] =
2891  0; // show follow row count
2892  else
2893  ++keyEntryCountMap.at(currKey);
2894 
2895  // add row (based on entry counts)
2896  if(keyEntryCountMap.size() == 1 ||
2897  (keyEntryCountMap.at(currKey) &&
2898  keyEntryCountMap.at(currKey) >
2899  row)) // if(col <= lastCol)
2900  {
2901  if(getNumberOfRows()) // skip first time
2902  sourceColumnMissingCount_ +=
2903  getNumberOfColumns() - colFoundCount;
2904 
2905  colFoundCount = 0; // reset column found count
2906  row = addRow();
2907  }
2908  lastCol = col;
2909  ++colFoundCount;
2910 
2911  if(getNumberOfRows() == 1) // only for first row
2912  sourceColumnNames_.emplace(currKey);
2913 
2914  // add value to row and column
2915 
2916  if(row >= getNumberOfRows())
2917  {
2918  __SS__ << "Invalid row"
2919  << __E__; // should be impossible?!
2920  __COUT__ << "\n" << ss.str();
2921  __SS_THROW__;
2922  return -1; // never gets here
2923  }
2924 
2925  theDataView_[row][col] = currVal;
2926  break;
2927  }
2928  }
2929  }
2930 
2931  if(ccnt >= getNumberOfColumns())
2932  {
2933  __COUT__
2934  << "Invalid column in JSON source data: " << currKey
2935  << " not found in column names of table named "
2936  << getTableName() << "."
2937  << __E__; // input data doesn't match config description
2938 
2939  // CHANGED on 11/10/2016
2940  // to.. try just not populating data instead of error
2941  ++sourceColumnMismatchCount_; // but count errors
2942  if(getNumberOfRows() ==
2943  1) // only for first row, track source column names
2944  sourceColumnNames_.emplace(currKey);
2945 
2946  //__SS_THROW__;
2947  __COUT_WARN__ << "Trying to ignore error, and not populating "
2948  "missing column."
2949  << __E__;
2950  }
2951  else // short cut to proper column hopefully in next search
2952  colSpeedup = (colSpeedup + 1) % noc;
2953  }
2954  break;
2955  default:; // unknown match?
2956  } // end switch statement to match json key
2957  } // end matched key if statement
2958 
2959  // clean up handling of new value
2960 
2961  newString = 0; // toggle flag
2962  newValue = 0; // toggle flag
2963  // isDataArray = 0;
2964  endNumber = -1; // reset end number index
2965  }
2966 
2967  // if(i>200) break; //185
2968  }
2969 
2970  //__COUT__ << "Done!" << __E__;
2971  __COUTTV__(fillWithLooseColumnMatching_);
2972  __COUTTV__(sourceColumnNames_.size());
2973  //__COUTV__(tableName_); // << "tableName_ = " << tableName_
2974 
2975  if(!fillWithLooseColumnMatching_ && sourceColumnMissingCount_ > 0)
2976  {
2977  __COUTV__(sourceColumnMissingCount_);
2978  __SS__ << "Can not ignore errors because not every column was found in the "
2979  "source data!"
2980  << ". Please see the details below:\n\n"
2981  << getMismatchColumnInfo() << StringMacros::stackTrace();
2982  __SS_ONLY_THROW__;
2983  }
2984 
2985  if(sourceColumnNames_.size() ==
2986  0) //if not populated by data (i.e. zero records), then use default column names
2987  {
2988  for(unsigned int i = 0; i < getNumberOfColumns(); ++i)
2989  sourceColumnNames_.emplace(getColumnsInfo()[i].getStorageName());
2990  }
2991 
2992  // print();
2993 
2994  return 0; // success
2995 } // end fillFromJSON()
2996 
2997 //==============================================================================
2998 std::string TableView::getMismatchColumnInfo(void) const
2999 {
3000  const std::set<std::string>& srcColNames = getSourceColumnNames();
3001  std::set<std::string> destColNames = getColumnStorageNames();
3002 
3003  __SS__ << "The source column size was found to be " << srcColNames.size()
3004  << ", and the current number of columns for this table is "
3005  << getNumberOfColumns() << ". This resulted in a count of "
3006  << getSourceColumnMismatch() << " source column mismatches, and a count of "
3007  << getSourceColumnMissing() << " table entries missing in "
3008  << getNumberOfRows() << " row(s) of data." << __E__;
3009 
3010  ss << "\n\n"
3011  << srcColNames.size()
3012  << " Source column names in ALPHABETICAL order were as follows:\n";
3013  char index = 'a';
3014  std::string preIndexStr = "";
3015  for(auto& srcColName : srcColNames)
3016  {
3017  if(destColNames.find(srcColName) == destColNames.end())
3018  ss << "\n\t*** " << preIndexStr << index << ". " << srcColName << " ***";
3019  else
3020  ss << "\n\t" << preIndexStr << index << ". " << srcColName;
3021 
3022  if(index == 'z') // wrap-around
3023  {
3024  preIndexStr += 'a'; // keep adding index 'digits' for wrap-around
3025  index = 'a';
3026  }
3027  else
3028  ++index;
3029  }
3030  ss << __E__;
3031 
3032  ss << "\n\n"
3033  << destColNames.size()
3034  << " Current table column names in ALPHABETICAL order are as follows:\n";
3035  index = 'a';
3036  preIndexStr = "";
3037  for(auto& destColName : destColNames)
3038  {
3039  if(srcColNames.find(destColName) == srcColNames.end())
3040  ss << "\n\t*** " << preIndexStr << index << ". " << destColName << " ***";
3041  else
3042  ss << "\n\t" << preIndexStr << index << ". " << destColName;
3043 
3044  if(index == 'z') // wrap-around
3045  {
3046  preIndexStr += 'a'; // keep adding index 'digits' for wrap-around
3047  index = 'a';
3048  }
3049  else
3050  ++index;
3051  }
3052  ss << __E__;
3053  return ss.str();
3054 } // end getMismatchColumnInfo()
3055 
3056 //==============================================================================
3057 bool TableView::isURIEncodedCommentTheSame(const std::string& comment) const
3058 {
3059  std::string compareStr = StringMacros::decodeURIComponent(comment);
3060  return comment_ == compareStr;
3061 }
3066 //{
3067 // __COUT__ << "valueStr " << valueStr << __E__;
3068 //
3069 // if(!(c < columnsInfo_.size() && r < getNumberOfRows()))
3070 // {
3071 // __SS__ << "Invalid row (" << (int)r << ") col (" << (int)c << ") requested!" <<
3072 //__E__;
3073 // __SS_THROW__;
3074 // }
3075 //
3076 // __COUT__ << "originalValueStr " << theDataView_[r][c] << __E__;
3077 //
3078 // if(columnsInfo_[c].getDataType() == TableViewColumnInfo::DATATYPE_TIME)
3079 // {
3080 // time_t valueTime(strtol(valueStr.c_str(),0,10));
3081 // time_t originalValueTime;
3082 // getValue(originalValueTime,r,c);
3083 // __COUT__ << "time_t valueStr " << valueTime << __E__;
3084 // __COUT__ << "time_t originalValueStr " << originalValueTime << __E__;
3085 // return valueTime == originalValueTime;
3086 // }
3087 // else
3088 // {
3089 // return valueStr == theDataView_[r][c];
3090 // }
3091 //}
3092 
3093 //==============================================================================
3104 void TableView::fillFromCSV(const std::string& data,
3105  const int& dataOffset /* = 0 */,
3106  const std::string& author /* = "" */,
3107  const char rowDelimter /* = ',' */,
3108  const char colDelimter /* = '\n' */)
3109 {
3110  int row = dataOffset;
3111  int col = 0;
3112  std::string currentValue = "";
3113  bool insideQuotes = false;
3114  int authorCol = findColByType(TableViewColumnInfo::TYPE_AUTHOR);
3115  int timestampCol = findColByType(TableViewColumnInfo::TYPE_TIMESTAMP);
3116 
3117  for(size_t i = 0; i < data.size(); ++i)
3118  {
3119  char c = data[i];
3120  const char nextChar = (i + 1 < data.size() ? data[i + 1] : ' ');
3121 
3122  if(c == '"')
3123  {
3124  if(insideQuotes && nextChar == '"') // "" will escape a double-quote in CSV
3125  {
3126  // Escaped double-quote
3127  currentValue += '"';
3128  ++i; //skip next quote
3129  }
3130  else
3131  {
3132  // Toggle quote mode
3133  insideQuotes = !insideQuotes;
3134  }
3135  }
3136  else if(c == rowDelimter && !insideQuotes)
3137  {
3138  if(col == 0 && row >= (int)getNumberOfRows())
3139  addRow(author);
3140  setValueAsString(StringMacros::trim(currentValue), row, col);
3141  ++col;
3142  currentValue = "";
3143  }
3144  else if((c == colDelimter || c == '\r') && !insideQuotes)
3145  {
3146  if(col > 0)
3147  {
3148  setValueAsString(StringMacros::trim(currentValue), row, col);
3149  __COUTV__(getValueAsString(row, col));
3150 
3151  //if row is actually column names, then delete the row
3152  if(getValueAsString(row, col) == getColumnsInfo()[col].getStorageName())
3153  {
3154  __COUT__ << "First row detected as column names." << __E__;
3155  deleteRow(row);
3156  --row; //rewind
3157  }
3158  else
3159  {
3160  //enforce author and timestamp not from CSV data
3161  setValue(author, row, authorCol);
3162  setValue(time(0), row, timestampCol);
3163  }
3164 
3165  col = 0;
3166  ++row; //prepare for next row
3167  currentValue = "";
3168  }
3169  }
3170  else
3171  {
3172  currentValue += c;
3173  }
3174  } //end text loop
3175 
3176  // Add last value if any
3177  if(col > 0)
3178  {
3179  setValueAsString(StringMacros::trim(currentValue), row, col);
3180  __COUTV__(getValueAsString(row, col));
3181  __COUTV__(getValueAsString(row, timestampCol));
3182 
3183  //if row is actually column names, then delete the row
3184  if(getValueAsString(row, col) == getColumnsInfo()[col].getStorageName())
3185  {
3186  __COUT__ << "First row detected as column names." << __E__;
3187  deleteRow(row);
3188  --row; //rewind
3189  }
3190  else
3191  {
3192  //enforce author and timestamp not from CSV data
3193  setValue(author, row, authorCol);
3194  setValue(time(0), row, timestampCol);
3195  }
3196 
3197  col = 0;
3198  ++row; //prepare for next row
3199  currentValue = "";
3200  }
3201 
3202  init(); // verify new table (throws runtime_errors)
3203 
3204 } //end fillFromCSV
3205 
3206 //==============================================================================
3232 int TableView::fillFromEncodedCSV(const std::string& data,
3233  const int& dataOffset,
3234  const std::string& author)
3235 {
3236  int retVal = 0;
3237 
3238  int r = dataOffset;
3239  int c = 0;
3240 
3241  int i = 0; // use to parse data std::string
3242  int j = data.find(',', i); // find next cell delimiter
3243  int k = data.find(';', i); // find next row delimiter
3244 
3245  bool rowWasModified;
3246  unsigned int countRowsModified = 0;
3247  int authorCol = findColByType(TableViewColumnInfo::TYPE_AUTHOR);
3248  int timestampCol = findColByType(TableViewColumnInfo::TYPE_TIMESTAMP);
3249  // std::string valueStr, tmpTimeStr, originalValueStr;
3250 
3251  while(k != (int)(std::string::npos))
3252  {
3253  rowWasModified = false;
3254  if(r >= (int)getNumberOfRows())
3255  {
3256  addRow();
3257  //__COUT__ << "Row added" << __E__;
3258  rowWasModified = true;
3259  }
3260 
3261  while(j < k && j != (int)(std::string::npos))
3262  {
3263  //__COUT__ << "Col " << (int)c << __E__;
3264 
3265  // skip last 2 columns
3266  if(c >= (int)getNumberOfColumns() - 2)
3267  {
3268  i = j + 1;
3269  j = data.find(',', i); // find next cell delimiter
3270  ++c;
3271  continue;
3272  }
3273 
3274  if(setURIEncodedValue(data.substr(i, j - i), r, c))
3275  rowWasModified = true;
3276 
3277  i = j + 1;
3278  j = data.find(',', i); // find next cell delimiter
3279  ++c;
3280  }
3281 
3282  // if row was modified, assign author and timestamp
3283  if(author != "" && rowWasModified)
3284  {
3285  __COUTT__ << "Row=" << (int)r << " was modified!" << __E__;
3286  setValue(author, r, authorCol);
3287  setValue(time(0), r, timestampCol);
3288  }
3289 
3290  if(rowWasModified)
3291  ++countRowsModified;
3292 
3293  ++r;
3294  c = 0;
3295 
3296  i = k + 1;
3297  j = data.find(',', i); // find next cell delimiter
3298  k = data.find(';', i); // find new row delimiter
3299  }
3300 
3301  // delete excess rows
3302  while(r < (int)getNumberOfRows())
3303  {
3304  deleteRow(r);
3305  __COUT__ << "Row deleted: " << (int)r << __E__;
3306  ++countRowsModified;
3307  }
3308 
3309  __COUT_INFO__ << "countRowsModified=" << countRowsModified << __E__;
3310 
3311  if(!countRowsModified)
3312  {
3313  // check that source columns match storage name
3314  // otherwise allow same data...
3315 
3316  bool match = getColumnStorageNames().size() == getSourceColumnNames().size();
3317  if(match)
3318  {
3319  for(auto& destColName : getColumnStorageNames())
3320  if(getSourceColumnNames().find(destColName) ==
3321  getSourceColumnNames().end())
3322  {
3323  __COUT__ << "Found column name mismach for '" << destColName
3324  << "'... So allowing same data!" << __E__;
3325 
3326  match = false;
3327  break;
3328  }
3329  }
3330  // if still a match, do not allow!
3331  if(match)
3332  {
3333  __SS__ << "No rows were modified! No reason to fill a view with same content."
3334  << __E__;
3335  __COUT__ << "\n" << ss.str();
3336  return -1;
3337  }
3338  // else mark with retVal
3339  retVal = 1;
3340  } // end same check
3341 
3342  // print(); //for debugging
3343 
3344  // setup sourceColumnNames_ to be correct
3345  sourceColumnNames_.clear();
3346  for(unsigned int i = 0; i < getNumberOfColumns(); ++i)
3347  sourceColumnNames_.emplace(getColumnsInfo()[i].getStorageName());
3348 
3349  init(); // verify new table (throws runtime_errors)
3350 
3351  // printout for debugging
3352  // __SS__ << "\n";
3353  // print(ss);
3354  // __COUT__ << "\n" << ss.str() << __E__;
3355 
3356  return retVal;
3357 } // end fillFromEncodedCSV()
3358 
3359 //==============================================================================
3368 bool TableView::setURIEncodedValue(const std::string& value,
3369  const unsigned int& r,
3370  const unsigned int& c,
3371  const std::string& author)
3372 {
3373  if(!(c < columnsInfo_.size() && r < getNumberOfRows()))
3374  {
3375  __SS__ << "Invalid row (" << (int)r << ") col (" << (int)c << ") requested!"
3376  << "Number of Rows = " << getNumberOfRows()
3377  << "Number of Columns = " << columnsInfo_.size() << __E__;
3378  print(ss);
3379  __SS_THROW__;
3380  }
3381 
3382  std::string valueStr = StringMacros::decodeURIComponent(value);
3383  std::string originalValueStr =
3384  getValueAsString(r, c, false); // do not convert env variables
3385 
3386  //__COUT__ << "valueStr " << valueStr << __E__;
3387  //__COUT__ << "originalValueStr " << originalValueStr << __E__;
3388 
3389  if(columnsInfo_[c].getDataType() == TableViewColumnInfo::DATATYPE_NUMBER)
3390  {
3391  // check if valid number
3392  std::string convertedString = StringMacros::convertEnvironmentVariables(valueStr);
3393  // do not check here, let init check
3394  // if this is a link to valid number, then this is an improper check.
3395  // if(!StringMacros::isNumber(convertedString))
3396  // {
3397  // __SS__ << "\tIn configuration " << tableName_
3398  // << " at column=" << columnsInfo_[c].getName() << " the value
3399  // set
3400  //("
3401  // << convertedString << ")"
3402  // << " is not a number! Please fix it or change the column
3403  // type..."
3404  // << __E__;
3405  // __SS_THROW__;
3406  // }
3407  theDataView_[r][c] = valueStr;
3408 
3409  // is it here that a new exception should be added to enforce min and max, given that they only appear with number type?
3410  }
3411  else if(columnsInfo_[c].getDataType() == TableViewColumnInfo::DATATYPE_TIME)
3412  {
3413  // valueStr = StringMacros::decodeURIComponent(data.substr(i,j-i));
3414  //
3415  // getValue(tmpTimeStr,r,c);
3416  // if(valueStr != tmpTimeStr)//theDataView_[r][c])
3417  // {
3418  // __COUT__ << "valueStr=" << valueStr <<
3419  // " theDataView_[r][c]=" << tmpTimeStr << __E__;
3420  // rowWasModified = true;
3421  // }
3422 
3423  setValue(time_t(strtol(valueStr.c_str(), 0, 10)), r, c);
3424  }
3425  else
3426  theDataView_[r][c] = valueStr;
3427 
3428  bool rowWasModified =
3429  (originalValueStr !=
3430  getValueAsString(r, c, false)); // do not convert env variables
3431 
3432  // if row was modified, assign author and timestamp
3433  if(author != "" && rowWasModified)
3434  {
3435  __COUT__ << "Row=" << (int)r << " was modified!" << __E__;
3436  int authorCol = findColByType(TableViewColumnInfo::TYPE_AUTHOR);
3437  int timestampCol = findColByType(TableViewColumnInfo::TYPE_TIMESTAMP);
3438  setValue(author, r, authorCol);
3439  setValue(time(0), r, timestampCol);
3440  }
3441 
3442  return rowWasModified;
3443 } // end setURIEncodedValue()
3444 
3445 //==============================================================================
3446 void TableView::resizeDataView(unsigned int nRows, unsigned int nCols)
3447 {
3448  // FIXME This maybe should disappear but I am using it in ConfigurationHandler
3449  // still...
3450  theDataView_.resize(nRows, std::vector<std::string>(nCols));
3451 }
3452 
3453 //==============================================================================
3460 unsigned int TableView::addRow(
3461  const std::string& author,
3462  unsigned char
3463  incrementUniqueData /* = false */, // leave as unsigned char rather than
3464  // bool, too many things (e.g. strings)
3465  // evaluate successfully to bool values
3466  const std::string& baseNameAutoUID /* = "" */,
3467  unsigned int rowToAdd /* = -1 */,
3468  std::string childLinkIndex /* = "" */,
3469  std::string groupId /* = "" */)
3470 {
3471  // default to last row
3472  if(rowToAdd == (unsigned int)-1)
3473  rowToAdd = getNumberOfRows();
3474 
3475  theDataView_.resize(getNumberOfRows() + 1,
3476  std::vector<std::string>(getNumberOfColumns()));
3477 
3478  // shift data down the table if necessary
3479  for(unsigned int r = getNumberOfRows() - 2; r >= rowToAdd; --r)
3480  {
3481  if(r == (unsigned int)-1)
3482  break; // quit wrap around case
3483  for(unsigned int col = 0; col < getNumberOfColumns(); ++col)
3484  theDataView_[r + 1][col] = theDataView_[r][col];
3485  }
3486 
3487  std::vector<std::string> defaultRowValues = getDefaultRowValues();
3488 
3489  // char indexString[1000];
3490  std::string tmpString, baseString;
3491  // bool foundAny;
3492  // unsigned int index;
3493  // unsigned int maxUniqueData;
3494  std::string numString;
3495 
3496  // fill each col of new row with default values
3497  // if a row is a unique data row, increment last row in attempt to make a legal
3498  // column
3499  for(unsigned int col = 0; col < getNumberOfColumns(); ++col)
3500  {
3501  // if(incrementUniqueData)
3502  // __COUT__ << col << " " << columnsInfo_[col].getType() << " basename= " <<
3503  // baseNameAutoUID << __E__;
3504 
3505  // baseNameAutoUID indicates to attempt to make row unique
3506  // add index to max number
3507  if(incrementUniqueData &&
3508  (col == getColUID() || columnsInfo_[col].isChildLinkGroupID() ||
3509  (getNumberOfRows() > 1 &&
3510  (columnsInfo_[col].getType() == TableViewColumnInfo::TYPE_UNIQUE_DATA ||
3511  columnsInfo_[col].getType() ==
3512  TableViewColumnInfo::TYPE_UNIQUE_GROUP_DATA))))
3513  {
3514  if(col == getColUID() || columnsInfo_[col].isChildLinkGroupID())
3516  rowToAdd, col, baseNameAutoUID /*baseValueAsString*/);
3517  else
3518  setUniqueColumnValue(rowToAdd,
3519  col,
3520  "" /* baseValueAsString */,
3521  false /* doMathAppendStrategy */,
3522  childLinkIndex,
3523  groupId);
3524  }
3525  else
3526  theDataView_[rowToAdd][col] = defaultRowValues[col];
3527  }
3528 
3529  if(author != "")
3530  {
3531  __COUT__ << "Row=" << rowToAdd << " was created!" << __E__;
3532 
3533  int authorCol = findColByType(TableViewColumnInfo::TYPE_AUTHOR);
3534  int timestampCol = findColByType(TableViewColumnInfo::TYPE_TIMESTAMP);
3535  setValue(author, rowToAdd, authorCol);
3536  setValue(time(0), rowToAdd, timestampCol);
3537  }
3538 
3539  return rowToAdd;
3540 } // end addRow()
3541 
3542 //==============================================================================
3546 {
3547  if(r >= (int)getNumberOfRows())
3548  {
3549  // out of bounds
3550  __SS__ << "Row " << (int)r
3551  << " is out of bounds (Row Count = " << getNumberOfRows()
3552  << ") and can not be deleted." << __E__;
3553  __SS_THROW__;
3554  }
3555 
3556  theDataView_.erase(theDataView_.begin() + r);
3557 } // end deleteRow()
3558 
3559 //==============================================================================
3576  const unsigned int& c,
3577  bool& isGroup,
3578  std::pair<unsigned int /*link col*/, unsigned int /*link id col*/>& linkPair) const
3579 {
3580  if(!(c < columnsInfo_.size()))
3581  {
3582  __SS__ << "Invalid col (" << (int)c << ") requested for child link!" << __E__;
3583  __SS_THROW__;
3584  }
3585 
3586  //__COUT__ << "getChildLink for col: " << (int)c << "-" <<
3587  // columnsInfo_[c].getType() << "-" << columnsInfo_[c].getName() << __E__;
3588 
3589  // check if column is a child link UID
3590  if((isGroup = columnsInfo_[c].isChildLinkGroupID()) ||
3591  columnsInfo_[c].isChildLinkUID())
3592  {
3593  // must be part of unique link, (or invalid table?)
3594  //__COUT__ << "col: " << (int)c << __E__;
3595  linkPair.second = c;
3596  std::string index = columnsInfo_[c].getChildLinkIndex();
3597 
3598  //__COUT__ << "index: " << index << __E__;
3599 
3600  // find pair link
3601  for(unsigned int col = 0; col < columnsInfo_.size(); ++col)
3602  {
3603  //__COUT__ << "try: " << col << "-" << columnsInfo_[col].getType() << "-" <<
3604  // columnsInfo_[col].getName() << __E__;
3605  if(col == c)
3606  continue; // skip column c that we know
3607  else if(columnsInfo_[col].isChildLink() &&
3608  index == columnsInfo_[col].getChildLinkIndex())
3609  {
3610  // found match!
3611  //__COUT__ << "getChildLink Found match for col: " << (int)c << " at " <<
3612  // col << __E__;
3613  linkPair.first = col;
3614  return true;
3615  }
3616  }
3617 
3618  // if here then invalid table!
3619  __SS__ << "\tIn view: " << tableName_
3620  << ", Can't find complete child link for column name "
3621  << columnsInfo_[c].getName() << __E__;
3622  __SS_THROW__;
3623  }
3624 
3625  if(!columnsInfo_[c].isChildLink())
3626  return false; // cant be unique link
3627 
3628  // this is child link, so find pair link uid or gid column
3629  linkPair.first = c;
3630  std::string index = columnsInfo_[c].getChildLinkIndex();
3631 
3632  //__COUT__ << "index: " << index << __E__;
3633 
3634  // find pair link
3635  for(unsigned int col = 0; col < columnsInfo_.size(); ++col)
3636  {
3637  //__COUT__ << "try: " << col << "-" << columnsInfo_[col].getType() << "-" <<
3638  // columnsInfo_[col].getName() << __E__;
3639  if(col == c)
3640  continue; // skip column c that we know
3641  // __COUT__ << "try: " << col << "-" << columnsInfo_[col].getType() <<
3642  // "-" << columnsInfo_[col].getName() <<
3643  // "-u" << columnsInfo_[col].isChildLinkUID() <<
3644  // "-g" << columnsInfo_[col].isChildLinkGroupID() << __E__;
3645  //
3646  // if(columnsInfo_[col].isChildLinkUID())
3647  // __COUT__ << "-L" << columnsInfo_[col].getChildLinkIndex() << __E__;
3648  //
3649  // if(columnsInfo_[col].isChildLinkGroupID())
3650  // __COUT__ << "-L" << columnsInfo_[col].getChildLinkIndex() << __E__;
3651 
3652  if(((columnsInfo_[col].isChildLinkUID() && !(isGroup = false)) ||
3653  (columnsInfo_[col].isChildLinkGroupID() && (isGroup = true))) &&
3654  index == columnsInfo_[col].getChildLinkIndex())
3655  {
3656  // found match!
3657  //__COUT__ << "getChildLink Found match for col: " << (int)c << " at " << col
3658  //<< __E__;
3659  linkPair.second = col;
3660  return true;
3661  }
3662  }
3663 
3664  // if here then invalid table!
3665  __SS__ << "\tIn view: " << tableName_
3666  << ", Can't find complete child link id for column name "
3667  << columnsInfo_[c].getName() << __E__;
3668  __SS_THROW__;
3669 } // end getChildLink()
static std::string convertToCaps(std::string &str, bool isConfigName=false)
Definition: TableBase.cc:1859
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:1643
void setValueAsString(const std::string &value, unsigned int row, unsigned int col)
Definition: TableView.cc:1081
void deleteRow(int r)
Definition: TableView.cc:3545
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:1828
bool removeRowFromGroup(const unsigned int &row, const unsigned int &col, const std::string &groupID, bool deleteRowIfNoGroupLeft=false)
Definition: TableView.cc:1590
unsigned int findColByType(const std::string &type, unsigned int startingCol=0) const
Definition: TableView.cc:1966
bool getChildLink(const unsigned int &col, bool &isGroup, std::pair< unsigned int, unsigned int > &linkPair) const
Definition: TableView.cc:3575
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:1727
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:1983
int fillFromJSON(const std::string &json)
Definition: TableView.cc:2344
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:3368
void fillFromCSV(const std::string &data, const int &dataOffset=0, const std::string &author="", const char rowDelimter=',', const char colDelimter='\n')
Definition: TableView.cc:3104
int fillFromEncodedCSV(const std::string &data, const int &dataOffset=0, const std::string &author="")
Definition: TableView.cc:3232
unsigned int findCol(const std::string &name) const
Definition: TableView.cc:1943
void setValue(const T &value, unsigned int row, unsigned int col)
< in included .icc source
void setURIEncodedComment(const std::string &uriComment)
Definition: TableView.cc:2098
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:3460
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:24
static std::string decodeURIComponent(const std::string &data)
static std::string stackTrace(void)
static bool getNumber(const std::string &s, T &retValue)