blob: 48b09594d545d03450b203e68721c1af97af49a5 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Assistant of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** 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 The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/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 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qhelpcollectionhandler_p.h"
#include "qhelp_global.h"
#include "qhelpdbreader_p.h"
#include "qhelpfilterdata.h"
#include <QtCore/QDataStream>
#include <QtCore/QDateTime>
#include <QtCore/QDir>
#include <QtCore/QFile>
#include <QtCore/QFileInfo>
#include <QtCore/QTimer>
#include <QtCore/QVector>
#include <QtCore/QVersionNumber>
#include <QtHelp/QHelpLink>
#include <QtSql/QSqlError>
#include <QtSql/QSqlDriver>
QT_BEGIN_NAMESPACE
class Transaction
{
public:
Transaction(const QString &connectionName)
: m_db(QSqlDatabase::database(connectionName)),
m_inTransaction(m_db.driver()->hasFeature(QSqlDriver::Transactions))
{
if (m_inTransaction)
m_inTransaction = m_db.transaction();
}
~Transaction()
{
if (m_inTransaction)
m_db.rollback();
}
void commit()
{
if (!m_inTransaction)
return;
m_db.commit();
m_inTransaction = false;
}
private:
QSqlDatabase m_db;
bool m_inTransaction;
};
QHelpCollectionHandler::QHelpCollectionHandler(const QString &collectionFile, QObject *parent)
: QObject(parent)
, m_collectionFile(collectionFile)
{
const QFileInfo fi(m_collectionFile);
if (!fi.isAbsolute())
m_collectionFile = fi.absoluteFilePath();
}
QHelpCollectionHandler::~QHelpCollectionHandler()
{
closeDB();
}
bool QHelpCollectionHandler::isDBOpened() const
{
if (m_query)
return true;
emit error(tr("The collection file \"%1\" is not set up yet.").
arg(m_collectionFile));
return false;
}
void QHelpCollectionHandler::closeDB()
{
if (!m_query)
return;
delete m_query;
m_query = nullptr;
QSqlDatabase::removeDatabase(m_connectionName);
m_connectionName = QString();
}
QString QHelpCollectionHandler::collectionFile() const
{
return m_collectionFile;
}
bool QHelpCollectionHandler::openCollectionFile()
{
if (m_query)
return true;
m_connectionName = QHelpGlobal::uniquifyConnectionName(
QLatin1String("QHelpCollectionHandler"), this);
{
QSqlDatabase db = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"),
m_connectionName);
if (db.driver()
&& db.driver()->lastError().type() == QSqlError::ConnectionError) {
emit error(tr("Cannot load sqlite database driver."));
return false;
}
db.setDatabaseName(collectionFile());
if (db.open())
m_query = new QSqlQuery(db);
if (!m_query) {
QSqlDatabase::removeDatabase(m_connectionName);
emit error(tr("Cannot open collection file: %1").arg(collectionFile()));
return false;
}
}
if (m_readOnly)
return true;
m_query->exec(QLatin1String("PRAGMA synchronous=OFF"));
m_query->exec(QLatin1String("PRAGMA cache_size=3000"));
m_query->exec(QLatin1String("SELECT COUNT(*) FROM sqlite_master WHERE TYPE=\'table\' "
"AND Name=\'NamespaceTable\'"));
m_query->next();
const bool tablesExist = m_query->value(0).toInt() > 0;
if (!tablesExist) {
if (!createTables(m_query)) {
closeDB();
emit error(tr("Cannot create tables in file %1.").arg(collectionFile()));
return false;
}
}
bool indexAndNamespaceFilterTablesMissing = false;
const QStringList newTables = {
QLatin1String("IndexTable"),
QLatin1String("FileNameTable"),
QLatin1String("ContentsTable"),
QLatin1String("FileFilterTable"),
QLatin1String("IndexFilterTable"),
QLatin1String("ContentsFilterTable"),
QLatin1String("FileAttributeSetTable"),
QLatin1String("OptimizedFilterTable"),
QLatin1String("TimeStampTable"),
QLatin1String("VersionTable"),
QLatin1String("Filter"),
QLatin1String("ComponentTable"),
QLatin1String("ComponentMapping"),
QLatin1String("ComponentFilter"),
QLatin1String("VersionFilter")
};
QString queryString = QLatin1String("SELECT COUNT(*) "
"FROM sqlite_master "
"WHERE TYPE=\'table\'");
queryString.append(QLatin1String(" AND (Name=\'"));
queryString.append(newTables.join(QLatin1String("\' OR Name=\'")));
queryString.append(QLatin1String("\')"));
m_query->exec(queryString);
m_query->next();
if (m_query->value(0).toInt() != newTables.count()) {
if (!recreateIndexAndNamespaceFilterTables(m_query)) {
emit error(tr("Cannot create index tables in file %1.").arg(collectionFile()));
return false;
}
// Old tables exist, index tables didn't, recreate index tables only in this case
indexAndNamespaceFilterTablesMissing = tablesExist;
}
const FileInfoList &docList = registeredDocumentations();
if (indexAndNamespaceFilterTablesMissing) {
for (const QHelpCollectionHandler::FileInfo &info : docList) {
if (!registerIndexAndNamespaceFilterTables(info.namespaceName, true)) {
emit error(tr("Cannot register index tables in file %1.").arg(collectionFile()));
return false;
}
}
return true;
}
QList<TimeStamp> timeStamps;
m_query->exec(QLatin1String("SELECT NamespaceId, FolderId, FilePath, Size, TimeStamp "
"FROM TimeStampTable"));
while (m_query->next()) {
TimeStamp timeStamp;
timeStamp.namespaceId = m_query->value(0).toInt();
timeStamp.folderId = m_query->value(1).toInt();
timeStamp.fileName = m_query->value(2).toString();
timeStamp.size = m_query->value(3).toInt();
timeStamp.timeStamp = m_query->value(4).toString();
timeStamps.append(timeStamp);
}
QVector<TimeStamp> toRemove;
for (const TimeStamp &timeStamp : timeStamps) {
if (!isTimeStampCorrect(timeStamp))
toRemove.append(timeStamp);
}
// TODO: we may optimize when toRemove.size() == timeStamps.size().
// In this case we remove all records from tables.
Transaction transaction(m_connectionName);
for (const TimeStamp &timeStamp : toRemove) {
if (!unregisterIndexTable(timeStamp.namespaceId, timeStamp.folderId)) {
emit error(tr("Cannot unregister index tables in file %1.").arg(collectionFile()));
return false;
}
}
transaction.commit();
for (const QHelpCollectionHandler::FileInfo &info : docList) {
if (!hasTimeStampInfo(info.namespaceName)
&& !registerIndexAndNamespaceFilterTables(info.namespaceName)) {
// we may have a doc registered without a timestamp
// and the doc may be missing currently
unregisterDocumentation(info.namespaceName);
}
}
return true;
}
QString QHelpCollectionHandler::absoluteDocPath(const QString &fileName) const
{
const QFileInfo fi(collectionFile());
return QDir::isAbsolutePath(fileName)
? fileName
: QFileInfo(fi.absolutePath() + QLatin1Char('/') + fileName)
.absoluteFilePath();
}
bool QHelpCollectionHandler::isTimeStampCorrect(const TimeStamp &timeStamp) const
{
const QFileInfo fi(absoluteDocPath(timeStamp.fileName));
if (!fi.exists())
return false;
if (fi.size() != timeStamp.size)
return false;
if (fi.lastModified().toString(Qt::ISODate) != timeStamp.timeStamp)
return false;
m_query->prepare(QLatin1String("SELECT FilePath "
"FROM NamespaceTable "
"WHERE Id = ?"));
m_query->bindValue(0, timeStamp.namespaceId);
if (!m_query->exec() || !m_query->next())
return false;
const QString oldFileName = m_query->value(0).toString();
m_query->clear();
if (oldFileName != timeStamp.fileName)
return false;
return true;
}
bool QHelpCollectionHandler::hasTimeStampInfo(const QString &nameSpace) const
{
m_query->prepare(QLatin1String("SELECT "
"TimeStampTable.NamespaceId "
"FROM "
"NamespaceTable, "
"TimeStampTable "
"WHERE NamespaceTable.Id = TimeStampTable.NamespaceId "
"AND NamespaceTable.Name = ? LIMIT 1"));
m_query->bindValue(0, nameSpace);
if (!m_query->exec())
return false;
if (!m_query->next())
return false;
m_query->clear();
return true;
}
void QHelpCollectionHandler::scheduleVacuum()
{
if (m_vacuumScheduled)
return;
m_vacuumScheduled = true;
QTimer::singleShot(0, this, &QHelpCollectionHandler::execVacuum);
}
void QHelpCollectionHandler::execVacuum()
{
if (!m_query)
return;
m_query->exec(QLatin1String("VACUUM"));
m_vacuumScheduled = false;
}
bool QHelpCollectionHandler::copyCollectionFile(const QString &fileName)
{
if (!m_query)
return false;
const QFileInfo fi(fileName);
if (fi.exists()) {
emit error(tr("The collection file \"%1\" already exists.").
arg(fileName));
return false;
}
if (!fi.absoluteDir().exists() && !QDir().mkpath(fi.absolutePath())) {
emit error(tr("Cannot create directory: %1").arg(fi.absolutePath()));
return false;
}
const QString &colFile = fi.absoluteFilePath();
const QString &connectionName = QHelpGlobal::uniquifyConnectionName(
QLatin1String("QHelpCollectionHandlerCopy"), this);
QSqlQuery *copyQuery = nullptr;
bool openingOk = true;
{
QSqlDatabase db = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), connectionName);
db.setDatabaseName(colFile);
openingOk = db.open();
if (openingOk)
copyQuery = new QSqlQuery(db);
}
if (!openingOk) {
emit error(tr("Cannot open collection file: %1").arg(colFile));
return false;
}
copyQuery->exec(QLatin1String("PRAGMA synchronous=OFF"));
copyQuery->exec(QLatin1String("PRAGMA cache_size=3000"));
if (!createTables(copyQuery) || !recreateIndexAndNamespaceFilterTables(copyQuery)) {
emit error(tr("Cannot copy collection file: %1").arg(colFile));
delete copyQuery;
return false;
}
const QString &oldBaseDir = QFileInfo(collectionFile()).absolutePath();
const QFileInfo newColFi(colFile);
m_query->exec(QLatin1String("SELECT Name, FilePath FROM NamespaceTable"));
while (m_query->next()) {
copyQuery->prepare(QLatin1String("INSERT INTO NamespaceTable VALUES(NULL, ?, ?)"));
copyQuery->bindValue(0, m_query->value(0).toString());
QString oldFilePath = m_query->value(1).toString();
if (!QDir::isAbsolutePath(oldFilePath))
oldFilePath = oldBaseDir + QLatin1Char('/') + oldFilePath;
copyQuery->bindValue(1, newColFi.absoluteDir().relativeFilePath(oldFilePath));
copyQuery->exec();
}
m_query->exec(QLatin1String("SELECT NamespaceId, Name FROM FolderTable"));
while (m_query->next()) {
copyQuery->prepare(QLatin1String("INSERT INTO FolderTable VALUES(NULL, ?, ?)"));
copyQuery->bindValue(0, m_query->value(0).toString());
copyQuery->bindValue(1, m_query->value(1).toString());
copyQuery->exec();
}
m_query->exec(QLatin1String("SELECT Name FROM FilterAttributeTable"));
while (m_query->next()) {
copyQuery->prepare(QLatin1String("INSERT INTO FilterAttributeTable VALUES(NULL, ?)"));
copyQuery->bindValue(0, m_query->value(0).toString());
copyQuery->exec();
}
m_query->exec(QLatin1String("SELECT Name FROM FilterNameTable"));
while (m_query->next()) {
copyQuery->prepare(QLatin1String("INSERT INTO FilterNameTable VALUES(NULL, ?)"));
copyQuery->bindValue(0, m_query->value(0).toString());
copyQuery->exec();
}
m_query->exec(QLatin1String("SELECT NameId, FilterAttributeId FROM FilterTable"));
while (m_query->next()) {
copyQuery->prepare(QLatin1String("INSERT INTO FilterTable VALUES(?, ?)"));
copyQuery->bindValue(0, m_query->value(0).toInt());
copyQuery->bindValue(1, m_query->value(1).toInt());
copyQuery->exec();
}
m_query->exec(QLatin1String("SELECT Key, Value FROM SettingsTable"));
while (m_query->next()) {
if (m_query->value(0).toString() == QLatin1String("FTS5IndexedNamespaces"))
continue;
copyQuery->prepare(QLatin1String("INSERT INTO SettingsTable VALUES(?, ?)"));
copyQuery->bindValue(0, m_query->value(0).toString());
copyQuery->bindValue(1, m_query->value(1));
copyQuery->exec();
}
copyQuery->clear();
delete copyQuery;
QSqlDatabase::removeDatabase(connectionName);
return true;
}
bool QHelpCollectionHandler::createTables(QSqlQuery *query)
{
const QStringList tables = QStringList()
<< QLatin1String("CREATE TABLE NamespaceTable ("
"Id INTEGER PRIMARY KEY, "
"Name TEXT, "
"FilePath TEXT )")
<< QLatin1String("CREATE TABLE FolderTable ("
"Id INTEGER PRIMARY KEY, "
"NamespaceId INTEGER, "
"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 SettingsTable ("
"Key TEXT PRIMARY KEY, "
"Value BLOB )");
for (const QString &q : tables) {
if (!query->exec(q))
return false;
}
return true;
}
bool QHelpCollectionHandler::recreateIndexAndNamespaceFilterTables(QSqlQuery *query)
{
const QStringList tables = QStringList()
<< QLatin1String("DROP TABLE IF EXISTS FileNameTable")
<< QLatin1String("DROP TABLE IF EXISTS IndexTable")
<< QLatin1String("DROP TABLE IF EXISTS ContentsTable")
<< QLatin1String("DROP TABLE IF EXISTS FileFilterTable") // legacy
<< QLatin1String("DROP TABLE IF EXISTS IndexFilterTable") // legacy
<< QLatin1String("DROP TABLE IF EXISTS ContentsFilterTable") // legacy
<< QLatin1String("DROP TABLE IF EXISTS FileAttributeSetTable") // legacy
<< QLatin1String("DROP TABLE IF EXISTS OptimizedFilterTable") // legacy
<< QLatin1String("DROP TABLE IF EXISTS TimeStampTable")
<< QLatin1String("DROP TABLE IF EXISTS VersionTable")
<< QLatin1String("DROP TABLE IF EXISTS Filter")
<< QLatin1String("DROP TABLE IF EXISTS ComponentTable")
<< QLatin1String("DROP TABLE IF EXISTS ComponentMapping")
<< QLatin1String("DROP TABLE IF EXISTS ComponentFilter")
<< QLatin1String("DROP TABLE IF EXISTS VersionFilter")
<< QLatin1String("CREATE TABLE FileNameTable ("
"FolderId INTEGER, "
"Name TEXT, "
"FileId INTEGER PRIMARY KEY, "
"Title TEXT)")
<< QLatin1String("CREATE TABLE IndexTable ("
"Id INTEGER PRIMARY KEY, "
"Name TEXT, "
"Identifier TEXT, "
"NamespaceId INTEGER, "
"FileId INTEGER, "
"Anchor TEXT)")
<< QLatin1String("CREATE TABLE ContentsTable ("
"Id INTEGER PRIMARY KEY, "
"NamespaceId INTEGER, "
"Data BLOB)")
<< QLatin1String("CREATE TABLE FileFilterTable ("
"FilterAttributeId INTEGER, "
"FileId INTEGER)")
<< QLatin1String("CREATE TABLE IndexFilterTable ("
"FilterAttributeId INTEGER, "
"IndexId INTEGER)")
<< QLatin1String("CREATE TABLE ContentsFilterTable ("
"FilterAttributeId INTEGER, "
"ContentsId INTEGER )")
<< QLatin1String("CREATE TABLE FileAttributeSetTable ("
"NamespaceId INTEGER, "
"FilterAttributeSetId INTEGER, "
"FilterAttributeId INTEGER)")
<< QLatin1String("CREATE TABLE OptimizedFilterTable ("
"NamespaceId INTEGER, "
"FilterAttributeId INTEGER)")
<< QLatin1String("CREATE TABLE TimeStampTable ("
"NamespaceId INTEGER, "
"FolderId INTEGER, "
"FilePath TEXT, "
"Size INTEGER, "
"TimeStamp TEXT)")
<< QLatin1String("CREATE TABLE VersionTable ("
"NamespaceId INTEGER, "
"Version TEXT)")
<< QLatin1String("CREATE TABLE Filter ("
"FilterId INTEGER PRIMARY KEY, "
"Name TEXT)")
<< QLatin1String("CREATE TABLE ComponentTable ("
"ComponentId INTEGER PRIMARY KEY, "
"Name TEXT)")
<< QLatin1String("CREATE TABLE ComponentMapping ("
"ComponentId INTEGER, "
"NamespaceId INTEGER)")
<< QLatin1String("CREATE TABLE ComponentFilter ("
"ComponentName TEXT, "
"FilterId INTEGER)")
<< QLatin1String("CREATE TABLE VersionFilter ("
"Version TEXT, "
"FilterId INTEGER)");
for (const QString &q : tables) {
if (!query->exec(q))
return false;
}
return true;
}
QStringList QHelpCollectionHandler::customFilters() const
{
QStringList list;
if (m_query) {
m_query->exec(QLatin1String("SELECT Name FROM FilterNameTable"));
while (m_query->next())
list.append(m_query->value(0).toString());
}
return list;
}
QStringList QHelpCollectionHandler::filters() const
{
QStringList list;
if (m_query) {
m_query->exec(QLatin1String("SELECT Name FROM Filter ORDER BY Name"));
while (m_query->next())
list.append(m_query->value(0).toString());
}
return list;
}
QStringList QHelpCollectionHandler::availableComponents() const
{
QStringList list;
if (m_query) {
m_query->exec(QLatin1String("SELECT DISTINCT Name FROM ComponentTable ORDER BY Name"));
while (m_query->next())
list.append(m_query->value(0).toString());
}
return list;
}
QList<QVersionNumber> QHelpCollectionHandler::availableVersions() const
{
QList<QVersionNumber> list;
if (m_query) {
m_query->exec(QLatin1String("SELECT DISTINCT Version FROM VersionTable ORDER BY Version"));
while (m_query->next())
list.append(QVersionNumber::fromString(m_query->value(0).toString()));
}
return list;
}
QMap<QString, QString> QHelpCollectionHandler::namespaceToComponent() const
{
QMap<QString, QString> result;
if (m_query) {
m_query->exec(QLatin1String("SELECT "
"NamespaceTable.Name, "
"ComponentTable.Name "
"FROM NamespaceTable, "
"ComponentTable, "
"ComponentMapping "
"WHERE NamespaceTable.Id = ComponentMapping.NamespaceId "
"AND ComponentMapping.ComponentId = ComponentTable.ComponentId"));
while (m_query->next())
result.insert(m_query->value(0).toString(), m_query->value(1).toString());
}
return result;
}
QMap<QString, QVersionNumber> QHelpCollectionHandler::namespaceToVersion() const
{
QMap<QString, QVersionNumber> result;
if (m_query) {
m_query->exec(QLatin1String("SELECT "
"NamespaceTable.Name, "
"VersionTable.Version "
"FROM NamespaceTable, "
"VersionTable "
"WHERE NamespaceTable.Id = VersionTable.NamespaceId"));
while (m_query->next()) {
result.insert(m_query->value(0).toString(),
QVersionNumber::fromString(m_query->value(1).toString()));
}
}
return result;
}
QHelpFilterData QHelpCollectionHandler::filterData(const QString &filterName) const
{
QStringList components;
QList<QVersionNumber> versions;
if (m_query) {
m_query->prepare(QLatin1String("SELECT ComponentFilter.ComponentName "
"FROM ComponentFilter, Filter "
"WHERE ComponentFilter.FilterId = Filter.FilterId "
"AND Filter.Name = ? "
"ORDER BY ComponentFilter.ComponentName"));
m_query->bindValue(0, filterName);
m_query->exec();
while (m_query->next())
components.append(m_query->value(0).toString());
m_query->prepare(QLatin1String("SELECT VersionFilter.Version "
"FROM VersionFilter, Filter "
"WHERE VersionFilter.FilterId = Filter.FilterId "
"AND Filter.Name = ? "
"ORDER BY VersionFilter.Version"));
m_query->bindValue(0, filterName);
m_query->exec();
while (m_query->next())
versions.append(QVersionNumber::fromString(m_query->value(0).toString()));
}
QHelpFilterData data;
data.setComponents(components);
data.setVersions(versions);
return data;
}
bool QHelpCollectionHandler::setFilterData(const QString &filterName,
const QHelpFilterData &filterData)
{
if (!removeFilter(filterName))
return false;
m_query->prepare(QLatin1String("INSERT INTO Filter "
"VALUES (NULL, ?)"));
m_query->bindValue(0, filterName);
if (!m_query->exec())
return false;
const int filterId = m_query->lastInsertId().toInt();
QVariantList componentList;
QVariantList versionList;
QVariantList filterIdList;
for (const QString &component : filterData.components()) {
componentList.append(component);
filterIdList.append(filterId);
}
m_query->prepare(QLatin1String("INSERT INTO ComponentFilter "
"VALUES (?, ?)"));
m_query->addBindValue(componentList);
m_query->addBindValue(filterIdList);
if (!m_query->execBatch())
return false;
filterIdList.clear();
for (const QVersionNumber &version : filterData.versions()) {
versionList.append(version.isNull() ? QString() : version.toString());
filterIdList.append(filterId);
}
m_query->prepare(QLatin1String("INSERT INTO VersionFilter "
"VALUES (?, ?)"));
m_query->addBindValue(versionList);
m_query->addBindValue(filterIdList);
if (!m_query->execBatch())
return false;
return true;
}
bool QHelpCollectionHandler::removeFilter(const QString &filterName)
{
m_query->prepare(QLatin1String("SELECT FilterId "
"FROM Filter "
"WHERE Name = ?"));
m_query->bindValue(0, filterName);
if (!m_query->exec())
return false;
if (!m_query->next())
return true; // no filter in DB
const int filterId = m_query->value(0).toInt();
m_query->prepare(QLatin1String("DELETE FROM Filter "
"WHERE Filter.Name = ?"));
m_query->bindValue(0, filterName);
if (!m_query->exec())
return false;
m_query->prepare(QLatin1String("DELETE FROM ComponentFilter "
"WHERE ComponentFilter.FilterId = ?"));
m_query->bindValue(0, filterId);
if (!m_query->exec())
return false;
m_query->prepare(QLatin1String("DELETE FROM VersionFilter "
"WHERE VersionFilter.FilterId = ?"));
m_query->bindValue(0, filterId);
if (!m_query->exec())
return false;
return true;
}
bool QHelpCollectionHandler::removeCustomFilter(const QString &filterName)
{
if (!isDBOpened() || filterName.isEmpty())
return false;
int filterNameId = -1;
m_query->prepare(QLatin1String("SELECT Id FROM FilterNameTable WHERE Name=?"));
m_query->bindValue(0, filterName);
m_query->exec();
if (m_query->next())
filterNameId = m_query->value(0).toInt();
if (filterNameId < 0) {
emit error(tr("Unknown filter \"%1\".").arg(filterName));
return false;
}
m_query->prepare(QLatin1String("DELETE FROM FilterTable WHERE NameId=?"));
m_query->bindValue(0, filterNameId);
m_query->exec();
m_query->prepare(QLatin1String("DELETE FROM FilterNameTable WHERE Id=?"));
m_query->bindValue(0, filterNameId);
m_query->exec();
return true;
}
bool QHelpCollectionHandler::addCustomFilter(const QString &filterName,
const QStringList &attributes)
{
if (!isDBOpened() || filterName.isEmpty())
return false;
int nameId = -1;
m_query->prepare(QLatin1String("SELECT Id FROM FilterNameTable WHERE Name=?"));
m_query->bindValue(0, filterName);
m_query->exec();
if (m_query->next())
nameId = m_query->value(0).toInt();
m_query->exec(QLatin1String("SELECT Id, Name FROM FilterAttributeTable"));
QStringList idsToInsert = attributes;
QMap<QString, int> attributeMap;
while (m_query->next()) {
// all old attributes
const QString attributeName = m_query->value(1).toString();
attributeMap.insert(attributeName, m_query->value(0).toInt());
if (idsToInsert.contains(attributeName))
idsToInsert.removeAll(attributeName);
}
for (const QString &id : qAsConst(idsToInsert)) {
m_query->prepare(QLatin1String("INSERT INTO FilterAttributeTable VALUES(NULL, ?)"));
m_query->bindValue(0, id);
m_query->exec();
attributeMap.insert(id, m_query->lastInsertId().toInt());
}
if (nameId < 0) {
m_query->prepare(QLatin1String("INSERT INTO FilterNameTable VALUES(NULL, ?)"));
m_query->bindValue(0, filterName);
if (m_query->exec())
nameId = m_query->lastInsertId().toInt();
}
if (nameId < 0) {
emit error(tr("Cannot register filter %1.").arg(filterName));
return false;
}
m_query->prepare(QLatin1String("DELETE FROM FilterTable WHERE NameId=?"));
m_query->bindValue(0, nameId);
m_query->exec();
for (const QString &att : attributes) {
m_query->prepare(QLatin1String("INSERT INTO FilterTable VALUES(?, ?)"));
m_query->bindValue(0, nameId);
m_query->bindValue(1, attributeMap[att]);
if (!m_query->exec())
return false;
}
return true;
}
QHelpCollectionHandler::FileInfo QHelpCollectionHandler::registeredDocumentation(
const QString &namespaceName) const
{
FileInfo fileInfo;
if (!m_query)
return fileInfo;
m_query->prepare(QLatin1String("SELECT "
"NamespaceTable.Name, "
"NamespaceTable.FilePath, "
"FolderTable.Name "
"FROM "
"NamespaceTable, "
"FolderTable "
"WHERE NamespaceTable.Id = FolderTable.NamespaceId "
"AND NamespaceTable.Name = ? LIMIT 1"));
m_query->bindValue(0, namespaceName);
if (!m_query->exec() || !m_query->next())
return fileInfo;
fileInfo.namespaceName = m_query->value(0).toString();
fileInfo.fileName = m_query->value(1).toString();
fileInfo.folderName = m_query->value(2).toString();
m_query->clear();
return fileInfo;
}
QHelpCollectionHandler::FileInfoList QHelpCollectionHandler::registeredDocumentations() const
{
FileInfoList list;
if (!m_query)
return list;
m_query->exec(QLatin1String("SELECT "
"NamespaceTable.Name, "
"NamespaceTable.FilePath, "
"FolderTable.Name "
"FROM "
"NamespaceTable, "
"FolderTable "
"WHERE NamespaceTable.Id = FolderTable.NamespaceId"));
while (m_query->next()) {
FileInfo fileInfo;
fileInfo.namespaceName = m_query->value(0).toString();
fileInfo.fileName = m_query->value(1).toString();
fileInfo.folderName = m_query->value(2).toString();
list.append(fileInfo);
}
return list;
}
bool QHelpCollectionHandler::registerDocumentation(const QString &fileName)
{
if (!isDBOpened())
return false;
QHelpDBReader reader(fileName, QHelpGlobal::uniquifyConnectionName(
QLatin1String("QHelpCollectionHandler"), this), nullptr);
if (!reader.init()) {
emit error(tr("Cannot open documentation file %1.").arg(fileName));
return false;
}
const QString &ns = reader.namespaceName();
if (ns.isEmpty()) {
emit error(tr("Invalid documentation file \"%1\".").arg(fileName));
return false;
}
const int nsId = registerNamespace(ns, fileName);
if (nsId < 1)
return false;
const int vfId = registerVirtualFolder(reader.virtualFolder(), nsId);
if (vfId < 1)
return false;
registerVersion(reader.version(), nsId);
registerFilterAttributes(reader.filterAttributeSets(), nsId); // qset, what happens when removing documentation?
for (const QString &filterName : reader.customFilters())
addCustomFilter(filterName, reader.filterAttributes(filterName));
if (!registerIndexTable(reader.indexTable(), nsId, vfId, registeredDocumentation(ns).fileName))
return false;
return true;
}
bool QHelpCollectionHandler::unregisterDocumentation(const QString &namespaceName)
{
if (!isDBOpened())
return false;
m_query->prepare(QLatin1String("SELECT Id FROM NamespaceTable WHERE Name = ?"));
m_query->bindValue(0, namespaceName);
m_query->exec();
if (!m_query->next()) {
emit error(tr("The namespace %1 was not registered.").arg(namespaceName));
return false;
}
const int nsId = m_query->value(0).toInt();
m_query->prepare(QLatin1String("DELETE FROM NamespaceTable WHERE Id = ?"));
m_query->bindValue(0, nsId);
if (!m_query->exec())
return false;
m_query->prepare(QLatin1String("SELECT Id FROM FolderTable WHERE NamespaceId = ?"));
m_query->bindValue(0, nsId);
m_query->exec();
if (!m_query->next()) {
emit error(tr("The namespace %1 was not registered.").arg(namespaceName));
return false;
}
const int vfId = m_query->value(0).toInt();
m_query->prepare(QLatin1String("DELETE FROM NamespaceTable WHERE Id = ?"));
m_query->bindValue(0, nsId);
if (!m_query->exec())
return false;
m_query->prepare(QLatin1String("DELETE FROM FolderTable WHERE NamespaceId = ?"));
m_query->bindValue(0, nsId);
if (!m_query->exec())
return false;
if (!unregisterIndexTable(nsId, vfId))
return false;
scheduleVacuum();
return true;
}
static QHelpCollectionHandler::FileInfo extractFileInfo(const QUrl &url)
{
QHelpCollectionHandler::FileInfo fileInfo;
if (!url.isValid() || url.toString().count(QLatin1Char('/')) < 4
|| url.scheme() != QLatin1String("qthelp")) {
return fileInfo;
}
fileInfo.namespaceName = url.authority();
fileInfo.fileName = url.path();
if (fileInfo.fileName.startsWith(QLatin1Char('/')))
fileInfo.fileName = fileInfo.fileName.mid(1);
fileInfo.folderName = fileInfo.fileName.mid(0, fileInfo.fileName.indexOf(QLatin1Char('/'), 1));
fileInfo.fileName.remove(0, fileInfo.folderName.length() + 1);
return fileInfo;
}
bool QHelpCollectionHandler::fileExists(const QUrl &url) const
{
if (!isDBOpened())
return false;
const FileInfo fileInfo = extractFileInfo(url);
if (fileInfo.namespaceName.isEmpty())
return false;
m_query->prepare(QLatin1String("SELECT COUNT (DISTINCT NamespaceTable.Id) "
"FROM "
"FileNameTable, "
"NamespaceTable, "
"FolderTable "
"WHERE FolderTable.Name = ? "
"AND FileNameTable.Name = ? "
"AND FileNameTable.FolderId = FolderTable.Id "
"AND FolderTable.NamespaceId = NamespaceTable.Id"));
m_query->bindValue(0, fileInfo.folderName);
m_query->bindValue(1, fileInfo.fileName);
if (!m_query->exec() || !m_query->next())
return false;
const int count = m_query->value(0).toInt();
m_query->clear();
return count;
}
static QString prepareFilterQuery(const QString &filterName)
{
if (filterName.isEmpty())
return QString();
return QString::fromLatin1(" AND EXISTS(SELECT * FROM Filter WHERE Filter.Name = ?) "
"AND ("
"(NOT EXISTS(" // 1. filter by component
"SELECT * FROM "
"ComponentFilter, "
"Filter "
"WHERE ComponentFilter.FilterId = Filter.FilterId "
"AND Filter.Name = ?) "
"OR NamespaceTable.Id IN ("
"SELECT "
"NamespaceTable.Id "
"FROM "
"NamespaceTable, "
"ComponentTable, "
"ComponentMapping, "
"ComponentFilter, "
"Filter "
"WHERE ComponentMapping.NamespaceId = NamespaceTable.Id "
"AND ComponentTable.ComponentId = ComponentMapping.ComponentId "
"AND ((ComponentTable.Name = ComponentFilter.ComponentName) "
"OR (ComponentTable.Name IS NULL AND ComponentFilter.ComponentName IS NULL)) "
"AND ComponentFilter.FilterId = Filter.FilterId "
"AND Filter.Name = ?))"
" AND "
"(NOT EXISTS(" // 2. filter by version
"SELECT * FROM "
"VersionFilter, "
"Filter "
"WHERE VersionFilter.FilterId = Filter.FilterId "
"AND Filter.Name = ?) "
"OR NamespaceTable.Id IN ("
"SELECT "
"NamespaceTable.Id "
"FROM "
"NamespaceTable, "
"VersionFilter, "
"VersionTable, "
"Filter "
"WHERE VersionFilter.FilterId = Filter.FilterId "
"AND ((VersionFilter.Version = VersionTable.Version) "
"OR (VersionFilter.Version IS NULL AND VersionTable.Version IS NULL)) "
"AND VersionTable.NamespaceId = NamespaceTable.Id "
"AND Filter.Name = ?))"
")");
}
static void bindFilterQuery(QSqlQuery *query, int bindStart, const QString &filterName)
{
if (filterName.isEmpty())
return;
query->bindValue(bindStart, filterName);
query->bindValue(bindStart + 1, filterName);
query->bindValue(bindStart + 2, filterName);
query->bindValue(bindStart + 3, filterName);
query->bindValue(bindStart + 4, filterName);
}
static QString prepareFilterQuery(int attributesCount,
const QString &idTableName,
const QString &idColumnName,
const QString &filterTableName,
const QString &filterColumnName)
{
if (!attributesCount)
return QString();
QString filterQuery = QString::fromLatin1(" AND (%1.%2 IN (").arg(idTableName, idColumnName);
const QString filterQueryTemplate = QString::fromLatin1(
"SELECT %1.%2 "
"FROM %1, FilterAttributeTable "
"WHERE %1.FilterAttributeId = FilterAttributeTable.Id "
"AND FilterAttributeTable.Name = ?")
.arg(filterTableName, filterColumnName);
for (int i = 0; i < attributesCount; ++i) {
if (i > 0)
filterQuery.append(QLatin1String(" INTERSECT "));
filterQuery.append(filterQueryTemplate);
}
filterQuery.append(QLatin1String(") OR NamespaceTable.Id IN ("));
const QString optimizedFilterQueryTemplate = QLatin1String(
"SELECT OptimizedFilterTable.NamespaceId "
"FROM OptimizedFilterTable, FilterAttributeTable "
"WHERE OptimizedFilterTable.FilterAttributeId = FilterAttributeTable.Id "
"AND FilterAttributeTable.Name = ?");
for (int i = 0; i < attributesCount; ++i) {
if (i > 0)
filterQuery.append(QLatin1String(" INTERSECT "));
filterQuery.append(optimizedFilterQueryTemplate);
}
filterQuery.append(QLatin1String("))"));
return filterQuery;
}
void bindFilterQuery(QSqlQuery *query, int startingBindPos, const QStringList &filterAttributes)
{
for (int i = 0; i < 2; ++i) {
for (int j = 0; j < filterAttributes.count(); j++) {
query->bindValue(i * filterAttributes.count() + j + startingBindPos,
filterAttributes.at(j));
}
}
}
QString QHelpCollectionHandler::namespaceForFile(const QUrl &url,
const QStringList &filterAttributes) const
{
if (!isDBOpened())
return QString();
const FileInfo fileInfo = extractFileInfo(url);
if (fileInfo.namespaceName.isEmpty())
return QString();
const QString filterlessQuery = QLatin1String(
"SELECT DISTINCT "
"NamespaceTable.Name "
"FROM "
"FileNameTable, "
"NamespaceTable, "
"FolderTable "
"WHERE FolderTable.Name = ? "
"AND FileNameTable.Name = ? "
"AND FileNameTable.FolderId = FolderTable.Id "
"AND FolderTable.NamespaceId = NamespaceTable.Id");
const QString filterQuery = filterlessQuery
+ prepareFilterQuery(filterAttributes.count(),
QLatin1String("FileNameTable"),
QLatin1String("FileId"),
QLatin1String("FileFilterTable"),
QLatin1String("FileId"));
m_query->prepare(filterQuery);
m_query->bindValue(0, fileInfo.folderName);
m_query->bindValue(1, fileInfo.fileName);
bindFilterQuery(m_query, 2, filterAttributes);
if (!m_query->exec())
return QString();
QVector<QString> namespaceList;
while (m_query->next())
namespaceList.append(m_query->value(0).toString());
if (namespaceList.isEmpty())
return QString();
if (namespaceList.contains(fileInfo.namespaceName))
return fileInfo.namespaceName;
const QString originalVersion = namespaceVersion(fileInfo.namespaceName);
for (const QString &ns : namespaceList) {
const QString nsVersion = namespaceVersion(ns);
if (originalVersion == nsVersion)
return ns;
}
// TODO: still, we may like to return the ns for the highest available version
return namespaceList.first();
}
QString QHelpCollectionHandler::namespaceForFile(const QUrl &url,
const QString &filterName) const
{
if (!isDBOpened())
return QString();
const FileInfo fileInfo = extractFileInfo(url);
if (fileInfo.namespaceName.isEmpty())
return QString();
const QString filterlessQuery = QLatin1String(
"SELECT DISTINCT "
"NamespaceTable.Name "
"FROM "
"FileNameTable, "
"NamespaceTable, "
"FolderTable "
"WHERE FolderTable.Name = ? "
"AND FileNameTable.Name = ? "
"AND FileNameTable.FolderId = FolderTable.Id "
"AND FolderTable.NamespaceId = NamespaceTable.Id");
const QString filterQuery = filterlessQuery
+ prepareFilterQuery(filterName);
m_query->prepare(filterQuery);
m_query->bindValue(0, fileInfo.folderName);
m_query->bindValue(1, fileInfo.fileName);
bindFilterQuery(m_query, 2, filterName);
if (!m_query->exec())
return QString();
QVector<QString> namespaceList;
while (m_query->next())
namespaceList.append(m_query->value(0).toString());
if (namespaceList.isEmpty())
return QString();
if (namespaceList.contains(fileInfo.namespaceName))
return fileInfo.namespaceName;
const QString originalVersion = namespaceVersion(fileInfo.namespaceName);
for (const QString &ns : namespaceList) {
const QString nsVersion = namespaceVersion(ns);
if (originalVersion == nsVersion)
return ns;
}
// TODO: still, we may like to return the ns for the highest available version
return namespaceList.first();
}
QStringList QHelpCollectionHandler::files(const QString &namespaceName,
const QStringList &filterAttributes,
const QString &extensionFilter) const
{
if (!isDBOpened())
return QStringList();
const QString extensionQuery = extensionFilter.isEmpty()
? QString() : QLatin1String(" AND FileNameTable.Name LIKE ?");
const QString filterlessQuery = QLatin1String(
"SELECT "
"FolderTable.Name, "
"FileNameTable.Name "
"FROM "
"FileNameTable, "
"FolderTable, "
"NamespaceTable "
"WHERE FileNameTable.FolderId = FolderTable.Id "
"AND FolderTable.NamespaceId = NamespaceTable.Id "
"AND NamespaceTable.Name = ?") + extensionQuery;
const QString filterQuery = filterlessQuery
+ prepareFilterQuery(filterAttributes.count(),
QLatin1String("FileNameTable"),
QLatin1String("FileId"),
QLatin1String("FileFilterTable"),
QLatin1String("FileId"));
m_query->prepare(filterQuery);
m_query->bindValue(0, namespaceName);
int bindCount = 1;
if (!extensionFilter.isEmpty()) {
m_query->bindValue(bindCount, QString::fromLatin1("%.%1").arg(extensionFilter));
++bindCount;
}
bindFilterQuery(m_query, bindCount, filterAttributes);
if (!m_query->exec())
return QStringList();
QStringList fileNames;
while (m_query->next()) {
fileNames.append(m_query->value(0).toString()
+ QLatin1Char('/')
+ m_query->value(1).toString());
}
return fileNames;
}
QStringList QHelpCollectionHandler::files(const QString &namespaceName,
const QString &filterName,
const QString &extensionFilter) const
{
if (!isDBOpened())
return QStringList();
const QString extensionQuery = extensionFilter.isEmpty()
? QString() : QLatin1String(" AND FileNameTable.Name LIKE ?");
const QString filterlessQuery = QLatin1String(
"SELECT "
"FolderTable.Name, "
"FileNameTable.Name "
"FROM "
"FileNameTable, "
"FolderTable, "
"NamespaceTable "
"WHERE FileNameTable.FolderId = FolderTable.Id "
"AND FolderTable.NamespaceId = NamespaceTable.Id "
"AND NamespaceTable.Name = ?") + extensionQuery;
const QString filterQuery = filterlessQuery
+ prepareFilterQuery(filterName);
m_query->prepare(filterQuery);
m_query->bindValue(0, namespaceName);
int bindCount = 1;
if (!extensionFilter.isEmpty()) {
m_query->bindValue(bindCount, QString::fromLatin1("%.%1").arg(extensionFilter));
++bindCount;
}
bindFilterQuery(m_query, bindCount, filterName);
if (!m_query->exec())
return QStringList();
QStringList fileNames;
while (m_query->next()) {
fileNames.append(m_query->value(0).toString()
+ QLatin1Char('/')
+ m_query->value(1).toString());
}
return fileNames;
}
QUrl QHelpCollectionHandler::findFile(const QUrl &url, const QStringList &filterAttributes) const
{
if (!isDBOpened())
return QUrl();
const QString namespaceName = namespaceForFile(url, filterAttributes);
if (namespaceName.isEmpty())
return QUrl();
QUrl result = url;
result.setAuthority(namespaceName);
return result;
}
QUrl QHelpCollectionHandler::findFile(const QUrl &url, const QString &filterName) const
{
if (!isDBOpened())
return QUrl();
const QString namespaceName = namespaceForFile(url, filterName);
if (namespaceName.isEmpty())
return QUrl();
QUrl result = url;
result.setAuthority(namespaceName);
return result;
}
QByteArray QHelpCollectionHandler::fileData(const QUrl &url) const
{
if (!isDBOpened())
return QByteArray();
const QString namespaceName = namespaceForFile(url, QString());
if (namespaceName.isEmpty())
return QByteArray();
const FileInfo fileInfo = extractFileInfo(url);
const FileInfo docInfo = registeredDocumentation(namespaceName);
const QString absFileName = absoluteDocPath(docInfo.fileName);
QHelpDBReader reader(absFileName, QHelpGlobal::uniquifyConnectionName(
docInfo.fileName, const_cast<QHelpCollectionHandler *>(this)), nullptr);
if (!reader.init())
return QByteArray();
return reader.fileData(fileInfo.folderName, fileInfo.fileName);
}
QStringList QHelpCollectionHandler::indicesForFilter(const QStringList &filterAttributes) const
{
QStringList indices;
if (!isDBOpened())
return indices;
const QString filterlessQuery = QString::fromLatin1(
"SELECT DISTINCT "
"IndexTable.Name "
"FROM "
"IndexTable, "
"FileNameTable, "
"FolderTable, "
"NamespaceTable "
"WHERE IndexTable.FileId = FileNameTable.FileId "
"AND FileNameTable.FolderId = FolderTable.Id "
"AND IndexTable.NamespaceId = NamespaceTable.Id");
const QString filterQuery = filterlessQuery
+ prepareFilterQuery(filterAttributes.count(),
QLatin1String("IndexTable"),
QLatin1String("Id"),
QLatin1String("IndexFilterTable"),
QLatin1String("IndexId"))
+ QLatin1String(" ORDER BY LOWER(IndexTable.Name), IndexTable.Name");
// this doesn't work: ASC COLLATE NOCASE
m_query->prepare(filterQuery);
bindFilterQuery(m_query, 0, filterAttributes);
m_query->exec();
while (m_query->next())
indices.append(m_query->value(0).toString());
return indices;
}
QStringList QHelpCollectionHandler::indicesForFilter(const QString &filterName) const
{
QStringList indices;
if (!isDBOpened())
return indices;
const QString filterlessQuery = QString::fromLatin1(
"SELECT DISTINCT "
"IndexTable.Name "
"FROM "
"IndexTable, "
"FileNameTable, "
"FolderTable, "
"NamespaceTable "
"WHERE IndexTable.FileId = FileNameTable.FileId "
"AND FileNameTable.FolderId = FolderTable.Id "
"AND IndexTable.NamespaceId = NamespaceTable.Id");
const QString filterQuery = filterlessQuery
+ prepareFilterQuery(filterName)
+ QLatin1String(" ORDER BY LOWER(IndexTable.Name), IndexTable.Name");
m_query->prepare(filterQuery);
bindFilterQuery(m_query, 0, filterName);
m_query->exec();
while (m_query->next())
indices.append(m_query->value(0).toString());
return indices;
}
static QString getTitle(const QByteArray &contents)
{
if (!contents.size())
return QString();
int depth = 0;
QString link;
QString title;
QDataStream s(contents);
s >> depth;
s >> link;
s >> title;
return title;
}
QList<QHelpCollectionHandler::ContentsData> QHelpCollectionHandler::contentsForFilter(
const QStringList &filterAttributes) const
{
if (!isDBOpened())
return QList<ContentsData>();
const QString filterlessQuery = QString::fromLatin1(
"SELECT DISTINCT "
"NamespaceTable.Name, "
"FolderTable.Name, "
"ContentsTable.Data, "
"VersionTable.Version "
"FROM "
"FolderTable, "
"NamespaceTable, "
"ContentsTable, "
"VersionTable "
"WHERE ContentsTable.NamespaceId = NamespaceTable.Id "
"AND NamespaceTable.Id = FolderTable.NamespaceId "
"AND ContentsTable.NamespaceId = NamespaceTable.Id "
"AND VersionTable.NamespaceId = NamespaceTable.Id");
const QString filterQuery = filterlessQuery
+ prepareFilterQuery(filterAttributes.count(),
QLatin1String("ContentsTable"),
QLatin1String("Id"),
QLatin1String("ContentsFilterTable"),
QLatin1String("ContentsId"));
m_query->prepare(filterQuery);
bindFilterQuery(m_query, 0, filterAttributes);
m_query->exec();
QMap<QString, QMap<QVersionNumber, ContentsData>> contentsMap;
while (m_query->next()) {
const QString namespaceName = m_query->value(0).toString();
const QByteArray contents = m_query->value(2).toByteArray();
const QString versionString = m_query->value(3).toString();
const QString title = getTitle(contents);
const QVersionNumber version = QVersionNumber::fromString(versionString);
// get existing or insert a new one otherwise
ContentsData &contentsData = contentsMap[title][version];
contentsData.namespaceName = namespaceName;
contentsData.folderName = m_query->value(1).toString();
contentsData.contentsList.append(contents);
}
QList<QHelpCollectionHandler::ContentsData> result;
for (const auto &versionContents : qAsConst(contentsMap)) {
// insert items in the reverse order of version number
const auto itBegin = versionContents.constBegin();
auto it = versionContents.constEnd();
while (it != itBegin) {
--it;
result.append(it.value());
}
}
return result;
}
QList<QHelpCollectionHandler::ContentsData> QHelpCollectionHandler::contentsForFilter(const QString &filterName) const
{
if (!isDBOpened())
return QList<ContentsData>();
const QString filterlessQuery = QString::fromLatin1(
"SELECT DISTINCT "
"NamespaceTable.Name, "
"FolderTable.Name, "
"ContentsTable.Data, "
"VersionTable.Version "
"FROM "
"FolderTable, "
"NamespaceTable, "
"ContentsTable, "
"VersionTable "
"WHERE ContentsTable.NamespaceId = NamespaceTable.Id "
"AND NamespaceTable.Id = FolderTable.NamespaceId "
"AND ContentsTable.NamespaceId = NamespaceTable.Id "
"AND VersionTable.NamespaceId = NamespaceTable.Id");
const QString filterQuery = filterlessQuery
+ prepareFilterQuery(filterName);
m_query->prepare(filterQuery);
bindFilterQuery(m_query, 0, filterName);
m_query->exec();
QMap<QString, QMap<QVersionNumber, ContentsData>> contentsMap;
while (m_query->next()) {
const QString namespaceName = m_query->value(0).toString();
const QByteArray contents = m_query->value(2).toByteArray();
const QString versionString = m_query->value(3).toString();
const QString title = getTitle(contents);
const QVersionNumber version = QVersionNumber::fromString(versionString);
// get existing or insert a new one otherwise
ContentsData &contentsData = contentsMap[title][version];
contentsData.namespaceName = namespaceName;
contentsData.folderName = m_query->value(1).toString();
contentsData.contentsList.append(contents);
}
QList<QHelpCollectionHandler::ContentsData> result;
for (const auto &versionContents : qAsConst(contentsMap)) {
// insert items in the reverse order of version number
const auto itBegin = versionContents.constBegin();
auto it = versionContents.constEnd();
while (it != itBegin) {
--it;
result.append(it.value());
}
}
return result;
}
bool QHelpCollectionHandler::removeCustomValue(const QString &key)
{
if (!isDBOpened())
return false;
m_query->prepare(QLatin1String("DELETE FROM SettingsTable WHERE Key=?"));
m_query->bindValue(0, key);
return m_query->exec();
}
QVariant QHelpCollectionHandler::customValue(const QString &key,
const QVariant &defaultValue) const
{
if (!m_query)
return defaultValue;
m_query->prepare(QLatin1String("SELECT COUNT(Key) FROM SettingsTable WHERE Key=?"));
m_query->bindValue(0, key);
if (!m_query->exec() || !m_query->next() || !m_query->value(0).toInt()) {
m_query->clear();
return defaultValue;
}
m_query->clear();
m_query->prepare(QLatin1String("SELECT Value FROM SettingsTable WHERE Key=?"));
m_query->bindValue(0, key);
if (m_query->exec() && m_query->next()) {
const QVariant &value = m_query->value(0);
m_query->clear();
return value;
}
return defaultValue;
}
bool QHelpCollectionHandler::setCustomValue(const QString &key,
const QVariant &value)
{
if (!isDBOpened())
return false;
m_query->prepare(QLatin1String("SELECT Value FROM SettingsTable WHERE Key=?"));
m_query->bindValue(0, key);
m_query->exec();
if (m_query->next()) {
m_query->prepare(QLatin1String("UPDATE SettingsTable SET Value=? where Key=?"));
m_query->bindValue(0, value);
m_query->bindValue(1, key);
} else {
m_query->prepare(QLatin1String("INSERT INTO SettingsTable VALUES(?, ?)"));
m_query->bindValue(0, key);
m_query->bindValue(1, value);
}
return m_query->exec();
}
bool QHelpCollectionHandler::registerFilterAttributes(const QList<QStringList> &attributeSets,
int nsId)
{
if (!isDBOpened())
return false;
m_query->exec(QLatin1String("SELECT Name FROM FilterAttributeTable"));
QSet<QString> atts;
while (m_query->next())
atts.insert(m_query->value(0).toString());
for (const QStringList &attributeSet : attributeSets) {
for (const QString &attribute : attributeSet) {
if (!atts.contains(attribute)) {
m_query->prepare(QLatin1String("INSERT INTO FilterAttributeTable VALUES(NULL, ?)"));
m_query->bindValue(0, attribute);
m_query->exec();
}
}
}
return registerFileAttributeSets(attributeSets, nsId);
}
bool QHelpCollectionHandler::registerFileAttributeSets(const QList<QStringList> &attributeSets,
int nsId)
{
if (!isDBOpened())
return false;
if (attributeSets.isEmpty())
return true;
QVariantList nsIds;
QVariantList attributeSetIds;
QVariantList filterAttributeIds;
if (!m_query->exec(QLatin1String("SELECT MAX(FilterAttributeSetId) FROM FileAttributeSetTable"))
|| !m_query->next()) {
return false;
}
int attributeSetId = m_query->value(0).toInt();
for (const QStringList &attributeSet : attributeSets) {
++attributeSetId;
for (const QString &attribute : attributeSet) {
m_query->prepare(QLatin1String("SELECT Id FROM FilterAttributeTable WHERE Name=?"));
m_query->bindValue(0, attribute);
if (!m_query->exec() || !m_query->next())
return false;
nsIds.append(nsId);
attributeSetIds.append(attributeSetId);
filterAttributeIds.append(m_query->value(0).toInt());
}
}
m_query->prepare(QLatin1String("INSERT INTO FileAttributeSetTable "
"(NamespaceId, FilterAttributeSetId, FilterAttributeId) "
"VALUES(?, ?, ?)"));
m_query->addBindValue(nsIds);
m_query->addBindValue(attributeSetIds);
m_query->addBindValue(filterAttributeIds);
return m_query->execBatch();
}
QStringList QHelpCollectionHandler::filterAttributes() const
{
QStringList list;
if (m_query) {
m_query->exec(QLatin1String("SELECT Name FROM FilterAttributeTable"));
while (m_query->next())
list.append(m_query->value(0).toString());
}
return list;
}
QStringList QHelpCollectionHandler::filterAttributes(const QString &filterName) const
{
QStringList list;
if (m_query) {
m_query->prepare(QLatin1String(
"SELECT "
"FilterAttributeTable.Name "
"FROM "
"FilterAttributeTable, "
"FilterTable, "
"FilterNameTable "
"WHERE FilterAttributeTable.Id = FilterTable.FilterAttributeId "
"AND FilterTable.NameId = FilterNameTable.Id "
"AND FilterNameTable.Name=?"));
m_query->bindValue(0, filterName);
m_query->exec();
while (m_query->next())
list.append(m_query->value(0).toString());
}
return list;
}
QList<QStringList> QHelpCollectionHandler::filterAttributeSets(const QString &namespaceName) const
{
QList<QStringList> result;
if (!isDBOpened())
return result;
m_query->prepare(QLatin1String(
"SELECT "
"FileAttributeSetTable.FilterAttributeSetId, "
"FilterAttributeTable.Name "
"FROM "
"FileAttributeSetTable, "
"FilterAttributeTable, "
"NamespaceTable "
"WHERE FileAttributeSetTable.FilterAttributeId = FilterAttributeTable.Id "
"AND FileAttributeSetTable.NamespaceId = NamespaceTable.Id "
"AND NamespaceTable.Name = ? "
"ORDER BY FileAttributeSetTable.FilterAttributeSetId"));
m_query->bindValue(0, namespaceName);
m_query->exec();
int oldId = -1;
while (m_query->next()) {
const int id = m_query->value(0).toInt();
if (id != oldId) {
result.append(QStringList());
oldId = id;
}
result.last().append(m_query->value(1).toString());
}
if (result.isEmpty())
result.append(QStringList());
return result;
}
QString QHelpCollectionHandler::namespaceVersion(const QString &namespaceName) const
{
if (!m_query)
return QString();
m_query->prepare(QLatin1String("SELECT "
"VersionTable.Version "
"FROM "
"NamespaceTable, "
"VersionTable "
"WHERE NamespaceTable.Name = ? "
"AND NamespaceTable.Id = VersionTable.NamespaceId"));
m_query->bindValue(0, namespaceName);
if (!m_query->exec() || !m_query->next())
return QString();
const QString ret = m_query->value(0).toString();
m_query->clear();
return ret;
}
int QHelpCollectionHandler::registerNamespace(const QString &nspace, const QString &fileName)
{
const int errorValue = -1;
if (!m_query)
return errorValue;
m_query->prepare(QLatin1String("SELECT COUNT(Id) FROM NamespaceTable WHERE Name=?"));
m_query->bindValue(0, nspace);
m_query->exec();
while (m_query->next()) {
if (m_query->value(0).toInt() > 0) {
emit error(tr("Namespace %1 already exists.").arg(nspace));
return errorValue;
}
}
QFileInfo fi(m_collectionFile);
m_query->prepare(QLatin1String("INSERT INTO NamespaceTable VALUES(NULL, ?, ?)"));
m_query->bindValue(0, nspace);
m_query->bindValue(1, fi.absoluteDir().relativeFilePath(fileName));
int namespaceId = errorValue;
if (m_query->exec()) {
namespaceId = m_query->lastInsertId().toInt();
m_query->clear();
}
if (namespaceId < 1) {
emit error(tr("Cannot register namespace \"%1\".").arg(nspace));
return errorValue;
}
return namespaceId;
}
int QHelpCollectionHandler::registerVirtualFolder(const QString &folderName, int namespaceId)
{
if (!m_query)
return false;
m_query->prepare(QLatin1String("INSERT INTO FolderTable VALUES(NULL, ?, ?)"));
m_query->bindValue(0, namespaceId);
m_query->bindValue(1, folderName);
int virtualId = -1;
if (m_query->exec()) {
virtualId = m_query->lastInsertId().toInt();
m_query->clear();
}
if (virtualId < 1) {
emit error(tr("Cannot register virtual folder '%1'.").arg(folderName));
return -1;
}
if (registerComponent(folderName, namespaceId) < 0)
return -1;
return virtualId;
}
int QHelpCollectionHandler::registerComponent(const QString &componentName, int namespaceId)
{
m_query->prepare(QLatin1String("SELECT ComponentId FROM ComponentTable WHERE Name = ?"));
m_query->bindValue(0, componentName);
if (!m_query->exec())
return -1;
if (!m_query->next()) {
m_query->prepare(QLatin1String("INSERT INTO ComponentTable VALUES(NULL, ?)"));
m_query->bindValue(0, componentName);
if (!m_query->exec())
return -1;
m_query->prepare(QLatin1String("SELECT ComponentId FROM ComponentTable WHERE Name = ?"));
m_query->bindValue(0, componentName);
if (!m_query->exec() || !m_query->next())
return -1;
}
const int componentId = m_query->value(0).toInt();
m_query->prepare(QLatin1String("INSERT INTO ComponentMapping VALUES(?, ?)"));
m_query->bindValue(0, componentId);
m_query->bindValue(1, namespaceId);
if (!m_query->exec())
return -1;
return componentId;
}
bool QHelpCollectionHandler::registerVersion(const QString &version, int namespaceId)
{
if (!m_query)
return false;
m_query->prepare(QLatin1String("INSERT INTO VersionTable "
"(NamespaceId, Version) "
"VALUES(?, ?)"));
m_query->addBindValue(namespaceId);
m_query->addBindValue(version);
return m_query->exec();
}
bool QHelpCollectionHandler::registerIndexAndNamespaceFilterTables(
const QString &nameSpace, bool createDefaultVersionFilter)
{
if (!isDBOpened())
return false;
m_query->prepare(QLatin1String("SELECT Id, FilePath FROM NamespaceTable WHERE Name=?"));
m_query->bindValue(0, nameSpace);
m_query->exec();
if (!m_query->next())
return false;
const int nsId = m_query->value(0).toInt();
const QString fileName = m_query->value(1).toString();
m_query->prepare(QLatin1String("SELECT Id, Name FROM FolderTable WHERE NamespaceId=?"));
m_query->bindValue(0, nsId);
m_query->exec();
if (!m_query->next())
return false;
const int vfId = m_query->value(0).toInt();
const QString vfName = m_query->value(1).toString();
const QString absFileName = absoluteDocPath(fileName);
QHelpDBReader reader(absFileName, QHelpGlobal::uniquifyConnectionName(
fileName, this), this);
if (!reader.init())
return false;
registerComponent(vfName, nsId);
registerVersion(reader.version(), nsId);
if (!registerFileAttributeSets(reader.filterAttributeSets(), nsId))
return false;
if (!registerIndexTable(reader.indexTable(), nsId, vfId, fileName))
return false;
if (createDefaultVersionFilter)
createVersionFilter(reader.version());
return true;
}
void QHelpCollectionHandler::createVersionFilter(const QString &version)
{
if (version.isEmpty())
return;
const QVersionNumber versionNumber = QVersionNumber::fromString(version);
if (versionNumber.isNull())
return;
const QString filterName = tr("Version %1").arg(version);
if (filters().contains(filterName))
return;
QHelpFilterData filterData;
filterData.setVersions(QList<QVersionNumber>() << versionNumber);
setFilterData(filterName, filterData);
}
bool QHelpCollectionHandler::registerIndexTable(const QHelpDBReader::IndexTable &indexTable,
int nsId, int vfId, const QString &fileName)
{
Transaction transaction(m_connectionName);
QMap<QString, QVariantList> filterAttributeToNewFileId;
QVariantList fileFolderIds;
QVariantList fileNames;
QVariantList fileTitles;
const int fileSize = indexTable.fileItems.size();
fileFolderIds.reserve(fileSize);
fileNames.reserve(fileSize);
fileTitles.reserve(fileSize);
if (!m_query->exec(QLatin1String("SELECT MAX(FileId) FROM FileNameTable")) || !m_query->next())
return false;
const int maxFileId = m_query->value(0).toInt();
int newFileId = 0;
for (const QHelpDBReader::FileItem &item : indexTable.fileItems) {
fileFolderIds.append(vfId);
fileNames.append(item.name);
fileTitles.append(item.title);
for (const QString &filterAttribute : item.filterAttributes)
filterAttributeToNewFileId[filterAttribute].append(maxFileId + newFileId + 1);
++newFileId;
}
m_query->prepare(QLatin1String("INSERT INTO FileNameTable VALUES(?, ?, NULL, ?)"));
m_query->addBindValue(fileFolderIds);
m_query->addBindValue(fileNames);
m_query->addBindValue(fileTitles);
if (!m_query->execBatch())
return false;
for (auto it = filterAttributeToNewFileId.cbegin(),
end = filterAttributeToNewFileId.cend(); it != end; ++it) {
const QString filterAttribute = it.key();
m_query->prepare(QLatin1String("SELECT Id From FilterAttributeTable WHERE Name = ?"));
m_query->bindValue(0, filterAttribute);
if (!m_query->exec() || !m_query->next())
return false;
const int attributeId = m_query->value(0).toInt();
QVariantList attributeIds;
for (int i = 0; i < it.value().count(); i++)
attributeIds.append(attributeId);
m_query->prepare(QLatin1String("INSERT INTO FileFilterTable VALUES(?, ?)"));
m_query->addBindValue(attributeIds);
m_query->addBindValue(it.value());
if (!m_query->execBatch())
return false;
}
QMap<QString, QVariantList> filterAttributeToNewIndexId;
if (!m_query->exec(QLatin1String("SELECT MAX(Id) FROM IndexTable")) || !m_query->next())
return false;
const int maxIndexId = m_query->value(0).toInt();
int newIndexId = 0;
QVariantList indexNames;
QVariantList indexIdentifiers;
QVariantList indexNamespaceIds;
QVariantList indexFileIds;
QVariantList indexAnchors;
const int indexSize = indexTable.indexItems.size();
indexNames.reserve(indexSize);
indexIdentifiers.reserve(indexSize);
indexNamespaceIds.reserve(indexSize);
indexFileIds.reserve(indexSize);
indexAnchors.reserve(indexSize);
for (const QHelpDBReader::IndexItem &item : indexTable.indexItems) {
indexNames.append(item.name);
indexIdentifiers.append(item.identifier);
indexNamespaceIds.append(nsId);
indexFileIds.append(maxFileId + item.fileId + 1);
indexAnchors.append(item.anchor);
for (const QString &filterAttribute : item.filterAttributes)
filterAttributeToNewIndexId[filterAttribute].append(maxIndexId + newIndexId + 1);
++newIndexId;
}
m_query->prepare(QLatin1String("INSERT INTO IndexTable VALUES(NULL, ?, ?, ?, ?, ?)"));
m_query->addBindValue(indexNames);
m_query->addBindValue(indexIdentifiers);
m_query->addBindValue(indexNamespaceIds);
m_query->addBindValue(indexFileIds);
m_query->addBindValue(indexAnchors);
if (!m_query->execBatch())
return false;
for (auto it = filterAttributeToNewIndexId.cbegin(),
end = filterAttributeToNewIndexId.cend(); it != end; ++it) {
const QString filterAttribute = it.key();
m_query->prepare(QLatin1String("SELECT Id From FilterAttributeTable WHERE Name = ?"));
m_query->bindValue(0, filterAttribute);
if (!m_query->exec() || !m_query->next())
return false;
const int attributeId = m_query->value(0).toInt();
QVariantList attributeIds;
for (int i = 0; i < it.value().count(); i++)
attributeIds.append(attributeId);
m_query->prepare(QLatin1String("INSERT INTO IndexFilterTable VALUES(?, ?)"));
m_query->addBindValue(attributeIds);
m_query->addBindValue(it.value());
if (!m_query->execBatch())
return false;
}
QMap<QString, QVariantList> filterAttributeToNewContentsId;
QVariantList contentsNsIds;
QVariantList contentsData;
const int contentsSize = indexTable.contentsItems.size();
contentsNsIds.reserve(contentsSize);
contentsData.reserve(contentsSize);
if (!m_query->exec(QLatin1String("SELECT MAX(Id) FROM ContentsTable")) || !m_query->next())
return false;
const int maxContentsId = m_query->value(0).toInt();
int newContentsId = 0;
for (const QHelpDBReader::ContentsItem &item : indexTable.contentsItems) {
contentsNsIds.append(nsId);
contentsData.append(item.data);
for (const QString &filterAttribute : item.filterAttributes) {
filterAttributeToNewContentsId[filterAttribute]
.append(maxContentsId + newContentsId + 1);
}
++newContentsId;
}
m_query->prepare(QLatin1String("INSERT INTO ContentsTable VALUES(NULL, ?, ?)"));
m_query->addBindValue(contentsNsIds);
m_query->addBindValue(contentsData);
if (!m_query->execBatch())
return false;
for (auto it = filterAttributeToNewContentsId.cbegin(),
end = filterAttributeToNewContentsId.cend(); it != end; ++it) {
const QString filterAttribute = it.key();
m_query->prepare(QLatin1String("SELECT Id From FilterAttributeTable WHERE Name = ?"));
m_query->bindValue(0, filterAttribute);
if (!m_query->exec() || !m_query->next())
return false;
const int attributeId = m_query->value(0).toInt();
QVariantList attributeIds;
for (int i = 0; i < it.value().count(); i++)
attributeIds.append(attributeId);
m_query->prepare(QLatin1String("INSERT INTO ContentsFilterTable VALUES(?, ?)"));
m_query->addBindValue(attributeIds);
m_query->addBindValue(it.value());
if (!m_query->execBatch())
return false;
}
QVariantList filterNsIds;
QVariantList filterAttributeIds;
for (const QString &filterAttribute : indexTable.usedFilterAttributes) {
filterNsIds.append(nsId);
m_query->prepare(QLatin1String("SELECT Id From FilterAttributeTable WHERE Name = ?"));
m_query->bindValue(0, filterAttribute);
if (!m_query->exec() || !m_query->next())
return false;
filterAttributeIds.append(m_query->value(0).toInt());
}
m_query->prepare(QLatin1String("INSERT INTO OptimizedFilterTable "
"(NamespaceId, FilterAttributeId) VALUES(?, ?)"));
m_query->addBindValue(filterNsIds);
m_query->addBindValue(filterAttributeIds);
if (!m_query->execBatch())
return false;
m_query->prepare(QLatin1String("INSERT INTO TimeStampTable "
"(NamespaceId, FolderId, FilePath, Size, TimeStamp) "
"VALUES(?, ?, ?, ?, ?)"));
m_query->addBindValue(nsId);
m_query->addBindValue(vfId);
m_query->addBindValue(fileName);
const QFileInfo fi(absoluteDocPath(fileName));
m_query->addBindValue(fi.size());
m_query->addBindValue(fi.lastModified().toString(Qt::ISODate));
if (!m_query->exec())
return false;
transaction.commit();
return true;
}
bool QHelpCollectionHandler::unregisterIndexTable(int nsId, int vfId)
{
m_query->prepare(QLatin1String("DELETE FROM IndexFilterTable WHERE IndexId IN "
"(SELECT Id FROM IndexTable WHERE NamespaceId = ?)"));
m_query->bindValue(0, nsId);
if (!m_query->exec())
return false;
m_query->prepare(QLatin1String("DELETE FROM IndexTable WHERE NamespaceId = ?"));
m_query->bindValue(0, nsId);
if (!m_query->exec())
return false;
m_query->prepare(QLatin1String("DELETE FROM FileFilterTable WHERE FileId IN "
"(SELECT FileId FROM FileNameTable WHERE FolderId = ?)"));
m_query->bindValue(0, vfId);
if (!m_query->exec())
return false;
m_query->prepare(QLatin1String("DELETE FROM FileNameTable WHERE FolderId = ?"));
m_query->bindValue(0, vfId);
if (!m_query->exec())
return false;
m_query->prepare(QLatin1String("DELETE FROM ContentsFilterTable WHERE ContentsId IN "
"(SELECT Id FROM ContentsTable WHERE NamespaceId = ?)"));
m_query->bindValue(0, nsId);
if (!m_query->exec())
return false;
m_query->prepare(QLatin1String("DELETE FROM ContentsTable WHERE NamespaceId = ?"));
m_query->bindValue(0, nsId);
if (!m_query->exec())
return false;
m_query->prepare(QLatin1String("DELETE FROM FileAttributeSetTable WHERE NamespaceId = ?"));
m_query->bindValue(0, nsId);
if (!m_query->exec())
return false;
m_query->prepare(QLatin1String("DELETE FROM OptimizedFilterTable WHERE NamespaceId = ?"));
m_query->bindValue(0, nsId);
if (!m_query->exec())
return false;
m_query->prepare(QLatin1String("DELETE FROM TimeStampTable WHERE NamespaceId = ?"));
m_query->bindValue(0, nsId);
if (!m_query->exec())
return false;
m_query->prepare(QLatin1String("DELETE FROM VersionTable WHERE NamespaceId = ?"));
m_query->bindValue(0, nsId);
if (!m_query->exec())
return false;
m_query->prepare(QLatin1String("SELECT ComponentId FROM ComponentMapping WHERE NamespaceId = ?"));
m_query->bindValue(0, nsId);
if (!m_query->exec())
return false;
if (!m_query->next())
return false;
const int componentId = m_query->value(0).toInt();
m_query->prepare(QLatin1String("DELETE FROM ComponentMapping WHERE NamespaceId = ?"));
m_query->bindValue(0, nsId);
if (!m_query->exec())
return false;
m_query->prepare(QLatin1String("SELECT ComponentId FROM ComponentMapping WHERE ComponentId = ?"));
m_query->bindValue(0, componentId);
if (!m_query->exec())
return false;
if (!m_query->next()) { // no more namespaces refer to the componentId
m_query->prepare(QLatin1String("DELETE FROM ComponentTable WHERE ComponentId = ?"));
m_query->bindValue(0, componentId);
if (!m_query->exec())
return false;
}
return true;
}
static QUrl buildQUrl(const QString &ns, const QString &folder,
const QString &relFileName, const QString &anchor)
{
QUrl url;
url.setScheme(QLatin1String("qthelp"));
url.setAuthority(ns);
url.setPath(QLatin1Char('/') + folder + QLatin1Char('/') + relFileName);
url.setFragment(anchor);
return url;
}
QMap<QString, QUrl> QHelpCollectionHandler::linksForIdentifier(const QString &id,
const QStringList &filterAttributes) const
{
return linksForField(QLatin1String("Identifier"), id, filterAttributes);
}
QMap<QString, QUrl> QHelpCollectionHandler::linksForKeyword(const QString &keyword,
const QStringList &filterAttributes) const
{
return linksForField(QLatin1String("Name"), keyword, filterAttributes);
}
QMap<QString, QUrl> QHelpCollectionHandler::linksForField(const QString &fieldName,
const QString &fieldValue,
const QStringList &filterAttributes) const
{
QMap<QString, QUrl> linkMap;
if (!isDBOpened())
return linkMap;
const QString filterlessQuery = QString::fromLatin1(
"SELECT "
"FileNameTable.Title, "
"NamespaceTable.Name, "
"FolderTable.Name, "
"FileNameTable.Name, "
"IndexTable.Anchor "
"FROM "
"IndexTable, "
"FileNameTable, "
"FolderTable, "
"NamespaceTable "
"WHERE IndexTable.FileId = FileNameTable.FileId "
"AND FileNameTable.FolderId = FolderTable.Id "
"AND IndexTable.NamespaceId = NamespaceTable.Id "
"AND IndexTable.%1 = ?").arg(fieldName);
const QString filterQuery = filterlessQuery
+ prepareFilterQuery(filterAttributes.count(),
QLatin1String("IndexTable"),
QLatin1String("Id"),
QLatin1String("IndexFilterTable"),
QLatin1String("IndexId"));
m_query->prepare(filterQuery);
m_query->bindValue(0, fieldValue);
bindFilterQuery(m_query, 1, filterAttributes);
m_query->exec();
while (m_query->next()) {
QString title = m_query->value(0).toString();
if (title.isEmpty()) // generate a title + corresponding path
title = fieldValue + QLatin1String(" : ") + m_query->value(3).toString();
static_cast<QMultiMap<QString, QUrl> &>(linkMap).insert(title, buildQUrl(
m_query->value(1).toString(),
m_query->value(2).toString(),
m_query->value(3).toString(),
m_query->value(4).toString()));
}
return linkMap;
}
QMap<QString, QUrl> QHelpCollectionHandler::linksForIdentifier(const QString &id,
const QString &filterName) const
{
return linksForField(QLatin1String("Identifier"), id, filterName);
}
QMap<QString, QUrl> QHelpCollectionHandler::linksForKeyword(const QString &keyword,
const QString &filterName) const
{
return linksForField(QLatin1String("Name"), keyword, filterName);
}
QList<QHelpLink> QHelpCollectionHandler::documentsForIdentifier(const QString &id,
const QString &filterName) const
{
return documentsForField(QLatin1String("Identifier"), id, filterName);
}
QList<QHelpLink> QHelpCollectionHandler::documentsForKeyword(const QString &keyword,
const QString &filterName) const
{
return documentsForField(QLatin1String("Name"), keyword, filterName);
}
QMap<QString, QUrl> QHelpCollectionHandler::linksForField(const QString &fieldName,
const QString &fieldValue,
const QString &filterName) const
{
QMap<QString, QUrl> linkMap;
const auto documents = documentsForField(fieldName, fieldValue, filterName);
for (const auto &document : documents)
static_cast<QMultiMap<QString, QUrl> &>(linkMap).insert(document.title, document.url);
return linkMap;
}
QList<QHelpLink> QHelpCollectionHandler::documentsForField(const QString &fieldName,
const QString &fieldValue,
const QString &filterName) const
{
QList<QHelpLink> docList;
if (!isDBOpened())
return docList;
const QString filterlessQuery = QString::fromLatin1(
"SELECT "
"FileNameTable.Title, "
"NamespaceTable.Name, "
"FolderTable.Name, "
"FileNameTable.Name, "
"IndexTable.Anchor "
"FROM "
"IndexTable, "
"FileNameTable, "
"FolderTable, "
"NamespaceTable "
"WHERE IndexTable.FileId = FileNameTable.FileId "
"AND FileNameTable.FolderId = FolderTable.Id "
"AND IndexTable.NamespaceId = NamespaceTable.Id "
"AND IndexTable.%1 = ?").arg(fieldName);
const QString filterQuery = filterlessQuery
+ prepareFilterQuery(filterName)
+ QLatin1String(" ORDER BY LOWER(FileNameTable.Title), FileNameTable.Title");
m_query->prepare(filterQuery);
m_query->bindValue(0, fieldValue);
bindFilterQuery(m_query, 1, filterName);
m_query->exec();
while (m_query->next()) {
QString title = m_query->value(0).toString();
if (title.isEmpty()) // generate a title + corresponding path
title = fieldValue + QLatin1String(" : ") + m_query->value(3).toString();
const QUrl url = buildQUrl(m_query->value(1).toString(),
m_query->value(2).toString(),
m_query->value(3).toString(),
m_query->value(4).toString());
docList.append(QHelpLink {url, title});
}
return docList;
}
QStringList QHelpCollectionHandler::namespacesForFilter(const QString &filterName) const
{
QStringList namespaceList;
if (!isDBOpened())
return namespaceList;
const QString filterlessQuery = QString::fromLatin1(
"SELECT "
"NamespaceTable.Name "
"FROM "
"NamespaceTable "
"WHERE TRUE");
const QString filterQuery = filterlessQuery
+ prepareFilterQuery(filterName);
m_query->prepare(filterQuery);
bindFilterQuery(m_query, 0, filterName);
m_query->exec();
while (m_query->next())
namespaceList.append(m_query->value(0).toString());
return namespaceList;
}
void QHelpCollectionHandler::setReadOnly(bool readOnly)
{
m_readOnly = readOnly;
}
QT_END_NAMESPACE