$treeview $search $mathjax $extrastylesheet
otsdaq
v2_03_00
$projectbrief
|
$projectbrief
|
$searchbox |
00001 #include "otsdaq-core/TableCore/TableView.h" 00002 00003 #include <cstdlib> 00004 #include <iostream> 00005 #include <regex> 00006 #include <sstream> 00007 00008 using namespace ots; 00009 00010 #undef __MF_SUBJECT__ 00011 #define __MF_SUBJECT__ "TableView-" + tableName_ 00012 #undef __COUT_HDR__ 00013 #define __COUT_HDR__ "v" << version_ << ":" << __COUT_HDR_FL__ 00014 00015 const unsigned int TableView::INVALID = -1; 00016 00017 //============================================================================== 00018 TableView::TableView(const std::string& name) 00019 : uniqueStorageIdentifier_("") 00020 , tableName_(name) 00021 , version_(TableVersion::INVALID) 00022 , comment_("") 00023 , author_("") 00024 , creationTime_(time(0)) 00025 , lastAccessTime_(0) 00026 , colUID_(INVALID) 00027 , colStatus_(INVALID) 00028 , colPriority_(INVALID) 00029 , fillWithLooseColumnMatching_(false) 00030 , sourceColumnMismatchCount_(0) 00031 , sourceColumnMissingCount_(0) 00032 { 00033 } 00034 00035 //============================================================================== 00036 TableView::~TableView(void) {} 00037 00038 //============================================================================== 00039 // operator= 00040 // Do NOT allow!... use TableView::copy 00041 // copy is used to maintain consistency with version, creationTime, lastAccessTime, etc) 00042 TableView& TableView::operator=(const TableView src) 00043 { 00044 __SS__ << "Invalid use of operator=... Should not directly copy a TableView. Please " 00045 "use TableView::copy(sourceView,author,comment)"; 00046 __SS_THROW__; 00047 } 00048 00049 //============================================================================== 00050 TableView& TableView::copy(const TableView& src, 00051 TableVersion destinationVersion, 00052 const std::string& author) 00053 { 00054 tableName_ = src.tableName_; 00055 version_ = destinationVersion; 00056 comment_ = src.comment_; 00057 author_ = author; // take new author 00058 // creationTime_ = time(0); //don't change creation time 00059 lastAccessTime_ = time(0); 00060 columnsInfo_ = src.columnsInfo_; 00061 theDataView_ = src.theDataView_; 00062 sourceColumnNames_ = src.sourceColumnNames_; 00063 init(); // verify consistency 00064 return *this; 00065 } 00066 00067 //============================================================================== 00068 // copyRows 00069 // return row offset of first row copied in 00070 unsigned int TableView::copyRows(const std::string& author, 00071 const TableView& src, 00072 unsigned int srcOffsetRow, 00073 unsigned int srcRowsToCopy, 00074 unsigned int destOffsetRow, 00075 bool generateUniqueDataColumns) 00076 { 00077 //__COUTV__(destOffsetRow); 00078 //__COUTV__(srcOffsetRow); 00079 //__COUTV__(srcRowsToCopy); 00080 00081 unsigned int retRow = (unsigned int)-1; 00082 00083 // check that column sizes match 00084 if(src.getNumberOfColumns() != getNumberOfColumns()) 00085 { 00086 __SS__ << "Error! Number of Columns of source view must match destination view." 00087 << "Dimension of source is [" << src.getNumberOfColumns() 00088 << "] and of destination is [" << getNumberOfColumns() << "]." << __E__; 00089 __SS_THROW__; 00090 } 00091 00092 unsigned int srcRows = src.getNumberOfRows(); 00093 00094 for(unsigned int r = 0; r < srcRowsToCopy; ++r) 00095 { 00096 if(r + srcOffsetRow >= srcRows) 00097 break; // end when no more source rows to copy (past bounds) 00098 00099 destOffsetRow = addRow(author, 00100 generateUniqueDataColumns /*incrementUniqueData*/, 00101 "" /*baseNameAutoUID*/, 00102 destOffsetRow); // add and get row created 00103 00104 if(retRow == (unsigned int)-1) 00105 retRow = destOffsetRow; // save row of first copied entry 00106 00107 // copy data 00108 for(unsigned int col = 0; col < getNumberOfColumns(); ++col) 00109 if(generateUniqueDataColumns && 00110 (columnsInfo_[col].getType() == TableViewColumnInfo::TYPE_UNIQUE_DATA || 00111 columnsInfo_[col].getType() == 00112 TableViewColumnInfo::TYPE_UNIQUE_GROUP_DATA)) 00113 continue; // if leaving unique data, then skip copy 00114 else 00115 theDataView_[destOffsetRow][col] = 00116 src.theDataView_[r + srcOffsetRow][col]; 00117 00118 // prepare for next row 00119 ++destOffsetRow; 00120 } 00121 00122 return retRow; 00123 } 00124 00125 //============================================================================== 00126 // init 00127 // Should be called after table is filled to setup special members 00128 // and verify consistency. 00129 // e.g. identifying the UID column, checking unique data fields, etc. 00130 // 00131 // Note: this function also sanitizes yes/no, on/off, and true/false types 00132 void TableView::init(void) 00133 { 00134 try 00135 { 00136 // verify column names are unique 00137 // make set of names,.. and CommentDescription == COMMENT 00138 std::set<std::string> colNameSet; 00139 std::string capsColName, colName; 00140 for(auto& colInfo : columnsInfo_) 00141 { 00142 colName = colInfo.getStorageName(); 00143 if(colName == "COMMENT_DESCRIPTION") 00144 colName = "COMMENT"; 00145 capsColName = ""; 00146 for(unsigned int i = 0; i < colName.size(); ++i) 00147 { 00148 if(colName[i] == '_') 00149 continue; 00150 capsColName += colName[i]; 00151 } 00152 00153 colNameSet.emplace(capsColName); 00154 } 00155 00156 if(colNameSet.size() != columnsInfo_.size()) 00157 { 00158 __SS__ << "Table Error:\t" 00159 << " Columns names must be unique! There are " << columnsInfo_.size() 00160 << " columns and the unique name count is " << colNameSet.size() 00161 << __E__; 00162 __SS_THROW__; 00163 } 00164 00165 getOrInitColUID(); // setup UID column 00166 try 00167 { 00168 getOrInitColStatus(); // setup Status column 00169 } 00170 catch(...) 00171 { 00172 } // ignore no Status column 00173 try 00174 { 00175 getOrInitColPriority(); // setup Priority column 00176 } 00177 catch(...) 00178 { 00179 } // ignore no Priority column 00180 00181 // require one comment column 00182 unsigned int colPos; 00183 if((colPos = findColByType(TableViewColumnInfo::TYPE_COMMENT)) != INVALID) 00184 { 00185 if(columnsInfo_[colPos].getName() != "CommentDescription") 00186 { 00187 __SS__ << "Table Error:\t" << TableViewColumnInfo::TYPE_COMMENT 00188 << " data type column must have name=" 00189 << "CommentDescription" << __E__; 00190 __SS_THROW__; 00191 } 00192 00193 if(findColByType(TableViewColumnInfo::TYPE_COMMENT, colPos + 1) != 00194 INVALID) // found two! 00195 { 00196 __SS__ << "Table Error:\t" << TableViewColumnInfo::TYPE_COMMENT 00197 << " data type in column " << columnsInfo_[colPos].getName() 00198 << " is repeated. This is not allowed." << __E__; 00199 __SS_THROW__; 00200 } 00201 00202 if(colPos != getNumberOfColumns() - 3) 00203 { 00204 __SS__ << "Table Error:\t" << TableViewColumnInfo::TYPE_COMMENT 00205 << " data type column must be 3rd to last (in column " 00206 << getNumberOfColumns() - 3 << ")." << __E__; 00207 __SS_THROW__; 00208 } 00209 } 00210 else 00211 { 00212 __SS__ << "Table Error:\t" << TableViewColumnInfo::TYPE_COMMENT 00213 << " data type column " 00214 << " is missing. This is not allowed." << __E__; 00215 __SS_THROW__; 00216 } 00217 00218 // require one author column 00219 if((colPos = findColByType(TableViewColumnInfo::TYPE_AUTHOR)) != INVALID) 00220 { 00221 if(findColByType(TableViewColumnInfo::TYPE_AUTHOR, colPos + 1) != 00222 INVALID) // found two! 00223 { 00224 __SS__ << "Table Error:\t" << TableViewColumnInfo::TYPE_AUTHOR 00225 << " data type in column " << columnsInfo_[colPos].getName() 00226 << " is repeated. This is not allowed." << __E__; 00227 __SS_THROW__; 00228 } 00229 00230 if(colPos != getNumberOfColumns() - 2) 00231 { 00232 __SS__ << "Table Error:\t" << TableViewColumnInfo::TYPE_AUTHOR 00233 << " data type column must be 2nd to last (in column " 00234 << getNumberOfColumns() - 2 << ")." << __E__; 00235 __SS_THROW__; 00236 } 00237 } 00238 else 00239 { 00240 __SS__ << "Table Error:\t" << TableViewColumnInfo::TYPE_AUTHOR 00241 << " data type column " 00242 << " is missing. This is not allowed." << __E__; 00243 __SS_THROW__; 00244 } 00245 00246 // require one timestamp column 00247 if((colPos = findColByType(TableViewColumnInfo::TYPE_TIMESTAMP)) != INVALID) 00248 { 00249 if(findColByType(TableViewColumnInfo::TYPE_TIMESTAMP, colPos + 1) != 00250 INVALID) // found two! 00251 { 00252 __SS__ << "Table Error:\t" << TableViewColumnInfo::TYPE_TIMESTAMP 00253 << " data type in column " << columnsInfo_[colPos].getName() 00254 << " is repeated. This is not allowed." << __E__; 00255 __SS_THROW__; 00256 } 00257 00258 if(colPos != getNumberOfColumns() - 1) 00259 { 00260 __SS__ << "Table Error:\t" << TableViewColumnInfo::TYPE_TIMESTAMP 00261 << " data type column must be last (in column " 00262 << getNumberOfColumns() - 1 << ")." << __E__; 00263 __COUT_ERR__ << "\n" << ss.str(); 00264 __SS_THROW__; 00265 } 00266 } 00267 else 00268 { 00269 __SS__ << "Table Error:\t" << TableViewColumnInfo::TYPE_TIMESTAMP 00270 << " data type column " 00271 << " is missing. This is not allowed." << __E__; 00272 __SS_THROW__; 00273 } 00274 00275 // check that UID is really unique ID (no repeats) 00276 // and ... allow letters, numbers, dash, underscore 00277 // and ... force size 1 00278 std::set<std::string /*uid*/> uidSet; 00279 for(unsigned int row = 0; row < getNumberOfRows(); ++row) 00280 { 00281 if(uidSet.find(theDataView_[row][colUID_]) != uidSet.end()) 00282 { 00283 __SS__ << ("Entries in UID are not unique. Specifically at row=" + 00284 std::to_string(row) + " value=" + theDataView_[row][colUID_]) 00285 << __E__; 00286 __SS_THROW__; 00287 } 00288 00289 if(theDataView_[row][colUID_].size() == 0) 00290 { 00291 __SS__ << "An invalid UID '" << theDataView_[row][colUID_] << "' " 00292 << " was identified. UIDs must contain at least 1 character." 00293 << __E__; 00294 __SS_THROW__; 00295 } 00296 00297 for(unsigned int i = 0; i < theDataView_[row][colUID_].size(); ++i) 00298 if(!((theDataView_[row][colUID_][i] >= 'A' && 00299 theDataView_[row][colUID_][i] <= 'Z') || 00300 (theDataView_[row][colUID_][i] >= 'a' && 00301 theDataView_[row][colUID_][i] <= 'z') || 00302 (theDataView_[row][colUID_][i] >= '0' && 00303 theDataView_[row][colUID_][i] <= '9') || 00304 (theDataView_[row][colUID_][i] == '-' || 00305 theDataView_[row][colUID_][i] <= '_'))) 00306 { 00307 __SS__ << "An invalid UID '" << theDataView_[row][colUID_] << "' " 00308 << " was identified. UIDs must contain only letters, numbers," 00309 << "dashes, and underscores." << __E__; 00310 __SS_THROW__; 00311 } 00312 00313 uidSet.insert(theDataView_[row][colUID_]); 00314 } 00315 if(uidSet.size() != getNumberOfRows()) 00316 { 00317 __SS__ << "Entries in UID are not unique!" 00318 << "There are " << getNumberOfRows() 00319 << " records and the unique UID count is " << uidSet.size() << __E__; 00320 __SS_THROW__; 00321 } 00322 00323 // check that any TYPE_UNIQUE_DATA columns are really unique (no repeats) 00324 colPos = (unsigned int)-1; 00325 while((colPos = findColByType(TableViewColumnInfo::TYPE_UNIQUE_DATA, 00326 colPos + 1)) != INVALID) 00327 { 00328 std::set<std::string /*unique data*/> uDataSet; 00329 for(unsigned int row = 0; row < getNumberOfRows(); ++row) 00330 { 00331 if(uDataSet.find(theDataView_[row][colPos]) != uDataSet.end()) 00332 { 00333 __SS__ << "Entries in Unique Data column " 00334 << columnsInfo_[colPos].getName() 00335 << (" are not unique. Specifically at row=" + 00336 std::to_string(row) + 00337 " value=" + theDataView_[row][colPos]) 00338 << __E__; 00339 __SS_THROW__; 00340 } 00341 uDataSet.insert(theDataView_[row][colPos]); 00342 } 00343 if(uDataSet.size() != getNumberOfRows()) 00344 { 00345 __SS__ << "Entries in Unique Data column " 00346 << columnsInfo_[colPos].getName() << " are not unique!" 00347 << "There are " << getNumberOfRows() 00348 << " records and the unique data count is " << uDataSet.size() 00349 << __E__; 00350 __SS_THROW__; 00351 } 00352 } 00353 00354 // check that any TYPE_UNIQUE_GROUP_DATA columns are really unique fpr groups (no 00355 // repeats) 00356 colPos = (unsigned int)-1; 00357 while((colPos = findColByType(TableViewColumnInfo::TYPE_UNIQUE_GROUP_DATA, 00358 colPos + 1)) != INVALID) 00359 { 00360 // colPos is a unique group data column 00361 // now, for each groupId column 00362 // check that data is unique for all groups 00363 for(unsigned int groupIdColPos = 0; groupIdColPos < columnsInfo_.size(); 00364 ++groupIdColPos) 00365 if(columnsInfo_[groupIdColPos].isGroupID()) 00366 { 00367 std::map<std::string /*group name*/, 00368 std::pair<unsigned int /*memberCount*/, 00369 std::set<std::string /*unique data*/> > > 00370 uGroupDataSets; 00371 00372 for(unsigned int row = 0; row < getNumberOfRows(); ++row) 00373 { 00374 auto groupIds = getSetOfGroupIDs(groupIdColPos, row); 00375 00376 for(const auto& groupId : groupIds) 00377 { 00378 uGroupDataSets[groupId].first++; // add to member count 00379 00380 if(uGroupDataSets[groupId].second.find( 00381 theDataView_[row][colPos]) != 00382 uGroupDataSets[groupId].second.end()) 00383 { 00384 __SS__ << "Entries in Unique Group Data column " << colPos 00385 << ":" << columnsInfo_[colPos].getName() 00386 << " are not unique for group ID '" << groupId 00387 << ".' Specifically at row=" << std::to_string(row) 00388 << " value=" << theDataView_[row][colPos] << __E__; 00389 __SS_THROW__; 00390 } 00391 uGroupDataSets[groupId].second.insert( 00392 theDataView_[row][colPos]); 00393 } 00394 } 00395 00396 for(const auto& groupPair : uGroupDataSets) 00397 if(uGroupDataSets[groupPair.first].second.size() != 00398 uGroupDataSets[groupPair.first].first) 00399 { 00400 __SS__ 00401 << "Entries in Unique Data column " 00402 << columnsInfo_[colPos].getName() 00403 << " are not unique for group '" << groupPair.first 00404 << "!'" 00405 << "There are " << uGroupDataSets[groupPair.first].first 00406 << " records and the unique data count is " 00407 << uGroupDataSets[groupPair.first].second.size() << __E__; 00408 __SS_THROW__; 00409 } 00410 } 00411 } // end TYPE_UNIQUE_GROUP_DATA check 00412 00413 auto rowDefaults = getDefaultRowValues(); 00414 00415 // check that column types are well behaved 00416 // - check that fixed choice data is one of choices 00417 // - sanitize booleans 00418 // - check that child link I are unique 00419 // note: childLinkId refers to childLinkGroupIDs AND childLinkUIDs 00420 std::set<std::string> groupIdIndexes, childLinkIndexes, childLinkIdLabels; 00421 unsigned int groupIdIndexesCount = 0, childLinkIndexesCount = 0, 00422 childLinkIdLabelsCount = 0; 00423 bool tmpIsGroup; 00424 std::pair<unsigned int /*link col*/, unsigned int /*link id col*/> tmpLinkPair; 00425 00426 for(unsigned int col = 0; col < getNumberOfColumns(); ++col) 00427 { 00428 if(columnsInfo_[col].getType() == TableViewColumnInfo::TYPE_FIXED_CHOICE_DATA) 00429 { 00430 const std::vector<std::string>& theDataChoices = 00431 columnsInfo_[col].getDataChoices(); 00432 00433 // check if arbitrary values allowed 00434 if(theDataChoices.size() && theDataChoices[0] == "arbitraryBool=1") 00435 continue; // arbitrary values allowed 00436 00437 bool found; 00438 for(unsigned int row = 0; row < getNumberOfRows(); ++row) 00439 { 00440 found = false; 00441 // check against default value first 00442 if(theDataView_[row][col] == rowDefaults[col]) 00443 continue; // default is always ok 00444 00445 for(const auto& choice : theDataChoices) 00446 { 00447 if(theDataView_[row][col] == choice) 00448 { 00449 found = true; 00450 break; 00451 } 00452 } 00453 if(!found) 00454 { 00455 __SS__ << "Table Error:\t'" << theDataView_[row][col] 00456 << "' in column " << columnsInfo_[col].getName() 00457 << " is not a valid Fixed Choice option. " 00458 << "Possible values are as follows: "; 00459 00460 for(unsigned int i = 0; 00461 i < columnsInfo_[col].getDataChoices().size(); 00462 ++i) 00463 { 00464 if(i) 00465 ss << ", "; 00466 ss << columnsInfo_[col].getDataChoices()[i]; 00467 } 00468 ss << "." << __E__; 00469 __SS_THROW__; 00470 } 00471 } 00472 } 00473 else if(columnsInfo_[col].isChildLink()) 00474 { 00475 // check if forcing fixed choices 00476 00477 const std::vector<std::string>& theDataChoices = 00478 columnsInfo_[col].getDataChoices(); 00479 00480 // check if arbitrary values allowed 00481 if(!theDataChoices.size() || theDataChoices[0] == "arbitraryBool=1") 00482 continue; // arbitrary values allowed 00483 00484 // skip one if arbitrary setting is embedded as first value 00485 bool skipOne = 00486 (theDataChoices.size() && theDataChoices[0] == "arbitraryBool=0"); 00487 bool hasSkipped; 00488 00489 bool found; 00490 for(unsigned int row = 0; row < getNumberOfRows(); ++row) 00491 { 00492 found = false; 00493 00494 hasSkipped = false; 00495 for(const auto& choice : theDataChoices) 00496 { 00497 if(skipOne && !hasSkipped) 00498 { 00499 hasSkipped = true; 00500 continue; 00501 } 00502 00503 if(theDataView_[row][col] == choice) 00504 { 00505 found = true; 00506 break; 00507 } 00508 } 00509 if(!found) 00510 { 00511 __SS__ << "Table Error:\t'" << theDataView_[row][col] 00512 << "' in column " << columnsInfo_[col].getName() 00513 << " is not a valid Fixed Choice option. " 00514 << "Possible values are as follows: "; 00515 00516 // ss << 00517 // StringMacros::vectorToString(columnsInfo_[col].getDataChoices()) 00518 // << __E__; 00519 for(unsigned int i = skipOne ? 1 : 0; 00520 i < columnsInfo_[col].getDataChoices().size(); 00521 ++i) 00522 { 00523 if(i > (skipOne ? 1 : 0)) 00524 ss << ", "; 00525 ss << columnsInfo_[col].getDataChoices()[i]; 00526 } 00527 ss << "." << __E__; 00528 __SS_THROW__; 00529 } 00530 } 00531 } 00532 else if(columnsInfo_[col].getType() == TableViewColumnInfo::TYPE_ON_OFF) 00533 for(unsigned int row = 0; row < getNumberOfRows(); ++row) 00534 { 00535 if(theDataView_[row][col] == "1" || theDataView_[row][col] == "on" || 00536 theDataView_[row][col] == "On" || theDataView_[row][col] == "ON") 00537 theDataView_[row][col] = TableViewColumnInfo::TYPE_VALUE_ON; 00538 else if(theDataView_[row][col] == "0" || 00539 theDataView_[row][col] == "off" || 00540 theDataView_[row][col] == "Off" || 00541 theDataView_[row][col] == "OFF") 00542 theDataView_[row][col] = TableViewColumnInfo::TYPE_VALUE_OFF; 00543 else 00544 { 00545 __SS__ << "Table Error:\t" << theDataView_[row][col] 00546 << " in column " << columnsInfo_[col].getName() 00547 << " is not a valid Type (On/Off) std::string. Possible " 00548 "values are 1, on, On, ON, 0, off, Off, OFF." 00549 << __E__; 00550 __SS_THROW__; 00551 } 00552 } 00553 else if(columnsInfo_[col].getType() == TableViewColumnInfo::TYPE_TRUE_FALSE) 00554 for(unsigned int row = 0; row < getNumberOfRows(); ++row) 00555 { 00556 if(theDataView_[row][col] == "1" || 00557 theDataView_[row][col] == "true" || 00558 theDataView_[row][col] == "True" || 00559 theDataView_[row][col] == "TRUE") 00560 theDataView_[row][col] = TableViewColumnInfo::TYPE_VALUE_TRUE; 00561 else if(theDataView_[row][col] == "0" || 00562 theDataView_[row][col] == "false" || 00563 theDataView_[row][col] == "False" || 00564 theDataView_[row][col] == "FALSE") 00565 theDataView_[row][col] = TableViewColumnInfo::TYPE_VALUE_FALSE; 00566 else 00567 { 00568 __SS__ << "Table Error:\t" << theDataView_[row][col] 00569 << " in column " << columnsInfo_[col].getName() 00570 << " is not a valid Type (True/False) std::string. " 00571 "Possible values are 1, true, True, TRUE, 0, false, " 00572 "False, FALSE." 00573 << __E__; 00574 __SS_THROW__; 00575 } 00576 } 00577 else if(columnsInfo_[col].getType() == TableViewColumnInfo::TYPE_YES_NO) 00578 for(unsigned int row = 0; row < getNumberOfRows(); ++row) 00579 { 00580 if(theDataView_[row][col] == "1" || theDataView_[row][col] == "yes" || 00581 theDataView_[row][col] == "Yes" || theDataView_[row][col] == "YES") 00582 theDataView_[row][col] = TableViewColumnInfo::TYPE_VALUE_YES; 00583 else if(theDataView_[row][col] == "0" || 00584 theDataView_[row][col] == "no" || 00585 theDataView_[row][col] == "No" || 00586 theDataView_[row][col] == "NO") 00587 theDataView_[row][col] = TableViewColumnInfo::TYPE_VALUE_NO; 00588 else 00589 { 00590 __SS__ << "Table Error:\t" << theDataView_[row][col] 00591 << " in column " << columnsInfo_[col].getName() 00592 << " is not a valid Type (Yes/No) std::string. Possible " 00593 "values are 1, yes, Yes, YES, 0, no, No, NO." 00594 << __E__; 00595 __SS_THROW__; 00596 } 00597 } 00598 else if(columnsInfo_[col].isGroupID()) // GroupID type 00599 { 00600 colLinkGroupIDs_[columnsInfo_[col].getChildLinkIndex()] = 00601 col; // add to groupid map 00602 // check uniqueness 00603 groupIdIndexes.emplace(columnsInfo_[col].getChildLinkIndex()); 00604 ++groupIdIndexesCount; 00605 } 00606 else if(columnsInfo_[col].isChildLink()) // Child Link type 00607 { 00608 // sanitize no link to default 00609 for(unsigned int row = 0; row < getNumberOfRows(); ++row) 00610 if(theDataView_[row][col] == "NoLink" || 00611 theDataView_[row][col] == "No_Link" || 00612 theDataView_[row][col] == "NOLINK" || 00613 theDataView_[row][col] == "NO_LINK" || 00614 theDataView_[row][col] == "Nolink" || 00615 theDataView_[row][col] == "nolink" || 00616 theDataView_[row][col] == "noLink") 00617 theDataView_[row][col] = 00618 TableViewColumnInfo::DATATYPE_LINK_DEFAULT; 00619 00620 // check uniqueness 00621 childLinkIndexes.emplace(columnsInfo_[col].getChildLinkIndex()); 00622 ++childLinkIndexesCount; 00623 00624 // force data type to TableViewColumnInfo::DATATYPE_STRING 00625 if(columnsInfo_[col].getDataType() != 00626 TableViewColumnInfo::DATATYPE_STRING) 00627 { 00628 __SS__ << "Table Error:\t" 00629 << "Column " << col << " with name " 00630 << columnsInfo_[col].getName() 00631 << " is a Child Link column and has an illegal data type of '" 00632 << columnsInfo_[col].getDataType() 00633 << "'. The data type for Child Link columns must be " 00634 << TableViewColumnInfo::DATATYPE_STRING << __E__; 00635 __SS_THROW__; 00636 } 00637 00638 // check for link mate (i.e. every child link needs link ID) 00639 getChildLink(col, tmpIsGroup, tmpLinkPair); 00640 } 00641 else if(columnsInfo_[col].isChildLinkUID() || // Child Link ID type 00642 columnsInfo_[col].isChildLinkGroupID()) 00643 { 00644 // check uniqueness 00645 childLinkIdLabels.emplace(columnsInfo_[col].getChildLinkIndex()); 00646 ++childLinkIdLabelsCount; 00647 00648 // check that the Link ID is not empty, and force to default 00649 for(unsigned int row = 0; row < getNumberOfRows(); ++row) 00650 if(theDataView_[row][col] == "") 00651 theDataView_[row][col] = rowDefaults[col]; 00652 00653 // check for link mate (i.e. every child link needs link ID) 00654 getChildLink(col, tmpIsGroup, tmpLinkPair); 00655 } 00656 } 00657 00658 // verify child link index uniqueness 00659 if(groupIdIndexes.size() != groupIdIndexesCount) 00660 { 00661 __SS__ << ("GroupId Labels are not unique!") << "There are " 00662 << groupIdIndexesCount << " GroupId Labels and the unique count is " 00663 << groupIdIndexes.size() << __E__; 00664 __SS_THROW__; 00665 } 00666 if(childLinkIndexes.size() != childLinkIndexesCount) 00667 { 00668 __SS__ << ("Child Link Labels are not unique!") << "There are " 00669 << childLinkIndexesCount 00670 << " Child Link Labels and the unique count is " 00671 << childLinkIndexes.size() << __E__; 00672 __SS_THROW__; 00673 } 00674 if(childLinkIdLabels.size() != childLinkIdLabelsCount) 00675 { 00676 __SS__ << ("Child Link ID Labels are not unique!") << "There are " 00677 << childLinkIdLabelsCount 00678 << " Child Link ID Labels and the unique count is " 00679 << childLinkIdLabels.size() << __E__; 00680 __SS_THROW__; 00681 } 00682 } 00683 catch(...) 00684 { 00685 __COUT__ << "Error occured in TableView::init() for version=" << version_ 00686 << __E__; 00687 throw; 00688 } 00689 } // end init() 00690 00691 //============================================================================== 00692 // getValue 00693 // string version 00694 // Note: necessary because types of std::basic_string<char> cause compiler problems if no 00695 // string specific function 00696 void TableView::getValue(std::string& value, 00697 unsigned int row, 00698 unsigned int col, 00699 bool doConvertEnvironmentVariables) const 00700 { 00701 if(!(col < columnsInfo_.size() && row < getNumberOfRows())) 00702 { 00703 __SS__ << "Invalid row col requested" << __E__; 00704 __SS_THROW__; 00705 } 00706 00707 value = validateValueForColumn( 00708 theDataView_[row][col], col, doConvertEnvironmentVariables); 00709 } 00710 00711 //============================================================================== 00712 // validateValueForColumn 00713 // string version 00714 // Note: necessary because types of std::basic_string<char> 00715 // cause compiler problems if no string specific function 00716 std::string TableView::validateValueForColumn(const std::string& value, 00717 unsigned int col, 00718 bool doConvertEnvironmentVariables) const 00719 { 00720 if(col >= columnsInfo_.size()) 00721 { 00722 __SS__ << "Invalid col requested" << __E__; 00723 __SS_THROW__; 00724 } 00725 00726 if(columnsInfo_[col].getDataType() == TableViewColumnInfo::DATATYPE_STRING) 00727 return doConvertEnvironmentVariables 00728 ? StringMacros::convertEnvironmentVariables(value) 00729 : value; 00730 else if(columnsInfo_[col].getDataType() == TableViewColumnInfo::DATATYPE_TIME) 00731 { 00732 return StringMacros::getTimestampString( 00733 doConvertEnvironmentVariables 00734 ? StringMacros::convertEnvironmentVariables(value) 00735 : value); 00736 00737 // retValue.resize(30); //known fixed size: Thu Aug 23 14:55:02 2001 CST 00738 // time_t timestamp( 00739 // strtol((doConvertEnvironmentVariables?StringMacros::convertEnvironmentVariables(value):value).c_str(), 00740 // 0,10)); 00741 // struct tm tmstruct; 00742 // ::localtime_r(×tamp, &tmstruct); 00743 // ::strftime(&retValue[0], 30, "%c %Z", &tmstruct); 00744 // retValue.resize(strlen(retValue.c_str())); 00745 } 00746 else 00747 { 00748 __SS__ << "\tUnrecognized column data type: " << columnsInfo_[col].getDataType() 00749 << " in configuration " << tableName_ 00750 << " at column=" << columnsInfo_[col].getName() 00751 << " for getValue with type '" 00752 << StringMacros::demangleTypeName(typeid(std::string).name()) << "'" 00753 << __E__; 00754 __SS_THROW__; 00755 } 00756 00757 // return retValue; 00758 } // end validateValueForColumn() 00759 00760 //============================================================================== 00761 // getValueAsString 00762 // gets the value with the proper data type and converts to string 00763 // as though getValue was called. 00764 std::string TableView::getValueAsString(unsigned int row, 00765 unsigned int col, 00766 bool doConvertEnvironmentVariables) const 00767 { 00768 if(!(col < columnsInfo_.size() && row < getNumberOfRows())) 00769 { 00770 __SS__ << ("Invalid row col requested") << __E__; 00771 __SS_THROW__; 00772 } 00773 00774 //__COUT__ << columnsInfo_[col].getType() << " " << col << __E__; 00775 00776 if(columnsInfo_[col].getType() == TableViewColumnInfo::TYPE_ON_OFF) 00777 { 00778 if(theDataView_[row][col] == "1" || theDataView_[row][col] == "on" || 00779 theDataView_[row][col] == "On" || theDataView_[row][col] == "ON") 00780 return TableViewColumnInfo::TYPE_VALUE_ON; 00781 else 00782 return TableViewColumnInfo::TYPE_VALUE_OFF; 00783 } 00784 else if(columnsInfo_[col].getType() == TableViewColumnInfo::TYPE_TRUE_FALSE) 00785 { 00786 if(theDataView_[row][col] == "1" || theDataView_[row][col] == "true" || 00787 theDataView_[row][col] == "True" || theDataView_[row][col] == "TRUE") 00788 return TableViewColumnInfo::TYPE_VALUE_TRUE; 00789 else 00790 return TableViewColumnInfo::TYPE_VALUE_FALSE; 00791 } 00792 else if(columnsInfo_[col].getType() == TableViewColumnInfo::TYPE_YES_NO) 00793 { 00794 if(theDataView_[row][col] == "1" || theDataView_[row][col] == "yes" || 00795 theDataView_[row][col] == "Yes" || theDataView_[row][col] == "YES") 00796 return TableViewColumnInfo::TYPE_VALUE_YES; 00797 else 00798 return TableViewColumnInfo::TYPE_VALUE_NO; 00799 } 00800 00801 //__COUT__ << __E__; 00802 return doConvertEnvironmentVariables 00803 ? StringMacros::convertEnvironmentVariables(theDataView_[row][col]) 00804 : theDataView_[row][col]; 00805 } 00806 00807 //============================================================================== 00808 // getEscapedValueAsString 00809 // gets the value with the proper data type and converts to string 00810 // as though getValue was called. 00811 // then escapes all special characters with slash. 00812 // Note: this should be useful for values placed in double quotes, i.e. JSON. 00813 std::string TableView::getEscapedValueAsString(unsigned int row, 00814 unsigned int col, 00815 bool doConvertEnvironmentVariables) const 00816 { 00817 std::string val = getValueAsString(row, col, doConvertEnvironmentVariables); 00818 std::string retVal = ""; 00819 retVal.reserve(val.size()); // reserve roughly right size 00820 for(unsigned int i = 0; i < val.size(); ++i) 00821 { 00822 if(val[i] == '\n') 00823 retVal += "\\n"; 00824 else if(val[i] == '\t') 00825 retVal += "\\t"; 00826 else if(val[i] == '\r') 00827 retVal += "\\r"; 00828 else 00829 { 00830 // escaped characters need a 00831 if(val[i] == '"' || val[i] == '\\') 00832 retVal += '\\'; 00833 retVal += val[i]; 00834 } 00835 } 00836 return retVal; 00837 } 00838 00839 //============================================================================== 00840 // setValue 00841 // string version 00842 void TableView::setValue(const std::string& value, unsigned int row, unsigned int col) 00843 { 00844 if(!(col < columnsInfo_.size() && row < getNumberOfRows())) 00845 { 00846 __SS__ << "Invalid row (" << row << ") col (" << col << ") requested!" << __E__; 00847 __SS_THROW__; 00848 } 00849 00850 if(columnsInfo_[col].getDataType() == TableViewColumnInfo::DATATYPE_STRING) 00851 theDataView_[row][col] = value; 00852 else // dont allow TableViewColumnInfo::DATATYPE_TIME to be set as string.. force use 00853 // as time_t to standardize string result 00854 { 00855 __SS__ << "\tUnrecognized column data type: " << columnsInfo_[col].getDataType() 00856 << " in configuration " << tableName_ 00857 << " at column=" << columnsInfo_[col].getName() 00858 << " for setValue with type '" 00859 << StringMacros::demangleTypeName(typeid(value).name()) << "'" << __E__; 00860 __SS_THROW__; 00861 } 00862 } 00863 void TableView::setValue(const char* value, unsigned int row, unsigned int col) 00864 { 00865 setValue(std::string(value), row, col); 00866 } 00867 00868 //============================================================================== 00869 // setValue 00870 // string version 00871 void TableView::setValueAsString(const std::string& value, 00872 unsigned int row, 00873 unsigned int col) 00874 { 00875 if(!(col < columnsInfo_.size() && row < getNumberOfRows())) 00876 { 00877 __SS__ << "Invalid row (" << row << ") col (" << col << ") requested!" << __E__; 00878 __SS_THROW__; 00879 } 00880 00881 theDataView_[row][col] = value; 00882 } 00883 00884 //============================================================================== 00885 // getOrInitColUID 00886 // if column not found throw error 00887 const unsigned int TableView::getOrInitColUID(void) 00888 { 00889 if(colUID_ != INVALID) 00890 return colUID_; 00891 00892 // if doesn't exist throw error! each view must have a UID column 00893 colUID_ = findColByType(TableViewColumnInfo::TYPE_UID); 00894 if(colUID_ == INVALID) 00895 { 00896 __COUT__ << "Column Types: " << __E__; 00897 for(unsigned int col = 0; col < columnsInfo_.size(); ++col) 00898 std::cout << columnsInfo_[col].getType() << "() " 00899 << columnsInfo_[col].getName() << __E__; 00900 __SS__ << "\tMissing UID Column in table named '" << tableName_ << "'" << __E__; 00901 __SS_THROW__; 00902 } 00903 return colUID_; 00904 } 00905 //============================================================================== 00906 // getColOfUID 00907 // const version, so don't attempt to lookup 00908 // if column not found throw error 00909 const unsigned int TableView::getColUID(void) const 00910 { 00911 if(colUID_ != INVALID) 00912 return colUID_; 00913 00914 __COUT__ << "Column Types: " << __E__; 00915 for(unsigned int col = 0; col < columnsInfo_.size(); ++col) 00916 std::cout << columnsInfo_[col].getType() << "() " << columnsInfo_[col].getName() 00917 << __E__; 00918 00919 __SS__ << ("Missing UID Column in config named " + tableName_ + 00920 ". (Possibly TableView was just not initialized?" + 00921 "This is the const call so can not alter class members)") 00922 << __E__; 00923 __SS_THROW__; 00924 } 00925 00926 //============================================================================== 00927 // getOrInitColStatus 00928 // if column not found throw error 00929 const unsigned int TableView::getOrInitColStatus(void) 00930 { 00931 if(colStatus_ != INVALID) 00932 return colStatus_; 00933 00934 // if doesn't exist throw error! each view must have a UID column 00935 colStatus_ = findCol(TableViewColumnInfo::COL_NAME_STATUS); 00936 if(colStatus_ == INVALID) 00937 { 00938 __SS__ << "\tMissing " << TableViewColumnInfo::COL_NAME_STATUS 00939 << " Column in table named '" << tableName_ << "'" << __E__; 00940 ss << "Column Types: " << __E__; 00941 for(unsigned int col = 0; col < columnsInfo_.size(); ++col) 00942 ss << columnsInfo_[col].getType() << "() " << columnsInfo_[col].getName() 00943 << __E__; 00944 00945 __SS_THROW__; 00946 } 00947 return colStatus_; 00948 } 00949 00950 //============================================================================== 00951 // getOrInitColPriority 00952 // if column not found throw error 00953 const unsigned int TableView::getOrInitColPriority(void) 00954 { 00955 if(colPriority_ != INVALID) 00956 return colPriority_; 00957 00958 // if doesn't exist throw error! each view must have a UID column 00959 colPriority_ = 00960 findCol("*" + TableViewColumnInfo::COL_NAME_PRIORITY); // wild card search 00961 if(colPriority_ == INVALID) 00962 { 00963 __SS__ << "\tMissing " << TableViewColumnInfo::COL_NAME_PRIORITY 00964 << " Column in table named '" << tableName_ << "'" << __E__; 00965 ss << "Column Types: " << __E__; 00966 for(unsigned int col = 0; col < columnsInfo_.size(); ++col) 00967 ss << columnsInfo_[col].getType() << "() " << columnsInfo_[col].getName() 00968 << __E__; 00969 00970 __SS_THROW__; 00971 } 00972 return colPriority_; 00973 } 00974 00975 //============================================================================== 00976 // getColStatus 00977 // const version, so don't attempt to lookup 00978 // if column not found throw error 00979 const unsigned int TableView::getColStatus(void) const 00980 { 00981 if(colStatus_ != INVALID) 00982 return colStatus_; 00983 00984 __COUT__ << "Column Types: " << __E__; 00985 for(unsigned int col = 0; col < columnsInfo_.size(); ++col) 00986 std::cout << columnsInfo_[col].getType() << "() " << columnsInfo_[col].getName() 00987 << __E__; 00988 00989 __SS__ << "Missing " << TableViewColumnInfo::COL_NAME_STATUS 00990 << " Column in config named " << tableName_ 00991 << ". (Possibly TableView was just not initialized?" 00992 << "This is the const call so can not alter class members)" << __E__; 00993 __SS_THROW__; 00994 } 00995 00996 //============================================================================== 00997 // getColPriority 00998 // const version, so don't attempt to lookup 00999 // if column not found throw error 01000 const unsigned int TableView::getColPriority(void) const 01001 { 01002 if(colPriority_ != INVALID) 01003 return colPriority_; 01004 01005 __COUT__ << "Column Types: " << __E__; 01006 for(unsigned int col = 0; col < columnsInfo_.size(); ++col) 01007 std::cout << columnsInfo_[col].getType() << "() " << columnsInfo_[col].getName() 01008 << __E__; 01009 01010 __SS__ << "Missing " << TableViewColumnInfo::COL_NAME_PRIORITY 01011 << " Column in config named " << tableName_ 01012 << ". (The Priority column is identified when TableView is initialized)" 01013 << __E__; // this is the const call, so can not identify the column and set 01014 // colPriority_ here 01015 __SS_THROW__; 01016 } 01017 01018 //============================================================================== 01019 // addRowToGroup 01020 // Group entry can include | to place a record in multiple groups 01021 void TableView::addRowToGroup( 01022 const unsigned int& row, 01023 const unsigned int& col, 01024 const std::string& groupID) //, 01025 // const std::string &colDefault) 01026 { 01027 if(isEntryInGroupCol(row, col, groupID)) 01028 { 01029 __SS__ << "GroupID (" << groupID << ") added to row (" << row 01030 << " is already present!" << __E__; 01031 __SS_THROW__; 01032 } 01033 01034 // not in group, so 01035 // if no groups 01036 // set groupid 01037 // if other groups 01038 // prepend groupId | 01039 if(getDataView()[row][col] == "" || 01040 getDataView()[row][col] == getDefaultRowValues()[col]) // colDefault) 01041 setValue(groupID, row, col); 01042 else 01043 setValue(groupID + " | " + getDataView()[row][col], row, col); 01044 01045 //__COUT__ << getDataView()[row][col] << __E__; 01046 } 01047 01048 //============================================================================== 01049 // removeRowFromGroup 01050 // Group entry can include | to place a record in multiple groups 01051 // 01052 // returns true if row was deleted because it had no group left 01053 bool TableView::removeRowFromGroup(const unsigned int& row, 01054 const unsigned int& col, 01055 const std::string& groupNeedle, 01056 bool deleteRowIfNoGroupLeft) 01057 { 01058 __COUT__ << "groupNeedle " << groupNeedle << __E__; 01059 std::set<std::string> groupIDList; 01060 if(!isEntryInGroupCol(row, col, groupNeedle, &groupIDList)) 01061 { 01062 __SS__ << "GroupID (" << groupNeedle << ") removed from row (" << row 01063 << ") was already removed!" << __E__; 01064 __SS_THROW__; 01065 } 01066 01067 // is in group, so 01068 // create new string based on set of groupids 01069 // but skip groupNeedle 01070 01071 std::string newValue = ""; 01072 unsigned int cnt = 0; 01073 for(const auto& groupID : groupIDList) 01074 { 01075 //__COUT__ << groupID << " " << groupNeedle << " " << newValue << __E__; 01076 if(groupID == groupNeedle) 01077 continue; // skip group to be removed 01078 01079 if(cnt) 01080 newValue += " | "; 01081 newValue += groupID; 01082 } 01083 01084 bool wasDeleted = false; 01085 if(deleteRowIfNoGroupLeft && newValue == "") 01086 { 01087 __COUT__ << "Delete row since it no longer part of any group." << __E__; 01088 deleteRow(row); 01089 wasDeleted = true; 01090 } 01091 else 01092 setValue(newValue, row, col); 01093 01094 //__COUT__ << getDataView()[row][col] << __E__; 01095 01096 return wasDeleted; 01097 } 01098 01099 //============================================================================== 01100 // isEntryInGroup 01101 // All group link checking should use this function 01102 // so that handling is consistent 01103 // 01104 // Group entry can include | to place a record in multiple groups 01105 bool TableView::isEntryInGroup(const unsigned int& r, 01106 const std::string& childLinkIndex, 01107 const std::string& groupNeedle) const 01108 { 01109 unsigned int c = getColLinkGroupID(childLinkIndex); // column in question 01110 01111 return isEntryInGroupCol(r, c, groupNeedle); 01112 } 01113 01114 //============================================================================== 01115 // isEntryInGroupCol 01116 // 01117 // if *groupIDList != 0 return set of groupIDs found 01118 // useful for removing groupIDs. 01119 // 01120 // Group entry can include | to place a record in multiple groups 01121 // 01122 // Note: should mirror what happens in TableView::getSetOfGroupIDs 01123 bool TableView::isEntryInGroupCol(const unsigned int& r, 01124 const unsigned int& c, 01125 const std::string& groupNeedle, 01126 std::set<std::string>* groupIDList) const 01127 { 01128 unsigned int i = 0; 01129 unsigned int j = 0; 01130 bool found = false; 01131 01132 //__COUT__ << "groupNeedle " << groupNeedle << __E__; 01133 01134 // go through the full groupString extracting groups and comparing to groupNeedle 01135 for(; j < theDataView_[r][c].size(); ++j) 01136 if((theDataView_[r][c][j] == ' ' || // ignore leading white space or | 01137 theDataView_[r][c][j] == '|') && 01138 i == j) 01139 ++i; 01140 else if((theDataView_[r][c][j] == 01141 ' ' || // trailing white space or | indicates group 01142 theDataView_[r][c][j] == '|') && 01143 i != j) // assume end of group name 01144 { 01145 if(groupIDList) 01146 groupIDList->emplace(theDataView_[r][c].substr(i, j - i)); 01147 01148 //__COUT__ << "Group found to compare: " << 01149 // theDataView_[r][c].substr(i,j-i) << __E__; 01150 if(groupNeedle == theDataView_[r][c].substr(i, j - i)) 01151 { 01152 if(!groupIDList) // dont return if caller is trying to get group list 01153 return true; 01154 found = true; 01155 } 01156 // if no match, setup i and j for next find 01157 i = j + 1; 01158 } 01159 01160 if(i != j) // last group check (for case when no ' ' or '|') 01161 { 01162 if(groupIDList) 01163 groupIDList->emplace(theDataView_[r][c].substr(i, j - i)); 01164 01165 //__COUT__ << "Group found to compare: " << 01166 // theDataView_[r][c].substr(i,j-i) << __E__; 01167 if(groupNeedle == theDataView_[r][c].substr(i, j - i)) 01168 return true; 01169 } 01170 01171 return found; 01172 } 01173 01174 //============================================================================== 01175 // getSetOfGroupIDs 01176 // if row == -1, then considers all rows 01177 // else just that row 01178 // returns unique set of groupIds in GroupID column 01179 // associate with childLinkIndex 01180 // 01181 // Note: should mirror what happens in TableView::isEntryInGroupCol 01182 std::set<std::string> TableView::getSetOfGroupIDs(const std::string& childLinkIndex, 01183 unsigned int r) const 01184 { 01185 return getSetOfGroupIDs(getColLinkGroupID(childLinkIndex), r); 01186 } 01187 std::set<std::string> TableView::getSetOfGroupIDs(const unsigned int& c, 01188 unsigned int r) const 01189 { 01190 //__COUT__ << "GroupID col=" << (int)c << __E__; 01191 01192 std::set<std::string> retSet; 01193 01194 unsigned int i = 0; 01195 unsigned int j = 0; 01196 01197 if(r != (unsigned int)-1) 01198 { 01199 if(r >= getNumberOfRows()) 01200 { 01201 __SS__ << "Invalid row requested!" << __E__; 01202 __SS_THROW__; 01203 } 01204 01205 StringMacros::getSetFromString(theDataView_[r][c], retSet); 01206 // //go through the full groupString extracting groups 01207 // //add each found groupId to set 01208 // for(;j<theDataView_[r][c].size();++j) 01209 // if((theDataView_[r][c][j] == ' ' || //ignore leading white space or | 01210 // theDataView_[r][c][j] == '|') 01211 // && i == j) 01212 // ++i; 01213 // else if((theDataView_[r][c][j] == ' ' || //trailing white space or | 01214 // indicates group theDataView_[r][c][j] == '|') 01215 // && i != j) // assume end of group name 01216 // { 01217 // //__COUT__ << "Group found: " << 01218 // // theDataView_[r][c].substr(i,j-i) << __E__; 01219 // 01220 // 01221 // retSet.emplace(theDataView_[r][c].substr(i,j-i)); 01222 // 01223 // //setup i and j for next find 01224 // i = j+1; 01225 // } 01226 // 01227 // if(i != j) //last group check (for case when no ' ' or '|') 01228 // retSet.emplace(theDataView_[r][c].substr(i,j-i)); 01229 } 01230 else 01231 { 01232 // do all rows 01233 for(r = 0; r < getNumberOfRows(); ++r) 01234 { 01235 StringMacros::getSetFromString(theDataView_[r][c], retSet); 01236 01237 // i=0; 01238 // j=0; 01239 // 01240 // //__COUT__ << (int)r << ": " << theDataView_[r][c] << __E__; 01241 // 01242 // //go through the full groupString extracting groups 01243 // //add each found groupId to set 01244 // for(;j<theDataView_[r][c].size();++j) 01245 // { 01246 // //__COUT__ << "i:" << i << " j:" << j << __E__; 01247 // 01248 // if((theDataView_[r][c][j] == ' ' || //ignore leading white 01249 // space or | theDataView_[r][c][j] == '|') 01250 // && i == j) 01251 // ++i; 01252 // else if((theDataView_[r][c][j] == ' ' || //trailing white 01253 // space or | indicates group theDataView_[r][c][j] 01254 // == 01255 // '|') 01256 // && i != j) // assume end of group name 01257 // { 01258 // //__COUT__ << "Group found: " << 01259 // // theDataView_[r][c].substr(i,j-i) << __E__; 01260 // 01261 // retSet.emplace(theDataView_[r][c].substr(i,j-i)); 01262 // 01263 // //setup i and j for next find 01264 // i = j+1; 01265 // } 01266 // } 01267 // 01268 // if(i != j) //last group (for case when no ' ' or '|') 01269 // { 01270 // //__COUT__ << "Group found: " << 01271 // // theDataView_[r][c].substr(i,j-i) << __E__; 01272 // retSet.emplace(theDataView_[r][c].substr(i,j-i)); 01273 // } 01274 } 01275 } 01276 01277 return retSet; 01278 } 01279 01280 //============================================================================== 01281 // getColOfLinkGroupID 01282 // const version, if column not found throw error 01283 const unsigned int TableView::getColLinkGroupID(const std::string& childLinkIndex) const 01284 { 01285 if(!childLinkIndex.size()) 01286 { 01287 __SS__ << "Empty childLinkIndex string parameter!" << __E__; 01288 __SS_THROW__; 01289 } 01290 01291 const char* needleChildLinkIndex = &childLinkIndex[0]; 01292 01293 // allow space syntax to target a childLinkIndex from a different parentLinkIndex 01294 // e.g. "parentLinkIndex childLinkIndex" 01295 size_t spacePos = childLinkIndex.find(' '); 01296 if(spacePos != std::string::npos && 01297 spacePos + 1 < childLinkIndex.size()) // make sure there are more characters 01298 { 01299 // found space syntax for targeting childLinkIndex 01300 needleChildLinkIndex = &childLinkIndex[spacePos + 1]; 01301 } 01302 01303 std::map<std::string, unsigned int>::const_iterator it = 01304 colLinkGroupIDs_.find(needleChildLinkIndex); 01305 if(it != // if already known, return it 01306 colLinkGroupIDs_.end()) 01307 return it->second; 01308 01309 __COUT__ << "Existing Column GroupIDs: " << __E__; 01310 for(auto& groupIdColPair : colLinkGroupIDs_) 01311 std::cout << "\t" << groupIdColPair.first << " : col-" << groupIdColPair.second 01312 << __E__; 01313 01314 __COUT__ << "Existing Column Types: " << __E__; 01315 for(unsigned int col = 0; col < columnsInfo_.size(); ++col) 01316 std::cout << "\t" << columnsInfo_[col].getType() << "() " 01317 << columnsInfo_[col].getName() << __E__; 01318 01319 __SS__ 01320 << "Incompatible table for this group link. Table '" << tableName_ 01321 << "' is missing a GroupID column with data type '" 01322 << TableViewColumnInfo::TYPE_START_GROUP_ID << "-" << needleChildLinkIndex 01323 << "'.\n\n" 01324 << "Note: you can separate the child GroupID column data type from " 01325 << "the parent GroupLink column data type; this is accomplished by using a space " 01326 << "character at the parent level - the string after the space will be treated " 01327 "as the " 01328 << "child GroupID column data type." << __E__; 01329 __SS_THROW__; 01330 } // end getColLinkGroupID() 01331 01332 //============================================================================== 01333 unsigned int TableView::findRow(unsigned int col, 01334 const std::string& value, 01335 unsigned int offsetRow) const 01336 { 01337 for(unsigned int row = offsetRow; row < theDataView_.size(); ++row) 01338 { 01339 if(theDataView_[row][col] == value) 01340 return row; 01341 } 01342 01343 __SS__ << "\tIn view: " << tableName_ << ", Can't find value=" << value 01344 << " in column named " << columnsInfo_[col].getName() 01345 << " with type=" << columnsInfo_[col].getType() << __E__; 01346 // Note: findRow gets purposely called by configuration GUI a lot looking for 01347 // exceptions so may not want to print out 01348 //__COUT__ << "\n" << ss.str(); 01349 __SS_ONLY_THROW__; 01350 } // end findRow() 01351 01352 //============================================================================== 01353 unsigned int TableView::findRowInGroup(unsigned int col, 01354 const std::string& value, 01355 const std::string& groupId, 01356 const std::string& childLinkIndex, 01357 unsigned int offsetRow) const 01358 { 01359 unsigned int groupIdCol = getColLinkGroupID(childLinkIndex); 01360 for(unsigned int row = offsetRow; row < theDataView_.size(); ++row) 01361 { 01362 if(theDataView_[row][col] == value && isEntryInGroupCol(row, groupIdCol, groupId)) 01363 return row; 01364 } 01365 01366 __SS__ << "\tIn view: " << tableName_ << ", Can't find in group the value=" << value 01367 << " in column named '" << columnsInfo_[col].getName() 01368 << "' with type=" << columnsInfo_[col].getType() << " and GroupID: '" 01369 << groupId << "' in column '" << groupIdCol 01370 << "' with GroupID child link index '" << childLinkIndex << "'" << __E__; 01371 // Note: findRowInGroup gets purposely called by configuration GUI a lot looking for 01372 // exceptions so may not want to print out 01373 __SS_ONLY_THROW__; 01374 } // end findRowInGroup() 01375 01376 //============================================================================== 01377 // findCol 01378 // throws exception if column not found by name 01379 unsigned int TableView::findCol(const std::string& wildCardName) const 01380 { 01381 for(unsigned int col = 0; col < columnsInfo_.size(); ++col) 01382 if(StringMacros::wildCardMatch(wildCardName /*needle*/, 01383 columnsInfo_[col].getName() /*haystack*/)) 01384 return col; 01385 01386 __SS__ << "\tIn view: " << tableName_ << ", Can't find column named '" << wildCardName 01387 << "'" << __E__; 01388 ss << "Existing columns:\n"; 01389 for(unsigned int col = 0; col < columnsInfo_.size(); ++col) 01390 ss << "\t" << columnsInfo_[col].getName() << "\n"; 01391 // Note: findCol gets purposely called by configuration GUI a lot looking for 01392 // exceptions so may not want to print out 01393 __SS_ONLY_THROW__; 01394 } // end findCol() 01395 01396 //============================================================================== 01397 // findColByType 01398 // return invalid if type not found 01399 unsigned int TableView::findColByType(const std::string& type, int startingCol) const 01400 { 01401 for(unsigned int col = startingCol; col < columnsInfo_.size(); ++col) 01402 if(columnsInfo_[col].getType() == type) 01403 return col; 01404 01405 return INVALID; 01406 } // end findColByType() 01407 01408 // Getters 01409 //============================================================================== 01410 const std::string& TableView::getUniqueStorageIdentifier(void) const 01411 { 01412 return uniqueStorageIdentifier_; 01413 } 01414 01415 //============================================================================== 01416 const std::string& TableView::getTableName(void) const { return tableName_; } 01417 01418 //============================================================================== 01419 const TableVersion& TableView::getVersion(void) const { return version_; } 01420 01421 //============================================================================== 01422 const std::string& TableView::getComment(void) const { return comment_; } 01423 01424 //============================================================================== 01425 const std::string& TableView::getAuthor(void) const { return author_; } 01426 01427 //============================================================================== 01428 const time_t& TableView::getCreationTime(void) const { return creationTime_; } 01429 01430 //============================================================================== 01431 const time_t& TableView::getLastAccessTime(void) const { return lastAccessTime_; } 01432 01433 //============================================================================== 01434 const bool& TableView::getLooseColumnMatching(void) const 01435 { 01436 return fillWithLooseColumnMatching_; 01437 } 01438 01439 //============================================================================== 01440 // getDataColumnSize 01441 const unsigned int TableView::getDataColumnSize(void) const 01442 { 01443 // if no data, give benefit of the doubt that phantom data has mockup column size 01444 if(!getNumberOfRows()) 01445 return getNumberOfColumns(); 01446 return theDataView_[0].size(); // number of columns in first row of data 01447 } 01448 01449 //============================================================================== 01450 // getSourceColumnMismatch 01451 // The source information is only valid after modifying the table with ::fillFromJSON 01452 const unsigned int& TableView::getSourceColumnMismatch(void) const 01453 { 01454 return sourceColumnMismatchCount_; 01455 } 01456 01457 //============================================================================== 01458 // getSourceColumnMissing 01459 // The source information is only valid after modifying the table with ::fillFromJSON 01460 const unsigned int& TableView::getSourceColumnMissing(void) const 01461 { 01462 return sourceColumnMissingCount_; 01463 } 01464 01465 //============================================================================== 01466 // getSourceColumnNames 01467 // The source information is only valid after modifying the table with ::fillFromJSON 01468 const std::set<std::string>& TableView::getSourceColumnNames(void) const 01469 { 01470 return sourceColumnNames_; 01471 } 01472 01473 //============================================================================== 01474 std::set<std::string> TableView::getColumnNames(void) const 01475 { 01476 std::set<std::string> retSet; 01477 for(auto& colInfo : columnsInfo_) 01478 retSet.emplace(colInfo.getName()); 01479 return retSet; 01480 } 01481 01482 //============================================================================== 01483 std::set<std::string> TableView::getColumnStorageNames(void) const 01484 { 01485 std::set<std::string> retSet; 01486 for(auto& colInfo : columnsInfo_) 01487 retSet.emplace(colInfo.getStorageName()); 01488 return retSet; 01489 } 01490 01491 //============================================================================== 01492 std::vector<std::string> TableView::getDefaultRowValues(void) const 01493 { 01494 std::vector<std::string> retVec; 01495 01496 // fill each col of new row with default values 01497 for(unsigned int col = 0; col < getNumberOfColumns(); ++col) 01498 { 01499 // if this is a fixed choice Link, and NO_LINK is not in list, 01500 // take first in list to avoid creating illegal rows. 01501 // NOTE: this is not a problem for standard fixed choice fields 01502 // because the default value is always required. 01503 01504 if(columnsInfo_[col].isChildLink()) 01505 { 01506 const std::vector<std::string>& theDataChoices = 01507 columnsInfo_[col].getDataChoices(); 01508 01509 // check if arbitrary values allowed 01510 if(!theDataChoices.size() || // if so, use default 01511 theDataChoices[0] == "arbitraryBool=1") 01512 retVec.push_back(columnsInfo_[col].getDefaultValue()); 01513 else 01514 { 01515 bool skipOne = 01516 (theDataChoices.size() && theDataChoices[0] == "arbitraryBool=0"); 01517 bool hasSkipped; 01518 01519 // look for default value in list 01520 01521 bool foundDefault = false; 01522 hasSkipped = false; 01523 for(const auto& choice : theDataChoices) 01524 if(skipOne && !hasSkipped) 01525 { 01526 hasSkipped = true; 01527 continue; 01528 } 01529 else if(choice == columnsInfo_[col].getDefaultValue()) 01530 { 01531 foundDefault = true; 01532 break; 01533 } 01534 01535 // use first choice if possible 01536 if(!foundDefault && theDataChoices.size() > (skipOne ? 1 : 0)) 01537 retVec.push_back(theDataChoices[(skipOne ? 1 : 0)]); 01538 else // else stick with default 01539 retVec.push_back(columnsInfo_[col].getDefaultValue()); 01540 } 01541 } 01542 else 01543 retVec.push_back(columnsInfo_[col].getDefaultValue()); 01544 } 01545 01546 return retVec; 01547 } // end getDefaultRowValues() 01548 01549 //============================================================================== 01550 unsigned int TableView::getNumberOfRows(void) const { return theDataView_.size(); } 01551 01552 //============================================================================== 01553 unsigned int TableView::getNumberOfColumns(void) const { return columnsInfo_.size(); } 01554 01555 //============================================================================== 01556 const TableView::DataView& TableView::getDataView(void) const { return theDataView_; } 01557 01558 //============================================================================== 01559 // TableView::DataView* TableView::getDataViewP(void) 01560 //{ 01561 // return &theDataView_; 01562 //} 01563 01564 //============================================================================== 01565 const std::vector<TableViewColumnInfo>& TableView::getColumnsInfo(void) const 01566 { 01567 return columnsInfo_; 01568 } 01569 01570 //============================================================================== 01571 std::vector<TableViewColumnInfo>* TableView::getColumnsInfoP(void) 01572 { 01573 return &columnsInfo_; 01574 } 01575 //============================================================================== 01576 const TableViewColumnInfo& TableView::getColumnInfo(unsigned int column) const 01577 { 01578 if(column >= columnsInfo_.size()) 01579 { 01580 std::stringstream errMsg; 01581 errMsg << __COUT_HDR_FL__ << "\nCan't find column " << column 01582 << "\n\n\n\nThe column info is likely missing due to incomplete " 01583 "Configuration View filling.\n\n" 01584 << __E__; 01585 __THROW__(errMsg.str().c_str()); 01586 } 01587 return columnsInfo_[column]; 01588 } // end getColumnInfo() 01589 01590 // Setters 01591 //============================================================================== 01592 void TableView::setUniqueStorageIdentifier(const std::string& storageUID) 01593 { 01594 uniqueStorageIdentifier_ = storageUID; 01595 } 01596 01597 //============================================================================== 01598 void TableView::setTableName(const std::string& name) { tableName_ = name; } 01599 01600 //============================================================================== 01601 void TableView::setComment(const std::string& comment) { comment_ = comment; } 01602 01603 //============================================================================== 01604 void TableView::setURIEncodedComment(const std::string& uriComment) 01605 { 01606 comment_ = StringMacros::decodeURIComponent(uriComment); 01607 } 01608 01609 //============================================================================== 01610 void TableView::setAuthor(const std::string& author) { author_ = author; } 01611 01612 //============================================================================== 01613 void TableView::setCreationTime(time_t t) { creationTime_ = t; } 01614 01615 //============================================================================== 01616 void TableView::setLastAccessTime(time_t t) { lastAccessTime_ = t; } 01617 01618 //============================================================================== 01619 void TableView::setLooseColumnMatching(bool setValue) 01620 { 01621 fillWithLooseColumnMatching_ = setValue; 01622 } 01623 01624 //============================================================================== 01625 void TableView::reset(void) 01626 { 01627 version_ = -1; 01628 comment_ = ""; 01629 author_ + ""; 01630 columnsInfo_.clear(); 01631 theDataView_.clear(); 01632 } // end reset() 01633 01634 //============================================================================== 01635 void TableView::print(std::ostream& out) const 01636 { 01637 out << "=============================================================================" 01638 "=" 01639 << __E__; 01640 out << "Print: " << tableName_ << " Version: " << version_ << " Comment: " << comment_ 01641 << " Author: " << author_ << " Creation Time: " << ctime(&creationTime_) << __E__; 01642 out << "\t\tNumber of Cols " << getNumberOfColumns() << __E__; 01643 out << "\t\tNumber of Rows " << getNumberOfRows() << __E__; 01644 01645 out << "Columns:\t"; 01646 for(int i = 0; i < (int)columnsInfo_.size(); ++i) 01647 out << i << ":" << columnsInfo_[i].getName() << ":" 01648 << columnsInfo_[i].getStorageName() << ":" << columnsInfo_[i].getType() << ":" 01649 << columnsInfo_[i].getDataType() << "\t "; 01650 out << __E__; 01651 01652 out << "Rows:" << __E__; 01653 int num; 01654 std::string val; 01655 for(int r = 0; r < (int)getNumberOfRows(); ++r) 01656 { 01657 out << (int)r << ":\t"; 01658 for(int c = 0; c < (int)getNumberOfColumns(); ++c) 01659 { 01660 out << (int)c << ":"; 01661 01662 // if fixed choice type, print index in choice 01663 if(columnsInfo_[c].getType() == TableViewColumnInfo::TYPE_FIXED_CHOICE_DATA) 01664 { 01665 int choiceIndex = -1; 01666 std::vector<std::string> choices = columnsInfo_[c].getDataChoices(); 01667 val = StringMacros::convertEnvironmentVariables(theDataView_[r][c]); 01668 01669 if(val == columnsInfo_[c].getDefaultValue()) 01670 choiceIndex = 0; 01671 else 01672 { 01673 for(int i = 0; i < (int)choices.size(); ++i) 01674 if(val == choices[i]) 01675 choiceIndex = i + 1; 01676 } 01677 01678 out << "ChoiceIndex=" << choiceIndex << ":"; 01679 } 01680 01681 out << theDataView_[r][c]; 01682 // stopped using below, because it is called sometimes during debugging when 01683 // numbers are set to environment variables: 01684 // if(columnsInfo_[c].getDataType() == "NUMBER") 01685 // { 01686 // getValue(num,r,c,false); 01687 // out << num; 01688 // } 01689 // else 01690 // { 01691 // getValue(val,r,c,false); 01692 // out << val; 01693 // } 01694 out << "\t\t"; 01695 } 01696 out << __E__; 01697 } 01698 } // end print() 01699 01700 //============================================================================== 01701 void TableView::printJSON(std::ostream& out) const 01702 { 01703 out << "{\n"; 01704 out << "\"NAME\" : \"" << tableName_ << "\",\n"; 01705 01706 // out << "\"VERSION\" : \"" << version_ << "\"\n"; 01707 01708 out << "\"COMMENT\" : "; 01709 01710 // output escaped comment 01711 std::string val; 01712 val = comment_; 01713 out << "\""; 01714 for(unsigned int i = 0; i < val.size(); ++i) 01715 { 01716 if(val[i] == '\n') 01717 out << "\\n"; 01718 else if(val[i] == '\t') 01719 out << "\\t"; 01720 else if(val[i] == '\r') 01721 out << "\\r"; 01722 else 01723 { 01724 // escaped characters need a 01725 if(val[i] == '"' || val[i] == '\\') 01726 out << '\\'; 01727 out << val[i]; 01728 } 01729 } 01730 out << "\",\n"; 01731 01732 out << "\"AUTHOR\" : \"" << author_ << "\",\n"; 01733 out << "\"CREATION_TIME\" : " << creationTime_ << ",\n"; 01734 01735 // USELESS... out << "\"NUM_OF_COLS\" : " << getNumberOfColumns() << ",\n"; 01736 // USELESS... out << "\"NUM_OF_ROWS\" : " << getNumberOfRows() << ",\n"; 01737 01738 out << "\"COL_TYPES\" : {\n"; 01739 for(int c = 0; c < (int)getNumberOfColumns(); ++c) 01740 { 01741 out << "\t\t\"" << columnsInfo_[c].getStorageName() << "\" : "; 01742 out << "\"" << columnsInfo_[c].getDataType() << "\""; 01743 if(c + 1 < (int)getNumberOfColumns()) 01744 out << ","; 01745 out << "\n"; 01746 } 01747 out << "},\n"; // close COL_TYPES 01748 01749 out << "\"DATA_SET\" : [\n"; 01750 int num; 01751 for(int r = 0; r < (int)getNumberOfRows(); ++r) 01752 { 01753 out << "\t{\n"; 01754 for(int c = 0; c < (int)getNumberOfColumns(); ++c) 01755 { 01756 out << "\t\t\"" << columnsInfo_[c].getStorageName() << "\" : "; 01757 01758 out << "\"" << getEscapedValueAsString(r, c, false) 01759 << "\""; // do not convert env variables 01760 01761 if(c + 1 < (int)getNumberOfColumns()) 01762 out << ","; 01763 out << "\n"; 01764 } 01765 out << "\t}"; 01766 if(r + 1 < (int)getNumberOfRows()) 01767 out << ","; 01768 out << "\n"; 01769 } 01770 out << "]\n"; // close DATA_SET 01771 01772 out << "}"; 01773 } // end printJSON() 01774 01775 //============================================================================== 01776 // restoreJSONStringEntities 01777 // returns string with literals \n \t \" \r \\ replaced with char 01778 std::string restoreJSONStringEntities(const std::string& str) 01779 { 01780 unsigned int sz = str.size(); 01781 if(!sz) 01782 return ""; // empty string, returns empty string 01783 01784 std::stringstream retStr; 01785 unsigned int i = 0; 01786 for(; i < sz - 1; ++i) 01787 { 01788 if(str[i] == '\\') // if 2 char escape sequence, replace with char 01789 switch(str[i + 1]) 01790 { 01791 case 'n': 01792 retStr << '\n'; 01793 ++i; 01794 break; 01795 case '"': 01796 retStr << '"'; 01797 ++i; 01798 break; 01799 case 't': 01800 retStr << '\t'; 01801 ++i; 01802 break; 01803 case 'r': 01804 retStr << '\r'; 01805 ++i; 01806 break; 01807 case '\\': 01808 retStr << '\\'; 01809 ++i; 01810 break; 01811 default: 01812 retStr << str[i]; 01813 } 01814 else 01815 retStr << str[i]; 01816 } 01817 if(i == sz - 1) 01818 retStr << str[sz - 1]; // output last character (which can't escape anything) 01819 01820 return retStr.str(); 01821 } // end restoreJSONStringEntities() 01822 01823 //============================================================================== 01824 // fillFromJSON 01825 // Clears and fills the view from the JSON string. 01826 // Returns -1 on failure 01827 // 01828 // first level keys: 01829 // NAME 01830 // DATA_SET 01831 int TableView::fillFromJSON(const std::string& json) 01832 { 01833 std::vector<std::string> keys; 01834 keys.push_back("NAME"); 01835 keys.push_back("COMMENT"); 01836 keys.push_back("AUTHOR"); 01837 keys.push_back("CREATION_TIME"); 01838 // keys.push_back ("COL_TYPES"); 01839 keys.push_back("DATA_SET"); 01840 enum 01841 { 01842 CV_JSON_FILL_NAME, 01843 CV_JSON_FILL_COMMENT, 01844 CV_JSON_FILL_AUTHOR, 01845 CV_JSON_FILL_CREATION_TIME, 01846 // CV_JSON_FILL_COL_TYPES, 01847 CV_JSON_FILL_DATA_SET 01848 }; 01849 01850 //__COUTV__(json); 01851 01852 sourceColumnMismatchCount_ = 0; 01853 sourceColumnMissingCount_ = 0; 01854 sourceColumnNames_.clear(); // reset 01855 unsigned int colFoundCount = 0; 01856 unsigned int i = 0; 01857 unsigned int row = -1; 01858 unsigned int colSpeedup = 0; 01859 unsigned int startString, startNumber, endNumber = -1; 01860 unsigned int bracketCount = 0; 01861 unsigned int sqBracketCount = 0; 01862 bool inQuotes = 0; 01863 bool newString = 0; 01864 bool newValue = 0; 01865 bool isDataArray = 0; 01866 bool keyIsMatch, keyIsComment; 01867 unsigned int keyIsMatchIndex, keyIsMatchStorageIndex, keyIsMatchCommentIndex; 01868 const std::string COMMENT_ALT_KEY = "COMMENT"; 01869 01870 std::string extractedString = "", currKey = "", currVal = ""; 01871 unsigned int currDepth; 01872 01873 std::vector<std::string> jsonPath; 01874 std::vector<char> jsonPathType; // indicator of type in jsonPath: { [ K 01875 char lastPopType = '_'; // either: _ { [ K 01876 // _ indicates reset pop (this happens when a new {obj} starts) 01877 unsigned int matchedKey = -1; 01878 unsigned int lastCol = -1; 01879 01880 // find all depth 1 matching keys 01881 for(; i < json.size(); ++i) 01882 { 01883 switch(json[i]) 01884 { 01885 case '"': 01886 if(i - 1 < json.size() && // ignore if escaped 01887 json[i - 1] == '\\') 01888 break; 01889 01890 inQuotes = !inQuotes; // toggle in quotes if not escaped 01891 if(inQuotes) 01892 startString = i; 01893 else 01894 { 01895 extractedString = restoreJSONStringEntities( 01896 json.substr(startString + 1, i - startString - 1)); 01897 newString = 1; // have new string! 01898 } 01899 break; 01900 case ':': 01901 if(inQuotes) 01902 break; // skip if in quote 01903 01904 // must be a json object level to have a key 01905 if(jsonPathType[jsonPathType.size() - 1] != '{' || 01906 !newString) // and must have a string for key 01907 { 01908 __COUT__ << "Invalid ':' position" << __E__; 01909 return -1; 01910 } 01911 01912 // valid, so take key 01913 jsonPathType.push_back('K'); 01914 jsonPath.push_back(extractedString); 01915 startNumber = i; 01916 newString = 0; // clear flag 01917 endNumber = -1; // reset end number index 01918 break; 01919 01920 // if(isKey || 01921 // isDataArray) 01922 // { 01923 // std::cout << "Invalid ':' position" << __E__; 01924 // return -1; 01925 // } 01926 // isKey = 1; //new value is a key 01927 // newValue = 1; 01928 // startNumber = i; 01929 // break; 01930 case ',': 01931 if(inQuotes) 01932 break; // skip if in quote 01933 if(lastPopType == '{') // don't need value again of nested object 01934 { 01935 // check if the nested object was the value to a key, if so, pop key 01936 if(jsonPathType[jsonPathType.size() - 1] == 'K') 01937 { 01938 lastPopType = 'K'; 01939 jsonPath.pop_back(); 01940 jsonPathType.pop_back(); 01941 } 01942 break; // skip , handling if {obj} just ended 01943 } 01944 01945 if(newString) 01946 currVal = extractedString; 01947 else // number value 01948 { 01949 if(endNumber == (unsigned int)-1 || // take i as end number if needed 01950 endNumber <= startNumber) 01951 endNumber = i; 01952 // extract number value 01953 if(endNumber <= startNumber) // empty data, could be {} 01954 currVal = ""; 01955 else 01956 currVal = json.substr(startNumber + 1, endNumber - startNumber - 1); 01957 } 01958 01959 currDepth = bracketCount; 01960 01961 if(jsonPathType[jsonPathType.size() - 1] == 'K') // this is the value to key 01962 { 01963 currKey = jsonPath[jsonPathType.size() - 1]; 01964 newValue = 1; // new value to consider! 01965 01966 // pop key 01967 lastPopType = 'K'; 01968 jsonPath.pop_back(); 01969 jsonPathType.pop_back(); 01970 } 01971 else if(jsonPathType[jsonPathType.size() - 1] == 01972 '[') // this is a value in array 01973 { 01974 // key is last key 01975 for(unsigned int k = jsonPathType.size() - 2; k < jsonPathType.size(); 01976 --k) 01977 if(jsonPathType[k] == 'K') 01978 { 01979 currKey = jsonPath[k]; 01980 break; 01981 } 01982 else if(k == 0) 01983 { 01984 __COUT__ << "Invalid array position" << __E__; 01985 return -1; 01986 } 01987 01988 newValue = 1; // new value to consider! 01989 isDataArray = 1; 01990 } 01991 else // { is an error 01992 { 01993 __COUT__ << "Invalid ',' position" << __E__; 01994 return -1; 01995 } 01996 01997 startNumber = i; 01998 break; 01999 02000 case '{': 02001 if(inQuotes) 02002 break; // skip if in quote 02003 lastPopType = '_'; // reset because of new object 02004 jsonPathType.push_back('{'); 02005 jsonPath.push_back("{"); 02006 ++bracketCount; 02007 break; 02008 02009 // ++bracketCount; 02010 // isDataArray = 0; 02011 // isKey = 0; 02012 // endingObject = 0; 02013 // break; 02014 case '}': 02015 if(inQuotes) 02016 break; // skip if in quote 02017 02018 if(lastPopType != '{' && // don't need value again of nested object 02019 jsonPathType[jsonPathType.size() - 1] == 'K') // this is the value to key 02020 { 02021 currDepth = bracketCount; 02022 currKey = jsonPath[jsonPathType.size() - 1]; 02023 if(newString) 02024 currVal = extractedString; 02025 else // number value 02026 { 02027 if(endNumber == (unsigned int)-1 || // take i as end number if needed 02028 endNumber <= startNumber) 02029 endNumber = i; 02030 // extract val 02031 if(endNumber <= startNumber) // empty data, could be {} 02032 currVal = ""; 02033 else 02034 currVal = 02035 json.substr(startNumber + 1, endNumber - startNumber - 1); 02036 } 02037 newValue = 1; // new value to consider! 02038 // pop key 02039 jsonPath.pop_back(); 02040 jsonPathType.pop_back(); 02041 } 02042 // pop { 02043 if(jsonPathType[jsonPathType.size() - 1] != '{') 02044 { 02045 __COUT__ << "Invalid '}' position" << __E__; 02046 return -1; 02047 } 02048 lastPopType = '{'; 02049 jsonPath.pop_back(); 02050 jsonPathType.pop_back(); 02051 --bracketCount; 02052 break; 02053 case '[': 02054 if(inQuotes) 02055 break; // skip if in quote 02056 jsonPathType.push_back('['); 02057 jsonPath.push_back("["); 02058 ++sqBracketCount; 02059 startNumber = i; 02060 break; 02061 case ']': 02062 if(inQuotes) 02063 break; // skip if in quote 02064 02065 // must be an array at this level (in order to close it) 02066 if(jsonPathType[jsonPathType.size() - 1] != '[') 02067 { 02068 __COUT__ << "Invalid ']' position" << __E__; 02069 return -1; 02070 } 02071 02072 currDepth = bracketCount; 02073 02074 // This is an array value 02075 if(newString) 02076 currVal = extractedString; 02077 else // number value 02078 { 02079 if(endNumber == (unsigned int)-1 || // take i as end number if needed 02080 endNumber <= startNumber) 02081 endNumber = i; 02082 // extract val 02083 if(endNumber <= startNumber) // empty data, could be {} 02084 currVal = ""; 02085 else 02086 currVal = json.substr(startNumber + 1, endNumber - startNumber - 1); 02087 } 02088 isDataArray = 1; 02089 02090 // key is last key 02091 for(unsigned int k = jsonPathType.size() - 2; k < jsonPathType.size(); --k) 02092 if(jsonPathType[k] == 'K') 02093 { 02094 currKey = jsonPath[k]; 02095 break; 02096 } 02097 else if(k == 0) 02098 { 02099 __COUT__ << "Invalid array position" << __E__; 02100 return -1; 02101 } 02102 02103 // pop [ 02104 if(jsonPathType[jsonPathType.size() - 1] != '[') 02105 { 02106 __COUT__ << "Invalid ']' position" << __E__; 02107 return -1; 02108 } 02109 lastPopType = '['; 02110 jsonPath.pop_back(); 02111 jsonPathType.pop_back(); 02112 --sqBracketCount; 02113 break; 02114 case ' ': // white space handling for numbers 02115 case '\t': 02116 case '\n': 02117 case '\r': 02118 if(inQuotes) 02119 break; // skip if in quote 02120 if(startNumber != (unsigned int)-1 && endNumber == (unsigned int)-1) 02121 endNumber = i; 02122 startNumber = i; 02123 break; 02124 default:; 02125 } 02126 02127 if(0) // for debugging 02128 { 02129 std::cout << i << ":\t" << json[i] << " - "; 02130 02131 std::cout << "ExtKey="; 02132 for(unsigned int k = 0; k < jsonPath.size(); ++k) 02133 std::cout << jsonPath[k] << "/"; 02134 std::cout << " - "; 02135 std::cout << lastPopType << " "; 02136 std::cout << bracketCount << " "; 02137 std::cout << sqBracketCount << " "; 02138 std::cout << inQuotes << " "; 02139 std::cout << newValue << "-"; 02140 std::cout << currKey << "-{" << currDepth << "}:"; 02141 std::cout << currVal << " "; 02142 std::cout << startNumber << "-"; 02143 std::cout << endNumber << " "; 02144 std::cout << "\n"; 02145 } 02146 02147 // continue; 02148 02149 // handle a new completed value 02150 if(newValue) 02151 { 02152 // std::cout << "ExtKey="; 02153 // for(unsigned int k=0;k<jsonPath.size();++k) 02154 // std::cout << jsonPath[k] << "/"; 02155 // 02156 // std::cout << currKey << "-{" << currDepth << "}: - "; 02157 // 02158 // if(isDataArray) 02159 // std::cout << "Array:: "; 02160 // if(newString) 02161 // std::cout << "New String:: "; 02162 // else 02163 // std::cout << "New Number:: "; 02164 // 02165 // std::cout << currVal << "\n"; 02166 02167 // extract only what we care about 02168 // for TableView only care about matching depth 1 02169 02170 // handle matching depth 1 keys 02171 02172 matchedKey = -1; // init to unfound 02173 for(unsigned int k = 0; k < keys.size(); ++k) 02174 if((currDepth == 1 && keys[k] == currKey) || 02175 (currDepth > 1 && keys[k] == jsonPath[1])) 02176 matchedKey = k; 02177 02178 if(matchedKey != (unsigned int)-1) 02179 { 02180 // std::cout << "New Data for:: key[" << matchedKey << "]-" 02181 //<< keys[matchedKey] << "\n"; 02182 02183 switch(matchedKey) 02184 { 02185 case CV_JSON_FILL_NAME: 02186 if(currDepth == 1) 02187 setTableName(currVal); 02188 break; 02189 case CV_JSON_FILL_COMMENT: 02190 if(currDepth == 1) 02191 setComment(currVal); 02192 break; 02193 case CV_JSON_FILL_AUTHOR: 02194 if(currDepth == 1) 02195 setAuthor(currVal); 02196 break; 02197 case CV_JSON_FILL_CREATION_TIME: 02198 if(currDepth == 1) 02199 setCreationTime(strtol(currVal.c_str(), 0, 10)); 02200 break; 02201 // case CV_JSON_FILL_COL_TYPES: 02202 // 02203 // break; 02204 case CV_JSON_FILL_DATA_SET: 02205 // std::cout << "CV_JSON_FILL_DATA_SET New Data for:: 02206 //" 02207 //<< matchedKey << "]-" << keys[matchedKey] 02208 //<< 02209 //"/" << currDepth << ".../" << currKey << 02210 //"\n"; 02211 02212 if(currDepth == 2) // second level depth 02213 { 02214 // if matches first column name.. then add new row 02215 // else add to current row 02216 unsigned int col, ccnt = 0; 02217 unsigned int noc = getNumberOfColumns(); 02218 for(; ccnt < noc; ++ccnt) 02219 { 02220 // use colSpeedup to change the first column we search 02221 // for each iteration.. since we expect the data to 02222 // be arranged in column order 02223 02224 if(fillWithLooseColumnMatching_) 02225 { 02226 // loose column matching makes no attempt to 02227 // match the column names 02228 // just assumes the data is in the correct order 02229 02230 col = colSpeedup; 02231 02232 // auto matched 02233 if(col <= lastCol) // add row (use lastCol in case new 02234 // column-0 was added 02235 row = addRow(); 02236 lastCol = col; 02237 if(getNumberOfRows() == 1) // only for first row 02238 sourceColumnNames_.emplace(currKey); 02239 02240 // add value to row and column 02241 02242 if(row >= getNumberOfRows()) 02243 { 02244 __SS__ << "Invalid row" 02245 << __E__; // should be impossible? 02246 std::cout << ss.str(); 02247 __SS_THROW__; 02248 return -1; 02249 } 02250 02251 theDataView_[row][col] = 02252 currVal; // THERE IS NO CHECK FOR WHAT IS READ FROM 02253 // THE DATABASE. IT SHOULD BE ALREADY 02254 // CONSISTENT 02255 break; 02256 } 02257 else 02258 { 02259 col = (ccnt + colSpeedup) % noc; 02260 02261 // match key by ignoring '_' 02262 // also accept COMMENT == COMMENT_DESCRIPTION 02263 // (this is for backwards compatibility..) 02264 keyIsMatch = true; 02265 keyIsComment = true; 02266 for(keyIsMatchIndex = 0, 02267 keyIsMatchStorageIndex = 0, 02268 keyIsMatchCommentIndex = 0; 02269 keyIsMatchIndex < currKey.size(); 02270 ++keyIsMatchIndex) 02271 { 02272 if(columnsInfo_[col] 02273 .getStorageName()[keyIsMatchStorageIndex] == 02274 '_') 02275 ++keyIsMatchStorageIndex; // skip to next storage 02276 // character 02277 if(currKey[keyIsMatchIndex] == '_') 02278 continue; // skip to next character 02279 02280 // match to storage name 02281 if(keyIsMatchStorageIndex >= 02282 columnsInfo_[col].getStorageName().size() || 02283 currKey[keyIsMatchIndex] != 02284 columnsInfo_[col] 02285 .getStorageName()[keyIsMatchStorageIndex]) 02286 { 02287 // size mismatch or character mismatch 02288 keyIsMatch = false; 02289 if(!keyIsComment) 02290 break; 02291 } 02292 02293 // check also if alternate comment is matched 02294 if(keyIsComment && 02295 keyIsMatchCommentIndex < COMMENT_ALT_KEY.size()) 02296 { 02297 if(currKey[keyIsMatchIndex] != 02298 COMMENT_ALT_KEY[keyIsMatchCommentIndex]) 02299 { 02300 // character mismatch with COMMENT 02301 keyIsComment = false; 02302 } 02303 } 02304 02305 ++keyIsMatchStorageIndex; // go to next character 02306 } 02307 02308 if(keyIsMatch || 02309 keyIsComment) // currKey == 02310 // columnsInfo_[c].getStorageName()) 02311 { 02312 // matched 02313 if(col <= lastCol) // add row (use lastCol in case 02314 // new column-0 was added 02315 { 02316 if(getNumberOfRows()) // skip first time 02317 sourceColumnMissingCount_ += 02318 getNumberOfColumns() - colFoundCount; 02319 02320 colFoundCount = 0; // reset column found count 02321 row = addRow(); 02322 } 02323 lastCol = col; 02324 ++colFoundCount; 02325 02326 if(getNumberOfRows() == 1) // only for first row 02327 sourceColumnNames_.emplace(currKey); 02328 02329 // add value to row and column 02330 02331 if(row >= getNumberOfRows()) 02332 { 02333 __SS__ << "Invalid row" 02334 << __E__; // should be impossible?! 02335 __COUT__ << "\n" << ss.str(); 02336 __SS_THROW__; 02337 return -1; // never gets here 02338 } 02339 02340 theDataView_[row][col] = currVal; 02341 break; 02342 } 02343 } 02344 } 02345 02346 colSpeedup = 02347 (colSpeedup + 1) % 02348 noc; // short cut to proper column hopefully in next search 02349 02350 if(ccnt >= getNumberOfColumns()) 02351 { 02352 __SS__ 02353 << "\n\nInvalid column in JSON source data: " << currKey 02354 << " not found in column names of table named " 02355 << getTableName() << "." 02356 << __E__; // input data doesn't match config description 02357 __COUT__ << "\n" << ss.str(); 02358 // CHANGED on 11/10/2016 02359 // to.. try just not populating data instead of error 02360 ++sourceColumnMismatchCount_; // but count errors 02361 if(getNumberOfRows() == 1) // only for first row 02362 sourceColumnNames_.emplace(currKey); 02363 02364 //__SS_THROW__; 02365 __COUT_WARN__ 02366 << "Ignoring error, and not populating missing column." 02367 << __E__; 02368 } 02369 } 02370 break; 02371 default:; // unknown match? 02372 } 02373 } 02374 02375 // clean up handling of new value 02376 02377 newString = 0; // toggle flag 02378 newValue = 0; // toggle flag 02379 isDataArray = 0; 02380 endNumber = -1; // reset end number index 02381 } 02382 02383 // if(i>200) break; //185 02384 } 02385 02386 //__COUT__ << "Done!" << __E__; 02387 02388 // print(); 02389 02390 return 0; // success 02391 } // end fillFromJSON() 02392 02393 //============================================================================== 02394 bool TableView::isURIEncodedCommentTheSame(const std::string& comment) const 02395 { 02396 std::string compareStr = StringMacros::decodeURIComponent(comment); 02397 return comment_ == compareStr; 02398 } 02399 // 02401 // bool TableView::isValueTheSame(const std::string &valueStr, 02402 // unsigned int r, unsigned int c) const 02403 //{ 02404 // __COUT__ << "valueStr " << valueStr << __E__; 02405 // 02406 // if(!(c < columnsInfo_.size() && r < getNumberOfRows())) 02407 // { 02408 // __SS__ << "Invalid row (" << (int)r << ") col (" << (int)c << ") requested!" << 02409 //__E__; 02410 // __SS_THROW__; 02411 // } 02412 // 02413 // __COUT__ << "originalValueStr " << theDataView_[r][c] << __E__; 02414 // 02415 // if(columnsInfo_[c].getDataType() == TableViewColumnInfo::DATATYPE_TIME) 02416 // { 02417 // time_t valueTime(strtol(valueStr.c_str(),0,10)); 02418 // time_t originalValueTime; 02419 // getValue(originalValueTime,r,c); 02420 // __COUT__ << "time_t valueStr " << valueTime << __E__; 02421 // __COUT__ << "time_t originalValueStr " << originalValueTime << __E__; 02422 // return valueTime == originalValueTime; 02423 // } 02424 // else 02425 // { 02426 // return valueStr == theDataView_[r][c]; 02427 // } 02428 //} 02429 02430 //============================================================================== 02431 // fillFromCSV 02432 // Fills the view from the CSV string. 02433 // 02434 // Note: converts all %## to the ascii character, # is hex nibble 02435 // (e.g. '%' must be represented as "%25") 02436 // 02437 // dataOffset := starting destination row 02438 // 02439 // while there are row entries in the data.. replace 02440 // data range from [dataOffset, dataOffset+chunkSize-1] 02441 // ... note if less rows, this means rows were deleted 02442 // ... if more, then rows were added. 02443 // 02444 // ',' next cell delimiter 02445 // ';' next row delimiter 02446 // 02447 // 02448 // if author == "", do nothing special for author and timestamp column 02449 // if author != "", assign author for any row that has been modified, and assign now as 02450 // timestamp 02451 // 02452 // Returns -1 if data was same and pre-existing content 02453 // Returns 1 if data was same, but columns are different 02454 // otherwise 0 02455 // 02456 int TableView::fillFromCSV(const std::string& data, 02457 const int& dataOffset, 02458 const std::string& author) 02459 { 02460 int retVal = 0; 02461 02462 int r = dataOffset; 02463 int c = 0; 02464 02465 int i = 0; // use to parse data std::string 02466 int j = data.find(',', i); // find next cell delimiter 02467 int k = data.find(';', i); // find next row delimiter 02468 02469 bool rowWasModified; 02470 unsigned int countRowsModified = 0; 02471 int authorCol = findColByType(TableViewColumnInfo::TYPE_AUTHOR); 02472 int timestampCol = findColByType(TableViewColumnInfo::TYPE_TIMESTAMP); 02473 // std::string valueStr, tmpTimeStr, originalValueStr; 02474 02475 while(k != (int)(std::string::npos)) 02476 { 02477 rowWasModified = false; 02478 if(r >= (int)getNumberOfRows()) 02479 { 02480 addRow(); 02481 //__COUT__ << "Row added" << __E__; 02482 rowWasModified = true; 02483 } 02484 02485 while(j < k && j != (int)(std::string::npos)) 02486 { 02487 //__COUT__ << "Col " << (int)c << __E__; 02488 02489 // skip last 2 columns 02490 if(c >= (int)getNumberOfColumns() - 2) 02491 { 02492 i = j + 1; 02493 j = data.find(',', i); // find next cell delimiter 02494 ++c; 02495 continue; 02496 } 02497 02498 if(setURIEncodedValue(data.substr(i, j - i), r, c)) 02499 rowWasModified = true; 02500 02501 i = j + 1; 02502 j = data.find(',', i); // find next cell delimiter 02503 ++c; 02504 } 02505 02506 // if row was modified, assign author and timestamp 02507 if(author != "" && rowWasModified) 02508 { 02509 __COUT__ << "Row=" << (int)r << " was modified!" << __E__; 02510 setValue(author, r, authorCol); 02511 setValue(time(0), r, timestampCol); 02512 } 02513 02514 if(rowWasModified) 02515 ++countRowsModified; 02516 02517 ++r; 02518 c = 0; 02519 02520 i = k + 1; 02521 j = data.find(',', i); // find next cell delimiter 02522 k = data.find(';', i); // find new row delimiter 02523 } 02524 02525 // delete excess rows 02526 while(r < (int)getNumberOfRows()) 02527 { 02528 deleteRow(r); 02529 __COUT__ << "Row deleted: " << (int)r << __E__; 02530 ++countRowsModified; 02531 } 02532 02533 __COUT_INFO__ << "countRowsModified=" << countRowsModified << __E__; 02534 02535 if(!countRowsModified) 02536 { 02537 // check that source columns match storage name 02538 // otherwise allow same data... 02539 02540 bool match = getColumnStorageNames().size() == getSourceColumnNames().size(); 02541 if(match) 02542 { 02543 for(auto& destColName : getColumnStorageNames()) 02544 if(getSourceColumnNames().find(destColName) == 02545 getSourceColumnNames().end()) 02546 { 02547 __COUT__ << "Found column name mismach for '" << destColName 02548 << "'... So allowing same data!" << __E__; 02549 02550 match = false; 02551 break; 02552 } 02553 } 02554 // if still a match, do not allow! 02555 if(match) 02556 { 02557 __SS__ << "No rows were modified! No reason to fill a view with same content." 02558 << __E__; 02559 __COUT__ << "\n" << ss.str(); 02560 return -1; 02561 } 02562 // else mark with retVal 02563 retVal = 1; 02564 } // end same check 02565 02566 // print(); //for debugging 02567 02568 // setup sourceColumnNames_ to be correct 02569 sourceColumnNames_.clear(); 02570 for(unsigned int i = 0; i < getNumberOfColumns(); ++i) 02571 sourceColumnNames_.emplace(getColumnsInfo()[i].getStorageName()); 02572 02573 init(); // verify new table (throws runtime_errors) 02574 02575 // printout for debugging 02576 // __SS__ << "\n"; 02577 // print(ss); 02578 // __COUT__ << "\n" << ss.str() << __E__; 02579 02580 return retVal; 02581 } // end fillFromCSV() 02582 02583 //============================================================================== 02584 // setURIEncodedValue 02585 // converts all %## to the ascii character 02586 // returns true if value was different than original value 02587 // 02588 // 02589 // if author == "", do nothing special for author and timestamp column 02590 // if author != "", assign author for any row that has been modified, and assign now as 02591 // timestamp 02592 bool TableView::setURIEncodedValue(const std::string& value, 02593 const unsigned int& r, 02594 const unsigned int& c, 02595 const std::string& author) 02596 { 02597 if(!(c < columnsInfo_.size() && r < getNumberOfRows())) 02598 { 02599 __SS__ << "Invalid row (" << (int)r << ") col (" << (int)c << ") requested!" 02600 << "Number of Rows = " << getNumberOfRows() 02601 << "Number of Columns = " << columnsInfo_.size() << __E__; 02602 print(ss); 02603 __SS_THROW__; 02604 } 02605 02606 std::string valueStr = StringMacros::decodeURIComponent(value); 02607 std::string originalValueStr = 02608 getValueAsString(r, c, false); // do not convert env variables 02609 02610 //__COUT__ << "valueStr " << valueStr << __E__; 02611 //__COUT__ << "originalValueStr " << originalValueStr << __E__; 02612 02613 if(columnsInfo_[c].getDataType() == TableViewColumnInfo::DATATYPE_NUMBER) 02614 { 02615 // check if valid number 02616 std::string convertedString = StringMacros::convertEnvironmentVariables(valueStr); 02617 // do not check here, let init check 02618 // if this is a link to valid number, then this is an improper check. 02619 // if(!StringMacros::isNumber(convertedString)) 02620 // { 02621 // __SS__ << "\tIn configuration " << tableName_ 02622 // << " at column=" << columnsInfo_[c].getName() << " the value 02623 //set 02624 //(" 02625 // << convertedString << ")" 02626 // << " is not a number! Please fix it or change the column 02627 // type..." 02628 // << __E__; 02629 // __SS_THROW__; 02630 // } 02631 theDataView_[r][c] = valueStr; 02632 } 02633 else if(columnsInfo_[c].getDataType() == TableViewColumnInfo::DATATYPE_TIME) 02634 { 02635 // valueStr = StringMacros::decodeURIComponent(data.substr(i,j-i)); 02636 // 02637 // getValue(tmpTimeStr,r,c); 02638 // if(valueStr != tmpTimeStr)//theDataView_[r][c]) 02639 // { 02640 // __COUT__ << "valueStr=" << valueStr << 02641 // " theDataView_[r][c]=" << tmpTimeStr << __E__; 02642 // rowWasModified = true; 02643 // } 02644 02645 setValue(time_t(strtol(valueStr.c_str(), 0, 10)), r, c); 02646 } 02647 else 02648 theDataView_[r][c] = valueStr; 02649 02650 bool rowWasModified = 02651 (originalValueStr != 02652 getValueAsString(r, c, false)); // do not convert env variables 02653 02654 // if row was modified, assign author and timestamp 02655 if(author != "" && rowWasModified) 02656 { 02657 __COUT__ << "Row=" << (int)r << " was modified!" << __E__; 02658 int authorCol = findColByType(TableViewColumnInfo::TYPE_AUTHOR); 02659 int timestampCol = findColByType(TableViewColumnInfo::TYPE_TIMESTAMP); 02660 setValue(author, r, authorCol); 02661 setValue(time(0), r, timestampCol); 02662 } 02663 02664 return rowWasModified; 02665 } // end setURIEncodedValue() 02666 02667 //============================================================================== 02668 void TableView::resizeDataView(unsigned int nRows, unsigned int nCols) 02669 { 02670 // FIXME This maybe should disappear but I am using it in ConfigurationHandler 02671 // still... 02672 theDataView_.resize(nRows, std::vector<std::string>(nCols)); 02673 } 02674 02675 //============================================================================== 02676 // addRow 02677 // returns index of added row, always is last row 02678 // return -1 on failure (throw error) 02679 // 02680 // if baseNameAutoUID != "", creates a UID based on this base name 02681 // and increments and appends an integer relative to the previous last row 02682 unsigned int TableView::addRow(const std::string& author, 02683 bool incrementUniqueData, 02684 std::string baseNameAutoUID, 02685 unsigned int rowToAdd) 02686 { 02687 // default to last row 02688 if(rowToAdd == (unsigned int)-1) 02689 rowToAdd = getNumberOfRows(); 02690 02691 theDataView_.resize(getNumberOfRows() + 1, 02692 std::vector<std::string>(getNumberOfColumns())); 02693 02694 // shift data down the table if necessary 02695 for(unsigned int r = getNumberOfRows() - 2; r >= rowToAdd; --r) 02696 { 02697 if(r == (unsigned int)-1) 02698 break; // quit wrap around case 02699 for(unsigned int col = 0; col < getNumberOfColumns(); ++col) 02700 theDataView_[r + 1][col] = theDataView_[r][col]; 02701 } 02702 02703 std::vector<std::string> defaultRowValues = getDefaultRowValues(); 02704 02705 char indexString[1000]; 02706 std::string tmpString, baseString; 02707 bool foundAny; 02708 unsigned int index; 02709 unsigned int maxUniqueData; 02710 std::string numString; 02711 02712 // fill each col of new row with default values 02713 // if a row is a unique data row, increment last row in attempt to make a legal 02714 // column 02715 for(unsigned int col = 0; col < getNumberOfColumns(); ++col) 02716 { 02717 // __COUT__ << col << " " << columnsInfo_[col].getType() << " == " << 02718 // TableViewColumnInfo::TYPE_UNIQUE_DATA << __E__; 02719 02720 // baseNameAutoUID indicates to attempt to make row unique 02721 // add index to max number 02722 if(incrementUniqueData && 02723 (col == getColUID() || 02724 (getNumberOfRows() > 1 && 02725 (columnsInfo_[col].getType() == TableViewColumnInfo::TYPE_UNIQUE_DATA || 02726 columnsInfo_[col].getType() == 02727 TableViewColumnInfo::TYPE_UNIQUE_GROUP_DATA)))) 02728 { 02729 // __COUT__ << "Current unique data entry is data[" << rowToAdd 02730 // << "][" << col << "] = '" << theDataView_[rowToAdd][col] 02731 //<< 02732 //"'" 02733 // << __E__; 02734 02735 maxUniqueData = 0; 02736 tmpString = ""; 02737 baseString = ""; 02738 02739 // find max in rows 02740 02741 // this->print(); 02742 02743 for(unsigned int r = 0; r < getNumberOfRows(); ++r) 02744 { 02745 if(r == rowToAdd) 02746 continue; // skip row to add 02747 02748 // find last non numeric character 02749 02750 foundAny = false; 02751 tmpString = theDataView_[r][col]; 02752 02753 //__COUT__ << "tmpString " << tmpString << __E__; 02754 02755 for(index = tmpString.length() - 1; index < tmpString.length(); --index) 02756 { 02757 //__COUT__ << index << " tmpString[index] " << tmpString[index] << 02758 //__E__; 02759 if(!(tmpString[index] >= '0' && tmpString[index] <= '9')) 02760 break; // if not numeric, break 02761 foundAny = true; 02762 } 02763 02764 //__COUT__ << "index " << index << __E__; 02765 02766 if(tmpString.length() && foundAny) // then found a numeric substring 02767 { 02768 // create numeric substring 02769 numString = tmpString.substr(index + 1); 02770 tmpString = tmpString.substr(0, index + 1); 02771 02772 //__COUT__ << "Found unique data base string '" << 02773 // tmpString << "' and number string '" << numString << 02774 // "' in last record '" << theDataView_[r][col] << "'" << __E__; 02775 02776 // extract number 02777 sscanf(numString.c_str(), "%u", &index); 02778 02779 if(index > maxUniqueData) 02780 { 02781 maxUniqueData = index; 02782 baseString = tmpString; 02783 } 02784 } 02785 } 02786 02787 ++maxUniqueData; // increment 02788 02789 sprintf(indexString, "%u", maxUniqueData); 02790 //__COUT__ << "indexString " << indexString << __E__; 02791 02792 //__COUT__ << "baseNameAutoUID " << baseNameAutoUID << __E__; 02793 if(col == getColUID()) 02794 { 02795 // handle UID case 02796 if(baseNameAutoUID != "") 02797 theDataView_[rowToAdd][col] = baseNameAutoUID + indexString; 02798 else 02799 theDataView_[rowToAdd][col] = baseString + indexString; 02800 } 02801 else 02802 theDataView_[rowToAdd][col] = baseString + indexString; 02803 02804 __COUT__ << "New unique data entry is data[" << rowToAdd << "][" << col 02805 << "] = '" << theDataView_[rowToAdd][col] << "'" << __E__; 02806 02807 // this->print(); 02808 } 02809 else 02810 theDataView_[rowToAdd][col] = defaultRowValues[col]; 02811 } 02812 02813 if(author != "") 02814 { 02815 __COUT__ << "Row=" << rowToAdd << " was created!" << __E__; 02816 int authorCol = findColByType(TableViewColumnInfo::TYPE_AUTHOR); 02817 int timestampCol = findColByType(TableViewColumnInfo::TYPE_TIMESTAMP); 02818 setValue(author, rowToAdd, authorCol); 02819 setValue(time(0), rowToAdd, timestampCol); 02820 } 02821 02822 return rowToAdd; 02823 } // end addRow() 02824 02825 //============================================================================== 02826 // deleteRow 02827 // throws exception on failure 02828 void TableView::deleteRow(int r) 02829 { 02830 if(r >= (int)getNumberOfRows()) 02831 { 02832 // out of bounds 02833 __SS__ << "Row " << (int)r 02834 << " is out of bounds (Row Count = " << getNumberOfRows() 02835 << ") and can not be deleted." << __E__; 02836 __SS_THROW__; 02837 } 02838 02839 theDataView_.erase(theDataView_.begin() + r); 02840 } // end deleteRow() 02841 02842 //============================================================================== 02843 // getChildLink ~ 02844 // find the pair of columns associated with a child link. 02845 // 02846 // c := a member column of the pair 02847 // 02848 // returns: 02849 // isGroup := indicates pair found is a group link 02850 // linkPair := pair of columns that are part of the link 02851 // 02852 // a unique link is defined by two columns: TYPE_START_CHILD_LINK, 02853 // TYPE_START_CHILD_LINK_UID 02854 // a group link is defined by two columns: TYPE_START_CHILD_LINK, 02855 // TYPE_START_CHILD_LINK_GROUP_ID 02856 // 02857 // returns true if column is member of a group or unique link. 02858 const bool TableView::getChildLink( 02859 const unsigned int& c, 02860 bool& isGroup, 02861 std::pair<unsigned int /*link col*/, unsigned int /*link id col*/>& linkPair) const 02862 { 02863 if(!(c < columnsInfo_.size())) 02864 { 02865 __SS__ << "Invalid col (" << (int)c << ") requested!" << __E__; 02866 __SS_THROW__; 02867 } 02868 02869 //__COUT__ << "getChildLink for col: " << (int)c << "-" << 02870 // columnsInfo_[c].getType() << "-" << columnsInfo_[c].getName() << __E__; 02871 02872 // check if column is a child link UID 02873 if((isGroup = columnsInfo_[c].isChildLinkGroupID()) || 02874 columnsInfo_[c].isChildLinkUID()) 02875 { 02876 // must be part of unique link, (or invalid table?) 02877 //__COUT__ << "col: " << (int)c << __E__; 02878 linkPair.second = c; 02879 std::string index = columnsInfo_[c].getChildLinkIndex(); 02880 02881 //__COUT__ << "index: " << index << __E__; 02882 02883 // find pair link 02884 for(unsigned int col = 0; col < columnsInfo_.size(); ++col) 02885 { 02886 //__COUT__ << "try: " << col << "-" << columnsInfo_[col].getType() << "-" << 02887 // columnsInfo_[col].getName() << __E__; 02888 if(col == c) 02889 continue; // skip column c that we know 02890 else if(columnsInfo_[col].isChildLink() && 02891 index == columnsInfo_[col].getChildLinkIndex()) 02892 { 02893 // found match! 02894 //__COUT__ << "getChildLink Found match for col: " << (int)c << " at " << 02895 // col << __E__; 02896 linkPair.first = col; 02897 return true; 02898 } 02899 } 02900 02901 // if here then invalid table! 02902 __SS__ << "\tIn view: " << tableName_ 02903 << ", Can't find complete child link for column name " 02904 << columnsInfo_[c].getName() << __E__; 02905 __SS_THROW__; 02906 } 02907 02908 if(!columnsInfo_[c].isChildLink()) 02909 return false; // cant be unique link 02910 02911 // this is child link, so find pair link uid or gid column 02912 linkPair.first = c; 02913 std::string index = columnsInfo_[c].getChildLinkIndex(); 02914 02915 //__COUT__ << "index: " << index << __E__; 02916 02917 // find pair link 02918 for(unsigned int col = 0; col < columnsInfo_.size(); ++col) 02919 { 02920 //__COUT__ << "try: " << col << "-" << columnsInfo_[col].getType() << "-" << 02921 // columnsInfo_[col].getName() << __E__; 02922 if(col == c) 02923 continue; // skip column c that we know 02924 // __COUT__ << "try: " << col << "-" << columnsInfo_[col].getType() << 02925 // "-" << columnsInfo_[col].getName() << 02926 // "-u" << columnsInfo_[col].isChildLinkUID() << 02927 // "-g" << columnsInfo_[col].isChildLinkGroupID() << __E__; 02928 // 02929 // if(columnsInfo_[col].isChildLinkUID()) 02930 // __COUT__ << "-L" << columnsInfo_[col].getChildLinkIndex() << __E__; 02931 // 02932 // if(columnsInfo_[col].isChildLinkGroupID()) 02933 // __COUT__ << "-L" << columnsInfo_[col].getChildLinkIndex() << __E__; 02934 02935 if(((columnsInfo_[col].isChildLinkUID() && !(isGroup = false)) || 02936 (columnsInfo_[col].isChildLinkGroupID() && (isGroup = true))) && 02937 index == columnsInfo_[col].getChildLinkIndex()) 02938 { 02939 // found match! 02940 //__COUT__ << "getChildLink Found match for col: " << (int)c << " at " << col 02941 //<< __E__; 02942 linkPair.second = col; 02943 return true; 02944 } 02945 } 02946 02947 // if here then invalid table! 02948 __SS__ << "\tIn view: " << tableName_ 02949 << ", Can't find complete child link id for column name " 02950 << columnsInfo_[c].getName() << __E__; 02951 __SS_THROW__; 02952 } // end getChildLink()