otsdaq  3.07.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" ||
641  theDataView_[row][col] == "TRUE" || //excel bool
642  theDataView_[row][col] == "on" || theDataView_[row][col] == "On" ||
643  theDataView_[row][col] == "ON")
644  theDataView_[row][col] = TableViewColumnInfo::TYPE_VALUE_ON;
645  else if(theDataView_[row][col] == "0" ||
646  theDataView_[row][col] == "FALSE" || //excel bool
647  theDataView_[row][col] == "off" ||
648  theDataView_[row][col] == "Off" ||
649  theDataView_[row][col] == "OFF")
650  theDataView_[row][col] = TableViewColumnInfo::TYPE_VALUE_OFF;
651  else
652  {
653  __SS__ << getTableName() << " Error:\t the value '"
654  << theDataView_[row][col] << "' in column "
655  << columnsInfo_[col].getName()
656  << " is not a valid Type (On/Off) std::string. Possible "
657  "values are 1, on, On, ON, 0, off, Off, OFF."
658  << __E__;
659  __SS_THROW__;
660  }
661  }
662  else if(columnsInfo_[col].getType() == TableViewColumnInfo::TYPE_TRUE_FALSE)
663  for(unsigned int row = 0; row < getNumberOfRows(); ++row)
664  {
665  if(theDataView_[row][col] == "1" ||
666  theDataView_[row][col] == "true" ||
667  theDataView_[row][col] == "True" ||
668  theDataView_[row][col] == "TRUE")
669  theDataView_[row][col] = TableViewColumnInfo::TYPE_VALUE_TRUE;
670  else if(theDataView_[row][col] == "0" ||
671  theDataView_[row][col] == "false" ||
672  theDataView_[row][col] == "False" ||
673  theDataView_[row][col] == "FALSE")
674  theDataView_[row][col] = TableViewColumnInfo::TYPE_VALUE_FALSE;
675  else
676  {
677  __SS__ << getTableName() << " Error:\t the value '"
678  << theDataView_[row][col] << "' in column "
679  << columnsInfo_[col].getName()
680  << " is not a valid Type (True/False) std::string. "
681  "Possible values are 1, true, True, TRUE, 0, false, "
682  "False, FALSE."
683  << __E__;
684  __SS_THROW__;
685  }
686  }
687  else if(columnsInfo_[col].getType() == TableViewColumnInfo::TYPE_YES_NO)
688  for(unsigned int row = 0; row < getNumberOfRows(); ++row)
689  {
690  if(theDataView_[row][col] == "1" ||
691  theDataView_[row][col] == "TRUE" || //excel bool
692  theDataView_[row][col] == "yes" ||
693  theDataView_[row][col] == "Yes" || theDataView_[row][col] == "YES")
694  theDataView_[row][col] = TableViewColumnInfo::TYPE_VALUE_YES;
695  else if(theDataView_[row][col] == "0" ||
696  theDataView_[row][col] == "FALSE" || //excel bool
697  theDataView_[row][col] == "no" ||
698  theDataView_[row][col] == "No" ||
699  theDataView_[row][col] == "NO")
700  theDataView_[row][col] = TableViewColumnInfo::TYPE_VALUE_NO;
701  else
702  {
703  __SS__ << getTableName() << " Error:\t the value '"
704  << theDataView_[row][col] << "' in column "
705  << columnsInfo_[col].getName()
706  << " is not a valid Type (Yes/No) std::string. Possible "
707  "values are 1, yes, Yes, YES, 0, no, No, NO."
708  << __E__;
709  __SS_THROW__;
710  }
711  }
712  else if(columnsInfo_[col].isGroupID()) // GroupID type
713  {
714  colLinkGroupIDs_[columnsInfo_[col].getChildLinkIndex()] =
715  col; // add to groupid map
716  // check uniqueness
717  groupIdIndexes.emplace(columnsInfo_[col].getChildLinkIndex());
718  ++groupIdIndexesCount;
719  }
720  else if(columnsInfo_[col].isChildLink()) // Child Link type
721  {
722  // sanitize no link to default
723  for(unsigned int row = 0; row < getNumberOfRows(); ++row)
724  if(theDataView_[row][col] == "NoLink" ||
725  theDataView_[row][col] == "No_Link" ||
726  theDataView_[row][col] == "NOLINK" ||
727  theDataView_[row][col] == "NO_LINK" ||
728  theDataView_[row][col] == "Nolink" ||
729  theDataView_[row][col] == "nolink" ||
730  theDataView_[row][col] == "noLink")
731  theDataView_[row][col] =
732  TableViewColumnInfo::DATATYPE_LINK_DEFAULT;
733 
734  // check uniqueness
735  childLinkIndexes.emplace(columnsInfo_[col].getChildLinkIndex());
736  ++childLinkIndexesCount;
737 
738  // force data type to TableViewColumnInfo::DATATYPE_STRING
739  if(columnsInfo_[col].getDataType() !=
740  TableViewColumnInfo::DATATYPE_STRING)
741  {
742  __SS__ << getTableName() << " Error:\t"
743  << "Column " << col << " with name '"
744  << columnsInfo_[col].getName()
745  << "' is a Child Link column and has an illegal data type of '"
746  << columnsInfo_[col].getDataType()
747  << "'. The data type for Child Link columns must be "
748  << TableViewColumnInfo::DATATYPE_STRING << __E__;
749  __SS_THROW__;
750  }
751 
752  // check for link mate (i.e. every child link needs link ID)
753  getChildLink(col, tmpIsGroup, tmpLinkPair);
754  }
755  else if(columnsInfo_[col].isChildLinkUID() || // Child Link ID type
756  columnsInfo_[col].isChildLinkGroupID())
757  {
758  // check uniqueness
759  childLinkIdLabels.emplace(columnsInfo_[col].getChildLinkIndex());
760  ++childLinkIdLabelsCount;
761 
762  // check that the Link ID is not empty, and force to default
763  for(unsigned int row = 0; row < getNumberOfRows(); ++row)
764  if(theDataView_[row][col] == "")
765  theDataView_[row][col] = rowDefaults[col];
766 
767  // check for link mate (i.e. every child link needs link ID)
768  getChildLink(col, tmpIsGroup, tmpLinkPair);
769  }
770 
771  // check if number exist and then if it is limited by min and max, use functions in here to get values different than stof
772  if(columnsInfo_[col].isNumberDataType())
773  {
774  std::string minimumValueString = columnsInfo_[col].getMinValue();
775  std::string maximumValueString = columnsInfo_[col].getMaxValue();
776  double minimumValue, maximumValue, valueFromTable;
777  bool minExists = false, maxExists = false;
778 
779  if(!minimumValueString.empty())
780  {
781  minExists = StringMacros::getNumber(
782  StringMacros::convertEnvironmentVariables(minimumValueString),
783  minimumValue);
784  if(!minExists)
785  {
786  __SS__ << "Inavlid user spec'd min value '" << minimumValueString
787  << "' which is not a valid number. The minimum value must "
788  "be a number (environment variables and math "
789  "operations are allowed)."
790  << __E__;
791  __SS_THROW__;
792  }
793  }
794 
795  if(!maximumValueString.empty())
796  {
797  maxExists = StringMacros::getNumber(
798  StringMacros::convertEnvironmentVariables(maximumValueString),
799  maximumValue);
800  if(!maxExists)
801  {
802  __SS__ << "Inavlid user spec'd max value '" << maximumValueString
803  << "' which is not a valid number. The maximum value must "
804  "be a number (environment variables and math "
805  "operations are allowed)."
806  << __E__;
807  __SS_THROW__;
808  }
809  }
810 
811  if(minExists && maxExists && minimumValue > maximumValue)
812  {
813  __SS__ << "Minimum value is greater than maximum, check table editor "
814  "to change this"
815  << __E__;
816  __SS_THROW__;
817  }
818 
819  if(minExists || maxExists)
820  for(unsigned int row = 0; row < getNumberOfRows(); ++row)
821  {
822  getValue(valueFromTable, row, col);
823  if(minExists && valueFromTable < minimumValue)
824  {
825  __SS__
826  << "The value '" << valueFromTable << "'("
827  << getValueAsString(
828  row, col, false /* convertEnvironmentVariables */)
829  << ") at [row,col]=[" << row << "," << col
830  << "] is outside the established limits: "
831  << valueFromTable
832  << " is lower than the specified minimum " << minimumValue
833  << "." << __E__;
834  __SS_THROW__;
835  }
836  if(maxExists && valueFromTable > maximumValue)
837  {
838  __SS__
839  << "This value '" << valueFromTable << "'("
840  << getValueAsString(
841  row, col, false /* convertEnvironmentVariables */)
842  << ") at [row,col]=[" << row << "," << col
843  << "] is outside the established limits: "
844  << valueFromTable
845  << " is greater than the specified maximum "
846  << maximumValue << "." << __E__;
847  __SS_THROW__;
848  }
849  }
850  } // end handling NUMBER data types
851  } // end column loop
852 
853  // verify child link index uniqueness
854  if(groupIdIndexes.size() != groupIdIndexesCount)
855  {
856  __SS__ << ("GroupId Labels are not unique!") << "There are "
857  << groupIdIndexesCount << " GroupId Labels and the unique count is "
858  << groupIdIndexes.size() << __E__;
859  __SS_THROW__;
860  }
861  if(childLinkIndexes.size() != childLinkIndexesCount)
862  {
863  __SS__ << ("Child Link Labels are not unique!") << "There are "
864  << childLinkIndexesCount
865  << " Child Link Labels and the unique count is "
866  << childLinkIndexes.size() << __E__;
867  __SS_THROW__;
868  }
869  if(childLinkIdLabels.size() != childLinkIdLabelsCount)
870  {
871  __SS__ << ("Child Link ID Labels are not unique!") << "There are "
872  << childLinkIdLabelsCount
873  << " Child Link ID Labels and the unique count is "
874  << childLinkIdLabels.size() << __E__;
875  __SS_THROW__;
876  }
877  }
878  catch(...)
879  {
880  __COUT__ << "Error occured in TableView::init() for version=" << version_
881  << __E__;
882  throw;
883  }
884 } // end init()
885 
886 //==============================================================================
891 void TableView::getValue(std::string& value,
892  unsigned int row,
893  unsigned int col,
894  bool doConvertEnvironmentVariables) const
895 {
896  if(!(row < getNumberOfRows() && col < theDataView_[row].size()))
897  {
898  __SS__ << "Invalid row col requested " << row << "," << col << " vs "
899  << getNumberOfRows() << "," << columnsInfo_.size() << "/"
900  << theDataView_[row].size() << __E__;
901  __SS_THROW__;
902  }
903 
904  value = validateValueForColumn(
905  theDataView_[row][col], col, doConvertEnvironmentVariables);
906 } // end getValue()
907 
908 //==============================================================================
913 std::string TableView::validateValueForColumn(const std::string& value,
914  unsigned int col,
915  bool doConvertEnvironmentVariables) const
916 {
917  if(col >= columnsInfo_.size())
918  {
919  __SS__ << "Invalid col requested" << __E__;
920  __SS_THROW__;
921  }
922 
923  if(columnsInfo_[col].getType() == TableViewColumnInfo::TYPE_FIXED_CHOICE_DATA &&
924  // value == columnsInfo_[col].getDefaultValue())
925  value == columnsInfo_[col].getDefaultDefaultValue(columnsInfo_[col].getType(),
926  columnsInfo_[col].getDataType()))
927  {
928  // if type string, fixed choice and DEFAULT, then return string of first choice
929 
930  std::vector<std::string> choices = columnsInfo_[col].getDataChoices();
931 
932  // consider arbitrary bool
933  bool skipOne = (choices.size() && choices[0].find("arbitraryBool=") == 0);
934  size_t index = (skipOne ? 1 : 0);
935  if(choices.size() > index)
936  {
937  return doConvertEnvironmentVariables
939  : choices[index]; // handled value from fixed choices
940  }
941  } // end handling default to fixed choice conversion
942 
943  if(columnsInfo_[col].getDataType() == TableViewColumnInfo::DATATYPE_STRING)
944  return doConvertEnvironmentVariables
946  : value;
947  else if(columnsInfo_[col].getDataType() == TableViewColumnInfo::DATATYPE_TIME)
948  {
950  doConvertEnvironmentVariables
952  : value);
953  }
954  else
955  {
956  __SS__ << "\tUnrecognized column data type: " << columnsInfo_[col].getDataType()
957  << " in configuration " << tableName_
958  << " at column=" << columnsInfo_[col].getName()
959  << " for getValue with type '"
960  << StringMacros::demangleTypeName(typeid(std::string).name()) << "'"
961  << __E__;
962  __SS_THROW__;
963  }
964 
965  // return retValue;
966 } // end validateValueForColumn()
967 
968 //==============================================================================
972 std::string TableView::getValueAsString(unsigned int row,
973  unsigned int col,
974  bool doConvertEnvironmentVariables) const
975 {
976  if(!(col < columnsInfo_.size() && row < getNumberOfRows()))
977  {
978  __SS__ << ("Invalid row col requested") << __E__;
979  __SS_THROW__;
980  }
981 
982  __COUTS__(30) << columnsInfo_[col].getType() << " " << col << __E__;
983 
984  if(columnsInfo_[col].getType() == TableViewColumnInfo::TYPE_ON_OFF)
985  {
986  if(theDataView_[row][col] == "1" || theDataView_[row][col] == "on" ||
987  theDataView_[row][col] == "On" || theDataView_[row][col] == "ON")
988  return TableViewColumnInfo::TYPE_VALUE_ON;
989  else
990  return TableViewColumnInfo::TYPE_VALUE_OFF;
991  }
992  else if(columnsInfo_[col].getType() == TableViewColumnInfo::TYPE_TRUE_FALSE)
993  {
994  if(theDataView_[row][col] == "1" || theDataView_[row][col] == "true" ||
995  theDataView_[row][col] == "True" || theDataView_[row][col] == "TRUE")
996  return TableViewColumnInfo::TYPE_VALUE_TRUE;
997  else
998  return TableViewColumnInfo::TYPE_VALUE_FALSE;
999  }
1000  else if(columnsInfo_[col].getType() == TableViewColumnInfo::TYPE_YES_NO)
1001  {
1002  if(theDataView_[row][col] == "1" || theDataView_[row][col] == "yes" ||
1003  theDataView_[row][col] == "Yes" || theDataView_[row][col] == "YES")
1004  return TableViewColumnInfo::TYPE_VALUE_YES;
1005  else
1006  return TableViewColumnInfo::TYPE_VALUE_NO;
1007  }
1008 
1009  return doConvertEnvironmentVariables
1010  ? StringMacros::convertEnvironmentVariables(theDataView_[row][col])
1011  : theDataView_[row][col];
1012 } //end getValueAsString()
1013 
1014 //==============================================================================
1022  unsigned int row,
1023  unsigned int col,
1024  bool doConvertEnvironmentVariables /* = true */,
1025  bool quotesToDoubleQuotes /* = false */) const
1026 {
1027  std::string val = getValueAsString(row, col, doConvertEnvironmentVariables);
1028  std::string retVal = "";
1029  retVal.reserve(val.size()); // reserve roughly right size
1030  for(unsigned int i = 0; i < val.size(); ++i)
1031  {
1032  if(val[i] == '\n')
1033  retVal += "\\n";
1034  else if(val[i] == '\t')
1035  retVal += "\\t";
1036  else if(val[i] == '\r')
1037  retVal += "\\r";
1038  else
1039  {
1040  // escaped characters need a
1041  if(val[i] == '\\')
1042  retVal += '\\';
1043  if(quotesToDoubleQuotes && val[i] == '"')
1044  retVal += '"'; //convert " to "" for excel style CSV
1045  else if(!quotesToDoubleQuotes && val[i] == '"')
1046  retVal += '\\';
1047  retVal += val[i];
1048  }
1049  }
1050  return retVal;
1051 } //end getEscapedValueAsString()
1052 
1053 //==============================================================================
1056 void TableView::setValue(const std::string& value, unsigned int row, unsigned int col)
1057 {
1058  if(!(col < columnsInfo_.size() && row < getNumberOfRows()))
1059  {
1060  __SS__ << "Invalid row (" << row << ") col (" << col << ") requested!" << __E__;
1061  __SS_THROW__;
1062  }
1063 
1064  if(columnsInfo_[col].getDataType() == TableViewColumnInfo::DATATYPE_STRING)
1065  theDataView_[row][col] = value;
1066  else // dont allow TableViewColumnInfo::DATATYPE_TIME to be set as string.. force use
1067  // as time_t to standardize string result
1068  {
1069  __SS__ << "\tUnrecognized column data type: " << columnsInfo_[col].getDataType()
1070  << " in configuration " << tableName_
1071  << " at column=" << columnsInfo_[col].getName()
1072  << " for setValue with type '"
1073  << StringMacros::demangleTypeName(typeid(value).name()) << "'" << __E__;
1074  __SS_THROW__;
1075  }
1076 } // end setValue()
1077 
1078 //==============================================================================
1079 void TableView::setValue(const char* value, unsigned int row, unsigned int col)
1080 {
1081  setValue(std::string(value), row, col);
1082 } // end setValue()
1083 
1084 //==============================================================================
1087 void TableView::setValueAsString(const std::string& value,
1088  unsigned int row,
1089  unsigned int col)
1090 {
1091  if(!(col < columnsInfo_.size() && row < getNumberOfRows()))
1092  {
1093  __SS__ << "Invalid row (" << row << ") col (" << col << ") requested!" << __E__;
1094  __SS_THROW__;
1095  }
1096 
1097  theDataView_[row][col] = value;
1098 } // end setValueAsString()
1099 
1100 //==============================================================================
1108  unsigned int row,
1109  unsigned int col,
1110  std::string baseValueAsString /*= "" */,
1111  bool doMathAppendStrategy /*= false*/,
1112  std::string childLinkIndex /* = "" */,
1113  std::string groupId /* = "" */)
1114 {
1115  if(!(col < columnsInfo_.size() && row < getNumberOfRows()))
1116  {
1117  __SS__ << "Invalid row (" << row << ") col (" << col << ") requested!" << __E__;
1118  __SS_THROW__;
1119  }
1120 
1121  bool isUniqueGroupCol =
1122  (columnsInfo_[col].getType() == TableViewColumnInfo::TYPE_UNIQUE_GROUP_DATA);
1123  unsigned int childLinkIndexCol = -1;
1124  if(isUniqueGroupCol)
1125  {
1126  __COUTVS__(12, childLinkIndex); //set TRACE level to TLVL_DEBUG + 12
1127  __COUTVS__(12, groupId); //set TRACE level to TLVL_DEBUG + 12
1128  childLinkIndexCol = getLinkGroupIDColumn(childLinkIndex); // column in question
1129  __COUTVS__(12, childLinkIndexCol); //set TRACE level to TLVL_DEBUG + 12
1130  }
1131 
1132  __COUTT__ << "Current '" << columnsInfo_[col].getName() << "' "
1133  << (isUniqueGroupCol ? "(Unique in Group) " : "")
1134  << "unique data entry is data[" << row << "][" << col << "] = '"
1135  << theDataView_[row][col] << "' baseValueAsString = " << baseValueAsString
1136  << " doMathAppendStrategy = " << doMathAppendStrategy << __E__;
1137 
1138  bool firstConflict = true;
1139  int maxUniqueData = -1;
1140  std::string tmpString = "";
1141  bool foundAny;
1142  unsigned int index;
1143  std::string numString;
1144  std::string opString; // for doMathAppendStrategy
1145 
1146  // find max in rows
1147 
1148  // this->print();
1149 
1150  for(unsigned int r = 0; r < getNumberOfRows(); ++r)
1151  {
1152  if(r == row)
1153  continue; // skip row to add
1154 
1155  if(isUniqueGroupCol && !isEntryInGroupCol(r, childLinkIndexCol, groupId))
1156  continue; // skip rows not in group
1157 
1158  // find last non numeric character
1159 
1160  foundAny = false;
1161  tmpString = theDataView_[r][col];
1162 
1163  __COUTS__(3) << "row[" << r << "] tmpString " << tmpString << __E__;
1164 
1165  for(index = tmpString.length() - 1; index < tmpString.length(); --index)
1166  {
1167  __COUTS__(3) << index << " tmpString[index] " << tmpString[index] << __E__;
1168  if(!(tmpString[index] >= '0' && tmpString[index] <= '9'))
1169  break; // if not numeric, break
1170  foundAny = true;
1171  }
1172 
1173  __COUTS__(3) << "index " << index << " foundAny " << foundAny << __E__;
1174 
1175  if(tmpString.length() && foundAny) // then found a numeric substring
1176  {
1177  // create numeric substring
1178  numString = tmpString.substr(index + 1);
1179 
1180  // and alpha basestring
1181  tmpString = tmpString.substr(0, index + 1);
1182 
1183  if(doMathAppendStrategy && tmpString.size())
1184  {
1185  // look for op string
1186  foundAny = false;
1187  for(index = tmpString.length() - 1; index < tmpString.length(); --index)
1188  {
1189  __COUTS__(4)
1190  << index << " tmpString[index] " << tmpString[index] << __E__;
1191  if(!(tmpString[index] == '+' || tmpString[index] == ' '))
1192  break; // if not plus op, break
1193  foundAny = true;
1194  }
1195 
1196  if(foundAny)
1197  {
1198  // create numeric substring
1199  opString = tmpString.substr(index + 1);
1200 
1201  // and alpha basestring
1202  tmpString = tmpString.substr(0, index + 1);
1203  }
1204  }
1205 
1206  __COUTS__(3) << tmpString << " vs " << baseValueAsString << __E__;
1207 
1208  if(baseValueAsString != "" && tmpString != baseValueAsString)
1209  continue; // skip max unique number if basestring does not match
1210 
1211  __COUTS__(3) << "Found unique data base string '" << tmpString
1212  << "' and number string '" << numString << "' in last record '"
1213  << theDataView_[r][col] << "'" << __E__;
1214 
1215  if(firstConflict)
1216  {
1217  // if baseValueAsString ends in number, then add _ to keep naming similar
1218  if(baseValueAsString.size() &&
1219  baseValueAsString[baseValueAsString.size() - 1] >= '0' &&
1220  baseValueAsString[baseValueAsString.size() - 1] <= '9')
1221  baseValueAsString += '_';
1222 
1223  firstConflict = false;
1224  }
1225 
1226  // extract number
1227  sscanf(numString.c_str(), "%u", &index);
1228 
1229  if((int)index > maxUniqueData)
1230  {
1231  maxUniqueData = (int)index;
1232 
1233  if(baseValueAsString == "")
1234  baseValueAsString = tmpString; // assume a value for base string
1235  }
1236  }
1237  else if(maxUniqueData < 0 &&
1238  (baseValueAsString == "" || tmpString == baseValueAsString))
1239  {
1240  if(firstConflict)
1241  {
1242  // if baseValueAsString ends in number, then add _ to keep naming similar
1243  if(baseValueAsString.size() &&
1244  baseValueAsString[baseValueAsString.size() - 1] >= '0' &&
1245  baseValueAsString[baseValueAsString.size() - 1] <= '9')
1246  baseValueAsString += '_';
1247 
1248  firstConflict = false;
1249  }
1250 
1251  maxUniqueData = 0; // start a number if basestring conflict
1252  }
1253  } //end loop finding max unique data (potentially for group)
1254 
1255  __COUTVS__(12, maxUniqueData); //set TRACE level to TLVL_DEBUG + 12
1256  __COUTVS__(12, baseValueAsString); //set TRACE level to TLVL_DEBUG + 12
1257 
1258  if(maxUniqueData == -1) // if no conflicts, then do not add number
1259  {
1260  if(baseValueAsString != "")
1261  theDataView_[row][col] = baseValueAsString;
1262  else
1263  theDataView_[row][col] = columnsInfo_[col].getDefaultValue();
1264  }
1265  else
1266  {
1267  ++maxUniqueData; // increment
1268 
1269  char indexString[1000];
1270  sprintf(indexString, "%u", maxUniqueData);
1271 
1272  __COUTVS__(12, indexString); //set TRACE level to TLVL_DEBUG + 12
1273  __COUTVS__(12, baseValueAsString); //set TRACE level to TLVL_DEBUG + 12
1274 
1275  if(doMathAppendStrategy)
1276  theDataView_[row][col] = baseValueAsString + " + " + indexString;
1277  else
1278  theDataView_[row][col] = baseValueAsString + indexString;
1279  }
1280 
1281  __COUTT__ << "New unique data entry is data[" << row << "][" << col << "] = '"
1282  << theDataView_[row][col] << "'" << __E__;
1283 
1284  if(TTEST(13))
1285  {
1286  std::stringstream ss;
1287  this->print(ss);
1288  __COUT_MULTI__(13, ss.str());
1289  }
1290 
1291  return theDataView_[row][col];
1292 } // end setUniqueColumnValue()
1293 
1294 //==============================================================================
1297 unsigned int TableView::initColUID(void)
1298 {
1299  if(colUID_ != INVALID)
1300  return colUID_;
1301 
1302  // if doesn't exist throw error! each view must have a UID column
1304  if(colUID_ == INVALID)
1305  {
1306  __COUT__ << "Column Types: " << __E__;
1307  for(unsigned int col = 0; col < columnsInfo_.size(); ++col)
1308  std::cout << columnsInfo_[col].getType() << "() "
1309  << columnsInfo_[col].getName() << __E__;
1310  __SS__ << "\tMissing UID Column in table named '" << tableName_ << "'" << __E__;
1311  __SS_THROW__;
1312  }
1313  return colUID_;
1314 }
1315 //==============================================================================
1319 unsigned int TableView::getColUID(void) const
1320 {
1321  if(colUID_ != INVALID)
1322  return colUID_;
1323 
1324  __COUT__ << "Column Types: " << __E__;
1325  for(unsigned int col = 0; col < columnsInfo_.size(); ++col)
1326  std::cout << columnsInfo_[col].getType() << "() " << columnsInfo_[col].getName()
1327  << __E__;
1328 
1329  __SS__ << ("Missing UID Column in config named " + tableName_ +
1330  ". (Possibly TableView was just not initialized?" +
1331  " This is the const call so can not alter class members)")
1332  << __E__;
1333 
1334  ss << StringMacros::stackTrace() << __E__;
1335 
1336  __SS_THROW__;
1337 }
1338 
1339 //==============================================================================
1342 unsigned int TableView::initColStatus(void)
1343 {
1344  if(colStatus_ != INVALID)
1345  return colStatus_;
1346 
1347  // if doesn't exist throw error! each view must have a UID column
1348  for(unsigned int col = 0; col < columnsInfo_.size(); ++col)
1349  if(columnsInfo_[col].getName() == TableViewColumnInfo::COL_NAME_STATUS)
1350  {
1351  colStatus_ = col;
1352  return colStatus_;
1353  }
1354  for(unsigned int col = 0; col < columnsInfo_.size(); ++col)
1355  if(columnsInfo_[col].getName() == TableViewColumnInfo::COL_NAME_ENABLED)
1356  {
1357  colStatus_ = col;
1358  return colStatus_;
1359  }
1360 
1361  // at this point not found!
1362 
1363  __SS__ << "\tMissing column named '" << TableViewColumnInfo::COL_NAME_STATUS
1364  << "' or '" << TableViewColumnInfo::COL_NAME_ENABLED << "' in table '"
1365  << tableName_ << ".'" << __E__;
1366  ss << "\n\nTable '" << tableName_ << "' Columns: " << __E__;
1367  for(unsigned int col = 0; col < columnsInfo_.size(); ++col)
1368  ss << columnsInfo_[col].getType() << "() " << columnsInfo_[col].getName()
1369  << __E__;
1370 
1371  __SS_ONLY_THROW__;
1372 
1373 } // end initColStatus()
1374 
1375 //==============================================================================
1378 unsigned int TableView::initColPriority(void)
1379 {
1380  if(colPriority_ != INVALID)
1381  return colPriority_;
1382 
1383  // if doesn't exist throw error! each view must have a UID column
1384  colPriority_ =
1385  findCol("*" + TableViewColumnInfo::COL_NAME_PRIORITY); // wild card search
1386  if(colPriority_ == INVALID)
1387  {
1388  __SS__ << "\tMissing column named '" << TableViewColumnInfo::COL_NAME_PRIORITY
1389  << "' in table '" << tableName_ << ".'" << __E__;
1390  ss << "\n\nTable '" << tableName_ << "' Columns: " << __E__;
1391  for(unsigned int col = 0; col < columnsInfo_.size(); ++col)
1392  ss << columnsInfo_[col].getType() << "() " << columnsInfo_[col].getName()
1393  << __E__;
1394 
1395  __SS_THROW__;
1396  }
1397  return colPriority_;
1398 }
1399 
1400 //==============================================================================
1404 unsigned int TableView::getColStatus(void) const
1405 {
1406  if(colStatus_ != INVALID)
1407  return colStatus_;
1408 
1409  __SS__ << "\tMissing column named '" << TableViewColumnInfo::COL_NAME_STATUS
1410  << "' or '" << TableViewColumnInfo::COL_NAME_ENABLED << "' in table '"
1411  << tableName_ << ".'"
1412  << " (The Status column is identified when the TableView is initialized)"
1413  << __E__;
1414 
1415  ss << "\n\nTable '" << tableName_ << "' Columns: " << __E__;
1416  for(unsigned int col = 0; col < columnsInfo_.size(); ++col)
1417  ss << "\t" << columnsInfo_[col].getType() << "() " << columnsInfo_[col].getName()
1418  << __E__;
1419 
1420  ss << __E__;
1421 
1422  ss << StringMacros::stackTrace() << __E__;
1423 
1424  __COUTT__ << ss.str();
1425  __SS_ONLY_THROW__;
1426 } // end getColStatus()
1427 
1428 //==============================================================================
1435 unsigned int TableView::getColPriority(void) const
1436 {
1437  if(colPriority_ != INVALID)
1438  return colPriority_;
1439 
1440  __SS__ << "Priority column was not found... \nColumn Types: " << __E__;
1441 
1442  ss << "Missing " << TableViewColumnInfo::COL_NAME_PRIORITY
1443  << " Column in table named '" << tableName_
1444  << ".' (The Priority column is identified when the TableView is initialized)"
1445  << __E__; // this is the const call, so can not identify the column and
1446  // set colPriority_ here
1447 
1448  ss << "\n\nTable '" << tableName_ << "' Columns: " << __E__;
1449  for(unsigned int col = 0; col < columnsInfo_.size(); ++col)
1450  ss << "\t" << columnsInfo_[col].getType() << "() " << columnsInfo_[col].getName()
1451  << __E__;
1452  ss << __E__;
1453 
1454  ss << StringMacros::stackTrace() << __E__;
1455 
1456  __SS_ONLY_THROW__; // keep it quiet
1457 } // end getColPriority()
1458 
1459 //==============================================================================
1462 void TableView::addRowToGroup(const unsigned int& row,
1463  const unsigned int& col,
1464  const std::string& groupID) //,
1465 // const std::string &colDefault)
1466 {
1467  if(isEntryInGroupCol(row, col, groupID))
1468  {
1469  __SS__ << "GroupID (" << groupID << ") added to row (" << row
1470  << " is already present!" << __E__;
1471  __SS_THROW__;
1472  }
1473 
1474  // not in group, so
1475  // if no groups
1476  // set groupid
1477  // if other groups
1478  // prepend groupId |
1479  if(getDataView()[row][col] == "" ||
1480  getDataView()[row][col] == getDefaultRowValues()[col]) // colDefault)
1481  setValue(groupID, row, col);
1482  else
1483  setValue(groupID + " | " + getDataView()[row][col], row, col);
1484 
1485  //__COUT__ << getDataView()[row][col] << __E__;
1486 } // end addRowToGroup()
1487 
1488 //==============================================================================
1494 std::vector<unsigned int /*group row*/> TableView::getGroupRows(
1495  const unsigned int groupIdCol,
1496  const std::string& groupID,
1497  bool onlyStatusTrue /*=false*/,
1498  bool orderedByPriority /*=false*/) const
1499 {
1500  std::vector<unsigned int /*group row*/> retVector;
1501  std::vector<std::vector<unsigned int /*group row*/>> groupRowVectors =
1502  getGroupRowsInVectors(groupIdCol, groupID, onlyStatusTrue, orderedByPriority);
1503 
1504  for(const auto& groupRowVector : groupRowVectors)
1505  for(const auto& groupRow : groupRowVector)
1506  retVector.push_back(groupRow);
1507 
1508  return retVector;
1509 } // end getGroupRows()
1510 
1511 //==============================================================================
1517 std::vector<std::vector<unsigned int /*group row*/>> TableView::getGroupRowsByPriority(
1518  const unsigned int groupIdCol,
1519  const std::string& groupID,
1520  bool onlyStatusTrue /*=false*/) const
1521 {
1522  return getGroupRowsInVectors(
1523  groupIdCol, groupID, onlyStatusTrue, true /*orderedByPriority*/);
1524 } // end getGroupRowsByPriority()
1525 
1526 //==============================================================================
1534 std::vector<std::vector<unsigned int /*group row*/>> TableView::getGroupRowsInVectors(
1535  const unsigned int groupIdCol,
1536  const std::string& groupID,
1537  bool onlyStatusTrue,
1538  bool orderedByPriority) const
1539 {
1540  std::map<uint64_t /*priority*/, std::vector<unsigned int /*child row*/>>
1541  mapByPriority;
1542  std::vector<std::vector<unsigned int /*group row*/>> retVector;
1543  uint64_t tmpPriority;
1544  bool tmpStatus;
1545 
1546  if(!(orderedByPriority &&
1547  colPriority_ != INVALID)) // if no priority column, all at same priorty [0]
1548  retVector.push_back(std::vector<unsigned int /*group row*/>());
1549 
1550  __COUTS__(2) << "getGroupRowsInVectors: " << groupID << " at col " << groupIdCol
1551  << __E__;
1552  for(unsigned int r = 0; r < getNumberOfRows(); ++r)
1553  if(groupID == "" || groupID == "*" || groupIdCol == INVALID ||
1554  isEntryInGroupCol(r, groupIdCol, groupID))
1555  {
1556  if(groupIdCol != INVALID)
1557  __COUTS__(2) << "Row " << r << " '" << getDataView()[r][groupIdCol]
1558  << "' is in group " << groupID << __E__;
1559  // check status if needed
1560  if(onlyStatusTrue && colStatus_ != INVALID)
1561  {
1562  getValue(tmpStatus, r, colStatus_);
1563 
1564  if(!tmpStatus)
1565  continue; // skip those with status false
1566  }
1567 
1568  if(orderedByPriority && colPriority_ != INVALID)
1569  {
1570  getValue(tmpPriority, r, colPriority_);
1571  // do not accept DEFAULT value of 0.. convert to 100
1572  mapByPriority[tmpPriority ? tmpPriority : 100].push_back(r);
1573  }
1574  else // assume equal priority
1575  retVector[0].push_back(r);
1576  }
1577  else // already true that... if(groupIdCol != INVALID)
1578  __COUTS__(2) << "Row " << r << " '" << getDataView()[r][groupIdCol]
1579  << "' is NOT in group " << groupID << __E__;
1580 
1581  if(orderedByPriority && colPriority_ != INVALID)
1582  {
1583  // at this point have priority map (which automatically sorts by priority)
1584  // now build return vector
1585  for(const auto& priorityChildRowVector : mapByPriority)
1586  {
1587  retVector.push_back(std::vector<unsigned int /*group row*/>());
1588  for(const auto& priorityChildRow : priorityChildRowVector.second)
1589  retVector[retVector.size() - 1].push_back(priorityChildRow);
1590  }
1591 
1592  __COUT__ << "Returning priority children list." << __E__;
1593  }
1594  // else equal priority vector already constructed
1595 
1596  return retVector;
1597 } // end getGroupRowsInVectors()
1598 
1599 //==============================================================================
1604 bool TableView::removeRowFromGroup(const unsigned int& row,
1605  const unsigned int& col,
1606  const std::string& groupNeedle,
1607  bool deleteRowIfNoGroupLeft)
1608 {
1609  __COUT__ << "removeRowFromGroup groupNeedle " << groupNeedle << __E__;
1610  std::set<std::string> groupIDList;
1611  if(!isEntryInGroupCol(row, col, groupNeedle, &groupIDList))
1612  {
1613  __SS__
1614  << "GroupID (" << groupNeedle << ") removed from row (" << row
1615  << ") was already removed! Is there a strange GroupID wildcard match issue? {"
1616  << StringMacros::setToString(groupIDList) << "}" << __E__;
1617  print();
1618  __SS_THROW__;
1619  }
1620 
1621  // is in group, so
1622  // create new string based on set of groupids
1623  // but skip groupNeedle
1624 
1625  std::string newValue = "";
1626  unsigned int cnt = 0;
1627  for(const auto& groupID : groupIDList)
1628  {
1629  __COUTT__ << groupID << " " << groupNeedle << " " << newValue << __E__;
1630  if(groupID == groupNeedle)
1631  continue; // skip group to be removed
1632 
1633  if(cnt)
1634  newValue += " | ";
1635  newValue += groupID;
1636  }
1637 
1638  bool wasDeleted = false;
1639  if(deleteRowIfNoGroupLeft && newValue == "")
1640  {
1641  __COUTT__ << "Delete row since it no longer part of any group." << __E__;
1642  deleteRow(row);
1643  wasDeleted = true;
1644  }
1645  else
1646  {
1647  setValue(newValue, row, col);
1648  __COUTT__ << getDataView()[row][col] << __E__;
1649  }
1650 
1651  return wasDeleted;
1652 } // end removeRowFromGroup()
1653 
1654 //==============================================================================
1660 bool TableView::isEntryInGroup(const unsigned int& r,
1661  const std::string& childLinkIndex,
1662  const std::string& groupNeedle) const
1663 {
1664  unsigned int c = getLinkGroupIDColumn(childLinkIndex); // column in question
1665 
1666  return isEntryInGroupCol(r, c, groupNeedle);
1667 } // end isEntryInGroup()
1668 
1669 //==============================================================================
1678 bool TableView::isEntryInGroupCol(const unsigned int& r,
1679  const unsigned int& c,
1680  const std::string& groupNeedle,
1681  std::set<std::string>* groupIDList) const
1682 {
1683  if(r >= getNumberOfRows() || c >= getNumberOfColumns())
1684  {
1685  __SS__ << "Invalid row/col requested!" << __E__;
1686  ss << StringMacros::stackTrace() << __E__;
1687  __SS_THROW__;
1688  }
1689 
1690  unsigned int i = 0;
1691  unsigned int j = 0;
1692  bool found = false;
1693 
1694  __COUTS__(2) << "groupNeedle " << groupNeedle << __E__;
1695 
1696  // go through the full groupString extracting groups and comparing to groupNeedle
1697  for(; j < theDataView_[r][c].size(); ++j)
1698  if((theDataView_[r][c][j] == ' ' || // ignore leading white space or |
1699  theDataView_[r][c][j] == '|') &&
1700  i == j)
1701  ++i;
1702  else if((theDataView_[r][c][j] ==
1703  ' ' || // trailing white space or | indicates group
1704  theDataView_[r][c][j] == '|') &&
1705  i != j) // assume end of group name
1706  {
1707  if(groupIDList)
1708  groupIDList->emplace(theDataView_[r][c].substr(i, j - i));
1709 
1710  __COUTS__(2) << "Group found to compare: "
1711  << theDataView_[r][c].substr(i, j - i) << __E__;
1712  if(groupIDList ? groupNeedle == theDataView_[r][c].substr(i, j - i)
1714  theDataView_[r][c].substr(i, j - i), groupNeedle))
1715  {
1716  __COUTS__(2) << "'" << theDataView_[r][c].substr(i, j - i)
1717  << "' is in group '" << groupNeedle << "'!" << __E__;
1718  if(!groupIDList) // dont return if caller is trying to get group list
1719  return true;
1720  found = true;
1721  }
1722  // if no match, setup i and j for next find
1723  i = j + 1;
1724  }
1725 
1726  if(i != j) // last group check (for case when no ' ' or '|')
1727  {
1728  if(groupIDList)
1729  groupIDList->emplace(theDataView_[r][c].substr(i, j - i));
1730 
1731  __COUTS__(2) << "Group found to compare: " << theDataView_[r][c].substr(i, j - i)
1732  << __E__;
1733  if(groupIDList ? groupNeedle == theDataView_[r][c].substr(i, j - i)
1734  : StringMacros::wildCardMatch(theDataView_[r][c].substr(i, j - i),
1735  groupNeedle))
1736  {
1737  __COUTS__(2) << "'" << theDataView_[r][c].substr(i, j - i)
1738  << "' is in group '" << groupNeedle << "'!" << __E__;
1739  return true;
1740  }
1741  }
1742 
1743  return found;
1744 } // end isEntryInGroupCol()
1745 
1746 //==============================================================================
1754 std::set<std::string> TableView::getSetOfGroupIDs(const std::string& childLinkIndex,
1755  unsigned int r) const
1756 {
1757  return getSetOfGroupIDs(getLinkGroupIDColumn(childLinkIndex), r);
1758 }
1759 std::set<std::string> TableView::getSetOfGroupIDs(const unsigned int& c,
1760  unsigned int r) const
1761 {
1762  //__COUT__ << "GroupID col=" << (int)c << __E__;
1763 
1764  std::set<std::string> retSet;
1765 
1766  // unsigned int i = 0;
1767  // unsigned int j = 0;
1768 
1769  if(r != (unsigned int)-1)
1770  {
1771  if(r >= getNumberOfRows())
1772  {
1773  __SS__ << "Invalid row requested!" << __E__;
1774  __SS_THROW__;
1775  }
1776 
1777  StringMacros::getSetFromString(theDataView_[r][c], retSet);
1778  // //go through the full groupString extracting groups
1779  // //add each found groupId to set
1780  // for(;j<theDataView_[r][c].size();++j)
1781  // if((theDataView_[r][c][j] == ' ' || //ignore leading white space or |
1782  // theDataView_[r][c][j] == '|')
1783  // && i == j)
1784  // ++i;
1785  // else if((theDataView_[r][c][j] == ' ' || //trailing white space or |
1786  // indicates group theDataView_[r][c][j] == '|')
1787  // && i != j) // assume end of group name
1788  // {
1789  // //__COUT__ << "Group found: " <<
1790  // // theDataView_[r][c].substr(i,j-i) << __E__;
1791  //
1792  //
1793  // retSet.emplace(theDataView_[r][c].substr(i,j-i));
1794  //
1795  // //setup i and j for next find
1796  // i = j+1;
1797  // }
1798  //
1799  // if(i != j) //last group check (for case when no ' ' or '|')
1800  // retSet.emplace(theDataView_[r][c].substr(i,j-i));
1801  }
1802  else
1803  {
1804  // do all rows
1805  for(r = 0; r < getNumberOfRows(); ++r)
1806  {
1807  StringMacros::getSetFromString(theDataView_[r][c], retSet);
1808 
1809  // i=0;
1810  // j=0;
1811  //
1812  // //__COUT__ << (int)r << ": " << theDataView_[r][c] << __E__;
1813  //
1814  // //go through the full groupString extracting groups
1815  // //add each found groupId to set
1816  // for(;j<theDataView_[r][c].size();++j)
1817  // {
1818  // //__COUT__ << "i:" << i << " j:" << j << __E__;
1819  //
1820  // if((theDataView_[r][c][j] == ' ' || //ignore leading white
1821  // space or | theDataView_[r][c][j] == '|')
1822  // && i == j)
1823  // ++i;
1824  // else if((theDataView_[r][c][j] == ' ' || //trailing white
1825  // space or | indicates group theDataView_[r][c][j]
1826  // ==
1827  // '|')
1828  // && i != j) // assume end of group name
1829  // {
1830  // //__COUT__ << "Group found: " <<
1831  // // theDataView_[r][c].substr(i,j-i) << __E__;
1832  //
1833  // retSet.emplace(theDataView_[r][c].substr(i,j-i));
1834  //
1835  // //setup i and j for next find
1836  // i = j+1;
1837  // }
1838  // }
1839  //
1840  // if(i != j) //last group (for case when no ' ' or '|')
1841  // {
1842  // //__COUT__ << "Group found: " <<
1843  // // theDataView_[r][c].substr(i,j-i) << __E__;
1844  // retSet.emplace(theDataView_[r][c].substr(i,j-i));
1845  // }
1846  }
1847  }
1848 
1849  return retSet;
1850 }
1851 
1852 //==============================================================================
1855 unsigned int TableView::getLinkGroupIDColumn(const std::string& childLinkIndex) const
1856 {
1857  if(!childLinkIndex.size())
1858  {
1859  __SS__ << "Empty childLinkIndex string parameter!" << __E__;
1860  ss << StringMacros::stackTrace() << __E__;
1861  __SS_THROW__;
1862  }
1863 
1864  const char* needleChildLinkIndex = &childLinkIndex[0];
1865 
1866  // allow space syntax to target a childLinkIndex from a different parentLinkIndex
1867  // e.g. "parentLinkIndex childLinkIndex"
1868  size_t spacePos = childLinkIndex.find(' ');
1869  if(spacePos != std::string::npos &&
1870  spacePos + 1 < childLinkIndex.size()) // make sure there are more characters
1871  {
1872  // found space syntax for targeting childLinkIndex
1873  needleChildLinkIndex = &childLinkIndex[spacePos + 1];
1874  }
1875 
1876  std::map<std::string, unsigned int>::const_iterator it =
1877  colLinkGroupIDs_.find(needleChildLinkIndex);
1878  if(it != // if already known, return it
1879  colLinkGroupIDs_.end())
1880  return it->second;
1881 
1882  // otherwise search (perhaps init() was not called)
1883  for(unsigned int col = 0; col < columnsInfo_.size(); ++col)
1884  {
1885  // only check columns with link index associated...
1886  if(columnsInfo_[col].isChildLink() || columnsInfo_[col].isChildLinkUID() ||
1887  columnsInfo_[col].isChildLinkGroupID() || columnsInfo_[col].isGroupID())
1888  {
1889  if(needleChildLinkIndex == columnsInfo_[col].getChildLinkIndex())
1890  return col;
1891  }
1892  }
1893 
1894  __SS__
1895  << "Error! Incompatible table for this group link! Table '" << tableName_
1896  << "' is missing a GroupID column with data type '"
1897  << TableViewColumnInfo::TYPE_START_GROUP_ID << "-" << needleChildLinkIndex
1898  << "'.\n\n"
1899  << "Note: you can separate the child GroupID column data type from "
1900  << "the parent GroupLink column data type; this is accomplished by using a space "
1901  << "character at the parent level - the string after the space will be treated "
1902  "as the "
1903  << "child GroupID column data type." << __E__;
1904  ss << "Existing Column GroupIDs: " << __E__;
1905  for(auto& groupIdColPair : colLinkGroupIDs_)
1906  ss << "\t" << groupIdColPair.first << " : col-" << groupIdColPair.second << __E__;
1907 
1908  ss << "Existing Column Types: " << __E__;
1909  for(unsigned int col = 0; col < columnsInfo_.size(); ++col)
1910  ss << "\t" << columnsInfo_[col].getType() << "() " << columnsInfo_[col].getName()
1911  << __E__;
1912 
1913  ss << StringMacros::stackTrace() << __E__;
1914 
1915  __SS_THROW__;
1916 } // end getLinkGroupIDColumn()
1917 
1918 //==============================================================================
1919 unsigned int TableView::findRow(unsigned int col,
1920  const std::string& value,
1921  unsigned int offsetRow,
1922  bool doNotThrow /*= false*/) const
1923 {
1924  for(unsigned int row = offsetRow; row < theDataView_.size(); ++row)
1925  {
1926  if(theDataView_[row][col] == value)
1927  return row;
1928  }
1929  if(doNotThrow)
1930  return TableView::INVALID;
1931 
1932  __SS__ << "\tIn view: " << tableName_ << ", Can't find value=" << value
1933  << " in column named " << columnsInfo_[col].getName()
1934  << " with type=" << columnsInfo_[col].getType() << __E__ << __E__
1935  << StringMacros::stackTrace() << __E__;
1936 
1937  // Note: findRow gets purposely called by configuration GUI a lot looking for
1938  // exceptions so may not want to print out
1939  //__COUT__ << "\n" << ss.str();
1940  __SS_ONLY_THROW__;
1941 } // end findRow()
1942 
1943 //==============================================================================
1944 unsigned int TableView::findRowInGroup(unsigned int col,
1945  const std::string& value,
1946  const std::string& groupId,
1947  const std::string& childLinkIndex,
1948  unsigned int offsetRow) const
1949 {
1950  unsigned int groupIdCol = getLinkGroupIDColumn(childLinkIndex);
1951  for(unsigned int row = offsetRow; row < theDataView_.size(); ++row)
1952  {
1953  if(theDataView_[row][col] == value && isEntryInGroupCol(row, groupIdCol, groupId))
1954  return row;
1955  }
1956 
1957  __SS__ << "\tIn view: " << tableName_ << ", Can't find in group the value=" << value
1958  << " in column named '" << columnsInfo_[col].getName()
1959  << "' with type=" << columnsInfo_[col].getType() << " and GroupID: '"
1960  << groupId << "' in column '" << groupIdCol
1961  << "' with GroupID child link index '" << childLinkIndex << "'" << __E__;
1962  // Note: findRowInGroup gets purposely called by configuration GUI a lot looking for
1963  // exceptions so may not want to print out
1964  __SS_ONLY_THROW__;
1965 } // end findRowInGroup()
1966 
1967 //==============================================================================
1970 unsigned int TableView::findCol(const std::string& wildCardName) const
1971 {
1972  for(unsigned int col = 0; col < columnsInfo_.size(); ++col)
1973  if(StringMacros::wildCardMatch(wildCardName /*needle*/,
1974  columnsInfo_[col].getName() /*haystack*/))
1975  return col;
1976 
1977  __SS__ << "\tIn view: " << tableName_ << ", Can't find column named '" << wildCardName
1978  << "'" << __E__;
1979  ss << "Existing columns:\n";
1980  for(unsigned int col = 0; col < columnsInfo_.size(); ++col)
1981  ss << "\t" << columnsInfo_[col].getName() << "\n";
1982 
1983  ss << StringMacros::stackTrace() << __E__;
1984 
1985  // Note: findCol gets purposely called by configuration GUI a lot looking for
1986  // exceptions so may not want to print out
1987  __SS_ONLY_THROW__;
1988 } // end findCol()
1989 
1990 //==============================================================================
1993 unsigned int TableView::findColByType(const std::string& type,
1994  unsigned int startingCol) const
1995 {
1996  for(unsigned int col = startingCol; col < columnsInfo_.size(); ++col)
1997  {
1998  __COUTS__(40) << columnsInfo_[col].getType() << __E__;
1999  if(columnsInfo_[col].getType() == type)
2000  return col;
2001  }
2002 
2003  return INVALID;
2004 } // end findColByType()
2005 
2008 //==============================================================================
2010 unsigned int TableView::getDataColumnSize(void) const
2011 {
2012  // if no data, give benefit of the doubt that phantom data has mockup column size
2013  if(!getNumberOfRows())
2014  return getNumberOfColumns();
2015  return theDataView_[0].size(); // number of columns in first row of data
2016 }
2017 
2018 //==============================================================================
2019 std::set<std::string> TableView::getColumnNames(void) const
2020 {
2021  std::set<std::string> retSet;
2022  for(auto& colInfo : columnsInfo_)
2023  retSet.emplace(colInfo.getName());
2024  return retSet;
2025 } // end getColumnNames()
2026 
2027 //==============================================================================
2028 std::map<std::string, unsigned int /*col*/> TableView::getColumnNamesMap(void) const
2029 {
2030  std::map<std::string, unsigned int /*col*/> retMap;
2031  unsigned int c = 0;
2032  for(auto& colInfo : columnsInfo_)
2033  retMap.emplace(std::make_pair(colInfo.getName(), c++));
2034  return retMap;
2035 } // end getColumnNamesMap()
2036 
2037 //==============================================================================
2038 std::set<std::string> TableView::getColumnStorageNames(void) const
2039 {
2040  std::set<std::string> retSet;
2041  for(auto& colInfo : columnsInfo_)
2042  retSet.emplace(colInfo.getStorageName());
2043  return retSet;
2044 }
2045 
2046 //==============================================================================
2047 const std::vector<std::string>& TableView::initRowDefaults(void)
2048 {
2049  std::vector<std::string>& retVec = rowDefaultValues_;
2050  retVec.clear();
2051 
2052  // fill each col of new row with default values
2053  for(unsigned int col = 0; col < getNumberOfColumns(); ++col)
2054  {
2055  // if this is a fixed choice Link, and NO_LINK is not in list,
2056  // take first in list to avoid creating illegal rows.
2057  // NOTE: this is not a problem for standard fixed choice fields
2058  // because the default value is always required.
2059 
2060  if(columnsInfo_[col].isChildLink())
2061  {
2062  const std::vector<std::string>& theDataChoices =
2063  columnsInfo_[col].getDataChoices();
2064 
2065  // check if arbitrary values allowed
2066  if(!theDataChoices.size() || // if so, use default
2067  theDataChoices[0] == "arbitraryBool=1")
2068  retVec.push_back(columnsInfo_[col].getDefaultValue());
2069  else
2070  {
2071  bool skipOne =
2072  (theDataChoices.size() && theDataChoices[0] == "arbitraryBool=0");
2073  bool hasSkipped;
2074 
2075  // look for default value in list
2076 
2077  bool foundDefault = false;
2078  hasSkipped = false;
2079  for(const auto& choice : theDataChoices)
2080  if(skipOne && !hasSkipped)
2081  {
2082  hasSkipped = true;
2083  continue;
2084  }
2085  else if(choice == columnsInfo_[col].getDefaultValue())
2086  {
2087  foundDefault = true;
2088  break;
2089  }
2090 
2091  // use first choice if possible
2092  if(!foundDefault && theDataChoices.size() > (skipOne ? 1 : 0))
2093  retVec.push_back(theDataChoices[(skipOne ? 1 : 0)]);
2094  else // else stick with default
2095  retVec.push_back(columnsInfo_[col].getDefaultValue());
2096  }
2097  }
2098  else
2099  retVec.push_back(columnsInfo_[col].getDefaultValue());
2100  }
2101 
2102  //__COUT__ << StringMacros::stackTrace() << __E__;
2103  //__COUTV__(StringMacros::vectorToString(rowDefaultValues_));
2104  return rowDefaultValues_;
2105 } // end getDefaultRowValues()
2106 
2107 //==============================================================================
2108 const TableViewColumnInfo& TableView::getColumnInfo(unsigned int column) const
2109 {
2110  if(column >= columnsInfo_.size())
2111  {
2112  __SS__ << "\nCan't find column " << column
2113  << "\n\n\n\nThe column info is likely missing due to incomplete "
2114  "Configuration View filling.\n\n"
2115  << __E__;
2116  ss << StringMacros::stackTrace() << __E__;
2117  __SS_THROW__;
2118  }
2119  return columnsInfo_[column];
2120 } // end getColumnInfo()
2121 
2124 //==============================================================================
2125 void TableView::setURIEncodedComment(const std::string& uriComment)
2126 {
2127  comment_ = StringMacros::decodeURIComponent(uriComment);
2128 }
2129 
2130 //==============================================================================
2131 void TableView::setAuthor(const std::string& author) { author_ = author; }
2132 
2133 //==============================================================================
2134 void TableView::setCreationTime(time_t t) { creationTime_ = t; }
2135 
2136 //==============================================================================
2137 void TableView::setLastAccessTime(time_t t) { lastAccessTime_ = t; }
2138 
2139 //==============================================================================
2140 void TableView::setLooseColumnMatching(bool setValue)
2141 {
2142  fillWithLooseColumnMatching_ = setValue;
2143 }
2144 
2145 //==============================================================================
2146 void TableView::doGetSourceRawData(bool setValue) { getSourceRawData_ = setValue; }
2147 
2148 //==============================================================================
2149 void TableView::reset(void)
2150 {
2151  version_ = -1;
2152  comment_ = "";
2153  author_ = "";
2154  columnsInfo_.clear();
2155  theDataView_.clear();
2156 } // end reset()
2157 
2158 //==============================================================================
2159 void TableView::print(std::ostream& out /* = std::cout */) const
2160 {
2161  out << "============================================================================="
2162  "="
2163  << __E__;
2164  out << "Print: " << tableName_ << " Version: " << version_ << " Comment: " << comment_
2165  << " Author: " << author_ << " Creation Time: " << ctime(&creationTime_) << __E__;
2166  out << "\t\tNumber of Cols " << getNumberOfColumns() << __E__;
2167  out << "\t\tNumber of Rows " << getNumberOfRows() << __E__;
2168 
2169  out << "Columns:\t";
2170  for(int i = 0; i < (int)columnsInfo_.size(); ++i)
2171  out << i << ":" << columnsInfo_[i].getName() << ":"
2172  << columnsInfo_[i].getStorageName() << ":" << columnsInfo_[i].getType() << ":"
2173  << columnsInfo_[i].getDataType() << "\t ";
2174  out << __E__;
2175 
2176  out << "Rows:" << __E__;
2177  // int num;
2178  std::string val;
2179  for(int r = 0; r < (int)getNumberOfRows(); ++r)
2180  {
2181  out << (int)r << ":\t";
2182  for(int c = 0; c < (int)getNumberOfColumns(); ++c)
2183  {
2184  out << (int)c << ":";
2185 
2186  // if fixed choice type, print index in choice
2187  if(columnsInfo_[c].getType() == TableViewColumnInfo::TYPE_FIXED_CHOICE_DATA)
2188  {
2189  int choiceIndex = -1;
2190  std::vector<std::string> choices = columnsInfo_[c].getDataChoices();
2191  val = StringMacros::convertEnvironmentVariables(theDataView_[r][c]);
2192 
2193  if(val == columnsInfo_[c].getDefaultValue())
2194  choiceIndex = 0;
2195  else
2196  {
2197  for(int i = 0; i < (int)choices.size(); ++i)
2198  if(val == choices[i])
2199  choiceIndex = i + 1;
2200  }
2201 
2202  out << "ChoiceIndex=" << choiceIndex << ":";
2203  }
2204 
2205  out << theDataView_[r][c];
2206  // stopped using below, because it is called sometimes during debugging when
2207  // numbers are set to environment variables:
2208  // if(columnsInfo_[c].getDataType() == "NUMBER")
2209  // {
2210  // getValue(num,r,c,false);
2211  // out << num;
2212  // }
2213  // else
2214  // {
2215  // getValue(val,r,c,false);
2216  // out << val;
2217  // }
2218  out << "\t\t";
2219  }
2220  out << __E__;
2221  }
2222 } // end print()
2223 
2224 //==============================================================================
2225 void TableView::printJSON(std::ostream& out /* = std::cout */) const
2226 {
2227  { //handle special GROUP CACHE table
2228  std::string tmpCachePrepend = TableBase::GROUP_CACHE_PREPEND;
2229  tmpCachePrepend = TableBase::convertToCaps(tmpCachePrepend);
2230  std::string tmpJsonDocPrepend = TableBase::JSON_DOC_PREPEND;
2231  tmpJsonDocPrepend = TableBase::convertToCaps(tmpJsonDocPrepend);
2232  __COUTS__(32) << " '" << tableName_ << "' vs " << tmpCachePrepend << " or "
2233  << tmpJsonDocPrepend << __E__;
2234  //if special GROUP CACHE table, handle construction in a special way
2235  if(tableName_.substr(0, tmpCachePrepend.length()) == tmpCachePrepend ||
2236  tableName_.substr(0, tmpJsonDocPrepend.length()) == tmpJsonDocPrepend)
2237  {
2238  out << getCustomStorageData();
2239  return;
2240  } //end special GROUP CACHE table construction
2241  } //end handle special GROUP CACHE table
2242 
2243  out << "{\n";
2244  out << "\"NAME\" : \"" << tableName_ << "\",\n";
2245 
2246  // out << "\"VERSION\": \"" << version_ << "\",\n";
2247 
2248  out << "\"COMMENT\" : ";
2249 
2250  // output escaped comment
2251  std::string val;
2252  val = comment_;
2253  out << "\"";
2254  for(unsigned int i = 0; i < val.size(); ++i)
2255  {
2256  if(val[i] == '\n')
2257  out << "\\n";
2258  else if(val[i] == '\t')
2259  out << "\\t";
2260  else if(val[i] == '\r')
2261  out << "\\r";
2262  else
2263  {
2264  // escaped characters need a
2265  if(val[i] == '"' || val[i] == '\\')
2266  out << '\\';
2267  out << val[i];
2268  }
2269  }
2270  out << "\",\n";
2271 
2272  out << "\"AUTHOR\" : \"" << author_ << "\",\n";
2273  out << "\"CREATION_TIME\" : " << creationTime_ << ",\n";
2274 
2275  // USELESS... out << "\"NUM_OF_COLS\" : " << getNumberOfColumns() << ",\n";
2276  // USELESS... out << "\"NUM_OF_ROWS\" : " << getNumberOfRows() << ",\n";
2277 
2278  out << "\"COL_TYPES\" : {\n";
2279  for(int c = 0; c < (int)getNumberOfColumns(); ++c)
2280  {
2281  out << "\t\t\"" << columnsInfo_[c].getStorageName() << "\" : ";
2282  out << "\"" << columnsInfo_[c].getDataType() << "\"";
2283  if(c + 1 < (int)getNumberOfColumns())
2284  out << ",";
2285  out << "\n";
2286  }
2287  out << "},\n"; // close COL_TYPES
2288 
2289  out << "\"DATA_SET\" : [\n";
2290  // int num;
2291  for(int r = 0; r < (int)getNumberOfRows(); ++r)
2292  {
2293  out << "\t{\n";
2294  for(int c = 0; c < (int)getNumberOfColumns(); ++c)
2295  {
2296  out << "\t\t\"" << columnsInfo_[c].getStorageName() << "\" : ";
2297 
2298  out << "\"" << getEscapedValueAsString(r, c, false)
2299  << "\""; // do not convert env variables
2300 
2301  if(c + 1 < (int)getNumberOfColumns())
2302  out << ",";
2303  out << "\n";
2304  }
2305  out << "\t}";
2306  if(r + 1 < (int)getNumberOfRows())
2307  out << ",";
2308  out << "\n";
2309  }
2310  out << "]\n"; // close DATA_SET
2311 
2312  out << "}";
2313 } // end printJSON()
2314 
2315 //==============================================================================
2316 void TableView::printCSV(std::ostream& out /* = std::cout */,
2317  const std::string& valueDelimeter /* = "," */,
2318  const std::string& recordDelimeter /* = "\n" */,
2319  bool includeColumnNames /* = false */) const
2320 {
2321  { //handle special GROUP CACHE table
2322  std::string tmpCachePrepend = TableBase::GROUP_CACHE_PREPEND;
2323  tmpCachePrepend = TableBase::convertToCaps(tmpCachePrepend);
2324  std::string tmpJsonDocPrepend = TableBase::JSON_DOC_PREPEND;
2325  tmpJsonDocPrepend = TableBase::convertToCaps(tmpJsonDocPrepend);
2326  __COUTS__(32) << " '" << tableName_ << "' vs " << tmpCachePrepend << " or "
2327  << tmpJsonDocPrepend << __E__;
2328  //if special GROUP CACHE table, handle construction in a special way
2329  if(tableName_.substr(0, tmpCachePrepend.length()) == tmpCachePrepend ||
2330  tableName_.substr(0, tmpJsonDocPrepend.length()) == tmpJsonDocPrepend)
2331  {
2332  __SS__ << "Cannot convert custom storage data to CSV!" << __E__;
2333  __SS_THROW__;
2334  // out << getCustomStorageData();
2335  // return;
2336  } //end special GROUP CACHE table construction
2337  } //end handle special GROUP CACHE table
2338 
2339  for(int c = 0; includeColumnNames && c < (int)getNumberOfColumns(); ++c)
2340  {
2341  if(c)
2342  out << valueDelimeter;
2343  out << "\"" << columnsInfo_[c].getStorageName() << "\"";
2344  }
2345  if(includeColumnNames)
2346  out << recordDelimeter;
2347 
2348  for(int r = 0; r < (int)getNumberOfRows(); ++r)
2349  {
2350  for(int c = 0; c < (int)getNumberOfColumns(); ++c)
2351  {
2352  if(c)
2353  out << valueDelimeter;
2354  out << "\""
2355  << getEscapedValueAsString(r, c, false, true /* quotesToDoubleQuotes*/)
2356  << "\""; // do not convert env variables, convert " to "" for excel style
2357  }
2358  out << recordDelimeter;
2359  }
2360 
2361 } // end printCSV()
2362 
2363 //==============================================================================
2371 int TableView::fillFromJSON(const std::string& json)
2372 {
2373  { //handle special GROUP CACHE table
2374  std::string tmpCachePrepend = TableBase::GROUP_CACHE_PREPEND;
2375  tmpCachePrepend = TableBase::convertToCaps(tmpCachePrepend);
2376  std::string tmpJsonDocPrepend = TableBase::JSON_DOC_PREPEND;
2377  tmpJsonDocPrepend = TableBase::convertToCaps(tmpJsonDocPrepend);
2378 
2379  //if special JSON DOC table, handle construction in a special way
2380  if(tableName_.substr(0, tmpJsonDocPrepend.length()) == tmpJsonDocPrepend ||
2381  tableName_.substr(0, tmpCachePrepend.length()) == tmpCachePrepend)
2382  {
2383  __COUTS__(3) << "Special JSON doc: " << json << __E__;
2384  setCustomStorageData(json);
2385  return 0; //success
2386  } //end special JSON DOC table construction or special GROUP CACHE table construction
2387  } //end handle special GROUP CACHE table
2388 
2389  bool dbg = false; //tableName_ == "TABLE_GROUP_METADATA";
2390  bool rawData = getSourceRawData_;
2391  if(getSourceRawData_)
2392  { // only get source raw data once, then revert member variable
2393  __COUTV__(getSourceRawData_);
2394  getSourceRawData_ = false;
2395  sourceRawData_ = ""; // clear for this fill
2396  }
2397 
2398  std::map<std::string /*key*/, unsigned int /*entries/rows*/> keyEntryCountMap;
2399  std::vector<std::string> keys;
2400  keys.push_back("NAME");
2401  keys.push_back("COMMENT");
2402  keys.push_back("AUTHOR");
2403  keys.push_back("CREATION_TIME");
2404  // keys.push_back ("COL_TYPES");
2405  keys.push_back("DATA_SET");
2406  enum
2407  {
2408  CV_JSON_FILL_NAME,
2409  CV_JSON_FILL_COMMENT,
2410  CV_JSON_FILL_AUTHOR,
2411  CV_JSON_FILL_CREATION_TIME,
2412  // CV_JSON_FILL_COL_TYPES,
2413  CV_JSON_FILL_DATA_SET
2414  };
2415 
2416  if(dbg)
2417  {
2418  __COUTV__(tableName_);
2419  __COUTTV__(getNumberOfRows());
2420  __COUTV__(json);
2421  }
2422 
2423  sourceColumnMismatchCount_ = 0;
2424  sourceColumnMissingCount_ = 0;
2425  sourceColumnNames_.clear(); // reset
2426  unsigned int colFoundCount = 0;
2427  unsigned int i = 0;
2428  unsigned int row = -1;
2429  unsigned int colSpeedup = 0;
2430  unsigned int startString, startNumber = 0, endNumber = -1;
2431  unsigned int bracketCount = 0;
2432  unsigned int sqBracketCount = 0;
2433  bool inQuotes = 0;
2434  bool newString = 0;
2435  bool newValue = 0;
2436  // bool isDataArray = 0;
2437  bool keyIsMatch, keyIsComment;
2438  unsigned int keyIsMatchIndex, keyIsMatchStorageIndex, keyIsMatchCommentIndex;
2439  const std::string COMMENT_ALT_KEY = "COMMENT";
2440 
2441  std::string extractedString = "", currKey = "", currVal = "";
2442  unsigned int currDepth = 0;
2443 
2444  std::vector<std::string> jsonPath;
2445  std::vector<char> jsonPathType; // indicator of type in jsonPath: { [ K
2446  char lastPopType = '_'; // either: _ { [ K
2447  // _ indicates reset pop (this happens when a new {obj} starts)
2448  unsigned int matchedKey = -1;
2449  unsigned int lastCol = -1;
2450 
2451  // find all depth 1 matching keys
2452  for(; i < json.size(); ++i)
2453  {
2454  switch(json[i])
2455  {
2456  case '"':
2457  if(i - 1 < json.size() && // ignore if escaped
2458  json[i - 1] == '\\')
2459  break;
2460 
2461  inQuotes = !inQuotes; // toggle in quotes if not escaped
2462  if(inQuotes)
2463  startString = i;
2464  else
2465  {
2466  extractedString = StringMacros::restoreJSONStringEntities(
2467  json.substr(startString + 1, i - startString - 1));
2468  newString = 1; // have new string!
2469  }
2470  break;
2471  case ':':
2472  if(inQuotes)
2473  break; // skip if in quote
2474 
2475  // must be a json object level to have a key
2476  if(jsonPathType[jsonPathType.size() - 1] != '{' ||
2477  !newString) // and must have a string for key
2478  {
2479  __COUT__ << "Invalid ':' position" << __E__;
2480  return -1;
2481  }
2482 
2483  // valid, so take key
2484  jsonPathType.push_back('K');
2485  jsonPath.push_back(extractedString);
2486  startNumber = i;
2487  newString = 0; // clear flag
2488  endNumber = -1; // reset end number index
2489  break;
2490 
2491  // if(isKey ||
2492  // isDataArray)
2493  // {
2494  // std::cout << "Invalid ':' position" << __E__;
2495  // return -1;
2496  // }
2497  // isKey = 1; //new value is a key
2498  // newValue = 1;
2499  // startNumber = i;
2500  // break;
2501  case ',':
2502  if(inQuotes)
2503  break; // skip if in quote
2504  if(lastPopType == '{') // don't need value again of nested object
2505  {
2506  // check if the nested object was the value to a key, if so, pop key
2507  if(jsonPathType[jsonPathType.size() - 1] == 'K')
2508  {
2509  lastPopType = 'K';
2510  jsonPath.pop_back();
2511  jsonPathType.pop_back();
2512  }
2513  break; // skip , handling if {obj} just ended
2514  }
2515 
2516  if(newString)
2517  currVal = extractedString;
2518  else // number value
2519  {
2520  if(endNumber == (unsigned int)-1 || // take i as end number if needed
2521  endNumber <= startNumber)
2522  endNumber = i;
2523  // extract number value
2524  if(endNumber <= startNumber) // empty data, could be {}
2525  currVal = "";
2526  else
2527  currVal = json.substr(startNumber + 1, endNumber - startNumber - 1);
2528  }
2529 
2530  currDepth = bracketCount;
2531 
2532  if(jsonPathType[jsonPathType.size() - 1] == 'K') // this is the value to key
2533  {
2534  currKey = jsonPath[jsonPathType.size() - 1];
2535  newValue = 1; // new value to consider!
2536 
2537  // pop key
2538  lastPopType = 'K';
2539  jsonPath.pop_back();
2540  jsonPathType.pop_back();
2541  }
2542  else if(jsonPathType[jsonPathType.size() - 1] ==
2543  '[') // this is a value in array
2544  {
2545  // key is last key
2546  for(unsigned int k = jsonPathType.size() - 2; k < jsonPathType.size();
2547  --k)
2548  if(jsonPathType[k] == 'K')
2549  {
2550  currKey = jsonPath[k];
2551  break;
2552  }
2553  else if(k == 0)
2554  {
2555  __COUT__ << "Invalid array position" << __E__;
2556  return -1;
2557  }
2558 
2559  newValue = 1; // new value to consider!
2560  // isDataArray = 1;
2561  }
2562  else // { is an error
2563  {
2564  __COUT__ << "Invalid ',' position" << __E__;
2565  return -1;
2566  }
2567 
2568  startNumber = i;
2569  break;
2570 
2571  case '{':
2572  if(inQuotes)
2573  break; // skip if in quote
2574  lastPopType = '_'; // reset because of new object
2575  jsonPathType.push_back('{');
2576  jsonPath.push_back("{");
2577  ++bracketCount;
2578  break;
2579 
2580  // ++bracketCount;
2581  // isDataArray = 0;
2582  // isKey = 0;
2583  // endingObject = 0;
2584  // break;
2585  case '}':
2586  if(inQuotes)
2587  break; // skip if in quote
2588 
2589  if(lastPopType != '{' && // don't need value again of nested object
2590  jsonPathType[jsonPathType.size() - 1] == 'K') // this is the value to key
2591  {
2592  currDepth = bracketCount;
2593  currKey = jsonPath[jsonPathType.size() - 1];
2594  if(newString)
2595  currVal = extractedString;
2596  else // number value
2597  {
2598  if(endNumber == (unsigned int)-1 || // take i as end number if needed
2599  endNumber <= startNumber)
2600  endNumber = i;
2601  // extract val
2602  if(endNumber <= startNumber) // empty data, could be {}
2603  currVal = "";
2604  else
2605  currVal =
2606  json.substr(startNumber + 1, endNumber - startNumber - 1);
2607  }
2608  newValue = 1; // new value to consider!
2609  // pop key
2610  jsonPath.pop_back();
2611  jsonPathType.pop_back();
2612  }
2613  // pop {
2614  if(jsonPathType[jsonPathType.size() - 1] != '{')
2615  {
2616  __COUT__ << "Invalid '}' position" << __E__;
2617  return -1;
2618  }
2619  lastPopType = '{';
2620  jsonPath.pop_back();
2621  jsonPathType.pop_back();
2622  --bracketCount;
2623  break;
2624  case '[':
2625  if(inQuotes)
2626  break; // skip if in quote
2627  jsonPathType.push_back('[');
2628  jsonPath.push_back("[");
2629  ++sqBracketCount;
2630  startNumber = i;
2631  break;
2632  case ']':
2633  if(inQuotes)
2634  break; // skip if in quote
2635 
2636  // must be an array at this level (in order to close it)
2637  if(jsonPathType[jsonPathType.size() - 1] != '[')
2638  {
2639  __COUT__ << "Invalid ']' position" << __E__;
2640  return -1;
2641  }
2642 
2643  currDepth = bracketCount;
2644 
2645  // This is an array value
2646  if(newString)
2647  currVal = extractedString;
2648  else // number value
2649  {
2650  if(endNumber == (unsigned int)-1 || // take i as end number if needed
2651  endNumber <= startNumber)
2652  endNumber = i;
2653  // extract val
2654  if(endNumber <= startNumber) // empty data, could be {}
2655  currVal = "";
2656  else
2657  currVal = json.substr(startNumber + 1, endNumber - startNumber - 1);
2658  }
2659  // isDataArray = 1;
2660 
2661  // key is last key
2662  for(unsigned int k = jsonPathType.size() - 2; k < jsonPathType.size(); --k)
2663  if(jsonPathType[k] == 'K')
2664  {
2665  currKey = jsonPath[k];
2666  break;
2667  }
2668  else if(k == 0)
2669  {
2670  __COUT__ << "Invalid array position" << __E__;
2671  return -1;
2672  }
2673 
2674  // pop [
2675  if(jsonPathType[jsonPathType.size() - 1] != '[')
2676  {
2677  __COUT__ << "Invalid ']' position" << __E__;
2678  return -1;
2679  }
2680  lastPopType = '[';
2681  jsonPath.pop_back();
2682  jsonPathType.pop_back();
2683  --sqBracketCount;
2684  break;
2685  case ' ': // white space handling for numbers
2686  case '\t':
2687  case '\n':
2688  case '\r':
2689  if(inQuotes)
2690  break; // skip if in quote
2691  if(startNumber != (unsigned int)-1 && endNumber == (unsigned int)-1)
2692  endNumber = i;
2693  startNumber = i;
2694  break;
2695  default:;
2696  }
2697 
2698  // continue;
2699 
2700  // handle a new completed value
2701  if(newValue)
2702  {
2703  if(dbg) // for debugging
2704  {
2705  std::cout << i << ":\t" << json[i] << " - ";
2706 
2707  // if(isDataArray)
2708  // std::cout << "Array:: ";
2709  // if(newString)
2710  // std::cout << "New String:: ";
2711  // else
2712  // std::cout << "New Number:: ";
2713  //
2714 
2715  std::cout << "ExtKey=";
2716  for(unsigned int k = 0; k < jsonPath.size(); ++k)
2717  std::cout << jsonPath[k] << "/";
2718  std::cout << " - ";
2719  std::cout << lastPopType << " ";
2720  std::cout << bracketCount << " ";
2721  std::cout << sqBracketCount << " ";
2722  std::cout << inQuotes << " ";
2723  std::cout << newValue << "-";
2724  std::cout << currKey << "-{" << currDepth << "}:";
2725  std::cout << currVal << " ";
2726  std::cout << startNumber << "-";
2727  std::cout << endNumber << " ";
2728  std::cout << "\n";
2729  __COUTTV__(fillWithLooseColumnMatching_);
2730  __COUTTV__(getNumberOfRows());
2731  }
2732 
2733  // extract only what we care about
2734  // for TableView only care about matching depth 1
2735 
2736  // handle matching depth 1 keys
2737 
2738  matchedKey = -1; // init to unfound
2739  for(unsigned int k = 0; k < keys.size(); ++k)
2740  if((currDepth == 1 && keys[k] == currKey) ||
2741  (currDepth > 1 && keys[k] == jsonPath[1]))
2742  matchedKey = k;
2743 
2744  if(rawData)
2745  {
2746  // raw data handling fills raw data string with row/col values
2747 
2748  if(currDepth == 1)
2749  {
2750  if(matchedKey == CV_JSON_FILL_COMMENT)
2751  setComment(currVal);
2752  else if(matchedKey == CV_JSON_FILL_AUTHOR)
2753  setAuthor(currVal);
2754  else if(matchedKey == CV_JSON_FILL_CREATION_TIME)
2755  setCreationTime(strtol(currVal.c_str(), 0, 10));
2756  }
2757  else if(currDepth == 2)
2758  {
2759  // encode URI component so commas are surviving delimiter
2760  sourceRawData_ += StringMacros::encodeURIComponent(currKey) + "," +
2761  StringMacros::encodeURIComponent(currVal) + ",";
2762  sourceColumnNames_.emplace(currKey);
2763  }
2764  }
2765  else if(matchedKey != (unsigned int)-1)
2766  {
2767  if(dbg)
2768  __COUTT__ << "New Data for:: key[" << matchedKey << "]-"
2769  << keys[matchedKey] << "\n";
2770 
2771  switch(matchedKey)
2772  {
2773  case CV_JSON_FILL_NAME:
2774  // table name is now constant, set by parent TableBase
2775  if(currDepth == 1)
2776  {
2777  // setTableName(currVal);
2778  // check for consistency, and show warning
2779  if(currVal != getTableName() &&
2780  getTableName() !=
2781  "TABLE_GROUP_METADATA") // allow metadata table to be illegal, since it is created by ConfigurationManager.cc
2782  __COUT_WARN__ << "JSON-fill Table name mismatch: " << currVal
2783  << " vs " << getTableName() << __E__;
2784  }
2785  break;
2786  case CV_JSON_FILL_COMMENT:
2787  if(currDepth == 1)
2788  setComment(currVal);
2789  break;
2790  case CV_JSON_FILL_AUTHOR:
2791  if(currDepth == 1)
2792  setAuthor(currVal);
2793  break;
2794  case CV_JSON_FILL_CREATION_TIME:
2795  if(currDepth == 1)
2796  setCreationTime(strtol(currVal.c_str(), 0, 10));
2797  break;
2798  // case CV_JSON_FILL_COL_TYPES:
2799  //
2800  // break;
2801  case CV_JSON_FILL_DATA_SET:
2802  if(dbg)
2803  __COUTT__ << "CV_JSON_FILL_DATA_SET New Data for::" << matchedKey
2804  << "]-" << keys[matchedKey] << "/" << currDepth
2805  << ".../" << currKey << "\n";
2806 
2807  if(currDepth == 2) // second level depth
2808  {
2809  // if matches first column name.. then add new row
2810  // else add to current row
2811  unsigned int col, ccnt = 0;
2812  unsigned int noc = getNumberOfColumns();
2813  for(; ccnt < noc; ++ccnt)
2814  {
2815  // use colSpeedup to change the first column we search
2816  // for each iteration.. since we expect the data to
2817  // be arranged in column order
2818 
2819  if(fillWithLooseColumnMatching_)
2820  {
2821  // loose column matching makes no attempt to
2822  // match the column names
2823  // just assumes the data is in the correct order
2824 
2825  col = colSpeedup;
2826 
2827  // auto matched
2828  if(col <= lastCol) // add row (use lastCol in case new
2829  // column-0 was added
2830  row = addRow();
2831  lastCol = col;
2832  if(getNumberOfRows() == 1) // only for first row
2833  sourceColumnNames_.emplace(currKey);
2834 
2835  // add value to row and column
2836 
2837  if(row >= getNumberOfRows())
2838  {
2839  __SS__ << "Invalid row"
2840  << __E__; // should be impossible?
2841  std::cout << ss.str();
2842  __SS_THROW__;
2843  return -1;
2844  }
2845 
2846  theDataView_[row][col] =
2847  currVal; // THERE IS NO CHECK FOR WHAT IS READ FROM
2848  // THE DATABASE. IT SHOULD BE ALREADY
2849  // CONSISTENT
2850  break;
2851  }
2852  else
2853  {
2854  col = (ccnt + colSpeedup) % noc;
2855 
2856  // match key by ignoring '_'
2857  // also accept COMMENT == COMMENT_DESCRIPTION
2858  // (this is for backwards compatibility..)
2859  keyIsMatch = true;
2860  keyIsComment = true;
2861  for(keyIsMatchIndex = 0,
2862  keyIsMatchStorageIndex = 0,
2863  keyIsMatchCommentIndex = 0;
2864  keyIsMatchIndex < currKey.size();
2865  ++keyIsMatchIndex)
2866  {
2867  if(columnsInfo_[col]
2868  .getStorageName()[keyIsMatchStorageIndex] ==
2869  '_')
2870  ++keyIsMatchStorageIndex; // skip to next storage
2871  // character
2872  if(currKey[keyIsMatchIndex] == '_')
2873  continue; // skip to next character
2874 
2875  // match to storage name
2876  if(keyIsMatchStorageIndex >=
2877  columnsInfo_[col].getStorageName().size() ||
2878  currKey[keyIsMatchIndex] !=
2879  columnsInfo_[col]
2880  .getStorageName()[keyIsMatchStorageIndex])
2881  {
2882  // size mismatch or character mismatch
2883  keyIsMatch = false;
2884  if(!keyIsComment)
2885  break;
2886  }
2887 
2888  // check also if alternate comment is matched
2889  if(keyIsComment &&
2890  keyIsMatchCommentIndex < COMMENT_ALT_KEY.size())
2891  {
2892  if(currKey[keyIsMatchIndex] !=
2893  COMMENT_ALT_KEY[keyIsMatchCommentIndex])
2894  {
2895  // character mismatch with COMMENT
2896  keyIsComment = false;
2897  }
2898  }
2899 
2900  ++keyIsMatchStorageIndex; // go to next character
2901  }
2902 
2903  if(dbg)
2904  {
2905  __COUTTV__(keyIsMatch);
2906  __COUTTV__(keyIsComment);
2907  __COUTTV__(currKey);
2908  __COUTTV__(columnsInfo_[col].getStorageName());
2909  __COUTTV__(getNumberOfRows());
2910  }
2911 
2912  if(keyIsMatch || keyIsComment) // currKey ==
2913  // columnsInfo_[c].getStorageName())
2914  {
2915  if(keyEntryCountMap.find(currKey) ==
2916  keyEntryCountMap.end())
2917  keyEntryCountMap[currKey] =
2918  0; // show follow row count
2919  else
2920  ++keyEntryCountMap.at(currKey);
2921 
2922  // add row (based on entry counts)
2923  if(keyEntryCountMap.size() == 1 ||
2924  (keyEntryCountMap.at(currKey) &&
2925  keyEntryCountMap.at(currKey) >
2926  row)) // if(col <= lastCol)
2927  {
2928  if(getNumberOfRows()) // skip first time
2929  sourceColumnMissingCount_ +=
2930  getNumberOfColumns() - colFoundCount;
2931 
2932  colFoundCount = 0; // reset column found count
2933  row = addRow();
2934  }
2935  lastCol = col;
2936  ++colFoundCount;
2937 
2938  if(getNumberOfRows() == 1) // only for first row
2939  sourceColumnNames_.emplace(currKey);
2940 
2941  // add value to row and column
2942 
2943  if(row >= getNumberOfRows())
2944  {
2945  __SS__ << "Invalid row"
2946  << __E__; // should be impossible?!
2947  __COUT__ << "\n" << ss.str();
2948  __SS_THROW__;
2949  return -1; // never gets here
2950  }
2951 
2952  theDataView_[row][col] = currVal;
2953  break;
2954  }
2955  }
2956  }
2957 
2958  if(ccnt >= getNumberOfColumns())
2959  {
2960  __COUT__
2961  << "Invalid column in JSON source data: " << currKey
2962  << " not found in column names of table named "
2963  << getTableName() << "."
2964  << __E__; // input data doesn't match config description
2965 
2966  // CHANGED on 11/10/2016
2967  // to.. try just not populating data instead of error
2968  ++sourceColumnMismatchCount_; // but count errors
2969  if(getNumberOfRows() ==
2970  1) // only for first row, track source column names
2971  sourceColumnNames_.emplace(currKey);
2972 
2973  //__SS_THROW__;
2974  __COUT_WARN__ << "Trying to ignore error, and not populating "
2975  "missing column."
2976  << __E__;
2977  }
2978  else // short cut to proper column hopefully in next search
2979  colSpeedup = (colSpeedup + 1) % noc;
2980  }
2981  break;
2982  default:; // unknown match?
2983  } // end switch statement to match json key
2984  } // end matched key if statement
2985 
2986  // clean up handling of new value
2987 
2988  newString = 0; // toggle flag
2989  newValue = 0; // toggle flag
2990  // isDataArray = 0;
2991  endNumber = -1; // reset end number index
2992  }
2993 
2994  // if(i>200) break; //185
2995  }
2996 
2997  //__COUT__ << "Done!" << __E__;
2998  __COUTTV__(fillWithLooseColumnMatching_);
2999  __COUTTV__(sourceColumnNames_.size());
3000  //__COUTV__(tableName_); // << "tableName_ = " << tableName_
3001 
3002  if(!fillWithLooseColumnMatching_ && sourceColumnMissingCount_ > 0)
3003  {
3004  __COUTV__(sourceColumnMissingCount_);
3005  __SS__ << "Can not ignore errors because not every column was found in the "
3006  "source data!"
3007  << ". Please see the details below:\n\n"
3008  << getMismatchColumnInfo() << StringMacros::stackTrace();
3009  __SS_ONLY_THROW__;
3010  }
3011 
3012  if(sourceColumnNames_.size() ==
3013  0) //if not populated by data (i.e. zero records), then use default column names
3014  {
3015  for(unsigned int i = 0; i < getNumberOfColumns(); ++i)
3016  sourceColumnNames_.emplace(getColumnsInfo()[i].getStorageName());
3017  }
3018 
3019  // print();
3020 
3021  return 0; // success
3022 } // end fillFromJSON()
3023 
3024 //==============================================================================
3025 std::string TableView::getMismatchColumnInfo(void) const
3026 {
3027  const std::set<std::string>& srcColNames = getSourceColumnNames();
3028  std::set<std::string> destColNames = getColumnStorageNames();
3029 
3030  __SS__ << "The source column size was found to be " << srcColNames.size()
3031  << ", and the current number of columns for this table is "
3032  << getNumberOfColumns() << ". This resulted in a count of "
3033  << getSourceColumnMismatch() << " source column mismatches, and a count of "
3034  << getSourceColumnMissing() << " table entries missing in "
3035  << getNumberOfRows() << " row(s) of data." << __E__;
3036 
3037  ss << "\n\n"
3038  << srcColNames.size()
3039  << " Source column names in ALPHABETICAL order were as follows:\n";
3040  char index = 'a';
3041  std::string preIndexStr = "";
3042  for(auto& srcColName : srcColNames)
3043  {
3044  if(destColNames.find(srcColName) == destColNames.end())
3045  ss << "\n\t*** " << preIndexStr << index << ". " << srcColName << " ***";
3046  else
3047  ss << "\n\t" << preIndexStr << index << ". " << srcColName;
3048 
3049  if(index == 'z') // wrap-around
3050  {
3051  preIndexStr += 'a'; // keep adding index 'digits' for wrap-around
3052  index = 'a';
3053  }
3054  else
3055  ++index;
3056  }
3057  ss << __E__;
3058 
3059  ss << "\n\n"
3060  << destColNames.size()
3061  << " Current table column names in ALPHABETICAL order are as follows:\n";
3062  index = 'a';
3063  preIndexStr = "";
3064  for(auto& destColName : destColNames)
3065  {
3066  if(srcColNames.find(destColName) == srcColNames.end())
3067  ss << "\n\t*** " << preIndexStr << index << ". " << destColName << " ***";
3068  else
3069  ss << "\n\t" << preIndexStr << index << ". " << destColName;
3070 
3071  if(index == 'z') // wrap-around
3072  {
3073  preIndexStr += 'a'; // keep adding index 'digits' for wrap-around
3074  index = 'a';
3075  }
3076  else
3077  ++index;
3078  }
3079  ss << __E__;
3080  return ss.str();
3081 } // end getMismatchColumnInfo()
3082 
3083 //==============================================================================
3084 bool TableView::isURIEncodedCommentTheSame(const std::string& comment) const
3085 {
3086  std::string compareStr = StringMacros::decodeURIComponent(comment);
3087  return comment_ == compareStr;
3088 }
3093 //{
3094 // __COUT__ << "valueStr " << valueStr << __E__;
3095 //
3096 // if(!(c < columnsInfo_.size() && r < getNumberOfRows()))
3097 // {
3098 // __SS__ << "Invalid row (" << (int)r << ") col (" << (int)c << ") requested!" <<
3099 //__E__;
3100 // __SS_THROW__;
3101 // }
3102 //
3103 // __COUT__ << "originalValueStr " << theDataView_[r][c] << __E__;
3104 //
3105 // if(columnsInfo_[c].getDataType() == TableViewColumnInfo::DATATYPE_TIME)
3106 // {
3107 // time_t valueTime(strtol(valueStr.c_str(),0,10));
3108 // time_t originalValueTime;
3109 // getValue(originalValueTime,r,c);
3110 // __COUT__ << "time_t valueStr " << valueTime << __E__;
3111 // __COUT__ << "time_t originalValueStr " << originalValueTime << __E__;
3112 // return valueTime == originalValueTime;
3113 // }
3114 // else
3115 // {
3116 // return valueStr == theDataView_[r][c];
3117 // }
3118 //}
3119 
3120 //==============================================================================
3131 void TableView::fillFromCSV(const std::string& data,
3132  const int& dataOffset /* = 0 */,
3133  const std::string& author /* = "" */,
3134  const char rowDelimter /* = ',' */,
3135  const char colDelimter /* = '\n' */)
3136 {
3137  int row = dataOffset;
3138  int col = 0;
3139  std::string currentValue = "";
3140  bool insideQuotes = false;
3141  int authorCol = findColByType(TableViewColumnInfo::TYPE_AUTHOR);
3142  int timestampCol = findColByType(TableViewColumnInfo::TYPE_TIMESTAMP);
3143 
3144  for(size_t i = 0; i < data.size(); ++i)
3145  {
3146  char c = data[i];
3147  const char nextChar = (i + 1 < data.size() ? data[i + 1] : ' ');
3148 
3149  if(c == '"')
3150  {
3151  if(insideQuotes && nextChar == '"') // "" will escape a double-quote in CSV
3152  {
3153  // Escaped double-quote
3154  currentValue += '"';
3155  ++i; //skip next quote
3156  }
3157  else
3158  {
3159  // Toggle quote mode
3160  insideQuotes = !insideQuotes;
3161  }
3162  }
3163  else if(c == rowDelimter && !insideQuotes)
3164  {
3165  if(col == 0 && row >= (int)getNumberOfRows())
3166  addRow(author);
3167  setValueAsString(StringMacros::trim(currentValue), row, col);
3168  ++col;
3169  currentValue = "";
3170  }
3171  else if((c == colDelimter || c == '\r') && !insideQuotes)
3172  {
3173  if(col > 0)
3174  {
3175  setValueAsString(StringMacros::trim(currentValue), row, col);
3176  __COUTV__(getValueAsString(row, col));
3177 
3178  //if row is actually column names, then delete the row
3179  if(getValueAsString(row, col) == getColumnsInfo()[col].getStorageName())
3180  {
3181  __COUT__ << "First row detected as column names." << __E__;
3182  deleteRow(row);
3183  --row; //rewind
3184  }
3185  else
3186  {
3187  //enforce author and timestamp not from CSV data
3188  setValue(author, row, authorCol);
3189  setValue(time(0), row, timestampCol);
3190  }
3191 
3192  col = 0;
3193  ++row; //prepare for next row
3194  currentValue = "";
3195  }
3196  }
3197  else
3198  {
3199  currentValue += c;
3200  }
3201  } //end text loop
3202 
3203  // Add last value if any
3204  if(col > 0)
3205  {
3206  setValueAsString(StringMacros::trim(currentValue), row, col);
3207  __COUTV__(getValueAsString(row, col));
3208  __COUTV__(getValueAsString(row, timestampCol));
3209 
3210  //if row is actually column names, then delete the row
3211  if(getValueAsString(row, col) == getColumnsInfo()[col].getStorageName())
3212  {
3213  __COUT__ << "First row detected as column names." << __E__;
3214  deleteRow(row);
3215  --row; //rewind
3216  }
3217  else
3218  {
3219  //enforce author and timestamp not from CSV data
3220  setValue(author, row, authorCol);
3221  setValue(time(0), row, timestampCol);
3222  }
3223 
3224  col = 0;
3225  ++row; //prepare for next row
3226  currentValue = "";
3227  }
3228 
3229  init(); // verify new table (throws runtime_errors)
3230 
3231 } //end fillFromCSV
3232 
3233 //==============================================================================
3259 int TableView::fillFromEncodedCSV(const std::string& data,
3260  const int& dataOffset,
3261  const std::string& author)
3262 {
3263  int retVal = 0;
3264 
3265  int r = dataOffset;
3266  int c = 0;
3267 
3268  int i = 0; // use to parse data std::string
3269  int j = data.find(',', i); // find next cell delimiter
3270  int k = data.find(';', i); // find next row delimiter
3271 
3272  bool rowWasModified;
3273  unsigned int countRowsModified = 0;
3274  int authorCol = findColByType(TableViewColumnInfo::TYPE_AUTHOR);
3275  int timestampCol = findColByType(TableViewColumnInfo::TYPE_TIMESTAMP);
3276  // std::string valueStr, tmpTimeStr, originalValueStr;
3277 
3278  while(k != (int)(std::string::npos))
3279  {
3280  rowWasModified = false;
3281  if(r >= (int)getNumberOfRows())
3282  {
3283  addRow();
3284  //__COUT__ << "Row added" << __E__;
3285  rowWasModified = true;
3286  }
3287 
3288  while(j < k && j != (int)(std::string::npos))
3289  {
3290  //__COUT__ << "Col " << (int)c << __E__;
3291 
3292  // skip last 2 columns
3293  if(c >= (int)getNumberOfColumns() - 2)
3294  {
3295  i = j + 1;
3296  j = data.find(',', i); // find next cell delimiter
3297  ++c;
3298  continue;
3299  }
3300 
3301  if(setURIEncodedValue(data.substr(i, j - i), r, c))
3302  rowWasModified = true;
3303 
3304  i = j + 1;
3305  j = data.find(',', i); // find next cell delimiter
3306  ++c;
3307  }
3308 
3309  // if row was modified, assign author and timestamp
3310  if(author != "" && rowWasModified)
3311  {
3312  __COUTT__ << "Row=" << (int)r << " was modified!" << __E__;
3313  setValue(author, r, authorCol);
3314  setValue(time(0), r, timestampCol);
3315  }
3316 
3317  if(rowWasModified)
3318  ++countRowsModified;
3319 
3320  ++r;
3321  c = 0;
3322 
3323  i = k + 1;
3324  j = data.find(',', i); // find next cell delimiter
3325  k = data.find(';', i); // find new row delimiter
3326  }
3327 
3328  // delete excess rows
3329  while(r < (int)getNumberOfRows())
3330  {
3331  deleteRow(r);
3332  __COUT__ << "Row deleted: " << (int)r << __E__;
3333  ++countRowsModified;
3334  }
3335 
3336  __COUT_INFO__ << "countRowsModified=" << countRowsModified << __E__;
3337 
3338  if(!countRowsModified)
3339  {
3340  // check that source columns match storage name
3341  // otherwise allow same data...
3342 
3343  bool match = getColumnStorageNames().size() == getSourceColumnNames().size();
3344  if(match)
3345  {
3346  for(auto& destColName : getColumnStorageNames())
3347  if(getSourceColumnNames().find(destColName) ==
3348  getSourceColumnNames().end())
3349  {
3350  __COUT__ << "Found column name mismach for '" << destColName
3351  << "'... So allowing same data!" << __E__;
3352 
3353  match = false;
3354  break;
3355  }
3356  }
3357  // if still a match, do not allow!
3358  if(match)
3359  {
3360  __SS__ << "No rows were modified! No reason to fill a view with same content."
3361  << __E__;
3362  __COUT__ << "\n" << ss.str();
3363  return -1;
3364  }
3365  // else mark with retVal
3366  retVal = 1;
3367  } // end same check
3368 
3369  // print(); //for debugging
3370 
3371  // setup sourceColumnNames_ to be correct
3372  sourceColumnNames_.clear();
3373  for(unsigned int i = 0; i < getNumberOfColumns(); ++i)
3374  sourceColumnNames_.emplace(getColumnsInfo()[i].getStorageName());
3375 
3376  init(); // verify new table (throws runtime_errors)
3377 
3378  // printout for debugging
3379  // __SS__ << "\n";
3380  // print(ss);
3381  // __COUT__ << "\n" << ss.str() << __E__;
3382 
3383  return retVal;
3384 } // end fillFromEncodedCSV()
3385 
3386 //==============================================================================
3395 bool TableView::setURIEncodedValue(const std::string& value,
3396  const unsigned int& r,
3397  const unsigned int& c,
3398  const std::string& author)
3399 {
3400  if(!(c < columnsInfo_.size() && r < getNumberOfRows()))
3401  {
3402  __SS__ << "Invalid row (" << (int)r << ") col (" << (int)c << ") requested!"
3403  << "Number of Rows = " << getNumberOfRows()
3404  << "Number of Columns = " << columnsInfo_.size() << __E__;
3405  print(ss);
3406  __SS_THROW__;
3407  }
3408 
3409  std::string valueStr = StringMacros::decodeURIComponent(value);
3410  std::string originalValueStr =
3411  getValueAsString(r, c, false); // do not convert env variables
3412 
3413  //__COUT__ << "valueStr " << valueStr << __E__;
3414  //__COUT__ << "originalValueStr " << originalValueStr << __E__;
3415 
3416  if(columnsInfo_[c].getDataType() == TableViewColumnInfo::DATATYPE_NUMBER)
3417  {
3418  // check if valid number
3419  std::string convertedString = StringMacros::convertEnvironmentVariables(valueStr);
3420  // do not check here, let init check
3421  // if this is a link to valid number, then this is an improper check.
3422  // if(!StringMacros::isNumber(convertedString))
3423  // {
3424  // __SS__ << "\tIn configuration " << tableName_
3425  // << " at column=" << columnsInfo_[c].getName() << " the value
3426  // set
3427  //("
3428  // << convertedString << ")"
3429  // << " is not a number! Please fix it or change the column
3430  // type..."
3431  // << __E__;
3432  // __SS_THROW__;
3433  // }
3434  theDataView_[r][c] = valueStr;
3435 
3436  // is it here that a new exception should be added to enforce min and max, given that they only appear with number type?
3437  }
3438  else if(columnsInfo_[c].getDataType() == TableViewColumnInfo::DATATYPE_TIME)
3439  {
3440  // valueStr = StringMacros::decodeURIComponent(data.substr(i,j-i));
3441  //
3442  // getValue(tmpTimeStr,r,c);
3443  // if(valueStr != tmpTimeStr)//theDataView_[r][c])
3444  // {
3445  // __COUT__ << "valueStr=" << valueStr <<
3446  // " theDataView_[r][c]=" << tmpTimeStr << __E__;
3447  // rowWasModified = true;
3448  // }
3449 
3450  setValue(time_t(strtol(valueStr.c_str(), 0, 10)), r, c);
3451  }
3452  else
3453  theDataView_[r][c] = valueStr;
3454 
3455  bool rowWasModified =
3456  (originalValueStr !=
3457  getValueAsString(r, c, false)); // do not convert env variables
3458 
3459  // if row was modified, assign author and timestamp
3460  if(author != "" && rowWasModified)
3461  {
3462  __COUT__ << "Row=" << (int)r << " was modified!" << __E__;
3463  int authorCol = findColByType(TableViewColumnInfo::TYPE_AUTHOR);
3464  int timestampCol = findColByType(TableViewColumnInfo::TYPE_TIMESTAMP);
3465  setValue(author, r, authorCol);
3466  setValue(time(0), r, timestampCol);
3467  }
3468 
3469  return rowWasModified;
3470 } // end setURIEncodedValue()
3471 
3472 //==============================================================================
3473 void TableView::resizeDataView(unsigned int nRows, unsigned int nCols)
3474 {
3475  // FIXME This maybe should disappear but I am using it in ConfigurationHandler
3476  // still...
3477  theDataView_.resize(nRows, std::vector<std::string>(nCols));
3478 }
3479 
3480 //==============================================================================
3487 unsigned int TableView::addRow(
3488  const std::string& author,
3489  unsigned char
3490  incrementUniqueData /* = false */, // leave as unsigned char rather than
3491  // bool, too many things (e.g. strings)
3492  // evaluate successfully to bool values
3493  const std::string& baseNameAutoUID /* = "" */,
3494  unsigned int rowToAdd /* = -1 */,
3495  std::string childLinkIndex /* = "" */,
3496  std::string groupId /* = "" */)
3497 {
3498  // default to last row
3499  if(rowToAdd == (unsigned int)-1)
3500  rowToAdd = getNumberOfRows();
3501 
3502  theDataView_.resize(getNumberOfRows() + 1,
3503  std::vector<std::string>(getNumberOfColumns()));
3504 
3505  // shift data down the table if necessary
3506  for(unsigned int r = getNumberOfRows() - 2; r >= rowToAdd; --r)
3507  {
3508  if(r == (unsigned int)-1)
3509  break; // quit wrap around case
3510  for(unsigned int col = 0; col < getNumberOfColumns(); ++col)
3511  theDataView_[r + 1][col] = theDataView_[r][col];
3512  }
3513 
3514  std::vector<std::string> defaultRowValues = getDefaultRowValues();
3515 
3516  // char indexString[1000];
3517  std::string tmpString, baseString;
3518  // bool foundAny;
3519  // unsigned int index;
3520  // unsigned int maxUniqueData;
3521  std::string numString;
3522 
3523  // fill each col of new row with default values
3524  // if a row is a unique data row, increment last row in attempt to make a legal
3525  // column
3526  for(unsigned int col = 0; col < getNumberOfColumns(); ++col)
3527  {
3528  // if(incrementUniqueData)
3529  // __COUT__ << col << " " << columnsInfo_[col].getType() << " basename= " <<
3530  // baseNameAutoUID << __E__;
3531 
3532  // baseNameAutoUID indicates to attempt to make row unique
3533  // add index to max number
3534  if(incrementUniqueData &&
3535  (col == getColUID() || columnsInfo_[col].isChildLinkGroupID() ||
3536  (getNumberOfRows() > 1 &&
3537  (columnsInfo_[col].getType() == TableViewColumnInfo::TYPE_UNIQUE_DATA ||
3538  columnsInfo_[col].getType() ==
3539  TableViewColumnInfo::TYPE_UNIQUE_GROUP_DATA))))
3540  {
3541  if(col == getColUID() || columnsInfo_[col].isChildLinkGroupID())
3543  rowToAdd, col, baseNameAutoUID /*baseValueAsString*/);
3544  else
3545  setUniqueColumnValue(rowToAdd,
3546  col,
3547  "" /* baseValueAsString */,
3548  false /* doMathAppendStrategy */,
3549  childLinkIndex,
3550  groupId);
3551  }
3552  else
3553  theDataView_[rowToAdd][col] = defaultRowValues[col];
3554  }
3555 
3556  if(author != "")
3557  {
3558  __COUT__ << "Row=" << rowToAdd << " was created!" << __E__;
3559 
3560  int authorCol = findColByType(TableViewColumnInfo::TYPE_AUTHOR);
3561  int timestampCol = findColByType(TableViewColumnInfo::TYPE_TIMESTAMP);
3562  setValue(author, rowToAdd, authorCol);
3563  setValue(time(0), rowToAdd, timestampCol);
3564  }
3565 
3566  return rowToAdd;
3567 } // end addRow()
3568 
3569 //==============================================================================
3573 {
3574  if(r >= (int)getNumberOfRows())
3575  {
3576  // out of bounds
3577  __SS__ << "Row " << (int)r
3578  << " is out of bounds (Row Count = " << getNumberOfRows()
3579  << ") and can not be deleted." << __E__;
3580  __SS_THROW__;
3581  }
3582 
3583  theDataView_.erase(theDataView_.begin() + r);
3584 } // end deleteRow()
3585 
3586 //==============================================================================
3603  const unsigned int& c,
3604  bool& isGroup,
3605  std::pair<unsigned int /*link col*/, unsigned int /*link id col*/>& linkPair) const
3606 {
3607  if(!(c < columnsInfo_.size()))
3608  {
3609  __SS__ << "Invalid col (" << (int)c << ") requested for child link!" << __E__;
3610  __SS_THROW__;
3611  }
3612 
3613  //__COUT__ << "getChildLink for col: " << (int)c << "-" <<
3614  // columnsInfo_[c].getType() << "-" << columnsInfo_[c].getName() << __E__;
3615 
3616  // check if column is a child link UID
3617  if((isGroup = columnsInfo_[c].isChildLinkGroupID()) ||
3618  columnsInfo_[c].isChildLinkUID())
3619  {
3620  // must be part of unique link, (or invalid table?)
3621  //__COUT__ << "col: " << (int)c << __E__;
3622  linkPair.second = c;
3623  std::string index = columnsInfo_[c].getChildLinkIndex();
3624 
3625  //__COUT__ << "index: " << index << __E__;
3626 
3627  // find pair link
3628  for(unsigned int col = 0; col < columnsInfo_.size(); ++col)
3629  {
3630  //__COUT__ << "try: " << col << "-" << columnsInfo_[col].getType() << "-" <<
3631  // columnsInfo_[col].getName() << __E__;
3632  if(col == c)
3633  continue; // skip column c that we know
3634  else if(columnsInfo_[col].isChildLink() &&
3635  index == columnsInfo_[col].getChildLinkIndex())
3636  {
3637  // found match!
3638  //__COUT__ << "getChildLink Found match for col: " << (int)c << " at " <<
3639  // col << __E__;
3640  linkPair.first = col;
3641  return true;
3642  }
3643  }
3644 
3645  // if here then invalid table!
3646  __SS__ << "\tIn view: " << tableName_
3647  << ", Can't find complete child link for column name "
3648  << columnsInfo_[c].getName() << __E__;
3649  __SS_THROW__;
3650  }
3651 
3652  if(!columnsInfo_[c].isChildLink())
3653  return false; // cant be unique link
3654 
3655  // this is child link, so find pair link uid or gid column
3656  linkPair.first = c;
3657  std::string index = columnsInfo_[c].getChildLinkIndex();
3658 
3659  //__COUT__ << "index: " << index << __E__;
3660 
3661  // find pair link
3662  for(unsigned int col = 0; col < columnsInfo_.size(); ++col)
3663  {
3664  //__COUT__ << "try: " << col << "-" << columnsInfo_[col].getType() << "-" <<
3665  // columnsInfo_[col].getName() << __E__;
3666  if(col == c)
3667  continue; // skip column c that we know
3668  // __COUT__ << "try: " << col << "-" << columnsInfo_[col].getType() <<
3669  // "-" << columnsInfo_[col].getName() <<
3670  // "-u" << columnsInfo_[col].isChildLinkUID() <<
3671  // "-g" << columnsInfo_[col].isChildLinkGroupID() << __E__;
3672  //
3673  // if(columnsInfo_[col].isChildLinkUID())
3674  // __COUT__ << "-L" << columnsInfo_[col].getChildLinkIndex() << __E__;
3675  //
3676  // if(columnsInfo_[col].isChildLinkGroupID())
3677  // __COUT__ << "-L" << columnsInfo_[col].getChildLinkIndex() << __E__;
3678 
3679  if(((columnsInfo_[col].isChildLinkUID() && !(isGroup = false)) ||
3680  (columnsInfo_[col].isChildLinkGroupID() && (isGroup = true))) &&
3681  index == columnsInfo_[col].getChildLinkIndex())
3682  {
3683  // found match!
3684  //__COUT__ << "getChildLink Found match for col: " << (int)c << " at " << col
3685  //<< __E__;
3686  linkPair.second = col;
3687  return true;
3688  }
3689  }
3690 
3691  // if here then invalid table!
3692  __SS__ << "\tIn view: " << tableName_
3693  << ", Can't find complete child link id for column name "
3694  << columnsInfo_[c].getName() << __E__;
3695  __SS_THROW__;
3696 } // end getChildLink()
static std::string convertToCaps(std::string &str, bool isConfigName=false)
Definition: TableBase.cc:1894
static const std::string DATATYPE_NUMBER
static const std::string TYPE_UID
NOTE: Do NOT put '-' in static const TYPEs because it will mess up javascript handling in the web gui...
unsigned int findRow(unsigned int col, const T &value, unsigned int offsetRow=0, bool doNotThrow=false) const
< in included .icc source
std::string getEscapedValueAsString(unsigned int row, unsigned int col, bool convertEnvironmentVariables=true, bool quotesToDoubleQuotes=false) const
Definition: TableView.cc:1021
bool isEntryInGroup(const unsigned int &row, const std::string &childLinkIndex, const std::string &groupNeedle) const
Definition: TableView.cc:1660
void setValueAsString(const std::string &value, unsigned int row, unsigned int col)
Definition: TableView.cc:1087
void deleteRow(int r)
Definition: TableView.cc:3572
std::vector< std::vector< unsigned int > > getGroupRowsByPriority(const unsigned int groupIdCol, const std::string &groupID, bool onlyStatusTrue=false) const
Definition: TableView.cc:1517
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:1404
unsigned int getLinkGroupIDColumn(const std::string &childLinkIndex) const
Definition: TableView.cc:1855
bool removeRowFromGroup(const unsigned int &row, const unsigned int &col, const std::string &groupID, bool deleteRowIfNoGroupLeft=false)
Definition: TableView.cc:1604
unsigned int findColByType(const std::string &type, unsigned int startingCol=0) const
Definition: TableView.cc:1993
bool getChildLink(const unsigned int &col, bool &isGroup, std::pair< unsigned int, unsigned int > &linkPair) const
Definition: TableView.cc:3602
void addRowToGroup(const unsigned int &row, const unsigned int &col, const std::string &groupID)
, const std::string& colDefault);
Definition: TableView.cc:1462
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:1435
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:1107
void init(void)
Definition: TableView.cc:195
std::string getValueAsString(unsigned int row, unsigned int col, bool convertEnvironmentVariables=true) const
Definition: TableView.cc:972
std::vector< unsigned int > getGroupRows(const unsigned int groupIdCol, const std::string &groupID, bool onlyStatusTrue=false, bool orderedByPriority=false) const
Definition: TableView.cc:1494
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:1754
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:2010
int fillFromJSON(const std::string &json)
Definition: TableView.cc:2371
unsigned int getColUID(void) const
Definition: TableView.cc:1319
bool setURIEncodedValue(const std::string &value, const unsigned int &row, const unsigned int &col, const std::string &author="")
Definition: TableView.cc:3395
void fillFromCSV(const std::string &data, const int &dataOffset=0, const std::string &author="", const char rowDelimter=',', const char colDelimter='\n')
Definition: TableView.cc:3131
int fillFromEncodedCSV(const std::string &data, const int &dataOffset=0, const std::string &author="")
Definition: TableView.cc:3259
unsigned int findCol(const std::string &name) const
Definition: TableView.cc:1970
void setValue(const T &value, unsigned int row, unsigned int col)
< in included .icc source
void setURIEncodedComment(const std::string &uriComment)
Definition: TableView.cc:2125
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:3487
unsigned int findRowInGroup(unsigned int col, const T &value, const std::string &groupId, const std::string &childLinkIndex, unsigned int offsetRow=0) const
< in included .icc source
void setCustomStorageData(const std::string &storageData)
Definition: TableView.h:168
defines used also by OtsConfigurationWizardSupervisor
static std::string getTimestampString(const std::string &linuxTimeInSeconds)
static const std::string & trim(std::string &s)
static void getSetFromString(const std::string &inputString, std::set< std::string > &setToReturn, const std::set< char > &delimiter={',', '|', '&'}, const std::set< char > &whitespace={' ', '\t', '\n', '\r'})
static std::string setToString(const std::set< T > &setToReturn, const std::string &delimeter=", ")
setToString ~
static std::string convertEnvironmentVariables(const std::string &data)
static std::string demangleTypeName(const char *name)
static std::string restoreJSONStringEntities(const std::string &str)
static bool wildCardMatch(const std::string &needle, const std::string &haystack, unsigned int *priorityIndex=0)
Definition: StringMacros.cc:30
static std::string decodeURIComponent(const std::string &data)
static std::string stackTrace(void)
static bool getNumber(const std::string &s, T &retValue)