summaryrefslogtreecommitdiff
path: root/src/qdoc/qdocindexfiles.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qdoc/qdocindexfiles.cpp')
-rw-r--r--src/qdoc/qdocindexfiles.cpp1628
1 files changed, 1628 insertions, 0 deletions
diff --git a/src/qdoc/qdocindexfiles.cpp b/src/qdoc/qdocindexfiles.cpp
new file mode 100644
index 000000000..b466f5981
--- /dev/null
+++ b/src/qdoc/qdocindexfiles.cpp
@@ -0,0 +1,1628 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qxmlstream.h"
+#include "qdocindexfiles.h"
+#include "qdoctagfiles.h"
+#include "config.h"
+#include "qdocdatabase.h"
+#include "location.h"
+#include "atom.h"
+#include "generator.h"
+#include <qdebug.h>
+
+#include <algorithm>
+
+QT_BEGIN_NAMESPACE
+
+static Node* top = 0;
+
+/*!
+ \class QDocIndexFiles
+
+ This class handles qdoc index files.
+ */
+
+QDocIndexFiles* QDocIndexFiles::qdocIndexFiles_ = NULL;
+
+/*!
+ Constructs the singleton QDocIndexFiles.
+ */
+QDocIndexFiles::QDocIndexFiles()
+ : gen_( 0 )
+{
+ qdb_ = QDocDatabase::qdocDB();
+}
+
+/*!
+ Destroys the singleton QDocIndexFiles.
+ */
+QDocIndexFiles::~QDocIndexFiles()
+{
+ qdb_ = 0;
+ gen_ = 0;
+}
+
+/*!
+ Creates the singleton. Allows only one instance of the class
+ to be created. Returns a pointer to the singleton.
+ */
+QDocIndexFiles* QDocIndexFiles::qdocIndexFiles()
+{
+ if (!qdocIndexFiles_)
+ qdocIndexFiles_ = new QDocIndexFiles;
+ return qdocIndexFiles_;
+}
+
+/*!
+ Destroys the singleton.
+ */
+void QDocIndexFiles::destroyQDocIndexFiles()
+{
+ if (qdocIndexFiles_) {
+ delete qdocIndexFiles_;
+ qdocIndexFiles_ = 0;
+ }
+}
+
+/*!
+ Reads and parses the list of index files in \a indexFiles.
+ */
+void QDocIndexFiles::readIndexes(const QStringList& indexFiles)
+{
+ relatedList_.clear();
+ foreach (const QString& indexFile, indexFiles) {
+ QString msg = "Loading index file: " + indexFile;
+ Location::logToStdErr(msg);
+ //qDebug() << msg;
+ readIndexFile(indexFile);
+ }
+}
+
+static bool readingRoot = true;
+
+/*!
+ Reads and parses the index file at \a path.
+ */
+void QDocIndexFiles::readIndexFile(const QString& path)
+{
+ QFile file(path);
+ if (!file.open(QFile::ReadOnly)) {
+ qWarning() << "Could not read index file" << path;
+ return;
+ }
+
+ QXmlStreamReader reader(&file);
+ reader.setNamespaceProcessing(false);
+
+ if (!reader.readNextStartElement())
+ return;
+
+ if (reader.name() != QLatin1String("INDEX"))
+ return;
+
+ QXmlStreamAttributes attrs = reader.attributes();
+
+ // Generate a relative URL between the install dir and the index file
+ // when the -installdir command line option is set.
+ QString indexUrl;
+ if (Config::installDir.isEmpty()) {
+ indexUrl = attrs.value(QLatin1String("url")).toString();
+ }
+ else {
+ // Use a fake directory, since we will copy the output to a sub directory of
+ // installDir when using "make install". This is just for a proper relative path.
+ //QDir installDir(path.section('/', 0, -3) + "/outputdir");
+ QDir installDir(path.section('/', 0, -3) + '/' + Generator::outputSubdir());
+ indexUrl = installDir.relativeFilePath(path).section('/', 0, -2);
+ }
+ project_ = attrs.value(QLatin1String("project")).toString();
+ basesList_.clear();
+
+ NamespaceNode* root = qdb_->newIndexTree(project_);
+
+ // Scan all elements in the XML file, constructing a map that contains
+ // base classes for each class found.
+ while (reader.readNextStartElement()) {
+ readingRoot = true;
+ readIndexSection(reader, root, indexUrl);
+ }
+
+ // Now that all the base classes have been found for this index,
+ // arrange them into an inheritance hierarchy.
+ resolveIndex();
+}
+
+/*!
+ Read a <section> element from the index file and create the
+ appropriate node(s).
+ */
+void QDocIndexFiles::readIndexSection(QXmlStreamReader& reader,
+ Node* current,
+ const QString& indexUrl)
+{
+ QXmlStreamAttributes attributes = reader.attributes();
+ QStringRef elementName = reader.name();
+
+ QString name = attributes.value(QLatin1String("name")).toString();
+ QString href = attributes.value(QLatin1String("href")).toString();
+ Node* node;
+ Location location;
+ Aggregate* parent = 0;
+
+ bool hasReadChildren = false;
+
+ if (current->isAggregate())
+ parent = static_cast<Aggregate*>(current);
+
+ QString filePath;
+ int lineNo = 0;
+ if (attributes.hasAttribute(QLatin1String("filepath"))) {
+ filePath = attributes.value(QLatin1String("filepath")).toString();
+ lineNo = attributes.value("lineno").toInt();
+ }
+ if (elementName == QLatin1String("namespace")) {
+ node = 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 (elementName == QLatin1String("class")) {
+ node = new ClassNode(parent, name);
+ if (attributes.hasAttribute(QLatin1String("bases"))) {
+ QString bases = attributes.value(QLatin1String("bases")).toString();
+ if (!bases.isEmpty())
+ basesList_.append(QPair<ClassNode*,QString>(static_cast<ClassNode*>(node), bases));
+ }
+ if (!indexUrl.isEmpty())
+ location = Location(indexUrl + QLatin1Char('/') + name.toLower() + ".html");
+ else if (!indexUrl.isNull())
+ location = Location(name.toLower() + ".html");
+ bool abstract = false;
+ if (attributes.value(QLatin1String("abstract")) == QLatin1String("true"))
+ abstract = true;
+ node->setAbstract(abstract);
+ }
+ else if (elementName == QLatin1String("qmlclass")) {
+ QmlTypeNode* qcn = new QmlTypeNode(parent, name);
+ qcn->setTitle(attributes.value(QLatin1String("title")).toString());
+ QString logicalModuleName = attributes.value(QLatin1String("qml-module-name")).toString();
+ if (!logicalModuleName.isEmpty())
+ qdb_->addToQmlModule(logicalModuleName, qcn);
+ bool abstract = false;
+ if (attributes.value(QLatin1String("abstract")) == QLatin1String("true"))
+ abstract = true;
+ qcn->setAbstract(abstract);
+ QString qmlFullBaseName = attributes.value(QLatin1String("qml-base-type")).toString();
+ if (!qmlFullBaseName.isEmpty()) {
+ qcn->setQmlBaseName(qmlFullBaseName);
+ }
+ if (attributes.hasAttribute(QLatin1String("location")))
+ name = attributes.value("location").toString();
+ if (!indexUrl.isEmpty())
+ location = Location(indexUrl + QLatin1Char('/') + name);
+ else if (!indexUrl.isNull())
+ location = Location(name);
+ node = qcn;
+ }
+ else if (elementName == QLatin1String("jstype")) {
+ QmlTypeNode* qcn = new QmlTypeNode(parent, name);
+ qcn->setGenus(Node::JS);
+ qcn->setTitle(attributes.value(QLatin1String("title")).toString());
+ QString logicalModuleName = attributes.value(QLatin1String("js-module-name")).toString();
+ if (!logicalModuleName.isEmpty())
+ qdb_->addToQmlModule(logicalModuleName, qcn);
+ bool abstract = false;
+ if (attributes.value(QLatin1String("abstract")) == QLatin1String("true"))
+ abstract = true;
+ qcn->setAbstract(abstract);
+ QString qmlFullBaseName = attributes.value(QLatin1String("js-base-type")).toString();
+ if (!qmlFullBaseName.isEmpty()) {
+ qcn->setQmlBaseName(qmlFullBaseName);
+ }
+ if (attributes.hasAttribute(QLatin1String("location")))
+ name = attributes.value("location").toString();
+ if (!indexUrl.isEmpty())
+ location = Location(indexUrl + QLatin1Char('/') + name);
+ else if (!indexUrl.isNull())
+ location = Location(name);
+ node = qcn;
+ }
+ else if (elementName == QLatin1String("qmlbasictype")) {
+ QmlBasicTypeNode* qbtn = new QmlBasicTypeNode(parent, name);
+ qbtn->setTitle(attributes.value(QLatin1String("title")).toString());
+ if (attributes.hasAttribute(QLatin1String("location")))
+ name = attributes.value("location").toString();
+ if (!indexUrl.isEmpty())
+ location = Location(indexUrl + QLatin1Char('/') + name);
+ else if (!indexUrl.isNull())
+ location = Location(name);
+ node = qbtn;
+ }
+ else if (elementName == QLatin1String("jsbasictype")) {
+ QmlBasicTypeNode* qbtn = new QmlBasicTypeNode(parent, name);
+ qbtn->setGenus(Node::JS);
+ qbtn->setTitle(attributes.value(QLatin1String("title")).toString());
+ if (attributes.hasAttribute(QLatin1String("location")))
+ name = attributes.value("location").toString();
+ if (!indexUrl.isEmpty())
+ location = Location(indexUrl + QLatin1Char('/') + name);
+ else if (!indexUrl.isNull())
+ location = Location(name);
+ node = qbtn;
+ }
+ else if (elementName == QLatin1String("qmlpropertygroup")) {
+ QmlTypeNode* qcn = static_cast<QmlTypeNode*>(parent);
+ QmlPropertyGroupNode* qpgn = new QmlPropertyGroupNode(qcn, name);
+ if (attributes.hasAttribute(QLatin1String("location")))
+ name = attributes.value("location").toString();
+ if (!indexUrl.isEmpty())
+ location = Location(indexUrl + QLatin1Char('/') + name);
+ else if (!indexUrl.isNull())
+ location = Location(name);
+ node = qpgn;
+ }
+ else if (elementName == QLatin1String("jspropertygroup")) {
+ QmlTypeNode* qcn = static_cast<QmlTypeNode*>(parent);
+ QmlPropertyGroupNode* qpgn = new QmlPropertyGroupNode(qcn, name);
+ qpgn->setGenus(Node::JS);
+ if (attributes.hasAttribute(QLatin1String("location")))
+ name = attributes.value("location").toString();
+ if (!indexUrl.isEmpty())
+ location = Location(indexUrl + QLatin1Char('/') + name);
+ else if (!indexUrl.isNull())
+ location = Location(name);
+ node = qpgn;
+ }
+ else if (elementName == QLatin1String("qmlproperty")) {
+ QString type = attributes.value(QLatin1String("type")).toString();
+ bool attached = false;
+ if (attributes.value(QLatin1String("attached")) == QLatin1String("true"))
+ attached = true;
+ bool readonly = false;
+ if (attributes.value(QLatin1String("writable")) == QLatin1String("false"))
+ readonly = true;
+ QmlPropertyNode* qpn = new QmlPropertyNode(parent, name, type, attached);
+ qpn->setReadOnly(readonly);
+ node = qpn;
+ }
+ else if (elementName == QLatin1String("jsproperty")) {
+ QString type = attributes.value(QLatin1String("type")).toString();
+ bool attached = false;
+ if (attributes.value(QLatin1String("attached")) == QLatin1String("true"))
+ attached = true;
+ bool readonly = false;
+ if (attributes.value(QLatin1String("writable")) == QLatin1String("false"))
+ readonly = true;
+ QmlPropertyNode* qpn = new QmlPropertyNode(parent, name, type, attached);
+ qpn->setGenus(Node::JS);
+ qpn->setReadOnly(readonly);
+ node = qpn;
+ }
+ else if ((elementName == QLatin1String("qmlmethod")) ||
+ (elementName == QLatin1String("qmlsignal")) ||
+ (elementName == QLatin1String("qmlsignalhandler"))) {
+ Node::NodeType t = Node::QmlMethod;
+ if (elementName == QLatin1String("qmlsignal"))
+ t = Node::QmlSignal;
+ else if (elementName == QLatin1String("qmlsignalhandler"))
+ t = Node::QmlSignalHandler;
+ bool attached = false;
+ FunctionNode* fn = new FunctionNode(t, parent, name, attached);
+ node = fn;
+ }
+ else if ((elementName == QLatin1String("jsmethod")) ||
+ (elementName == QLatin1String("jssignal")) ||
+ (elementName == QLatin1String("jssignalhandler"))) {
+ Node::NodeType t = Node::QmlMethod;
+ if (elementName == QLatin1String("jssignal"))
+ t = Node::QmlSignal;
+ else if (elementName == QLatin1String("jssignalhandler"))
+ t = Node::QmlSignalHandler;
+ bool attached = false;
+ FunctionNode* fn = new FunctionNode(t, parent, name, attached);
+ fn->setGenus(Node::JS);
+ node = fn;
+ }
+ else if (elementName == QLatin1String("group")) {
+ CollectionNode* cn = qdb_->addGroup(name);
+ cn->setTitle(attributes.value(QLatin1String("title")).toString());
+ cn->setSubTitle(attributes.value(QLatin1String("subtitle")).toString());
+ if (attributes.value(QLatin1String("seen")) == QLatin1String("true"))
+ cn->markSeen();
+ node = cn;
+ }
+ else if (elementName == QLatin1String("module")) {
+ CollectionNode* cn = qdb_->addModule(name);
+ cn->setTitle(attributes.value(QLatin1String("title")).toString());
+ cn->setSubTitle(attributes.value(QLatin1String("subtitle")).toString());
+ if (attributes.value(QLatin1String("seen")) == QLatin1String("true"))
+ cn->markSeen();
+ node = cn;
+ }
+ else if (elementName == QLatin1String("qmlmodule")) {
+ QString t = attributes.value(QLatin1String("qml-module-name")).toString();
+ CollectionNode* cn = qdb_->addQmlModule(t);
+ QStringList info;
+ info << t << attributes.value(QLatin1String("qml-module-version")).toString();
+ cn->setLogicalModuleInfo(info);
+ cn->setTitle(attributes.value(QLatin1String("title")).toString());
+ cn->setSubTitle(attributes.value(QLatin1String("subtitle")).toString());
+ if (attributes.value(QLatin1String("seen")) == QLatin1String("true"))
+ cn->markSeen();
+ node = cn;
+ }
+ else if (elementName == QLatin1String("jsmodule")) {
+ QString t = attributes.value(QLatin1String("js-module-name")).toString();
+ CollectionNode* cn = qdb_->addJsModule(t);
+ QStringList info;
+ info << t << attributes.value(QLatin1String("js-module-version")).toString();
+ cn->setLogicalModuleInfo(info);
+ cn->setTitle(attributes.value(QLatin1String("title")).toString());
+ cn->setSubTitle(attributes.value(QLatin1String("subtitle")).toString());
+ if (attributes.value(QLatin1String("seen")) == QLatin1String("true"))
+ cn->markSeen();
+ node = cn;
+ }
+ else if (elementName == QLatin1String("page")) {
+ Node::DocSubtype subtype;
+ Node::PageType ptype = Node::NoPageType;
+ QString attr = attributes.value(QLatin1String("subtype")).toString();
+ if (attr == QLatin1String("example")) {
+ subtype = Node::Example;
+ ptype = Node::ExamplePage;
+ }
+ else if (attr == QLatin1String("header")) {
+ subtype = Node::HeaderFile;
+ ptype = Node::ApiPage;
+ }
+ else if (attr == QLatin1String("file")) {
+ subtype = Node::File;
+ ptype = Node::NoPageType;
+ }
+ else if (attr == QLatin1String("page")) {
+ subtype = Node::Page;
+ ptype = Node::ArticlePage;
+ }
+ else if (attr == QLatin1String("externalpage")) {
+ subtype = Node::ExternalPage;
+ ptype = Node::ArticlePage;
+ }
+ else
+ goto done;
+
+ DocumentNode* docNode = new DocumentNode(parent, name, subtype, ptype);
+ docNode->setTitle(attributes.value(QLatin1String("title")).toString());
+
+ if (attributes.hasAttribute(QLatin1String("location")))
+ name = attributes.value(QLatin1String("location")).toString();
+
+ if (!indexUrl.isEmpty())
+ location = Location(indexUrl + QLatin1Char('/') + name);
+ else if (!indexUrl.isNull())
+ location = Location(name);
+
+ node = docNode;
+
+ }
+ else if (elementName == QLatin1String("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");
+
+ while (reader.readNextStartElement()) {
+ QXmlStreamAttributes childAttributes = reader.attributes();
+ if (reader.name() == QLatin1String("value")) {
+
+ EnumItem item(childAttributes.value(QLatin1String("name")).toString(), childAttributes.value(QLatin1String("value")).toString());
+ enumNode->addItem(item);
+ } else if (reader.name() == QLatin1String("keyword")) {
+ insertTarget(TargetRec::Keyword, childAttributes, enumNode);
+ } else if (reader.name() == QLatin1String("target")) {
+ insertTarget(TargetRec::Target, childAttributes, enumNode);
+ }
+ reader.skipCurrentElement();
+ }
+
+ node = enumNode;
+
+ hasReadChildren = true;
+ }
+ else if (elementName == QLatin1String("typedef")) {
+ node = 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 (elementName == QLatin1String("property")) {
+ node = 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 (elementName == QLatin1String("function")) {
+ FunctionNode::Virtualness virt;
+ QString t = attributes.value(QLatin1String("virtual")).toString();
+ if (t == QLatin1String("non"))
+ virt = FunctionNode::NonVirtual;
+ else if (t == QLatin1String("virtual"))
+ virt = FunctionNode::NormalVirtual;
+ else if (t == QLatin1String("pure"))
+ virt = FunctionNode::PureVirtual;
+ else
+ goto done;
+
+ t = attributes.value(QLatin1String("meta")).toString();
+ FunctionNode::Metaness meta;
+ if (t == QLatin1String("plain"))
+ meta = FunctionNode::Plain;
+ else if (t == QLatin1String("signal"))
+ meta = FunctionNode::Signal;
+ else if (t == QLatin1String("slot"))
+ meta = FunctionNode::Slot;
+ else if (t == QLatin1String("constructor"))
+ meta = FunctionNode::Ctor;
+ else if (t == QLatin1String("destructor"))
+ meta = FunctionNode::Dtor;
+ else if (t == QLatin1String("macro"))
+ meta = FunctionNode::MacroWithParams;
+ else if (t == QLatin1String("macrowithparams"))
+ meta = FunctionNode::MacroWithParams;
+ else if (t == QLatin1String("macrowithoutparams"))
+ meta = FunctionNode::MacroWithoutParams;
+ else
+ goto done;
+
+ FunctionNode* functionNode = new FunctionNode(parent, name);
+ functionNode->setReturnType(attributes.value(QLatin1String("return")).toString());
+ functionNode->setVirtualness(virt);
+ functionNode->setMetaness(meta);
+ functionNode->setConst(attributes.value(QLatin1String("const")) == QLatin1String("true"));
+ functionNode->setStatic(attributes.value(QLatin1String("static")) == QLatin1String("true"));
+ if (attributes.value(QLatin1String("overload")) == QLatin1String("true")) {
+ functionNode->setOverloadFlag(true);
+ functionNode->setOverloadNumber(attributes.value(QLatin1String("overload-number")).toUInt());
+ }
+ else {
+ functionNode->setOverloadFlag(false);
+ functionNode->setOverloadNumber(0);
+ }
+ if (attributes.hasAttribute(QLatin1String("relates"))
+ && attributes.value(QLatin1String("relates")) != parent->name()) {
+ relatedList_.append(
+ QPair<FunctionNode*,QString>(functionNode,
+ attributes.value(QLatin1String("relates")).toString()));
+ }
+ /*
+ Note: The "signature" attribute was written to the
+ index file, but it is not read back in. Is that ok?
+ */
+
+ while (reader.readNextStartElement()) {
+ QXmlStreamAttributes childAttributes = reader.attributes();
+ if (reader.name() == QLatin1String("parameter")) {
+ // Do not use the default value for the parameter; it is not
+ // required, and has been known to cause problems.
+ Parameter parameter(childAttributes.value(QLatin1String("left")).toString(),
+ childAttributes.value(QLatin1String("right")).toString(),
+ childAttributes.value(QLatin1String("name")).toString(),
+ QString()); // childAttributes.value(QLatin1String("default"))
+ functionNode->addParameter(parameter);
+ } else if (reader.name() == QLatin1String("keyword")) {
+ insertTarget(TargetRec::Keyword, childAttributes, functionNode);
+ } else if (reader.name() == QLatin1String("target")) {
+ insertTarget(TargetRec::Target, childAttributes, functionNode);
+ }
+ reader.skipCurrentElement();
+ }
+
+ node = functionNode;
+ if (!indexUrl.isEmpty())
+ location = Location(indexUrl + QLatin1Char('/') + parent->name().toLower() + ".html");
+ else if (!indexUrl.isNull())
+ location = Location(parent->name().toLower() + ".html");
+
+ hasReadChildren = true;
+ }
+ else if (elementName == QLatin1String("variable")) {
+ node = 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 (elementName == QLatin1String("keyword")) {
+ insertTarget(TargetRec::Keyword, attributes, current);
+ goto done;
+ }
+ else if (elementName == QLatin1String("target")) {
+ insertTarget(TargetRec::Target, attributes, current);
+ goto done;
+ }
+ else if (elementName == QLatin1String("contents")) {
+ insertTarget(TargetRec::Contents, attributes, current);
+ goto done;
+ }
+ else
+ goto done;
+
+ {
+ QString access = attributes.value(QLatin1String("access")).toString();
+ if (access == "public")
+ node->setAccess(Node::Public);
+ else if (access == "protected")
+ node->setAccess(Node::Protected);
+ else if ((access == "private") || (access == "internal"))
+ node->setAccess(Node::Private);
+ else
+ node->setAccess(Node::Public);
+
+ if ((elementName != QLatin1String("page")) &&
+ (elementName != QLatin1String("qmlclass")) &&
+ (elementName != QLatin1String("qmlbasictype")) &&
+ (elementName != QLatin1String("jstype")) &&
+ (elementName != QLatin1String("jsbasictype"))) {
+ QString threadSafety = attributes.value(QLatin1String("threadsafety")).toString();
+ if (threadSafety == QLatin1String("non-reentrant"))
+ node->setThreadSafeness(Node::NonReentrant);
+ else if (threadSafety == QLatin1String("reentrant"))
+ node->setThreadSafeness(Node::Reentrant);
+ else if (threadSafety == QLatin1String("thread safe"))
+ node->setThreadSafeness(Node::ThreadSafe);
+ else
+ node->setThreadSafeness(Node::UnspecifiedSafeness);
+ }
+ else
+ node->setThreadSafeness(Node::UnspecifiedSafeness);
+
+ QString status = attributes.value(QLatin1String("status")).toString();
+ if (status == QLatin1String("compat"))
+ node->setStatus(Node::Compat);
+ else if (status == QLatin1String("obsolete"))
+ node->setStatus(Node::Obsolete);
+ else if (status == QLatin1String("deprecated"))
+ node->setStatus(Node::Obsolete);
+ else if (status == QLatin1String("preliminary"))
+ node->setStatus(Node::Preliminary);
+ else if (status == QLatin1String("active"))
+ node->setStatus(Node::Active);
+ else if (status == QLatin1String("internal"))
+ node->setStatus(Node::Internal);
+ else
+ node->setStatus(Node::Active);
+
+ QString physicalModuleName = attributes.value(QLatin1String("module")).toString();
+ if (!physicalModuleName.isEmpty())
+ qdb_->addToModule(physicalModuleName, node);
+ if (!href.isEmpty()) {
+ if (node->isExternalPage())
+ node->setUrl(href);
+ else if (!indexUrl.isEmpty())
+ node->setUrl(indexUrl + QLatin1Char('/') + href);
+ }
+
+ QString since = attributes.value(QLatin1String("since")).toString();
+ if (!since.isEmpty()) {
+ node->setSince(since);
+ }
+
+ QString groupsAttr = attributes.value(QLatin1String("groups")).toString();
+ if (!groupsAttr.isEmpty()) {
+ QStringList groupNames = groupsAttr.split(QLatin1Char(','));
+ foreach (const QString &name, groupNames) {
+ qdb_->addToGroup(name, node);
+ }
+ }
+
+ // Create some content for the node.
+ QSet<QString> emptySet;
+ Location t(filePath);
+ if (!filePath.isEmpty()) {
+ t.setLineNo(lineNo);
+ node->setLocation(t);
+ location = t;
+ }
+ Doc doc(location, location, " ", emptySet, emptySet); // placeholder
+ node->setDoc(doc);
+ node->setIndexNodeFlag();
+ node->setOutputSubdirectory(project_.toLower());
+ QString briefAttr = attributes.value(QLatin1String("brief")).toString();
+ if (!briefAttr.isEmpty()) {
+ node->setReconstitutedBrief(briefAttr);
+ }
+
+ if (!hasReadChildren) {
+ bool useParent = (elementName == QLatin1String("namespace") && name.isEmpty());
+ while (reader.readNextStartElement()) {
+ if (useParent)
+ readIndexSection(reader, parent, indexUrl);
+ else
+ readIndexSection(reader, node, indexUrl);
+ }
+ }
+ }
+
+ done:
+ while (!reader.isEndElement()) {
+ if (reader.readNext() == QXmlStreamReader::Invalid) {
+ break;
+ }
+ }
+}
+
+void QDocIndexFiles::insertTarget(TargetRec::TargetType type,
+ const QXmlStreamAttributes &attributes,
+ Node *node)
+{
+ int priority;
+ switch (type) {
+ case TargetRec::Keyword:
+ priority = 1;
+ break;
+ case TargetRec::Target:
+ priority = 2;
+ break;
+ case TargetRec::Contents:
+ priority = 3;
+ break;
+ default:
+ return;
+ }
+
+ QString name = attributes.value(QLatin1String("name")).toString();
+ QString title = attributes.value(QLatin1String("title")).toString();
+ qdb_->insertTarget(name, title, type, node, priority);
+}
+
+/*!
+ This function tries to resolve class inheritance immediately
+ after the index file is read. It is not always possible to
+ resolve a class inheritance at this point, because the base
+ class might be in an index file that hasn't been read yet, or
+ it might be in one of the header files that will be read for
+ the current module. These cases will be resolved after all
+ the index files and header and source files have been read,
+ just prior to beginning the generate phase for the current
+ module.
+
+ I don't think this is completely correct because it always
+ sets the access to public.
+ */
+void QDocIndexFiles::resolveIndex()
+{
+ QPair<ClassNode*,QString> pair;
+ foreach (pair, basesList_) {
+ foreach (const QString& base, pair.second.split(QLatin1Char(','))) {
+ QStringList basePath = base.split(QString("::"));
+ Node* n = qdb_->findClassNode(basePath);
+ if (n)
+ pair.first->addResolvedBaseClass(Node::Public, static_cast<ClassNode*>(n));
+ else
+ pair.first->addUnresolvedBaseClass(Node::Public, basePath, QString());
+ }
+ }
+ // No longer needed.
+ basesList_.clear();
+}
+
+/*
+ Goes though the list of nodes that are related to other aggregates
+ that were read from all index files, and tries to find the aggregate
+ nodes from the database. Calls the node's setRelates() for each
+ aggregate that is found in the local module (primary tree).
+
+ This function is meant to be called before starting the doc generation,
+ after all the index files are read.
+ */
+void QDocIndexFiles::resolveRelates()
+{
+ if (relatedList_.isEmpty())
+ return;
+
+ // Restrict searching only to the local (primary) tree
+ QVector<Tree*> searchOrder = qdb_->searchOrder();
+ qdb_->setLocalSearch();
+
+ QPair<FunctionNode*,QString> relatedPair;
+ foreach (relatedPair, relatedList_) {
+ QStringList path = relatedPair.second.split("::");
+ Node* n = qdb_->findRelatesNode(path);
+ if (n)
+ relatedPair.first->setRelates(static_cast<Aggregate*>(n));
+ }
+ // Restore original search order
+ qdb_->setSearchOrder(searchOrder);
+ relatedList_.clear();
+}
+
+/*!
+ 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 QDocIndexFiles::generateIndexSection(QXmlStreamWriter& writer,
+ Node* node,
+ bool generateInternalNodes)
+{
+ /*
+ Don't include index nodes in a new index file. Or DITA map nodes.
+ */
+ if (node->isIndexNode() || node->docSubtype() == Node::DitaMap)
+ return false;
+
+ QString nodeName;
+ QString logicalModuleName;
+ QString logicalModuleVersion;
+ QString qmlFullBaseName;
+ switch (node->type()) {
+ case Node::Namespace:
+ nodeName = "namespace";
+ break;
+ case Node::Class:
+ nodeName = "class";
+ break;
+ case Node::QmlType:
+ {
+ if (node->isQmlNode())
+ nodeName = "qmlclass";
+ else
+ nodeName = "jstype";
+ CollectionNode* cn = node->logicalModule();
+ if (cn)
+ logicalModuleName = cn->logicalModuleName();
+ qmlFullBaseName = node->qmlFullBaseName();
+ }
+ break;
+ case Node::QmlBasicType:
+ if (node->isQmlNode())
+ nodeName = "qmlbasictype";
+ else
+ nodeName = "jsbasictype";
+ break;
+ case Node::Document:
+ nodeName = "page";
+ break;
+ case Node::Group:
+ nodeName = "group";
+ break;
+ case Node::Module:
+ nodeName = "module";
+ break;
+ case Node::QmlModule:
+ if (node->isQmlNode())
+ nodeName = "qmlmodule";
+ else
+ nodeName = "jsmodule";
+ 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::QmlProperty:
+ if (node->isQmlNode())
+ nodeName = "qmlproperty";
+ else
+ nodeName = "jsProperty";
+ break;
+ case Node::QmlPropertyGroup:
+ if (node->isQmlNode())
+ nodeName = "qmlpropertygroup";
+ else
+ nodeName = "jspropertygroup";
+ break;
+ case Node::QmlSignal:
+ if (node->isQmlNode())
+ nodeName = "qmlsignal";
+ else
+ nodeName = "jssignal";
+ break;
+ case Node::QmlSignalHandler:
+ if (node->isQmlNode())
+ nodeName = "qmlsignalhandler";
+ else
+ nodeName = "jssignalhandler";
+ break;
+ case Node::QmlMethod:
+ if (node->isQmlNode())
+ nodeName = "qmlmethod";
+ else
+ nodeName = "jsmethod";
+ break;
+ default:
+ return false;
+ }
+
+ QString access;
+ switch (node->access()) {
+ case Node::Public:
+ access = "public";
+ break;
+ case Node::Protected:
+ access = "protected";
+ break;
+ case Node::Private:
+ {
+ access = "private";
+ bool b = generateInternalNodes;
+ if (b)
+ b = false;
+ }
+ break;
+ default:
+ return false;
+ }
+
+ QString objName = node->name();
+ // Special case: only the root node should have an empty name.
+ if (objName.isEmpty() && node != qdb_->primaryTreeRoot())
+ return false;
+
+ writer.writeStartElement(nodeName);
+
+ QXmlStreamAttributes attributes;
+
+ if (!node->isDocumentNode() && !node->isCollectionNode()) {
+ 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 = "obsolete";
+ break;
+ case Node::Preliminary:
+ status = "preliminary";
+ break;
+ case Node::Active:
+ status = "active";
+ break;
+ case Node::Internal:
+ status = "internal";
+ break;
+ default:
+ status = "main";
+ break;
+ }
+
+ writer.writeAttribute("name", objName);
+
+ // Write module and base type info for QML/JS types
+ if (node->type() == Node::QmlType || node->type() == Node::QmlModule) {
+ QString baseNameAttr("qml-base-type");
+ QString moduleNameAttr("qml-module-name");
+ QString moduleVerAttr("qml-module-version");
+ if (node->isJsNode()) {
+ baseNameAttr = "js-base-type";
+ moduleNameAttr = "js-module-name";
+ moduleVerAttr = "js-module-version";
+ }
+ if (node->type() == Node::QmlModule) {
+ logicalModuleName = node->logicalModuleName();
+ logicalModuleVersion = node->logicalModuleVersion();
+ }
+ if (!logicalModuleName.isEmpty())
+ writer.writeAttribute(moduleNameAttr, logicalModuleName);
+ if (!logicalModuleVersion.isEmpty())
+ writer.writeAttribute(moduleVerAttr, logicalModuleVersion);
+ if (!qmlFullBaseName.isEmpty())
+ writer.writeAttribute(baseNameAttr, qmlFullBaseName);
+ }
+
+ QString href;
+ if (!node->isExternalPage()) {
+ QString fullName = node->fullDocumentName();
+ if (fullName != objName)
+ writer.writeAttribute("fullname", fullName);
+#if 0
+ if (Generator::useOutputSubdirs())
+ href = node->outputSubdirectory();
+ if (!href.isEmpty())
+ href.append(QLatin1Char('/'));
+ href.append(gen_->fullDocumentLocation(node));
+#endif
+ href = gen_->fullDocumentLocation(node);
+ }
+ else
+ href = node->name();
+ if (node->isQmlNode() || node->isJsNode()) {
+ Aggregate* p = node->parent();
+ if (p) {
+ if (p->isQmlPropertyGroup() || p->isJsPropertyGroup())
+ p = p->parent();
+ if (p && (p->isQmlType() || p->isJsType()) && p->isAbstract())
+ href.clear();
+ }
+ }
+ if (!href.isEmpty())
+ writer.writeAttribute("href", href);
+
+ writer.writeAttribute("status", status);
+ if (!node->isDocumentNode() && !node->isCollectionNode()) {
+ writer.writeAttribute("access", access);
+ if (node->isAbstract())
+ writer.writeAttribute("abstract", "true");
+ }
+ const Location& declLocation = node->declLocation();
+ if (!declLocation.fileName().isEmpty())
+ writer.writeAttribute("location", declLocation.fileName());
+ if (!declLocation.filePath().isEmpty()) {
+ writer.writeAttribute("filepath", declLocation.filePath());
+ writer.writeAttribute("lineno", QString("%1").arg(declLocation.lineNo()));
+ }
+
+ if (!node->since().isEmpty()) {
+ writer.writeAttribute("since", node->since());
+ }
+
+ QString brief = node->doc().trimmedBriefText(node->name()).toString();
+ 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* n = related.node_;
+ if (n)
+ baseStrings.insert(n->fullName());
+ }
+ if (!baseStrings.isEmpty())
+ writer.writeAttribute("bases", QStringList(baseStrings.toList()).join(QLatin1Char(',')));
+ if (!node->physicalModuleName().isEmpty())
+ writer.writeAttribute("module", node->physicalModuleName());
+ if (!classNode->groupNames().isEmpty())
+ writer.writeAttribute("groups", classNode->groupNames().join(QLatin1Char(',')));
+ if (!brief.isEmpty())
+ writer.writeAttribute("brief", brief);
+ }
+ break;
+ case Node::Namespace:
+ {
+ const NamespaceNode* namespaceNode = static_cast<const NamespaceNode*>(node);
+ if (!namespaceNode->physicalModuleName().isEmpty())
+ writer.writeAttribute("module", namespaceNode->physicalModuleName());
+ if (!namespaceNode->groupNames().isEmpty())
+ writer.writeAttribute("groups", namespaceNode->groupNames().join(QLatin1Char(',')));
+ if (!brief.isEmpty())
+ writer.writeAttribute("brief", brief);
+ }
+ break;
+ case Node::QmlType:
+ {
+ const QmlTypeNode* qcn = static_cast<const QmlTypeNode*>(node);
+ writer.writeAttribute("title", qcn->title());
+ writer.writeAttribute("fulltitle", qcn->fullTitle());
+ writer.writeAttribute("subtitle", qcn->subTitle());
+ if (!qcn->groupNames().isEmpty())
+ writer.writeAttribute("groups", qcn->groupNames().join(QLatin1Char(',')));
+ if (!brief.isEmpty())
+ writer.writeAttribute("brief", brief);
+ }
+ break;
+ case Node::Document:
+ {
+ /*
+ Document nodes (such as manual pages) have a subtype,
+ a title, and other attributes.
+ */
+ bool writeModuleName = false;
+ const DocumentNode* docNode = static_cast<const DocumentNode*>(node);
+ switch (docNode->docSubtype()) {
+ case Node::Example:
+ writer.writeAttribute("subtype", "example");
+ writeModuleName = true;
+ break;
+ case Node::HeaderFile:
+ writer.writeAttribute("subtype", "header");
+ writeModuleName = true;
+ break;
+ case Node::File:
+ writer.writeAttribute("subtype", "file");
+ break;
+ case Node::Page:
+ writer.writeAttribute("subtype", "page");
+ writeModuleName = true;
+ break;
+ case Node::ExternalPage:
+ writer.writeAttribute("subtype", "externalpage");
+ break;
+ default:
+ break;
+ }
+ writer.writeAttribute("title", docNode->title());
+ writer.writeAttribute("fulltitle", docNode->fullTitle());
+ writer.writeAttribute("subtitle", docNode->subTitle());
+ if (!node->physicalModuleName().isEmpty() && writeModuleName) {
+ writer.writeAttribute("module", node->physicalModuleName());
+ }
+ if (!docNode->groupNames().isEmpty())
+ writer.writeAttribute("groups", docNode->groupNames().join(QLatin1Char(',')));
+ if (!brief.isEmpty())
+ writer.writeAttribute("brief", brief);
+ }
+ break;
+ case Node::Group:
+ {
+ const CollectionNode* cn = static_cast<const CollectionNode*>(node);
+ writer.writeAttribute("seen", cn->wasSeen() ? "true" : "false");
+ writer.writeAttribute("title", cn->title());
+ if (!cn->subTitle().isEmpty())
+ writer.writeAttribute("subtitle", cn->subTitle());
+ if (!cn->physicalModuleName().isEmpty())
+ writer.writeAttribute("module", cn->physicalModuleName());
+ if (!cn->groupNames().isEmpty())
+ writer.writeAttribute("groups", cn->groupNames().join(QLatin1Char(',')));
+ /*
+ This is not read back in, so it probably
+ shouldn't be written out in the first place.
+ */
+ if (!cn->members().isEmpty()) {
+ QStringList names;
+ foreach (const Node* member, cn->members())
+ names.append(member->name());
+ writer.writeAttribute("members", names.join(QLatin1Char(',')));
+ }
+ if (!brief.isEmpty())
+ writer.writeAttribute("brief", brief);
+ }
+ break;
+ case Node::Module:
+ {
+ const CollectionNode* cn = static_cast<const CollectionNode*>(node);
+ writer.writeAttribute("seen", cn->wasSeen() ? "true" : "false");
+ writer.writeAttribute("title", cn->title());
+ if (!cn->subTitle().isEmpty())
+ writer.writeAttribute("subtitle", cn->subTitle());
+ if (!cn->physicalModuleName().isEmpty())
+ writer.writeAttribute("module", cn->physicalModuleName());
+ if (!cn->groupNames().isEmpty())
+ writer.writeAttribute("groups", cn->groupNames().join(QLatin1Char(',')));
+ /*
+ This is not read back in, so it probably
+ shouldn't be written out in the first place.
+ */
+ if (!cn->members().isEmpty()) {
+ QStringList names;
+ foreach (const Node* member, cn->members())
+ names.append(member->name());
+ writer.writeAttribute("members", names.join(QLatin1Char(',')));
+ }
+ if (!brief.isEmpty())
+ writer.writeAttribute("brief", brief);
+ }
+ break;
+ case Node::QmlModule:
+ {
+ const CollectionNode* cn = static_cast<const CollectionNode*>(node);
+ writer.writeAttribute("seen", cn->wasSeen() ? "true" : "false");
+ writer.writeAttribute("title", cn->title());
+ if (!cn->subTitle().isEmpty())
+ writer.writeAttribute("subtitle", cn->subTitle());
+ if (!cn->physicalModuleName().isEmpty())
+ writer.writeAttribute("module", cn->physicalModuleName());
+ if (!cn->groupNames().isEmpty())
+ writer.writeAttribute("groups", cn->groupNames().join(QLatin1Char(',')));
+ /*
+ This is not read back in, so it probably
+ shouldn't be written out in the first place.
+ */
+ if (!cn->members().isEmpty()) {
+ QStringList names;
+ foreach (const Node* member, cn->members())
+ names.append(member->name());
+ writer.writeAttribute("members", names.join(QLatin1Char(',')));
+ }
+ if (!brief.isEmpty())
+ writer.writeAttribute("brief", brief);
+ }
+ 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::NormalVirtual:
+ writer.writeAttribute("virtual", "virtual");
+ 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());
+ }
+ if (functionNode->hasAssociatedProperties()) {
+ QString associatedProperties;
+ foreach (PropertyNode* pn, functionNode->associatedProperties()) {
+ if (!associatedProperties.isEmpty())
+ associatedProperties += QLatin1String(", ");
+ associatedProperties += pn->name();
+ }
+ writer.writeAttribute("associated-property", associatedProperties);
+ }
+ writer.writeAttribute("type", functionNode->returnType());
+ if (!brief.isEmpty())
+ writer.writeAttribute("brief", brief);
+
+ /*
+ Note: The "signature" attribute is written to the
+ index file, but it is not read back in. Is that ok?
+ */
+ QString signature = functionNode->signature();
+ 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", parameter.dataType());
+ writer.writeAttribute("right", parameter.rightType());
+ writer.writeAttribute("name", parameter.name());
+ writer.writeAttribute("default", parameter.defaultValue());
+ writer.writeEndElement(); // parameter
+ }
+ }
+ break;
+ case Node::QmlProperty:
+ {
+ QmlPropertyNode* qpn = static_cast<QmlPropertyNode*>(node);
+ writer.writeAttribute("type", qpn->dataType());
+ writer.writeAttribute("attached", qpn->isAttached() ? "true" : "false");
+ writer.writeAttribute("writable", qpn->isWritable() ? "true" : "false");
+ if (!brief.isEmpty())
+ writer.writeAttribute("brief", brief);
+ }
+ break;
+ case Node::QmlPropertyGroup:
+ {
+ if (!brief.isEmpty())
+ writer.writeAttribute("brief", brief);
+ }
+ break;
+ case Node::Property:
+ {
+ const PropertyNode* propertyNode = static_cast<const PropertyNode*>(node);
+ writer.writeAttribute("type", propertyNode->dataType());
+ if (!brief.isEmpty())
+ writer.writeAttribute("brief", brief);
+ 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(); // setter
+ }
+ }
+ 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(); // resetter
+ }
+ }
+ foreach (const Node* fnNode, propertyNode->notifiers()) {
+ if (fnNode) {
+ const FunctionNode* functionNode = static_cast<const FunctionNode*>(fnNode);
+ writer.writeStartElement("notifier");
+ writer.writeAttribute("name", functionNode->name());
+ writer.writeEndElement(); // notifier
+ }
+ }
+ }
+ break;
+ case Node::Variable:
+ {
+ const VariableNode* variableNode = static_cast<const VariableNode*>(node);
+ writer.writeAttribute("type", variableNode->dataType());
+ writer.writeAttribute("static", variableNode->isStatic() ? "true" : "false");
+ if (!brief.isEmpty())
+ writer.writeAttribute("brief", brief);
+ }
+ break;
+ case 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
+ }
+ }
+ break;
+ case Node::Typedef:
+ {
+ const TypedefNode* typedefNode = static_cast<const TypedefNode*>(node);
+ if (typedefNode->associatedEnum()) {
+ writer.writeAttribute("enum",typedefNode->associatedEnum()->fullDocumentName());
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ /*
+ For our 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.
+
+ targets and keywords are now allowed in any node, not just inner nodes.
+ */
+
+ if (node->doc().hasTargets()) {
+ bool external = false;
+ if (node->type() == Node::Document) {
+ const DocumentNode* docNode = static_cast<const DocumentNode*>(node);
+ if (docNode->docSubtype() == Node::ExternalPage)
+ external = true;
+ }
+ foreach (const Atom* target, node->doc().targets()) {
+ QString title = target->string();
+ QString name = Doc::canonicalTitle(title);
+ writer.writeStartElement("target");
+ if (!external)
+ writer.writeAttribute("name", name);
+ else
+ writer.writeAttribute("name", title);
+ if (name != title)
+ writer.writeAttribute("title", title);
+ writer.writeEndElement(); // target
+ }
+ }
+ if (node->doc().hasKeywords()) {
+ foreach (const Atom* keyword, node->doc().keywords()) {
+ QString title = keyword->string();
+ QString name = Doc::canonicalTitle(title);
+ writer.writeStartElement("keyword");
+ writer.writeAttribute("name", name);
+ if (name != title)
+ writer.writeAttribute("title", title);
+ writer.writeEndElement(); // keyword
+ }
+ }
+
+ // 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->isAggregate()) {
+ const Aggregate* inner = static_cast<const Aggregate*>(node);
+
+ 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
+ }
+ }
+ }
+ return true;
+}
+
+/*!
+ Returns \c 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 false;
+
+ 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->isDocumentNode() && n2->isDocumentNode()) {
+ const DocumentNode* f1 = static_cast<const DocumentNode*>(n1);
+ const DocumentNode* f2 = static_cast<const DocumentNode*>(n2);
+ if (f1->fullTitle() < f2->fullTitle())
+ return true;
+ else if (f1->fullTitle() > f2->fullTitle())
+ return false;
+ }
+ else if (n1->isCollectionNode() && n2->isCollectionNode()) {
+ const CollectionNode* f1 = static_cast<const CollectionNode*>(n1);
+ const CollectionNode* f2 = static_cast<const CollectionNode*>(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 QDocIndexFiles::generateIndexSections(QXmlStreamWriter& writer,
+ Node* node,
+ bool generateInternalNodes)
+{
+ /*
+ Note that groups, modules, and QML modules are written
+ after all the other nodes.
+ */
+ if (node->isGroup() || node->isModule() || node->isQmlModule() || node->isJsModule())
+ return;
+
+ if (generateIndexSection(writer, node, generateInternalNodes)) {
+ if (node->isAggregate()) {
+ const Aggregate* inner = static_cast<const Aggregate*>(node);
+
+ NodeList cnodes = inner->childNodes();
+ std::sort(cnodes.begin(), cnodes.end(), compareNodes);
+
+ foreach (Node* child, cnodes) {
+ generateIndexSections(writer, child, generateInternalNodes);
+ }
+ }
+
+ if (node == top) {
+ /*
+ We wait until the end of the index file to output the group, module,
+ and QML module elements. By outputting them at the end, when we read
+ the index file back in, all the group, module, and QML module member
+ elements will have already been created. It is then only necessary to
+ create the group, module, or QML module element and add each member to
+ its member list.
+ */
+ const CNMap& groups = qdb_->groups();
+ if (!groups.isEmpty()) {
+ CNMap::ConstIterator g = groups.constBegin();
+ while (g != groups.constEnd()) {
+ if (generateIndexSection(writer, g.value(), generateInternalNodes))
+ writer.writeEndElement();
+ ++g;
+ }
+ }
+
+ const CNMap& modules = qdb_->modules();
+ if (!modules.isEmpty()) {
+ CNMap::ConstIterator g = modules.constBegin();
+ while (g != modules.constEnd()) {
+ if (generateIndexSection(writer, g.value(), generateInternalNodes))
+ writer.writeEndElement();
+ ++g;
+ }
+ }
+
+ const CNMap& qmlModules = qdb_->qmlModules();
+ if (!qmlModules.isEmpty()) {
+ CNMap::ConstIterator g = qmlModules.constBegin();
+ while (g != qmlModules.constEnd()) {
+ if (generateIndexSection(writer, g.value(), generateInternalNodes))
+ writer.writeEndElement();
+ ++g;
+ }
+ }
+
+ const CNMap& jsModules = qdb_->jsModules();
+ if (!jsModules.isEmpty()) {
+ CNMap::ConstIterator g = jsModules.constBegin();
+ while (g != jsModules.constEnd()) {
+ if (generateIndexSection(writer, g.value(), generateInternalNodes))
+ writer.writeEndElement();
+ ++g;
+ }
+ }
+ }
+
+ writer.writeEndElement();
+ }
+}
+
+/*!
+ Outputs an index file.
+ */
+void QDocIndexFiles::generateIndex(const QString& fileName,
+ const QString& url,
+ const QString& title,
+ Generator* g,
+ bool generateInternalNodes)
+{
+ QFile file(fileName);
+ if (!file.open(QFile::WriteOnly | QFile::Text))
+ return;
+
+ QString msg = "Writing index file: " + fileName;
+ Location::logToStdErr(msg);
+
+ gen_ = g;
+ 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", qdb_->version());
+ writer.writeAttribute("project", g->config()->getString(CONFIG_PROJECT));
+
+ top = qdb_->primaryTreeRoot();
+ generateIndexSections(writer, top, generateInternalNodes);
+
+ writer.writeEndElement(); // INDEX
+ writer.writeEndElement(); // QDOCINDEX
+ writer.writeEndDocument();
+ file.close();
+}
+
+QT_END_NAMESPACE