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