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