diff options
author | Tor Arne Vestbø <tor.arne.vestbo@nokia.com> | 2010-05-03 12:34:27 +0200 |
---|---|---|
committer | Tor Arne Vestbø <tor.arne.vestbo@nokia.com> | 2010-05-03 12:34:27 +0200 |
commit | 415b44ffb6ffcdeb4ad5b7cdd6d9d9e17ab3e47f (patch) | |
tree | d9417f99ef9665167668fe3047416a0d11e1518f /htmlhelpparser.cpp |
Diffstat (limited to 'htmlhelpparser.cpp')
-rw-r--r-- | htmlhelpparser.cpp | 476 |
1 files changed, 476 insertions, 0 deletions
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 <QtCore/QString> +#include <QtCore/QStringList> +#include <QtCore/QFile> +#include <QtCore/QHash> +#include <QtCore/QSet> + +#ifdef HELP_LIB_DATA_DEBUG +# include <QDebug> +#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<HtmlHelpNode *> m_children; + + ~ContainerNode() + { + foreach(const HtmlHelpNode * node, m_children) { + delete node; + } + } + + static void insertItem(QHelpDataIndexItem &item, QList<QHelpDataIndexItem> &dest, + QSet<QHelpDataIndexItem> &pastEntries, QSet<QString> &pastIds); + + QHelpDataContentItem * convertAndStealChildren( + QHelpDataContentItem * parent, + const QString &name = QString(), const QString &href = QString()); + QList<QHelpDataIndexItem> convertAndStealChildren(int level, + const QString &rootLabel, QSet<QHelpDataIndexItem> &pastEntries, + QSet<QString> &pastIds); + + bool isContainer() const { return true; } + ContainerNode * toContainer() const + { + return const_cast<ContainerNode *>(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<QHelpDataIndexItem> &dest, QSet<QHelpDataIndexItem> &pastEntries, + QSet<QString> &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<QHelpDataIndexItem> ContainerNode::convertAndStealChildren( + int level, const QString &rootLabel, + QSet<QHelpDataIndexItem> &pastEntries, QSet<QString> &pastIds) +{ + QList<QHelpDataIndexItem> 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<QHelpDataIndexItem> HtmlHelpParser::stealIndex() +{ + QSet<QHelpDataIndexItem> noDupEntries; + QSet<QString> noDupIds; + return m_tree->convertAndStealChildren(0, QString(), + noDupEntries, noDupIds); +} + +const QList<QHelpDataContentItem*> 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<QHelpDataContentItem*> 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 |