diff --git a/src/tools/qdoc/ditaxmlgenerator.cpp b/src/tools/qdoc/ditaxmlgenerator.cpp index 10caa436b7d..bfd834845ae 100644 --- a/src/tools/qdoc/ditaxmlgenerator.cpp +++ b/src/tools/qdoc/ditaxmlgenerator.cpp @@ -532,6 +532,7 @@ void DitaXmlGenerator::initializeGenerator(const Config &config) projectDescription = project + " Reference Documentation"; projectUrl = config.getString(CONFIG_URL); + tagFile_ = config.getString(CONFIG_TAGFILE); outputEncoding = config.getString(CONFIG_OUTPUTENCODING); if (outputEncoding.isEmpty()) @@ -676,8 +677,15 @@ void DitaXmlGenerator::generateTree() generateCollisionPages(); QString fileBase = project.toLower().simplified().replace(QLatin1Char(' '), QLatin1Char('-')); - generateIndex(fileBase, projectUrl, projectDescription); + qdb_->generateIndex(outputDir() + QLatin1Char('/') + fileBase + ".index", + projectUrl, + projectDescription, + this); writeDitaMap(); + /* + Generate the XML tag file, if it was requested. + */ + qdb_->generateTagFile(tagFile_, this); } static int countTableColumns(const Atom* t) @@ -1755,7 +1763,7 @@ int DitaXmlGenerator::generateAtom(const Atom *atom, columnText = pieces.at(0); pieces.pop_front(); QString path = pieces.join(' ').trimmed(); - node = qdb_->findNodeForTarget(path, relative, atom); + node = qdb_->findNodeForTarget(path, relative); if (!node) relative->doc().location().warning(tr("Cannot link to '%1'").arg(path)); } @@ -3940,13 +3948,6 @@ QString DitaXmlGenerator::getLink(const Atom* atom, const Node* relative, const return link; } -void DitaXmlGenerator::generateIndex(const QString& fileBase, - const QString& url, - const QString& title) -{ - qdb_->generateIndex(outputDir() + QLatin1Char('/') + fileBase + ".index", url, title, this); -} - void DitaXmlGenerator::generateStatus(const Node* node, CodeMarker* marker) { Text text; diff --git a/src/tools/qdoc/ditaxmlgenerator.h b/src/tools/qdoc/ditaxmlgenerator.h index 78548234a8d..831bbdeca1b 100644 --- a/src/tools/qdoc/ditaxmlgenerator.h +++ b/src/tools/qdoc/ditaxmlgenerator.h @@ -436,9 +436,6 @@ private: static int hOffset(const Node *node); static bool isThreeColumnEnumValueTable(const Atom *atom); QString getLink(const Atom *atom, const Node *relative, const Node **node); - virtual void generateIndex(const QString& fileBase, - const QString& url, - const QString& title); #ifdef GENERATE_MAC_REFS void generateMacRef(const Node* node, CodeMarker* marker); #endif diff --git a/src/tools/qdoc/generator.cpp b/src/tools/qdoc/generator.cpp index 5f2fae6808a..75d66299902 100644 --- a/src/tools/qdoc/generator.cpp +++ b/src/tools/qdoc/generator.cpp @@ -1456,9 +1456,11 @@ void Generator::initialize(const Config &config) baseDir_ = config.getString(CONFIG_BASEDIR); if (!baseDir_.isEmpty()) config.location().warning(tr("\"basedir\" specified in config file. " - "All output will be in module directories of the output directory")); + "All output will be in module directories " + "of the output directory")); if (outDir_.isEmpty()) - config.lastLocation().fatal(tr("No output directory specified in configuration file or on the command line")); + config.lastLocation().fatal(tr("No output directory specified in " + "configuration file or on the command line")); QDir dirInfo; if (dirInfo.exists(outDir_)) { @@ -1471,17 +1473,13 @@ void Generator::initialize(const Config &config) } if (!dirInfo.mkdir(outDir_ + "/images")) - config.lastLocation().fatal(tr("Cannot create output directory '%1'") - .arg(outDir_ + "/images")); + config.lastLocation().fatal(tr("Cannot create output directory '%1'").arg(outDir_ + "/images")); if (!dirInfo.mkdir(outDir_ + "/images/used-in-examples")) - config.lastLocation().fatal(tr("Cannot create output directory '%1'") - .arg(outDir_ + "/images/used-in-examples")); + config.lastLocation().fatal(tr("Cannot create output directory '%1'").arg(outDir_ + "/images/used-in-examples")); if (!dirInfo.mkdir(outDir_ + "/scripts")) - config.lastLocation().fatal(tr("Cannot create output directory '%1'") - .arg(outDir_ + "/scripts")); + config.lastLocation().fatal(tr("Cannot create output directory '%1'").arg(outDir_ + "/scripts")); if (!dirInfo.mkdir(outDir_ + "/style")) - config.lastLocation().fatal(tr("Cannot create output directory '%1'") - .arg(outDir_ + "/style")); + config.lastLocation().fatal(tr("Cannot create output directory '%1'").arg(outDir_ + "/style")); } imageFiles = config.getCleanPathList(CONFIG_IMAGES); @@ -1491,16 +1489,13 @@ void Generator::initialize(const Config &config) styleFiles = config.getCleanPathList(CONFIG_STYLES); styleDirs = config.getCleanPathList(CONFIG_STYLEDIRS); exampleDirs = config.getCleanPathList(CONFIG_EXAMPLEDIRS); - exampleImgExts = config.getStringList(CONFIG_EXAMPLES + Config::dot + - CONFIG_IMAGEEXTENSIONS); + exampleImgExts = config.getStringList(CONFIG_EXAMPLES + Config::dot + CONFIG_IMAGEEXTENSIONS); - QString imagesDotFileExtensions = - CONFIG_IMAGES + Config::dot + CONFIG_FILEEXTENSIONS; + QString imagesDotFileExtensions = CONFIG_IMAGES + Config::dot + CONFIG_FILEEXTENSIONS; QSet formats = config.subVars(imagesDotFileExtensions); QSet::ConstIterator f = formats.constBegin(); while (f != formats.constEnd()) { - imgFileExts[*f] = config.getStringList(imagesDotFileExtensions + - Config::dot + *f); + imgFileExts[*f] = config.getStringList(imagesDotFileExtensions + Config::dot + *f); ++f; } @@ -1509,8 +1504,7 @@ void Generator::initialize(const Config &config) if (outputFormats.contains((*g)->format())) { currentGenerator_ = (*g); (*g)->initializeGenerator(config); - QStringList extraImages = - config.getCleanPathList(CONFIG_EXTRAIMAGES+Config::dot+(*g)->format()); + QStringList extraImages = config.getCleanPathList(CONFIG_EXTRAIMAGES+Config::dot+(*g)->format()); QStringList::ConstIterator e = extraImages.constBegin(); while (e != extraImages.constEnd()) { QString userFriendlyFilePath; @@ -1530,8 +1524,7 @@ void Generator::initialize(const Config &config) } // Documentation template handling - QString templateDir = config.getString( - (*g)->format() + Config::dot + CONFIG_TEMPLATEDIR); + QString templateDir = config.getString((*g)->format() + Config::dot + CONFIG_TEMPLATEDIR); QStringList searchDirs; if (!templateDir.isEmpty()) { @@ -1543,8 +1536,7 @@ void Generator::initialize(const Config &config) if (!searchDirs.isEmpty()) { QStringList noExts; - QStringList scripts = - config.getCleanPathList((*g)->format()+Config::dot+CONFIG_SCRIPTS); + QStringList scripts = config.getCleanPathList((*g)->format()+Config::dot+CONFIG_SCRIPTS); e = scripts.constBegin(); while (e != scripts.constEnd()) { QString userFriendlyFilePath; @@ -1563,8 +1555,7 @@ void Generator::initialize(const Config &config) ++e; } - QStringList styles = - config.getCleanPathList((*g)->format()+Config::dot+CONFIG_STYLESHEETS); + QStringList styles = config.getCleanPathList((*g)->format()+Config::dot+CONFIG_STYLESHEETS); e = styles.constBegin(); while (e != styles.constEnd()) { QString userFriendlyFilePath; @@ -1592,16 +1583,13 @@ void Generator::initialize(const Config &config) QSet::ConstIterator n = formattingNames.constBegin(); while (n != formattingNames.constEnd()) { QString formattingDotName = CONFIG_FORMATTING + Config::dot + *n; - QSet formats = config.subVars(formattingDotName); QSet::ConstIterator f = formats.constBegin(); while (f != formats.constEnd()) { - QString def = config.getString(formattingDotName + - Config::dot + *f); + QString def = config.getString(formattingDotName + Config::dot + *f); if (!def.isEmpty()) { int numParams = Config::numParams(def); int numOccs = def.count("\1"); - if (numParams != 1) { config.lastLocation().warning(tr("Formatting '%1' must " "have exactly one " @@ -1631,9 +1619,9 @@ void Generator::initialize(const Config &config) QStringList prefixes = config.getStringList(CONFIG_OUTPUTPREFIXES); if (!prefixes.isEmpty()) { foreach (const QString &prefix, prefixes) - outputPrefixes[prefix] = config.getString( - CONFIG_OUTPUTPREFIXES + Config::dot + prefix); - } else + outputPrefixes[prefix] = config.getString(CONFIG_OUTPUTPREFIXES + Config::dot + prefix); + } + else outputPrefixes[QLatin1String("QML")] = QLatin1String("qml-"); } diff --git a/src/tools/qdoc/generator.h b/src/tools/qdoc/generator.h index 55a2520753e..474fb4367c6 100644 --- a/src/tools/qdoc/generator.h +++ b/src/tools/qdoc/generator.h @@ -166,6 +166,7 @@ protected: QString naturalLanguage; QTextCodec* outputCodec; QString outputEncoding; + QString tagFile_; QStack outStreamStack; private: diff --git a/src/tools/qdoc/htmlgenerator.cpp b/src/tools/qdoc/htmlgenerator.cpp index 30d979a7430..8fe941bda78 100644 --- a/src/tools/qdoc/htmlgenerator.cpp +++ b/src/tools/qdoc/htmlgenerator.cpp @@ -176,6 +176,7 @@ void HtmlGenerator::initializeGenerator(const Config &config) projectDescription = project + " Reference Documentation"; projectUrl = config.getString(CONFIG_URL); + tagFile_ = config.getString(CONFIG_TAGFILE); outputEncoding = config.getString(CONFIG_OUTPUTENCODING); if (outputEncoding.isEmpty()) @@ -259,10 +260,17 @@ void HtmlGenerator::generateTree() generateCollisionPages(); QString fileBase = project.toLower().simplified().replace(QLatin1Char(' '), QLatin1Char('-')); - generateIndex(fileBase, projectUrl, projectDescription); + qdb_->generateIndex(outputDir() + QLatin1Char('/') + fileBase + ".index", + projectUrl, + projectDescription, + this); helpProjectWriter->generate(); generateManifestFiles(); + /* + Generate the XML tag file, if it was requested. + */ + qdb_->generateTagFile(tagFile_, this); } /*! @@ -3636,13 +3644,6 @@ QString HtmlGenerator::getLink(const Atom *atom, const Node *relative, const Nod return link; } -void HtmlGenerator::generateIndex(const QString &fileBase, - const QString &url, - const QString &title) -{ - qdb_->generateIndex(outputDir() + QLatin1Char('/') + fileBase + ".index", url, title, this); -} - void HtmlGenerator::generateStatus(const Node *node, CodeMarker *marker) { Text text; diff --git a/src/tools/qdoc/htmlgenerator.h b/src/tools/qdoc/htmlgenerator.h index 74f013f92d2..be9fc709e10 100644 --- a/src/tools/qdoc/htmlgenerator.h +++ b/src/tools/qdoc/htmlgenerator.h @@ -203,9 +203,6 @@ private: static int hOffset(const Node *node); static bool isThreeColumnEnumValueTable(const Atom *atom); QString getLink(const Atom *atom, const Node *relative, const Node** node); - virtual void generateIndex(const QString &fileBase, - const QString &url, - const QString &title); #ifdef GENERATE_MAC_REFS void generateMacRef(const Node *node, CodeMarker *marker); #endif diff --git a/src/tools/qdoc/main.cpp b/src/tools/qdoc/main.cpp index 592aa476e4d..e9ab11d4d67 100644 --- a/src/tools/qdoc/main.cpp +++ b/src/tools/qdoc/main.cpp @@ -435,10 +435,6 @@ static void processQdocconfFile(const QString &fileName) ++of; } - /* - Generate the XML tag file, if it was requested. - */ - qdb->generateTagFile(config.getString(CONFIG_TAGFILE)); //Generator::writeOutFileNames(); diff --git a/src/tools/qdoc/qdoc.pro b/src/tools/qdoc/qdoc.pro index 9ca089cb1c8..8d3b4e49aec 100644 --- a/src/tools/qdoc/qdoc.pro +++ b/src/tools/qdoc/qdoc.pro @@ -43,6 +43,8 @@ HEADERS += atom.h \ plaincodemarker.h \ puredocparser.h \ qdocdatabase.h \ + qdoctagfiles.h \ + qdocindexfiles.h \ quoter.h \ separator.h \ text.h \ @@ -69,6 +71,8 @@ SOURCES += atom.cpp \ plaincodemarker.cpp \ puredocparser.cpp \ qdocdatabase.cpp \ + qdoctagfiles.cpp \ + qdocindexfiles.cpp \ quoter.cpp \ separator.cpp \ text.cpp \ diff --git a/src/tools/qdoc/qdocdatabase.cpp b/src/tools/qdoc/qdocdatabase.cpp index 8248d97754c..9d3a8bee5c3 100644 --- a/src/tools/qdoc/qdocdatabase.cpp +++ b/src/tools/qdoc/qdocdatabase.cpp @@ -39,8 +39,11 @@ ** ****************************************************************************/ +#include "atom.h" #include "tree.h" #include "qdocdatabase.h" +#include "qdoctagfiles.h" +#include "qdocindexfiles.h" #include QT_BEGIN_NAMESPACE @@ -555,7 +558,7 @@ const NodeMultiMap& QDocDatabase::getSinceMap(const QString& key) const */ void QDocDatabase::resolveIssues() { tree_->resolveGroups(); - tree_->resolveTargets(tree_->root()); + resolveTargets(treeRoot()); tree_->resolveCppToQmlLinks(); } @@ -589,7 +592,7 @@ const Node* QDocDatabase::resolveTarget(const QString& target, QString funcName = target; funcName.chop(2); QStringList path = funcName.split("::"); - const FunctionNode* fn = tree_->findFunctionNode(path, relative, Tree::SearchBaseClasses); + const FunctionNode* fn = tree_->findFunctionNode(path, relative, SearchBaseClasses); if (fn) { /* Why is this case not accepted? @@ -604,19 +607,17 @@ const Node* QDocDatabase::resolveTarget(const QString& target, } else { QStringList path = target.split("::"); - int flags = Tree::SearchBaseClasses | Tree::SearchEnumValues | Tree::NonFunction; + int flags = SearchBaseClasses | SearchEnumValues | NonFunction; node = tree_->findNode(path, relative, flags, self); } return node; } /*! - zzz - Is the atom necessary? + Finds the node that will generate the documentation that + contains the \a target and returns a pointer to it. */ -const Node* QDocDatabase::findNodeForTarget(const QString& target, - const Node* relative, - const Atom* atom) +const Node* QDocDatabase::findNodeForTarget(const QString& target, const Node* relative) { const Node* node = 0; if (target.isEmpty()) @@ -626,13 +627,244 @@ const Node* QDocDatabase::findNodeForTarget(const QString& target, else { node = resolveTarget(target, relative); if (!node) - node = tree_->findDocNodeByTitle(target, relative); - if (!node && atom) { - qDebug() << "USING ATOM!"; - node = tree_->findUnambiguousTarget(target, *const_cast(&atom), relative); - } + node = findDocNodeByTitle(target, relative); } return node; } +/*! + Inserts a new target into the target table with the specified + \a name, \a node, and \a priority. + */ +void QDocDatabase::insertTarget(const QString& name, Node* node, int priority) +{ + Target target; + target.node = node; + target.priority = priority; + target.atom = new Atom(Atom::Target, name); + targetHash_.insert(name, target); +} + +static const int NumSuffixes = 3; +static const char* const suffixes[NumSuffixes] = { "", "s", "es" }; + +/*! + 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* +QDocDatabase::findUnambiguousTarget(const QString& target, Atom *&atom, const Node* relative) const +{ + Target bestTarget = {0, 0, INT_MAX}; + int numBestTargets = 0; + QList bestTargetList; + + for (int pass = 0; pass < NumSuffixes; ++pass) { + TargetHash::const_iterator i = targetHash_.constFind(Doc::canonicalTitle(target + suffixes[pass])); + if (i != 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 != 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; iqmlModuleIdentifier() == n->qmlModuleIdentifier()) { + atom = bestTargetList.at(i).atom; + return n; + } + } + } + } + } + } + return 0; +} + +/*! + This function searches for a node with the specified \a title. + If \a relative node is provided, it is used to disambiguate if + it has a QML module identifier. + */ +const DocNode* QDocDatabase::findDocNodeByTitle(const QString& title, const Node* relative) const +{ + for (int pass = 0; pass < NumSuffixes; ++pass) { + DocNodeHash::const_iterator i = docNodesByTitle_.constFind(Doc::canonicalTitle(title + suffixes[pass])); + if (i != docNodesByTitle_.constEnd()) { + if (relative && !relative->qmlModuleIdentifier().isEmpty()) { + const DocNode* dn = i.value(); + InnerNode* parent = dn->parent(); + if (parent && parent->type() == Node::Document && parent->subType() == Node::Collision) { + const NodeList& nl = parent->childNodes(); + NodeList::ConstIterator it = nl.constBegin(); + while (it != nl.constEnd()) { + 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. + */ + dn = static_cast(*it); + return dn; + } + ++it; + } + } + } + /* + Reporting all these duplicate section titles is probably + overkill. We should report the duplicate file and let + that suffice. + */ + DocNodeHash::const_iterator j = i; + ++j; + if (j != docNodesByTitle_.constEnd() && j.key() == i.key()) { + QList internalLocations; + while (j != docNodesByTitle_.constEnd()) { + if (j.key() == i.key() && j.value()->url().isEmpty()) + internalLocations.append(j.value()->location()); + ++j; + } + if (internalLocations.size() > 0) { + i.value()->location().warning(tr("This page exists in more than one file: \"%1\"").arg(title)); + foreach (const Location &location, internalLocations) + location.warning(tr("[It also exists here]")); + } + } + return i.value(); + } + } + 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* QDocDatabase::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 = targetHash_.constFind(key); + + if (i != targetHash_.constEnd()) { + do { + if (i.value().node == node) + return i.value().atom; + ++i; + } while (i != targetHash_.constEnd() && i.key() == key); + } + } + return 0; +} + +/*! + */ +void QDocDatabase::resolveTargets(InnerNode* root) +{ + // need recursion + + foreach (Node* child, root->childNodes()) { + if (child->type() == Node::Document) { + DocNode* node = static_cast(child); + if (!node->title().isEmpty()) + docNodesByTitle_.insert(Doc::canonicalTitle(node->title()), node); + if (node->subType() == Node::Collision) { + resolveTargets(node); + } + } + + if (child->doc().hasTableOfContents()) { + const QList& 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()) + targetHash_.insert(Doc::canonicalTitle(title), target); + } + } + if (child->doc().hasKeywords()) { + const QList& 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); + targetHash_.insert(Doc::canonicalTitle(target.atom->string()), target); + } + } + if (child->doc().hasTargets()) { + const QList& 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); + targetHash_.insert(Doc::canonicalTitle(target.atom->string()), target); + } + } + } +} + +/*! + Generates a tag file and writes it to \a name. + */ +void QDocDatabase::generateTagFile(const QString& name, Generator* g) +{ + if (!name.isEmpty()) { + QDocTagFiles::qdocTagFiles()->generateTagFile(name, g); + QDocTagFiles::destroyQDocTagFiles(); + } +} + +/*! + Reads and parses the qdoc index files listed in \a indexFiles. + */ +void QDocDatabase::readIndexes(const QStringList& indexFiles) +{ + QDocIndexFiles::qdocIndexFiles()->readIndexes(indexFiles); + QDocIndexFiles::destroyQDocIndexFiles(); +} + +/*! + Generates a qdoc index file and write it to \a fileName. The + index file is generated with the parameters \a url, \a title, + \a g, and \a generateInternalNodes. + */ +void QDocDatabase::generateIndex(const QString& fileName, + const QString& url, + const QString& title, + Generator* g, + bool generateInternalNodes) +{ + QDocIndexFiles::qdocIndexFiles()->generateIndex(fileName, url, title, g, generateInternalNodes); + QDocIndexFiles::destroyQDocIndexFiles(); +} + QT_END_NAMESPACE diff --git a/src/tools/qdoc/qdocdatabase.h b/src/tools/qdoc/qdocdatabase.h index 6eb36a836e7..a9999549a3b 100644 --- a/src/tools/qdoc/qdocdatabase.h +++ b/src/tools/qdoc/qdocdatabase.h @@ -54,10 +54,29 @@ typedef QMap NodeMapMap; typedef QMap NodeMultiMapMap; typedef QMultiMap QDocMultiMap; typedef QMap TextToNodeMap; +typedef QMultiHash DocNodeHash; + +class Atom; +class Generator; + +enum FindFlag { + SearchBaseClasses = 0x1, + SearchEnumValues = 0x2, + NonFunction = 0x4 +}; class QDocDatabase { - public: + + struct Target + { + Node* node; + Atom* atom; + int priority; + }; + typedef QMultiHash TargetHash; + + public: static QDocDatabase* qdocDB(); static void destroyQdocDB(); ~QDocDatabase(); @@ -97,19 +116,16 @@ class QDocDatabase const NodeMultiMap& getSinceMap(const QString& key) const; const Node* resolveTarget(const QString& target, const Node* relative, const Node* self=0); - const Node* findNodeForTarget(const QString& target, const Node* relative, const Atom* atom=0); + const Node* findNodeForTarget(const QString& target, const Node* relative); + void insertTarget(const QString& name, Node* node, int priority); /* convenience functions Many of these will be either eliminated or replaced. */ Tree* tree() { return tree_; } - QString version() const { return tree_->version(); } NamespaceNode* treeRoot() { return tree_->root(); } - void setVersion(const QString& version) { tree_->setVersion(version); } void resolveInheritance() { tree_->resolveInheritance(); } - void readIndexes(const QStringList& indexFiles) { tree_->readIndexes(indexFiles); } void resolveIssues(); - void generateTagFile(const QString& name) { if (!name.isEmpty()) tree_->generateTagFile(name); } void addToGroup(Node* node, const QString& group) { tree_->addToGroup(node, group); } void addToPublicGroup(Node* node, const QString& group) { tree_->addToPublicGroup(node, group); } void fixInheritance() { tree_->fixInheritance(); } @@ -124,22 +140,11 @@ class QDocDatabase return tree_->findCollisionNode(name); } - const DocNode* findDocNodeByTitle(const QString& title, const Node* relative=0) const { - return tree_->findDocNodeByTitle(title, relative); - } - const Node* findUnambiguousTarget(const QString& target, Atom* &atom, const Node* relative) const { - return tree_->findUnambiguousTarget(target, atom, relative); - } - Atom* findTarget(const QString& target, const Node* node) const { - return tree_->findTarget(target, node); - } - void generateIndex(const QString& fileName, - const QString& url, - const QString& title, - Generator* g, - bool generateInternalNodes = false) { - tree_->generateIndex(fileName, url, title, g, generateInternalNodes); - } + const DocNode* findDocNodeByTitle(const QString& title, const Node* relative = 0) const; + const Node *findUnambiguousTarget(const QString &target, Atom *&atom, const Node* relative) const; + Atom *findTarget(const QString &target, const Node *node) const; + void resolveTargets(InnerNode* root); + FunctionNode* findFunctionNode(const QStringList& parentPath, const FunctionNode* clone) { return tree_->findFunctionNode(parentPath, clone); } @@ -162,10 +167,29 @@ class QDocDatabase tree_->addPropertyFunction(property, funcName, funcRole); } + void setVersion(const QString& v) { version_ = v; } + QString version() const { return version_; } + + void generateTagFile(const QString& name, Generator* g); + void readIndexes(const QStringList& indexFiles); + void generateIndex(const QString& fileName, + const QString& url, + const QString& title, + Generator* g, + bool generateInternalNodes = false); + /* debugging functions */ void printModules() const; void printQmlModules() const; + private: + friend class QDocIndexFiles; + friend class QDocTagFiles; + + const Node* findNode(const QStringList& path, const Node* relative, int findFlags) { + return tree_->findNode(path, relative, findFlags); + } + private: QDocDatabase(); QDocDatabase(QDocDatabase const& ) { }; // copy constructor is private @@ -173,6 +197,7 @@ class QDocDatabase private: static QDocDatabase* qdocDB_; + QString version_; QDocMultiMap masterMap_; Tree* tree_; DocNodeMap modules_; @@ -191,6 +216,8 @@ class QDocDatabase NodeMultiMapMap newSinceMaps_; NodeMapMap funcIndex_; TextToNodeMap legaleseTexts_; + TargetHash targetHash_; + DocNodeHash docNodesByTitle_; }; QT_END_NAMESPACE diff --git a/src/tools/qdoc/qdocindexfiles.cpp b/src/tools/qdoc/qdocindexfiles.cpp new file mode 100644 index 00000000000..d68fe0e6fa1 --- /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 +//include + +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
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(static_cast(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, + 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 emptySet; + Doc doc(location, location, " ", emptySet); // placeholder + section->setDoc(doc); + section->setIndexNodeFlag(); + + if (section->isInnerNode()) { + InnerNode* inner = static_cast(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 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(n)); + } + } + } + + QPair relatedPair; + foreach (relatedPair, relatedList_) { + Node* n = qdb_->treeRoot()->findChildNodeByNameAndType(relatedPair.second, Node::Class); + if (n) + relatedPair.first->setRelates(static_cast(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(node); + QList bases = classNode->baseClasses(); + QSet 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(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(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(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(node); + writer.writeAttribute("type", propertyNode->dataType()); + foreach (const Node* fnNode, propertyNode->getters()) { + if (fnNode) { + const FunctionNode* functionNode = static_cast(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(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(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(fnNode); + writer.writeStartElement("notifier"); + writer.writeAttribute("name", functionNode->name()); + writer.writeEndElement(); // notifier + } + } + } + break; + case Node::Variable: + { + const VariableNode* variableNode = static_cast(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(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(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(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(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(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(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(n1); + const FunctionNode* f2 = static_cast(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(n1); + const DocNode* f2 = static_cast(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(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(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(""); + + 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 diff --git a/src/tools/qdoc/qdocindexfiles.h b/src/tools/qdoc/qdocindexfiles.h new file mode 100644 index 00000000000..c53e62dddfe --- /dev/null +++ b/src/tools/qdoc/qdocindexfiles.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QDOCINDEXFILES_H +#define QDOCINDEXFILES_H + +#include "node.h" + +QT_BEGIN_NAMESPACE + +class Atom; +class Generator; +class QStringList; +class QDocDatabase; +class QDomElement; +class QXmlStreamWriter; + +class QDocIndexFiles +{ + friend class QDocDatabase; + + private: + static QDocIndexFiles* qdocIndexFiles(); + static void destroyQDocIndexFiles(); + + QDocIndexFiles(); + ~QDocIndexFiles(); + + void readIndexes(const QStringList& indexFiles); + void generateIndex(const QString& fileName, + const QString& url, + const QString& title, + Generator* g, + bool generateInternalNodes = false); + + void readIndexFile(const QString& path); + void readIndexSection(const QDomElement& element, InnerNode* parent, const QString& indexUrl); + void resolveIndex(); + bool generateIndexSection(QXmlStreamWriter& writer, Node* node, bool generateInternalNodes = false); + void generateIndexSections(QXmlStreamWriter& writer, Node* node, bool generateInternalNodes = false); + + private: + static QDocIndexFiles* qdocIndexFiles_; + QDocDatabase* qdb_; + Generator* gen_; + QList > basesList_; + QList > relatedList_; + +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/tools/qdoc/qdoctagfiles.cpp b/src/tools/qdoc/qdoctagfiles.cpp new file mode 100644 index 00000000000..e3e3bfdb6d5 --- /dev/null +++ b/src/tools/qdoc/qdoctagfiles.cpp @@ -0,0 +1,404 @@ +/**************************************************************************** +** +** 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 "node.h" +#include "qdoctagfiles.h" +#include "qdocdatabase.h" + +#include "qdom.h" +#include "atom.h" +#include "doc.h" +#include "htmlgenerator.h" +#include "location.h" +#include "node.h" +#include "text.h" +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \class QDocTagFiles + + This class handles the generation of the qdoc tag file. + */ + +QDocTagFiles* QDocTagFiles::qdocTagFiles_ = NULL; + +/*! + Constructs the singleton. \a qdb is the pointer to the + qdoc database that is used when reading and writing the + index files. + */ +QDocTagFiles::QDocTagFiles() +{ + qdb_ = QDocDatabase::qdocDB(); +} + +/*! + Destroys the singleton QDocTagFiles. + */ +QDocTagFiles::~QDocTagFiles() +{ + qdb_ = 0; +} + +/*! + Creates the singleton. Allows only one instance of the class + to be created. Returns a pointer to the singleton. + */ +QDocTagFiles* QDocTagFiles::qdocTagFiles() +{ + if (!qdocTagFiles_) + qdocTagFiles_ = new QDocTagFiles; + return qdocTagFiles_; +} + +/*! + Destroys the singleton. + */ +void QDocTagFiles::destroyQDocTagFiles() +{ + if (qdocTagFiles_) { + delete qdocTagFiles_; + qdocTagFiles_ = 0; + } +} + +/*! + 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 QDocTagFiles::generateTagFileCompounds(QXmlStreamWriter& writer, const InnerNode* inner) +{ + 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: + 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 != qdb_->treeRoot()) + 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", gen_->fullDocumentLocation(node,true)); + + // Classes contain information about their base classes. + const ClassNode* classNode = static_cast(node); + QList 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(node)); + writer.writeEndElement(); + + // Recurse to write all compounds. + generateTagFileCompounds(writer, static_cast(node)); + } + else { + writer.writeTextElement("name", node->fullDocumentName()); + writer.writeTextElement("filename", gen_->fullDocumentLocation(node,true)); + + // Recurse to write all members. + generateTagFileMembers(writer, static_cast(node)); + writer.writeEndElement(); + + // Recurse to write all compounds. + generateTagFileCompounds(writer, static_cast(node)); + } + } +} + +/*! + Writes all the members of the \a inner node with the \a writer. + The node represents a C++ class, namespace, etc. + */ +void QDocTagFiles::generateTagFileMembers(QXmlStreamWriter& writer, const InnerNode* inner) +{ + 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: + 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 != qdb_->treeRoot()) + 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(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 = gen_->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 = 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) { + const TypedefNode* typedefNode = static_cast(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(node); + writer.writeAttribute("type", propertyNode->dataType()); + writer.writeTextElement("name", objName); + QStringList pieces = gen_->fullDocumentLocation(node,true).split(QLatin1Char('#')); + writer.writeTextElement("anchorfile", pieces[0]); + writer.writeTextElement("anchor", pieces[1]); + writer.writeTextElement("arglist", QString()); + } + writer.writeEndElement(); // member + break; + case Node::Enum: + { + const EnumNode* enumNode = static_cast(node); + writer.writeTextElement("name", objName); + QStringList pieces = gen_->fullDocumentLocation(node).split(QLatin1Char('#')); + writer.writeTextElement("anchor", pieces[1]); + writer.writeTextElement("arglist", QString()); + 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", QString()); + writer.writeEndElement(); // member + } + } + break; + case Node::Typedef: + { + const TypedefNode* typedefNode = static_cast(node); + if (typedefNode->associatedEnum()) + writer.writeAttribute("type", typedefNode->associatedEnum()->fullDocumentName()); + else + writer.writeAttribute("type", QString()); + writer.writeTextElement("name", objName); + QStringList pieces = gen_->fullDocumentLocation(node,true).split(QLatin1Char('#')); + writer.writeTextElement("anchorfile", pieces[0]); + writer.writeTextElement("anchor", pieces[1]); + writer.writeTextElement("arglist", QString()); + } + writer.writeEndElement(); // member + break; + + case Node::Variable: + default: + break; + } + } +} + +/*! + Writes a tag file named \a fileName. + */ +void QDocTagFiles::generateTagFile(const QString& fileName, Generator* g) +{ + QFile file(fileName); + if (!file.open(QFile::WriteOnly | QFile::Text)) + return ; + + gen_ = g; + QXmlStreamWriter writer(&file); + writer.setAutoFormatting(true); + writer.writeStartDocument(); + writer.writeStartElement("tagfile"); + generateTagFileCompounds(writer, qdb_->treeRoot()); + writer.writeEndElement(); // tagfile + writer.writeEndDocument(); + file.close(); +} + +QT_END_NAMESPACE diff --git a/src/tools/qdoc/qdoctagfiles.h b/src/tools/qdoc/qdoctagfiles.h new file mode 100644 index 00000000000..c982cd35dbd --- /dev/null +++ b/src/tools/qdoc/qdoctagfiles.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QDOCTAGFILES_H +#define QDOCTAGFILES_H + +#include "qxmlstream.h" + +QT_BEGIN_NAMESPACE + +class InnerNode; +class QDocDatabase; +class Generator; + +class QDocTagFiles +{ + friend class QDocDatabase; + + private: + static QDocTagFiles* qdocTagFiles(); + static void destroyQDocTagFiles(); + + QDocTagFiles(); + ~QDocTagFiles(); + + void generateTagFileCompounds(QXmlStreamWriter& writer, const InnerNode* inner); + void generateTagFileMembers(QXmlStreamWriter& writer, const InnerNode* inner); + void generateTagFile(const QString& fileName, Generator* g); + + private: + static QDocTagFiles* qdocTagFiles_; + QDocDatabase* qdb_; + Generator* gen_; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/tools/qdoc/tree.cpp b/src/tools/qdoc/tree.cpp index d1b70fad9f5..c94179b813f 100644 --- a/src/tools/qdoc/tree.cpp +++ b/src/tools/qdoc/tree.cpp @@ -39,12 +39,6 @@ ** ****************************************************************************/ -/* - tree.cpp -*/ - -#include -#include "atom.h" #include "doc.h" #include "htmlgenerator.h" #include "location.h" @@ -57,49 +51,6 @@ 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 RoleMap; -typedef QMap PropertyMap; -typedef QMultiHash DocNodeHash; -typedef QMultiHash TargetHash; - -class TreePrivate -{ -public: - QMap > unresolvedInheritanceMap; - PropertyMap unresolvedPropertyMap; - NodeMultiMap groupMap; - QMultiMap publicGroupMap; - DocNodeHash docNodesByTitle; - TargetHash targetHash; - QList > basesList; - QList > relatedList; -}; - /*! \class Tree @@ -119,15 +70,13 @@ public: Tree::Tree(QDocDatabase* qdb) : qdb_(qdb), root_(0, QString()) { - priv = new TreePrivate; } /*! - The destructor deletes the internal, private tree. + Destroys the singleton Tree. */ Tree::~Tree() { - delete priv; } // 1 calls 2 @@ -423,143 +372,6 @@ const FunctionNode* Tree::findFunctionNode(const QStringList& parentPath, return 0; return ((InnerNode*)parent)->findFunctionNode(clone); } -//findNode(parameter.leftType().split("::"), 0, SearchBaseClasses|NonFunction); - -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 node is provided, it is used to disambiguate if - it has a QML module identifier. - */ -const DocNode* Tree::findDocNodeByTitle(const QString& title, const Node* relative ) const -{ - for (int pass = 0; pass < NumSuffixes; ++pass) { - DocNodeHash::const_iterator i = priv->docNodesByTitle.constFind(Doc::canonicalTitle(title + suffixes[pass])); - if (i != priv->docNodesByTitle.constEnd()) { - if (relative && !relative->qmlModuleIdentifier().isEmpty()) { - const DocNode* dn = i.value(); - InnerNode* parent = dn->parent(); - if (parent && parent->type() == Node::Document && parent->subType() == Node::Collision) { - const NodeList& nl = parent->childNodes(); - NodeList::ConstIterator it = nl.constBegin(); - while (it != nl.constEnd()) { - 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. - */ - dn = static_cast(*it); - return dn; - } - ++it; - } - } - } - /* - Reporting all these duplicate section titles is probably - overkill. We should report the duplicate file and let - that suffice. - */ - DocNodeHash::const_iterator j = i; - ++j; - if (j != priv->docNodesByTitle.constEnd() && j.key() == i.key()) { - QList internalLocations; - while (j != priv->docNodesByTitle.constEnd()) { - if (j.key() == i.key() && j.value()->url().isEmpty()) - internalLocations.append(j.value()->location()); - ++j; - } - if (internalLocations.size() > 0) { - i.value()->location().warning(tr("This page exists in more than one file: \"%1\"").arg(title)); - foreach (const Location &location, internalLocations) - location.warning(tr("[It also exists 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 bestTargetList; - - if (priv == 0) - return 0; - - for (int pass = 0; pass < NumSuffixes; ++pass) { - TargetHash::const_iterator i = priv->targetHash.constFind(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; iqmlModuleIdentifier() == 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.constFind(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; -} /*! */ @@ -568,7 +380,7 @@ void Tree::addBaseClass(ClassNode* subclass, Node::Access access, const QString& dataTypeWithTemplateArgs, InnerNode* parent) { - priv->unresolvedInheritanceMap[subclass].append( + unresolvedInheritanceMap[subclass].append( InheritanceBound(access, basePath, dataTypeWithTemplateArgs, @@ -582,7 +394,7 @@ void Tree::addPropertyFunction(PropertyNode* property, const QString& funcName, PropertyNode::FunctionRole funcRole) { - priv->unresolvedPropertyMap[property].insert(funcRole, funcName); + unresolvedPropertyMap[property].insert(funcRole, funcName); } /*! @@ -591,7 +403,7 @@ void Tree::addPropertyFunction(PropertyNode* property, */ void Tree::addToGroup(Node* node, const QString& group) { - priv->groupMap.insert(group, node); + groupMap.insert(group, node); } /*! @@ -599,7 +411,7 @@ void Tree::addToGroup(Node* node, const QString& group) */ const NodeMultiMap& Tree::groups() const { - return priv->groupMap; + return groupMap; } /*! @@ -608,7 +420,7 @@ const NodeMultiMap& Tree::groups() const */ void Tree::addToPublicGroup(Node* node, const QString& group) { - priv->publicGroupMap.insert(node->name(), group); + publicGroupMap.insert(node->name(), group); addToGroup(node, group); } @@ -617,7 +429,7 @@ void Tree::addToPublicGroup(Node* node, const QString& group) */ QMultiMap Tree::publicGroups() const { - return priv->publicGroupMap; + return publicGroupMap; } /*! @@ -640,7 +452,7 @@ void Tree::resolveInheritance(NamespaceNode* rootNode) ++c; } if (rootNode == root()) - priv->unresolvedInheritanceMap.clear(); + unresolvedInheritanceMap.clear(); } } @@ -650,8 +462,8 @@ void Tree::resolveProperties() { PropertyMap::ConstIterator propEntry; - propEntry = priv->unresolvedPropertyMap.constBegin(); - while (propEntry != priv->unresolvedPropertyMap.constEnd()) { + propEntry = unresolvedPropertyMap.constBegin(); + while (propEntry != unresolvedPropertyMap.constEnd()) { PropertyNode* property = propEntry.key(); InnerNode* parent = property->parent(); QString getterName = (*propEntry)[PropertyNode::Getter]; @@ -685,8 +497,8 @@ void Tree::resolveProperties() ++propEntry; } - propEntry = priv->unresolvedPropertyMap.constBegin(); - while (propEntry != priv->unresolvedPropertyMap.constEnd()) { + propEntry = unresolvedPropertyMap.constBegin(); + while (propEntry != unresolvedPropertyMap.constEnd()) { PropertyNode* property = propEntry.key(); // redo it to set the property functions if (property->overriddenFrom()) @@ -694,7 +506,7 @@ void Tree::resolveProperties() ++propEntry; } - priv->unresolvedPropertyMap.clear(); + unresolvedPropertyMap.clear(); } /*! @@ -702,7 +514,7 @@ void Tree::resolveProperties() void Tree::resolveInheritance(int pass, ClassNode* classe) { if (pass == 0) { - QList bounds = priv->unresolvedInheritanceMap[classe]; + QList bounds = unresolvedInheritanceMap[classe]; QList::ConstIterator b = bounds.constBegin(); while (b != bounds.constEnd()) { Node* n = findClassNode((*b).basePath); @@ -742,7 +554,7 @@ void Tree::resolveInheritance(int pass, ClassNode* classe) void Tree::resolveGroups() { NodeMultiMap::const_iterator i; - for (i = priv->groupMap.constBegin(); i != priv->groupMap.constEnd(); ++i) { + for (i = groupMap.constBegin(); i != groupMap.constEnd(); ++i) { if (i.value()->access() == Node::Private) continue; @@ -752,60 +564,6 @@ void Tree::resolveGroups() } } -/*! - */ -void Tree::resolveTargets(InnerNode* root) -{ - // need recursion - - foreach (Node* child, root->childNodes()) { - if (child->type() == Node::Document) { - DocNode* node = static_cast(child); - if (!node->title().isEmpty()) - priv->docNodesByTitle.insert(Doc::canonicalTitle(node->title()), node); - if (node->subType() == Node::Collision) { - resolveTargets(node); - } - } - - if (child->doc().hasTableOfContents()) { - const QList& 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& 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& 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 @@ -892,1359 +650,6 @@ NodeList Tree::allBaseClasses(const ClassNode* classe) const 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(); - - // 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); - } - - 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(); - } -} - -/*! - Read a
element from the index file and create the - appropriate node(s). - */ -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( - static_cast(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()) { - priv->relatedList.append( - QPair(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") { - 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()) { - section->setUrl(indexUrl + QLatin1Char('/') + href); - } - - // Create some content for the node. - QSet emptySet; - - Doc doc(location, location, " ", emptySet); // placeholder - section->setDoc(doc); - section->setIndexNodeFlag(); - - if (section->isInnerNode()) { - InnerNode* inner = static_cast(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 pair; - - foreach (pair, priv->basesList) { - foreach (const QString& base, pair.second.split(QLatin1Char(','))) { - Node* n = root()->findChildNodeByNameAndType(base, Node::Class); - if (n) { - pair.first->addBaseClass(Node::Public, static_cast(n)); - } - } - } - - QPair relatedPair; - - foreach (relatedPair, priv->relatedList) { - Node* n = root()->findChildNodeByNameAndType(relatedPair.second, Node::Class); - if (n) - relatedPair.first->setRelates(static_cast(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 Tree::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 != root()) - 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(node); - QList bases = classNode->baseClasses(); - QSet 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(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(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(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(node); - writer.writeAttribute("type", propertyNode->dataType()); - foreach (const Node* fnNode, propertyNode->getters()) { - if (fnNode) { - const FunctionNode* functionNode = static_cast(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(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(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(fnNode); - writer.writeStartElement("notifier"); - writer.writeAttribute("name", functionNode->name()); - writer.writeEndElement(); // notifier - } - } - } - break; - - case Node::Variable: - { - const VariableNode* variableNode = - static_cast(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(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(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(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(this)->findNode(parameter.leftType().split("::"), - 0, SearchBaseClasses|NonFunction); - if (!leftNode || leftNode->type() != Node::Typedef) { - leftNode = const_cast(this)->findNode(parameter.leftType().split("::"), - node->parent(), SearchBaseClasses|NonFunction); - } - if (leftNode && leftNode->type() == Node::Typedef) { - if (leftNode->type() == Node::Typedef) { - const TypedefNode* typedefNode = - static_cast(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(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(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(n1); - const FunctionNode* f2 = static_cast(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(n1); - const DocNode* f2 = static_cast(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, - Node* node, - bool generateInternalNodes) -{ - if (generateIndexSection(writer, node, generateInternalNodes)) { - - if (node->isInnerNode()) { - const InnerNode* inner = static_cast(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(child); - foreach (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, - 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(""); - - 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) -{ - 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: - 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", gen_->fullDocumentLocation(node,true)); - - // Classes contain information about their base classes. - const ClassNode* classNode = static_cast(node); - QList 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(node)); - writer.writeEndElement(); - - // Recurse to write all compounds. - generateTagFileCompounds(writer, static_cast(node)); - } else { - writer.writeTextElement("name", node->fullDocumentName()); - writer.writeTextElement("filename", gen_->fullDocumentLocation(node,true)); - - // Recurse to write all members. - generateTagFileMembers(writer, static_cast(node)); - writer.writeEndElement(); - - // Recurse to write all compounds. - generateTagFileCompounds(writer, static_cast(node)); - } - } -} - -/*! - */ -void Tree::generateTagFileMembers(QXmlStreamWriter& writer, const InnerNode* inner) -{ - 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: - 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(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 = gen_->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(this)->findNode(parameter.leftType().split("::"), - 0, SearchBaseClasses|NonFunction); - if (!leftNode || leftNode->type() != Node::Typedef) { - leftNode = const_cast(this)->findNode(parameter.leftType().split("::"), - node->parent(), SearchBaseClasses|NonFunction); - } - if (leftNode && leftNode->type() == Node::Typedef) { - const TypedefNode* typedefNode = static_cast(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(node); - writer.writeAttribute("type", propertyNode->dataType()); - writer.writeTextElement("name", objName); - QStringList pieces = gen_->fullDocumentLocation(node,true).split(QLatin1Char('#')); - writer.writeTextElement("anchorfile", pieces[0]); - writer.writeTextElement("anchor", pieces[1]); - writer.writeTextElement("arglist", QString()); - } - writer.writeEndElement(); // member - break; - - case Node::Enum: - { - const EnumNode* enumNode = static_cast(node); - writer.writeTextElement("name", objName); - QStringList pieces = gen_->fullDocumentLocation(node).split(QLatin1Char('#')); - writer.writeTextElement("anchor", pieces[1]); - writer.writeTextElement("arglist", QString()); - 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", QString()); - writer.writeEndElement(); // member - } - } - break; - - case Node::Typedef: - { - const TypedefNode* typedefNode = static_cast(node); - if (typedefNode->associatedEnum()) - writer.writeAttribute("type", typedefNode->associatedEnum()->fullDocumentName()); - else - writer.writeAttribute("type", QString()); - writer.writeTextElement("name", objName); - QStringList pieces = gen_->fullDocumentLocation(node,true).split(QLatin1Char('#')); - writer.writeTextElement("anchorfile", pieces[0]); - writer.writeTextElement("anchor", pieces[1]); - writer.writeTextElement("arglist", QString()); - } - writer.writeEndElement(); // member - break; - - case Node::Variable: - default: - break; - } - } -} - -/*! - Writes a tag file named \a fileName. - */ -void Tree::generateTagFile(const QString& fileName) -{ - 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) -{ - DocNode* docNode = new DocNode(root(), url, Node::ExternalPage, Node::ArticlePage); - docNode->setAccess(Node::Public); - - // Create some content for the node. - QSet emptySet; - Location location(relative->doc().location()); - Doc doc(location, location, " ", emptySet); // placeholder - docNode->setDoc(doc); -} - /*! Find the node with the specified \a path name that is of the specified \a type and \a subtype. Begin the search at diff --git a/src/tools/qdoc/tree.h b/src/tools/qdoc/tree.h index df7fa332d44..2066def091c 100644 --- a/src/tools/qdoc/tree.h +++ b/src/tools/qdoc/tree.h @@ -47,14 +47,10 @@ #define TREE_H #include "node.h" -#include -#include QT_BEGIN_NAMESPACE -class Generator; class QStringList; -class TreePrivate; class QDocDatabase; class Tree @@ -62,9 +58,25 @@ class Tree private: friend class QDocDatabase; - enum FindFlag { SearchBaseClasses = 0x1, - SearchEnumValues = 0x2, - NonFunction = 0x4 }; + typedef QMap RoleMap; + typedef QMap PropertyMap; + + 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) { } + }; Tree(QDocDatabase* qdb); ~Tree(); @@ -125,12 +137,9 @@ class Tree void resolveInheritance(NamespaceNode *rootNode = 0); void resolveProperties(); void resolveGroups(); - void resolveTargets(InnerNode* root); void resolveCppToQmlLinks(); void fixInheritance(NamespaceNode *rootNode = 0); - void setVersion(const QString &version) { vers = version; } NamespaceNode *root() { return &root_; } - QString version() const { return vers; } const FunctionNode *findFunctionNode(const QStringList &path, const Node *relative = 0, @@ -139,42 +148,21 @@ class Tree const FunctionNode *clone, const Node *relative = 0, int findFlags = 0) const; - const DocNode *findDocNodeByTitle(const QString &title, const Node* relative = 0) const; - const Node *findUnambiguousTarget(const QString &target, Atom *&atom, const Node* relative) const; - Atom *findTarget(const QString &target, const Node *node) const; const NamespaceNode *root() const { return &root_; } - void readIndexes(const QStringList &indexFiles); - bool generateIndexSection(QXmlStreamWriter& writer, Node* node, bool generateInternalNodes = false); - void generateIndexSections(QXmlStreamWriter& writer, Node* node, bool generateInternalNodes = false); - void generateIndex(const QString &fileName, - const QString &url, - const QString &title, - Generator* g, - bool generateInternalNodes = false); - void generateTagFileCompounds(QXmlStreamWriter &writer, - const InnerNode *inner); - void generateTagFileMembers(QXmlStreamWriter &writer, - const InnerNode *inner); - void generateTagFile(const QString &fileName); - void addExternalLink(const QString &url, const Node *relative); void resolveInheritance(int pass, ClassNode *classe); FunctionNode *findVirtualFunctionInBaseClasses(ClassNode *classe, FunctionNode *clone); void fixPropertyUsingBaseClasses(ClassNode *classe, PropertyNode *property); NodeList allBaseClasses(const ClassNode *classe) const; - void readIndexFile(const QString &path); - void readIndexSection(const QDomElement &element, InnerNode *parent, - const QString &indexUrl); - QString readIndexText(const QDomElement &element); - void resolveIndex(); private: QDocDatabase* qdb_; NamespaceNode root_; - QString vers; - Generator* gen_; - TreePrivate *priv; + QMap > unresolvedInheritanceMap; + PropertyMap unresolvedPropertyMap; + NodeMultiMap groupMap; + QMultiMap publicGroupMap; }; QT_END_NAMESPACE