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