diff options
Diffstat (limited to 'src/tools/qdoc/qdocindexfiles.cpp')
-rw-r--r-- | src/tools/qdoc/qdocindexfiles.cpp | 1070 |
1 files changed, 1070 insertions, 0 deletions
diff --git a/src/tools/qdoc/qdocindexfiles.cpp b/src/tools/qdoc/qdocindexfiles.cpp new file mode 100644 index 0000000000..d68fe0e6fa --- /dev/null +++ b/src/tools/qdoc/qdocindexfiles.cpp @@ -0,0 +1,1070 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 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, Digia gives you certain additional +** rights. These rights are described in the Digia 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. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdom.h" +#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 "doc.h" +//include "htmlgenerator.h" +//include "node.h" +//include "text.h" +//include <limits.h> +//include <qdebug.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QDocIndexFiles + + This class handles qdoc index files. + */ + +QDocIndexFiles* QDocIndexFiles::qdocIndexFiles_ = NULL; + +/*! + Constructs the singleton QDocIndexFiles. + */ +QDocIndexFiles::QDocIndexFiles() +{ + qdb_ = QDocDatabase::qdocDB(); +} + +/*! + Destroys the singleton QDocIndexFiles. + */ +QDocIndexFiles::~QDocIndexFiles() +{ + qdb_ = 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) +{ + foreach (const QString& indexFile, indexFiles) + readIndexFile(indexFile); +} + +/*! + Reads and parses the index file at \a path. + */ +void QDocIndexFiles::readIndexFile(const QString& path) +{ + QFile file(path); + if (file.open(QFile::ReadOnly)) { + QDomDocument document; + document.setContent(&file); + file.close(); + + QDomElement indexElement = document.documentElement(); + + // 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 = indexElement.attribute("url", QString()); + } + 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"); + indexUrl = installDir.relativeFilePath(path).section('/', 0, -2); + } + + basesList_.clear(); + 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, qdb_->treeRoot(), indexUrl); + child = child.nextSiblingElement(); + } + + // 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(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); + 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); + qcn->setTitle(element.attribute("title")); + if (element.hasAttribute("location")) + name = element.attribute("location", QString()); + 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", QString()); + 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; + + DocNode* docNode = new DocNode(parent, name, subtype, ptype); + docNode->setTitle(element.attribute("title")); + + if (element.hasAttribute("location")) + name = element.attribute("location", QString()); + + if (!indexUrl.isEmpty()) + location = Location(indexUrl + QLatin1Char('/') + name); + else if (!indexUrl.isNull()) + location = Location(name); + + section = docNode; + + } + 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()) { + 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"), + QString()); // 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") { + qdb_->insertTarget(name, parent,1); + return; + } + else if (element.nodeName() == "target") { + qdb_->insertTarget(name, parent,2); + return; + } + else if (element.nodeName() == "contents") { + qdb_->insertTarget(name, parent,3); + 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()) { + section->setUrl(indexUrl + QLatin1Char('/') + href); + } + + // Create some content for the node. + QSet<QString> emptySet; + Doc doc(location, location, " ", emptySet); // placeholder + section->setDoc(doc); + section->setIndexNodeFlag(); + + 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(); + } + } + } +} + +/*! + */ +void QDocIndexFiles::resolveIndex() +{ + QPair<ClassNode*,QString> pair; + foreach (pair, basesList_) { + foreach (const QString& base, pair.second.split(QLatin1Char(','))) { + Node* n = qdb_->treeRoot()->findChildNodeByNameAndType(base, Node::Class); + if (n) { + pair.first->addBaseClass(Node::Public, static_cast<ClassNode*>(n)); + } + } + } + + QPair<FunctionNode*,QString> relatedPair; + foreach (relatedPair, relatedList_) { + Node* n = qdb_->treeRoot()->findChildNodeByNameAndType(relatedPair.second, Node::Class); + if (n) + relatedPair.first->setRelates(static_cast<ClassNode*>(n)); + } +} + +/*! + 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) +{ + if (!node->url().isEmpty() || node->subType() == Node::DitaMap) + return false; + + QString nodeName; + switch (node->type()) { + case Node::Namespace: + nodeName = "namespace"; + break; + case Node::Class: + nodeName = "class"; + break; + case Node::Document: + 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::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 != qdb_->treeRoot()) + return false; + + writer.writeStartElement(nodeName); + + QXmlStreamAttributes attributes; + writer.writeAttribute("access", access); + + if (node->type() != Node::Document) { + 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(gen_->fullDocumentLocation(node)); + writer.writeAttribute("href", href); + if ((node->type() != Node::Document) && (!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::Document: + { + /* + Document nodes (such as manual pages) contain subtypes, + titles and other attributes. + */ + const DocNode* docNode = static_cast<const DocNode*>(node); + switch (docNode->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", docNode->title()); + writer.writeAttribute("fulltitle", docNode->fullTitle()); + writer.writeAttribute("subtitle", docNode->subTitle()); + writer.writeAttribute("location", docNode->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: + { + QmlPropertyNode* qpn = static_cast<QmlPropertyNode*>(node); + writer.writeAttribute("type", qpn->dataType()); + writer.writeAttribute("attached", qpn->isAttached() ? "true" : "false"); + writer.writeAttribute("writable", qpn->isWritable(qdb_) ? "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(); // 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"); + } + 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::Document) { + const DocNode* docNode = static_cast<const DocNode*>(inner); + if (docNode->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 = qdb_->findNode(parameter.leftType().split("::"), + 0, + SearchBaseClasses|NonFunction); + if (!leftNode || leftNode->type() != Node::Typedef) { + leftNode = qdb_->findNode(parameter.leftType().split("::"), + node->parent(), + SearchBaseClasses|NonFunction); + } + if (leftNode && leftNode->type() == Node::Typedef) { + 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::Document && n2->type() == Node::Document) { + const DocNode* f1 = static_cast<const DocNode*>(n1); + const DocNode* f2 = static_cast<const DocNode*>(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) +{ + 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 (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 (Node* c, pgn->childNodes()) { + generateIndexSections(writer, c, generateInternalNodes); + } + } + else + generateIndexSections(writer, child, generateInternalNodes); + } + } + 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; + + 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()); + + generateIndexSections(writer, qdb_->treeRoot(), generateInternalNodes); + + writer.writeEndElement(); // INDEX + writer.writeEndElement(); // QDOCINDEX + writer.writeEndDocument(); + file.close(); +} + +QT_END_NAMESPACE |