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