/**************************************************************************** | |
** | |
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). | |
** All rights reserved. | |
** Contact: Nokia Corporation (qt-info@nokia.com) | |
** | |
** This file is part of the Qt Assistant of the Qt Toolkit. | |
** | |
** $QT_BEGIN_LICENSE:LGPL$ | |
** GNU Lesser General Public License Usage | |
** This file may be used under the terms of the GNU Lesser General Public | |
** License version 2.1 as published by the Free Software Foundation and | |
** appearing in the file LICENSE.LGPL included in the packaging of this | |
** file. Please review the following information to ensure the GNU Lesser | |
** General Public License version 2.1 requirements will be met: | |
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. | |
** | |
** In addition, as a special exception, Nokia gives you certain additional | |
** rights. These rights are described in the Nokia Qt LGPL Exception | |
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. | |
** | |
** GNU General Public License Usage | |
** Alternatively, this file may be used under the terms of the GNU General | |
** Public License version 3.0 as published by the Free Software Foundation | |
** and appearing in the file LICENSE.GPL included in the packaging of this | |
** file. Please review the following information to ensure the GNU General | |
** Public License version 3.0 requirements will be met: | |
** http://www.gnu.org/copyleft/gpl.html. | |
** | |
** Other Usage | |
** Alternatively, this file may be used in accordance with the terms and | |
** conditions contained in a signed written agreement between you and Nokia. | |
** | |
** | |
** | |
** | |
** | |
** $QT_END_LICENSE$ | |
** | |
****************************************************************************/ | |
#include "qhelpcollectionhandler_p.h" | |
#include "qhelp_global.h" | |
#include "qhelpdbreader_p.h" | |
#include <QtCore/QFile> | |
#include <QtCore/QDir> | |
#include <QtCore/QFileInfo> | |
#include <QtCore/QDebug> | |
#include <QtSql/QSqlError> | |
#include <QtSql/QSqlDriver> | |
QT_BEGIN_NAMESPACE | |
QHelpCollectionHandler::QHelpCollectionHandler(const QString &collectionFile, QObject *parent) | |
: QObject(parent) | |
, m_dbOpened(false) | |
, m_collectionFile(collectionFile) | |
, m_connectionName(QString()) | |
{ | |
QFileInfo fi(m_collectionFile); | |
if (!fi.isAbsolute()) | |
m_collectionFile = fi.absoluteFilePath(); | |
m_query.clear(); | |
} | |
QHelpCollectionHandler::~QHelpCollectionHandler() | |
{ | |
m_query.clear(); | |
if (m_dbOpened) | |
QSqlDatabase::removeDatabase(m_connectionName); | |
} | |
bool QHelpCollectionHandler::isDBOpened() | |
{ | |
if (m_dbOpened) | |
return true; | |
emit error(tr("The collection file '%1' is not set up yet!"). | |
arg(m_collectionFile)); | |
return false; | |
} | |
QString QHelpCollectionHandler::collectionFile() const | |
{ | |
return m_collectionFile; | |
} | |
bool QHelpCollectionHandler::openCollectionFile() | |
{ | |
if (m_dbOpened) | |
return m_dbOpened; | |
m_connectionName = QHelpGlobal::uniquifyConnectionName( | |
QLatin1String("QHelpCollectionHandler"), this); | |
bool openingOk = true; | |
{ | |
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()); | |
openingOk = db.open(); | |
if (openingOk) | |
m_query = QSqlQuery(db); | |
} | |
if (!openingOk) { | |
QSqlDatabase::removeDatabase(m_connectionName); | |
emit error(tr("Cannot open collection file: %1").arg(collectionFile())); | |
return false; | |
} | |
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(); | |
if (m_query.value(0).toInt() < 1) { | |
if (!createTables(&m_query)) { | |
emit error(tr("Cannot create tables in file %1!").arg(collectionFile())); | |
return false; | |
} | |
} | |
m_dbOpened = true; | |
return m_dbOpened; | |
} | |
bool QHelpCollectionHandler::copyCollectionFile(const QString &fileName) | |
{ | |
if (!m_dbOpened) | |
return false; | |
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; | |
} | |
QString colFile = fi.absoluteFilePath(); | |
QString connectionName = QHelpGlobal::uniquifyConnectionName( | |
QLatin1String("QHelpCollectionHandlerCopy"), this); | |
QSqlQuery *copyQuery = 0; | |
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)) { | |
emit error(tr("Cannot copy collection file: %1").arg(colFile)); | |
return false; | |
} | |
QString oldBaseDir = QFileInfo(collectionFile()).absolutePath(); | |
QString oldFilePath; | |
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()); | |
oldFilePath = m_query.value(1).toString(); | |
if (!QDir::isAbsolutePath(oldFilePath)) | |
oldFilePath = oldBaseDir + QDir::separator() + 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("CluceneSearchNamespaces")) | |
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) | |
{ | |
QStringList tables; | |
tables << 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 )"); | |
foreach (const QString &q, tables) { | |
if (!query->exec(q)) | |
return false; | |
} | |
return true; | |
} | |
QStringList QHelpCollectionHandler::customFilters() const | |
{ | |
QStringList list; | |
if (m_dbOpened) { | |
m_query.exec(QLatin1String("SELECT Name FROM FilterNameTable")); | |
while (m_query.next()) | |
list.append(m_query.value(0).toString()); | |
} | |
return list; | |
} | |
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()) { | |
attributeMap.insert(m_query.value(1).toString(), | |
m_query.value(0).toInt()); | |
if (idsToInsert.contains(m_query.value(1).toString())) | |
idsToInsert.removeAll(m_query.value(1).toString()); | |
} | |
foreach (const QString &id, 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(); | |
foreach (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::DocInfoList QHelpCollectionHandler::registeredDocumentations() const | |
{ | |
DocInfoList list; | |
if (m_dbOpened) { | |
m_query.exec(QLatin1String("SELECT a.Name, a.FilePath, b.Name " | |
"FROM NamespaceTable a, FolderTable b WHERE a.Id=b.NamespaceId")); | |
while (m_query.next()) { | |
DocInfo info; | |
info.fileName = m_query.value(1).toString(); | |
info.folderName = m_query.value(2).toString(); | |
info.namespaceName = m_query.value(0).toString(); | |
list.append(info); | |
} | |
} | |
return list; | |
} | |
bool QHelpCollectionHandler::registerDocumentation(const QString &fileName) | |
{ | |
if (!isDBOpened()) | |
return false; | |
QHelpDBReader reader(fileName, QHelpGlobal::uniquifyConnectionName( | |
QLatin1String("QHelpCollectionHandler"), this), 0); | |
if (!reader.init()) { | |
emit error(tr("Cannot open documentation file %1!").arg(fileName)); | |
return false; | |
} | |
QString ns = reader.namespaceName(); | |
if (ns.isEmpty()) { | |
emit error(tr("Invalid documentation file '%1'!").arg(fileName)); | |
return false; | |
} | |
int nsId = registerNamespace(ns, fileName); | |
if (nsId < 1) | |
return false; | |
if (!registerVirtualFolder(reader.virtualFolder(), nsId)) | |
return false; | |
addFilterAttributes(reader.filterAttributes()); | |
foreach (const QString &filterName, reader.customFilters()) | |
addCustomFilter(filterName, reader.filterAttributes(filterName)); | |
optimizeDatabase(fileName); | |
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(); | |
int nsId = -1; | |
if (m_query.next()) | |
nsId = m_query.value(0).toInt(); | |
if (nsId < 0) { | |
emit error(tr("The namespace %1 was not registered!").arg(namespaceName)); | |
return false; | |
} | |
m_query.prepare(QLatin1String("DELETE FROM NamespaceTable WHERE Id=?")); | |
m_query.bindValue(0, nsId); | |
m_query.exec(); | |
m_query.prepare(QLatin1String("DELETE FROM FolderTable WHERE NamespaceId=?")); | |
m_query.bindValue(0, nsId); | |
return m_query.exec(); | |
} | |
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 | |
{ | |
QVariant value = defaultValue; | |
if (m_dbOpened) { | |
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()) | |
value = m_query.value(0); | |
m_query.clear(); | |
} | |
return value; | |
} | |
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::addFilterAttributes(const QStringList &attributes) | |
{ | |
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()); | |
foreach (const QString &s, attributes) { | |
if (!atts.contains(s)) { | |
m_query.prepare(QLatin1String("INSERT INTO FilterAttributeTable VALUES(NULL, ?)")); | |
m_query.bindValue(0, s); | |
m_query.exec(); | |
} | |
} | |
return true; | |
} | |
QStringList QHelpCollectionHandler::filterAttributes() const | |
{ | |
QStringList list; | |
if (m_dbOpened) { | |
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_dbOpened) { | |
m_query.prepare(QLatin1String("SELECT a.Name FROM FilterAttributeTable a, " | |
"FilterTable b, FilterNameTable c WHERE a.Id=b.FilterAttributeId " | |
"AND b.NameId=c.Id AND c.Name=?")); | |
m_query.bindValue(0, filterName); | |
m_query.exec(); | |
while (m_query.next()) | |
list.append(m_query.value(0).toString()); | |
} | |
return list; | |
} | |
int QHelpCollectionHandler::registerNamespace(const QString &nspace, const QString &fileName) | |
{ | |
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 -1; | |
} | |
} | |
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 = -1; | |
if (m_query.exec()) | |
namespaceId = m_query.lastInsertId().toInt(); | |
if (namespaceId < 1) { | |
emit error(tr("Cannot register namespace '%1'!").arg(nspace)); | |
return -1; | |
} | |
return namespaceId; | |
} | |
bool QHelpCollectionHandler::registerVirtualFolder(const QString &folderName, int namespaceId) | |
{ | |
m_query.prepare(QLatin1String("INSERT INTO FolderTable VALUES(NULL, ?, ?)")); | |
m_query.bindValue(0, namespaceId); | |
m_query.bindValue(1, folderName); | |
return m_query.exec(); | |
} | |
void QHelpCollectionHandler::optimizeDatabase(const QString &fileName) | |
{ | |
if (!QFile::exists(fileName)) | |
return; | |
{ // according to removeDatabase() documentation | |
QSqlDatabase db = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), QLatin1String("optimize")); | |
db.setDatabaseName(fileName); | |
if (!db.open()) { | |
QSqlDatabase::removeDatabase(QLatin1String("optimize")); | |
emit error(tr("Cannot open database '%1' to optimize!").arg(fileName)); | |
return; | |
} | |
QSqlQuery query(db); | |
db.exec(QLatin1String("PRAGMA synchronous=OFF")); | |
db.exec(QLatin1String("PRAGMA cache_size=3000")); | |
db.exec(QLatin1String("CREATE INDEX IF NOT EXISTS NameIndex ON IndexTable(Name)")); | |
db.exec(QLatin1String("CREATE INDEX IF NOT EXISTS FileNameIndex ON FileNameTable(Name)")); | |
db.exec(QLatin1String("CREATE INDEX IF NOT EXISTS FileIdIndex ON FileNameTable(FileId)")); | |
db.close(); | |
} | |
QSqlDatabase::removeDatabase(QLatin1String("optimize")); | |
} | |
QT_END_NAMESPACE |