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