tools/assistant/lib/qhelpgenerator.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the Qt Assistant of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "qhelpgenerator_p.h"
       
    43 #include "qhelpdatainterface_p.h"
       
    44 
       
    45 #include <math.h>
       
    46 #include <QtCore/QFile>
       
    47 #include <QtCore/QFileInfo>
       
    48 #include <QtCore/QDir>
       
    49 #include <QtCore/QDebug>
       
    50 #include <QtCore/QVariant>
       
    51 #include <QtCore/QDateTime>
       
    52 #include <QtCore/QTextCodec>
       
    53 #include <QtSql/QSqlQuery>
       
    54 
       
    55 QT_BEGIN_NAMESPACE
       
    56 
       
    57 class QHelpGeneratorPrivate
       
    58 {
       
    59 public:
       
    60     QHelpGeneratorPrivate();
       
    61     ~QHelpGeneratorPrivate();
       
    62 
       
    63     QString error;
       
    64     QSqlQuery *query;
       
    65 
       
    66     int namespaceId;
       
    67     int virtualFolderId;
       
    68 
       
    69     QMap<QString, int> fileMap;
       
    70     QMap<int, QSet<int> > fileFilterMap;
       
    71 
       
    72     double progress;
       
    73     double oldProgress;
       
    74     double contentStep;
       
    75     double fileStep;
       
    76     double indexStep;
       
    77 };
       
    78 
       
    79 QHelpGeneratorPrivate::QHelpGeneratorPrivate()
       
    80 {
       
    81     query = 0;
       
    82     namespaceId = -1;
       
    83     virtualFolderId = -1;
       
    84 }
       
    85 
       
    86 QHelpGeneratorPrivate::~QHelpGeneratorPrivate()
       
    87 {
       
    88 }
       
    89 
       
    90 
       
    91 
       
    92 /*!
       
    93     \internal
       
    94     \class QHelpGenerator
       
    95     \since 4.4
       
    96     \brief The QHelpGenerator class generates a new
       
    97     Qt compressed help file (.qch).
       
    98 
       
    99     The help generator takes a help data structure as
       
   100     input for generating a new Qt compressed help files. Since
       
   101     the generation may takes some time, the generator emits
       
   102     various signals to inform about its current state.
       
   103 */
       
   104 
       
   105 /*!
       
   106     \fn void QHelpGenerator::statusChanged(const QString &msg)
       
   107 
       
   108     This signal is emitted when the generation status changes.
       
   109     The status is basically a specific task like inserting
       
   110     files or building up the keyword index. The parameter
       
   111     \a msg contains the detailed status description.
       
   112 */
       
   113 
       
   114 /*!
       
   115     \fn void QHelpGenerator::progressChanged(double progress)
       
   116 
       
   117     This signal is emitted when the progress changes. The
       
   118     \a progress ranges from 0 to 100.
       
   119 */
       
   120 
       
   121 /*!
       
   122     \fn void QHelpGenerator::warning(const QString &msg)
       
   123 
       
   124     This signal is emitted when a non critical error occurs,
       
   125     e.g. when a referenced file cannot be found. \a msg
       
   126     contains the exact warning message.
       
   127 */
       
   128 
       
   129 /*!
       
   130     Constructs a new help generator with the give \a parent.
       
   131 */
       
   132 QHelpGenerator::QHelpGenerator(QObject *parent)
       
   133     : QObject(parent)
       
   134 {
       
   135     d = new QHelpGeneratorPrivate;
       
   136 }
       
   137 
       
   138 /*!
       
   139     Destructs the help generator.
       
   140 */
       
   141 QHelpGenerator::~QHelpGenerator()
       
   142 {
       
   143     delete d;
       
   144 }
       
   145 
       
   146 /*!
       
   147     Takes the \a helpData and generates a new documentation
       
   148     set from it. The Qt compressed help file is written to \a
       
   149     outputFileName. Returns true on success, otherwise false.
       
   150 */
       
   151 bool QHelpGenerator::generate(QHelpDataInterface *helpData,
       
   152                               const QString &outputFileName)
       
   153 {
       
   154     emit progressChanged(0);
       
   155     d->error.clear();
       
   156     if (!helpData || helpData->namespaceName().isEmpty()) {
       
   157         d->error = tr("Invalid help data!");
       
   158         return false;
       
   159     }
       
   160 
       
   161     QString outFileName = outputFileName;
       
   162     if (outFileName.isEmpty()) {
       
   163         d->error = tr("No output file name specified!");
       
   164         return false;
       
   165     }
       
   166 
       
   167     QFileInfo fi(outFileName);
       
   168     if (fi.exists()) {
       
   169         if (!fi.dir().remove(fi.fileName())) {
       
   170             d->error = tr("The file %1 cannot be overwritten!").arg(outFileName);
       
   171             return false;
       
   172         }
       
   173     }
       
   174 
       
   175     setupProgress(helpData);
       
   176 
       
   177     emit statusChanged(tr("Building up file structure..."));
       
   178     bool openingOk = true;
       
   179     {
       
   180         QSqlDatabase db = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), QLatin1String("builder"));
       
   181         db.setDatabaseName(outFileName);
       
   182         openingOk = db.open();
       
   183         if (openingOk)
       
   184             d->query = new QSqlQuery(db);
       
   185     }
       
   186 
       
   187     if (!openingOk) {
       
   188         d->error = tr("Cannot open data base file %1!").arg(outFileName);
       
   189         cleanupDB();
       
   190         return false;
       
   191     }
       
   192 
       
   193     addProgress(1.0);
       
   194     createTables();
       
   195     insertFileNotFoundFile();
       
   196     insertMetaData(helpData->metaData());
       
   197 
       
   198     if (!registerVirtualFolder(helpData->virtualFolder(), helpData->namespaceName())) {
       
   199         d->error = tr("Cannot register namespace %1!").arg(helpData->namespaceName());
       
   200         cleanupDB();
       
   201         return false;
       
   202     }
       
   203     addProgress(1.0);
       
   204 
       
   205     emit statusChanged(tr("Insert custom filters..."));
       
   206     foreach (QHelpDataCustomFilter f, helpData->customFilters()) {
       
   207         if (!registerCustomFilter(f.name, f.filterAttributes, true)) {
       
   208             cleanupDB();
       
   209             return false;
       
   210         }
       
   211     }
       
   212     addProgress(1.0);
       
   213 
       
   214     int i = 1;
       
   215     QList<QHelpDataFilterSection>::const_iterator it = helpData->filterSections().constBegin();
       
   216     while (it != helpData->filterSections().constEnd()) {
       
   217         emit statusChanged(tr("Insert help data for filter section (%1 of %2)...")
       
   218             .arg(i++).arg(helpData->filterSections().count()));
       
   219         insertFilterAttributes((*it).filterAttributes());
       
   220         QByteArray ba;
       
   221         QDataStream s(&ba, QIODevice::WriteOnly);
       
   222         foreach (QHelpDataContentItem *itm, (*it).contents())
       
   223             writeTree(s, itm, 0);
       
   224         if (!insertFiles((*it).files(), helpData->rootPath(), (*it).filterAttributes())
       
   225             || !insertContents(ba, (*it).filterAttributes())
       
   226             || !insertKeywords((*it).indices(), (*it).filterAttributes())) {
       
   227             cleanupDB();
       
   228             return false;
       
   229         }
       
   230         ++it;
       
   231     }
       
   232 
       
   233     cleanupDB();
       
   234     emit progressChanged(100);
       
   235     emit statusChanged(tr("Documentation successfully generated."));
       
   236     return true;
       
   237 }
       
   238 
       
   239 void QHelpGenerator::setupProgress(QHelpDataInterface *helpData)
       
   240 {
       
   241     d->progress = 0;
       
   242     d->oldProgress = 0;
       
   243 
       
   244     int numberOfFiles = 0;
       
   245     int numberOfIndices = 0;
       
   246     QList<QHelpDataFilterSection>::const_iterator it = helpData->filterSections().constBegin();
       
   247     while (it != helpData->filterSections().constEnd()) {
       
   248         numberOfFiles += (*it).files().count();
       
   249         numberOfIndices += (*it).indices().count();
       
   250         ++it;
       
   251     }
       
   252     // init      2%
       
   253     // filters   1%
       
   254     // contents 10%
       
   255     // files    60%
       
   256     // indices  27%
       
   257     d->contentStep = 10.0/(double)helpData->customFilters().count();
       
   258     d->fileStep = 60.0/(double)numberOfFiles;
       
   259     d->indexStep = 27.0/(double)numberOfIndices;
       
   260 }
       
   261 
       
   262 void QHelpGenerator::addProgress(double step)
       
   263 {
       
   264     d->progress += step;
       
   265     if ((d->progress-d->oldProgress) >= 1.0 && d->progress <= 100.0) {
       
   266         d->oldProgress = d->progress;
       
   267         emit progressChanged(ceil(d->progress));
       
   268     }
       
   269 }
       
   270 
       
   271 void QHelpGenerator::cleanupDB()
       
   272 {
       
   273     if (d->query) {
       
   274         d->query->clear();
       
   275         delete d->query;
       
   276         d->query = 0;
       
   277     }
       
   278     QSqlDatabase::removeDatabase(QLatin1String("builder"));
       
   279 }
       
   280 
       
   281 void QHelpGenerator::writeTree(QDataStream &s, QHelpDataContentItem *item, int depth)
       
   282 {
       
   283     QString fReference = QDir::cleanPath(item->reference());
       
   284     if (fReference.startsWith(QLatin1String("./")))
       
   285         fReference = fReference.mid(2);
       
   286 
       
   287     s << depth;
       
   288     s << fReference;
       
   289     s << item->title();
       
   290     foreach (QHelpDataContentItem *i, item->children())
       
   291         writeTree(s, i, depth+1);
       
   292 }
       
   293 
       
   294 /*!
       
   295     Returns the last error message.
       
   296 */
       
   297 QString QHelpGenerator::error() const
       
   298 {
       
   299     return d->error;
       
   300 }
       
   301 
       
   302 bool QHelpGenerator::createTables()
       
   303 {
       
   304     if (!d->query)
       
   305         return false;
       
   306 
       
   307     d->query->exec(QLatin1String("SELECT COUNT(*) FROM sqlite_master WHERE TYPE=\'table\'"
       
   308         "AND Name=\'NamespaceTable\'"));
       
   309     d->query->next();
       
   310     if (d->query->value(0).toInt() > 0) {
       
   311         d->error = tr("Some tables already exist!");
       
   312         return false;
       
   313     }
       
   314 
       
   315     QStringList tables;
       
   316     tables << QLatin1String("CREATE TABLE NamespaceTable ("
       
   317             "Id INTEGER PRIMARY KEY,"
       
   318             "Name TEXT )")
       
   319         << QLatin1String("CREATE TABLE FilterAttributeTable ("
       
   320             "Id INTEGER PRIMARY KEY, "
       
   321             "Name TEXT )")
       
   322         << QLatin1String("CREATE TABLE FilterNameTable ("
       
   323             "Id INTEGER PRIMARY KEY, "
       
   324             "Name TEXT )")
       
   325         << QLatin1String("CREATE TABLE FilterTable ("
       
   326             "NameId INTEGER, "
       
   327             "FilterAttributeId INTEGER )")
       
   328         << QLatin1String("CREATE TABLE IndexTable ("
       
   329             "Id INTEGER PRIMARY KEY, "
       
   330             "Name TEXT, "
       
   331             "Identifier TEXT, "
       
   332             "NamespaceId INTEGER, "
       
   333             "FileId INTEGER, "
       
   334             "Anchor TEXT )")
       
   335         << QLatin1String("CREATE TABLE IndexItemTable ("
       
   336             "Id INTEGER, "
       
   337             "IndexId INTEGER )")
       
   338         << QLatin1String("CREATE TABLE IndexFilterTable ("
       
   339             "FilterAttributeId INTEGER, "
       
   340             "IndexId INTEGER )")
       
   341         << QLatin1String("CREATE TABLE ContentsTable ("
       
   342             "Id INTEGER PRIMARY KEY, "
       
   343             "NamespaceId INTEGER, "
       
   344             "Data BLOB )")
       
   345         << QLatin1String("CREATE TABLE ContentsFilterTable ("
       
   346             "FilterAttributeId INTEGER, "
       
   347             "ContentsId INTEGER )")
       
   348         << QLatin1String("CREATE TABLE FileAttributeSetTable ("
       
   349             "Id INTEGER, "
       
   350             "FilterAttributeId INTEGER )")
       
   351         << QLatin1String("CREATE TABLE FileDataTable ("
       
   352             "Id INTEGER PRIMARY KEY, "
       
   353             "Data BLOB )")
       
   354         << QLatin1String("CREATE TABLE FileFilterTable ("
       
   355             "FilterAttributeId INTEGER, "
       
   356             "FileId INTEGER )")
       
   357         << QLatin1String("CREATE TABLE FileNameTable ("
       
   358             "FolderId INTEGER, "
       
   359             "Name TEXT, "
       
   360             "FileId INTEGER, "
       
   361             "Title TEXT )")
       
   362         << QLatin1String("CREATE TABLE FolderTable("
       
   363             "Id INTEGER PRIMARY KEY, "
       
   364             "Name Text, "
       
   365             "NamespaceID INTEGER )")
       
   366         << QLatin1String("CREATE TABLE MetaDataTable("
       
   367             "Name Text, "
       
   368             "Value BLOB )");
       
   369 
       
   370     foreach (QString q, tables) {
       
   371         if (!d->query->exec(q)) {
       
   372             d->error = tr("Cannot create tables!");
       
   373             return false;
       
   374         }
       
   375     }
       
   376 
       
   377     d->query->exec(QLatin1String("INSERT INTO MetaDataTable VALUES('qchVersion', '1.0')"));
       
   378 
       
   379     d->query->prepare(QLatin1String("INSERT INTO MetaDataTable VALUES('CreationDate', ?)"));
       
   380     d->query->bindValue(0, QDateTime::currentDateTime().toString(Qt::ISODate));
       
   381     d->query->exec();
       
   382 
       
   383     return true;
       
   384 }
       
   385 
       
   386 bool QHelpGenerator::insertFileNotFoundFile()
       
   387 {
       
   388     if (!d->query)
       
   389         return false;
       
   390 
       
   391     d->query->exec(QLatin1String("SELECT id FROM FileNameTable WHERE Name=\'\'"));
       
   392     if (d->query->next() && d->query->isValid())
       
   393         return true;
       
   394 
       
   395     d->query->prepare(QLatin1String("INSERT INTO FileDataTable VALUES (Null, ?)"));
       
   396     d->query->bindValue(0, QByteArray());
       
   397     if (!d->query->exec())
       
   398         return false;
       
   399 
       
   400     int fileId = d->query->lastInsertId().toInt();
       
   401     d->query->prepare(QLatin1String("INSERT INTO FileNameTable (FolderId, Name, FileId, Title) "
       
   402         " VALUES (0, '', ?, '')"));
       
   403     d->query->bindValue(0, fileId);
       
   404     if (fileId > -1 && d->query->exec()) {
       
   405         d->fileMap.insert(QString(), fileId);
       
   406         return true;
       
   407     }
       
   408     return false;
       
   409 }
       
   410 
       
   411 bool QHelpGenerator::registerVirtualFolder(const QString &folderName, const QString &ns)
       
   412 {
       
   413     if (!d->query || folderName.isEmpty() || ns.isEmpty())
       
   414         return false;
       
   415 
       
   416     d->query->prepare(QLatin1String("SELECT Id FROM FolderTable WHERE Name=?"));
       
   417     d->query->bindValue(0, folderName);
       
   418     d->query->exec();
       
   419     d->query->next();
       
   420     if (d->query->isValid() && d->query->value(0).toInt() > 0)
       
   421         return true;
       
   422 
       
   423     d->namespaceId = -1;
       
   424     d->query->prepare(QLatin1String("SELECT Id FROM NamespaceTable WHERE Name=?"));
       
   425     d->query->bindValue(0, ns);
       
   426     d->query->exec();
       
   427     while (d->query->next()) {
       
   428         d->namespaceId = d->query->value(0).toInt();
       
   429         break;
       
   430     }
       
   431 
       
   432     if (d->namespaceId < 0) {
       
   433         d->query->prepare(QLatin1String("INSERT INTO NamespaceTable VALUES(NULL, ?)"));
       
   434         d->query->bindValue(0, ns);
       
   435         if (d->query->exec())
       
   436             d->namespaceId = d->query->lastInsertId().toInt();
       
   437     }
       
   438 
       
   439     if (d->namespaceId > 0) {
       
   440         d->query->prepare(QLatin1String("SELECT Id FROM FolderTable WHERE Name=?"));
       
   441         d->query->bindValue(0, folderName);
       
   442         d->query->exec();
       
   443         while (d->query->next())
       
   444             d->virtualFolderId = d->query->value(0).toInt();
       
   445 
       
   446         if (d->virtualFolderId > 0)
       
   447             return true;
       
   448 
       
   449         d->query->prepare(QLatin1String("INSERT INTO FolderTable (NamespaceId, Name) "
       
   450             "VALUES (?, ?)"));
       
   451         d->query->bindValue(0, d->namespaceId);
       
   452         d->query->bindValue(1, folderName);
       
   453         if (d->query->exec()) {
       
   454             d->virtualFolderId = d->query->lastInsertId().toInt();
       
   455             return d->virtualFolderId > 0;
       
   456         }
       
   457     }
       
   458     d->error = tr("Cannot register virtual folder!");
       
   459     return false;
       
   460 }
       
   461 
       
   462 bool QHelpGenerator::insertFiles(const QStringList &files, const QString &rootPath,
       
   463                                  const QStringList &filterAttributes)
       
   464 {
       
   465     if (!d->query)
       
   466         return false;
       
   467 
       
   468     emit statusChanged(tr("Insert files..."));
       
   469     QList<int> filterAtts;
       
   470     foreach (const QString &filterAtt, filterAttributes) {
       
   471         d->query->prepare(QLatin1String("SELECT Id FROM FilterAttributeTable "
       
   472             "WHERE Name=?"));
       
   473         d->query->bindValue(0, filterAtt);
       
   474         d->query->exec();
       
   475         if (d->query->next())
       
   476             filterAtts.append(d->query->value(0).toInt());
       
   477     }
       
   478 
       
   479     int filterSetId = -1;
       
   480     d->query->exec(QLatin1String("SELECT MAX(Id) FROM FileAttributeSetTable"));
       
   481     if (d->query->next())
       
   482         filterSetId = d->query->value(0).toInt();
       
   483     if (filterSetId < 0)
       
   484         return false;
       
   485     ++filterSetId;
       
   486     foreach (const int &attId, filterAtts) {
       
   487         d->query->prepare(QLatin1String("INSERT INTO FileAttributeSetTable "
       
   488             "VALUES(?, ?)"));
       
   489         d->query->bindValue(0, filterSetId);
       
   490         d->query->bindValue(1, attId);
       
   491         d->query->exec();
       
   492     }
       
   493 
       
   494     int tableFileId = 1;
       
   495     d->query->exec(QLatin1String("SELECT MAX(Id) FROM FileDataTable"));
       
   496     if (d->query->next())
       
   497         tableFileId = d->query->value(0).toInt() + 1;
       
   498 
       
   499     QString title;
       
   500     QString charSet;
       
   501     FileNameTableData fileNameData;
       
   502     QList<QByteArray> fileDataList;
       
   503     QMap<int, QSet<int> > tmpFileFilterMap;
       
   504     QList<FileNameTableData> fileNameDataList;
       
   505 
       
   506     int i = 0;
       
   507     foreach (const QString &file, files) {
       
   508         const QString fileName = QDir::cleanPath(file);
       
   509         if (fileName.startsWith(QLatin1String("../"))) {
       
   510             emit warning(tr("The referenced file %1 must be inside or within a "
       
   511                 "subdirectory of (%2). Skipping it.").arg(fileName).arg(rootPath));
       
   512             continue;
       
   513         }
       
   514 
       
   515         QFile fi(rootPath + QDir::separator() + fileName);
       
   516         if (!fi.exists()) {
       
   517             emit warning(tr("The file %1 does not exist! Skipping it.")
       
   518                 .arg(QDir::cleanPath(rootPath + QDir::separator() + fileName)));
       
   519             continue;
       
   520         }
       
   521 
       
   522         if (!fi.open(QIODevice::ReadOnly)) {
       
   523             emit warning(tr("Cannot open file %1! Skipping it.")
       
   524                 .arg(QDir::cleanPath(rootPath + QDir::separator() + fileName)));
       
   525             continue;
       
   526         }
       
   527 
       
   528         QByteArray data = fi.readAll();
       
   529         if (fileName.endsWith(QLatin1String(".html"))
       
   530             || fileName.endsWith(QLatin1String(".htm"))) {
       
   531                 charSet = QHelpGlobal::charsetFromData(data);
       
   532                 QTextStream stream(&data);
       
   533                 stream.setCodec(QTextCodec::codecForName(charSet.toLatin1().constData()));
       
   534                 title = QHelpGlobal::documentTitle(stream.readAll());
       
   535         } else {
       
   536             title = fileName.mid(fileName.lastIndexOf(QLatin1Char('/')) + 1);
       
   537         }
       
   538 
       
   539         int fileId = -1;
       
   540         if (!d->fileMap.contains(fileName)) {
       
   541             fileDataList.append(qCompress(data));
       
   542 
       
   543             fileNameData.name = fileName;
       
   544             fileNameData.fileId = tableFileId;
       
   545             fileNameData.title = title;
       
   546             fileNameDataList.append(fileNameData);
       
   547 
       
   548             d->fileMap.insert(fileName, tableFileId);
       
   549             d->fileFilterMap.insert(tableFileId, filterAtts.toSet());
       
   550             tmpFileFilterMap.insert(tableFileId, filterAtts.toSet());
       
   551 
       
   552             ++tableFileId;
       
   553         } else {
       
   554             fileId = d->fileMap.value(fileName);
       
   555             foreach (const int &filter, filterAtts) {
       
   556                 if (!d->fileFilterMap.value(fileId).contains(filter)
       
   557                     && !tmpFileFilterMap.value(fileId).contains(filter)) {
       
   558                         d->fileFilterMap[fileId].insert(filter);
       
   559                         tmpFileFilterMap[fileId].insert(filter);
       
   560                 }
       
   561             }
       
   562         }
       
   563     }
       
   564 
       
   565     if (tmpFileFilterMap.count()) {
       
   566         d->query->exec(QLatin1String("BEGIN"));
       
   567         QMap<int, QSet<int> >::const_iterator it = tmpFileFilterMap.constBegin();
       
   568         while (it != tmpFileFilterMap.constEnd()) {
       
   569             QSet<int>::const_iterator si = it.value().constBegin();
       
   570             while (si != it.value().constEnd()) {
       
   571                 d->query->prepare(QLatin1String("INSERT INTO FileFilterTable "
       
   572                     "VALUES(?, ?)"));
       
   573                 d->query->bindValue(0, *si);
       
   574                 d->query->bindValue(1, it.key());
       
   575                 d->query->exec();
       
   576                 ++si;
       
   577             }
       
   578             ++it;
       
   579         }
       
   580 
       
   581         QList<QByteArray>::const_iterator fileIt = fileDataList.constBegin();
       
   582         while (fileIt != fileDataList.constEnd()) {
       
   583             d->query->prepare(QLatin1String("INSERT INTO FileDataTable VALUES "
       
   584                 "(Null, ?)"));
       
   585             d->query->bindValue(0, *fileIt);
       
   586             d->query->exec();
       
   587             ++fileIt;
       
   588             if (++i%20 == 0)
       
   589                 addProgress(d->fileStep*20.0);
       
   590         }
       
   591 
       
   592         QList<FileNameTableData>::const_iterator fileNameIt =
       
   593                                                   fileNameDataList.constBegin();
       
   594         while (fileNameIt != fileNameDataList.constEnd()) {
       
   595             d->query->prepare(QLatin1String("INSERT INTO FileNameTable "
       
   596                 "(FolderId, Name, FileId, Title) VALUES (?, ?, ?, ?)"));
       
   597             d->query->bindValue(0, 1);
       
   598             d->query->bindValue(1, (*fileNameIt).name);
       
   599             d->query->bindValue(2, (*fileNameIt).fileId);
       
   600             d->query->bindValue(3, (*fileNameIt).title);
       
   601             d->query->exec();
       
   602             ++fileNameIt;
       
   603         }
       
   604         d->query->exec(QLatin1String("COMMIT"));
       
   605     }
       
   606 
       
   607     d->query->exec(QLatin1String("SELECT MAX(Id) FROM FileDataTable"));
       
   608     if (d->query->next()
       
   609         && d->query->value(0).toInt() == tableFileId-1) {
       
   610         addProgress(d->fileStep*(i%20));
       
   611         return true;
       
   612     }
       
   613     return false;
       
   614 }
       
   615 
       
   616 bool QHelpGenerator::registerCustomFilter(const QString &filterName,
       
   617     const QStringList &filterAttribs, bool forceUpdate)
       
   618 {
       
   619     if (!d->query)
       
   620         return false;
       
   621 
       
   622     d->query->exec(QLatin1String("SELECT Id, Name FROM FilterAttributeTable"));
       
   623     QStringList idsToInsert = filterAttribs;
       
   624     QMap<QString, int> attributeMap;
       
   625     while (d->query->next()) {
       
   626         attributeMap.insert(d->query->value(1).toString(),
       
   627             d->query->value(0).toInt());
       
   628         if (idsToInsert.contains(d->query->value(1).toString()))
       
   629             idsToInsert.removeAll(d->query->value(1).toString());
       
   630     }
       
   631 
       
   632     foreach (QString id, idsToInsert) {
       
   633         d->query->prepare(QLatin1String("INSERT INTO FilterAttributeTable VALUES(NULL, ?)"));
       
   634         d->query->bindValue(0, id);
       
   635         d->query->exec();
       
   636         attributeMap.insert(id, d->query->lastInsertId().toInt());
       
   637     }
       
   638 
       
   639     int nameId = -1;
       
   640     d->query->prepare(QLatin1String("SELECT Id FROM FilterNameTable WHERE Name=?"));
       
   641     d->query->bindValue(0, filterName);
       
   642     d->query->exec();
       
   643     while (d->query->next()) {
       
   644         nameId = d->query->value(0).toInt();
       
   645         break;
       
   646     }
       
   647 
       
   648     if (nameId < 0) {
       
   649         d->query->prepare(QLatin1String("INSERT INTO FilterNameTable VALUES(NULL, ?)"));
       
   650         d->query->bindValue(0, filterName);
       
   651         if (d->query->exec())
       
   652             nameId = d->query->lastInsertId().toInt();
       
   653     } else if (!forceUpdate) {
       
   654         d->error = tr("The filter %1 is already registered!").arg(filterName);
       
   655         return false;
       
   656     }
       
   657 
       
   658     if (nameId < 0) {
       
   659         d->error = tr("Cannot register filter %1!").arg(filterName);
       
   660         return false;
       
   661     }
       
   662 
       
   663     d->query->prepare(QLatin1String("DELETE FROM FilterTable WHERE NameId=?"));
       
   664     d->query->bindValue(0, nameId);
       
   665     d->query->exec();
       
   666 
       
   667     foreach (QString att, filterAttribs) {
       
   668         d->query->prepare(QLatin1String("INSERT INTO FilterTable VALUES(?, ?)"));
       
   669         d->query->bindValue(0, nameId);
       
   670         d->query->bindValue(1, attributeMap[att]);
       
   671         if (!d->query->exec())
       
   672             return false;
       
   673     }
       
   674     return true;
       
   675 }
       
   676 
       
   677 bool QHelpGenerator::insertKeywords(const QList<QHelpDataIndexItem> keywords,
       
   678                                     const QStringList &filterAttributes)
       
   679 {
       
   680     if (!d->query)
       
   681         return false;
       
   682 
       
   683     emit statusChanged(tr("Insert indices..."));
       
   684     int indexId = 1;
       
   685     d->query->exec(QLatin1String("SELECT MAX(Id) FROM IndexTable"));
       
   686     if (d->query->next())
       
   687         indexId = d->query->value(0).toInt() + 1;
       
   688 
       
   689     QList<int> filterAtts;
       
   690     foreach (QString filterAtt, filterAttributes) {
       
   691         d->query->prepare(QLatin1String("SELECT Id FROM FilterAttributeTable WHERE Name=?"));
       
   692         d->query->bindValue(0, filterAtt);
       
   693         d->query->exec();
       
   694         if (d->query->next())
       
   695             filterAtts.append(d->query->value(0).toInt());
       
   696     }
       
   697 
       
   698     int pos = -1;
       
   699     QString fileName;
       
   700     QString anchor;
       
   701     QString fName;
       
   702     int fileId = 1;
       
   703     QList<int> indexFilterTable;
       
   704 
       
   705     int i = 0;
       
   706     d->query->exec(QLatin1String("BEGIN"));
       
   707     foreach (QHelpDataIndexItem itm, keywords) {
       
   708         pos = itm.reference.indexOf(QLatin1Char('#'));
       
   709         fileName = itm.reference.left(pos);
       
   710         if (pos > -1)
       
   711             anchor = itm.reference.mid(pos+1);
       
   712         else
       
   713             anchor.clear();
       
   714 
       
   715         fName = QDir::cleanPath(fileName);
       
   716         if (fName.startsWith(QLatin1String("./")))
       
   717             fName = fName.mid(2);
       
   718 
       
   719         if (d->fileMap.contains(fName))
       
   720             fileId = d->fileMap.value(fName);
       
   721         else
       
   722             fileId = 1;
       
   723 
       
   724         d->query->prepare(QLatin1String("INSERT INTO IndexTable (Name, Identifier, NamespaceId, FileId, Anchor) "
       
   725             "VALUES(?, ?, ?, ?, ?)"));
       
   726         d->query->bindValue(0, itm.name);
       
   727         d->query->bindValue(1, itm.identifier);
       
   728         d->query->bindValue(2, d->namespaceId);
       
   729         d->query->bindValue(3, fileId);
       
   730         d->query->bindValue(4, anchor);
       
   731         d->query->exec();
       
   732 
       
   733         indexFilterTable.append(indexId++);
       
   734         if (++i%100 == 0)
       
   735             addProgress(d->indexStep*100.0);
       
   736     }
       
   737     d->query->exec(QLatin1String("COMMIT"));
       
   738 
       
   739     d->query->exec(QLatin1String("BEGIN"));
       
   740     foreach (int idx, indexFilterTable) {
       
   741         foreach (int a, filterAtts) {
       
   742             d->query->prepare(QLatin1String("INSERT INTO IndexFilterTable (FilterAttributeId, IndexId) "
       
   743                 "VALUES(?, ?)"));
       
   744             d->query->bindValue(0, a);
       
   745             d->query->bindValue(1, idx);
       
   746             d->query->exec();
       
   747         }
       
   748     }
       
   749     d->query->exec(QLatin1String("COMMIT"));
       
   750 
       
   751     d->query->exec(QLatin1String("SELECT COUNT(Id) FROM IndexTable"));
       
   752     if (d->query->next() && d->query->value(0).toInt() >= keywords.count())
       
   753         return true;
       
   754     return false;
       
   755 }
       
   756 
       
   757 bool QHelpGenerator::insertContents(const QByteArray &ba,
       
   758                                     const QStringList &filterAttributes)
       
   759 {
       
   760     if (!d->query)
       
   761         return false;
       
   762 
       
   763     emit statusChanged(tr("Insert contents..."));
       
   764     d->query->prepare(QLatin1String("INSERT INTO ContentsTable (NamespaceId, Data) "
       
   765         "VALUES(?, ?)"));
       
   766     d->query->bindValue(0, d->namespaceId);
       
   767     d->query->bindValue(1, ba);
       
   768     d->query->exec();
       
   769     int contentId = d->query->lastInsertId().toInt();
       
   770     if (contentId < 1) {
       
   771         d->error = tr("Cannot insert contents!");
       
   772         return false;
       
   773     }
       
   774 
       
   775     // associate the filter attributes
       
   776     foreach (QString filterAtt, filterAttributes) {
       
   777         d->query->prepare(QLatin1String("INSERT INTO ContentsFilterTable (FilterAttributeId, ContentsId) "
       
   778             "SELECT Id, ? FROM FilterAttributeTable WHERE Name=?"));
       
   779         d->query->bindValue(0, contentId);
       
   780         d->query->bindValue(1, filterAtt);
       
   781         d->query->exec();
       
   782         if (!d->query->isActive()) {
       
   783             d->error = tr("Cannot register contents!");
       
   784             return false;
       
   785         }
       
   786     }
       
   787     addProgress(d->contentStep);
       
   788     return true;
       
   789 }
       
   790 
       
   791 bool QHelpGenerator::insertFilterAttributes(const QStringList &attributes)
       
   792 {
       
   793     if (!d->query)
       
   794         return false;
       
   795 
       
   796     d->query->exec(QLatin1String("SELECT Name FROM FilterAttributeTable"));
       
   797     QSet<QString> atts;
       
   798     while (d->query->next())
       
   799         atts.insert(d->query->value(0).toString());
       
   800 
       
   801     foreach (QString s, attributes) {
       
   802         if (!atts.contains(s)) {
       
   803             d->query->prepare(QLatin1String("INSERT INTO FilterAttributeTable VALUES(NULL, ?)"));
       
   804             d->query->bindValue(0, s);
       
   805             d->query->exec();
       
   806         }
       
   807     }
       
   808     return true;
       
   809 }
       
   810 
       
   811 bool QHelpGenerator::insertMetaData(const QMap<QString, QVariant> &metaData)
       
   812 {
       
   813     if (!d->query)
       
   814         return false;
       
   815 
       
   816     QMap<QString, QVariant>::const_iterator it = metaData.constBegin();
       
   817     while (it != metaData.constEnd()) {
       
   818         d->query->prepare(QLatin1String("INSERT INTO MetaDataTable VALUES(?, ?)"));
       
   819         d->query->bindValue(0, it.key());
       
   820         d->query->bindValue(1, it.value());
       
   821         d->query->exec();
       
   822         ++it;
       
   823     }
       
   824     return true;
       
   825 }
       
   826 
       
   827 QT_END_NAMESPACE