From 415b44ffb6ffcdeb4ad5b7cdd6d9d9e17ab3e47f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Mon, 3 May 2010 12:34:27 +0200 Subject: Initial checkin --- config.h | 30 +++ doxygen2qthelp.cpp | 407 ++++++++++++++++++++++++++++ doxygen2qthelp.pro | 75 ++++++ examples/cpptest.ini | 5 + examples/libspiff.ini | 5 + examples/make_docs_cpptest.sh | 4 + examples/make_docs_helper.sh | 58 ++++ examples/make_docs_libspiff.sh | 4 + examples/make_docs_qwt.sh | 4 + examples/qwt.ini | 5 + htmlhelpdatainterface.cpp | 191 +++++++++++++ htmlhelpdatainterface_p.h | 86 ++++++ htmlhelpparser.cpp | 476 ++++++++++++++++++++++++++++++++ htmlhelpparser_p.h | 81 ++++++ qthelpprojectwriter.cpp | 172 ++++++++++++ qthelpprojectwriter_p.h | 59 ++++ simplehtmlparser.cpp | 600 +++++++++++++++++++++++++++++++++++++++++ simplehtmlparser_p.h | 60 +++++ simplehtmlreceiver_p.h | 49 ++++ 19 files changed, 2371 insertions(+) create mode 100644 config.h create mode 100644 doxygen2qthelp.cpp create mode 100644 doxygen2qthelp.pro create mode 100644 examples/cpptest.ini create mode 100644 examples/libspiff.ini create mode 100644 examples/make_docs_cpptest.sh create mode 100644 examples/make_docs_helper.sh create mode 100644 examples/make_docs_libspiff.sh create mode 100644 examples/make_docs_qwt.sh create mode 100644 examples/qwt.ini create mode 100644 htmlhelpdatainterface.cpp create mode 100644 htmlhelpdatainterface_p.h create mode 100644 htmlhelpparser.cpp create mode 100644 htmlhelpparser_p.h create mode 100644 qthelpprojectwriter.cpp create mode 100644 qthelpprojectwriter_p.h create mode 100644 simplehtmlparser.cpp create mode 100644 simplehtmlparser_p.h create mode 100644 simplehtmlreceiver_p.h diff --git a/config.h b/config.h new file mode 100644 index 0000000..966df21 --- /dev/null +++ b/config.h @@ -0,0 +1,30 @@ +/**************************************************************************** + ** + ** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies). + ** Contact: Nokia Corporation (qt-info@nokia.com) + ** + ** This file is part of the doxygen2qthelp project on Trolltech Labs. + ** + ** This file may be used under the terms of the GNU General Public + ** License version 2.0 or 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 GNU + ** General Public Licensing requirements will be met: + ** http://www.fsf.org/licensing/licenses/info/GPLv2.html and + ** http://www.gnu.org/copyleft/gpl.html. + ** + ** If you are unsure which license is appropriate for your use, please + ** contact the sales department at qt-sales@nokia.com. + ** + ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + ** + ****************************************************************************/ + +#ifndef CONFIG_H +#define CONFIG_H + +#define VERSION "1.1.0" + +#endif // CONFIG_H + diff --git a/doxygen2qthelp.cpp b/doxygen2qthelp.cpp new file mode 100644 index 0000000..ae5a5be --- /dev/null +++ b/doxygen2qthelp.cpp @@ -0,0 +1,407 @@ +/**************************************************************************** + ** + ** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies). + ** Contact: Nokia Corporation (qt-info@nokia.com) + ** + ** This file is part of the doxygen2qthelp project on Trolltech Labs. + ** + ** This file may be used under the terms of the GNU General Public + ** License version 2.0 or 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 GNU + ** General Public Licensing requirements will be met: + ** http://www.fsf.org/licensing/licenses/info/GPLv2.html and + ** http://www.gnu.org/copyleft/gpl.html. + ** + ** If you are unsure which license is appropriate for your use, please + ** contact the sales department at qt-sales@nokia.com. + ** + ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + ** + ****************************************************************************/ + +#include "config.h" +#include "htmlhelpdatainterface_p.h" +#include "qhelpgenerator_p.h" +#include "qthelpprojectwriter_p.h" + +#include +#include +#include +#include +#include +#include +#include + +#if (QT_VERSION <= 0x040400) +# error Please compile doxygen2qthelp against a Qt release _after_ 4.4.0, e.g. a recent snapshot +#endif + +QT_BEGIN_NAMESPACE + +const char* const FILE_MISSING_FORMAT = "Error: File '%s' does not exist.\n"; + +enum Action +{ + ACTION_USAGE, + ACTION_RUN, + ACTION_QUIT, + ACTION_VERSION +}; + +QString virtualFolder; +QString nameSpace; +QString outputFileName; +QString inputFileName; +bool verbose = false; +bool forceOverwrite = false; +bool helpProjectMode = false; +bool error = false; + +void printUsage() +{ + printf( + "Usage: doxygen2qthelp OPTIONS []\n" + " doxygen2qthelp --config=\n" + "\n" + "Parameters Description\n" + " Read input from \n" + " (InputFile=)\n" + "\n" + " Write output to \n" + " (OutputFile=)\n" + "\n" + " --config= Read config from \n" + "\n" + " --folder= Set virtual folder [required]\n" + " (VirtualFolder=)\n" + "\n" + " --force Overwrite existing files\n" + " (ForceOverwrite=true)\n" + "\n" + " --help, -h Print help and exit\n" + "\n" + " --namespace= Set namespace to [required]\n" + " (Namespace=)\n" + "\n" + " --qch Output as Qt Compressed Help file (*.qch) [default]\n" + " (QhpMode=false)\n" + "\n" + " --qhp Output as Qt Help Project (*.qhp)\n" + " (QhpMode=true)\n" + "\n" + " --verbose, -v Report in more detail\n" + " (Verbose=true)\n" + "\n" + " --version, -V Print version information and exit\n" + "\n" + ); +} + +void printVersion() +{ + printf( + "doxygen2qthelp " VERSION " of Qt " QT_VERSION_STR " by Trolltech ASA " + "(compiled " __TIME__ ", " __DATE__ ")\n" + ); +} + +void printSettings() +{ + printf( + "Settings\n" + " Input filename ....... %s\n" + " Output filename ...... %s\n" + "\n" + " QtHelpProject mode ... %s\n" + " Force overwrite ...... %s\n" + "\n" + " Namespace ............ %s\n" + " Virtual folder ....... %s\n" + "\n", + qPrintable(inputFileName), + qPrintable(outputFileName), + helpProjectMode ? "yes" : "no", + forceOverwrite ? "yes" : "no", + qPrintable(nameSpace), + qPrintable(virtualFolder) + ); +} + +bool checkNamespace() +{ + const QString uri(QLatin1String("http://") + nameSpace + + QLatin1String("/")); + const QUrl url = QUrl::fromEncoded(uri.toAscii()); + if (!url.isValid()) { + printf("Error: Namespace does not conform to the rules " + "for the host part of a URI.\n"); + return false; + } + return true; +} + +bool checkVirtualFolder() +{ + const char * const VIRTUAL_FOLDER_ERROR = "Error: Virtual folder does not " + "conform to the rules for a path segment of a URI.\n"; + if (virtualFolder.contains('/') || virtualFolder.contains('?') + || virtualFolder.contains('#')) { + printf(VIRTUAL_FOLDER_ERROR); + return false; + } + + const QString uri(QLatin1String("http://example.org/") + virtualFolder + + QLatin1String("/")); + const QUrl url = QUrl::fromEncoded(uri.toAscii()); + if (!url.isValid()) { + printf(VIRTUAL_FOLDER_ERROR); + return false; + } + return true; +} + +bool checkFiles() +{ + if (inputFileName == outputFileName) { + printf("Error: Input and output filename identical\n"); + return false; + } + + if (!QFile(inputFileName).exists()) { + printf(FILE_MISSING_FORMAT, qPrintable(inputFileName)); + return false; + } + + if (!forceOverwrite && QFile(outputFileName).exists()) { + printf("Error: File '%s' exists. Use --force to overwrite.\n", + qPrintable(outputFileName)); + return false; + } + return true; +} + +void guessOutputFilename() +{ + if (!outputFileName.isEmpty()) + return; + + const QString newSuffix(helpProjectMode ? ".qhp" : ".qch"); + if (inputFileName.endsWith(QLatin1String(".hhp"))) { + outputFileName = inputFileName.left(inputFileName.count() - 4); + } else { + outputFileName = inputFileName; + } + outputFileName += newSuffix; +} + +bool run() +{ + guessOutputFilename(); + Q_ASSERT(!inputFileName.isEmpty()); + Q_ASSERT(!outputFileName.isEmpty()); + + if (verbose) + printSettings(); + + if (!checkNamespace() || !checkVirtualFolder() || !checkFiles()) + return false; + + HtmlHelpDataInterface helpData(inputFileName, nameSpace, virtualFolder); + if (helpProjectMode) { + // Create .qhp + QtHelpProjectWriter writer; + if (!writer.writeFile(&helpData, outputFileName)) { + printf("Error: Could not write to file '%s'.\n", + qPrintable(outputFileName)); + return false; + } + } else { + // Create .qch + QHelpGenerator generator; + if (!generator.generate(&helpData, outputFileName)) { + printf("Error: %s\n", qPrintable(generator.error())); + return false; + } + } + + printf("Done.\n"); + return true; +} + +void setIfSpecified(const QSettings &iniReader, const QString &key, + QString &output) +{ + const QString res = iniReader.value(key).toString(); + if (!res.isEmpty()) + output = res; +} + +void setIfSpecified(const QSettings &iniReader, const QString &key, + bool &output) +{ + const QVariant res = iniReader.value(key); + if (!res.toString().isEmpty()) + output = res.toBool(); +} + +bool readConfigFile(const QString &fileName) +{ + if (!QFile(fileName).exists()) { + printf(FILE_MISSING_FORMAT, qPrintable(fileName)); + return false; + } + + QSettings iniReader(fileName, QSettings::IniFormat); + setIfSpecified(iniReader, QLatin1String("ForceOverwrite"), forceOverwrite); + setIfSpecified(iniReader, QLatin1String("InputFile"), inputFileName); + setIfSpecified(iniReader, QLatin1String("Namespace"), nameSpace); + setIfSpecified(iniReader, QLatin1String("OutputFile"), outputFileName); + setIfSpecified(iniReader, QLatin1String("QhpMode"), helpProjectMode); + setIfSpecified(iniReader, QLatin1String("Verbose"), verbose); + setIfSpecified(iniReader, QLatin1String("VirtualFolder"), virtualFolder); + return true; +} + +Action parseArgs() +{ + const char * const ILLEGAL_OPTION_FORMAT = "Error: Illegal option '%s'\n\n"; + const QStringList args = QCoreApplication::arguments(); + + bool fileMode = false; + for (int i = 1; i < args.count(); i++) { + const QString &curArg = args.at(i); + if (fileMode || (curArg[0] != '-')) { + // Filenames only + switch (args.count() - i) { + case 2: // second last + inputFileName = curArg; + break; + + case 1: // last + if (inputFileName.isEmpty()) { + inputFileName = curArg; + } else { + outputFileName = curArg; + } + break; + + default: + if (!fileMode) + printf(ILLEGAL_OPTION_FORMAT, qPrintable(curArg)); + + error = true; + return ACTION_USAGE; + } + } else { + // Options or filenames + QString opt; + if (curArg[1] == '-') { + // Long option + opt = curArg.mid(2); + if (opt.isEmpty()) { + fileMode = true; + } else if (opt.startsWith(QLatin1String("config="))) { + if (!readConfigFile(opt.mid(6 + 1))) { + return ACTION_QUIT; + } + } else if (opt.startsWith("folder=")) { + virtualFolder = opt.mid(6 + 1); + } else if (opt == QLatin1String("force")) { + forceOverwrite = true; + } else if (opt == QLatin1String("help")) { + return ACTION_USAGE; + } else if (opt.startsWith("namespace=")) { + nameSpace = opt.mid(9 + 1); + } else if (opt == QLatin1String("qch")) { + helpProjectMode = false; + } else if (opt == QLatin1String("qhp")) { + helpProjectMode = true; + } else if (opt == QLatin1String("verbose")) { + verbose = true; + } else if (opt == QLatin1String("version")) { + return ACTION_VERSION; + } else { + printf(ILLEGAL_OPTION_FORMAT, qPrintable(curArg)); + error = true; + return ACTION_USAGE; + } + } else { + // Short option + opt = curArg.mid(1); + if (opt.count() != 1) { + printf(ILLEGAL_OPTION_FORMAT, qPrintable(curArg)); + error = true; + return ACTION_USAGE; + } + + switch (opt.at(0).toAscii()) { + case 'h': + return ACTION_USAGE; + + case 'v': + verbose = true; + break; + + case 'V': + return ACTION_VERSION; + + default: + printf(ILLEGAL_OPTION_FORMAT, qPrintable(curArg)); + error = true; + return ACTION_USAGE; + } + } + } + } + + if (inputFileName.isEmpty()) { + printf("Error: No input filename specified\n\n"); + error = true; + return ACTION_USAGE; + } + + if (nameSpace.isEmpty()) { + printf("Error: Namespace must not be empty.\n\n"); + error = true; + return ACTION_USAGE; + } + + if (virtualFolder.isEmpty()) { + printf("Error: Virtual folder must not be empty.\n\n"); + error = true; + return ACTION_USAGE; + } + + return ACTION_RUN; +} + +QT_END_NAMESPACE + +int main(int argc, char ** argv) +{ + QCoreApplication app(argc, argv); + const Action action = parseArgs(); + + switch (action) { + case ACTION_RUN: + error = ! run(); + break; + + case ACTION_VERSION: + printVersion(); + break; + + case ACTION_QUIT: + break; + + case ACTION_USAGE: // fall through + default: + printUsage(); + break; + + } + + return error ? 1 : 0; +} diff --git a/doxygen2qthelp.pro b/doxygen2qthelp.pro new file mode 100644 index 0000000..495f9fb --- /dev/null +++ b/doxygen2qthelp.pro @@ -0,0 +1,75 @@ +message(Using Qt sources from $(QTSRCDIR)) +ASSISTANT_LIB_DIR = "$(QTSRCDIR)/tools/assistant/lib" + +QBASE_LOCATION = "$(QTSRCDIR)/src/qbase.pri" +include($${QBASE_LOCATION}) | error(File $${QBASE_LOCATION} not found) + +TARGET = doxygen2qthelp +TEMPLATE = app +DESTDIR = $$PWD +QT += sql xml +DEFINES += QHELP_LIB +# DEFINES += HELP_LIB_DATA_DEBUG +CONFIG += qt warn_on console +CONFIG += release + +QMAKE_TARGET_PRODUCT = doxygen2qthelp +QMAKE_TARGET_DESCRIPTION = Doxygen to Qt Help command line converter. +DEFINES -= QT_ASCII_CAST_WARNINGS + +RESOURCES += $${ASSISTANT_LIB_DIR}/helpsystem.qrc + +SOURCES += \ + $${ASSISTANT_LIB_DIR}/qhelpenginecore.cpp \ + $${ASSISTANT_LIB_DIR}/qhelpengine.cpp \ + $${ASSISTANT_LIB_DIR}/qhelpdbreader.cpp \ + $${ASSISTANT_LIB_DIR}/qhelpcontentwidget.cpp \ + $${ASSISTANT_LIB_DIR}/qhelpindexwidget.cpp \ + $${ASSISTANT_LIB_DIR}/qhelpgenerator.cpp \ + $${ASSISTANT_LIB_DIR}/qhelpdatainterface.cpp \ + $${ASSISTANT_LIB_DIR}/qhelpprojectdata.cpp \ + $${ASSISTANT_LIB_DIR}/qhelpcollectionhandler.cpp \ + $${ASSISTANT_LIB_DIR}/qhelpsearchengine.cpp \ + $${ASSISTANT_LIB_DIR}/qhelpsearchquerywidget.cpp \ + $${ASSISTANT_LIB_DIR}/qhelpsearchresultwidget.cpp \ + $${ASSISTANT_LIB_DIR}/qhelpsearchindex_default.cpp \ + $${ASSISTANT_LIB_DIR}/qhelpsearchindexwriter_default.cpp \ + $${ASSISTANT_LIB_DIR}/qhelpsearchindexreader_default.cpp \ + +HEADERS += \ + $${ASSISTANT_LIB_DIR}/qhelpenginecore.h \ + $${ASSISTANT_LIB_DIR}/qhelpengine.h \ + $${ASSISTANT_LIB_DIR}/qhelpengine_p.h \ + $${ASSISTANT_LIB_DIR}/qhelp_global.h \ + $${ASSISTANT_LIB_DIR}/qhelpdbreader_p.h \ + $${ASSISTANT_LIB_DIR}/qhelpcontentwidget.h \ + $${ASSISTANT_LIB_DIR}/qhelpindexwidget.h \ + $${ASSISTANT_LIB_DIR}/qhelpgenerator_p.h \ + $${ASSISTANT_LIB_DIR}/qhelpdatainterface_p.h \ + $${ASSISTANT_LIB_DIR}/qhelpprojectdata_p.h \ + $${ASSISTANT_LIB_DIR}/qhelpcollectionhandler_p.h \ + $${ASSISTANT_LIB_DIR}/qhelpsearchengine.h \ + $${ASSISTANT_LIB_DIR}/qhelpsearchquerywidget.h \ + $${ASSISTANT_LIB_DIR}/qhelpsearchresultwidget.h \ + $${ASSISTANT_LIB_DIR}/qhelpsearchindex_default_p.h \ + $${ASSISTANT_LIB_DIR}/qhelpsearchindexwriter_default_p.h \ + $${ASSISTANT_LIB_DIR}/qhelpsearchindexreader_default_p.h \ + +INCLUDEPATH += $${ASSISTANT_LIB_DIR} + +SOURCES += \ + htmlhelpdatainterface.cpp \ + htmlhelpparser.cpp \ + qthelpprojectwriter.cpp \ + simplehtmlparser.cpp \ + \ + doxygen2qthelp.cpp \ + +HEADERS += \ + htmlhelpdatainterface_p.h \ + htmlhelpparser_p.h \ + qthelpprojectwriter_p.h \ + simplehtmlparser_p.h \ + \ + simplehtmlreceiver_p.h \ + diff --git a/examples/cpptest.ini b/examples/cpptest.ini new file mode 100644 index 0000000..fc6ec60 --- /dev/null +++ b/examples/cpptest.ini @@ -0,0 +1,5 @@ +ForceOverwrite=1 +Namespace=net.sourceforge.cpptest +Verbose=1 +VirtualFolder=cpptest-1.1.0 + diff --git a/examples/libspiff.ini b/examples/libspiff.ini new file mode 100644 index 0000000..cde73ae --- /dev/null +++ b/examples/libspiff.ini @@ -0,0 +1,5 @@ +ForceOverwrite=1 +Namespace=net.sourceforge.libspiff +Verbose=1 +VirtualFolder=libspiff-0.8.3 + diff --git a/examples/make_docs_cpptest.sh b/examples/make_docs_cpptest.sh new file mode 100644 index 0000000..9823560 --- /dev/null +++ b/examples/make_docs_cpptest.sh @@ -0,0 +1,4 @@ +#! /usr/bin/env bash +source make_docs_helper.sh +go cpptest 1.1.0 .tar.gz + diff --git a/examples/make_docs_helper.sh b/examples/make_docs_helper.sh new file mode 100644 index 0000000..ec5b0d4 --- /dev/null +++ b/examples/make_docs_helper.sh @@ -0,0 +1,58 @@ +function go() { + +PACKAGE_NAME=$1 +PACKAGE_VERSION=$2 +ARCHIVE_EXT=$3 + +URI_PREFIX="http://downloads.sourceforge.net/${PACKAGE_NAME}/" +URI_BASE="${PACKAGE_NAME}-${PACKAGE_VERSION}" +URI_FILE="${URI_BASE}${ARCHIVE_EXT}" +INI_FILE="${PACKAGE_NAME}.ini" +QCH_FILE="${PACKAGE_NAME}-${PACKAGE_VERSION}-doc.qch" +QHP_FILE="${PACKAGE_NAME}-${PACKAGE_VERSION}-doc.qhp" + +HPP_FILE="html/index.hhp" +HPP_LOCATION="${URI_BASE}/doc/${HPP_FILE}" + +## Download +if [ ! -f "${URI_FILE}" ]; then + wget "${URI_PREFIX}${URI_FILE}" || exit 1 +fi + +## Unpack +if [ ! -d "${URI_BASE}" ]; then + tar xf "${URI_FILE}" +fi + +pushd "${URI_BASE}/doc" || exit 1 + +## Make temp file +TEMPFILE=`mktemp` +if [ "x${TEMPFILE}" = x ]; then + popd ; exit 1 +fi + +## Fix Doxygen input +sed -e 's/^\(GENERATE_HTMLHELP\).\+$/\1 = YES/' -e 's/^\(HHC_LOCATION.\+\)$/# \1/' Doxyfile > "${TEMPFILE}" +rm Doxyfile +mv "${TEMPFILE}" Doxyfile || { popd ; exit 1 ; } + +## Create Doxygen output +if [ ! -f "${HPP_FILE}" ]; then + doxygen || { popd ; exit 1 ; } +fi + +popd || exit 1 + + +## Create Qt help files +LD_LIBRARY_PATH=$QTDIR/lib ../doxygen2qthelp "--config=${INI_FILE}" \ + --qch "${HPP_LOCATION}" "${QCH_FILE}" || exit 1 + +LD_LIBRARY_PATH=$QTDIR/lib ../doxygen2qthelp "--config=${INI_FILE}" \ + --qhp "${HPP_LOCATION}" "${QHP_FILE}" || exit 1 + +exit 0 + +} + diff --git a/examples/make_docs_libspiff.sh b/examples/make_docs_libspiff.sh new file mode 100644 index 0000000..7151fec --- /dev/null +++ b/examples/make_docs_libspiff.sh @@ -0,0 +1,4 @@ +#! /usr/bin/env bash +source make_docs_helper.sh +go libspiff 0.8.3 .tar.bz2 + diff --git a/examples/make_docs_qwt.sh b/examples/make_docs_qwt.sh new file mode 100644 index 0000000..f4e66aa --- /dev/null +++ b/examples/make_docs_qwt.sh @@ -0,0 +1,4 @@ +#! /usr/bin/env bash +source make_docs_helper.sh +go qwt 5.0.2 .tar.bz2 + diff --git a/examples/qwt.ini b/examples/qwt.ini new file mode 100644 index 0000000..af6ed08 --- /dev/null +++ b/examples/qwt.ini @@ -0,0 +1,5 @@ +ForceOverwrite=1 +Namespace=net.sourceforge.qwt +Verbose=1 +VirtualFolder=qwt-5.0.2 + diff --git a/htmlhelpdatainterface.cpp b/htmlhelpdatainterface.cpp new file mode 100644 index 0000000..5a2fe58 --- /dev/null +++ b/htmlhelpdatainterface.cpp @@ -0,0 +1,191 @@ +/**************************************************************************** + ** + ** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies). + ** Contact: Nokia Corporation (qt-info@nokia.com) + ** + ** This file is part of the doxygen2qthelp project on Trolltech Labs. + ** + ** This file may be used under the terms of the GNU General Public + ** License version 2.0 or 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 GNU + ** General Public Licensing requirements will be met: + ** http://www.fsf.org/licensing/licenses/info/GPLv2.html and + ** http://www.gnu.org/copyleft/gpl.html. + ** + ** If you are unsure which license is appropriate for your use, please + ** contact the sales department at qt-sales@nokia.com. + ** + ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + ** + ****************************************************************************/ + +#include "htmlhelpdatainterface_p.h" +#include "htmlhelpparser_p.h" + +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +HtmlHelpDataInterface::HtmlHelpDataInterface(const QString &projectFileName, + const QString &namespaceName, const QString &virtualFolder) + : m_nameSpace(namespaceName), m_virtualFolder(virtualFolder) +{ + initFromDoxygenHtmlHelpProject(projectFileName); +} + +void HtmlHelpDataInterface::initFromDoxygenHtmlHelpProject( + const QString &projectFileName) +{ + // Parse hhc/hhk/hhp + QSettings iniReader(projectFileName, QSettings::IniFormat); + m_projectTitle = iniReader.value("OPTIONS/Title").toString(); + m_filterIdent = m_projectTitle; + m_entryPageHref = iniReader.value("OPTIONS/Default topic", + "index.html").toString(); + parseToc(getTocFileName(projectFileName)); + parseIndex(getIndexFileName(projectFileName)); + + // File list + m_rootPath = getRootPath(projectFileName); + scanRootPath(); + + // Custom filters + QHelpDataCustomFilter everything; + everything.filterAttributes << m_filterIdent; + everything.name = m_projectTitle; + m_custumFilters << everything; + + // Sections + QHelpDataFilterSection section; + section.addFilterAttribute(m_filterIdent); + section.setIndices(m_index); + section.setContents(m_toc); + section.setFiles(m_files); + m_filterSections.append(section); +} + +const QStringList HtmlHelpDataInterface::applyPrefix(const QStringList &list, + const QString &prefix) const +{ + if (prefix.isNull()) { + return list; + } + + QStringList processed; + foreach (const QString &entry, list) { + processed << prefix + "/" + entry; + } + return processed; +} + +QStringList HtmlHelpDataInterface::findSubDirs(const QString &dir) const +{ + return QDir(dir).entryList(QDir::Dirs | QDir::NoDotAndDotDot); +} + +QStringList HtmlHelpDataInterface::findDoxygenFiles(const QString &dir) const +{ + const QStringList filter = QStringList() + << "*.css" + << "*.gif" + << "*.htm" + << "*.html" + << "*.jpeg" + << "*.jpg" + << "*.png" + ; + return QDir(dir).entryList(filter); +} + +QStringList HtmlHelpDataInterface::findDoxygenFilesRecursively(const QString &dir, + const QString &prefix) const +{ + QStringList files; + foreach (const QString &subDir, findSubDirs(dir)) { + files += findDoxygenFilesRecursively(dir + "/" + subDir, + prefix.isNull() ? subDir : prefix + "/" + subDir); + } + return files + applyPrefix(findDoxygenFiles(dir), prefix); +} + +void HtmlHelpDataInterface::scanRootPath() +{ + m_files = findDoxygenFilesRecursively(m_rootPath); +} + +void HtmlHelpDataInterface::parseToc(const QString &fileName) +{ + HtmlHelpParser parser; + parser.parseFile(fileName); + m_toc = parser.stealToc(m_projectTitle, m_entryPageHref); +} + +void HtmlHelpDataInterface::parseIndex(const QString &fileName) +{ + HtmlHelpParser parser; + parser.parseFile(fileName); + m_index = parser.stealIndex(); +} + +QString HtmlHelpDataInterface::replaceFileExtension(const QString &fileName, + const QString &extension) +{ + QFileInfo fileInfo(fileName); + const int suffixLen = fileInfo.suffix().count(); + return fileName.left(fileName.count() - suffixLen) + extension; +} + +QString HtmlHelpDataInterface::getTocFileName(const QString &projectFileName) +{ + return replaceFileExtension(projectFileName, QLatin1String("hhc")); +} + +QString HtmlHelpDataInterface::getIndexFileName(const QString &projectFileName) +{ + return replaceFileExtension(projectFileName, QLatin1String("hhk")); +} + +QString HtmlHelpDataInterface::getRootPath(const QString &projectFileName) +{ + QFileInfo fileInfo(projectFileName); + return fileInfo.dir().absolutePath(); +} + +QString HtmlHelpDataInterface::namespaceName() const +{ + return m_nameSpace; +} + +QString HtmlHelpDataInterface::virtualFolder() const +{ + return m_virtualFolder; +} + +QList HtmlHelpDataInterface::customFilters() const +{ + return m_custumFilters; +} + +QList HtmlHelpDataInterface::filterSections() const +{ + return m_filterSections; +} + +QMap HtmlHelpDataInterface::metaData() const +{ + return QMap(); +} + +QString HtmlHelpDataInterface::rootPath() const +{ + return m_rootPath; +} + +QT_END_NAMESPACE diff --git a/htmlhelpdatainterface_p.h b/htmlhelpdatainterface_p.h new file mode 100644 index 0000000..1efd2e1 --- /dev/null +++ b/htmlhelpdatainterface_p.h @@ -0,0 +1,86 @@ +/**************************************************************************** + ** + ** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies). + ** Contact: Nokia Corporation (qt-info@nokia.com) + ** + ** This file is part of the doxygen2qthelp project on Trolltech Labs. + ** + ** This file may be used under the terms of the GNU General Public + ** License version 2.0 or 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 GNU + ** General Public Licensing requirements will be met: + ** http://www.fsf.org/licensing/licenses/info/GPLv2.html and + ** http://www.gnu.org/copyleft/gpl.html. + ** + ** If you are unsure which license is appropriate for your use, please + ** contact the sales department at qt-sales@nokia.com. + ** + ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + ** + ****************************************************************************/ + +#ifndef HTMLHELPDATAINTERFACE_P_H +#define HTMLHELPDATAINTERFACE_P_H + +#include "qhelpdatainterface_p.h" +#include +#include + +QT_BEGIN_NAMESPACE + +class HtmlHelpParser; + +class HtmlHelpDataInterface : public QHelpDataInterface +{ +private: + QString m_projectTitle; + QString m_filterIdent; + QString m_entryPageHref; + QList m_index; + QList m_toc; + + QString m_rootPath; + QString m_nameSpace; + QString m_virtualFolder; + QStringList m_files; + QList m_custumFilters; + QList m_filterSections; + +public: + HtmlHelpDataInterface(const QString &projectFileName, + const QString &namespaceName, const QString &virtualFolder); + +private: + void initFromDoxygenHtmlHelpProject(const QString &projectFileName); + + const QStringList applyPrefix(const QStringList &list, const QString &prefix) const; + QStringList findSubDirs(const QString &dir) const; + QStringList findDoxygenFiles(const QString &dir) const; + QStringList findDoxygenFilesRecursively(const QString &dir, + const QString &prefix = QString()) const; + void scanRootPath(); + void parseToc(const QString &fileName); + void parseIndex(const QString &fileName); + + QString replaceFileExtension(const QString &fileName, + const QString &extension); + QString getTocFileName(const QString &projectFileName); + QString getIndexFileName(const QString &projectFileName); + QString getRootPath(const QString &projectFileName); + +private: + QString namespaceName() const; + QString virtualFolder() const; + QList customFilters() const; + QList filterSections() const; + QMap metaData() const; + QString rootPath() const; + +}; + +QT_END_NAMESPACE + +#endif // HTMLHELPDATAINTERFACE_P_H + diff --git a/htmlhelpparser.cpp b/htmlhelpparser.cpp new file mode 100644 index 0000000..3298568 --- /dev/null +++ b/htmlhelpparser.cpp @@ -0,0 +1,476 @@ +/**************************************************************************** + ** + ** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies). + ** Contact: Nokia Corporation (qt-info@nokia.com) + ** + ** This file is part of the doxygen2qthelp project on Trolltech Labs. + ** + ** This file may be used under the terms of the GNU General Public + ** License version 2.0 or 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 GNU + ** General Public Licensing requirements will be met: + ** http://www.fsf.org/licensing/licenses/info/GPLv2.html and + ** http://www.gnu.org/copyleft/gpl.html. + ** + ** If you are unsure which license is appropriate for your use, please + ** contact the sales department at qt-sales@nokia.com. + ** + ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + ** + ****************************************************************************/ + +#include "htmlhelpparser_p.h" +#include "simplehtmlparser_p.h" +#include "qhelpdatainterface_p.h" + +#include +#include +#include +#include +#include + +#ifdef HELP_LIB_DATA_DEBUG +# include +#endif + +QT_BEGIN_NAMESPACE + +class ContainerNode; +class LinkNode; + +class HtmlHelpNode +{ +public: + virtual ~HtmlHelpNode() { } + + virtual bool isContainer() const = 0; + virtual ContainerNode * toContainer() const = 0; + virtual const LinkNode * toLink() const = 0; + +#ifdef HELP_LIB_DATA_DEBUG + virtual void dump(int level) const = 0; +#endif +}; + +class ContainerNode : public HtmlHelpNode +{ +public: + QList m_children; + + ~ContainerNode() + { + foreach(const HtmlHelpNode * node, m_children) { + delete node; + } + } + + static void insertItem(QHelpDataIndexItem &item, QList &dest, + QSet &pastEntries, QSet &pastIds); + + QHelpDataContentItem * convertAndStealChildren( + QHelpDataContentItem * parent, + const QString &name = QString(), const QString &href = QString()); + QList convertAndStealChildren(int level, + const QString &rootLabel, QSet &pastEntries, + QSet &pastIds); + + bool isContainer() const { return true; } + ContainerNode * toContainer() const + { + return const_cast(this); + } + const LinkNode * toLink() const { return NULL; } + +#ifdef HELP_LIB_DATA_DEBUG + void dump(int level = 0) const + { + QString indent; + for (int i = 0; i < level; i++) { + indent.append(" _"); + } + foreach(const HtmlHelpNode * node, m_children) { + Q_ASSERT(node != NULL); + node->dump(level + 1); + } + } +#endif +}; + +class LinkNode : public HtmlHelpNode +{ +public: + QString m_name; + QString m_subName; + QString m_href; + + LinkNode(const QString &name, const QString &subName, const QString &href) + : m_name(name), m_subName(subName), m_href(href) { } + + QHelpDataContentItem * convert(QHelpDataContentItem * parent) const; + QHelpDataIndexItem convert() const; + bool isContainer() const { return false; } + ContainerNode * toContainer() const { return NULL; } + const LinkNode * toLink() const { return this; } + +#ifdef HELP_LIB_DATA_DEBUG + void dump(int level = 0) const + { + QString indent; + for (int i = 0; i < level; i++) { + indent.append(" _"); + } + qDebug() << indent << m_name << m_subName << m_href; + } +#endif +}; + +QHelpDataContentItem * ContainerNode::convertAndStealChildren( + QHelpDataContentItem * parent, const QString &name, + const QString &href) +{ + QHelpDataContentItem * const res + = new QHelpDataContentItem(parent, name, href); + + const int childCount = m_children.count(); + for (int i = 0; i < childCount; i++) { + QHelpDataContentItem * item = NULL; + HtmlHelpNode * const node = m_children[i]; + Q_ASSERT(node != NULL); + if (node->isContainer()) { + // No Name node before, + item = node->toContainer()->convertAndStealChildren(res); + } else { + const LinkNode * const linkNode = node->toLink(); + if ((i < childCount - 1) && (m_children[i + 1]->isContainer())) { + // Not last node, check successor + HtmlHelpNode * const succ = m_children[i + 1]; + item = succ->toContainer()->convertAndStealChildren( + res, linkNode->m_name, linkNode->m_href); + delete succ; + // Skip container node + i++; + } else { + // Successor (if existing) is a link node too + // Delete link to the 'main page' so we don't have two of them + if ((parent != NULL) || (i != 0) + || (linkNode->m_href != href)) { + if (!linkNode->m_href.isEmpty()) { + item = linkNode->convert(res); + } + } + } + } + + delete node; + } + m_children.clear(); + + return res; +} + +static uint qHash(const QHelpDataIndexItem &key) +{ + return qHash(key.name) ^ qHash(key.reference); +} + +/*static*/ void ContainerNode::insertItem(QHelpDataIndexItem &item, + QList &dest, QSet &pastEntries, + QSet &pastIds) +{ + const QString &id = item.identifier; + const bool nameRefPairFound = pastEntries.contains(item); + const bool idFound = pastIds.contains(id); + if (!(nameRefPairFound && idFound)) { + if (nameRefPairFound) { + if (idFound) { + // (1) Name with exact same link already there + // (2) ID already taken + // \--> don't add + Q_ASSERT(false); + } else { + // (1) Name with exact same link already there + // (2) ID not taken + // \--> add as ("", id, ref) + item.name = QString(); + } + } else { + if (idFound) { + // (1) Name not linking to this reference yet + // (2) ID already taken + // \--> add as (name, "", ref) + item.identifier = QString(); + } else { + // (1) Name not linking to this reference yet + // (2) ID not taken + // \--> add unmodified + } + } +#ifdef HELP_LIB_DATA_DEBUG + item.dump(); +#endif + pastEntries.insert(item); + pastIds.insert(id); + dest << item; + } +} + +QList ContainerNode::convertAndStealChildren( + int level, const QString &rootLabel, + QSet &pastEntries, QSet &pastIds) +{ + QList res; + QString workLabel(rootLabel); + + foreach (HtmlHelpNode * node, m_children) { + if (node->isContainer()) { + res += node->toContainer()->convertAndStealChildren( + level + 1, workLabel, pastEntries, pastIds); + } else { + QHelpDataIndexItem item = node->toLink()->convert(); + insertItem(item, res, pastEntries, pastIds); + if (level == 0) { + workLabel = item.name; + } else { + // -- Sample input index tree + // clone {#1, #2} + // * Chicken {#3} + // * Salad {#4} + // + // Chicken {#3} + // * clone {#1} + // + // Salad {#4} + // * clone {#2} + // + // -- Created index entries + // NAME | ID | REF + // --------+----------------+-------- + // clone | clone | 1 + // clone | | 2 + // Chicken | Chicken | 3 + // | clone::Chicken | 3 + // | Chicken::clone | 1 + // Salad | Salad | 4 + // | clone::Salad | 4 + // | Salad::clone | 2 + const QString id = rootLabel + QLatin1String("::") + item.name; + QHelpDataIndexItem extraItem = QHelpDataIndexItem(QString(), id, item.reference); + insertItem(extraItem, res, pastEntries, pastIds); + } + } + delete node; + } + + m_children.clear(); + return res; +} + +QHelpDataContentItem * LinkNode::convert(QHelpDataContentItem * parent) const +{ + return new QHelpDataContentItem(parent, m_name, m_href); +} + +QHelpDataIndexItem LinkNode::convert() const +{ + return QHelpDataIndexItem(m_name, m_name, m_href); +} + +HtmlHelpParser::HtmlHelpParser() + : m_state(WAIT_NAME_OR_LOCAL), + m_tree(new ContainerNode) +{ + m_nodeStack.push(m_tree); +} + +HtmlHelpParser::~HtmlHelpParser() +{ + delete m_tree; +} + +void HtmlHelpParser::parseFile(const QString &fileName) +{ + QFile file(fileName); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + return; + } + + QString content; + while (!file.atEnd()) { + content.append(file.readLine()); + } + file.close(); + + parseString(content); +} + +void HtmlHelpParser::parseString(const QString &text) +{ + SimpleHtmlParser parser; + parser.parse(text, this); +} + +const QList HtmlHelpParser::stealIndex() +{ + QSet noDupEntries; + QSet noDupIds; + return m_tree->convertAndStealChildren(0, QString(), + noDupEntries, noDupIds); +} + +const QList HtmlHelpParser::stealToc( + const QString &rootName, const QString &rootHref) +{ + Q_ASSERT(m_tree != NULL); + QHelpDataContentItem * const convertedTree + = m_tree->convertAndStealChildren(NULL, rootName, rootHref); + Q_ASSERT(convertedTree != NULL); +#ifdef HELP_LIB_DATA_DEBUG + convertedTree->dump(); +#endif + QList res; + res.append(convertedTree); + return res; +} + +void HtmlHelpParser::onTagOpen(const QString &tagName, + const QStringList &attributes) +{ +#ifdef HELP_LIB_DATA_DEBUG + qDebug() << "OPEN" << tagName << "ATTS" << attributes; +#endif + if (tagName == QString("ul")) { + ContainerNode * const top = m_nodeStack.top(); + Q_ASSERT(top != NULL); + if (!((top == m_tree) && (m_tree->m_children.isEmpty()))) { + ContainerNode * const node = new ContainerNode; + Q_ASSERT(node != NULL); + top->m_children.append(node); + m_nodeStack.push(node); + } + } else if (tagName == QString("param")) { + const QString name = findAttribute(attributes, "name"); + if (name.isEmpty()) { + return; + } + const QString value = findAttribute(attributes, "value"); + if (value.isEmpty()) { + return; + } + + if (name == QString("Name")) { + switch (m_state) { + case WAIT_NAME_OR_LOCAL: + // First entry is a name, nothing fixed yet + m_name = value; + m_state = WAIT_NAME_NAME_OR_NAME_LOCAL; + break; + + case WAIT_NAME_NAME_OR_NAME_LOCAL: + // Second entry is a name too -> must be a list + m_subName = value; + m_state = WAIT_NAME_NAME_LOCAL; + break; + + case WAIT_NAME_NAME_LOCAL_NAME: + m_subName = value; + m_state = WAIT_NAME_NAME_LOCAL; + break; + + case WAIT_LOCAL_NAME: + { ContainerNode * const top = m_nodeStack.top(); + Q_ASSERT(top != NULL); + top->m_children.append(new LinkNode(value, "", m_href)); } + m_state = WAIT_NAME_OR_LOCAL; + break; + + default: + ; // NOOP + } + } else if (name == QString("Local")) { + switch (m_state) { + case WAIT_NAME_OR_LOCAL: + // First entry is a local -> must be a single link, name follows + m_href = value; + m_state = WAIT_LOCAL_NAME; + break; + + case WAIT_NAME_NAME_OR_NAME_LOCAL: + // Second entry is a href -> must be a single link + { ContainerNode * const top = m_nodeStack.top(); + Q_ASSERT(top != NULL); + top->m_children.append(new LinkNode(m_name, "", value)); } + + m_state = WAIT_NAME_OR_LOCAL; + break; + + case WAIT_NAME_NAME_LOCAL: + { ContainerNode * const top = m_nodeStack.top(); + Q_ASSERT(top != NULL); + top->m_children.append(new LinkNode(m_name, m_subName, value)); } + + m_state = WAIT_NAME_NAME_LOCAL_NAME; + break; + + default: + ; // NOOP + } + } + } +} + +void HtmlHelpParser::onTagClose(const QString &tagName) +{ +#ifdef HELP_LIB_DATA_DEBUG + qDebug() << "CLOSE" << tagName; +#endif + if (tagName == QString("ul")) { + if (m_nodeStack.size() > 1) { + m_nodeStack.pop(); + } + } else if (tagName == QString("object")) { + switch (m_state) { + case WAIT_NAME_NAME_OR_NAME_LOCAL: + // Name only, no href + { ContainerNode * const top = m_nodeStack.top(); + Q_ASSERT(top != NULL); + top->m_children.append(new LinkNode(m_name, "", "")); } + break; + + default: + ; // NOOP + } + + m_state = WAIT_NAME_OR_LOCAL; + m_name.clear(); + m_subName.clear(); + } +} + +void HtmlHelpParser::onTextChunk(const QString & /*text*/) +{ + // NOOP +} + +void HtmlHelpParser::onStop() +{ + m_nodeStack.clear(); +#ifdef HELP_LIB_DATA_DEBUG + m_tree->dump(); +#endif +} + +/*static*/ QString HtmlHelpParser::findAttribute(const QStringList &list, + const QString &key) +{ + const int count = list.count(); + for (int i = 0; i < count - 1; i += 2) { + if (list.at(i) == key) { + return list.at(i + 1); + } + } + return QString(); +} + +QT_END_NAMESPACE diff --git a/htmlhelpparser_p.h b/htmlhelpparser_p.h new file mode 100644 index 0000000..277147f --- /dev/null +++ b/htmlhelpparser_p.h @@ -0,0 +1,81 @@ +/**************************************************************************** + ** + ** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies). + ** Contact: Nokia Corporation (qt-info@nokia.com) + ** + ** This file is part of the doxygen2qthelp project on Trolltech Labs. + ** + ** This file may be used under the terms of the GNU General Public + ** License version 2.0 or 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 GNU + ** General Public Licensing requirements will be met: + ** http://www.fsf.org/licensing/licenses/info/GPLv2.html and + ** http://www.gnu.org/copyleft/gpl.html. + ** + ** If you are unsure which license is appropriate for your use, please + ** contact the sales department at qt-sales@nokia.com. + ** + ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + ** + ****************************************************************************/ + +#ifndef HTMLHELPPARSER_P_H +#define HTMLHELPPARSER_P_H + +#include "simplehtmlreceiver_p.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class ContainerNode; +struct QHelpDataIndexItem; +class QHelpDataContentItem; + + +class HtmlHelpParser : public SimpleHtmlReceiver +{ +private: + enum State { + WAIT_NAME_OR_LOCAL, + WAIT_NAME_NAME_OR_NAME_LOCAL, + WAIT_LOCAL_NAME, + WAIT_NAME_NAME_LOCAL_NAME, + WAIT_NAME_NAME_LOCAL + }; + +public: + HtmlHelpParser(); + ~HtmlHelpParser(); + + void parseFile(const QString &fileName); + void parseString(const QString &text); + const QList stealIndex(); + const QList stealToc(const QString &rootName, + const QString &rootHref); + +private: + void onTagOpen(const QString &tagName, const QStringList &attributes); + void onTagClose(const QString &tagName); + void onTextChunk(const QString &text); + void onStop(); + + void finishLastNode(); + + static QString findAttribute(const QStringList &list, const QString &key); + + QString m_name; + QString m_href; + QString m_subName; + State m_state; + ContainerNode * const m_tree; + QStack m_nodeStack; +}; + +QT_END_NAMESPACE + +#endif // HTMLHELPPARSER_P_H + diff --git a/qthelpprojectwriter.cpp b/qthelpprojectwriter.cpp new file mode 100644 index 0000000..22ea738 --- /dev/null +++ b/qthelpprojectwriter.cpp @@ -0,0 +1,172 @@ +/**************************************************************************** + ** + ** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies). + ** Contact: Nokia Corporation (qt-info@nokia.com) + ** + ** This file is part of the doxygen2qthelp project on Trolltech Labs. + ** + ** This file may be used under the terms of the GNU General Public + ** License version 2.0 or 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 GNU + ** General Public Licensing requirements will be met: + ** http://www.fsf.org/licensing/licenses/info/GPLv2.html and + ** http://www.gnu.org/copyleft/gpl.html. + ** + ** If you are unsure which license is appropriate for your use, please + ** contact the sales department at qt-sales@nokia.com. + ** + ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + ** + ****************************************************************************/ + +#include "qthelpprojectwriter_p.h" +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QtHelpProjectWriter::QtHelpProjectWriter() + : m_helpData(NULL) { + setAutoFormatting(true); +} + +void QtHelpProjectWriter::writeHead() +{ + writeStartDocument(); + writeStartElement(QLatin1String("QtHelpProject")); + writeAttribute(QLatin1String("version"), QLatin1String("1.0")); + + Q_ASSERT(m_helpData != NULL); + writeTextElement(QLatin1String("namespace"), + m_helpData->namespaceName()); + writeTextElement(QLatin1String("virtualFolder"), + m_helpData->virtualFolder()); +} + +void QtHelpProjectWriter::writeCustomFilters() +{ + Q_ASSERT(m_helpData != NULL); + const QList filters = m_helpData->customFilters(); + foreach (const QHelpDataCustomFilter & filter, filters) { + writeStartElement(QLatin1String("customFilter")); + writeAttribute(QLatin1String("name"), filter.name); + foreach (const QString & att, filter.filterAttributes) { + writeTextElement(QLatin1String("filterAttribute"), att); + } + writeEndElement(); + } +} + +void QtHelpProjectWriter::writeTocItems( + const QList &items) +{ + if (items.isEmpty()) { + return; + } + + foreach (const QHelpDataContentItem *item, items) { + const QList children = item->children(); + if (children.isEmpty()) { + writeEmptyElement(QLatin1String("section")); + } else { + writeStartElement(QLatin1String("section")); + } + writeAttribute(QLatin1String("title"), item->title()); + writeAttribute(QLatin1String("ref"), item->reference()); + if (!children.isEmpty()) { + writeTocItems(children); + writeEndElement(); + } + } +} + +void QtHelpProjectWriter::writeFilterSections() +{ + Q_ASSERT(m_helpData != NULL); + const QList sections = m_helpData->filterSections(); + foreach (const QHelpDataFilterSection & section, sections) { + // Filter section + writeStartElement(QLatin1String("filterSection")); + + // Filter attributes + const QStringList atts = section.filterAttributes(); + foreach (const QString & att, atts) { + writeTextElement(QLatin1String("filterAttribute"), att); + } + + // TOC + writeStartElement(QLatin1String("toc")); + writeTocItems(section.contents()); + writeEndElement(); + + // Keywords + const QList indices = section.indices(); + writeStartElement(QLatin1String("keywords")); + foreach (const QHelpDataIndexItem & indexItem, indices) { + const bool nameEmpty = indexItem.name.isEmpty(); + const bool idEmpty = indexItem.identifier.isEmpty(); + if (nameEmpty && idEmpty) { + continue; + } + + writeEmptyElement(QLatin1String("keyword")); + if (!nameEmpty) { + writeAttribute(QLatin1String("name"), indexItem.name); + } + if (!idEmpty) { + writeAttribute(QLatin1String("id"), indexItem.identifier); + } + writeAttribute(QLatin1String("ref"), indexItem.reference); + } + writeEndElement(); + + // Files + const QStringList files = section.files(); + writeStartElement(QLatin1String("files")); + foreach (const QString & file, files) { + writeTextElement(QLatin1String("file"), file); + } + writeEndElement(); + + // Filter section + writeEndElement(); + } +} + +void QtHelpProjectWriter::writeBody() +{ + writeCustomFilters(); + writeFilterSections(); +} + +void QtHelpProjectWriter::writeFoot() +{ + writeEndElement(); + writeEndDocument(); +} + +bool QtHelpProjectWriter::writeFile(const QHelpDataInterface *helpData, + const QString &fileName) +{ + Q_ASSERT(helpData != NULL); + m_helpData = helpData; + + QFile out(fileName); + if (!out.open(QIODevice::WriteOnly)) + return false; + setDevice(&out); + + writeHead(); + writeBody(); + writeFoot(); + + out.close(); + return true; +} + +QT_END_NAMESPACE diff --git a/qthelpprojectwriter_p.h b/qthelpprojectwriter_p.h new file mode 100644 index 0000000..0d86f01 --- /dev/null +++ b/qthelpprojectwriter_p.h @@ -0,0 +1,59 @@ +/**************************************************************************** + ** + ** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies). + ** Contact: Nokia Corporation (qt-info@nokia.com) + ** + ** This file is part of the doxygen2qthelp project on Trolltech Labs. + ** + ** This file may be used under the terms of the GNU General Public + ** License version 2.0 or 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 GNU + ** General Public Licensing requirements will be met: + ** http://www.fsf.org/licensing/licenses/info/GPLv2.html and + ** http://www.gnu.org/copyleft/gpl.html. + ** + ** If you are unsure which license is appropriate for your use, please + ** contact the sales department at qt-sales@nokia.com. + ** + ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + ** + ****************************************************************************/ + +#ifndef QTHELPPROJECTWRITER_H +#define QTHELPPROJECTWRITER_H + +#include + +QT_BEGIN_NAMESPACE + +class QHelpDataInterface; +class QHelpDataContentItem; + +template +class QList; + +class QtHelpProjectWriter : public QXmlStreamWriter +{ +private: + const QHelpDataInterface *m_helpData; + +public: + QtHelpProjectWriter(); + bool writeFile(const QHelpDataInterface *helpData, + const QString &fileName); + +private: + void writeHead(); + void writeCustomFilters(); + void writeTocItems(const QList &items); + void writeFilterSections(); + void writeBody(); + void writeFoot(); +}; + +QT_END_NAMESPACE + +#endif // QTHELPPROJECTWRITER_H + diff --git a/simplehtmlparser.cpp b/simplehtmlparser.cpp new file mode 100644 index 0000000..579564b --- /dev/null +++ b/simplehtmlparser.cpp @@ -0,0 +1,600 @@ +/**************************************************************************** + ** + ** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies). + ** Contact: Nokia Corporation (qt-info@nokia.com) + ** + ** This file is part of the doxygen2qthelp project on Trolltech Labs. + ** + ** This file may be used under the terms of the GNU General Public + ** License version 2.0 or 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 GNU + ** General Public Licensing requirements will be met: + ** http://www.fsf.org/licensing/licenses/info/GPLv2.html and + ** http://www.gnu.org/copyleft/gpl.html. + ** + ** If you are unsure which license is appropriate for your use, please + ** contact the sales department at qt-sales@nokia.com. + ** + ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + ** + ****************************************************************************/ + +#include "simplehtmlparser_p.h" +#include "simplehtmlreceiver_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +// see also tst_qtextdocumentfragment.cpp +#define MAX_ENTITY 258 +static const struct QTextHtmlEntity +{ + const char *name; + quint16 code; +} entities[MAX_ENTITY]= { + { "AElig", 0x00c6 }, + { "AMP", 38 }, + { "Aacute", 0x00c1 }, + { "Acirc", 0x00c2 }, + { "Agrave", 0x00c0 }, + { "Alpha", 0x0391 }, + { "Aring", 0x00c5 }, + { "Atilde", 0x00c3 }, + { "Auml", 0x00c4 }, + { "Beta", 0x0392 }, + { "Ccedil", 0x00c7 }, + { "Chi", 0x03a7 }, + { "Dagger", 0x2021 }, + { "Delta", 0x0394 }, + { "ETH", 0x00d0 }, + { "Eacute", 0x00c9 }, + { "Ecirc", 0x00ca }, + { "Egrave", 0x00c8 }, + { "Epsilon", 0x0395 }, + { "Eta", 0x0397 }, + { "Euml", 0x00cb }, + { "GT", 62 }, + { "Gamma", 0x0393 }, + { "Iacute", 0x00cd }, + { "Icirc", 0x00ce }, + { "Igrave", 0x00cc }, + { "Iota", 0x0399 }, + { "Iuml", 0x00cf }, + { "Kappa", 0x039a }, + { "LT", 60 }, + { "Lambda", 0x039b }, + { "Mu", 0x039c }, + { "Ntilde", 0x00d1 }, + { "Nu", 0x039d }, + { "OElig", 0x0152 }, + { "Oacute", 0x00d3 }, + { "Ocirc", 0x00d4 }, + { "Ograve", 0x00d2 }, + { "Omega", 0x03a9 }, + { "Omicron", 0x039f }, + { "Oslash", 0x00d8 }, + { "Otilde", 0x00d5 }, + { "Ouml", 0x00d6 }, + { "Phi", 0x03a6 }, + { "Pi", 0x03a0 }, + { "Prime", 0x2033 }, + { "Psi", 0x03a8 }, + { "QUOT", 34 }, + { "Rho", 0x03a1 }, + { "Scaron", 0x0160 }, + { "Sigma", 0x03a3 }, + { "THORN", 0x00de }, + { "Tau", 0x03a4 }, + { "Theta", 0x0398 }, + { "Uacute", 0x00da }, + { "Ucirc", 0x00db }, + { "Ugrave", 0x00d9 }, + { "Upsilon", 0x03a5 }, + { "Uuml", 0x00dc }, + { "Xi", 0x039e }, + { "Yacute", 0x00dd }, + { "Yuml", 0x0178 }, + { "Zeta", 0x0396 }, + { "aacute", 0x00e1 }, + { "acirc", 0x00e2 }, + { "acute", 0x00b4 }, + { "aelig", 0x00e6 }, + { "agrave", 0x00e0 }, + { "alefsym", 0x2135 }, + { "alpha", 0x03b1 }, + { "amp", 38 }, + { "and", 0x22a5 }, + { "ang", 0x2220 }, + { "apos", 0x0027 }, + { "aring", 0x00e5 }, + { "asymp", 0x2248 }, + { "atilde", 0x00e3 }, + { "auml", 0x00e4 }, + { "bdquo", 0x201e }, + { "beta", 0x03b2 }, + { "brvbar", 0x00a6 }, + { "bull", 0x2022 }, + { "cap", 0x2229 }, + { "ccedil", 0x00e7 }, + { "cedil", 0x00b8 }, + { "cent", 0x00a2 }, + { "chi", 0x03c7 }, + { "circ", 0x02c6 }, + { "clubs", 0x2663 }, + { "cong", 0x2245 }, + { "copy", 0x00a9 }, + { "crarr", 0x21b5 }, + { "cup", 0x222a }, + { "curren", 0x00a4 }, + { "dArr", 0x21d3 }, + { "dagger", 0x2020 }, + { "darr", 0x2193 }, + { "deg", 0x00b0 }, + { "delta", 0x03b4 }, + { "diams", 0x2666 }, + { "divide", 0x00f7 }, + { "eacute", 0x00e9 }, + { "ecirc", 0x00ea }, + { "egrave", 0x00e8 }, + { "empty", 0x2205 }, + { "emsp", 0x2003 }, + { "ensp", 0x2002 }, + { "epsilon", 0x03b5 }, + { "equiv", 0x2261 }, + { "eta", 0x03b7 }, + { "eth", 0x00f0 }, + { "euml", 0x00eb }, + { "euro", 0x20ac }, + { "exist", 0x2203 }, + { "fnof", 0x0192 }, + { "forall", 0x2200 }, + { "frac12", 0x00bd }, + { "frac14", 0x00bc }, + { "frac34", 0x00be }, + { "frasl", 0x2044 }, + { "gamma", 0x03b3 }, + { "ge", 0x2265 }, + { "gt", 62 }, + { "hArr", 0x21d4 }, + { "harr", 0x2194 }, + { "hearts", 0x2665 }, + { "hellip", 0x2026 }, + { "iacute", 0x00ed }, + { "icirc", 0x00ee }, + { "iexcl", 0x00a1 }, + { "igrave", 0x00ec }, + { "image", 0x2111 }, + { "infin", 0x221e }, + { "int", 0x222b }, + { "iota", 0x03b9 }, + { "iquest", 0x00bf }, + { "isin", 0x2208 }, + { "iuml", 0x00ef }, + { "kappa", 0x03ba }, + { "lArr", 0x21d0 }, + { "lambda", 0x03bb }, + { "lang", 0x2329 }, + { "laquo", 0x00ab }, + { "larr", 0x2190 }, + { "lceil", 0x2308 }, + { "ldquo", 0x201c }, + { "le", 0x2264 }, + { "lfloor", 0x230a }, + { "lowast", 0x2217 }, + { "loz", 0x25ca }, + { "lrm", 0x200e }, + { "lsaquo", 0x2039 }, + { "lsquo", 0x2018 }, + { "lt", 60 }, + { "macr", 0x00af }, + { "mdash", 0x2014 }, + { "micro", 0x00b5 }, + { "middot", 0x00b7 }, + { "minus", 0x2212 }, + { "mu", 0x03bc }, + { "nabla", 0x2207 }, + { "nbsp", 0x00a0 }, + { "ndash", 0x2013 }, + { "ne", 0x2260 }, + { "ni", 0x220b }, + { "not", 0x00ac }, + { "notin", 0x2209 }, + { "nsub", 0x2284 }, + { "ntilde", 0x00f1 }, + { "nu", 0x03bd }, + { "oacute", 0x00f3 }, + { "ocirc", 0x00f4 }, + { "oelig", 0x0153 }, + { "ograve", 0x00f2 }, + { "oline", 0x203e }, + { "omega", 0x03c9 }, + { "omicron", 0x03bf }, + { "oplus", 0x2295 }, + { "or", 0x22a6 }, + { "ordf", 0x00aa }, + { "ordm", 0x00ba }, + { "oslash", 0x00f8 }, + { "otilde", 0x00f5 }, + { "otimes", 0x2297 }, + { "ouml", 0x00f6 }, + { "para", 0x00b6 }, + { "part", 0x2202 }, + { "percnt", 0x0025 }, + { "permil", 0x2030 }, + { "perp", 0x22a5 }, + { "phi", 0x03c6 }, + { "pi", 0x03c0 }, + { "piv", 0x03d6 }, + { "plusmn", 0x00b1 }, + { "pound", 0x00a3 }, + { "prime", 0x2032 }, + { "prod", 0x220f }, + { "prop", 0x221d }, + { "psi", 0x03c8 }, + { "quot", 34 }, + { "rArr", 0x21d2 }, + { "radic", 0x221a }, + { "rang", 0x232a }, + { "raquo", 0x00bb }, + { "rarr", 0x2192 }, + { "rceil", 0x2309 }, + { "rdquo", 0x201d }, + { "real", 0x211c }, + { "reg", 0x00ae }, + { "rfloor", 0x230b }, + { "rho", 0x03c1 }, + { "rlm", 0x200f }, + { "rsaquo", 0x203a }, + { "rsquo", 0x2019 }, + { "sbquo", 0x201a }, + { "scaron", 0x0161 }, + { "sdot", 0x22c5 }, + { "sect", 0x00a7 }, + { "shy", 0x00ad }, + { "sigma", 0x03c3 }, + { "sigmaf", 0x03c2 }, + { "sim", 0x223c }, + { "spades", 0x2660 }, + { "sub", 0x2282 }, + { "sube", 0x2286 }, + { "sum", 0x2211 }, + { "sup", 0x2283 }, + { "sup1", 0x00b9 }, + { "sup2", 0x00b2 }, + { "sup3", 0x00b3 }, + { "supe", 0x2287 }, + { "szlig", 0x00df }, + { "tau", 0x03c4 }, + { "there4", 0x2234 }, + { "theta", 0x03b8 }, + { "thetasym", 0x03d1 }, + { "thinsp", 0x2009 }, + { "thorn", 0x00fe }, + { "tilde", 0x02dc }, + { "times", 0x00d7 }, + { "trade", 0x2122 }, + { "uArr", 0x21d1 }, + { "uacute", 0x00fa }, + { "uarr", 0x2191 }, + { "ucirc", 0x00fb }, + { "ugrave", 0x00f9 }, + { "uml", 0x00a8 }, + { "upsih", 0x03d2 }, + { "upsilon", 0x03c5 }, + { "uuml", 0x00fc }, + { "weierp", 0x2118 }, + { "xi", 0x03be }, + { "yacute", 0x00fd }, + { "yen", 0x00a5 }, + { "yuml", 0x00ff }, + { "zeta", 0x03b6 }, + { "zwj", 0x200d }, + { "zwnj", 0x200c } +}; + +static bool operator<(const QString &entityStr, const QTextHtmlEntity &entity) +{ + return entityStr < QLatin1String(entity.name); +} + +static bool operator<(const QTextHtmlEntity &entity, const QString &entityStr) +{ + return QLatin1String(entity.name) < entityStr; +} + +static QChar resolveEntity(const QString &entity) +{ + const QTextHtmlEntity *start = &entities[0]; + const QTextHtmlEntity *end = &entities[MAX_ENTITY]; + const QTextHtmlEntity *e = qBinaryFind(start, end, entity); + if (e == end) + return QChar(); + return e->code; +} + +static const uint windowsLatin1ExtendedCharacters[0xA0 - 0x80] = { + 0x20ac, // 0x80 + 0x0081, // 0x81 direct mapping + 0x201a, // 0x82 + 0x0192, // 0x83 + 0x201e, // 0x84 + 0x2026, // 0x85 + 0x2020, // 0x86 + 0x2021, // 0x87 + 0x02C6, // 0x88 + 0x2030, // 0x89 + 0x0160, // 0x8A + 0x2039, // 0x8B + 0x0152, // 0x8C + 0x008D, // 0x8D direct mapping + 0x017D, // 0x8E + 0x008F, // 0x8F directmapping + 0x0090, // 0x90 directmapping + 0x2018, // 0x91 + 0x2019, // 0x92 + 0x201C, // 0x93 + 0X201D, // 0x94 + 0x2022, // 0x95 + 0x2013, // 0x96 + 0x2014, // 0x97 + 0x02DC, // 0x98 + 0x2122, // 0x99 + 0x0161, // 0x9A + 0x203A, // 0x9B + 0x0153, // 0x9C + 0x009D, // 0x9D direct mapping + 0x017E, // 0x9E + 0x0178 // 0x9F +}; + +void SimpleHtmlParser::parse(const QString &text, SimpleHtmlReceiver *receiver) +{ + m_receiver = receiver; + txt = text; + pos = 0; + len = txt.length(); + parse(); + Q_ASSERT(m_receiver != NULL); + m_receiver->onStop(); + //dumpHtml(); +} + +void SimpleHtmlParser::eatSpace() +{ + while (pos < len && txt.at(pos).isSpace() && txt.at(pos) + != QChar::ParagraphSeparator) { + pos++; + } +} + +void SimpleHtmlParser::parse() +{ + QString buffer; + while (pos < len) { + QChar c = txt.at(pos++); + if (c == QLatin1Char('<')) { + if (!buffer.isEmpty()) { + Q_ASSERT(m_receiver != NULL); + m_receiver->onTextChunk(buffer); + buffer.clear(); + } + parseTag(); + } else if (c == QLatin1Char('&')) { + buffer += parseEntity(); + } else { + buffer += c; + } + } + + if (!buffer.isEmpty()) { + Q_ASSERT(m_receiver != NULL); + m_receiver->onTextChunk(buffer); + buffer.clear(); + } +} + +// parses a tag after "<" +void SimpleHtmlParser::parseTag() +{ + eatSpace(); + + // handle comments and other exclamation mark declarations + if (hasPrefix(QLatin1Char('!'))) { + parseExclamationTag(); + return; + } + + // if close tag just close + if (hasPrefix(QLatin1Char('/'))) { + parseCloseTag(); + return; + } + + // parse tag name + const QString tagName = parseWord().toLower(); + + // _need_ at least one space after the tag name, + // otherwise there can't be attributes + QStringList attributes; + if (pos < len && txt.at(pos).isSpace()) { + attributes = parseAttributes(); + } + + Q_ASSERT(m_receiver != NULL); + m_receiver->onTagOpen(tagName, attributes); + + // finish tag + bool tagClosed = false; + while (pos < len && txt.at(pos) != QLatin1Char('>')) { + if (txt.at(pos) == QLatin1Char('/')) + tagClosed = true; + + pos++; + } + pos++; +} + +// parses a tag beginning with "/" +void SimpleHtmlParser::parseCloseTag() +{ + ++pos; + const QString tagName = parseWord().toLower().trimmed(); + Q_ASSERT(m_receiver != NULL); + m_receiver->onTagClose(tagName); + + while (pos < len) { + QChar c = txt.at(pos++); + if (c == QLatin1Char('>')) + break; + } +} + +// parses a tag beginning with "!" +void SimpleHtmlParser::parseExclamationTag() +{ + ++pos; + if (hasPrefix(QLatin1Char('-'),1) && hasPrefix(QLatin1Char('-'),2)) { + pos += 3; + // eat comments + int end = txt.indexOf(QLatin1String("-->"), pos); + pos = (end >= 0 ? end + 3 : len); + } else { + // eat internal tags + while (pos < len) { + QChar c = txt.at(pos++); + if (c == QLatin1Char('>')) + break; + } + } +} + +// parses an entity after "&", and returns it +QString SimpleHtmlParser::parseEntity() +{ + int recover = pos; + QString entity; + while (pos < len) { + QChar c = txt.at(pos++); + if (c.isSpace() || pos - recover > 9) { + goto error; + } + if (c == QLatin1Char(';')) + break; + entity += c; + } + { + QChar resolved = resolveEntity(entity); + if (!resolved.isNull()) + return QString(resolved); + } + if (entity.length() > 1 && entity.at(0) == QLatin1Char('#')) { + entity.remove(0, 1); // removing leading # + + int base = 10; + bool ok = false; + + if (entity.at(0).toLower() == QLatin1Char('x')) { // hex entity? + entity.remove(0, 1); + base = 16; + } + + uint uc = entity.toUInt(&ok, base); + if (ok) { + if (uc >= 0x80 && uc < 0x80 + + (sizeof(windowsLatin1ExtendedCharacters) + /sizeof(windowsLatin1ExtendedCharacters[0]))) + uc = windowsLatin1ExtendedCharacters[uc - 0x80]; + QString str; + if (uc > 0xffff) { + // surrogate pair + uc -= 0x10000; + ushort high = uc/0x400 + 0xd800; + ushort low = uc%0x400 + 0xdc00; + str.append(QChar(high)); + str.append(QChar(low)); + } else { + str.append(QChar(uc)); + } + return str; + } + } +error: + pos = recover; + return QLatin1String("&"); +} + +// parses one word, possibly quoted, and returns it +QString SimpleHtmlParser::parseWord() +{ + QString word; + if (hasPrefix(QLatin1Char('\"'))) { // double quotes + ++pos; + while (pos < len) { + QChar c = txt.at(pos++); + if (c == QLatin1Char('\"')) + break; + else if (c == QLatin1Char('&')) + word += parseEntity(); + else + word += c; + } + } else if (hasPrefix(QLatin1Char('\''))) { // single quotes + ++pos; + while (pos < len) { + QChar c = txt.at(pos++); + if (c == QLatin1Char('\'')) + break; + else + word += c; + } + } else { // normal text + while (pos < len) { + QChar c = txt.at(pos++); + if (c == QLatin1Char('>') + || (c == QLatin1Char('/') && hasPrefix(QLatin1Char('>'), 1)) + || c == QLatin1Char('<') + || c == QLatin1Char('=') + || c.isSpace()) { + --pos; + break; + } + if (c == QLatin1Char('&')) + word += parseEntity(); + else + word += c; + } + } + return word; +} + +QStringList SimpleHtmlParser::parseAttributes() +{ + QStringList attrs; + + while (pos < len) { + eatSpace(); + if (hasPrefix(QLatin1Char('>')) || hasPrefix(QLatin1Char('/'))) + break; + QString key = parseWord().toLower(); + QString value = QLatin1String("1"); + if (key.size() == 0) + break; + eatSpace(); + if (hasPrefix(QLatin1Char('='))){ + pos++; + eatSpace(); + value = parseWord(); + } + if (value.size() == 0) + continue; + attrs << key << value; + } + + return attrs; +} + +QT_END_NAMESPACE diff --git a/simplehtmlparser_p.h b/simplehtmlparser_p.h new file mode 100644 index 0000000..d3c36ec --- /dev/null +++ b/simplehtmlparser_p.h @@ -0,0 +1,60 @@ +/**************************************************************************** + ** + ** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies). + ** Contact: Nokia Corporation (qt-info@nokia.com) + ** + ** This file is part of the doxygen2qthelp project on Trolltech Labs. + ** + ** This file may be used under the terms of the GNU General Public + ** License version 2.0 or 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 GNU + ** General Public Licensing requirements will be met: + ** http://www.fsf.org/licensing/licenses/info/GPLv2.html and + ** http://www.gnu.org/copyleft/gpl.html. + ** + ** If you are unsure which license is appropriate for your use, please + ** contact the sales department at qt-sales@nokia.com. + ** + ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + ** + ****************************************************************************/ + +#ifndef SIMPLEHTMLPARSER_H +#define SIMPLEHTMLPARSER_H + +#include + +QT_BEGIN_NAMESPACE + +class SimpleHtmlReceiver; + +class SimpleHtmlParser +{ +public: + void parse(const QString &text, SimpleHtmlReceiver *receiver); + +private: + void parse(); + void parseTag(); + void parseCloseTag(); + void parseExclamationTag(); + QString parseEntity(); + QString parseWord(); + QStringList parseAttributes(); + void eatSpace(); + inline bool hasPrefix(QChar c, int lookahead = 0) const + {return pos + lookahead < len && txt.at(pos) == c; } + + SimpleHtmlReceiver *m_receiver; + QString txt; + int pos; + int len; + +}; + +QT_END_NAMESPACE + +#endif // SIMPLEHTMLPARSER_H + diff --git a/simplehtmlreceiver_p.h b/simplehtmlreceiver_p.h new file mode 100644 index 0000000..fbc1bd9 --- /dev/null +++ b/simplehtmlreceiver_p.h @@ -0,0 +1,49 @@ +/**************************************************************************** + ** + ** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies). + ** Contact: Nokia Corporation (qt-info@nokia.com) + ** + ** This file is part of the doxygen2qthelp project on Trolltech Labs. + ** + ** This file may be used under the terms of the GNU General Public + ** License version 2.0 or 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 GNU + ** General Public Licensing requirements will be met: + ** http://www.fsf.org/licensing/licenses/info/GPLv2.html and + ** http://www.gnu.org/copyleft/gpl.html. + ** + ** If you are unsure which license is appropriate for your use, please + ** contact the sales department at qt-sales@nokia.com. + ** + ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + ** + ****************************************************************************/ + +#ifndef SIMPLEHTMLRECEIVER_H +#define SIMPLEHTMLRECEIVER_H + +#include + +QT_BEGIN_NAMESPACE + +class QStringList; + +class SimpleHtmlReceiver +{ +public: + virtual ~SimpleHtmlReceiver() { } + + virtual void onTagOpen(const QString &tagName, + const QStringList & attributes) = 0; + virtual void onTagClose(const QString &tagName) = 0; + virtual void onTextChunk(const QString &text) = 0; + virtual void onStop() = 0; + +}; + +QT_END_NAMESPACE + +#endif // SIMPLEHTMLRECEIVER_H + -- cgit v1.2.3