diff options
Diffstat (limited to 'tools/assistant/lib')
28 files changed, 664 insertions, 330 deletions
diff --git a/tools/assistant/lib/fulltextsearch/fulltextsearch.pro b/tools/assistant/lib/fulltextsearch/fulltextsearch.pro index e0cd13a..4d2fddb 100644 --- a/tools/assistant/lib/fulltextsearch/fulltextsearch.pro +++ b/tools/assistant/lib/fulltextsearch/fulltextsearch.pro @@ -29,7 +29,7 @@ unix:QMAKE_PKGCONFIG_REQUIRES = QtCore CONFIG(exceptions_off) { CONFIG -= exceptions_off CONFIG += exceptions - !win32|win32-g++ { + !win32|win32-g++* { QMAKE_CFLAGS -= -fno-exceptions QMAKE_CXXFLAGS -= -fno-exceptions QMAKE_LFLAGS -= -fno-exceptions @@ -46,5 +46,5 @@ win32-msvc.net | win32-msvc2* { # the following define could be set globally in case we need it elsewhere solaris* { - DEFINES += Q_SOLARIS_VERSION=$$system(uname -r | sed -e 's/5\.//') + DEFINES += Q_SOLARIS_VERSION=$$system(uname -r | sed -e 's/5\\.//') } diff --git a/tools/assistant/lib/fulltextsearch/qclucene-config_p.h b/tools/assistant/lib/fulltextsearch/qclucene-config_p.h index 0c70718..387d64d 100644 --- a/tools/assistant/lib/fulltextsearch/qclucene-config_p.h +++ b/tools/assistant/lib/fulltextsearch/qclucene-config_p.h @@ -314,14 +314,12 @@ configure. #define _CL_HAVE_SYS_TYPES_H 1 #endif -// Do not use the tchar.h that ships with mingw, this causes the qt build to -// fail (211547, 211401, etc...), reuse the replacement as with any other compiler -// #if defined(__MINGW32__) -// /* Define to 1 if you have the <tchar.h> header file. */ -// # ifndef _CL_HAVE_TCHAR_H -// # define _CL_HAVE_TCHAR_H 1 -// # endif -// #endif +#if defined(__MINGW32__) + /* Define to 1 if you have the <tchar.h> header file. */ + # ifndef _CL_HAVE_TCHAR_H + # define _CL_HAVE_TCHAR_H 1 + # endif +#endif #if defined(__MINGW32__) || defined(__SUNPRO_CC) || defined(__SUNPRO_C) /* Define to 1 if you have the `tell' function. */ diff --git a/tools/assistant/lib/fulltextsearch/qclucene_global_p.h b/tools/assistant/lib/fulltextsearch/qclucene_global_p.h index f4b744d..206725b 100644 --- a/tools/assistant/lib/fulltextsearch/qclucene_global_p.h +++ b/tools/assistant/lib/fulltextsearch/qclucene_global_p.h @@ -36,7 +36,7 @@ #include <QtCore/QChar> #include <QtCore/QString> -#if !defined(_MSC_VER) && defined(_CL_HAVE_WCHAR_H) && defined(_CL_HAVE_WCHAR_T) +#if !defined(_MSC_VER) && !defined(__MINGW32__) && defined(_CL_HAVE_WCHAR_H) && defined(_CL_HAVE_WCHAR_T) # if !defined(TCHAR) # define TCHAR wchar_t # endif diff --git a/tools/assistant/lib/fulltextsearch/qsort_p.h b/tools/assistant/lib/fulltextsearch/qsort_p.h index e6f024b..5c75c58 100644 --- a/tools/assistant/lib/fulltextsearch/qsort_p.h +++ b/tools/assistant/lib/fulltextsearch/qsort_p.h @@ -64,15 +64,15 @@ private: class QHELP_EXPORT QCLuceneSort { public: - QCLuceneSort(); - QCLuceneSort(const QStringList &fieldNames); - QCLuceneSort(const QString &field, bool reverse = false); + QCLuceneSort(); + explicit QCLuceneSort(const QStringList &fieldNames); + explicit QCLuceneSort(const QString &field, bool reverse = false); virtual ~QCLuceneSort(); QString toString() const; void setSort(const QStringList &fieldNames); - void setSort(const QString &field, bool reverse = false); + void setSort(const QString &field, bool reverse = false); protected: friend class QCLuceneHits; diff --git a/tools/assistant/lib/lib.pro b/tools/assistant/lib/lib.pro index 51933de..26d3456 100644 --- a/tools/assistant/lib/lib.pro +++ b/tools/assistant/lib/lib.pro @@ -23,7 +23,6 @@ unix:QMAKE_PKGCONFIG_REQUIRES += QtNetwork \ QtSql \ QtXml LIBS_PRIVATE += -l$$qclucene - RESOURCES += helpsystem.qrc SOURCES += qhelpenginecore.cpp \ qhelpengine.cpp \ @@ -41,6 +40,7 @@ SOURCES += qhelpenginecore.cpp \ qhelpsearchindexwriter_default.cpp \ qhelpsearchindexreader_default.cpp \ qhelpsearchindexreader.cpp \ + qclucenefieldnames.cpp \ qhelp_global.cpp # access to clucene @@ -63,7 +63,8 @@ HEADERS += qhelpenginecore.h \ qhelpsearchindex_default_p.h \ qhelpsearchindexwriter_default_p.h \ qhelpsearchindexreader_default_p.h \ - qhelpsearchindexreader_p.h + qhelpsearchindexreader_p.h \ + qclucenefieldnames_p.h # access to clucene HEADERS += qhelpsearchindexwriter_clucene_p.h \ diff --git a/tools/assistant/lib/qclucenefieldnames.cpp b/tools/assistant/lib/qclucenefieldnames.cpp new file mode 100644 index 0000000..5c3a474 --- /dev/null +++ b/tools/assistant/lib/qclucenefieldnames.cpp @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qclucenefieldnames_p.h" + +QT_BEGIN_NAMESPACE + +namespace fulltextsearch { +namespace clucene { +const QString AttributeField(QLatin1String("attribute")); +const QString ContentField(QLatin1String("content")); +const QString NamespaceField(QLatin1String("namespace")); +const QString PathField(QLatin1String("path")); +const QString TitleField(QLatin1String("title")); +const QString TitleTokenizedField(QLatin1String("titleTokenized")); +} // namespace clucene +} // namespace fulltextsearch + +QT_END_NAMESPACE diff --git a/tools/assistant/lib/qclucenefieldnames_p.h b/tools/assistant/lib/qclucenefieldnames_p.h new file mode 100644 index 0000000..a611382 --- /dev/null +++ b/tools/assistant/lib/qclucenefieldnames_p.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCLUCENEFIELDNAMES_P_H +#define QCLUCENEFIELDNAMES_P_H + +#include <QtCore/QtGlobal> +#include <QtCore/QString> + +QT_BEGIN_NAMESPACE + +namespace fulltextsearch { +namespace clucene { + extern const QString AttributeField; + extern const QString ContentField; + extern const QString NamespaceField; + extern const QString PathField; + extern const QString TitleField; + extern const QString TitleTokenizedField; +} // namespace clucene +} // namespace fulltextsearch + +QT_END_NAMESPACE + +#endif // QCLUCENEFIELDNAMES_P_H diff --git a/tools/assistant/lib/qhelp_global.cpp b/tools/assistant/lib/qhelp_global.cpp index 539b504..c2c916b 100644 --- a/tools/assistant/lib/qhelp_global.cpp +++ b/tools/assistant/lib/qhelp_global.cpp @@ -39,6 +39,7 @@ ** ****************************************************************************/ +#include <QtCore/QCoreApplication> #include <QtCore/QRegExp> #include <QtCore/QMutexLocker> #include <QtGui/QTextDocument> @@ -55,12 +56,12 @@ QString QHelpGlobal::uniquifyConnectionName(const QString &name, void *pointer) counter = 0; return QString::fromLatin1("%1-%2-%3"). - arg(name).arg(long(pointer)).arg(counter); + arg(name).arg(quintptr(pointer)).arg(counter); } QString QHelpGlobal::documentTitle(const QString &content) { - QString title = QObject::tr("Untitled"); + QString title = QCoreApplication::translate("QHelp", "Untitled"); if (!content.isEmpty()) { int start = content.indexOf(QLatin1String("<title>"), 0, Qt::CaseInsensitive) + 7; int end = content.indexOf(QLatin1String("</title>"), 0, Qt::CaseInsensitive); @@ -86,17 +87,18 @@ QString QHelpGlobal::codecFromData(const QByteArray &data) QString QHelpGlobal::codecFromHtmlData(const QByteArray &data) { - QString content = QString::fromUtf8(data.constData(), data.size()); - int start = content.indexOf(QLatin1String("<meta"), 0, Qt::CaseInsensitive); + QString head = QString::fromUtf8(data.constData(), qMin(1000, data.size())); + int start = head.indexOf(QLatin1String("<meta"), 0, Qt::CaseInsensitive); if (start > 0) { - int end; QRegExp r(QLatin1String("charset=([^\"\\s]+)")); while (start != -1) { - end = content.indexOf(QLatin1Char('>'), start) + 1; - const QString &meta = content.mid(start, end - start).toLower(); + const int end = head.indexOf(QLatin1Char('>'), start) + 1; + if (end <= start) + break; + const QString &meta = head.mid(start, end - start).toLower(); if (r.indexIn(meta) != -1) return r.cap(1); - start = content.indexOf(QLatin1String("<meta"), end, + start = head.indexOf(QLatin1String("<meta"), end, Qt::CaseInsensitive); } } @@ -105,8 +107,8 @@ QString QHelpGlobal::codecFromHtmlData(const QByteArray &data) QString QHelpGlobal::codecFromXmlData(const QByteArray &data) { - QString content = QString::fromUtf8(data.constData(), data.size()); + QString head = QString::fromUtf8(data.constData(), qMin(1000, data.size())); const QRegExp encodingExp(QLatin1String("^\\s*<\\?xml version=" "\"\\d\\.\\d\" encoding=\"([^\"]+)\"\\?>.*")); - return encodingExp.exactMatch(content) ? encodingExp.cap(1) : QString(); + return encodingExp.exactMatch(head) ? encodingExp.cap(1) : QString(); } diff --git a/tools/assistant/lib/qhelpcollectionhandler.cpp b/tools/assistant/lib/qhelpcollectionhandler.cpp index cb7e457..bd8dc20 100644 --- a/tools/assistant/lib/qhelpcollectionhandler.cpp +++ b/tools/assistant/lib/qhelpcollectionhandler.cpp @@ -114,6 +114,9 @@ bool QHelpCollectionHandler::openCollectionFile() 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(); @@ -163,6 +166,9 @@ bool QHelpCollectionHandler::copyCollectionFile(const QString &fileName) 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; @@ -308,10 +314,8 @@ bool QHelpCollectionHandler::addCustomFilter(const QString &filterName, m_query.prepare(QLatin1String("SELECT Id FROM FilterNameTable WHERE Name=?")); m_query.bindValue(0, filterName); m_query.exec(); - while (m_query.next()) { + if (m_query.next()) nameId = m_query.value(0).toInt(); - break; - } m_query.exec(QLatin1String("SELECT Id, Name FROM FilterAttributeTable")); QStringList idsToInsert = attributes; @@ -584,6 +588,8 @@ void QHelpCollectionHandler::optimizeDatabase(const QString &fileName) } 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)")); diff --git a/tools/assistant/lib/qhelpcollectionhandler_p.h b/tools/assistant/lib/qhelpcollectionhandler_p.h index 8efe507..8901b06 100644 --- a/tools/assistant/lib/qhelpcollectionhandler_p.h +++ b/tools/assistant/lib/qhelpcollectionhandler_p.h @@ -76,7 +76,8 @@ public: }; typedef QList<DocInfo> DocInfoList; - QHelpCollectionHandler(const QString &collectionFile, QObject *parent = 0); + explicit QHelpCollectionHandler(const QString &collectionFile, + QObject *parent = 0); ~QHelpCollectionHandler(); QString collectionFile() const; diff --git a/tools/assistant/lib/qhelpcontentwidget.cpp b/tools/assistant/lib/qhelpcontentwidget.cpp index a80dc39..6f3f942 100644 --- a/tools/assistant/lib/qhelpcontentwidget.cpp +++ b/tools/assistant/lib/qhelpcontentwidget.cpp @@ -370,7 +370,8 @@ void QHelpContentModel::invalidateContents(bool onShutDown) delete d->rootItem; d->rootItem = 0; } - reset(); + if (!onShutDown) + reset(); } /*! diff --git a/tools/assistant/lib/qhelpdbreader.cpp b/tools/assistant/lib/qhelpdbreader.cpp index 6dd949a..5c0f595 100644 --- a/tools/assistant/lib/qhelpdbreader.cpp +++ b/tools/assistant/lib/qhelpdbreader.cpp @@ -205,7 +205,7 @@ QByteArray QHelpDBReader::fileData(const QString &virtualFolder, "NamespaceTable d WHERE a.Id=b.FileId AND (b.Name=? OR b.Name=?) AND b.FolderId=c.Id " "AND c.Name=? AND c.NamespaceId=d.Id AND d.Name=?")); m_query->bindValue(0, filePath); - m_query->bindValue(1, QLatin1String("./") + filePath); + m_query->bindValue(1, QString(QLatin1String("./") + filePath)); m_query->bindValue(2, virtualFolder); m_query->bindValue(3, m_namespace); m_query->exec(); diff --git a/tools/assistant/lib/qhelpengine.cpp b/tools/assistant/lib/qhelpengine.cpp index 96cf0fd..e8ae31b 100644 --- a/tools/assistant/lib/qhelpengine.cpp +++ b/tools/assistant/lib/qhelpengine.cpp @@ -75,14 +75,15 @@ void QHelpEnginePrivate::init(const QString &collectionFile, { QHelpEngineCorePrivate::init(collectionFile, helpEngineCore); - contentModel = new QHelpContentModel(this); - indexModel = new QHelpIndexModel(this); - - connect(helpEngineCore, SIGNAL(setupFinished()), - this, SLOT(applyCurrentFilter())); - connect(helpEngineCore, SIGNAL(currentFilterChanged(QString)), - this, SLOT(applyCurrentFilter())); - + if (!contentModel) + contentModel = new QHelpContentModel(this); + if (!indexModel) + indexModel = new QHelpIndexModel(this); + + connect(helpEngineCore, SIGNAL(setupFinished()), this, + SLOT(applyCurrentFilter())); + connect(helpEngineCore, SIGNAL(currentFilterChanged(QString)), this, + SLOT(applyCurrentFilter())); } void QHelpEnginePrivate::applyCurrentFilter() diff --git a/tools/assistant/lib/qhelpengine.h b/tools/assistant/lib/qhelpengine.h index 2bdb983..3577f4b 100644 --- a/tools/assistant/lib/qhelpengine.h +++ b/tools/assistant/lib/qhelpengine.h @@ -62,7 +62,7 @@ class QHELP_EXPORT QHelpEngine : public QHelpEngineCore Q_OBJECT public: - QHelpEngine(const QString &collectionFile, QObject *parent = 0); + explicit QHelpEngine(const QString &collectionFile, QObject *parent = 0); ~QHelpEngine(); QHelpContentModel *contentModel() const; diff --git a/tools/assistant/lib/qhelpenginecore.cpp b/tools/assistant/lib/qhelpenginecore.cpp index 066e4d5..d72dfe4 100644 --- a/tools/assistant/lib/qhelpenginecore.cpp +++ b/tools/assistant/lib/qhelpenginecore.cpp @@ -119,7 +119,7 @@ bool QHelpEngineCorePrivate::setup() QHelpDBReader *reader = new QHelpDBReader(absFileName, QHelpGlobal::uniquifyConnectionName(info.fileName, this), this); if (!reader->init()) { - emit q->warning(tr("Cannot open documentation file %1: %2!") + emit q->warning(QHelpEngineCore::tr("Cannot open documentation file %1: %2!") .arg(absFileName, reader->errorMessage())); continue; } @@ -406,8 +406,9 @@ QStringList QHelpEngineCore::customFilters() const /*! Adds the new custom filter \a filterName. The filter attributes - are specified by \a attributes. The function returns false if - the filter can not be added, e.g. when the filter already exists. + are specified by \a attributes. If the filter already exists, + its attribute set is replaced. The function returns true if + the operation succeeded, otherwise it returns false. \sa customFilters(), removeCustomFilter() */ @@ -705,7 +706,7 @@ QVariant QHelpEngineCore::metaData(const QString &documentationFileName, } /*! - Returns a description of the last error that occured. + Returns a description of the last error that occurred. */ QString QHelpEngineCore::error() const { diff --git a/tools/assistant/lib/qhelpenginecore.h b/tools/assistant/lib/qhelpenginecore.h index 118c310..db25c0b 100644 --- a/tools/assistant/lib/qhelpenginecore.h +++ b/tools/assistant/lib/qhelpenginecore.h @@ -65,7 +65,7 @@ class QHELP_EXPORT QHelpEngineCore : public QObject Q_PROPERTY(QString currentFilter READ currentFilter WRITE setCurrentFilter) public: - QHelpEngineCore(const QString &collectionFile, QObject *parent = 0); + explicit QHelpEngineCore(const QString &collectionFile, QObject *parent = 0); virtual ~QHelpEngineCore(); bool setupData(); diff --git a/tools/assistant/lib/qhelpgenerator.cpp b/tools/assistant/lib/qhelpgenerator.cpp index 4b94ebf..f94031b 100644 --- a/tools/assistant/lib/qhelpgenerator.cpp +++ b/tools/assistant/lib/qhelpgenerator.cpp @@ -47,6 +47,7 @@ #include <QtCore/QFileInfo> #include <QtCore/QDir> #include <QtCore/QDebug> +#include <QtCore/QSet> #include <QtCore/QVariant> #include <QtCore/QDateTime> #include <QtCore/QTextCodec> @@ -190,6 +191,9 @@ bool QHelpGenerator::generate(QHelpDataInterface *helpData, return false; } + d->query->exec(QLatin1String("PRAGMA synchronous=OFF")); + d->query->exec(QLatin1String("PRAGMA cache_size=3000")); + addProgress(1.0); createTables(); insertFileNotFoundFile(); @@ -537,7 +541,8 @@ bool QHelpGenerator::insertFiles(const QStringList &files, const QString &rootPa } int fileId = -1; - if (!d->fileMap.contains(fileName)) { + QMap<QString, int>::Iterator fileMapIt = d->fileMap.find(fileName); + if (fileMapIt == d->fileMap.end()) { fileDataList.append(qCompress(data)); fileNameData.name = fileName; @@ -551,18 +556,20 @@ bool QHelpGenerator::insertFiles(const QStringList &files, const QString &rootPa ++tableFileId; } else { - fileId = d->fileMap.value(fileName); + fileId = fileMapIt.value(); + QSet<int> &fileFilterSet = d->fileFilterMap[fileId]; + QSet<int> &tmpFileFilterSet = tmpFileFilterMap[fileId]; foreach (const int &filter, filterAtts) { - if (!d->fileFilterMap.value(fileId).contains(filter) - && !tmpFileFilterMap.value(fileId).contains(filter)) { - d->fileFilterMap[fileId].insert(filter); - tmpFileFilterMap[fileId].insert(filter); + if (!fileFilterSet.contains(filter) + && !tmpFileFilterSet.contains(filter)) { + fileFilterSet.insert(filter); + tmpFileFilterSet.insert(filter); } } } } - if (tmpFileFilterMap.count()) { + if (!tmpFileFilterMap.isEmpty()) { d->query->exec(QLatin1String("BEGIN")); QMap<int, QSet<int> >::const_iterator it = tmpFileFilterMap.constBegin(); while (it != tmpFileFilterMap.constEnd()) { @@ -625,8 +632,7 @@ bool QHelpGenerator::registerCustomFilter(const QString &filterName, while (d->query->next()) { attributeMap.insert(d->query->value(1).toString(), d->query->value(0).toInt()); - if (idsToInsert.contains(d->query->value(1).toString())) - idsToInsert.removeAll(d->query->value(1).toString()); + idsToInsert.removeAll(d->query->value(1).toString()); } foreach (const QString &id, idsToInsert) { @@ -674,7 +680,7 @@ bool QHelpGenerator::registerCustomFilter(const QString &filterName, return true; } -bool QHelpGenerator::insertKeywords(const QList<QHelpDataIndexItem> keywords, +bool QHelpGenerator::insertKeywords(const QList<QHelpDataIndexItem> &keywords, const QStringList &filterAttributes) { if (!d->query) @@ -704,7 +710,18 @@ bool QHelpGenerator::insertKeywords(const QList<QHelpDataIndexItem> keywords, int i = 0; d->query->exec(QLatin1String("BEGIN")); + QSet<QString> indices; foreach (const QHelpDataIndexItem &itm, keywords) { + // Identical ids make no sense and just confuse the Assistant user, + // so we ignore all repetitions. + if (indices.contains(itm.identifier)) + continue; + + // Still empty ids should be ignored, as otherwise we will include only + // the first keyword with an empty id. + if (!itm.identifier.isEmpty()) + indices.insert(itm.identifier); + pos = itm.reference.indexOf(QLatin1Char('#')); fileName = itm.reference.left(pos); if (pos > -1) @@ -716,8 +733,9 @@ bool QHelpGenerator::insertKeywords(const QList<QHelpDataIndexItem> keywords, if (fName.startsWith(QLatin1String("./"))) fName = fName.mid(2); - if (d->fileMap.contains(fName)) - fileId = d->fileMap.value(fName); + QMap<QString, int>::ConstIterator it = d->fileMap.find(fName); + if (it != d->fileMap.end()) + fileId = it.value(); else fileId = 1; @@ -749,7 +767,7 @@ bool QHelpGenerator::insertKeywords(const QList<QHelpDataIndexItem> keywords, d->query->exec(QLatin1String("COMMIT")); d->query->exec(QLatin1String("SELECT COUNT(Id) FROM IndexTable")); - if (d->query->next() && d->query->value(0).toInt() >= keywords.count()) + if (d->query->next() && d->query->value(0).toInt() >= indices.count()) return true; return false; } @@ -824,4 +842,68 @@ bool QHelpGenerator::insertMetaData(const QMap<QString, QVariant> &metaData) return true; } +bool QHelpGenerator::checkLinks(const QHelpDataInterface &helpData) +{ + /* + * Step 1: Gather the canoncal file paths of all files in the project. + * We use a set, because there will be a lot of look-ups. + */ + QSet<QString> files; + foreach (const QHelpDataFilterSection &filterSection, helpData.filterSections()) { + foreach (const QString &file, filterSection.files()) { + QFileInfo fileInfo(helpData.rootPath() + QDir::separator() + file); + const QString &canonicalFileName = fileInfo.canonicalFilePath(); + if (!fileInfo.exists()) + emit warning(tr("File '%1' does not exist.").arg(file)); + else + files.insert(canonicalFileName); + } + } + + /* + * Step 2: Check the hypertext and image references of all HTML files. + * Note that we don't parse the files, but simply grep for the + * respective HTML elements. Therefore. contents that are e.g. + * commented out can cause false warning. + */ + bool allLinksOk = true; + foreach (const QString &fileName, files) { + if (!fileName.endsWith(QLatin1String("html")) + && !fileName.endsWith(QLatin1String("htm"))) + continue; + QFile htmlFile(fileName); + if (!htmlFile.open(QIODevice::ReadOnly)) { + emit warning(tr("File '%1' cannot be opened.").arg(fileName)); + continue; + } + const QRegExp linkPattern(QLatin1String("<(?:a href|img src)=\"?([^#\">]+)[#\">]")); + QTextStream stream(&htmlFile); + const QString codec = QHelpGlobal::codecFromData(htmlFile.read(1000)); + stream.setCodec(QTextCodec::codecForName(codec.toLatin1().constData())); + const QString &content = stream.readAll(); + QStringList invalidLinks; + for (int pos = linkPattern.indexIn(content); pos != -1; + pos = linkPattern.indexIn(content, pos + 1)) { + const QString& linkedFileName = linkPattern.cap(1); + if (linkedFileName.contains(QLatin1String("://"))) + continue; + const QString curDir = QFileInfo(fileName).dir().path(); + const QString &canonicalLinkedFileName = + QFileInfo(curDir + QDir::separator() + linkedFileName).canonicalFilePath(); + if (!files.contains(canonicalLinkedFileName) + && !invalidLinks.contains(canonicalLinkedFileName)) { + emit warning(tr("File '%1' contains an invalid link to file '%2'"). + arg(fileName).arg(linkedFileName)); + allLinksOk = false; + invalidLinks.append(canonicalLinkedFileName); + } + } + } + + if (!allLinksOk) + d->error = tr("Invalid links in HTML files."); + return allLinksOk; +} + QT_END_NAMESPACE + diff --git a/tools/assistant/lib/qhelpgenerator_p.h b/tools/assistant/lib/qhelpgenerator_p.h index c589e25..823a07a 100644 --- a/tools/assistant/lib/qhelpgenerator_p.h +++ b/tools/assistant/lib/qhelpgenerator_p.h @@ -74,6 +74,7 @@ public: bool generate(QHelpDataInterface *helpData, const QString &outputFileName); + bool checkLinks(const QHelpDataInterface &helpData); QString error() const; Q_SIGNALS: @@ -96,7 +97,7 @@ private: const QStringList &filterAttribs, bool forceUpdate = false); bool registerVirtualFolder(const QString &folderName, const QString &ns); bool insertFilterAttributes(const QStringList &attributes); - bool insertKeywords(const QList<QHelpDataIndexItem> keywords, + bool insertKeywords(const QList<QHelpDataIndexItem> &keywords, const QStringList &filterAttributes); bool insertFiles(const QStringList &files, const QString &rootPath, const QStringList &filterAttributes); diff --git a/tools/assistant/lib/qhelpindexwidget.cpp b/tools/assistant/lib/qhelpindexwidget.cpp index 270bcdd..11b9966 100644 --- a/tools/assistant/lib/qhelpindexwidget.cpp +++ b/tools/assistant/lib/qhelpindexwidget.cpp @@ -244,7 +244,8 @@ void QHelpIndexModel::invalidateIndex(bool onShutDown) disconnect(this, SLOT(insertIndices())); d->indexProvider->stopCollecting(); d->indices.clear(); - filter(QString()); + if (!onShutDown) + filter(QString()); } /*! diff --git a/tools/assistant/lib/qhelpprojectdata.cpp b/tools/assistant/lib/qhelpprojectdata.cpp index 869a446..5247847 100644 --- a/tools/assistant/lib/qhelpprojectdata.cpp +++ b/tools/assistant/lib/qhelpprojectdata.cpp @@ -41,11 +41,13 @@ #include "qhelpprojectdata_p.h" +#include <QtCore/QCoreApplication> #include <QtCore/QDir> #include <QtCore/QFileInfo> #include <QtCore/QStack> #include <QtCore/QMap> #include <QtCore/QRegExp> +#include <QtCore/QUrl> #include <QtCore/QVariant> #include <QtXml/QXmlStreamReader> @@ -76,13 +78,14 @@ private: void readFiles(); void raiseUnknownTokenError(); void addMatchingFiles(const QString &pattern); + bool hasValidSyntax(const QString &nameSpace, const QString &vFolder) const; QMap<QString, QStringList> dirEntriesCache; }; void QHelpProjectDataPrivate::raiseUnknownTokenError() { - raiseError(QObject::tr("Unknown token.")); + raiseError(QCoreApplication::translate("QHelpProject", "Unknown token.")); } void QHelpProjectDataPrivate::readData(const QByteArray &contents) @@ -95,12 +98,14 @@ void QHelpProjectDataPrivate::readData(const QByteArray &contents) && attributes().value(QLatin1String("version")) == QLatin1String("1.0")) readProject(); else - raiseError(QObject::tr("Unknown token. Expected \"QtHelpProject\"!")); + raiseError(QCoreApplication::translate("QHelpProject", + "Unknown token. Expected \"QtHelpProject\"!")); } } if (hasError()) { - raiseError(QObject::tr("Error in line %1: %2").arg(lineNumber()) + raiseError(QCoreApplication::translate("QHelpProject", + "Error in line %1: %2").arg(lineNumber()) .arg(errorString())); } } @@ -112,12 +117,14 @@ void QHelpProjectDataPrivate::readProject() if (isStartElement()) { if (name() == QLatin1String("virtualFolder")) { virtualFolder = readElementText(); - if (virtualFolder.contains(QLatin1String("/"))) - raiseError(QObject::tr("A virtual folder must not contain a \'/\' character!")); + if (!hasValidSyntax(QLatin1String("test"), virtualFolder)) + raiseError(QCoreApplication::translate("QHelpProject", + "Virtual folder has invalid syntax.")); } else if (name() == QLatin1String("namespace")) { namespaceName = readElementText(); - if (namespaceName.contains(QLatin1String("/"))) - raiseError(QObject::tr("A namespace must not contain a \'/\' character!")); + if (!hasValidSyntax(namespaceName, QLatin1String("test"))) + raiseError(QCoreApplication::translate("QHelpProject", + "Namespace has invalid syntax.")); } else if (name() == QLatin1String("customFilter")) { readCustomFilter(); } else if (name() == QLatin1String("filterSection")) { @@ -125,17 +132,21 @@ void QHelpProjectDataPrivate::readProject() } else if (name() == QLatin1String("metaData")) { QString n = attributes().value(QLatin1String("name")).toString(); if (!metaData.contains(n)) - metaData[n] = attributes().value(QLatin1String("value")).toString(); + metaData[n] + = attributes().value(QLatin1String("value")).toString(); else - metaData.insert(n, attributes().value(QLatin1String("value")).toString()); + metaData.insert(n, attributes(). + value(QLatin1String("value")).toString()); } else { raiseUnknownTokenError(); } } else if (isEndElement() && name() == QLatin1String("QtHelpProject")) { if (namespaceName.isEmpty()) - raiseError(QObject::tr("Missing namespace in QtHelpProject.")); + raiseError(QCoreApplication::translate("QHelpProject", + "Missing namespace in QtHelpProject.")); else if (virtualFolder.isEmpty()) - raiseError(QObject::tr("Missing virtual folder in QtHelpProject")); + raiseError(QCoreApplication::translate("QHelpProject", + "Missing virtual folder in QtHelpProject")); break; } } @@ -223,12 +234,14 @@ void QHelpProjectDataPrivate::readKeywords() if (attributes().value(QLatin1String("ref")).toString().isEmpty() || (attributes().value(QLatin1String("name")).toString().isEmpty() && attributes().value(QLatin1String("id")).toString().isEmpty())) - raiseError(QObject::tr("Missing attribute in keyword at line %1.") - .arg(lineNumber())); - filterSectionList.last().addIndex( - QHelpDataIndexItem(attributes().value(QLatin1String("name")).toString(), - attributes().value(QLatin1String("id")).toString(), - attributes().value(QLatin1String("ref")).toString())); + raiseError(QCoreApplication::translate("QHelpProject", + "Missing attribute in keyword at line %1.") + .arg(lineNumber())); + filterSectionList.last() + .addIndex(QHelpDataIndexItem(attributes(). + value(QLatin1String("name")).toString(), + attributes().value(QLatin1String("id")).toString(), + attributes().value(QLatin1String("ref")).toString())); } else { raiseUnknownTokenError(); } @@ -305,6 +318,24 @@ void QHelpProjectDataPrivate::addMatchingFiles(const QString &pattern) filterSectionList.last().addFile(pattern); } +bool QHelpProjectDataPrivate::hasValidSyntax(const QString &nameSpace, + const QString &vFolder) const +{ + const QLatin1Char slash('/'); + if (nameSpace.contains(slash) || vFolder.contains(slash)) + return false; + QUrl url; + const QLatin1String scheme("qthelp"); + url.setScheme(scheme); + const QString canonicalNamespace = nameSpace.toLower(); + url.setHost(canonicalNamespace); + url.setPath(vFolder); + + const QString expectedUrl(scheme + QLatin1String("://") + + canonicalNamespace + slash + vFolder); + return url.isValid() && url.toString() == expectedUrl; +} + /*! \internal \class QHelpProjectData @@ -346,8 +377,8 @@ bool QHelpProjectData::readData(const QString &fileName) d->rootPath = QFileInfo(fileName).absolutePath(); QFile file(fileName); if (!file.open(QIODevice::ReadOnly)) { - d->errorMsg = QObject::tr("The input file %1 could not be opened!") - .arg(fileName); + d->errorMsg = QCoreApplication::translate("QHelpProject", + "The input file %1 could not be opened!").arg(fileName); return false; } diff --git a/tools/assistant/lib/qhelpsearchengine.cpp b/tools/assistant/lib/qhelpsearchengine.cpp index 9914efa..164163a 100644 --- a/tools/assistant/lib/qhelpsearchengine.cpp +++ b/tools/assistant/lib/qhelpsearchengine.cpp @@ -263,17 +263,17 @@ private: engines setupFinished() signal to know when it can start to index documentation. After starting the indexing process the signal indexingStarted() is emitted and - on the end of the indexing process the indexingFinished() is emited. To stop + on the end of the indexing process the indexingFinished() is emitted. To stop the indexing one can call cancelIndexing(). While the indexing process has finished, the search engine can now be used to search thru its index for a given term. To do this one may use the possibility of creating the QHelpSearchQuery list by self or reuse the QHelpSearchQueryWidget which has the inbuild - functionality to set up a proper search querys list that get's passed to the search engines + functionality to set up a proper search queries list that get's passed to the search engines search() function. After the list of querys has been passed to the search engine, the signal searchingStarted() - is emited and after the search has finished the searchingFinished() signal is emited. The + is emitted and after the search has finished the searchingFinished() signal is emitted. The search process can be stopped by calling cancelSearching(). If the search succeeds, the searchingFinished() will be called with the search hits count, @@ -318,7 +318,7 @@ private: uses the given \a helpEngine to access the documentation that needs to be indexed. The QHelpEngine's setupFinished() signal is automatically connected to the QHelpSearchEngine's indexing function, so that new documentation will be indexed - after the signal is emited. + after the signal is emitted. */ QHelpSearchEngine::QHelpSearchEngine(QHelpEngineCore *helpEngine, QObject *parent) : QObject(parent) @@ -434,7 +434,7 @@ void QHelpSearchEngine::cancelSearching() } /*! - Starts the search process using the given list of querys \a queryList + Starts the search process using the given list of queries \a queryList build by the search field name and the values to search for. */ void QHelpSearchEngine::search(const QList<QHelpSearchQuery> &queryList) diff --git a/tools/assistant/lib/qhelpsearchengine.h b/tools/assistant/lib/qhelpsearchengine.h index 545360c..0b07920 100644 --- a/tools/assistant/lib/qhelpsearchengine.h +++ b/tools/assistant/lib/qhelpsearchengine.h @@ -80,7 +80,8 @@ class QHELP_EXPORT QHelpSearchEngine : public QObject Q_OBJECT public: - QHelpSearchEngine(QHelpEngineCore *helpEngine, QObject *parent = 0); + explicit QHelpSearchEngine(QHelpEngineCore *helpEngine, + QObject *parent = 0); ~QHelpSearchEngine(); QHelpSearchQueryWidget* queryWidget(); diff --git a/tools/assistant/lib/qhelpsearchindexreader_clucene.cpp b/tools/assistant/lib/qhelpsearchindexreader_clucene.cpp index b050361..75cc2b9 100644 --- a/tools/assistant/lib/qhelpsearchindexreader_clucene.cpp +++ b/tools/assistant/lib/qhelpsearchindexreader_clucene.cpp @@ -39,16 +39,19 @@ ** ****************************************************************************/ -#include "qhelpenginecore.h" -#include "fulltextsearch/qsearchable_p.h" -#include "fulltextsearch/qqueryparser_p.h" #include "fulltextsearch/qindexreader_p.h" +#include "fulltextsearch/qqueryparser_p.h" +#include "fulltextsearch/qsearchable_p.h" +#include "qclucenefieldnames_p.h" +#include "qhelpenginecore.h" + #include "qhelpsearchindexreader_clucene_p.h" #include <QtCore/QDir> #include <QtCore/QSet> #include <QtCore/QString> #include <QtCore/QFileInfo> +#include <QtCore/QSharedPointer> #include <QtCore/QStringList> #include <QtCore/QTextStream> #include <QtCore/QMutexLocker> @@ -107,64 +110,88 @@ void QHelpSearchIndexReaderClucene::run() #if !defined(QT_NO_EXCEPTIONS) try { #endif - QCLuceneBooleanQuery booleanQuery; + QCLuceneBooleanQuery booleanQueryTitle; + QCLuceneBooleanQuery booleanQueryContent; QCLuceneStandardAnalyzer analyzer; - if (!buildQuery(booleanQuery, queryList, analyzer)) { + const QStringList& attribList = + engine.filterAttributes(engine.currentFilter()); + bool titleQueryIsValid = buildQuery(queryList, TitleTokenizedField, + attribList, booleanQueryTitle, analyzer); + bool contentQueryIsValid = buildQuery(queryList, ContentField, + attribList, booleanQueryContent, analyzer); + if (!titleQueryIsValid && !contentQueryIsValid) { emit searchingFinished(0); return; } - const QStringList attribList = engine.filterAttributes(engine.currentFilter()); - if (!attribList.isEmpty()) { - QCLuceneQuery* query = QCLuceneQueryParser::parse(QLatin1String("+") - + attribList.join(QLatin1String(" +")), QLatin1String("attribute"), analyzer); + QCLuceneIndexSearcher indexSearcher(indexPath); - if (!query) { + // QCLuceneHits object must be allocated on the heap, because + // there is no default constructor. + QSharedPointer<QCLuceneHits> titleHits; + QSharedPointer<QCLuceneHits> contentHits; + if (titleQueryIsValid) { + titleHits = QSharedPointer<QCLuceneHits>(new QCLuceneHits( + indexSearcher.search(booleanQueryTitle))); + } + if (contentQueryIsValid) { + contentHits = QSharedPointer<QCLuceneHits>(new QCLuceneHits( + indexSearcher.search(booleanQueryContent))); + } + bool boost = true; + if ((titleHits.isNull() || titleHits->length() == 0) + && (contentHits.isNull() || contentHits->length() == 0)) { + booleanQueryTitle = QCLuceneBooleanQuery(); + booleanQueryContent = QCLuceneBooleanQuery(); + titleQueryIsValid = + buildTryHarderQuery(queryList, TitleTokenizedField, + attribList, booleanQueryTitle, analyzer); + contentQueryIsValid = + buildTryHarderQuery(queryList, ContentField, attribList, + booleanQueryContent, analyzer); + if (!titleQueryIsValid && !contentQueryIsValid) { emit searchingFinished(0); return; } - booleanQuery.add(query, true, true, false); - } - - QCLuceneIndexSearcher indexSearcher(indexPath); - QCLuceneHits hits = indexSearcher.search(booleanQuery); - - bool boost = true; - QCLuceneBooleanQuery tryHarderQuery; - if (hits.length() == 0) { - if (buildTryHarderQuery(tryHarderQuery, queryList, analyzer)) { - if (!attribList.isEmpty()) { - QCLuceneQuery* query = QCLuceneQueryParser::parse(QLatin1String("+") - + attribList.join(QLatin1String(" +")), QLatin1String("attribute"), - analyzer); - tryHarderQuery.add(query, true, true, false); - } - hits = indexSearcher.search(tryHarderQuery); - boost = (hits.length() == 0); + if (titleQueryIsValid) { + titleHits = QSharedPointer<QCLuceneHits>(new QCLuceneHits( + indexSearcher.search(booleanQueryTitle))); } + if (contentQueryIsValid) { + contentHits = QSharedPointer<QCLuceneHits>(new QCLuceneHits( + indexSearcher.search(booleanQueryContent))); + } + boost = false; } + QList<QSharedPointer<QCLuceneHits> > cluceneHitsList; + if (!titleHits.isNull()) + cluceneHitsList.append(titleHits); + if (!contentHits.isNull()) + cluceneHitsList.append(contentHits); QSet<QString> pathSet; QCLuceneDocument document; const QStringList namespaceList = engine.registeredDocumentations(); - for (qint32 i = 0; i < hits.length(); i++) { - document = hits.document(i); - const QString path = document.get(QLatin1String("path")); - if (!pathSet.contains(path) && namespaceList.contains( - document.get(QLatin1String("namespace")), Qt::CaseInsensitive)) { - pathSet.insert(path); - hitList.append(qMakePair(path, document.get(QLatin1String("title")))); - } - document.clear(); + foreach (const QSharedPointer<QCLuceneHits> &hits, cluceneHitsList) { + for (qint32 i = 0; i < hits->length(); i++) { + document = hits->document(i); + const QString path = document.get(PathField); + if (!pathSet.contains(path) && namespaceList.contains( + document.get(NamespaceField), Qt::CaseInsensitive)) { + pathSet.insert(path); + hitList.append(qMakePair(path, document.get(TitleField))); + } + document.clear(); - mutex.lock(); - if (m_cancel) { + mutex.lock(); + if (m_cancel) { + mutex.unlock(); + emit searchingFinished(0); + return; + } mutex.unlock(); - emit searchingFinished(0); - return; } - mutex.unlock(); } indexSearcher.close(); @@ -184,150 +211,211 @@ void QHelpSearchIndexReaderClucene::run() } } -bool QHelpSearchIndexReaderClucene::defaultQuery(const QString &term, QCLuceneBooleanQuery &booleanQuery, - QCLuceneStandardAnalyzer &analyzer) +bool QHelpSearchIndexReaderClucene::buildQuery( + const QList<QHelpSearchQuery> &queries, const QString &fieldName, + const QStringList &filterAttributes, QCLuceneBooleanQuery &booleanQuery, + QCLuceneAnalyzer &analyzer) { - const QLatin1String c("content"); - const QLatin1String t("titleTokenized"); - - QCLuceneQuery *query = QCLuceneQueryParser::parse(term, c, analyzer); - QCLuceneQuery *query2 = QCLuceneQueryParser::parse(term, t, analyzer); - if (query && query2) { - booleanQuery.add(query, true, false, false); - booleanQuery.add(query2, true, false, false); - return true; + bool queryIsValid = false; + foreach (const QHelpSearchQuery &query, queries) { + if (fieldName != ContentField && isNegativeQuery(query)) { + queryIsValid = false; + break; + } + switch (query.fieldName) { + case QHelpSearchQuery::FUZZY: + if (addFuzzyQuery(query, fieldName, booleanQuery, analyzer)) + queryIsValid = true; + break; + case QHelpSearchQuery::WITHOUT: + if (fieldName != ContentField) + return false; + if (addWithoutQuery(query, fieldName, booleanQuery)) + queryIsValid = true; + break; + case QHelpSearchQuery::PHRASE: + if (addPhraseQuery(query, fieldName, booleanQuery)) + queryIsValid = true; + break; + case QHelpSearchQuery::ALL: + if (addAllQuery(query, fieldName, booleanQuery)) + queryIsValid = true; + break; + case QHelpSearchQuery::DEFAULT: + if (addDefaultQuery(query, fieldName, true, booleanQuery, analyzer)) + queryIsValid = true; + break; + case QHelpSearchQuery::ATLEAST: + if (addAtLeastQuery(query, fieldName, booleanQuery, analyzer)) + queryIsValid = true; + break; + default: + Q_ASSERT(!"Invalid field name"); + } + } + + if (queryIsValid && !filterAttributes.isEmpty()) { + queryIsValid = + addAttributesQuery(filterAttributes, booleanQuery, analyzer); } - return false; + return queryIsValid; } -bool QHelpSearchIndexReaderClucene::buildQuery(QCLuceneBooleanQuery &booleanQuery, - const QList<QHelpSearchQuery> &queryList, QCLuceneStandardAnalyzer &analyzer) +bool QHelpSearchIndexReaderClucene::buildTryHarderQuery( + const QList<QHelpSearchQuery> &queries, const QString &fieldName, + const QStringList &filterAttributes, QCLuceneBooleanQuery &booleanQuery, + QCLuceneAnalyzer &analyzer) { - foreach (const QHelpSearchQuery query, queryList) { - switch (query.fieldName) { - case QHelpSearchQuery::FUZZY: { - const QLatin1String fuzzy("~"); - foreach (const QString &term, query.wordList) { - if (term.isEmpty() - || !defaultQuery(term.toLower() + fuzzy, booleanQuery, analyzer)) { - return false; - } - } - } break; - - case QHelpSearchQuery::WITHOUT: { - QStringList stopWords = QCLuceneStopAnalyzer().englishStopWords(); - foreach (const QString &term, query.wordList) { - if (stopWords.contains(term, Qt::CaseInsensitive)) - continue; - - QCLuceneQuery *query = new QCLuceneTermQuery(QCLuceneTerm( - QLatin1String("content"), term.toLower())); - QCLuceneQuery *query2 = new QCLuceneTermQuery(QCLuceneTerm( - QLatin1String("titleTokenized"), term.toLower())); - - if (query && query2) { - booleanQuery.add(query, true, false, true); - booleanQuery.add(query2, true, false, true); - } else { - return false; - } - } - } break; - - case QHelpSearchQuery::PHRASE: { - const QString &term = query.wordList.at(0).toLower(); - if (term.contains(QLatin1Char(' '))) { - QStringList termList = term.split(QLatin1String(" ")); - QCLucenePhraseQuery *q = new QCLucenePhraseQuery(); - QStringList stopWords = QCLuceneStopAnalyzer().englishStopWords(); - foreach (const QString &term, termList) { - if (!stopWords.contains(term, Qt::CaseInsensitive)) - q->addTerm(QCLuceneTerm(QLatin1String("content"), term.toLower())); - } - booleanQuery.add(q, true, true, false); - } else { - QCLuceneQuery *query = new QCLuceneTermQuery(QCLuceneTerm( - QLatin1String("content"), term.toLower())); - QCLuceneQuery *query2 = new QCLuceneTermQuery(QCLuceneTerm( - QLatin1String("titleTokenized"), term.toLower())); - - if (query && query2) { - booleanQuery.add(query, true, true, false); - booleanQuery.add(query2, true, false, false); - } else { - return false; - } - } - } break; + if (queries.isEmpty()) + return false; + const QHelpSearchQuery &query = queries.front(); + if (query.fieldName != QHelpSearchQuery::DEFAULT) + return false; + if (isNegativeQuery(query)) + return false; + if (!addDefaultQuery(query, fieldName, false, booleanQuery, analyzer)) + return false; + if (filterAttributes.isEmpty()) + return true; + return addAttributesQuery(filterAttributes, booleanQuery, analyzer); +} - case QHelpSearchQuery::ALL: { - QStringList stopWords = QCLuceneStopAnalyzer().englishStopWords(); - foreach (const QString &term, query.wordList) { - if (stopWords.contains(term, Qt::CaseInsensitive)) - continue; +bool QHelpSearchIndexReaderClucene::isNegativeQuery(const QHelpSearchQuery &query) const +{ + const QString &search = query.wordList.join(" "); + return search.contains('!') || search.contains('-') + || search.contains(QLatin1String(" NOT ")); +} - QCLuceneQuery *query = new QCLuceneTermQuery(QCLuceneTerm( - QLatin1String("content"), term.toLower())); +bool QHelpSearchIndexReaderClucene::addFuzzyQuery(const QHelpSearchQuery &query, + const QString &fieldName, QCLuceneBooleanQuery &booleanQuery, + QCLuceneAnalyzer &analyzer) +{ + bool queryIsValid = false; + const QLatin1String fuzzy("~"); + foreach (const QString &term, query.wordList) { + if (!term.isEmpty()) { + QCLuceneQuery *lQuery = + QCLuceneQueryParser::parse(term + fuzzy, fieldName, analyzer); + if (lQuery != 0) { + booleanQuery.add(lQuery, true, false, false); + queryIsValid = true; + } + } + } + return queryIsValid; +} - if (query) { - booleanQuery.add(query, true, true, false); - } else { - return false; - } - } - } break; +bool QHelpSearchIndexReaderClucene::addWithoutQuery(const QHelpSearchQuery &query, + const QString &fieldName, QCLuceneBooleanQuery &booleanQuery) +{ + bool queryIsValid = false; + const QStringList &stopWords = QCLuceneStopAnalyzer().englishStopWords(); + foreach (const QString &term, query.wordList) { + if (stopWords.contains(term, Qt::CaseInsensitive)) + continue; + QCLuceneQuery *lQuery = new QCLuceneTermQuery(QCLuceneTerm( + fieldName, term.toLower())); + booleanQuery.add(lQuery, true, false, true); + queryIsValid = true; + } + return queryIsValid; +} - case QHelpSearchQuery::DEFAULT: { - foreach (const QString &term, query.wordList) { - QCLuceneQuery *query = QCLuceneQueryParser::parse(term.toLower(), - QLatin1String("content"), analyzer); +bool QHelpSearchIndexReaderClucene::addPhraseQuery(const QHelpSearchQuery &query, + const QString &fieldName, QCLuceneBooleanQuery &booleanQuery) +{ + bool queryIsValid = false; + const QString &term = query.wordList.at(0).toLower(); + if (term.contains(QLatin1Char(' '))) { + const QStringList termList = term.split(QLatin1String(" ")); + QCLucenePhraseQuery *q = new QCLucenePhraseQuery(); + const QStringList stopWords = QCLuceneStopAnalyzer().englishStopWords(); + foreach (const QString &term, termList) { + if (!stopWords.contains(term, Qt::CaseInsensitive)) + q->addTerm(QCLuceneTerm(fieldName, term.toLower())); + } + if (!q->getTerms().isEmpty()) { + booleanQuery.add(q, true, true, false); + queryIsValid = true; + } + } else { + QCLuceneQuery *lQuery = new QCLuceneTermQuery(QCLuceneTerm( + fieldName, term.toLower())); + booleanQuery.add(lQuery, true, true, false); + queryIsValid = true; + } + return queryIsValid; +} - if (query) - booleanQuery.add(query, true, true, false); - } - } break; +bool QHelpSearchIndexReaderClucene::addAllQuery(const QHelpSearchQuery &query, + const QString &fieldName, QCLuceneBooleanQuery &booleanQuery) +{ + bool queryIsValid = false; + const QStringList &stopWords = QCLuceneStopAnalyzer().englishStopWords(); + foreach (const QString &term, query.wordList) { + if (stopWords.contains(term, Qt::CaseInsensitive)) + continue; + QCLuceneQuery *lQuery = new QCLuceneTermQuery(QCLuceneTerm( + fieldName, term.toLower())); + booleanQuery.add(lQuery, true, true, false); + queryIsValid = true; + } + return queryIsValid; +} - case QHelpSearchQuery::ATLEAST: { - foreach (const QString &term, query.wordList) { - if (term.isEmpty() || !defaultQuery(term.toLower(), booleanQuery, analyzer)) - return false; - } - } +bool QHelpSearchIndexReaderClucene::addDefaultQuery(const QHelpSearchQuery &query, + const QString &fieldName, bool allTermsRequired, + QCLuceneBooleanQuery &booleanQuery, + QCLuceneAnalyzer &analyzer) +{ + bool queryIsValid = false; + foreach (const QString &term, query.wordList) { + QCLuceneQuery *lQuery = + QCLuceneQueryParser::parse(term.toLower(), fieldName, analyzer); + if (lQuery) { + booleanQuery.add(lQuery, true, allTermsRequired, false); + queryIsValid = true; } } - - return true; + return queryIsValid; } -bool QHelpSearchIndexReaderClucene::buildTryHarderQuery(QCLuceneBooleanQuery &booleanQuery, - const QList<QHelpSearchQuery> &queryList, QCLuceneStandardAnalyzer &analyzer) +bool QHelpSearchIndexReaderClucene::addAtLeastQuery( + const QHelpSearchQuery &query, const QString &fieldName, + QCLuceneBooleanQuery &booleanQuery, QCLuceneAnalyzer &analyzer) { - bool retVal = false; - foreach (const QHelpSearchQuery query, queryList) { - switch (query.fieldName) { - default: break; - case QHelpSearchQuery::DEFAULT: { - foreach (const QString &term, query.wordList) { - QCLuceneQuery *query = QCLuceneQueryParser::parse(term.toLower(), - QLatin1String("content"), analyzer); - - if (query) { - retVal = true; - booleanQuery.add(query, true, false, false); - } - } - } break; + bool queryIsValid = false; + foreach (const QString &term, query.wordList) { + if (!term.isEmpty()) { + QCLuceneQuery *lQuery = + QCLuceneQueryParser::parse(term, fieldName, analyzer); + if (lQuery) { + booleanQuery.add(lQuery, true, false, false); + queryIsValid = true; + } } } - return retVal; + return queryIsValid; +} + +bool QHelpSearchIndexReaderClucene::addAttributesQuery( + const QStringList &filterAttributes, QCLuceneBooleanQuery &booleanQuery, + QCLuceneAnalyzer &analyzer) +{ + QCLuceneQuery* lQuery = QCLuceneQueryParser::parse(QLatin1String("+") + + filterAttributes.join(QLatin1String(" +")), AttributeField, analyzer); + if (!lQuery) + return false; + booleanQuery.add(lQuery, true, true, false); + return true; } void QHelpSearchIndexReaderClucene::boostSearchHits(const QHelpEngineCore &engine, QList<QHelpSearchEngine::SearchHit> &hitList, const QList<QHelpSearchQuery> &queryList) { - foreach (const QHelpSearchQuery query, queryList) { + foreach (const QHelpSearchQuery &query, queryList) { if (query.fieldName != QHelpSearchQuery::DEFAULT) continue; @@ -335,21 +423,22 @@ void QHelpSearchIndexReaderClucene::boostSearchHits(const QHelpEngineCore &engin QCLuceneStandardAnalyzer analyzer; QCLuceneQuery *parsedQuery = QCLuceneQueryParser::parse( - joinedQuery, QLatin1String("content"), analyzer); + joinedQuery, ContentField, analyzer); if (parsedQuery) { joinedQuery = parsedQuery->toString(); delete parsedQuery; } - int length = QString(QLatin1String("content:")).length(); - int index = joinedQuery.indexOf(QLatin1String("content:")); + const QString contentString(ContentField + QLatin1String(":")); + int length = contentString.length(); + int index = joinedQuery.indexOf(contentString); QString term; int nextIndex = 0; QStringList searchTerms; while (index != -1) { - nextIndex = joinedQuery.indexOf(QLatin1String("content:"), index + 1); + nextIndex = joinedQuery.indexOf(contentString, index + 1); term = joinedQuery.mid(index + length, nextIndex - (length + index)).simplified(); if (term.startsWith(QLatin1String("\"")) && term.endsWith(QLatin1String("\""))) { diff --git a/tools/assistant/lib/qhelpsearchindexreader_clucene_p.h b/tools/assistant/lib/qhelpsearchindexreader_clucene_p.h index 8f51cb1..7f53733 100644 --- a/tools/assistant/lib/qhelpsearchindexreader_clucene_p.h +++ b/tools/assistant/lib/qhelpsearchindexreader_clucene_p.h @@ -53,10 +53,13 @@ // We mean it. // -#include "qhelpsearchindexreader_p.h" +#include <QtCore/QList> +#include <QtCore/QString> +#include <QtCore/QStringList> #include "fulltextsearch/qanalyzer_p.h" #include "fulltextsearch/qquery_p.h" +#include "qhelpsearchindexreader_p.h" QT_BEGIN_NAMESPACE @@ -73,14 +76,34 @@ public: private: void run(); - bool defaultQuery(const QString &term, QCLuceneBooleanQuery &booleanQuery, - QCLuceneStandardAnalyzer &analyzer); - bool buildQuery(QCLuceneBooleanQuery &booleanQuery, const QList<QHelpSearchQuery> &queryList, - QCLuceneStandardAnalyzer &analyzer); - bool buildTryHarderQuery(QCLuceneBooleanQuery &booleanQuery, - const QList<QHelpSearchQuery> &queryList, QCLuceneStandardAnalyzer &analyzer); void boostSearchHits(const QHelpEngineCore &engine, QList<QHelpSearchEngine::SearchHit> &hitList, const QList<QHelpSearchQuery> &queryList); + bool buildQuery(const QList<QHelpSearchQuery> &queries, + const QString &fieldName, + const QStringList &filterAttributes, + QCLuceneBooleanQuery &booleanQuery, + QCLuceneAnalyzer &analyzer); + bool buildTryHarderQuery(const QList<QHelpSearchQuery> &queries, + const QString &fieldName, + const QStringList &filterAttributes, + QCLuceneBooleanQuery &booleanQuery, + QCLuceneAnalyzer &analyzer); + bool addFuzzyQuery(const QHelpSearchQuery &query, const QString &fieldName, + QCLuceneBooleanQuery &booleanQuery, QCLuceneAnalyzer &analyzer); + bool addWithoutQuery(const QHelpSearchQuery &query, const QString &fieldName, + QCLuceneBooleanQuery &booleanQuery); + bool addPhraseQuery(const QHelpSearchQuery &query, const QString &fieldName, + QCLuceneBooleanQuery &booleanQuery); + bool addAllQuery(const QHelpSearchQuery &query, const QString &fieldName, + QCLuceneBooleanQuery &booleanQuery); + bool addDefaultQuery(const QHelpSearchQuery &query, const QString &fieldName, + bool allTermsRequired, QCLuceneBooleanQuery &booleanQuery, + QCLuceneAnalyzer &analyzer); + bool addAtLeastQuery(const QHelpSearchQuery &query, const QString &fieldName, + QCLuceneBooleanQuery &booleanQuery, QCLuceneAnalyzer &analyzer); + bool addAttributesQuery(const QStringList &filterAttributes, + QCLuceneBooleanQuery &booleanQuery, QCLuceneAnalyzer &analyzer); + bool isNegativeQuery(const QHelpSearchQuery &query) const; }; } // namespace clucene diff --git a/tools/assistant/lib/qhelpsearchindexreader_default_p.h b/tools/assistant/lib/qhelpsearchindexreader_default_p.h index b30fa4b..27764db 100644 --- a/tools/assistant/lib/qhelpsearchindexreader_default_p.h +++ b/tools/assistant/lib/qhelpsearchindexreader_default_p.h @@ -61,9 +61,6 @@ QT_BEGIN_NAMESPACE -struct Entry; -struct PosEntry; - namespace fulltextsearch { namespace std { diff --git a/tools/assistant/lib/qhelpsearchindexwriter_clucene.cpp b/tools/assistant/lib/qhelpsearchindexwriter_clucene.cpp index 4f9fa3f..4baa376 100644 --- a/tools/assistant/lib/qhelpsearchindexwriter_clucene.cpp +++ b/tools/assistant/lib/qhelpsearchindexwriter_clucene.cpp @@ -39,6 +39,7 @@ ** ****************************************************************************/ +#include "qclucenefieldnames_p.h" #include "qhelpenginecore.h" #include "qhelp_global.h" #include "fulltextsearch/qhits_p.h" @@ -406,17 +407,17 @@ public: QString parsedTitle = QHelpGlobal::documentTitle(data); if(!parsedData.isEmpty()) { - document->add(new QCLuceneField(QLatin1String("content"), + document->add(new QCLuceneField(ContentField, parsedData,QCLuceneField::INDEX_TOKENIZED)); - document->add(new QCLuceneField(QLatin1String("path"), fileName, + document->add(new QCLuceneField(PathField, fileName, QCLuceneField::STORE_YES | QCLuceneField::INDEX_UNTOKENIZED)); - document->add(new QCLuceneField(QLatin1String("title"), parsedTitle, + document->add(new QCLuceneField(TitleField, parsedTitle, QCLuceneField::STORE_YES | QCLuceneField::INDEX_UNTOKENIZED)); - document->add(new QCLuceneField(QLatin1String("titleTokenized"), parsedTitle, + document->add(new QCLuceneField(TitleTokenizedField, parsedTitle, QCLuceneField::STORE_YES | QCLuceneField::INDEX_TOKENIZED)); - document->add(new QCLuceneField(QLatin1String("namespace"), namespaceName, + document->add(new QCLuceneField(NamespaceField, namespaceName, QCLuceneField::STORE_YES | QCLuceneField::INDEX_UNTOKENIZED)); - document->add(new QCLuceneField(QLatin1String("attribute"), attributes, + document->add(new QCLuceneField(AttributeField, attributes, QCLuceneField::STORE_YES | QCLuceneField::INDEX_TOKENIZED)); return true; } @@ -715,9 +716,7 @@ void QHelpSearchIndexWriter::run() if (indexMap.contains(namespaceName)) { // make sure we really have content indexed for namespace - // NOTE: Extra variable just for GCC 3.3.5 - QLatin1String key("namespace"); - QCLuceneTermQuery query(QCLuceneTerm(key, namespaceName)); + QCLuceneTermQuery query(QCLuceneTerm(NamespaceField, namespaceName)); QCLuceneIndexSearcher indexSearcher(indexPath); QCLuceneHits hits = indexSearcher.search(query); if (hits.length() <= 0) @@ -851,8 +850,7 @@ void QHelpSearchIndexWriter::removeDocuments(const QString &indexPath, return; QCLuceneIndexReader reader = QCLuceneIndexReader::open(indexPath); - reader.deleteDocuments(QCLuceneTerm(QLatin1String("namespace"), - namespaceName)); + reader.deleteDocuments(QCLuceneTerm(NamespaceField, namespaceName)); reader.close(); } diff --git a/tools/assistant/lib/qhelpsearchquerywidget.cpp b/tools/assistant/lib/qhelpsearchquerywidget.cpp index 3c3919e..1634a0d 100644 --- a/tools/assistant/lib/qhelpsearchquerywidget.cpp +++ b/tools/assistant/lib/qhelpsearchquerywidget.cpp @@ -41,8 +41,6 @@ #include "qhelpsearchquerywidget.h" -#include <QtCore/QDebug> - #include <QtCore/QAbstractListModel> #include <QtCore/QObject> #include <QtCore/QStringList> @@ -101,8 +99,9 @@ private: }; QHelpSearchQueryWidgetPrivate() - : QObject(), simpleSearch(true), - searchCompleter(new CompleterModel(this), this) + : QObject() + , simpleSearch(true) + , searchCompleter(new CompleterModel(this), this) { searchButton = 0; advancedSearchWidget = 0; @@ -136,33 +135,6 @@ private: #endif } - QString escapeString(const QString &text) - { - QString retValue = text; - const QString escape(QLatin1String("\\")); - QStringList escapableCharsList; - escapableCharsList << QLatin1String("\\") << QLatin1String("+") - << QLatin1String("-") << QLatin1String("!") << QLatin1String("(") - << QLatin1String(")") << QLatin1String(":") << QLatin1String("^") - << QLatin1String("[") << QLatin1String("]") << QLatin1String("{") - << QLatin1String("}") << QLatin1String("~"); - - // make sure we won't end up with an empty string - foreach (const QString &escapeChar, escapableCharsList) { - if (retValue.contains(escapeChar)) - retValue.replace(escapeChar, QLatin1String("")); - } - if (retValue.trimmed().isEmpty()) - return retValue; - - retValue = text; // now really escape the string... - foreach (const QString &escapeChar, escapableCharsList) { - if (retValue.contains(escapeChar)) - retValue.replace(escapeChar, escape + escapeChar); - } - return retValue; - } - QStringList buildTermList(const QString query) { bool s = false; @@ -222,8 +194,8 @@ private: } } - void nextOrPrevQuery(int maxOrMinIndex, int addend, - QToolButton *thisButton, QToolButton *otherButton) + void nextOrPrevQuery(int maxOrMinIndex, int addend, QToolButton *thisButton, + QToolButton *otherButton) { QueryHistory *queryHist; QList<QLineEdit *> lineEdits; @@ -233,7 +205,7 @@ private: } else { queryHist = &complexQueries; lineEdits << allQuery << atLeastQuery << similarQuery - << withoutQuery << exactQuery; + << withoutQuery << exactQuery; } foreach (QLineEdit *lineEdit, lineEdits) lineEdit->clear(); @@ -278,11 +250,11 @@ private: void enableOrDisableToolButtons() { - const QueryHistory &queryHist = - simpleSearch ? simpleQueries : complexQueries; + const QueryHistory &queryHist = simpleSearch ? simpleQueries + : complexQueries; prevQueryButton->setEnabled(queryHist.curQuery > 0); - nextQueryButton->setEnabled(queryHist.curQuery < - queryHist.queries.size() - 1); + nextQueryButton->setEnabled(queryHist.curQuery + < queryHist.queries.size() - 1); } private slots: @@ -306,41 +278,45 @@ private slots: QList<QHelpSearchQuery> queryList; #if !defined(QT_CLUCENE_SUPPORT) queryList.append(QHelpSearchQuery(QHelpSearchQuery::DEFAULT, - QStringList(defaultQuery->text()))); + QStringList(defaultQuery->text()))); #else if (defaultQuery->isEnabled()) { queryList.append(QHelpSearchQuery(QHelpSearchQuery::DEFAULT, - buildTermList(escapeString(defaultQuery->text())))); + buildTermList(defaultQuery->text()))); } else { const QRegExp exp(QLatin1String("\\s+")); - QStringList lst = similarQuery->text().split(exp, QString::SkipEmptyParts); + QStringList lst = similarQuery->text().split(exp, + QString::SkipEmptyParts); if (!lst.isEmpty()) { QStringList fuzzy; foreach (const QString &term, lst) - fuzzy += buildTermList(escapeString(term)); - queryList.append(QHelpSearchQuery(QHelpSearchQuery::FUZZY, fuzzy)); + fuzzy += buildTermList(term); + queryList.append(QHelpSearchQuery(QHelpSearchQuery::FUZZY, + fuzzy)); } lst = withoutQuery->text().split(exp, QString::SkipEmptyParts); if (!lst.isEmpty()) { QStringList without; foreach (const QString &term, lst) - without.append(escapeString(term)); - queryList.append(QHelpSearchQuery(QHelpSearchQuery::WITHOUT, without)); + without.append(term); + queryList.append(QHelpSearchQuery(QHelpSearchQuery::WITHOUT, + without)); } if (!exactQuery->text().isEmpty()) { QString phrase = exactQuery->text().remove(QLatin1Char('\"')); - phrase = escapeString(phrase.simplified()); - queryList.append(QHelpSearchQuery(QHelpSearchQuery::PHRASE, QStringList(phrase))); + phrase = phrase.simplified(); + queryList.append(QHelpSearchQuery(QHelpSearchQuery::PHRASE, + QStringList(phrase))); } lst = allQuery->text().split(exp, QString::SkipEmptyParts); if (!lst.isEmpty()) { QStringList all; foreach (const QString &term, lst) - all.append(escapeString(term)); + all.append(term); queryList.append(QHelpSearchQuery(QHelpSearchQuery::ALL, all)); } @@ -348,8 +324,9 @@ private slots: if (!lst.isEmpty()) { QStringList atLeast; foreach (const QString &term, lst) - atLeast += buildTermList(escapeString(term)); - queryList.append(QHelpSearchQuery(QHelpSearchQuery::ATLEAST, atLeast)); + atLeast += buildTermList(term); + queryList.append(QHelpSearchQuery(QHelpSearchQuery::ATLEAST, + atLeast)); } } #endif @@ -363,8 +340,9 @@ private slots: void nextQuery() { - nextOrPrevQuery((simpleSearch ? simpleQueries : complexQueries).queries.size() - 1, - 1, nextQueryButton, prevQueryButton); + nextOrPrevQuery((simpleSearch ? simpleQueries + : complexQueries).queries.size() - 1, 1, nextQueryButton, + prevQueryButton); } void prevQuery() @@ -415,8 +393,9 @@ private: \fn void QHelpSearchQueryWidget::search() This signal is emitted when a the user has the search button invoked. - After reciving the signal you can ask the QHelpSearchQueryWidget for the build list - of QHelpSearchQuery's that you may pass to the QHelpSearchEngine's search() function. + After reciving the signal you can ask the QHelpSearchQueryWidget for the + build list of QHelpSearchQuery's that you may pass to the QHelpSearchEngine's + search() function. */ /*! @@ -544,7 +523,8 @@ QList<QHelpSearchQuery> QHelpSearchQueryWidget::query() const QList<QHelpSearchQuery>() : queryHist.queries.last(); } -/*! \reimp +/*! + \reimp */ void QHelpSearchQueryWidget::focusInEvent(QFocusEvent *focusEvent) { diff --git a/tools/assistant/lib/qhelpsearchresultwidget.cpp b/tools/assistant/lib/qhelpsearchresultwidget.cpp index ad540c6..2f5489b 100644 --- a/tools/assistant/lib/qhelpsearchresultwidget.cpp +++ b/tools/assistant/lib/qhelpsearchresultwidget.cpp @@ -304,7 +304,7 @@ private: last = resultLastToShow > count ? count : resultLastToShow; } } - hitsLabel->setText(tr("%1 - %2 of %3 Hits").arg(first).arg(last).arg(count)); + hitsLabel->setText(QHelpSearchResultWidget::tr("%1 - %2 of %n Hits", 0, count).arg(first).arg(last)); } private: |