![]() |
XML Mill
1.0.0
A GUI based XML editor with a memory.
|
00001 /* Copyright (c) 2012 - 2013 by William Hallatt. 00002 * 00003 * This file forms part of "XML Mill". 00004 * 00005 * The official website for this project is <http://www.goblincoding.com> and, 00006 * although not compulsory, it would be appreciated if all works of whatever 00007 * nature using this source code (in whole or in part) include a reference to 00008 * this site. 00009 * 00010 * Should you wish to contact me for whatever reason, please do so via: 00011 * 00012 * <http://www.goblincoding.com/contact> 00013 * 00014 * This program is free software: you can redistribute it and/or modify it under 00015 * the terms of the GNU General Public License as published by the Free Software 00016 * Foundation, either version 3 of the License, or (at your option) any later 00017 * version. 00018 * 00019 * This program is distributed in the hope that it will be useful, but WITHOUT 00020 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 00021 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 00022 * 00023 * You should have received a copy of the GNU General Public License along with 00024 * this program (GNUGPL.txt). If not, see 00025 * 00026 * <http://www.gnu.org/licenses/> 00027 */ 00028 00029 #include "gcdatabaseinterface.h" 00030 #include "gcbatchprocessorhelper.h" 00031 00032 #include <QDomDocument> 00033 #include <QStringList> 00034 #include <QFile> 00035 #include <QTextStream> 00036 #include <QApplication> 00037 #include <QtSql/QSqlDatabase> 00038 #include <QtSql/QSqlError> 00039 #include <QtSql/QSqlRecord> 00040 #include <QtSql/QSqlField> 00041 00042 /*--------------------------------------------------------------------------------------*/ 00043 00044 /* Have a look at "createTables" to see how the DB is set up. */ 00045 static const QLatin1String INSERT_ELEMENT( 00046 "INSERT INTO xmlelements( element, children, attributes ) VALUES( ?, ?, ? )" ); 00047 00048 static const QLatin1String INSERT_ATTRIBUTEVALUES( 00049 "INSERT INTO xmlattributes( attribute, associatedElement, attributeValues ) VALUES( ?, ?, ? )" ); 00050 00051 static const QLatin1String UPDATE_CHILDREN( 00052 "UPDATE xmlelements SET children = ? WHERE element = ?" ); 00053 00054 static const QLatin1String UPDATE_ATTRIBUTES( 00055 "UPDATE xmlelements SET attributes = ? WHERE element = ?" ); 00056 00057 static const QLatin1String UPDATE_ATTRIBUTEVALUES( 00058 "UPDATE xmlattributes SET attributeValues = ? WHERE attribute = ? AND associatedElement = ?" ); 00059 00060 /*--------------------------------------------------------------------------------------*/ 00061 00062 /* Flat file containing list of databases. */ 00063 static const QString DB_FILE( "dblist.txt" ); 00064 00065 /* Regular expression string to split "\" (Windows) or "/" (Unix) from file path. */ 00066 static const QString REGEXP_SLASHES( "(\\\\|\\/)" ); 00067 00068 /* The database tables have fields containing strings of strings. For example, the 00069 "xmlelements" table maps a unique element against a list of all associated attribute 00070 values. Since these values have to be entered into a single record, the easiest way 00071 is to insert a single (possibly massive) string containing all the associated attributes. 00072 To ensure that we can later extract the individual attributes again, we separate them with 00073 a sequence that should (theoretically) never be encountered. This is that sequence. */ 00074 static const QString SEPARATOR( "~!@" ); 00075 00076 /*--------------------------- NON-MEMBER UTILITY FUNCTIONS ----------------------------*/ 00077 00078 void cleanList( QStringList& list ) 00079 { 00080 list.removeDuplicates(); 00081 list.removeAll( "" ); 00082 } 00083 00084 /*--------------------------------------------------------------------------------------*/ 00085 00086 QString cleanAndJoinListElements( QStringList list ) 00087 { 00088 cleanList( list ); 00089 return list.join( SEPARATOR ); 00090 } 00091 00092 /*--------------------------------- MEMBER FUNCTIONS ----------------------------------*/ 00093 00094 GCDataBaseInterface* GCDataBaseInterface::m_instance = NULL; 00095 00096 GCDataBaseInterface* GCDataBaseInterface::instance() 00097 { 00098 if( !m_instance ) 00099 { 00100 m_instance = new GCDataBaseInterface; 00101 } 00102 00103 return m_instance; 00104 } 00105 00106 /*--------------------------------------------------------------------------------------*/ 00107 00108 GCDataBaseInterface::GCDataBaseInterface() 00109 : m_sessionDB (), 00110 m_lastErrorMsg ( "" ), 00111 m_hasActiveSession( false ), 00112 m_initialised ( false ), 00113 m_dbMap () 00114 { 00115 QFile flatFile( DB_FILE ); 00116 00117 /* ReadWrite mode is required to create the file if it doesn't exist. */ 00118 if( flatFile.open( QIODevice::ReadWrite | QIODevice::Text ) ) 00119 { 00120 m_lastErrorMsg = ""; 00121 m_initialised = true; 00122 00123 QTextStream inStream( &flatFile ); 00124 QString fileContent = inStream.readAll(); 00125 flatFile.close(); 00126 00127 /* Split the input into separate lines (path/to/file lines). */ 00128 QStringList list = fileContent.split( "\n", QString::SkipEmptyParts ); 00129 00130 foreach( QString str, list ) 00131 { 00132 if( !addDatabase( str ) ) 00133 { 00134 m_lastErrorMsg = QString( "Failed to load existing connection: \n %1" ).arg( str ); 00135 m_initialised = false; 00136 } 00137 } 00138 } 00139 else 00140 { 00141 m_lastErrorMsg = QString( "Failed to access list of databases, file open error: [%1]." ).arg( flatFile.errorString() ); 00142 m_initialised = false; 00143 } 00144 } 00145 00146 /*--------------------------------------------------------------------------------------*/ 00147 00148 bool GCDataBaseInterface::isInitialised() const 00149 { 00150 return m_initialised; 00151 } 00152 00153 /*--------------------------------------------------------------------------------------*/ 00154 00155 bool GCDataBaseInterface::batchProcessDomDocument( const QDomDocument* domDoc ) const 00156 { 00157 GCBatchProcessorHelper helper( domDoc, 00158 SEPARATOR, 00159 knownElements(), 00160 knownAttributeKeys() ); 00161 00162 qApp->processEvents( QEventLoop::ExcludeUserInputEvents ); 00163 00164 if( !addRootElement( domDoc->documentElement().tagName() ) ) 00165 { 00166 /* Last error message is set in "addRootElement". */ 00167 return false; 00168 } 00169 00170 QSqlQuery query( m_sessionDB ); 00171 00172 /* Batch insert all the new elements. */ 00173 if( !query.prepare( INSERT_ELEMENT ) ) 00174 { 00175 m_lastErrorMsg = QString( "Prepare batch INSERT elements failed: [%1]" ) 00176 .arg( query.lastError().text() ); 00177 return false; 00178 } 00179 00180 query.addBindValue( helper.newElementsToAdd() ); 00181 query.addBindValue( helper.newElementChildrenToAdd() ); 00182 query.addBindValue( helper.newElementAttributesToAdd() ); 00183 00184 if( !query.execBatch() ) 00185 { 00186 m_lastErrorMsg = QString( "Batch INSERT elements failed: [%1]" ) 00187 .arg( query.lastError().text() ); 00188 return false; 00189 } 00190 00191 qApp->processEvents( QEventLoop::ExcludeUserInputEvents ); 00192 00193 /* Batch update all the existing elements by concatenating the new values to the 00194 existing values. The second '?' represents our string SEPARATOR. */ 00195 if( !query.prepare( "UPDATE xmlelements " 00196 "SET children = ( IFNULL( ?, \"\" ) || IFNULL( ?, \"\" ) || IFNULL( children, \"\" ) ) " 00197 "WHERE element = ?" ) ) 00198 { 00199 m_lastErrorMsg = QString( "Prepare batch UPDATE element children failed: [%1]" ) 00200 .arg( query.lastError().text() ); 00201 return false; 00202 } 00203 00204 /* Since we're doing batch updates we need to ensure that all the variant lists we provide 00205 have exactly the same size. We furthermore require that the concatenation of new and old values 00206 are done in a way that includes our SEPARATOR string (which is why the separator list below). */ 00207 QVariantList separatorList; 00208 00209 for( int i = 0; i < helper.elementsToUpdate().size(); ++i ) 00210 { 00211 separatorList << SEPARATOR; 00212 } 00213 00214 query.addBindValue( helper.elementChildrenToUpdate() ); 00215 query.addBindValue( separatorList ); 00216 query.addBindValue( helper.elementsToUpdate() ); 00217 00218 if( !query.execBatch() ) 00219 { 00220 m_lastErrorMsg = QString( "Batch UPDATE element children failed: [%1]" ) 00221 .arg( query.lastError().text() ); 00222 return false; 00223 } 00224 00225 qApp->processEvents( QEventLoop::ExcludeUserInputEvents ); 00226 00227 if( !query.prepare( "UPDATE xmlelements " 00228 "SET attributes = ( IFNULL( ?, \"\" ) || IFNULL( ?, \"\" ) || IFNULL( attributes, \"\" ) ) " 00229 "WHERE element = ?" ) ) 00230 { 00231 m_lastErrorMsg = QString( "Prepare batch UPDATE element attributes failed: [%1]" ) 00232 .arg( query.lastError().text() ); 00233 return false; 00234 } 00235 00236 query.addBindValue( helper.elementAttributesToUpdate() ); 00237 query.addBindValue( separatorList ); 00238 query.addBindValue( helper.elementsToUpdate() ); 00239 00240 if( !query.execBatch() ) 00241 { 00242 m_lastErrorMsg = QString( "Batch UPDATE element attributes failed: [%1]" ) 00243 .arg( query.lastError().text() ); 00244 return false; 00245 } 00246 00247 qApp->processEvents( QEventLoop::ExcludeUserInputEvents ); 00248 00249 /* Batch insert all the new attribute values. */ 00250 if( !query.prepare( INSERT_ATTRIBUTEVALUES ) ) 00251 { 00252 m_lastErrorMsg = QString( "Prepare batch INSERT attribute values failed: [%1]" ) 00253 .arg( query.lastError().text() ); 00254 return false; 00255 } 00256 00257 query.addBindValue( helper.newAttributeKeysToAdd() ); 00258 query.addBindValue( helper.newAssociatedElementsToAdd() ); 00259 query.addBindValue( helper.newAttributeValuesToAdd() ); 00260 00261 if( !query.execBatch() ) 00262 { 00263 m_lastErrorMsg = QString( "Batch INSERT attribute values failed: [%1]" ) 00264 .arg( query.lastError().text() ); 00265 return false; 00266 } 00267 00268 qApp->processEvents( QEventLoop::ExcludeUserInputEvents ); 00269 00270 /* Batch update all the existing attribute values. */ 00271 if( !query.prepare( "UPDATE xmlattributes " 00272 "SET attributeValues = ( IFNULL( ?, \"\" ) || IFNULL( ?, \"\" ) || IFNULL( attributeValues, \"\" ) ) " 00273 "WHERE attribute = ? " 00274 "AND associatedElement = ?" ) ) 00275 { 00276 m_lastErrorMsg = QString( "Prepare batch UPDATE attribute values failed: [%1]" ) 00277 .arg( query.lastError().text() ); 00278 return false; 00279 } 00280 00281 separatorList.clear(); 00282 00283 for( int i = 0; i < helper.attributeKeysToUpdate().size(); ++i ) 00284 { 00285 separatorList << SEPARATOR; 00286 } 00287 00288 query.addBindValue( helper.attributeValuesToUpdate() ); 00289 query.addBindValue( separatorList ); 00290 query.addBindValue( helper.attributeKeysToUpdate() ); 00291 query.addBindValue( helper.associatedElementsToUpdate() ); 00292 00293 if( !query.execBatch() ) 00294 { 00295 m_lastErrorMsg = QString( "Batch UPDATE attribute values failed: [%1]" ) 00296 .arg( query.lastError().text() ); 00297 return false; 00298 } 00299 00300 return removeDuplicatesFromFields(); 00301 } 00302 00303 /*--------------------------------------------------------------------------------------*/ 00304 00305 bool GCDataBaseInterface::addElement( const QString& element, const QStringList& children, const QStringList& attributes ) const 00306 { 00307 if( element.isEmpty() ) 00308 { 00309 m_lastErrorMsg = QString( "Trying to add an empty element name." ); 00310 return false; 00311 } 00312 00313 QSqlQuery query = selectElement( element ); 00314 00315 /* If we don't have an existing record, add it. */ 00316 if( !query.first() ) 00317 { 00318 if( !query.prepare( INSERT_ELEMENT ) ) 00319 { 00320 m_lastErrorMsg = QString( "Prepare INSERT element failed for element \"%1\": [%2]" ) 00321 .arg( element ) 00322 .arg( query.lastError().text() ); 00323 return false; 00324 } 00325 00326 query.addBindValue( element ); 00327 query.addBindValue( cleanAndJoinListElements( children ) ); 00328 query.addBindValue( cleanAndJoinListElements( attributes ) ); 00329 00330 if( !query.exec() ) 00331 { 00332 m_lastErrorMsg = QString( "INSERT element failed for element \"%1\": [%2]" ) 00333 .arg( element ) 00334 .arg( query.lastError().text() ); 00335 return false; 00336 } 00337 } 00338 00339 m_lastErrorMsg = ""; 00340 return true; 00341 } 00342 00343 /*--------------------------------------------------------------------------------------*/ 00344 00345 bool GCDataBaseInterface::addRootElement( const QString& root ) const 00346 { 00347 if( root.isEmpty() ) 00348 { 00349 m_lastErrorMsg = QString( "Trying to add an empty root element name." ); 00350 return false; 00351 } 00352 00353 QSqlQuery query( m_sessionDB ); 00354 00355 if( !query.prepare( "SELECT * FROM rootelements WHERE root = ? " ) ) 00356 { 00357 m_lastErrorMsg = QString( "Prepare SELECT root element failed for root \"%1\": [%2]" ) 00358 .arg( root ) 00359 .arg( query.lastError().text() ); 00360 return false; 00361 } 00362 00363 query.addBindValue( root ); 00364 00365 if( !query.exec() ) 00366 { 00367 m_lastErrorMsg = QString( "SELECT root element failed for root \"%1\": [%2]" ) 00368 .arg( root ) 00369 .arg( query.lastError().text() ); 00370 return false; 00371 } 00372 00373 /* Make sure we aren't trying to insert a known root element. */ 00374 if( !query.first() ) 00375 { 00376 if( !query.prepare( "INSERT INTO rootelements ( root ) VALUES( ? )" ) ) 00377 { 00378 m_lastErrorMsg = QString( "Prepare INSERT root element failed for root \"%1\": [%2]" ) 00379 .arg( root ) 00380 .arg( query.lastError().text() ); 00381 return false; 00382 } 00383 00384 query.addBindValue( root ); 00385 00386 if( !query.exec() ) 00387 { 00388 m_lastErrorMsg = QString( "INSERT root element failed for element \"%1\": [%2]" ) 00389 .arg( root ) 00390 .arg( query.lastError().text() ); 00391 return false; 00392 } 00393 } 00394 00395 m_lastErrorMsg = ""; 00396 return true; 00397 } 00398 00399 /*--------------------------------------------------------------------------------------*/ 00400 00401 bool GCDataBaseInterface::updateElementChildren( const QString& element, const QStringList& children, bool replace ) const 00402 { 00403 if( element.isEmpty() ) 00404 { 00405 m_lastErrorMsg = QString( "Invalid element name provided." ); 00406 return false; 00407 } 00408 00409 QSqlQuery query = selectElement( element ); 00410 00411 /* Update the existing record (if we have one). */ 00412 if( query.first() ) 00413 { 00414 QStringList allChildren( children ); 00415 00416 if( !replace ) 00417 { 00418 allChildren.append( query.record().field( "children" ).value().toString().split( SEPARATOR ) ); 00419 } 00420 00421 if( !query.prepare( UPDATE_CHILDREN ) ) 00422 { 00423 m_lastErrorMsg = QString( "Prepare UPDATE element children failed for element \"%1\": [%2]" ) 00424 .arg( element ) 00425 .arg( query.lastError().text() ); 00426 return false; 00427 } 00428 00429 query.addBindValue( cleanAndJoinListElements( allChildren ) ); 00430 query.addBindValue( element ); 00431 00432 if( !query.exec() ) 00433 { 00434 m_lastErrorMsg = QString( "UPDATE children failed for element \"%1\": [%2]" ) 00435 .arg( element ) 00436 .arg( query.lastError().text() ); 00437 return false; 00438 } 00439 } 00440 else 00441 { 00442 m_lastErrorMsg = QString( "No knowledge of element \"%1\", add it first." ) 00443 .arg( element ); 00444 return false; 00445 } 00446 00447 m_lastErrorMsg = ""; 00448 return true; 00449 } 00450 00451 /*--------------------------------------------------------------------------------------*/ 00452 00453 bool GCDataBaseInterface::updateElementAttributes( const QString& element, const QStringList& attributes, bool replace ) const 00454 { 00455 if( element.isEmpty() ) 00456 { 00457 m_lastErrorMsg = QString( "Invalid element name provided." ); 00458 return false; 00459 } 00460 00461 QSqlQuery query = selectElement( element ); 00462 00463 /* Update the existing record (if we have one). */ 00464 if( query.first() ) 00465 { 00466 QStringList allAttributes; 00467 00468 if( !replace ) 00469 { 00470 allAttributes.append( query.record().field( "attributes" ).value().toString().split( SEPARATOR ) ); 00471 } 00472 00473 /* Add it here so that we append the new attributes to the end of the list. */ 00474 allAttributes.append( attributes ); 00475 00476 if( !query.prepare( UPDATE_ATTRIBUTES ) ) 00477 { 00478 m_lastErrorMsg = QString( "Prepare UPDATE element attribute failed for element \"%1\": [%2]" ) 00479 .arg( element ) 00480 .arg( query.lastError().text() ); 00481 return false; 00482 } 00483 00484 query.addBindValue( cleanAndJoinListElements( allAttributes ) ); 00485 query.addBindValue( element ); 00486 00487 if( !query.exec() ) 00488 { 00489 m_lastErrorMsg = QString( "UPDATE attribute failed for element \"%1\": [%2]" ) 00490 .arg( element ) 00491 .arg( query.lastError().text() ); 00492 return false; 00493 } 00494 } 00495 else 00496 { 00497 m_lastErrorMsg = QString( "No element \"%1\" exists." ) 00498 .arg( element ); 00499 return false; 00500 } 00501 00502 m_lastErrorMsg = ""; 00503 return true; 00504 } 00505 00506 /*--------------------------------------------------------------------------------------*/ 00507 00508 bool GCDataBaseInterface::updateAttributeValues( const QString& element, const QString& attribute, const QStringList& attributeValues, bool replace ) const 00509 { 00510 if( element.isEmpty() || attribute.isEmpty() ) 00511 { 00512 m_lastErrorMsg = QString( "Invalid element or attribute values provided." ); 00513 return false; 00514 } 00515 00516 QSqlQuery query = selectAttribute( attribute, element ); 00517 00518 /* If we don't have an existing record, add it, otherwise update the existing one. */ 00519 if( !query.first() ) 00520 { 00521 if( !query.prepare( INSERT_ATTRIBUTEVALUES ) ) 00522 { 00523 m_lastErrorMsg = QString( "Prepare INSERT attribute value failed for element \"%1\": [%2]" ) 00524 .arg( element ) 00525 .arg( query.lastError().text() ); 00526 return false; 00527 } 00528 00529 query.addBindValue( attribute ); 00530 query.addBindValue( element ); 00531 query.addBindValue( cleanAndJoinListElements( attributeValues ) ); 00532 00533 if( !query.exec() ) 00534 { 00535 m_lastErrorMsg = QString( "INSERT attribute failed for element \"%1\": [%2]" ) 00536 .arg( element ) 00537 .arg( query.lastError().text() ); 00538 return false; 00539 } 00540 } 00541 else 00542 { 00543 QStringList existingValues( attributeValues ); 00544 00545 if( !replace ) 00546 { 00547 existingValues.append( query.record().field( "attributeValues" ).value().toString().split( SEPARATOR ) ); 00548 } 00549 00550 /* The reason for not using concatenating values here is that we don't simply want to add 00551 all the supposed new values, we want to make sure they are all unique by removing all duplicates 00552 before sticking it all back into the DB. */ 00553 if( !query.prepare( UPDATE_ATTRIBUTEVALUES ) ) 00554 { 00555 m_lastErrorMsg = QString( "Prepare UPDATE attribute values failed for element \"%1\" and attribute \"%2\": [%3]" ) 00556 .arg( element ) 00557 .arg( attribute ) 00558 .arg( query.lastError().text() ); 00559 return false; 00560 } 00561 00562 query.addBindValue( cleanAndJoinListElements( existingValues ) ); 00563 query.addBindValue( attribute ); 00564 query.addBindValue( element ); 00565 00566 if( !query.exec() ) 00567 { 00568 m_lastErrorMsg = QString( "UPDATE attribute values failed for element \"%1\" and attribute [%2]: [%3]" ) 00569 .arg( element ) 00570 .arg( attribute ) 00571 .arg( query.lastError().text() ); 00572 return false; 00573 } 00574 } 00575 00576 m_lastErrorMsg = ""; 00577 return true; 00578 } 00579 00580 /*--------------------------------------------------------------------------------------*/ 00581 00582 bool GCDataBaseInterface::removeElement( const QString& element ) const 00583 { 00584 QSqlQuery query = selectElement( element ); 00585 00586 /* Only continue if we have an existing record. */ 00587 if( query.first() ) 00588 { 00589 if( !query.prepare( "DELETE FROM xmlelements WHERE element = ?" ) ) 00590 { 00591 m_lastErrorMsg = QString( "Prepare DELETE element failed for element \"%1\": [%3]" ) 00592 .arg( element ) 00593 .arg( query.lastError().text() ); 00594 return false; 00595 } 00596 00597 query.addBindValue( element ); 00598 00599 if( !query.exec() ) 00600 { 00601 m_lastErrorMsg = QString( "DELETE element failed for element \"%1\": [%3]" ) 00602 .arg( element ) 00603 .arg( query.lastError().text() ); 00604 return false; 00605 } 00606 } 00607 00608 m_lastErrorMsg = ""; 00609 return true; 00610 } 00611 00612 /*--------------------------------------------------------------------------------------*/ 00613 00614 bool GCDataBaseInterface::removeChildElement( const QString& element, const QString& child ) const 00615 { 00616 QSqlQuery query = selectElement( element ); 00617 00618 /* Update the existing record (if we have one). */ 00619 if( query.first() ) 00620 { 00621 QStringList allChildren( query.record().field( "children" ).value().toString().split( SEPARATOR ) ); 00622 allChildren.removeAll( child ); 00623 updateElementChildren( element, allChildren, true ); 00624 } 00625 00626 m_lastErrorMsg = ""; 00627 return true; 00628 } 00629 00630 /*--------------------------------------------------------------------------------------*/ 00631 00632 bool GCDataBaseInterface::removeAttribute( const QString& element, const QString& attribute ) const 00633 { 00634 QSqlQuery query = selectAttribute( attribute, element ); 00635 00636 /* Only continue if we have an existing record. */ 00637 if( query.first() ) 00638 { 00639 if( !query.prepare( "DELETE FROM xmlattributes " 00640 "WHERE attribute = ? " 00641 "AND associatedElement = ?" ) ) 00642 { 00643 m_lastErrorMsg = QString( "Prepare DELETE attribute failed for element \"%1\" and attribute \"%2\": [%3]" ) 00644 .arg( element ) 00645 .arg( attribute ) 00646 .arg( query.lastError().text() ); 00647 return false; 00648 } 00649 00650 query.addBindValue( attribute ); 00651 query.addBindValue( element ); 00652 00653 if( !query.exec() ) 00654 { 00655 m_lastErrorMsg = QString( "DELETE attribute failed for element \"%1\" and attribute [%2]: [%3]" ) 00656 .arg( element ) 00657 .arg( attribute ) 00658 .arg( query.lastError().text() ); 00659 return false; 00660 } 00661 } 00662 00663 query = selectElement( element ); 00664 QStringList allAttributes = attributes( element ); 00665 allAttributes.removeAll( attribute ); 00666 updateElementAttributes( element, allAttributes, true ); 00667 00668 m_lastErrorMsg = ""; 00669 return true; 00670 } 00671 00672 /*--------------------------------------------------------------------------------------*/ 00673 00674 bool GCDataBaseInterface::removeRootElement( const QString& element ) const 00675 { 00676 QSqlQuery query( m_sessionDB ); 00677 00678 if( !query.prepare( "DELETE FROM rootelements WHERE root = ?" ) ) 00679 { 00680 m_lastErrorMsg = QString( "Prepare DELETE failed for root \"%1\": [%2]" ) 00681 .arg( element ) 00682 .arg( query.lastError().text() ); 00683 return false; 00684 } 00685 00686 query.addBindValue( element ); 00687 00688 if( !query.exec() ) 00689 { 00690 m_lastErrorMsg = QString( "DELETE root element failed for root \"%1\": [%2]" ) 00691 .arg( element ) 00692 .arg( query.lastError().text() ); 00693 return false; 00694 } 00695 00696 m_lastErrorMsg = ""; 00697 return true; 00698 } 00699 00700 /*--------------------------------------------------------------------------------------*/ 00701 00702 bool GCDataBaseInterface::hasActiveSession() const 00703 { 00704 return m_hasActiveSession; 00705 } 00706 00707 /*--------------------------------------------------------------------------------------*/ 00708 00709 QString GCDataBaseInterface::activeSessionName() const 00710 { 00711 if( m_hasActiveSession ) 00712 { 00713 return m_sessionDB.connectionName(); 00714 } 00715 00716 return QString(); 00717 } 00718 00719 /*--------------------------------------------------------------------------------------*/ 00720 00721 bool GCDataBaseInterface::isProfileEmpty() const 00722 { 00723 return knownRootElements().isEmpty(); 00724 } 00725 00726 /*--------------------------------------------------------------------------------------*/ 00727 00728 bool GCDataBaseInterface::isUniqueChildElement( const QString& parentElement, const QString& element ) const 00729 { 00730 QSqlQuery query = selectAllElements(); 00731 00732 while( query.next() ) 00733 { 00734 if( query.record().field( "element" ).value().toString() != parentElement && 00735 query.record().value( "children" ).toString().split( SEPARATOR ).contains( element ) ) 00736 { 00737 return false; 00738 } 00739 } 00740 00741 return true; 00742 } 00743 00744 /*--------------------------------------------------------------------------------------*/ 00745 00746 bool GCDataBaseInterface::isDocumentCompatible( const QDomDocument* doc ) const 00747 { 00748 GCBatchProcessorHelper helper( doc, 00749 SEPARATOR, 00750 knownElements(), 00751 knownAttributeKeys() ); 00752 00753 qApp->processEvents( QEventLoop::ExcludeUserInputEvents ); 00754 00755 /* If there are any new elements or attributes to add, the document is incompatible (not checking 00756 for new attribute values since new values don't affect XML relationships, i.e. it isn't important 00757 enough to import entire documents each time an unknown value is encountered) */ 00758 if( !helper.newAssociatedElementsToAdd().isEmpty() || 00759 !helper.newAttributeKeysToAdd().isEmpty() || 00760 !helper.newElementAttributesToAdd().isEmpty() || 00761 !helper.newElementChildrenToAdd().isEmpty() || 00762 !helper.newElementsToAdd().isEmpty() ) 00763 { 00764 return false; 00765 } 00766 00767 /* If there are no new items, we may still have previously unknown relationships which we 00768 need to check. This is slightly more involved than simply checking for empty lists. 00769 Also remember that the lists returned by GCBatchProcessorHelper are all synchronised 00770 with regards to their indices (which is the only reason why we can loop through the 00771 lists like this). */ 00772 for( int i = 0; i < helper.elementsToUpdate().size(); ++i ) 00773 { 00774 /* If any new element children were added, we have an incompatible document. */ 00775 QStringList knownChildren = children( helper.elementsToUpdate().at( i ).toString() ); 00776 QStringList allChildren = QStringList() << knownChildren << helper.elementChildrenToUpdate().at( i ).toString().split( SEPARATOR ); 00777 cleanList( allChildren ); 00778 00779 /* Check for larger since we only care about added items. */ 00780 if( allChildren.size() > knownChildren.size() ) 00781 { 00782 return false; 00783 } 00784 00785 /* If any new attributes were added, we also have an incompatible document. */ 00786 QStringList knownAttributes = attributes( helper.elementsToUpdate().at( i ).toString() ); 00787 QStringList allAttributes = QStringList() << knownAttributes << helper.elementAttributesToUpdate().at( i ).toString().split( SEPARATOR ); 00788 cleanList( allAttributes ); 00789 00790 if( allAttributes.size() > knownAttributes.size() ) 00791 { 00792 return false; 00793 } 00794 } 00795 00796 return true; 00797 } 00798 00799 /*--------------------------------------------------------------------------------------*/ 00800 00801 QStringList GCDataBaseInterface::knownElements() const 00802 { 00803 QSqlQuery query = selectAllElements(); 00804 00805 m_lastErrorMsg = ""; 00806 00807 QStringList elementNames; 00808 00809 while( query.next() ) 00810 { 00811 elementNames.append( query.record().field( "element" ).value().toString() ); 00812 } 00813 00814 cleanList( elementNames ); 00815 elementNames.sort(); 00816 return elementNames; 00817 } 00818 00819 /*--------------------------------------------------------------------------------------*/ 00820 00821 QStringList GCDataBaseInterface::children( const QString& element ) const 00822 { 00823 QSqlQuery query = selectElement( element ); 00824 00825 /* There should be only one record corresponding to this element. */ 00826 if( !query.first() ) 00827 { 00828 m_lastErrorMsg = QString( "Failed to obtain the list of children for element \"%1\"" ) 00829 .arg( element ); 00830 return QStringList(); 00831 } 00832 00833 m_lastErrorMsg = ""; 00834 00835 QStringList children = query.record().value( "children" ).toString().split( SEPARATOR ); 00836 cleanList( children ); 00837 children.sort(); 00838 return children; 00839 } 00840 00841 /*--------------------------------------------------------------------------------------*/ 00842 00843 QStringList GCDataBaseInterface::attributes( const QString& element ) const 00844 { 00845 QSqlQuery query = selectElement( element ); 00846 00847 /* There should be only one record corresponding to this element. */ 00848 if( !query.first() ) 00849 { 00850 m_lastErrorMsg = QString( "Failed to obtain the list of attributes for element \"%1\"" ) 00851 .arg( element ); 00852 return QStringList(); 00853 } 00854 00855 m_lastErrorMsg = ""; 00856 00857 QStringList attributes = query.record().value( "attributes" ).toString().split( SEPARATOR ); 00858 cleanList( attributes ); 00859 return attributes; 00860 } 00861 00862 /*--------------------------------------------------------------------------------------*/ 00863 00864 QStringList GCDataBaseInterface::attributeValues( const QString& element, const QString& attribute ) const 00865 { 00866 QSqlQuery query = selectAttribute( attribute, element ); 00867 00868 /* There should be only one record corresponding to this element. */ 00869 if( !query.first() ) 00870 { 00871 m_lastErrorMsg = QString( "Failed to obtain the list of attribute values for attribute \"%1\"" ) 00872 .arg( attribute ); 00873 return QStringList(); 00874 } 00875 00876 m_lastErrorMsg = ""; 00877 00878 QStringList attributeValues = query.record().value( "attributeValues" ).toString().split( SEPARATOR ); 00879 cleanList( attributeValues ); 00880 attributeValues.sort(); 00881 return attributeValues; 00882 } 00883 00884 /*--------------------------------------------------------------------------------------*/ 00885 00886 QStringList GCDataBaseInterface::knownRootElements() const 00887 { 00888 return knownRootElements( m_sessionDB ); 00889 } 00890 00891 /*--------------------------------------------------------------------------------------*/ 00892 00893 QStringList GCDataBaseInterface::knownRootElements( QSqlDatabase db ) const 00894 { 00895 QSqlQuery query( db ); 00896 00897 if( !query.exec( "SELECT * FROM rootelements" ) ) 00898 { 00899 m_lastErrorMsg = QString( "SELECT all root elements failed: [%1]" ) 00900 .arg( query.lastError().text() ); 00901 return QStringList(); 00902 } 00903 00904 m_lastErrorMsg = ""; 00905 00906 QStringList rootElements; 00907 00908 while( query.next() ) 00909 { 00910 rootElements.append( query.record().field( "root" ).value().toString() ); 00911 } 00912 00913 cleanList( rootElements ); 00914 return rootElements; 00915 } 00916 00917 /*--------------------------------------------------------------------------------------*/ 00918 00919 bool GCDataBaseInterface::containsKnownRootElement( const QString& dbName, const QString& root ) const 00920 { 00921 /* In case the db name passed in consists of a path/to/file string. */ 00922 QString dbConName = dbName.split( QRegExp( REGEXP_SLASHES ), QString::SkipEmptyParts ).last(); 00923 00924 /* No error messages are logged for this specific query since we aren't necessarily concerned with 00925 the session we're querying (it may not be the active session). */ 00926 if( QSqlDatabase::contains( dbConName ) ) 00927 { 00928 QSqlDatabase db = QSqlDatabase::database( dbConName ); 00929 00930 if( db.isValid() && db.open() ) 00931 { 00932 if( knownRootElements( db ).contains( root ) ) 00933 { 00934 return true; 00935 } 00936 else 00937 { 00938 return false; 00939 } 00940 } 00941 } 00942 00943 return false; 00944 } 00945 00946 /*--------------------------------------------------------------------------------------*/ 00947 00948 QStringList GCDataBaseInterface::connectionList() const 00949 { 00950 return m_dbMap.keys(); 00951 } 00952 00953 /*--------------------------------------------------------------------------------------*/ 00954 00955 const QString& GCDataBaseInterface::lastError() const 00956 { 00957 return m_lastErrorMsg; 00958 } 00959 00960 /*--------------------------------------------------------------------------------------*/ 00961 00962 bool GCDataBaseInterface::addDatabase( const QString& dbName ) 00963 { 00964 if( !dbName.isEmpty() ) 00965 { 00966 /* In case the db name passed in consists of a path/to/file string. */ 00967 QString dbConName = dbName.split( QRegExp( REGEXP_SLASHES ), QString::SkipEmptyParts ).last(); 00968 00969 if( !m_dbMap.contains( dbConName ) ) 00970 { 00971 QSqlDatabase db = QSqlDatabase::addDatabase( "QSQLITE", dbConName ); 00972 00973 if( db.isValid() ) 00974 { 00975 db.setDatabaseName( dbName ); 00976 m_dbMap.insert( dbConName, dbName ); 00977 saveDatabaseFile(); 00978 00979 m_lastErrorMsg = ""; 00980 return true; 00981 } 00982 00983 m_lastErrorMsg = QString( "Failed to add database \"%1\": [%2]." ).arg( dbConName ).arg( db.lastError().text() ); 00984 return false; 00985 } 00986 else 00987 { 00988 m_lastErrorMsg = QString( "Connection \"%1\" already exists." ).arg( dbConName ); 00989 return false; 00990 } 00991 } 00992 00993 m_lastErrorMsg = QString( "Database name is empty." ); 00994 return false; 00995 } 00996 00997 /*--------------------------------------------------------------------------------------*/ 00998 00999 bool GCDataBaseInterface::removeDatabase( const QString& dbName ) 01000 { 01001 if( !dbName.isEmpty() ) 01002 { 01003 /* In case the db name passed in consists of a path/to/file string. */ 01004 QString dbConName = dbName.split( QRegExp( REGEXP_SLASHES ), QString::SkipEmptyParts ).last(); 01005 01006 if( m_sessionDB.connectionName() == dbConName ) 01007 { 01008 m_sessionDB.close(); 01009 m_hasActiveSession = false; 01010 } 01011 01012 QFile file( m_dbMap.value( dbConName ) ); 01013 01014 if( !file.remove() ) 01015 { 01016 m_lastErrorMsg = QString( "Failed to remove database file: [%1]" ) 01017 .arg( dbName ); 01018 return false; 01019 } 01020 01021 /* If the DB connection being removed was also the active one, "removeDatabase" will output 01022 a warning (to the Qt IDE's "Application Output" window). This is purely because we have a 01023 DB member variable and isn't cause for concern as there seems to be no way around it with 01024 the current QtSQL modules. */ 01025 QSqlDatabase::removeDatabase( dbConName ); 01026 m_dbMap.remove( dbConName ); 01027 saveDatabaseFile(); 01028 01029 m_lastErrorMsg = ""; 01030 return true; 01031 } 01032 01033 m_lastErrorMsg = QString( "Database name is empty." ); 01034 return false; 01035 } 01036 01037 /*--------------------------------------------------------------------------------------*/ 01038 01039 bool GCDataBaseInterface::setActiveDatabase( const QString& dbName ) 01040 { 01041 /* In case the db name passed in consists of a path/to/file string. */ 01042 QString dbConName = dbName.split( QRegExp( REGEXP_SLASHES ), QString::SkipEmptyParts ).last(); 01043 01044 if( QSqlDatabase::contains( dbConName ) ) 01045 { 01046 if( openConnection( dbConName ) ) 01047 { 01048 m_hasActiveSession = true; 01049 return true; 01050 } 01051 } 01052 else 01053 { 01054 /* If we set a DB for the session that doesn't exist (new, unknown), 01055 then we'll automatically try to add it and set it as active. */ 01056 if( addDatabase( dbName ) ) 01057 { 01058 if( openConnection( dbConName ) ) 01059 { 01060 m_hasActiveSession = true; 01061 return true; 01062 } 01063 } 01064 01065 m_hasActiveSession = false; 01066 return false; 01067 } 01068 01069 m_hasActiveSession = false; 01070 return false; 01071 } 01072 01073 /*--------------------------------------------------------------------------------------*/ 01074 01075 QStringList GCDataBaseInterface::knownAttributeKeys() const 01076 { 01077 QSqlQuery query = selectAllAttributes(); 01078 01079 m_lastErrorMsg = ""; 01080 01081 QStringList attributeNames; 01082 01083 while( query.next() ) 01084 { 01085 /* Concatenate the attribute name and associated element into a single string 01086 so that it is easier to determine whether a record already exists for that 01087 particular combination (this is used in GCBatchProcessorHelper). */ 01088 attributeNames.append( query.record().field( "attribute" ).value().toString() + 01089 "!" + 01090 query.record().field( "associatedElement" ).value().toString() ); 01091 } 01092 01093 return attributeNames; 01094 } 01095 01096 /*--------------------------------------------------------------------------------------*/ 01097 01098 QSqlQuery GCDataBaseInterface::selectElement( const QString& element ) const 01099 { 01100 /* See if we already have this element in the DB. */ 01101 QSqlQuery query( m_sessionDB ); 01102 01103 if( !query.prepare( "SELECT * FROM xmlelements WHERE element = ?" ) ) 01104 { 01105 m_lastErrorMsg = QString( "Prepare SELECT failed for element \"%1\": [%2]" ) 01106 .arg( element ) 01107 .arg( query.lastError().text() ); 01108 } 01109 01110 query.addBindValue( element ); 01111 01112 if( !query.exec() ) 01113 { 01114 m_lastErrorMsg = QString( "SELECT element failed for element \"%1\": [%2]" ) 01115 .arg( element ) 01116 .arg( query.lastError().text() ); 01117 } 01118 01119 return query; 01120 } 01121 01122 /*--------------------------------------------------------------------------------------*/ 01123 01124 QSqlQuery GCDataBaseInterface::selectAllElements() const 01125 { 01126 QSqlQuery query( m_sessionDB ); 01127 01128 if( !query.exec( "SELECT * FROM xmlelements" ) ) 01129 { 01130 m_lastErrorMsg = QString( "SELECT all root elements failed: [%1]" ) 01131 .arg( query.lastError().text() ); 01132 } 01133 01134 return query; 01135 } 01136 01137 /*--------------------------------------------------------------------------------------*/ 01138 01139 QSqlQuery GCDataBaseInterface::selectAttribute( const QString& attribute, const QString& associatedElement ) const 01140 { 01141 QSqlQuery query( m_sessionDB ); 01142 01143 if( !query.prepare( "SELECT * FROM xmlattributes " 01144 "WHERE attribute = ? " 01145 "AND associatedElement = ?" ) ) 01146 { 01147 m_lastErrorMsg = QString( "Prepare SELECT attribute failed for attribute \"%1\" and element \"%2\": [%3]" ) 01148 .arg( attribute ) 01149 .arg( associatedElement ) 01150 .arg( query.lastError().text() ); 01151 } 01152 01153 query.addBindValue( attribute ); 01154 query.addBindValue( associatedElement ); 01155 01156 if( !query.exec() ) 01157 { 01158 m_lastErrorMsg = QString( "SELECT attribute failed for attribute \"%1\" and element \"%2\": [%3]" ) 01159 .arg( attribute ) 01160 .arg( associatedElement ) 01161 .arg( query.lastError().text() ); 01162 } 01163 01164 return query; 01165 } 01166 01167 /*--------------------------------------------------------------------------------------*/ 01168 01169 QSqlQuery GCDataBaseInterface::selectAllAttributes() const 01170 { 01171 QSqlQuery query( m_sessionDB ); 01172 01173 if( !query.exec( "SELECT * FROM xmlattributes" ) ) 01174 { 01175 m_lastErrorMsg = QString( "SELECT all attribute values failed: [%1]" ) 01176 .arg( query.lastError().text() ); 01177 } 01178 01179 return query; 01180 } 01181 01182 /*--------------------------------------------------------------------------------------*/ 01183 01184 bool GCDataBaseInterface::removeDuplicatesFromFields() const 01185 { 01186 /* Remove duplicates and update the element records. */ 01187 QStringList elementNames = knownElements(); 01188 QString element( "" ); 01189 01190 for( int i = 0; i < elementNames.size(); ++i ) 01191 { 01192 element = elementNames.at( i ); 01193 QSqlQuery query = selectElement( element ); 01194 01195 /* Not checking for query validity since the table may still be empty when 01196 this funciton gets called (i.e. there is a potentially valid reason for cases 01197 where no valid records exist). */ 01198 if( query.first() ) 01199 { 01200 QStringList allChildren ( query.record().field( "children" ).value().toString().split( SEPARATOR ) ); 01201 QStringList allAttributes( query.record().field( "attributes" ).value().toString().split( SEPARATOR ) ); 01202 01203 if( !query.prepare( UPDATE_CHILDREN ) ) 01204 { 01205 m_lastErrorMsg = QString( "Prepare UPDATE element children failed for element \"%1\": [%2]" ) 01206 .arg( element ) 01207 .arg( query.lastError().text() ); 01208 return false; 01209 } 01210 01211 query.addBindValue( cleanAndJoinListElements( allChildren ) ); 01212 query.addBindValue( element ); 01213 01214 if( !query.exec() ) 01215 { 01216 m_lastErrorMsg = QString( "UPDATE children failed for element \"%1\": [%2]" ) 01217 .arg( element ) 01218 .arg( query.lastError().text() ); 01219 return false; 01220 } 01221 01222 if( !query.prepare( UPDATE_ATTRIBUTES ) ) 01223 { 01224 m_lastErrorMsg = QString( "Prepare UPDATE element attributes failed for element \"%1\": [%2]" ) 01225 .arg( element ) 01226 .arg( query.lastError().text() ); 01227 return false; 01228 } 01229 01230 query.addBindValue( cleanAndJoinListElements( allAttributes ) ); 01231 query.addBindValue( element ); 01232 01233 if( !query.exec() ) 01234 { 01235 m_lastErrorMsg = QString( "UPDATE attributes failed for element \"%1\": [%2]" ) 01236 .arg( element ) 01237 .arg( query.lastError().text() ); 01238 return false; 01239 } 01240 01241 qApp->processEvents( QEventLoop::ExcludeUserInputEvents ); 01242 } 01243 } 01244 01245 /* Remove duplicates and update the attribute values records. */ 01246 QStringList attributeKeys = knownAttributeKeys(); 01247 01248 /* Not checking for query validity since the table may still be empty when 01249 this funciton gets called (i.e. there is a potentially valid reason for cases 01250 where no valid records exist). */ 01251 for( int i = 0; i < attributeKeys.size(); ++i ) 01252 { 01253 QString attribute = attributeKeys.at( i ).split( "!" ).at( 0 ); 01254 QString associatedElement = attributeKeys.at( i ).split( "!" ).at( 1 ); 01255 QSqlQuery query = selectAttribute( attribute, associatedElement ); 01256 01257 /* Does a record for this attribute exist? */ 01258 if( query.first() ) 01259 { 01260 QStringList allValues( query.record().field( "attributeValues" ).value().toString().split( SEPARATOR ) ); 01261 01262 if( !query.prepare( UPDATE_ATTRIBUTEVALUES ) ) 01263 { 01264 m_lastErrorMsg = QString( "Prepare UPDATE attribute values failed for element \"%1\" and attribute \"%2\": [%3]" ) 01265 .arg( associatedElement ) 01266 .arg( attribute ) 01267 .arg( query.lastError().text() ); 01268 return false; 01269 } 01270 01271 query.addBindValue( cleanAndJoinListElements( allValues ) ); 01272 query.addBindValue( attribute ); 01273 query.addBindValue( associatedElement ); 01274 01275 if( !query.exec() ) 01276 { 01277 m_lastErrorMsg = QString( "UPDATE attribute values failed for element \"%1\" and attribute \"%2\": [%3]" ) 01278 .arg( associatedElement ) 01279 .arg( attribute ) 01280 .arg( query.lastError().text() ); 01281 return false; 01282 } 01283 01284 qApp->processEvents( QEventLoop::ExcludeUserInputEvents ); 01285 } 01286 } 01287 01288 m_lastErrorMsg = ""; 01289 return true; 01290 } 01291 01292 /*--------------------------------------------------------------------------------------*/ 01293 01294 bool GCDataBaseInterface::openConnection( const QString& dbConName ) 01295 { 01296 /* If we have a previous connection open, close it. */ 01297 if( m_sessionDB.isValid() && m_sessionDB.isOpen() ) 01298 { 01299 m_sessionDB.close(); 01300 } 01301 01302 /* Open the new connection. */ 01303 m_sessionDB = QSqlDatabase::database( dbConName ); 01304 01305 if( m_sessionDB.isValid() ) 01306 { 01307 if( !m_sessionDB.open() ) 01308 { 01309 m_lastErrorMsg = QString( "Failed to open database \"%1\": [%2]." ) 01310 .arg( m_dbMap.value( dbConName ) ) 01311 .arg( m_sessionDB.lastError().text() ); 01312 return false; 01313 } 01314 01315 /* If the DB has not yet been initialised. */ 01316 QStringList tables = m_sessionDB.tables(); 01317 01318 if( !tables.contains( "xmlelements", Qt::CaseInsensitive ) ) 01319 { 01320 return createTables(); 01321 } 01322 } 01323 else 01324 { 01325 m_lastErrorMsg = QString( "Failed to open a valid session connection \"%1\": [%2]" ) 01326 .arg( dbConName ) 01327 .arg( m_sessionDB.lastError().text() ); 01328 return false; 01329 } 01330 01331 m_lastErrorMsg = ""; 01332 return true; 01333 } 01334 01335 /*--------------------------------------------------------------------------------------*/ 01336 01337 bool GCDataBaseInterface::createTables() const 01338 { 01339 /* DB connection will be open from openConnection() above so no need to do any checks here. */ 01340 QSqlQuery query( m_sessionDB ); 01341 01342 if( !query.exec( "CREATE TABLE xmlelements( element QString primary key, children QString, attributes QString )" ) ) 01343 { 01344 m_lastErrorMsg = QString( "Failed to create elements table for \"%1\": [%2]." ) 01345 .arg( m_sessionDB.connectionName() ) 01346 .arg( query.lastError().text() ); 01347 return false; 01348 } 01349 01350 if( !query.exec( "CREATE TABLE xmlattributes( attribute QString, associatedElement QString, attributeValues QString, " 01351 "UNIQUE(attribute, associatedElement), " 01352 "FOREIGN KEY(associatedElement) REFERENCES xmlelements(element) )" ) ) 01353 { 01354 m_lastErrorMsg = QString( "Failed to create attribute values table for \"%1\": [%2]" ) 01355 .arg( m_sessionDB.connectionName() ) 01356 .arg( query.lastError().text() ); 01357 return false; 01358 } 01359 else 01360 { 01361 if( !query.exec( "CREATE UNIQUE INDEX attributeKey ON xmlattributes( attribute, associatedElement)" ) ) 01362 { 01363 m_lastErrorMsg = QString( "Failed to create unique index for \"%1\": [%2]" ) 01364 .arg( m_sessionDB.connectionName() ) 01365 .arg( query.lastError().text() ); 01366 return false; 01367 } 01368 } 01369 01370 if( !query.exec( "CREATE TABLE rootelements( root QString primary key )" ) ) 01371 { 01372 m_lastErrorMsg = QString( "Failed to create root elements table for \"%1\": [%2]" ) 01373 .arg( m_sessionDB.connectionName() ) 01374 .arg( query.lastError().text() ); 01375 return false; 01376 } 01377 01378 m_lastErrorMsg = ""; 01379 return true; 01380 } 01381 01382 /*--------------------------------------------------------------------------------------*/ 01383 01384 void GCDataBaseInterface::saveDatabaseFile() const 01385 { 01386 QFile flatFile( DB_FILE ); 01387 01388 if( flatFile.open( QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate ) ) 01389 { 01390 QTextStream outStream( &flatFile ); 01391 01392 foreach( QString str, m_dbMap.values() ) 01393 { 01394 outStream << str << "\n"; 01395 } 01396 01397 flatFile.close(); 01398 } 01399 } 01400 01401 /*--------------------------------------------------------------------------------------*/