summaryrefslogtreecommitdiffstats
path: root/src/assistant/lib/qhelpgenerator.cpp
diff options
context:
space:
mode:
authorQt by Nokia <qt-info@nokia.com>2011-04-27 12:05:43 +0200
committeraxis <qt-info@nokia.com>2011-04-27 12:05:43 +0200
commit50123887ba0f33cf47520bee7c419d68742af2d1 (patch)
tree0eb8679b9e4e4370e59b44bfdcae616816e39aca /src/assistant/lib/qhelpgenerator.cpp
Initial import from the monolithic Qt.
This is the beginning of revision history for this module. If you want to look at revision history older than this, please refer to the Qt Git wiki for how to use Git history grafting. At the time of writing, this wiki is located here: http://qt.gitorious.org/qt/pages/GitIntroductionWithQt If you have already performed the grafting and you don't see any history beyond this commit, try running "git log" with the "--follow" argument. Branched from the monolithic repo, Qt master branch, at commit 896db169ea224deb96c59ce8af800d019de63f12
Diffstat (limited to 'src/assistant/lib/qhelpgenerator.cpp')
-rw-r--r--src/assistant/lib/qhelpgenerator.cpp909
1 files changed, 909 insertions, 0 deletions
diff --git a/src/assistant/lib/qhelpgenerator.cpp b/src/assistant/lib/qhelpgenerator.cpp
new file mode 100644
index 000000000..ce9c8562d
--- /dev/null
+++ b/src/assistant/lib/qhelpgenerator.cpp
@@ -0,0 +1,909 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt Assistant of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qhelpgenerator_p.h"
+#include "qhelpdatainterface_p.h"
+
+#include <math.h>
+#include <QtCore/QFile>
+#include <QtCore/QFileInfo>
+#include <QtCore/QDir>
+#include <QtCore/QDebug>
+#include <QtCore/QSet>
+#include <QtCore/QVariant>
+#include <QtCore/QDateTime>
+#include <QtCore/QTextCodec>
+#include <QtSql/QSqlQuery>
+
+QT_BEGIN_NAMESPACE
+
+class QHelpGeneratorPrivate
+{
+public:
+ QHelpGeneratorPrivate();
+ ~QHelpGeneratorPrivate();
+
+ QString error;
+ QSqlQuery *query;
+
+ int namespaceId;
+ int virtualFolderId;
+
+ QMap<QString, int> fileMap;
+ QMap<int, QSet<int> > fileFilterMap;
+
+ double progress;
+ double oldProgress;
+ double contentStep;
+ double fileStep;
+ double indexStep;
+};
+
+QHelpGeneratorPrivate::QHelpGeneratorPrivate()
+{
+ query = 0;
+ namespaceId = -1;
+ virtualFolderId = -1;
+}
+
+QHelpGeneratorPrivate::~QHelpGeneratorPrivate()
+{
+}
+
+
+
+/*!
+ \internal
+ \class QHelpGenerator
+ \since 4.4
+ \brief The QHelpGenerator class generates a new
+ Qt compressed help file (.qch).
+
+ The help generator takes a help data structure as
+ input for generating a new Qt compressed help files. Since
+ the generation may takes some time, the generator emits
+ various signals to inform about its current state.
+*/
+
+/*!
+ \fn void QHelpGenerator::statusChanged(const QString &msg)
+
+ This signal is emitted when the generation status changes.
+ The status is basically a specific task like inserting
+ files or building up the keyword index. The parameter
+ \a msg contains the detailed status description.
+*/
+
+/*!
+ \fn void QHelpGenerator::progressChanged(double progress)
+
+ This signal is emitted when the progress changes. The
+ \a progress ranges from 0 to 100.
+*/
+
+/*!
+ \fn void QHelpGenerator::warning(const QString &msg)
+
+ This signal is emitted when a non critical error occurs,
+ e.g. when a referenced file cannot be found. \a msg
+ contains the exact warning message.
+*/
+
+/*!
+ Constructs a new help generator with the give \a parent.
+*/
+QHelpGenerator::QHelpGenerator(QObject *parent)
+ : QObject(parent)
+{
+ d = new QHelpGeneratorPrivate;
+}
+
+/*!
+ Destructs the help generator.
+*/
+QHelpGenerator::~QHelpGenerator()
+{
+ delete d;
+}
+
+/*!
+ Takes the \a helpData and generates a new documentation
+ set from it. The Qt compressed help file is written to \a
+ outputFileName. Returns true on success, otherwise false.
+*/
+bool QHelpGenerator::generate(QHelpDataInterface *helpData,
+ const QString &outputFileName)
+{
+ emit progressChanged(0);
+ d->error.clear();
+ if (!helpData || helpData->namespaceName().isEmpty()) {
+ d->error = tr("Invalid help data!");
+ return false;
+ }
+
+ QString outFileName = outputFileName;
+ if (outFileName.isEmpty()) {
+ d->error = tr("No output file name specified!");
+ return false;
+ }
+
+ QFileInfo fi(outFileName);
+ if (fi.exists()) {
+ if (!fi.dir().remove(fi.fileName())) {
+ d->error = tr("The file %1 cannot be overwritten!").arg(outFileName);
+ return false;
+ }
+ }
+
+ setupProgress(helpData);
+
+ emit statusChanged(tr("Building up file structure..."));
+ bool openingOk = true;
+ {
+ QSqlDatabase db = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), QLatin1String("builder"));
+ db.setDatabaseName(outFileName);
+ openingOk = db.open();
+ if (openingOk)
+ d->query = new QSqlQuery(db);
+ }
+
+ if (!openingOk) {
+ d->error = tr("Cannot open data base file %1!").arg(outFileName);
+ cleanupDB();
+ return false;
+ }
+
+ d->query->exec(QLatin1String("PRAGMA synchronous=OFF"));
+ d->query->exec(QLatin1String("PRAGMA cache_size=3000"));
+
+ addProgress(1.0);
+ createTables();
+ insertFileNotFoundFile();
+ insertMetaData(helpData->metaData());
+
+ if (!registerVirtualFolder(helpData->virtualFolder(), helpData->namespaceName())) {
+ d->error = tr("Cannot register namespace %1!").arg(helpData->namespaceName());
+ cleanupDB();
+ return false;
+ }
+ addProgress(1.0);
+
+ emit statusChanged(tr("Insert custom filters..."));
+ foreach (const QHelpDataCustomFilter &f, helpData->customFilters()) {
+ if (!registerCustomFilter(f.name, f.filterAttributes, true)) {
+ cleanupDB();
+ return false;
+ }
+ }
+ addProgress(1.0);
+
+ int i = 1;
+ QList<QHelpDataFilterSection>::const_iterator it = helpData->filterSections().constBegin();
+ while (it != helpData->filterSections().constEnd()) {
+ emit statusChanged(tr("Insert help data for filter section (%1 of %2)...")
+ .arg(i++).arg(helpData->filterSections().count()));
+ insertFilterAttributes((*it).filterAttributes());
+ QByteArray ba;
+ QDataStream s(&ba, QIODevice::WriteOnly);
+ foreach (QHelpDataContentItem *itm, (*it).contents())
+ writeTree(s, itm, 0);
+ if (!insertFiles((*it).files(), helpData->rootPath(), (*it).filterAttributes())
+ || !insertContents(ba, (*it).filterAttributes())
+ || !insertKeywords((*it).indices(), (*it).filterAttributes())) {
+ cleanupDB();
+ return false;
+ }
+ ++it;
+ }
+
+ cleanupDB();
+ emit progressChanged(100);
+ emit statusChanged(tr("Documentation successfully generated."));
+ return true;
+}
+
+void QHelpGenerator::setupProgress(QHelpDataInterface *helpData)
+{
+ d->progress = 0;
+ d->oldProgress = 0;
+
+ int numberOfFiles = 0;
+ int numberOfIndices = 0;
+ QList<QHelpDataFilterSection>::const_iterator it = helpData->filterSections().constBegin();
+ while (it != helpData->filterSections().constEnd()) {
+ numberOfFiles += (*it).files().count();
+ numberOfIndices += (*it).indices().count();
+ ++it;
+ }
+ // init 2%
+ // filters 1%
+ // contents 10%
+ // files 60%
+ // indices 27%
+ d->contentStep = 10.0/(double)helpData->customFilters().count();
+ d->fileStep = 60.0/(double)numberOfFiles;
+ d->indexStep = 27.0/(double)numberOfIndices;
+}
+
+void QHelpGenerator::addProgress(double step)
+{
+ d->progress += step;
+ if ((d->progress-d->oldProgress) >= 1.0 && d->progress <= 100.0) {
+ d->oldProgress = d->progress;
+ emit progressChanged(ceil(d->progress));
+ }
+}
+
+void QHelpGenerator::cleanupDB()
+{
+ if (d->query) {
+ d->query->clear();
+ delete d->query;
+ d->query = 0;
+ }
+ QSqlDatabase::removeDatabase(QLatin1String("builder"));
+}
+
+void QHelpGenerator::writeTree(QDataStream &s, QHelpDataContentItem *item, int depth)
+{
+ QString fReference = QDir::cleanPath(item->reference());
+ if (fReference.startsWith(QLatin1String("./")))
+ fReference = fReference.mid(2);
+
+ s << depth;
+ s << fReference;
+ s << item->title();
+ foreach (QHelpDataContentItem *i, item->children())
+ writeTree(s, i, depth+1);
+}
+
+/*!
+ Returns the last error message.
+*/
+QString QHelpGenerator::error() const
+{
+ return d->error;
+}
+
+bool QHelpGenerator::createTables()
+{
+ if (!d->query)
+ return false;
+
+ d->query->exec(QLatin1String("SELECT COUNT(*) FROM sqlite_master WHERE TYPE=\'table\'"
+ "AND Name=\'NamespaceTable\'"));
+ d->query->next();
+ if (d->query->value(0).toInt() > 0) {
+ d->error = tr("Some tables already exist!");
+ return false;
+ }
+
+ QStringList tables;
+ tables << QLatin1String("CREATE TABLE NamespaceTable ("
+ "Id INTEGER PRIMARY KEY,"
+ "Name TEXT )")
+ << QLatin1String("CREATE TABLE FilterAttributeTable ("
+ "Id INTEGER PRIMARY KEY, "
+ "Name TEXT )")
+ << QLatin1String("CREATE TABLE FilterNameTable ("
+ "Id INTEGER PRIMARY KEY, "
+ "Name TEXT )")
+ << QLatin1String("CREATE TABLE FilterTable ("
+ "NameId INTEGER, "
+ "FilterAttributeId INTEGER )")
+ << QLatin1String("CREATE TABLE IndexTable ("
+ "Id INTEGER PRIMARY KEY, "
+ "Name TEXT, "
+ "Identifier TEXT, "
+ "NamespaceId INTEGER, "
+ "FileId INTEGER, "
+ "Anchor TEXT )")
+ << QLatin1String("CREATE TABLE IndexItemTable ("
+ "Id INTEGER, "
+ "IndexId INTEGER )")
+ << QLatin1String("CREATE TABLE IndexFilterTable ("
+ "FilterAttributeId INTEGER, "
+ "IndexId INTEGER )")
+ << QLatin1String("CREATE TABLE ContentsTable ("
+ "Id INTEGER PRIMARY KEY, "
+ "NamespaceId INTEGER, "
+ "Data BLOB )")
+ << QLatin1String("CREATE TABLE ContentsFilterTable ("
+ "FilterAttributeId INTEGER, "
+ "ContentsId INTEGER )")
+ << QLatin1String("CREATE TABLE FileAttributeSetTable ("
+ "Id INTEGER, "
+ "FilterAttributeId INTEGER )")
+ << QLatin1String("CREATE TABLE FileDataTable ("
+ "Id INTEGER PRIMARY KEY, "
+ "Data BLOB )")
+ << QLatin1String("CREATE TABLE FileFilterTable ("
+ "FilterAttributeId INTEGER, "
+ "FileId INTEGER )")
+ << QLatin1String("CREATE TABLE FileNameTable ("
+ "FolderId INTEGER, "
+ "Name TEXT, "
+ "FileId INTEGER, "
+ "Title TEXT )")
+ << QLatin1String("CREATE TABLE FolderTable("
+ "Id INTEGER PRIMARY KEY, "
+ "Name Text, "
+ "NamespaceID INTEGER )")
+ << QLatin1String("CREATE TABLE MetaDataTable("
+ "Name Text, "
+ "Value BLOB )");
+
+ foreach (const QString &q, tables) {
+ if (!d->query->exec(q)) {
+ d->error = tr("Cannot create tables!");
+ return false;
+ }
+ }
+
+ d->query->exec(QLatin1String("INSERT INTO MetaDataTable VALUES('qchVersion', '1.0')"));
+
+ d->query->prepare(QLatin1String("INSERT INTO MetaDataTable VALUES('CreationDate', ?)"));
+ d->query->bindValue(0, QDateTime::currentDateTime().toString(Qt::ISODate));
+ d->query->exec();
+
+ return true;
+}
+
+bool QHelpGenerator::insertFileNotFoundFile()
+{
+ if (!d->query)
+ return false;
+
+ d->query->exec(QLatin1String("SELECT id FROM FileNameTable WHERE Name=\'\'"));
+ if (d->query->next() && d->query->isValid())
+ return true;
+
+ d->query->prepare(QLatin1String("INSERT INTO FileDataTable VALUES (Null, ?)"));
+ d->query->bindValue(0, QByteArray());
+ if (!d->query->exec())
+ return false;
+
+ int fileId = d->query->lastInsertId().toInt();
+ d->query->prepare(QLatin1String("INSERT INTO FileNameTable (FolderId, Name, FileId, Title) "
+ " VALUES (0, '', ?, '')"));
+ d->query->bindValue(0, fileId);
+ if (fileId > -1 && d->query->exec()) {
+ d->fileMap.insert(QString(), fileId);
+ return true;
+ }
+ return false;
+}
+
+bool QHelpGenerator::registerVirtualFolder(const QString &folderName, const QString &ns)
+{
+ if (!d->query || folderName.isEmpty() || ns.isEmpty())
+ return false;
+
+ d->query->prepare(QLatin1String("SELECT Id FROM FolderTable WHERE Name=?"));
+ d->query->bindValue(0, folderName);
+ d->query->exec();
+ d->query->next();
+ if (d->query->isValid() && d->query->value(0).toInt() > 0)
+ return true;
+
+ d->namespaceId = -1;
+ d->query->prepare(QLatin1String("SELECT Id FROM NamespaceTable WHERE Name=?"));
+ d->query->bindValue(0, ns);
+ d->query->exec();
+ while (d->query->next()) {
+ d->namespaceId = d->query->value(0).toInt();
+ break;
+ }
+
+ if (d->namespaceId < 0) {
+ d->query->prepare(QLatin1String("INSERT INTO NamespaceTable VALUES(NULL, ?)"));
+ d->query->bindValue(0, ns);
+ if (d->query->exec())
+ d->namespaceId = d->query->lastInsertId().toInt();
+ }
+
+ if (d->namespaceId > 0) {
+ d->query->prepare(QLatin1String("SELECT Id FROM FolderTable WHERE Name=?"));
+ d->query->bindValue(0, folderName);
+ d->query->exec();
+ while (d->query->next())
+ d->virtualFolderId = d->query->value(0).toInt();
+
+ if (d->virtualFolderId > 0)
+ return true;
+
+ d->query->prepare(QLatin1String("INSERT INTO FolderTable (NamespaceId, Name) "
+ "VALUES (?, ?)"));
+ d->query->bindValue(0, d->namespaceId);
+ d->query->bindValue(1, folderName);
+ if (d->query->exec()) {
+ d->virtualFolderId = d->query->lastInsertId().toInt();
+ return d->virtualFolderId > 0;
+ }
+ }
+ d->error = tr("Cannot register virtual folder!");
+ return false;
+}
+
+bool QHelpGenerator::insertFiles(const QStringList &files, const QString &rootPath,
+ const QStringList &filterAttributes)
+{
+ if (!d->query)
+ return false;
+
+ emit statusChanged(tr("Insert files..."));
+ QList<int> filterAtts;
+ foreach (const QString &filterAtt, filterAttributes) {
+ d->query->prepare(QLatin1String("SELECT Id FROM FilterAttributeTable "
+ "WHERE Name=?"));
+ d->query->bindValue(0, filterAtt);
+ d->query->exec();
+ if (d->query->next())
+ filterAtts.append(d->query->value(0).toInt());
+ }
+
+ int filterSetId = -1;
+ d->query->exec(QLatin1String("SELECT MAX(Id) FROM FileAttributeSetTable"));
+ if (d->query->next())
+ filterSetId = d->query->value(0).toInt();
+ if (filterSetId < 0)
+ return false;
+ ++filterSetId;
+ foreach (const int &attId, filterAtts) {
+ d->query->prepare(QLatin1String("INSERT INTO FileAttributeSetTable "
+ "VALUES(?, ?)"));
+ d->query->bindValue(0, filterSetId);
+ d->query->bindValue(1, attId);
+ d->query->exec();
+ }
+
+ int tableFileId = 1;
+ d->query->exec(QLatin1String("SELECT MAX(Id) FROM FileDataTable"));
+ if (d->query->next())
+ tableFileId = d->query->value(0).toInt() + 1;
+
+ QString title;
+ QString charSet;
+ FileNameTableData fileNameData;
+ QList<QByteArray> fileDataList;
+ QMap<int, QSet<int> > tmpFileFilterMap;
+ QList<FileNameTableData> fileNameDataList;
+
+ int i = 0;
+ foreach (const QString &file, files) {
+ const QString fileName = QDir::cleanPath(file);
+ if (fileName.startsWith(QLatin1String("../"))) {
+ emit warning(tr("The referenced file %1 must be inside or within a "
+ "subdirectory of (%2). Skipping it.").arg(fileName).arg(rootPath));
+ continue;
+ }
+
+ QFile fi(rootPath + QDir::separator() + fileName);
+ if (!fi.exists()) {
+ emit warning(tr("The file %1 does not exist! Skipping it.")
+ .arg(QDir::cleanPath(rootPath + QDir::separator() + fileName)));
+ continue;
+ }
+
+ if (!fi.open(QIODevice::ReadOnly)) {
+ emit warning(tr("Cannot open file %1! Skipping it.")
+ .arg(QDir::cleanPath(rootPath + QDir::separator() + fileName)));
+ continue;
+ }
+
+ QByteArray data = fi.readAll();
+ if (fileName.endsWith(QLatin1String(".html"))
+ || fileName.endsWith(QLatin1String(".htm"))) {
+ charSet = QHelpGlobal::codecFromData(data);
+ QTextStream stream(&data);
+ stream.setCodec(QTextCodec::codecForName(charSet.toLatin1().constData()));
+ title = QHelpGlobal::documentTitle(stream.readAll());
+ } else {
+ title = fileName.mid(fileName.lastIndexOf(QLatin1Char('/')) + 1);
+ }
+
+ int fileId = -1;
+ QMap<QString, int>::Iterator fileMapIt = d->fileMap.find(fileName);
+ if (fileMapIt == d->fileMap.end()) {
+ fileDataList.append(qCompress(data));
+
+ fileNameData.name = fileName;
+ fileNameData.fileId = tableFileId;
+ fileNameData.title = title;
+ fileNameDataList.append(fileNameData);
+
+ d->fileMap.insert(fileName, tableFileId);
+ d->fileFilterMap.insert(tableFileId, filterAtts.toSet());
+ tmpFileFilterMap.insert(tableFileId, filterAtts.toSet());
+
+ ++tableFileId;
+ } else {
+ fileId = fileMapIt.value();
+ QSet<int> &fileFilterSet = d->fileFilterMap[fileId];
+ QSet<int> &tmpFileFilterSet = tmpFileFilterMap[fileId];
+ foreach (const int &filter, filterAtts) {
+ if (!fileFilterSet.contains(filter)
+ && !tmpFileFilterSet.contains(filter)) {
+ fileFilterSet.insert(filter);
+ tmpFileFilterSet.insert(filter);
+ }
+ }
+ }
+ }
+
+ if (!tmpFileFilterMap.isEmpty()) {
+ d->query->exec(QLatin1String("BEGIN"));
+ QMap<int, QSet<int> >::const_iterator it = tmpFileFilterMap.constBegin();
+ while (it != tmpFileFilterMap.constEnd()) {
+ QSet<int>::const_iterator si = it.value().constBegin();
+ while (si != it.value().constEnd()) {
+ d->query->prepare(QLatin1String("INSERT INTO FileFilterTable "
+ "VALUES(?, ?)"));
+ d->query->bindValue(0, *si);
+ d->query->bindValue(1, it.key());
+ d->query->exec();
+ ++si;
+ }
+ ++it;
+ }
+
+ QList<QByteArray>::const_iterator fileIt = fileDataList.constBegin();
+ while (fileIt != fileDataList.constEnd()) {
+ d->query->prepare(QLatin1String("INSERT INTO FileDataTable VALUES "
+ "(Null, ?)"));
+ d->query->bindValue(0, *fileIt);
+ d->query->exec();
+ ++fileIt;
+ if (++i%20 == 0)
+ addProgress(d->fileStep*20.0);
+ }
+
+ QList<FileNameTableData>::const_iterator fileNameIt =
+ fileNameDataList.constBegin();
+ while (fileNameIt != fileNameDataList.constEnd()) {
+ d->query->prepare(QLatin1String("INSERT INTO FileNameTable "
+ "(FolderId, Name, FileId, Title) VALUES (?, ?, ?, ?)"));
+ d->query->bindValue(0, 1);
+ d->query->bindValue(1, (*fileNameIt).name);
+ d->query->bindValue(2, (*fileNameIt).fileId);
+ d->query->bindValue(3, (*fileNameIt).title);
+ d->query->exec();
+ ++fileNameIt;
+ }
+ d->query->exec(QLatin1String("COMMIT"));
+ }
+
+ d->query->exec(QLatin1String("SELECT MAX(Id) FROM FileDataTable"));
+ if (d->query->next()
+ && d->query->value(0).toInt() == tableFileId-1) {
+ addProgress(d->fileStep*(i%20));
+ return true;
+ }
+ return false;
+}
+
+bool QHelpGenerator::registerCustomFilter(const QString &filterName,
+ const QStringList &filterAttribs, bool forceUpdate)
+{
+ if (!d->query)
+ return false;
+
+ d->query->exec(QLatin1String("SELECT Id, Name FROM FilterAttributeTable"));
+ QStringList idsToInsert = filterAttribs;
+ QMap<QString, int> attributeMap;
+ while (d->query->next()) {
+ attributeMap.insert(d->query->value(1).toString(),
+ d->query->value(0).toInt());
+ idsToInsert.removeAll(d->query->value(1).toString());
+ }
+
+ foreach (const QString &id, idsToInsert) {
+ d->query->prepare(QLatin1String("INSERT INTO FilterAttributeTable VALUES(NULL, ?)"));
+ d->query->bindValue(0, id);
+ d->query->exec();
+ attributeMap.insert(id, d->query->lastInsertId().toInt());
+ }
+
+ int nameId = -1;
+ d->query->prepare(QLatin1String("SELECT Id FROM FilterNameTable WHERE Name=?"));
+ d->query->bindValue(0, filterName);
+ d->query->exec();
+ while (d->query->next()) {
+ nameId = d->query->value(0).toInt();
+ break;
+ }
+
+ if (nameId < 0) {
+ d->query->prepare(QLatin1String("INSERT INTO FilterNameTable VALUES(NULL, ?)"));
+ d->query->bindValue(0, filterName);
+ if (d->query->exec())
+ nameId = d->query->lastInsertId().toInt();
+ } else if (!forceUpdate) {
+ d->error = tr("The filter %1 is already registered!").arg(filterName);
+ return false;
+ }
+
+ if (nameId < 0) {
+ d->error = tr("Cannot register filter %1!").arg(filterName);
+ return false;
+ }
+
+ d->query->prepare(QLatin1String("DELETE FROM FilterTable WHERE NameId=?"));
+ d->query->bindValue(0, nameId);
+ d->query->exec();
+
+ foreach (const QString &att, filterAttribs) {
+ d->query->prepare(QLatin1String("INSERT INTO FilterTable VALUES(?, ?)"));
+ d->query->bindValue(0, nameId);
+ d->query->bindValue(1, attributeMap[att]);
+ if (!d->query->exec())
+ return false;
+ }
+ return true;
+}
+
+bool QHelpGenerator::insertKeywords(const QList<QHelpDataIndexItem> &keywords,
+ const QStringList &filterAttributes)
+{
+ if (!d->query)
+ return false;
+
+ emit statusChanged(tr("Insert indices..."));
+ int indexId = 1;
+ d->query->exec(QLatin1String("SELECT MAX(Id) FROM IndexTable"));
+ if (d->query->next())
+ indexId = d->query->value(0).toInt() + 1;
+
+ QList<int> filterAtts;
+ foreach (const QString &filterAtt, filterAttributes) {
+ d->query->prepare(QLatin1String("SELECT Id FROM FilterAttributeTable WHERE Name=?"));
+ d->query->bindValue(0, filterAtt);
+ d->query->exec();
+ if (d->query->next())
+ filterAtts.append(d->query->value(0).toInt());
+ }
+
+ int pos = -1;
+ QString fileName;
+ QString anchor;
+ QString fName;
+ int fileId = 1;
+ QList<int> indexFilterTable;
+
+ int i = 0;
+ d->query->exec(QLatin1String("BEGIN"));
+ QSet<QString> indices;
+ foreach (const QHelpDataIndexItem &itm, keywords) {
+ // Identical ids make no sense and just confuse the Assistant user,
+ // so we ignore all repetitions.
+ if (indices.contains(itm.identifier))
+ continue;
+
+ // Still empty ids should be ignored, as otherwise we will include only
+ // the first keyword with an empty id.
+ if (!itm.identifier.isEmpty())
+ indices.insert(itm.identifier);
+
+ pos = itm.reference.indexOf(QLatin1Char('#'));
+ fileName = itm.reference.left(pos);
+ if (pos > -1)
+ anchor = itm.reference.mid(pos+1);
+ else
+ anchor.clear();
+
+ fName = QDir::cleanPath(fileName);
+ if (fName.startsWith(QLatin1String("./")))
+ fName = fName.mid(2);
+
+ QMap<QString, int>::ConstIterator it = d->fileMap.find(fName);
+ if (it != d->fileMap.end())
+ fileId = it.value();
+ else
+ fileId = 1;
+
+ d->query->prepare(QLatin1String("INSERT INTO IndexTable (Name, Identifier, NamespaceId, FileId, Anchor) "
+ "VALUES(?, ?, ?, ?, ?)"));
+ d->query->bindValue(0, itm.name);
+ d->query->bindValue(1, itm.identifier);
+ d->query->bindValue(2, d->namespaceId);
+ d->query->bindValue(3, fileId);
+ d->query->bindValue(4, anchor);
+ d->query->exec();
+
+ indexFilterTable.append(indexId++);
+ if (++i%100 == 0)
+ addProgress(d->indexStep*100.0);
+ }
+ d->query->exec(QLatin1String("COMMIT"));
+
+ d->query->exec(QLatin1String("BEGIN"));
+ foreach (int idx, indexFilterTable) {
+ foreach (int a, filterAtts) {
+ d->query->prepare(QLatin1String("INSERT INTO IndexFilterTable (FilterAttributeId, IndexId) "
+ "VALUES(?, ?)"));
+ d->query->bindValue(0, a);
+ d->query->bindValue(1, idx);
+ d->query->exec();
+ }
+ }
+ d->query->exec(QLatin1String("COMMIT"));
+
+ d->query->exec(QLatin1String("SELECT COUNT(Id) FROM IndexTable"));
+ if (d->query->next() && d->query->value(0).toInt() >= indices.count())
+ return true;
+ return false;
+}
+
+bool QHelpGenerator::insertContents(const QByteArray &ba,
+ const QStringList &filterAttributes)
+{
+ if (!d->query)
+ return false;
+
+ emit statusChanged(tr("Insert contents..."));
+ d->query->prepare(QLatin1String("INSERT INTO ContentsTable (NamespaceId, Data) "
+ "VALUES(?, ?)"));
+ d->query->bindValue(0, d->namespaceId);
+ d->query->bindValue(1, ba);
+ d->query->exec();
+ int contentId = d->query->lastInsertId().toInt();
+ if (contentId < 1) {
+ d->error = tr("Cannot insert contents!");
+ return false;
+ }
+
+ // associate the filter attributes
+ foreach (const QString &filterAtt, filterAttributes) {
+ d->query->prepare(QLatin1String("INSERT INTO ContentsFilterTable (FilterAttributeId, ContentsId) "
+ "SELECT Id, ? FROM FilterAttributeTable WHERE Name=?"));
+ d->query->bindValue(0, contentId);
+ d->query->bindValue(1, filterAtt);
+ d->query->exec();
+ if (!d->query->isActive()) {
+ d->error = tr("Cannot register contents!");
+ return false;
+ }
+ }
+ addProgress(d->contentStep);
+ return true;
+}
+
+bool QHelpGenerator::insertFilterAttributes(const QStringList &attributes)
+{
+ if (!d->query)
+ return false;
+
+ d->query->exec(QLatin1String("SELECT Name FROM FilterAttributeTable"));
+ QSet<QString> atts;
+ while (d->query->next())
+ atts.insert(d->query->value(0).toString());
+
+ foreach (const QString &s, attributes) {
+ if (!atts.contains(s)) {
+ d->query->prepare(QLatin1String("INSERT INTO FilterAttributeTable VALUES(NULL, ?)"));
+ d->query->bindValue(0, s);
+ d->query->exec();
+ }
+ }
+ return true;
+}
+
+bool QHelpGenerator::insertMetaData(const QMap<QString, QVariant> &metaData)
+{
+ if (!d->query)
+ return false;
+
+ QMap<QString, QVariant>::const_iterator it = metaData.constBegin();
+ while (it != metaData.constEnd()) {
+ d->query->prepare(QLatin1String("INSERT INTO MetaDataTable VALUES(?, ?)"));
+ d->query->bindValue(0, it.key());
+ d->query->bindValue(1, it.value());
+ d->query->exec();
+ ++it;
+ }
+ return true;
+}
+
+bool QHelpGenerator::checkLinks(const QHelpDataInterface &helpData)
+{
+ /*
+ * Step 1: Gather the canoncal file paths of all files in the project.
+ * We use a set, because there will be a lot of look-ups.
+ */
+ QSet<QString> files;
+ foreach (const QHelpDataFilterSection &filterSection, helpData.filterSections()) {
+ foreach (const QString &file, filterSection.files()) {
+ QFileInfo fileInfo(helpData.rootPath() + QDir::separator() + file);
+ const QString &canonicalFileName = fileInfo.canonicalFilePath();
+ if (!fileInfo.exists())
+ emit warning(tr("File '%1' does not exist.").arg(file));
+ else
+ files.insert(canonicalFileName);
+ }
+ }
+
+ /*
+ * Step 2: Check the hypertext and image references of all HTML files.
+ * Note that we don't parse the files, but simply grep for the
+ * respective HTML elements. Therefore. contents that are e.g.
+ * commented out can cause false warning.
+ */
+ bool allLinksOk = true;
+ foreach (const QString &fileName, files) {
+ if (!fileName.endsWith(QLatin1String("html"))
+ && !fileName.endsWith(QLatin1String("htm")))
+ continue;
+ QFile htmlFile(fileName);
+ if (!htmlFile.open(QIODevice::ReadOnly)) {
+ emit warning(tr("File '%1' cannot be opened.").arg(fileName));
+ continue;
+ }
+ const QRegExp linkPattern(QLatin1String("<(?:a href|img src)=\"?([^#\">]+)[#\">]"));
+ QTextStream stream(&htmlFile);
+ const QString codec = QHelpGlobal::codecFromData(htmlFile.read(1000));
+ stream.setCodec(QTextCodec::codecForName(codec.toLatin1().constData()));
+ const QString &content = stream.readAll();
+ QStringList invalidLinks;
+ for (int pos = linkPattern.indexIn(content); pos != -1;
+ pos = linkPattern.indexIn(content, pos + 1)) {
+ const QString& linkedFileName = linkPattern.cap(1);
+ if (linkedFileName.contains(QLatin1String("://")))
+ continue;
+ const QString curDir = QFileInfo(fileName).dir().path();
+ const QString &canonicalLinkedFileName =
+ QFileInfo(curDir + QDir::separator() + linkedFileName).canonicalFilePath();
+ if (!files.contains(canonicalLinkedFileName)
+ && !invalidLinks.contains(canonicalLinkedFileName)) {
+ emit warning(tr("File '%1' contains an invalid link to file '%2'").
+ arg(fileName).arg(linkedFileName));
+ allLinksOk = false;
+ invalidLinks.append(canonicalLinkedFileName);
+ }
+ }
+ }
+
+ if (!allLinksOk)
+ d->error = tr("Invalid links in HTML files.");
+ return allLinksOk;
+}
+
+QT_END_NAMESPACE
+