summaryrefslogtreecommitdiffstats
path: root/htmlhelpparser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'htmlhelpparser.cpp')
-rw-r--r--htmlhelpparser.cpp476
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