summaryrefslogtreecommitdiffstats
path: root/src/tools/qdoc/tree.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/qdoc/tree.cpp')
-rw-r--r--src/tools/qdoc/tree.cpp2358
1 files changed, 2358 insertions, 0 deletions
diff --git a/src/tools/qdoc/tree.cpp b/src/tools/qdoc/tree.cpp
new file mode 100644
index 0000000000..c52e45739a
--- /dev/null
+++ b/src/tools/qdoc/tree.cpp
@@ -0,0 +1,2358 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ tree.cpp
+*/
+
+#include <QDomDocument>
+#include "atom.h"
+#include "doc.h"
+#include "htmlgenerator.h"
+#include "location.h"
+#include "node.h"
+#include "text.h"
+#include "tree.h"
+#include <limits.h>
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+struct InheritanceBound
+{
+ Node::Access access;
+ QStringList basePath;
+ QString dataTypeWithTemplateArgs;
+ InnerNode* parent;
+
+ InheritanceBound()
+ : access(Node::Public) { }
+ InheritanceBound(Node::Access access0,
+ const QStringList& basePath0,
+ const QString& dataTypeWithTemplateArgs0,
+ InnerNode* parent)
+ : access(access0), basePath(basePath0),
+ dataTypeWithTemplateArgs(dataTypeWithTemplateArgs0),
+ parent(parent) { }
+};
+
+struct Target
+{
+ Node* node;
+ Atom* atom;
+ int priority;
+};
+
+typedef QMap<PropertyNode::FunctionRole, QString> RoleMap;
+typedef QMap<PropertyNode*, RoleMap> PropertyMap;
+typedef QMultiHash<QString, FakeNode*> FakeNodeHash;
+typedef QMultiHash<QString, Target> TargetHash;
+
+class TreePrivate
+{
+public:
+ QMap<ClassNode* , QList<InheritanceBound> > unresolvedInheritanceMap;
+ PropertyMap unresolvedPropertyMap;
+ NodeMultiMap groupMap;
+ NodeMultiMap qmlModuleMap;
+ QMultiMap<QString, QString> publicGroupMap;
+ FakeNodeHash fakeNodesByTitle;
+ TargetHash targetHash;
+ QList<QPair<ClassNode*,QString> > basesList;
+ QList<QPair<FunctionNode*,QString> > relatedList;
+};
+
+/*!
+ \class Tree
+
+ This class constructs and maintains a tree of instances of
+ Node and its many subclasses.
+ */
+
+/*!
+ The default constructor is the only constructor.
+ */
+Tree::Tree()
+ : roo(0, "")
+{
+ priv = new TreePrivate;
+}
+
+/*!
+ The destructor deletes the internal, private tree.
+ */
+Tree::~Tree()
+{
+ delete priv;
+}
+
+/*!
+ This function simply calls the const version of itself and
+ returns the result.
+ */
+Node* Tree::findNode(const QStringList& path, Node* relative, int findFlags, const Node* self)
+{
+ return const_cast<Node*>(const_cast<const Tree*>(this)->findNode(path,
+ relative,
+ findFlags,
+ self));
+}
+
+/*!
+ Searches the tree for a node that matches the \a path. The
+ search begins at \a start but can move up the parent chain
+ recursively if no match is found.
+ */
+const Node* Tree::findNode(const QStringList& path,
+ const Node* start,
+ int findFlags,
+ const Node* self) const
+{
+ const Node* current = start;
+ if (!current)
+ current = root();
+
+ /*
+ First, search for a node assuming we don't want a QML node.
+ If that search fails, search again assuming we do want a
+ QML node.
+ */
+ const Node* n = findNode(path,current,findFlags,self,false);
+ if (!n) {
+ n = findNode(path,current,findFlags,self,true);
+ }
+ return n;
+}
+
+/*!
+ This code in this function was extracted from the other
+ version of findNode() that has the same signature without
+ the last bool parameter \a qml. This function is called
+ only by that other findNode(). It can be called a second
+ time if the first call returns null. If \a qml is false,
+ the search will only match a node that is not a QML node.
+ If \a qml is true, the search will only match a node that
+ is a QML node.
+ */
+const Node* Tree::findNode(const QStringList& path,
+ const Node* start,
+ int findFlags,
+ const Node* self,
+ bool qml) const
+{
+ const Node* current = start;
+ do {
+ const Node* node = current;
+ int i;
+ int start_idx = 0;
+
+ /*
+ If the path contains one or two double colons ("::"),
+ check first to see if the first two path strings refer
+ to a QML element. If yes, that reference identifies a
+ QML class node.
+ */
+ if (qml && path.size() >= 2) {
+ QmlClassNode* qcn = QmlClassNode::moduleMap.value(path[0]+ "::" +path[1]);
+ if (qcn) {
+ node = qcn;
+ if (path.size() == 2)
+ return node;
+ start_idx = 2;
+ }
+ }
+
+ for (i = start_idx; i < path.size(); ++i) {
+ if (node == 0 || !node->isInnerNode())
+ break;
+
+ const Node* next = static_cast<const InnerNode*>(node)->findNode(path.at(i), qml);
+
+ if (!next && (findFlags & SearchEnumValues) && i == path.size()-1)
+ next = static_cast<const InnerNode*>(node)->findEnumNodeForValue(path.at(i));
+
+ if (!next && !qml && node->type() == Node::Class && (findFlags & SearchBaseClasses)) {
+ NodeList baseClasses = allBaseClasses(static_cast<const ClassNode*>(node));
+ foreach (const Node* baseClass, baseClasses) {
+ next = static_cast<const InnerNode*>(baseClass)->findNode(path.at(i));
+ if (!next && (findFlags & SearchEnumValues) && i == path.size() - 1)
+ next = static_cast<const InnerNode*>(baseClass)
+ ->findEnumNodeForValue(path.at(i));
+ if (next)
+ break;
+ }
+ }
+ node = next;
+ }
+ if (node && i == path.size()
+ && (!(findFlags & NonFunction) || node->type() != Node::Function
+ || ((FunctionNode*)node)->metaness() == FunctionNode::MacroWithoutParams)) {
+ if ((node != self) && (node->subType() != Node::QmlPropertyGroup)) {
+ if (node->subType() == Node::Collision) {
+ node = node->applyModuleIdentifier(start);
+ }
+ return node;
+ }
+ }
+ current = current->parent();
+ } while (current);
+
+ return 0;
+}
+
+/*!
+ Find the node with the specified \a path name of the
+ specified \a type.
+ */
+Node* Tree::findNode(const QStringList& path,
+ Node::Type type,
+ Node* relative,
+ int findFlags)
+{
+ return const_cast<Node*>(const_cast<const Tree*>(this)->findNode(path,
+ type,
+ relative,
+ findFlags));
+}
+
+/*!
+ This function just calls the const version of itself and returns
+ a pointer to the QML class node, or null.
+ */
+QmlClassNode* Tree::findQmlClassNode(const QString& module, const QString& element)
+{
+ return const_cast<QmlClassNode*>(const_cast<const Tree*>(this)->findQmlClassNode(module, element));
+}
+
+/*!
+ Find the node with the specified \a path name of the
+ specified \a type.
+ */
+const Node* Tree::findNode(const QStringList& path,
+ Node::Type type,
+ const Node* relative,
+ int findFlags) const
+{
+ const Node* node = findNode(path, relative, findFlags);
+ if (node != 0 && node->type() == type)
+ return node;
+ return 0;
+}
+
+/*!
+ Find the QML class node for the specified \a module and \a name
+ identifiers. The \a module identifier may be empty. If the module
+ identifier is empty, then begin by finding the FakeNode that has
+ the specified \a name. If that FakeNode is a QML class, return it.
+ If it is a collision node, return its current child, if the current
+ child is a QML class. If the collision node does not have a child
+ that is a QML class node, return 0.
+ */
+const QmlClassNode* Tree::findQmlClassNode(const QString& module, const QString& name) const
+{
+ if (module.isEmpty()) {
+ const Node* n = findNode(QStringList(name), Node::Fake);
+ if (n) {
+ if (n->subType() == Node::QmlClass)
+ return static_cast<const QmlClassNode*>(n);
+ else if (n->subType() == Node::Collision) {
+ const NameCollisionNode* ncn;
+ ncn = static_cast<const NameCollisionNode*>(n);
+ return static_cast<const QmlClassNode*>(ncn->findAny(Node::Fake,Node::QmlClass));
+ }
+ }
+ return 0;
+ }
+ return QmlClassNode::moduleMap.value(module + "::" + name);
+}
+
+/*!
+ First, search for a node with the specified \a name. If a matching
+ node is found, if it is a collision node, another collision with
+ this name has been found, so return the collision node. If the
+ matching node is not a collision node, the first collision for this
+ name has been found, so create a NameCollisionNode with the matching
+ node as its first child, and return a pointer to the new
+ NameCollisionNode. Otherwise return 0.
+ */
+NameCollisionNode* Tree::checkForCollision(const QString& name) const
+{
+ Node* n = const_cast<Node*>(findNode(QStringList(name)));
+ if (n) {
+ if (n->subType() == Node::Collision) {
+ NameCollisionNode* ncn = static_cast<NameCollisionNode*>(n);
+ return ncn;
+ }
+ if (n->isInnerNode())
+ return new NameCollisionNode(static_cast<InnerNode*>(n));
+ }
+ return 0;
+}
+
+/*!
+ This function is like checkForCollision() in that it searches
+ for a collision node with the specified \a name. But it doesn't
+ create anything. If it finds a match, it returns the pointer.
+ Otherwise it returns 0.
+ */
+NameCollisionNode* Tree::findCollisionNode(const QString& name) const
+{
+ Node* n = const_cast<Node*>(findNode(QStringList(name)));
+ if (n) {
+ if (n->subType() == Node::Collision) {
+ NameCollisionNode* ncn = static_cast<NameCollisionNode*>(n);
+ return ncn;
+ }
+ }
+ return 0;
+}
+
+/*!
+ This function just calls the const version of the same function
+ and returns the function node.
+ */
+FunctionNode* Tree::findFunctionNode(const QStringList& path,
+ Node* relative,
+ int findFlags)
+{
+ return const_cast<FunctionNode*>
+ (const_cast<const Tree*>(this)->findFunctionNode(path,relative,findFlags));
+}
+
+/*!
+ This function begins searching the tree at \a relative for
+ the \l {FunctionNode} {function node} identified by \a path.
+ The \a findFlags are used to restrict the search. If a node
+ that matches the \a path is found, it is returned. Otherwise,
+ 0 is returned. If \a relative is 0, the root of the tree is
+ used as the starting point.
+ */
+const FunctionNode* Tree::findFunctionNode(const QStringList& path,
+ const Node* relative,
+ int findFlags) const
+{
+ if (!relative)
+ relative = root();
+
+ /*
+ If the path contains two double colons ("::"), check
+ first to see if it is a reference to a QML method. If
+ it is a reference to a QML method, first look up the
+ QML class node in the QML module map.
+ */
+ if (path.size() == 3) {
+ QmlClassNode* qcn = QmlClassNode::moduleMap.value(path[0]+ "::" +path[1]);
+ if (qcn) {
+ return static_cast<const FunctionNode*>(qcn->findFunctionNode(path[2]));
+ }
+ }
+
+ do {
+ const Node* node = relative;
+ int i;
+
+ for (i = 0; i < path.size(); ++i) {
+ if (node == 0 || !node->isInnerNode())
+ break;
+
+ const Node* next;
+ if (i == path.size() - 1)
+ next = ((InnerNode*) node)->findFunctionNode(path.at(i));
+ else
+ next = ((InnerNode*) node)->findNode(path.at(i));
+
+ if (!next && node->type() == Node::Class &&
+ (findFlags & SearchBaseClasses)) {
+ NodeList baseClasses = allBaseClasses(static_cast<const ClassNode*>(node));
+ foreach (const Node* baseClass, baseClasses) {
+ if (i == path.size() - 1)
+ next = static_cast<const InnerNode*>(baseClass)->findFunctionNode(path.at(i));
+ else
+ next = static_cast<const InnerNode*>(baseClass)->findNode(path.at(i));
+
+ if (next)
+ break;
+ }
+ }
+
+ node = next;
+ }
+ if (node && i == path.size() && node->isFunction()) {
+ // CppCodeParser::processOtherMetaCommand ensures that reimplemented
+ // functions are private.
+ const FunctionNode* func = static_cast<const FunctionNode*>(node);
+ while (func->access() == Node::Private) {
+ const FunctionNode* from = func->reimplementedFrom();
+ if (from != 0) {
+ if (from->access() != Node::Private)
+ return from;
+ else
+ func = from;
+ }
+ else
+ break;
+ }
+ return func;
+ }
+ relative = relative->parent();
+ } while (relative);
+
+ return 0;
+}
+
+/*!
+ This function just calls the const version of itself and
+ returns the result.
+ */
+FunctionNode* Tree::findFunctionNode(const QStringList& parentPath,
+ const FunctionNode* clone,
+ Node* relative,
+ int findFlags)
+{
+ return const_cast<FunctionNode*>(
+ const_cast<const Tree*>(this)->findFunctionNode(parentPath,
+ clone,
+ relative,
+ findFlags));
+}
+
+/*!
+ This function first ignores the \a clone node and searches
+ for the node having the \a parentPath by calling the main
+ findFunction(\a {parentPath}, \a {relative}, \a {findFlags}).
+ If that search is successful, then it searches for the \a clone
+ in the found parent node.
+ */
+const FunctionNode* Tree::findFunctionNode(const QStringList& parentPath,
+ const FunctionNode* clone,
+ const Node* relative,
+ int findFlags) const
+{
+ const Node* parent = findNode(parentPath, relative, findFlags);
+ if (parent == 0 || !parent->isInnerNode()) {
+ return 0;
+ }
+ else {
+ return ((InnerNode*)parent)->findFunctionNode(clone);
+ }
+}
+
+static const int NumSuffixes = 3;
+static const char* const suffixes[NumSuffixes] = { "", "s", "es" };
+
+/*!
+ This function searches for a node with the specified \a title.
+ If \a relative is provided, use it to disambiguate if it has a
+ QML module identifier.
+ */
+const FakeNode* Tree::findFakeNodeByTitle(const QString& title, const Node* relative ) const
+{
+ for (int pass = 0; pass < NumSuffixes; ++pass) {
+ FakeNodeHash::const_iterator i = priv->fakeNodesByTitle.find(Doc::canonicalTitle(title + suffixes[pass]));
+ if (i != priv->fakeNodesByTitle.constEnd()) {
+ if (relative && !relative->qmlModuleIdentifier().isEmpty()) {
+ const FakeNode* fn = i.value();
+ InnerNode* parent = fn->parent();
+ if (parent && parent->type() == Node::Fake && parent->subType() == Node::Collision) {
+ const NodeList& nl = parent->childNodes();
+ NodeList::ConstIterator it = nl.begin();
+ while (it != nl.end()) {
+ if ((*it)->qmlModuleIdentifier() == relative->qmlModuleIdentifier()) {
+ /*
+ By returning here, we avoid printing all the duplicate
+ header warnings, which are not really duplicates now,
+ because of the QML module identifier being used as a
+ namespace qualifier.
+ */
+ fn = static_cast<const FakeNode*>(*it);
+ return fn;
+ }
+ ++it;
+ }
+ }
+ }
+ /*
+ Reporting all these duplicate section titles is probably
+ overkill. We should report the duplicate file and let
+ that suffice.
+ */
+ FakeNodeHash::const_iterator j = i;
+ ++j;
+ if (j != priv->fakeNodesByTitle.constEnd() && j.key() == i.key()) {
+ QList<Location> internalLocations;
+ while (j != priv->fakeNodesByTitle.constEnd()) {
+ if (j.key() == i.key() && j.value()->url().isEmpty())
+ internalLocations.append(j.value()->doc().location());
+ ++j;
+ }
+ if (internalLocations.size() > 0) {
+ i.value()->doc().location().warning(
+ tr("Page '%1' defined in more than one location:").arg(title));
+ foreach (const Location &location, internalLocations)
+ location.warning(tr("(defined here)"));
+ }
+ }
+ return i.value();
+ }
+ }
+ return 0;
+}
+
+/*!
+ This function searches for a \a target anchor node. If it
+ finds one, it sets \a atom from the found node and returns
+ the found node.
+ */
+const Node*
+Tree::findUnambiguousTarget(const QString& target, Atom *&atom, const Node* relative) const
+{
+ Target bestTarget = {0, 0, INT_MAX};
+ int numBestTargets = 0;
+ QList<Target> bestTargetList;
+
+ for (int pass = 0; pass < NumSuffixes; ++pass) {
+ TargetHash::const_iterator i = priv->targetHash.find(Doc::canonicalTitle(target + suffixes[pass]));
+ if (i != priv->targetHash.constEnd()) {
+ TargetHash::const_iterator j = i;
+ do {
+ const Target& candidate = j.value();
+ if (candidate.priority < bestTarget.priority) {
+ bestTarget = candidate;
+ bestTargetList.clear();
+ bestTargetList.append(candidate);
+ numBestTargets = 1;
+ } else if (candidate.priority == bestTarget.priority) {
+ bestTargetList.append(candidate);
+ ++numBestTargets;
+ }
+ ++j;
+ } while (j != priv->targetHash.constEnd() && j.key() == i.key());
+
+ if (numBestTargets == 1) {
+ atom = bestTarget.atom;
+ return bestTarget.node;
+ }
+ else if (bestTargetList.size() > 1) {
+ if (relative && !relative->qmlModuleIdentifier().isEmpty()) {
+ for (int i=0; i<bestTargetList.size(); ++i) {
+ const Node* n = bestTargetList.at(i).node;
+ if (relative->qmlModuleIdentifier() == n->qmlModuleIdentifier()) {
+ atom = bestTargetList.at(i).atom;
+ return n;
+ }
+ }
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+/*!
+ This function searches for a node with a canonical title
+ constructed from \a target and each of the possible suffixes.
+ If the node it finds is \a node, it returns the Atom from that
+ node. Otherwise it returns null.
+ */
+Atom* Tree::findTarget(const QString& target, const Node* node) const
+{
+ for (int pass = 0; pass < NumSuffixes; ++pass) {
+ QString key = Doc::canonicalTitle(target + suffixes[pass]);
+ TargetHash::const_iterator i = priv->targetHash.find(key);
+
+ if (i != priv->targetHash.constEnd()) {
+ do {
+ if (i.value().node == node)
+ return i.value().atom;
+ ++i;
+ } while (i != priv->targetHash.constEnd() && i.key() == key);
+ }
+ }
+ return 0;
+}
+
+/*!
+ */
+void Tree::addBaseClass(ClassNode* subclass, Node::Access access,
+ const QStringList& basePath,
+ const QString& dataTypeWithTemplateArgs,
+ InnerNode* parent)
+{
+ priv->unresolvedInheritanceMap[subclass].append(
+ InheritanceBound(access,
+ basePath,
+ dataTypeWithTemplateArgs,
+ parent)
+ );
+}
+
+/*!
+ */
+void Tree::addPropertyFunction(PropertyNode* property,
+ const QString& funcName,
+ PropertyNode::FunctionRole funcRole)
+{
+ priv->unresolvedPropertyMap[property].insert(funcRole, funcName);
+}
+
+/*!
+ This function adds the \a node to the \a group. The group
+ can be listed anywhere using the \e{annotated list} command.
+ */
+void Tree::addToGroup(Node* node, const QString& group)
+{
+ priv->groupMap.insert(group, node);
+}
+
+/*!
+ This function adds the \a node to the QML \a module. The QML
+ module can be listed anywhere using the \e{annotated list}
+ command.
+ */
+void Tree::addToQmlModule(Node* node, const QString& module)
+{
+ priv->qmlModuleMap.insert(module, node);
+}
+
+/*!
+ Returns the group map.
+ */
+NodeMultiMap Tree::groups() const
+{
+ return priv->groupMap;
+}
+
+/*!
+ Returns the QML module map.
+ */
+NodeMultiMap Tree::qmlModules() const
+{
+ return priv->qmlModuleMap;
+}
+
+/*!
+ */
+void Tree::addToPublicGroup(Node* node, const QString& group)
+{
+ priv->publicGroupMap.insert(node->name(), group);
+ addToGroup(node, group);
+}
+
+/*!
+ */
+QMultiMap<QString, QString> Tree::publicGroups() const
+{
+ return priv->publicGroupMap;
+}
+
+/*!
+ */
+void Tree::resolveInheritance(NamespaceNode* rootNode)
+{
+ if (!rootNode)
+ rootNode = root();
+
+ for (int pass = 0; pass < 2; pass++) {
+ NodeList::ConstIterator c = rootNode->childNodes().begin();
+ while (c != rootNode->childNodes().end()) {
+ if ((*c)->type() == Node::Class) {
+ resolveInheritance(pass, (ClassNode*)* c);
+ }
+ else if ((*c)->type() == Node::Namespace) {
+ NamespaceNode* ns = static_cast<NamespaceNode*>(*c);
+ resolveInheritance(ns);
+ }
+ ++c;
+ }
+ if (rootNode == root())
+ priv->unresolvedInheritanceMap.clear();
+ }
+}
+
+/*!
+ */
+void Tree::resolveProperties()
+{
+ PropertyMap::ConstIterator propEntry;
+
+ propEntry = priv->unresolvedPropertyMap.begin();
+ while (propEntry != priv->unresolvedPropertyMap.end()) {
+ PropertyNode* property = propEntry.key();
+ InnerNode* parent = property->parent();
+ QString getterName = (*propEntry)[PropertyNode::Getter];
+ QString setterName = (*propEntry)[PropertyNode::Setter];
+ QString resetterName = (*propEntry)[PropertyNode::Resetter];
+ QString notifierName = (*propEntry)[PropertyNode::Notifier];
+
+ NodeList::ConstIterator c = parent->childNodes().begin();
+ while (c != parent->childNodes().end()) {
+ if ((*c)->type() == Node::Function) {
+ FunctionNode* function = static_cast<FunctionNode*>(*c);
+ if (function->access() == property->access() &&
+ (function->status() == property->status() ||
+ function->doc().isEmpty())) {
+ if (function->name() == getterName) {
+ property->addFunction(function, PropertyNode::Getter);
+ }
+ else if (function->name() == setterName) {
+ property->addFunction(function, PropertyNode::Setter);
+ }
+ else if (function->name() == resetterName) {
+ property->addFunction(function, PropertyNode::Resetter);
+ }
+ else if (function->name() == notifierName) {
+ property->addSignal(function, PropertyNode::Notifier);
+ }
+ }
+ }
+ ++c;
+ }
+ ++propEntry;
+ }
+
+ propEntry = priv->unresolvedPropertyMap.begin();
+ while (propEntry != priv->unresolvedPropertyMap.end()) {
+ PropertyNode* property = propEntry.key();
+ // redo it to set the property functions
+ if (property->overriddenFrom())
+ property->setOverriddenFrom(property->overriddenFrom());
+ ++propEntry;
+ }
+
+ priv->unresolvedPropertyMap.clear();
+}
+
+/*!
+ */
+void Tree::resolveInheritance(int pass, ClassNode* classe)
+{
+ if (pass == 0) {
+ QList<InheritanceBound> bounds = priv->unresolvedInheritanceMap[classe];
+ QList<InheritanceBound>::ConstIterator b = bounds.begin();
+ while (b != bounds.end()) {
+ ClassNode* baseClass = (ClassNode*)findNode((*b).basePath,
+ Node::Class);
+ if (!baseClass && (*b).parent) {
+ baseClass = (ClassNode*)findNode((*b).basePath,
+ Node::Class,
+ (*b).parent);
+ }
+ if (baseClass) {
+ classe->addBaseClass((*b).access,
+ baseClass,
+ (*b).dataTypeWithTemplateArgs);
+ }
+ ++b;
+ }
+ }
+ else {
+ NodeList::ConstIterator c = classe->childNodes().begin();
+ while (c != classe->childNodes().end()) {
+ if ((*c)->type() == Node::Function) {
+ FunctionNode* func = (FunctionNode*)* c;
+ FunctionNode* from = findVirtualFunctionInBaseClasses(classe, func);
+ if (from != 0) {
+ if (func->virtualness() == FunctionNode::NonVirtual)
+ func->setVirtualness(FunctionNode::ImpureVirtual);
+ func->setReimplementedFrom(from);
+ }
+ }
+ else if ((*c)->type() == Node::Property) {
+ fixPropertyUsingBaseClasses(classe, static_cast<PropertyNode*>(*c));
+ }
+ ++c;
+ }
+ }
+}
+
+/*!
+ For each node in the group map, add the node to the appropriate
+ group node.
+ */
+void Tree::resolveGroups()
+{
+ NodeMultiMap::const_iterator i;
+ for (i = priv->groupMap.constBegin(); i != priv->groupMap.constEnd(); ++i) {
+ if (i.value()->access() == Node::Private)
+ continue;
+
+ FakeNode* fake =
+ static_cast<FakeNode*>(findNode(QStringList(i.key()),Node::Fake));
+ if (fake && fake->subType() == Node::Group) {
+ fake->addGroupMember(i.value());
+ }
+ }
+}
+
+/*!
+ For each node in the QML module map, add the node to the
+ appropriate QML module node.
+ */
+void Tree::resolveQmlModules()
+{
+ NodeMultiMap::const_iterator i;
+ for (i = priv->qmlModuleMap.constBegin(); i != priv->qmlModuleMap.constEnd(); ++i) {
+ FakeNode* fake =
+ static_cast<FakeNode*>(findNode(QStringList(i.key()),Node::Fake));
+ if (fake && fake->subType() == Node::QmlModule) {
+ fake->addQmlModuleMember(i.value());
+ }
+ }
+}
+
+/*!
+ */
+void Tree::resolveTargets(InnerNode* root)
+{
+ // need recursion
+
+ foreach (Node* child, root->childNodes()) {
+ if (child->type() == Node::Fake) {
+ FakeNode* node = static_cast<FakeNode*>(child);
+ if (!node->title().isEmpty())
+ priv->fakeNodesByTitle.insert(Doc::canonicalTitle(node->title()), node);
+ if (node->subType() == Node::Collision) {
+ resolveTargets(node);
+ }
+ }
+
+ if (child->doc().hasTableOfContents()) {
+ const QList<Atom*>& toc = child->doc().tableOfContents();
+ Target target;
+ target.node = child;
+ target.priority = 3;
+
+ for (int i = 0; i < toc.size(); ++i) {
+ target.atom = toc.at(i);
+ QString title = Text::sectionHeading(target.atom).toString();
+ if (!title.isEmpty())
+ priv->targetHash.insert(Doc::canonicalTitle(title), target);
+ }
+ }
+ if (child->doc().hasKeywords()) {
+ const QList<Atom*>& keywords = child->doc().keywords();
+ Target target;
+ target.node = child;
+ target.priority = 1;
+
+ for (int i = 0; i < keywords.size(); ++i) {
+ target.atom = keywords.at(i);
+ priv->targetHash.insert(Doc::canonicalTitle(target.atom->string()), target);
+ }
+ }
+ if (child->doc().hasTargets()) {
+ const QList<Atom*>& toc = child->doc().targets();
+ Target target;
+ target.node = child;
+ target.priority = 2;
+
+ for (int i = 0; i < toc.size(); ++i) {
+ target.atom = toc.at(i);
+ priv->targetHash.insert(Doc::canonicalTitle(target.atom->string()), target);
+ }
+ }
+ }
+}
+
+/*!
+ For each QML class node that points to a C++ class node,
+ follow its C++ class node pointer and set the C++ class
+ node's QML class node pointer back to the QML class node.
+ */
+void Tree::resolveCppToQmlLinks()
+{
+
+ foreach (Node* child, roo.childNodes()) {
+ if (child->type() == Node::Fake && child->subType() == Node::QmlClass) {
+ QmlClassNode* qcn = static_cast<QmlClassNode*>(child);
+ ClassNode* cn = const_cast<ClassNode*>(qcn->classNode());
+ if (cn)
+ cn->setQmlElement(qcn);
+ }
+ }
+}
+
+/*!
+ For each QML class node in the tree, determine whether
+ it inherits a QML base class and, if so, which one, and
+ store that pointer in the QML class node's state.
+ */
+void Tree::resolveQmlInheritance()
+{
+
+ foreach (Node* child, roo.childNodes()) {
+ if (child->type() == Node::Fake) {
+ if (child->subType() == Node::QmlClass) {
+ QmlClassNode* qcn = static_cast<QmlClassNode*>(child);
+ qcn->resolveInheritance(this);
+ }
+ else if (child->subType() == Node::Collision) {
+ NameCollisionNode* ncn = static_cast<NameCollisionNode*>(child);
+ foreach (Node* child, ncn->childNodes()) {
+ if (child->type() == Node::Fake) {
+ if (child->subType() == Node::QmlClass) {
+ QmlClassNode* qcn = static_cast<QmlClassNode*>(child);
+ qcn->resolveInheritance(this);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+/*!
+ */
+void Tree::fixInheritance(NamespaceNode* rootNode)
+{
+ if (!rootNode)
+ rootNode = root();
+
+ NodeList::ConstIterator c = rootNode->childNodes().begin();
+ while (c != rootNode->childNodes().end()) {
+ if ((*c)->type() == Node::Class)
+ static_cast<ClassNode*>(*c)->fixBaseClasses();
+ else if ((*c)->type() == Node::Namespace) {
+ NamespaceNode* ns = static_cast<NamespaceNode*>(*c);
+ fixInheritance(ns);
+ }
+ ++c;
+ }
+}
+
+/*!
+ */
+FunctionNode* Tree::findVirtualFunctionInBaseClasses(ClassNode* classe,
+ FunctionNode* clone)
+{
+ QList<RelatedClass>::ConstIterator r = classe->baseClasses().begin();
+ while (r != classe->baseClasses().end()) {
+ FunctionNode* func;
+ if (((func = findVirtualFunctionInBaseClasses((*r).node, clone)) != 0 ||
+ (func = (*r).node->findFunctionNode(clone)) != 0)) {
+ if (func->virtualness() != FunctionNode::NonVirtual)
+ return func;
+ }
+ ++r;
+ }
+ return 0;
+}
+
+/*!
+ */
+void Tree::fixPropertyUsingBaseClasses(ClassNode* classe,
+ PropertyNode* property)
+{
+ QList<RelatedClass>::const_iterator r = classe->baseClasses().begin();
+ while (r != classe->baseClasses().end()) {
+ PropertyNode* baseProperty =
+ static_cast<PropertyNode*>(r->node->findNode(property->name(),
+ Node::Property));
+ if (baseProperty) {
+ fixPropertyUsingBaseClasses(r->node, baseProperty);
+ property->setOverriddenFrom(baseProperty);
+ }
+ else {
+ fixPropertyUsingBaseClasses(r->node, property);
+ }
+ ++r;
+ }
+}
+
+/*!
+ */
+NodeList Tree::allBaseClasses(const ClassNode* classe) const
+{
+ NodeList result;
+ foreach (const RelatedClass& r, classe->baseClasses()) {
+ result += r.node;
+ result += allBaseClasses(r.node);
+ }
+ return result;
+}
+
+/*!
+ */
+void Tree::readIndexes(const QStringList& indexFiles)
+{
+ foreach (const QString& indexFile, indexFiles)
+ readIndexFile(indexFile);
+}
+
+/*!
+ Read the QDomDocument at \a path and get the index from it.
+ */
+void Tree::readIndexFile(const QString& path)
+{
+ QFile file(path);
+ if (file.open(QFile::ReadOnly)) {
+ QDomDocument document;
+ document.setContent(&file);
+ file.close();
+
+ QDomElement indexElement = document.documentElement();
+ QString indexUrl = indexElement.attribute("url", "");
+ priv->basesList.clear();
+ priv->relatedList.clear();
+
+ // Scan all elements in the XML file, constructing a map that contains
+ // base classes for each class found.
+
+ QDomElement child = indexElement.firstChildElement();
+ while (!child.isNull()) {
+ readIndexSection(child, root(), indexUrl);
+ child = child.nextSiblingElement();
+ }
+
+ // Now that all the base classes have been found for this index,
+ // arrange them into an inheritance hierarchy.
+
+ resolveIndex();
+ }
+}
+
+/*!
+ */
+void Tree::readIndexSection(const QDomElement& element,
+ InnerNode* parent,
+ const QString& indexUrl)
+{
+ QString name = element.attribute("name");
+ QString href = element.attribute("href");
+
+ Node* section;
+ Location location;
+
+ if (element.nodeName() == "namespace") {
+ section = new NamespaceNode(parent, name);
+
+ if (!indexUrl.isEmpty())
+ location = Location(indexUrl + QLatin1Char('/') + name.toLower() + ".html");
+ else if (!indexUrl.isNull())
+ location = Location(name.toLower() + ".html");
+
+ }
+ else if (element.nodeName() == "class") {
+ section = new ClassNode(parent, name);
+ priv->basesList.append(QPair<ClassNode*,QString>(
+ static_cast<ClassNode*>(section), element.attribute("bases")));
+
+ if (!indexUrl.isEmpty())
+ location = Location(indexUrl + QLatin1Char('/') + name.toLower() + ".html");
+ else if (!indexUrl.isNull())
+ location = Location(name.toLower() + ".html");
+
+ }
+ else if ((element.nodeName() == "qmlclass") ||
+ ((element.nodeName() == "page") && (element.attribute("subtype") == "qmlclass"))) {
+ QmlClassNode* qcn = new QmlClassNode(parent, name, 0);
+ qcn->setTitle(element.attribute("title"));
+ if (element.hasAttribute("location"))
+ name = element.attribute("location", "");
+ if (!indexUrl.isEmpty())
+ location = Location(indexUrl + QLatin1Char('/') + name);
+ else if (!indexUrl.isNull())
+ location = Location(name);
+ section = qcn;
+ }
+ else if (element.nodeName() == "qmlbasictype") {
+ QmlBasicTypeNode* qbtn = new QmlBasicTypeNode(parent, name);
+ qbtn->setTitle(element.attribute("title"));
+ if (element.hasAttribute("location"))
+ name = element.attribute("location", "");
+ if (!indexUrl.isEmpty())
+ location = Location(indexUrl + QLatin1Char('/') + name);
+ else if (!indexUrl.isNull())
+ location = Location(name);
+ section = qbtn;
+ }
+ else if (element.nodeName() == "page") {
+ Node::SubType subtype;
+ Node::PageType ptype = Node::NoPageType;
+ if (element.attribute("subtype") == "example") {
+ subtype = Node::Example;
+ ptype = Node::ExamplePage;
+ }
+ else if (element.attribute("subtype") == "header") {
+ subtype = Node::HeaderFile;
+ ptype = Node::ApiPage;
+ }
+ else if (element.attribute("subtype") == "file") {
+ subtype = Node::File;
+ ptype = Node::NoPageType;
+ }
+ else if (element.attribute("subtype") == "group") {
+ subtype = Node::Group;
+ ptype = Node::OverviewPage;
+ }
+ else if (element.attribute("subtype") == "module") {
+ subtype = Node::Module;
+ ptype = Node::OverviewPage;
+ }
+ else if (element.attribute("subtype") == "page") {
+ subtype = Node::Page;
+ ptype = Node::ArticlePage;
+ }
+ else if (element.attribute("subtype") == "externalpage") {
+ subtype = Node::ExternalPage;
+ ptype = Node::ArticlePage;
+ }
+ else if (element.attribute("subtype") == "qmlclass") {
+ subtype = Node::QmlClass;
+ ptype = Node::ApiPage;
+ }
+ else if (element.attribute("subtype") == "qmlpropertygroup") {
+ subtype = Node::QmlPropertyGroup;
+ ptype = Node::ApiPage;
+ }
+ else if (element.attribute("subtype") == "qmlbasictype") {
+ subtype = Node::QmlBasicType;
+ ptype = Node::ApiPage;
+ }
+ else
+ return;
+
+ FakeNode* fakeNode = new FakeNode(parent, name, subtype, ptype);
+ fakeNode->setTitle(element.attribute("title"));
+
+ if (element.hasAttribute("location"))
+ name = element.attribute("location", "");
+
+ if (!indexUrl.isEmpty())
+ location = Location(indexUrl + QLatin1Char('/') + name);
+ else if (!indexUrl.isNull())
+ location = Location(name);
+
+ section = fakeNode;
+
+ }
+ else if (element.nodeName() == "enum") {
+ EnumNode* enumNode = new EnumNode(parent, name);
+
+ if (!indexUrl.isEmpty())
+ location =
+ Location(indexUrl + QLatin1Char('/') + parent->name().toLower() + ".html");
+ else if (!indexUrl.isNull())
+ location = Location(parent->name().toLower() + ".html");
+
+ QDomElement child = element.firstChildElement("value");
+ while (!child.isNull()) {
+ EnumItem item(child.attribute("name"), child.attribute("value"));
+ enumNode->addItem(item);
+ child = child.nextSiblingElement("value");
+ }
+
+ section = enumNode;
+
+ } else if (element.nodeName() == "typedef") {
+ section = new TypedefNode(parent, name);
+
+ if (!indexUrl.isEmpty())
+ location =
+ Location(indexUrl + QLatin1Char('/') + parent->name().toLower() + ".html");
+ else if (!indexUrl.isNull())
+ location = Location(parent->name().toLower() + ".html");
+
+ }
+ else if (element.nodeName() == "property") {
+ section = new PropertyNode(parent, name);
+
+ if (!indexUrl.isEmpty())
+ location =
+ Location(indexUrl + QLatin1Char('/') + parent->name().toLower() + ".html");
+ else if (!indexUrl.isNull())
+ location = Location(parent->name().toLower() + ".html");
+
+ } else if (element.nodeName() == "function") {
+ FunctionNode::Virtualness virt;
+ if (element.attribute("virtual") == "non")
+ virt = FunctionNode::NonVirtual;
+ else if (element.attribute("virtual") == "impure")
+ virt = FunctionNode::ImpureVirtual;
+ else if (element.attribute("virtual") == "pure")
+ virt = FunctionNode::PureVirtual;
+ else
+ return;
+
+ FunctionNode::Metaness meta;
+ if (element.attribute("meta") == "plain")
+ meta = FunctionNode::Plain;
+ else if (element.attribute("meta") == "signal")
+ meta = FunctionNode::Signal;
+ else if (element.attribute("meta") == "slot")
+ meta = FunctionNode::Slot;
+ else if (element.attribute("meta") == "constructor")
+ meta = FunctionNode::Ctor;
+ else if (element.attribute("meta") == "destructor")
+ meta = FunctionNode::Dtor;
+ else if (element.attribute("meta") == "macro")
+ meta = FunctionNode::MacroWithParams;
+ else if (element.attribute("meta") == "macrowithparams")
+ meta = FunctionNode::MacroWithParams;
+ else if (element.attribute("meta") == "macrowithoutparams")
+ meta = FunctionNode::MacroWithoutParams;
+ else
+ return;
+
+ FunctionNode* functionNode = new FunctionNode(parent, name);
+ functionNode->setReturnType(element.attribute("return"));
+ functionNode->setVirtualness(virt);
+ functionNode->setMetaness(meta);
+ functionNode->setConst(element.attribute("const") == "true");
+ functionNode->setStatic(element.attribute("static") == "true");
+ functionNode->setOverload(element.attribute("overload") == "true");
+
+ if (element.hasAttribute("relates")
+ && element.attribute("relates") != parent->name()) {
+ priv->relatedList.append(
+ QPair<FunctionNode*,QString>(functionNode,
+ element.attribute("relates")));
+ }
+
+ QDomElement child = element.firstChildElement("parameter");
+ while (!child.isNull()) {
+ // Do not use the default value for the parameter; it is not
+ // required, and has been known to cause problems.
+ Parameter parameter(child.attribute("left"),
+ child.attribute("right"),
+ child.attribute("name"),
+ ""); // child.attribute("default")
+ functionNode->addParameter(parameter);
+ child = child.nextSiblingElement("parameter");
+ }
+
+ section = functionNode;
+
+ if (!indexUrl.isEmpty())
+ location =
+ Location(indexUrl + QLatin1Char('/') + parent->name().toLower() + ".html");
+ else if (!indexUrl.isNull())
+ location = Location(parent->name().toLower() + ".html");
+
+ }
+ else if (element.nodeName() == "variable") {
+ section = new VariableNode(parent, name);
+
+ if (!indexUrl.isEmpty())
+ location = Location(indexUrl + QLatin1Char('/') + parent->name().toLower() + ".html");
+ else if (!indexUrl.isNull())
+ location = Location(parent->name().toLower() + ".html");
+
+ }
+ else if (element.nodeName() == "keyword") {
+ Target target;
+ target.node = parent;
+ target.priority = 1;
+ target.atom = new Atom(Atom::Target, name);
+ priv->targetHash.insert(name, target);
+ return;
+
+ }
+ else if (element.nodeName() == "target") {
+ Target target;
+ target.node = parent;
+ target.priority = 2;
+ target.atom = new Atom(Atom::Target, name);
+ priv->targetHash.insert(name, target);
+ return;
+
+ }
+ else if (element.nodeName() == "contents") {
+ Target target;
+ target.node = parent;
+ target.priority = 3;
+ target.atom = new Atom(Atom::Target, name);
+ priv->targetHash.insert(name, target);
+ return;
+
+ }
+ else
+ return;
+
+ QString access = element.attribute("access");
+ if (access == "public")
+ section->setAccess(Node::Public);
+ else if (access == "protected")
+ section->setAccess(Node::Protected);
+ else if (access == "private")
+ section->setAccess(Node::Private);
+ else
+ section->setAccess(Node::Public);
+
+ if ((element.nodeName() != "page") &&
+ (element.nodeName() != "qmlclass") &&
+ (element.nodeName() != "qmlbasictype")) {
+ QString threadSafety = element.attribute("threadsafety");
+ if (threadSafety == "non-reentrant")
+ section->setThreadSafeness(Node::NonReentrant);
+ else if (threadSafety == "reentrant")
+ section->setThreadSafeness(Node::Reentrant);
+ else if (threadSafety == "thread safe")
+ section->setThreadSafeness(Node::ThreadSafe);
+ else
+ section->setThreadSafeness(Node::UnspecifiedSafeness);
+ }
+ else
+ section->setThreadSafeness(Node::UnspecifiedSafeness);
+
+ QString status = element.attribute("status");
+ if (status == "compat")
+ section->setStatus(Node::Compat);
+ else if (status == "obsolete")
+ section->setStatus(Node::Obsolete);
+ else if (status == "deprecated")
+ section->setStatus(Node::Deprecated);
+ else if (status == "preliminary")
+ section->setStatus(Node::Preliminary);
+ else if (status == "commendable")
+ section->setStatus(Node::Commendable);
+ else if (status == "internal")
+ section->setStatus(Node::Internal);
+ else if (status == "main")
+ section->setStatus(Node::Main);
+ else
+ section->setStatus(Node::Commendable);
+
+ section->setModuleName(element.attribute("module"));
+ if (!indexUrl.isEmpty()) {
+ if (indexUrl.startsWith(QLatin1Char('.')))
+ section->setUrl(href);
+ else
+ section->setUrl(indexUrl + QLatin1Char('/') + href);
+ }
+
+ // Create some content for the node.
+ QSet<QString> emptySet;
+
+ Doc doc(location, location, " ", emptySet); // placeholder
+ section->setDoc(doc);
+
+ if (section->isInnerNode()) {
+ InnerNode* inner = static_cast<InnerNode*>(section);
+ if (inner) {
+ QDomElement child = element.firstChildElement();
+
+ while (!child.isNull()) {
+ if (element.nodeName() == "class")
+ readIndexSection(child, inner, indexUrl);
+ else if (element.nodeName() == "qmlclass")
+ readIndexSection(child, inner, indexUrl);
+ else if (element.nodeName() == "page")
+ readIndexSection(child, inner, indexUrl);
+ else if (element.nodeName() == "namespace" && !name.isEmpty())
+ // The root node in the index is a namespace with an empty name.
+ readIndexSection(child, inner, indexUrl);
+ else
+ readIndexSection(child, parent, indexUrl);
+
+ child = child.nextSiblingElement();
+ }
+ }
+ }
+}
+
+/*!
+ */
+QString Tree::readIndexText(const QDomElement& element)
+{
+ QString text;
+ QDomNode child = element.firstChild();
+ while (!child.isNull()) {
+ if (child.isText())
+ text += child.toText().nodeValue();
+ child = child.nextSibling();
+ }
+ return text;
+}
+
+/*!
+ */
+void Tree::resolveIndex()
+{
+ QPair<ClassNode*,QString> pair;
+
+ foreach (pair, priv->basesList) {
+ foreach (const QString& base, pair.second.split(QLatin1Char(','))) {
+ Node* baseClass = root()->findNode(base, Node::Class);
+ if (baseClass) {
+ pair.first->addBaseClass(Node::Public,
+ static_cast<ClassNode*>(baseClass));
+ }
+ }
+ }
+
+ QPair<FunctionNode*,QString> relatedPair;
+
+ foreach (relatedPair, priv->relatedList) {
+ Node* classNode = root()->findNode(relatedPair.second, Node::Class);
+ if (classNode)
+ relatedPair.first->setRelates(static_cast<ClassNode*>(classNode));
+ }
+}
+
+/*!
+ Generate the index section with the given \a writer for the \a node
+ specified, returning true if an element was written; otherwise returns
+ false.
+ */
+bool Tree::generateIndexSection(QXmlStreamWriter& writer,
+ const Node* node,
+ bool generateInternalNodes) const
+{
+ if (!node->url().isEmpty())
+ return false;
+
+ QString nodeName;
+ switch (node->type()) {
+ case Node::Namespace:
+ nodeName = "namespace";
+ break;
+ case Node::Class:
+ nodeName = "class";
+ break;
+ case Node::Fake:
+ nodeName = "page";
+ if (node->subType() == Node::QmlClass)
+ nodeName = "qmlclass";
+ else if (node->subType() == Node::QmlBasicType)
+ nodeName = "qmlbasictype";
+ break;
+ case Node::Enum:
+ nodeName = "enum";
+ break;
+ case Node::Typedef:
+ nodeName = "typedef";
+ break;
+ case Node::Property:
+ nodeName = "property";
+ break;
+ case Node::Function:
+ nodeName = "function";
+ break;
+ case Node::Variable:
+ nodeName = "variable";
+ break;
+ case Node::Target:
+ nodeName = "target";
+ break;
+ case Node::QmlProperty:
+ nodeName = "qmlproperty";
+ break;
+ case Node::QmlSignal:
+ nodeName = "qmlsignal";
+ break;
+ case Node::QmlSignalHandler:
+ nodeName = "qmlsignalhandler";
+ break;
+ case Node::QmlMethod:
+ nodeName = "qmlmethod";
+ break;
+ default:
+ return false;
+ }
+
+ QString access;
+ switch (node->access()) {
+ case Node::Public:
+ access = "public";
+ break;
+ case Node::Protected:
+ access = "protected";
+ break;
+ case Node::Private:
+ // Do not include private non-internal nodes in the index.
+ // (Internal public and protected nodes are marked as private
+ // by qdoc. We can check their internal status to determine
+ // whether they were really private to begin with.)
+ if (node->status() == Node::Internal && generateInternalNodes)
+ access = "internal";
+ else
+ return false;
+ break;
+ default:
+ return false;
+ }
+
+ QString objName = node->name();
+
+ // Special case: only the root node should have an empty name.
+ if (objName.isEmpty() && node != root())
+ return false;
+
+ writer.writeStartElement(nodeName);
+
+ QXmlStreamAttributes attributes;
+ writer.writeAttribute("access", access);
+
+ if (node->type() != Node::Fake) {
+ QString threadSafety;
+ switch (node->threadSafeness()) {
+ case Node::NonReentrant:
+ threadSafety = "non-reentrant";
+ break;
+ case Node::Reentrant:
+ threadSafety = "reentrant";
+ break;
+ case Node::ThreadSafe:
+ threadSafety = "thread safe";
+ break;
+ case Node::UnspecifiedSafeness:
+ default:
+ threadSafety = "unspecified";
+ break;
+ }
+ writer.writeAttribute("threadsafety", threadSafety);
+ }
+
+ QString status;
+ switch (node->status()) {
+ case Node::Compat:
+ status = "compat";
+ break;
+ case Node::Obsolete:
+ status = "obsolete";
+ break;
+ case Node::Deprecated:
+ status = "deprecated";
+ break;
+ case Node::Preliminary:
+ status = "preliminary";
+ break;
+ case Node::Commendable:
+ status = "commendable";
+ break;
+ case Node::Internal:
+ status = "internal";
+ break;
+ case Node::Main:
+ default:
+ status = "main";
+ break;
+ }
+ writer.writeAttribute("status", status);
+
+ writer.writeAttribute("name", objName);
+ QString fullName = node->fullDocumentName();
+ if (fullName != objName)
+ writer.writeAttribute("fullname", fullName);
+ QString href = node->outputSubdirectory();
+ if (!href.isEmpty())
+ href.append(QLatin1Char('/'));
+ href.append(HtmlGenerator::fullDocumentLocation(node));
+ writer.writeAttribute("href", href);
+ if ((node->type() != Node::Fake) && (!node->isQmlNode()))
+ writer.writeAttribute("location", node->location().fileName());
+
+ switch (node->type()) {
+
+ case Node::Class:
+ {
+ // Classes contain information about their base classes.
+
+ const ClassNode* classNode = static_cast<const ClassNode*>(node);
+ QList<RelatedClass> bases = classNode->baseClasses();
+ QSet<QString> baseStrings;
+ foreach (const RelatedClass& related, bases) {
+ ClassNode* baseClassNode = related.node;
+ baseStrings.insert(baseClassNode->name());
+ }
+ writer.writeAttribute("bases", QStringList(baseStrings.toList()).join(","));
+ writer.writeAttribute("module", node->moduleName());
+ }
+ break;
+
+ case Node::Namespace:
+ writer.writeAttribute("module", node->moduleName());
+ break;
+
+ case Node::Fake:
+ {
+ /*
+ Fake nodes (such as manual pages) contain subtypes,
+ titles and other attributes.
+ */
+
+ const FakeNode* fakeNode = static_cast<const FakeNode*>(node);
+ switch (fakeNode->subType()) {
+ case Node::Example:
+ writer.writeAttribute("subtype", "example");
+ break;
+ case Node::HeaderFile:
+ writer.writeAttribute("subtype", "header");
+ break;
+ case Node::File:
+ writer.writeAttribute("subtype", "file");
+ break;
+ case Node::Group:
+ writer.writeAttribute("subtype", "group");
+ break;
+ case Node::Module:
+ writer.writeAttribute("subtype", "module");
+ break;
+ case Node::Page:
+ writer.writeAttribute("subtype", "page");
+ break;
+ case Node::ExternalPage:
+ writer.writeAttribute("subtype", "externalpage");
+ break;
+ case Node::QmlClass:
+ //writer.writeAttribute("subtype", "qmlclass");
+ break;
+ case Node::QmlBasicType:
+ //writer.writeAttribute("subtype", "qmlbasictype");
+ break;
+ default:
+ break;
+ }
+ writer.writeAttribute("title", fakeNode->title());
+ writer.writeAttribute("fulltitle", fakeNode->fullTitle());
+ writer.writeAttribute("subtitle", fakeNode->subTitle());
+ writer.writeAttribute("location", fakeNode->doc().location().fileName());
+ }
+ break;
+
+ case Node::Function:
+ {
+ /*
+ Function nodes contain information about the type of
+ function being described.
+ */
+
+ const FunctionNode* functionNode =
+ static_cast<const FunctionNode*>(node);
+
+ switch (functionNode->virtualness()) {
+ case FunctionNode::NonVirtual:
+ writer.writeAttribute("virtual", "non");
+ break;
+ case FunctionNode::ImpureVirtual:
+ writer.writeAttribute("virtual", "impure");
+ break;
+ case FunctionNode::PureVirtual:
+ writer.writeAttribute("virtual", "pure");
+ break;
+ default:
+ break;
+ }
+ switch (functionNode->metaness()) {
+ case FunctionNode::Plain:
+ writer.writeAttribute("meta", "plain");
+ break;
+ case FunctionNode::Signal:
+ writer.writeAttribute("meta", "signal");
+ break;
+ case FunctionNode::Slot:
+ writer.writeAttribute("meta", "slot");
+ break;
+ case FunctionNode::Ctor:
+ writer.writeAttribute("meta", "constructor");
+ break;
+ case FunctionNode::Dtor:
+ writer.writeAttribute("meta", "destructor");
+ break;
+ case FunctionNode::MacroWithParams:
+ writer.writeAttribute("meta", "macrowithparams");
+ break;
+ case FunctionNode::MacroWithoutParams:
+ writer.writeAttribute("meta", "macrowithoutparams");
+ break;
+ default:
+ break;
+ }
+ writer.writeAttribute("const", functionNode->isConst()?"true":"false");
+ writer.writeAttribute("static", functionNode->isStatic()?"true":"false");
+ writer.writeAttribute("overload", functionNode->isOverload()?"true":"false");
+ if (functionNode->isOverload())
+ writer.writeAttribute("overload-number", QString::number(functionNode->overloadNumber()));
+ if (functionNode->relates())
+ writer.writeAttribute("relates", functionNode->relates()->name());
+ const PropertyNode* propertyNode = functionNode->associatedProperty();
+ if (propertyNode)
+ writer.writeAttribute("associated-property", propertyNode->name());
+ writer.writeAttribute("type", functionNode->returnType());
+ }
+ break;
+
+ case Node::QmlProperty:
+ {
+ const QmlPropertyNode* qpn = static_cast<const QmlPropertyNode*>(node);
+ writer.writeAttribute("type", qpn->dataType());
+ writer.writeAttribute("attached", qpn->isAttached() ? "true" : "false");
+ writer.writeAttribute("writable", qpn->isWritable(this) ? "true" : "false");
+ }
+ break;
+ case Node::Property:
+ {
+ const PropertyNode* propertyNode = static_cast<const PropertyNode*>(node);
+ writer.writeAttribute("type", propertyNode->dataType());
+ foreach (const Node* fnNode, propertyNode->getters()) {
+ if (fnNode) {
+ const FunctionNode* functionNode = static_cast<const FunctionNode*>(fnNode);
+ writer.writeStartElement("getter");
+ writer.writeAttribute("name", functionNode->name());
+ writer.writeEndElement(); // getter
+ }
+ }
+ foreach (const Node* fnNode, propertyNode->setters()) {
+ if (fnNode) {
+ const FunctionNode* functionNode = static_cast<const FunctionNode*>(fnNode);
+ writer.writeStartElement("setter");
+ writer.writeAttribute("name", functionNode->name());
+ writer.writeEndElement(); // getter
+ }
+ }
+ foreach (const Node* fnNode, propertyNode->resetters()) {
+ if (fnNode) {
+ const FunctionNode* functionNode = static_cast<const FunctionNode*>(fnNode);
+ writer.writeStartElement("resetter");
+ writer.writeAttribute("name", functionNode->name());
+ writer.writeEndElement(); // getter
+ }
+ }
+ }
+ break;
+
+ case Node::Variable:
+ {
+ const VariableNode* variableNode =
+ static_cast<const VariableNode*>(node);
+ writer.writeAttribute("type", variableNode->dataType());
+ writer.writeAttribute("static",
+ variableNode->isStatic() ? "true" : "false");
+ }
+ break;
+ default:
+ break;
+ }
+
+ // Inner nodes and function nodes contain child nodes of some sort, either
+ // actual child nodes or function parameters. For these, we close the
+ // opening tag, create child elements, then add a closing tag for the
+ // element. Elements for all other nodes are closed in the opening tag.
+
+ if (node->isInnerNode()) {
+
+ const InnerNode* inner = static_cast<const InnerNode*>(node);
+
+ // For internal pages, we canonicalize the target, keyword and content
+ // item names so that they can be used by qdoc for other sets of
+ // documentation.
+ // The reason we do this here is that we don't want to ruin
+ // externally composed indexes, containing non-qdoc-style target names
+ // when reading in indexes.
+
+ if (inner->doc().hasTargets()) {
+ bool external = false;
+ if (inner->type() == Node::Fake) {
+ const FakeNode* fakeNode = static_cast<const FakeNode*>(inner);
+ if (fakeNode->subType() == Node::ExternalPage)
+ external = true;
+ }
+
+ foreach (const Atom* target, inner->doc().targets()) {
+ QString targetName = target->string();
+ if (!external)
+ targetName = Doc::canonicalTitle(targetName);
+
+ writer.writeStartElement("target");
+ writer.writeAttribute("name", targetName);
+ writer.writeEndElement(); // target
+ }
+ }
+ if (inner->doc().hasKeywords()) {
+ foreach (const Atom* keyword, inner->doc().keywords()) {
+ writer.writeStartElement("keyword");
+ writer.writeAttribute("name",
+ Doc::canonicalTitle(keyword->string()));
+ writer.writeEndElement(); // keyword
+ }
+ }
+ if (inner->doc().hasTableOfContents()) {
+ for (int i = 0; i < inner->doc().tableOfContents().size(); ++i) {
+ Atom* item = inner->doc().tableOfContents()[i];
+ int level = inner->doc().tableOfContentsLevels()[i];
+
+ QString title = Text::sectionHeading(item).toString();
+ writer.writeStartElement("contents");
+ writer.writeAttribute("name", Doc::canonicalTitle(title));
+ writer.writeAttribute("title", title);
+ writer.writeAttribute("level", QString::number(level));
+ writer.writeEndElement(); // contents
+ }
+ }
+
+ }
+ else if (node->type() == Node::Function) {
+
+ const FunctionNode* functionNode = static_cast<const FunctionNode*>(node);
+ // Write a signature attribute for convenience.
+ QStringList signatureList;
+ QStringList resolvedParameters;
+
+ foreach (const Parameter& parameter, functionNode->parameters()) {
+ QString leftType = parameter.leftType();
+ const Node* leftNode =
+ const_cast<Tree*>(this)->findNode(parameter.leftType().split("::"),
+ Node::Typedef, 0, SearchBaseClasses|NonFunction);
+ if (!leftNode) {
+ leftNode = const_cast<Tree*>(this)->findNode(
+ parameter.leftType().split("::"), Node::Typedef,
+ node->parent(), SearchBaseClasses|NonFunction);
+ }
+ if (leftNode) {
+ if (leftNode->type() == Node::Typedef) {
+ const TypedefNode* typedefNode =
+ static_cast<const TypedefNode*>(leftNode);
+ if (typedefNode->associatedEnum()) {
+ leftType = "QFlags<" +
+ typedefNode->associatedEnum()->fullDocumentName() +
+ QLatin1Char('>');
+ }
+ }
+ else
+ leftType = leftNode->fullDocumentName();
+ }
+ resolvedParameters.append(leftType);
+ signatureList.append(leftType + QLatin1Char(' ') + parameter.name());
+ }
+
+ QString signature = functionNode->name()+QLatin1Char('(')+signatureList.join(", ")+QLatin1Char(')');
+ if (functionNode->isConst())
+ signature += " const";
+ writer.writeAttribute("signature", signature);
+
+ for (int i = 0; i < functionNode->parameters().size(); ++i) {
+ Parameter parameter = functionNode->parameters()[i];
+ writer.writeStartElement("parameter");
+ writer.writeAttribute("left", resolvedParameters[i]);
+ writer.writeAttribute("right", parameter.rightType());
+ writer.writeAttribute("name", parameter.name());
+ writer.writeAttribute("default", parameter.defaultValue());
+ writer.writeEndElement(); // parameter
+ }
+
+ }
+ else if (node->type() == Node::Enum) {
+
+ const EnumNode* enumNode = static_cast<const EnumNode*>(node);
+ if (enumNode->flagsType()) {
+ writer.writeAttribute("typedef",enumNode->flagsType()->fullDocumentName());
+ }
+ foreach (const EnumItem& item, enumNode->items()) {
+ writer.writeStartElement("value");
+ writer.writeAttribute("name", item.name());
+ writer.writeAttribute("value", item.value());
+ writer.writeEndElement(); // value
+ }
+
+ }
+ else if (node->type() == Node::Typedef) {
+
+ const TypedefNode* typedefNode = static_cast<const TypedefNode*>(node);
+ if (typedefNode->associatedEnum()) {
+ writer.writeAttribute("enum",typedefNode->associatedEnum()->fullDocumentName());
+ }
+ }
+
+ return true;
+}
+
+
+/*!
+ Returns true if the node \a n1 is less than node \a n2.
+ The comparison is performed by comparing properties of the nodes in order
+ of increasing complexity.
+*/
+bool compareNodes(const Node* n1, const Node* n2)
+{
+ // Private nodes can occur in any order since they won't normally be
+ // written to the index.
+ if (n1->access() == Node::Private && n2->access() == Node::Private)
+ return true;
+
+ if (n1->location().filePath() < n2->location().filePath())
+ return true;
+ else if (n1->location().filePath() > n2->location().filePath())
+ return false;
+
+ if (n1->type() < n2->type())
+ return true;
+ else if (n1->type() > n2->type())
+ return false;
+
+ if (n1->name() < n2->name())
+ return true;
+ else if (n1->name() > n2->name())
+ return false;
+
+ if (n1->access() < n2->access())
+ return true;
+ else if (n1->access() > n2->access())
+ return false;
+
+ if (n1->type() == Node::Function && n2->type() == Node::Function) {
+ const FunctionNode* f1 = static_cast<const FunctionNode*>(n1);
+ const FunctionNode* f2 = static_cast<const FunctionNode*>(n2);
+
+ if (f1->isConst() < f2->isConst())
+ return true;
+ else if (f1->isConst() > f2->isConst())
+ return false;
+
+ if (f1->signature() < f2->signature())
+ return true;
+ else if (f1->signature() > f2->signature())
+ return false;
+ }
+
+ if (n1->type() == Node::Fake && n2->type() == Node::Fake) {
+ const FakeNode* f1 = static_cast<const FakeNode*>(n1);
+ const FakeNode* f2 = static_cast<const FakeNode*>(n2);
+ if (f1->fullTitle() < f2->fullTitle())
+ return true;
+ else if (f1->fullTitle() > f2->fullTitle())
+ return false;
+ }
+
+ return false;
+}
+
+/*!
+ Generate index sections for the child nodes of the given \a node
+ using the \a writer specified. If \a generateInternalNodes is true,
+ nodes marked as internal will be included in the index; otherwise,
+ they will be omitted.
+*/
+void Tree::generateIndexSections(QXmlStreamWriter& writer,
+ const Node* node,
+ bool generateInternalNodes) const
+{
+ if (generateIndexSection(writer, node, generateInternalNodes)) {
+
+ if (node->isInnerNode()) {
+ const InnerNode* inner = static_cast<const InnerNode*>(node);
+
+ NodeList cnodes = inner->childNodes();
+ qSort(cnodes.begin(), cnodes.end(), compareNodes);
+
+ foreach (const Node* child, cnodes) {
+ /*
+ Don't generate anything for a QML property group node.
+ It is just a place holder for a collection of QML property
+ nodes. Recurse to its children, which are the QML property
+ nodes.
+ */
+ if (child->subType() == Node::QmlPropertyGroup) {
+ const InnerNode* pgn = static_cast<const InnerNode*>(child);
+ foreach (const Node* c, pgn->childNodes()) {
+ generateIndexSections(writer, c, generateInternalNodes);
+ }
+ }
+ else
+ generateIndexSections(writer, child, generateInternalNodes);
+ }
+
+ /*
+ foreach (const Node* child, inner->relatedNodes()) {
+ QDomElement childElement = generateIndexSections(document, child);
+ element.appendChild(childElement);
+ }
+*/
+ }
+ writer.writeEndElement();
+ }
+}
+
+/*!
+ Outputs an index file.
+ */
+void Tree::generateIndex(const QString& fileName,
+ const QString& url,
+ const QString& title,
+ bool generateInternalNodes) const
+{
+ QFile file(fileName);
+ if (!file.open(QFile::WriteOnly | QFile::Text))
+ return ;
+
+ QXmlStreamWriter writer(&file);
+ writer.setAutoFormatting(true);
+ writer.writeStartDocument();
+ writer.writeDTD("<!DOCTYPE QDOCINDEX>");
+
+ writer.writeStartElement("INDEX");
+ writer.writeAttribute("url", url);
+ writer.writeAttribute("title", title);
+ writer.writeAttribute("version", version());
+
+ generateIndexSections(writer, root(), generateInternalNodes);
+
+ writer.writeEndElement(); // INDEX
+ writer.writeEndElement(); // QDOCINDEX
+ writer.writeEndDocument();
+ file.close();
+}
+
+/*!
+ Generate the tag file section with the given \a writer for the \a node
+ specified, returning true if an element was written; otherwise returns
+ false.
+ */
+void Tree::generateTagFileCompounds(QXmlStreamWriter& writer,
+ const InnerNode* inner) const
+{
+ foreach (const Node* node, inner->childNodes()) {
+
+ if (!node->url().isEmpty())
+ continue;
+
+ QString kind;
+ switch (node->type()) {
+ case Node::Namespace:
+ kind = "namespace";
+ break;
+ case Node::Class:
+ kind = "class";
+ break;
+ case Node::Enum:
+ case Node::Typedef:
+ case Node::Property:
+ case Node::Function:
+ case Node::Variable:
+ case Node::Target:
+ default:
+ continue;
+ }
+
+ QString access;
+ switch (node->access()) {
+ case Node::Public:
+ access = "public";
+ break;
+ case Node::Protected:
+ access = "protected";
+ break;
+ case Node::Private:
+ default:
+ continue;
+ }
+
+ QString objName = node->name();
+
+ // Special case: only the root node should have an empty name.
+ if (objName.isEmpty() && node != root())
+ continue;
+
+ // *** Write the starting tag for the element here. ***
+ writer.writeStartElement("compound");
+ writer.writeAttribute("kind", kind);
+
+ if (node->type() == Node::Class) {
+ writer.writeTextElement("name", node->fullDocumentName());
+ writer.writeTextElement("filename", HtmlGenerator::fullDocumentLocation(node,true));
+
+ // Classes contain information about their base classes.
+ const ClassNode* classNode = static_cast<const ClassNode*>(node);
+ QList<RelatedClass> bases = classNode->baseClasses();
+ foreach (const RelatedClass& related, bases) {
+ ClassNode* baseClassNode = related.node;
+ writer.writeTextElement("base", baseClassNode->name());
+ }
+
+ // Recurse to write all members.
+ generateTagFileMembers(writer, static_cast<const InnerNode*>(node));
+ writer.writeEndElement();
+
+ // Recurse to write all compounds.
+ generateTagFileCompounds(writer, static_cast<const InnerNode*>(node));
+ } else {
+ writer.writeTextElement("name", node->fullDocumentName());
+ writer.writeTextElement("filename", HtmlGenerator::fullDocumentLocation(node,true));
+
+ // Recurse to write all members.
+ generateTagFileMembers(writer, static_cast<const InnerNode*>(node));
+ writer.writeEndElement();
+
+ // Recurse to write all compounds.
+ generateTagFileCompounds(writer, static_cast<const InnerNode*>(node));
+ }
+ }
+}
+
+/*!
+ */
+void Tree::generateTagFileMembers(QXmlStreamWriter& writer,
+ const InnerNode* inner) const
+{
+ foreach (const Node* node, inner->childNodes()) {
+
+ if (!node->url().isEmpty())
+ continue;
+
+ QString nodeName;
+ QString kind;
+ switch (node->type()) {
+ case Node::Enum:
+ nodeName = "member";
+ kind = "enum";
+ break;
+ case Node::Typedef:
+ nodeName = "member";
+ kind = "typedef";
+ break;
+ case Node::Property:
+ nodeName = "member";
+ kind = "property";
+ break;
+ case Node::Function:
+ nodeName = "member";
+ kind = "function";
+ break;
+ case Node::Namespace:
+ nodeName = "namespace";
+ break;
+ case Node::Class:
+ nodeName = "class";
+ break;
+ case Node::Variable:
+ case Node::Target:
+ default:
+ continue;
+ }
+
+ QString access;
+ switch (node->access()) {
+ case Node::Public:
+ access = "public";
+ break;
+ case Node::Protected:
+ access = "protected";
+ break;
+ case Node::Private:
+ default:
+ continue;
+ }
+
+ QString objName = node->name();
+
+ // Special case: only the root node should have an empty name.
+ if (objName.isEmpty() && node != root())
+ continue;
+
+ // *** Write the starting tag for the element here. ***
+ writer.writeStartElement(nodeName);
+ if (!kind.isEmpty())
+ writer.writeAttribute("kind", kind);
+
+ switch (node->type()) {
+
+ case Node::Class:
+ writer.writeCharacters(node->fullDocumentName());
+ writer.writeEndElement();
+ break;
+ case Node::Namespace:
+ writer.writeCharacters(node->fullDocumentName());
+ writer.writeEndElement();
+ break;
+ case Node::Function:
+ {
+ /*
+ Function nodes contain information about
+ the type of function being described.
+ */
+
+ const FunctionNode* functionNode =
+ static_cast<const FunctionNode*>(node);
+ writer.writeAttribute("protection", access);
+
+ switch (functionNode->virtualness()) {
+ case FunctionNode::NonVirtual:
+ writer.writeAttribute("virtualness", "non");
+ break;
+ case FunctionNode::ImpureVirtual:
+ writer.writeAttribute("virtualness", "virtual");
+ break;
+ case FunctionNode::PureVirtual:
+ writer.writeAttribute("virtual", "pure");
+ break;
+ default:
+ break;
+ }
+ writer.writeAttribute("static",
+ functionNode->isStatic() ? "yes" : "no");
+
+ if (functionNode->virtualness() == FunctionNode::NonVirtual)
+ writer.writeTextElement("type", functionNode->returnType());
+ else
+ writer.writeTextElement("type",
+ "virtual " + functionNode->returnType());
+
+ writer.writeTextElement("name", objName);
+ QStringList pieces = HtmlGenerator::fullDocumentLocation(node,true).split(QLatin1Char('#'));
+ writer.writeTextElement("anchorfile", pieces[0]);
+ writer.writeTextElement("anchor", pieces[1]);
+
+ // Write a signature attribute for convenience.
+ QStringList signatureList;
+
+ foreach (const Parameter& parameter, functionNode->parameters()) {
+ QString leftType = parameter.leftType();
+ const Node* leftNode = const_cast<Tree*>(this)->findNode(parameter.leftType().split("::"),
+ Node::Typedef, 0, SearchBaseClasses|NonFunction);
+ if (!leftNode) {
+ leftNode = const_cast<Tree*>(this)->findNode(
+ parameter.leftType().split("::"), Node::Typedef,
+ node->parent(), SearchBaseClasses|NonFunction);
+ }
+ if (leftNode) {
+ const TypedefNode* typedefNode = static_cast<const TypedefNode*>(leftNode);
+ if (typedefNode->associatedEnum()) {
+ leftType = "QFlags<" +
+ typedefNode->associatedEnum()->fullDocumentName() +
+ QLatin1Char('>');
+ }
+ }
+ signatureList.append(leftType + QLatin1Char(' ') + parameter.name());
+ }
+
+ QString signature = QLatin1Char('(')+signatureList.join(", ")+QLatin1Char(')');
+ if (functionNode->isConst())
+ signature += " const";
+ if (functionNode->virtualness() == FunctionNode::PureVirtual)
+ signature += " = 0";
+ writer.writeTextElement("arglist", signature);
+ }
+ writer.writeEndElement(); // member
+ break;
+
+ case Node::Property:
+ {
+ const PropertyNode* propertyNode = static_cast<const PropertyNode*>(node);
+ writer.writeAttribute("type", propertyNode->dataType());
+ writer.writeTextElement("name", objName);
+ QStringList pieces = HtmlGenerator::fullDocumentLocation(node,true).split(QLatin1Char('#'));
+ writer.writeTextElement("anchorfile", pieces[0]);
+ writer.writeTextElement("anchor", pieces[1]);
+ writer.writeTextElement("arglist", "");
+ }
+ writer.writeEndElement(); // member
+ break;
+
+ case Node::Enum:
+ {
+ const EnumNode* enumNode = static_cast<const EnumNode*>(node);
+ writer.writeTextElement("name", objName);
+ QStringList pieces = HtmlGenerator::fullDocumentLocation(node).split(QLatin1Char('#'));
+ writer.writeTextElement("anchor", pieces[1]);
+ writer.writeTextElement("arglist", "");
+ writer.writeEndElement(); // member
+
+ for (int i = 0; i < enumNode->items().size(); ++i) {
+ EnumItem item = enumNode->items().value(i);
+ writer.writeStartElement("member");
+ writer.writeAttribute("name", item.name());
+ writer.writeTextElement("anchor", pieces[1]);
+ writer.writeTextElement("arglist", "");
+ writer.writeEndElement(); // member
+ }
+ }
+ break;
+
+ case Node::Typedef:
+ {
+ const TypedefNode* typedefNode = static_cast<const TypedefNode*>(node);
+ if (typedefNode->associatedEnum())
+ writer.writeAttribute("type", typedefNode->associatedEnum()->fullDocumentName());
+ else
+ writer.writeAttribute("type", "");
+ writer.writeTextElement("name", objName);
+ QStringList pieces = HtmlGenerator::fullDocumentLocation(node,true).split(QLatin1Char('#'));
+ writer.writeTextElement("anchorfile", pieces[0]);
+ writer.writeTextElement("anchor", pieces[1]);
+ writer.writeTextElement("arglist", "");
+ }
+ writer.writeEndElement(); // member
+ break;
+
+ case Node::Variable:
+ case Node::Target:
+ default:
+ break;
+ }
+ }
+}
+
+/*!
+ */
+void Tree::generateTagFile(const QString& fileName) const
+{
+ QFile file(fileName);
+ if (!file.open(QFile::WriteOnly | QFile::Text))
+ return ;
+
+ QXmlStreamWriter writer(&file);
+ writer.setAutoFormatting(true);
+ writer.writeStartDocument();
+
+ writer.writeStartElement("tagfile");
+
+ generateTagFileCompounds(writer, root());
+
+ writer.writeEndElement(); // tagfile
+ writer.writeEndDocument();
+ file.close();
+}
+
+/*!
+ */
+void Tree::addExternalLink(const QString& url, const Node* relative)
+{
+ FakeNode* fakeNode = new FakeNode(root(), url, Node::ExternalPage, Node::ArticlePage);
+ fakeNode->setAccess(Node::Public);
+
+ // Create some content for the node.
+ QSet<QString> emptySet;
+ Location location(relative->doc().location());
+ Doc doc(location, location, " ", emptySet); // placeholder
+ fakeNode->setDoc(doc);
+}
+
+QT_END_NAMESPACE