-
Antti Kokko authored
- Renamed LICENSE.LGPL to LICENSE.LGPLv21 - Added LICENSE.LGPLv3 - Removed LICENSE.GPL Change-Id: I23ef9591f4d9054e0b6a252ba7767baf4189aeab Reviewed-by:
Jani Heikkinen <jani.heikkinen@digia.com>
794e271d
/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the Qt Assistant of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** 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 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** $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;
};
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
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
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
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;
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
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)
{
s << depth;
s << item->reference();
281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
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, "
351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
"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();
421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
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;
491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
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);
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()) {
561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
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);
631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700
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))
701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770
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();
771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840
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));
841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893
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;
}
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