From 5fecc6512f0e869713658502674665f9077cc340 Mon Sep 17 00:00:00 2001 From: Martin Smith Date: Fri, 31 Jan 2014 11:56:10 +0100 Subject: qdoc: Teach qdoc to use multiple trees (part 2) qdoc now knows how to search the forrest of node trees in an optimal order. But there remain some problems with specific searches that cross module boundaries. These include group membership and C++ and QML module membership, as well ass C++ base class resolution. Part 3 will be concerned with fixing these remaining bugs. With this update, qdoc now takes less time to generate the docs for Qt 5. Testing indicates that qdoc run time has dropped from about 14 minutes to about 7.5 minutes on an iMac. Task-number: QTBUG-35377 Change-Id: I6bded6ef54124b4f6e5914cad4548f0b600209b0 Reviewed-by: Martin Smith --- src/tools/qdoc/codemarker.h | 1 - src/tools/qdoc/codeparser.cpp | 14 +- src/tools/qdoc/config.cpp | 2 + src/tools/qdoc/config.h | 4 + src/tools/qdoc/cppcodeparser.cpp | 138 +++--- src/tools/qdoc/ditaxmlgenerator.cpp | 18 +- src/tools/qdoc/ditaxmlgenerator.h | 2 +- src/tools/qdoc/doc.cpp | 64 +-- src/tools/qdoc/generator.cpp | 16 +- src/tools/qdoc/generator.h | 8 +- src/tools/qdoc/helpprojectwriter.cpp | 2 +- src/tools/qdoc/htmlgenerator.cpp | 53 ++- src/tools/qdoc/htmlgenerator.h | 2 +- src/tools/qdoc/main.cpp | 40 +- src/tools/qdoc/node.cpp | 156 ++++--- src/tools/qdoc/node.h | 11 +- src/tools/qdoc/puredocparser.cpp | 2 +- src/tools/qdoc/puredocparser.h | 1 - src/tools/qdoc/qdocdatabase.cpp | 872 +++++++++++++++++++++++------------ src/tools/qdoc/qdocdatabase.h | 265 ++++++++--- src/tools/qdoc/qdocindexfiles.cpp | 15 +- src/tools/qdoc/qdoctagfiles.cpp | 6 +- src/tools/qdoc/qmlcodeparser.h | 1 - src/tools/qdoc/qmlvisitor.cpp | 2 +- src/tools/qdoc/tree.cpp | 269 ++++++++++- src/tools/qdoc/tree.h | 37 +- 26 files changed, 1375 insertions(+), 626 deletions(-) diff --git a/src/tools/qdoc/codemarker.h b/src/tools/qdoc/codemarker.h index 02caedf9b8..f7524735b5 100644 --- a/src/tools/qdoc/codemarker.h +++ b/src/tools/qdoc/codemarker.h @@ -54,7 +54,6 @@ QT_BEGIN_NAMESPACE class Config; -class Tree; typedef QMultiMap MemberMap; // the string is the member signature typedef QPair ClassMap; // the node is the QML type diff --git a/src/tools/qdoc/codeparser.cpp b/src/tools/qdoc/codeparser.cpp index a0ea561b28..5811dc8afc 100644 --- a/src/tools/qdoc/codeparser.cpp +++ b/src/tools/qdoc/codeparser.cpp @@ -412,9 +412,9 @@ bool CodeParser::isParsingQdoc() const for an entity that will produce a documentation page will contain an \inmodule command to tell qdoc which module the entity belongs to. - But now that we normally run qdoc on each module in two passes. The - first produces an index file; the second pass generates the docs - after reading all the index files it needs. + But now we normally run qdoc on each module in two passes. The first + produces an index file; the second pass generates the docs after + reading all the index files it needs. This means that all the pages generated during each pass 2 run of qdoc almost certainly belong to a single module, and the name of @@ -431,10 +431,10 @@ bool CodeParser::isParsingQdoc() const void CodeParser::checkModuleInclusion(Node* n) { if (n->moduleName().isEmpty()) { + n->setModuleName(Generator::defaultModuleName()); switch (n->type()) { case Node::Class: if (n->access() != Node::Private && !n->doc().isEmpty()) { - n->setModuleName(Generator::defaultModuleName()); n->doc().location().warning(tr("Class %1 has no \\inmodule command; " "using project name by default: %2") .arg(n->name()).arg(Generator::defaultModuleName())); @@ -442,16 +442,15 @@ void CodeParser::checkModuleInclusion(Node* n) break; case Node::Namespace: if (n->access() != Node::Private && !n->name().isEmpty() && !n->doc().isEmpty()) { - n->setModuleName(Generator::defaultModuleName()); n->doc().location().warning(tr("Namespace %1 has no \\inmodule command; " "using project name by default: %2") .arg(n->name()).arg(Generator::defaultModuleName())); } break; +#if 0 case Node::Document: if (n->access() != Node::Private && !n->doc().isEmpty()) { if (n->subType() == Node::HeaderFile) { - n->setModuleName(Generator::defaultModuleName()); #if 0 n->doc().location().warning(tr("Header file with title \"%1\" has no \\inmodule command; " "using project name by default: %2") @@ -459,7 +458,6 @@ void CodeParser::checkModuleInclusion(Node* n) #endif } else if (n->subType() == Node::Page) { - n->setModuleName(Generator::defaultModuleName()); #if 0 n->doc().location().warning(tr("Page with title \"%1\" has no \\inmodule command; " "using project name by default: %2") @@ -467,7 +465,6 @@ void CodeParser::checkModuleInclusion(Node* n) #endif } else if (n->subType() == Node::Example) { - n->setModuleName(Generator::defaultModuleName()); #if 0 n->doc().location().warning(tr("Example with title \"%1\" has no \\inmodule command; " "using project name by default: %2") @@ -476,6 +473,7 @@ void CodeParser::checkModuleInclusion(Node* n) } } break; +#endif default: break; } diff --git a/src/tools/qdoc/config.cpp b/src/tools/qdoc/config.cpp index 56e7287c40..185c1c77f1 100644 --- a/src/tools/qdoc/config.cpp +++ b/src/tools/qdoc/config.cpp @@ -56,6 +56,7 @@ QT_BEGIN_NAMESPACE QString ConfigStrings::ALIAS = QStringLiteral("alias"); +QString ConfigStrings::AUTOLINKERRORS = QStringLiteral("autolinkerrors"); QString ConfigStrings::BASE = QStringLiteral("base"); QString ConfigStrings::BASEDIR = QStringLiteral("basedir"); QString ConfigStrings::BUILDVERSION = QStringLiteral("buildversion"); @@ -242,6 +243,7 @@ QStringList MetaStack::getExpanded(const Location& location) } QT_STATIC_CONST_IMPL QString Config::dot = QLatin1String("."); +bool Config::debug_ = false; bool Config::generateExamples = true; QString Config::overrideOutputDir; QString Config::installDir; diff --git a/src/tools/qdoc/config.h b/src/tools/qdoc/config.h index 54ee8de47f..ebe7956907 100644 --- a/src/tools/qdoc/config.h +++ b/src/tools/qdoc/config.h @@ -88,6 +88,8 @@ public: Config(const QString& programName); ~Config(); + static bool debug_; + void load(const QString& fileName); void setStringList(const QString& var, const QStringList& values); @@ -167,6 +169,7 @@ private: struct ConfigStrings { static QString ALIAS; + static QString AUTOLINKERRORS; static QString BASE; static QString BASEDIR; static QString BUILDVERSION; @@ -239,6 +242,7 @@ struct ConfigStrings }; #define CONFIG_ALIAS ConfigStrings::ALIAS +#define CONFIG_AUTOLINKERRORS ConfigStrings::AUTOLINKERRORS #define CONFIG_BASE ConfigStrings::BASE #define CONFIG_BASEDIR ConfigStrings::BASEDIR #define CONFIG_BUILDVERSION ConfigStrings::BUILDVERSION diff --git a/src/tools/qdoc/cppcodeparser.cpp b/src/tools/qdoc/cppcodeparser.cpp index c22a5227a6..73fe16fb46 100644 --- a/src/tools/qdoc/cppcodeparser.cpp +++ b/src/tools/qdoc/cppcodeparser.cpp @@ -52,6 +52,7 @@ #include "tokenizer.h" #include "qdocdatabase.h" #include +#include "generator.h" QT_BEGIN_NAMESPACE @@ -173,7 +174,7 @@ void CppCodeParser::parseHeaderFile(const Location& location, const QString& fil Tokenizer fileTokenizer(fileLocation, in); tokenizer = &fileTokenizer; readToken(); - matchDeclList(qdb_->treeRoot()); + matchDeclList(qdb_->primaryTreeRoot()); if (!fileTokenizer.version().isEmpty()) qdb_->setVersion(fileTokenizer.version()); in.close(); @@ -218,10 +219,10 @@ void CppCodeParser::parseSourceFile(const Location& location, const QString& fil } /*! - This is called after all the header files have been parsed. - I think the most important thing it does is resolve class - inheritance links in the tree. But it also initializes a - bunch of stuff. + This is called after all the C++ header files have been + parsed. The most important thing it does is resolve C++ + class inheritance links in the tree. It also initializes + a bunch of other collections. */ void CppCodeParser::doneParsingHeaderFiles() { @@ -263,11 +264,11 @@ void CppCodeParser::doneParsingHeaderFiles() */ void CppCodeParser::doneParsingSourceFiles() { - qdb_->treeRoot()->clearCurrentChildPointers(); - qdb_->treeRoot()->normalizeOverloads(); + qdb_->primaryTreeRoot()->clearCurrentChildPointers(); + qdb_->primaryTreeRoot()->normalizeOverloads(); qdb_->fixInheritance(); qdb_->resolveProperties(); - qdb_->treeRoot()->makeUndocumentedChildrenInternal(); + qdb_->primaryTreeRoot()->makeUndocumentedChildrenInternal(); } static QSet topicCommands_; @@ -326,38 +327,33 @@ Node* CppCodeParser::processTopicCommand(const Doc& doc, doc.startLocation().warning(tr("Invalid syntax in '\\%1'").arg(COMMAND_FN)); } else { - func = qdb_->findNodeInOpenNamespace(parentPath, clone); - /* - Search the root namespace if no match was found. - */ + func = qdb_->findFunctionNode(parentPath, clone); if (func == 0) { - func = qdb_->findFunctionNode(parentPath, clone); + if (parentPath.isEmpty() && !lastPath_.isEmpty()) + func = qdb_->findFunctionNode(lastPath_, clone); } + /* + If the node was not found, then search for it in the + open C++ namespaces. We don't expect this search to + be necessary often. Nor do we expect it to succeed + very often. + */ + if (func == 0) + func = qdb_->findNodeInOpenNamespace(parentPath, clone); + if (func == 0) { - if (parentPath.isEmpty() && !lastPath_.isEmpty()) { - func = qdb_->findFunctionNode(lastPath_, clone); - } - if (func == 0) { - doc.location().warning(tr("Cannot find '%1' in '\\%2' %3") - .arg(clone->name() + "(...)") - .arg(COMMAND_FN) - .arg(arg.first), - tr("I cannot find any function of that name with the " - "specified signature. Make sure that the signature " - "is identical to the declaration, including 'const' " - "qualifiers.")); - } - else { - doc.location().warning(tr("Missing '%1::' for '%2' in '\\%3'") - .arg(lastPath_.join("::")) - .arg(clone->name() + "()") - .arg(COMMAND_FN)); - } + doc.location().warning(tr("Cannot find '%1' in '\\%2' %3") + .arg(clone->name() + "(...)") + .arg(COMMAND_FN) + .arg(arg.first), + tr("I cannot find any function of that name with the " + "specified signature. Make sure that the signature " + "is identical to the declaration, including 'const' " + "qualifiers.")); } - else { + else lastPath_ = parentPath; - } if (func) { func->borrowParameterNames(clone); func->setParentPath(clone->parentPath()); @@ -370,7 +366,7 @@ Node* CppCodeParser::processTopicCommand(const Doc& doc, QStringList parentPath; FunctionNode *func = 0; - extra.root = qdb_->treeRoot(); + extra.root = qdb_->primaryTreeRoot(); extra.isMacro = true; if (makeFunctionNode(arg.first, &parentPath, &func, extra)) { if (!parentPath.isEmpty()) { @@ -392,7 +388,7 @@ Node* CppCodeParser::processTopicCommand(const Doc& doc, return func; } else if (QRegExp("[A-Za-z_][A-Za-z0-9_]+").exactMatch(arg.first)) { - func = new FunctionNode(qdb_->treeRoot(), arg.first); + func = new FunctionNode(qdb_->primaryTreeRoot(), arg.first); func->setAccess(Node::Public); func->setLocation(doc.startLocation()); func->setMetaness(FunctionNode::MacroWithoutParams); @@ -421,21 +417,9 @@ Node* CppCodeParser::processTopicCommand(const Doc& doc, QStringList path = paths[0].split("::"); Node *node = 0; - /* - If the command refers to something that can be in a - C++ namespace, search for it first in all the known - C++ namespaces. - */ node = qdb_->findNodeInOpenNamespace(path, type, subtype); - - /* - If the node was not found in a C++ namespace, search - for it in the root namespace. - */ - if (node == 0) { + if (node == 0) node = qdb_->findNodeByNameAndType(path, type, subtype); - } - if (node == 0) { doc.location().warning(tr("Cannot find '%1' specified with '\\%2' in any header file") .arg(arg.first).arg(command)); @@ -444,31 +428,33 @@ Node* CppCodeParser::processTopicCommand(const Doc& doc, } else if (node->isInnerNode()) { /* - This treets a class as a namespace. + This treats a class as a namespace. */ - if (path.size() > 1) { - path.pop_back(); - QString ns = path.join("::"); - qdb_->insertOpenNamespace(ns); + if ((type == Node::Class) || (type == Node::Namespace)) { + if (path.size() > 1) { + path.pop_back(); + QString ns = path.join("::"); + qdb_->insertOpenNamespace(ns); + } } } return node; } else if (command == COMMAND_EXAMPLE) { if (Config::generateExamples) { - ExampleNode* en = new ExampleNode(qdb_->treeRoot(), arg.first); + ExampleNode* en = new ExampleNode(qdb_->primaryTreeRoot(), arg.first); en->setLocation(doc.startLocation()); createExampleFileNodes(en); return en; } } else if (command == COMMAND_EXTERNALPAGE) { - DocNode* dn = new DocNode(qdb_->treeRoot(), arg.first, Node::ExternalPage, Node::ArticlePage); + DocNode* dn = new DocNode(qdb_->primaryTreeRoot(), arg.first, Node::ExternalPage, Node::ArticlePage); dn->setLocation(doc.startLocation()); return dn; } else if (command == COMMAND_FILE) { - DocNode* dn = new DocNode(qdb_->treeRoot(), arg.first, Node::File, Node::NoPageType); + DocNode* dn = new DocNode(qdb_->primaryTreeRoot(), arg.first, Node::File, Node::NoPageType); dn->setLocation(doc.startLocation()); return dn; } @@ -478,7 +464,7 @@ Node* CppCodeParser::processTopicCommand(const Doc& doc, return dn; } else if (command == COMMAND_HEADERFILE) { - DocNode* dn = new DocNode(qdb_->treeRoot(), arg.first, Node::HeaderFile, Node::ApiPage); + DocNode* dn = new DocNode(qdb_->primaryTreeRoot(), arg.first, Node::HeaderFile, Node::ApiPage); dn->setLocation(doc.startLocation()); return dn; } @@ -527,9 +513,9 @@ Node* CppCodeParser::processTopicCommand(const Doc& doc, NameCollisionNode* ncn = qdb_->checkForCollision(args[0]); DocNode* dn = 0; if (ptype == Node::DitaMapPage) - dn = new DitaMapNode(qdb_->treeRoot(), args[0]); + dn = new DitaMapNode(qdb_->primaryTreeRoot(), args[0]); else - dn = new DocNode(qdb_->treeRoot(), args[0], Node::Page, ptype); + dn = new DocNode(qdb_->primaryTreeRoot(), args[0], Node::Page, ptype); dn->setLocation(doc.startLocation()); if (ncn) { ncn->addCollision(dn); @@ -537,7 +523,7 @@ Node* CppCodeParser::processTopicCommand(const Doc& doc, return dn; } else if (command == COMMAND_DITAMAP) { - DocNode* dn = new DitaMapNode(qdb_->treeRoot(), arg.first); + DocNode* dn = new DitaMapNode(qdb_->primaryTreeRoot(), arg.first); dn->setLocation(doc.startLocation()); return dn; } @@ -577,7 +563,7 @@ Node* CppCodeParser::processTopicCommand(const Doc& doc, node and return that one. */ NameCollisionNode* ncn = qdb_->checkForCollision(names[0]); - QmlClassNode* qcn = new QmlClassNode(qdb_->treeRoot(), names[0]); + QmlClassNode* qcn = new QmlClassNode(qdb_->primaryTreeRoot(), names[0]); qcn->setClassNode(classNode); qcn->setLocation(doc.startLocation()); #if 0 @@ -596,12 +582,13 @@ Node* CppCodeParser::processTopicCommand(const Doc& doc, } } #endif - if (ncn) + if (ncn) { ncn->addCollision(qcn); + } return qcn; } else if (command == COMMAND_QMLBASICTYPE) { - QmlBasicTypeNode* n = new QmlBasicTypeNode(qdb_->treeRoot(), arg.first); + QmlBasicTypeNode* n = new QmlBasicTypeNode(qdb_->primaryTreeRoot(), arg.first); n->setLocation(doc.startLocation()); return n; } @@ -1644,7 +1631,7 @@ bool CppCodeParser::matchClassDecl(InnerNode *parent, while (tok == Tok_Ident) readToken(); if (tok == Tok_Gulbrandsen) { - Node* n = parent->findChildNodeByNameAndType(previousLexeme(),Node::Class); + Node* n = parent->findChildNode(previousLexeme(),Node::Class); if (n) { parent = static_cast(n); if (parent) { @@ -1704,7 +1691,7 @@ bool CppCodeParser::matchNamespaceDecl(InnerNode *parent) QString namespaceName = previousLexeme(); NamespaceNode* ns = 0; if (parent) - ns = static_cast(parent->findChildNodeByNameAndType(namespaceName, Node::Namespace)); + ns = static_cast(parent->findChildNode(namespaceName, Node::Namespace)); if (!ns) { ns = new NamespaceNode(parent, namespaceName); ns->setAccess(access); @@ -1842,7 +1829,7 @@ bool CppCodeParser::matchTypedefDecl(InnerNode *parent) if (!match(Tok_Semicolon)) return false; - if (parent && !parent->findChildNodeByNameAndType(name, Node::Typedef)) { + if (parent && !parent->findChildNode(name, Node::Typedef)) { TypedefNode* td = new TypedefNode(parent, name); td->setAccess(access); td->setLocation(location()); @@ -2077,7 +2064,7 @@ bool CppCodeParser::matchDeclList(InnerNode *parent) TypedefNode *flagsNode = new TypedefNode(parent, flagsType); flagsNode->setAccess(access); flagsNode->setLocation(location()); - EnumNode* en = static_cast(parent->findChildNodeByNameAndType(name, Node::Enum)); + EnumNode* en = static_cast(parent->findChildNode(name, Node::Enum)); if (en) en->setFlagsType(flagsNode); } @@ -2156,9 +2143,15 @@ bool CppCodeParser::matchDocsAndStuff() FunctionNode *func = 0; if (matchFunctionDecl(0, &parentPath, &clone, QString(), extra)) { - func = qdb_->findNodeInOpenNamespace(parentPath, clone); + func = qdb_->findFunctionNode(parentPath, clone); + /* + If the node was not found, then search for it in the + open C++ namespaces. We don't expect this search to + be necessary often. Nor do we expect it to succeed + very often. + */ if (func == 0) - func = qdb_->findFunctionNode(parentPath, clone); + func = qdb_->findNodeInOpenNamespace(parentPath, clone); if (func) { func->borrowParameterNames(clone); @@ -2220,8 +2213,9 @@ bool CppCodeParser::matchDocsAndStuff() checkModuleInclusion(*n); if ((*n)->isInnerNode() && ((InnerNode *)*n)->includes().isEmpty()) { InnerNode *m = static_cast(*n); - while (m->parent() != qdb_->treeRoot()) + while (m->parent() && m->moduleName().isEmpty()) { m = m->parent(); + } if (m == *n) ((InnerNode *)*n)->addInclude((*n)->name()); else @@ -2358,7 +2352,7 @@ void CppCodeParser::instantiateIteratorMacro(const QString &container, Tokenizer stringTokenizer(loc, latin1); tokenizer = &stringTokenizer; readToken(); - matchDeclList(QDocDatabase::qdocDB()->treeRoot()); + matchDeclList(QDocDatabase::qdocDB()->primaryTreeRoot()); } void CppCodeParser::createExampleFileNodes(DocNode *dn) diff --git a/src/tools/qdoc/ditaxmlgenerator.cpp b/src/tools/qdoc/ditaxmlgenerator.cpp index c4f08e45df..b341decbb6 100644 --- a/src/tools/qdoc/ditaxmlgenerator.cpp +++ b/src/tools/qdoc/ditaxmlgenerator.cpp @@ -667,13 +667,13 @@ GuidMap* DitaXmlGenerator::lookupGuidMap(const QString& fileName) } /*! - Traverses the database generating all the DITA XML documentation. + Traverses the current tree generating all the DITA XML documentation. */ -void DitaXmlGenerator::generateTree() +void DitaXmlGenerator::generateDocs() { qdb_->buildCollections(); if (!runPrepareOnly()) { - Generator::generateTree(); + Generator::generateDocs(); generateCollisionPages(); } @@ -3111,7 +3111,7 @@ void DitaXmlGenerator::generateOverviewList(const Node* relative) QMap uncategorizedNodeMap; QRegExp singleDigit("\\b([0-9])\\b"); - const NodeList children = qdb_->treeRoot()->childNodes(); + const NodeList children = qdb_->primaryTreeRoot()->childNodes(); foreach (Node* child, children) { if (child->type() == Node::Document && child != relative) { DocNode* docNode = static_cast(child); @@ -5511,7 +5511,7 @@ void DitaXmlGenerator::writeDitaMap() Remove #if 0 to get a flat ditamap. */ #if 0 - beginSubPage(qdb_->treeRoot(),"qt.ditamap"); + beginSubPage(qdb_->primaryTreeRoot(),"qt.ditamap"); doctype = ""; xmlWriter().writeDTD(doctype); writeStartTag(DT_map); @@ -5537,9 +5537,9 @@ void DitaXmlGenerator::writeDitaMap() nodeSubtypeMaps[i] = new NodeMultiMap; for (unsigned i=0; itreeRoot()); + Node* rootPageNode = collectNodesByTypeAndSubtype(qdb_->primaryTreeRoot()); - beginSubPage(qdb_->treeRoot(),"qt.ditamap"); + beginSubPage(qdb_->primaryTreeRoot(),"qt.ditamap"); doctype = ""; xmlWriter().writeDTD(doctype); @@ -6132,7 +6132,7 @@ void DitaXmlGenerator::generateCollisionPages() int count = 0; for (int i=0; i(collisions.at(i)); - if (n->findChildNodeByName(t.key())) { + if (n->findChildNode(t.key())) { ++count; if (count > 1) { targets.append(t.key()); @@ -6154,7 +6154,7 @@ void DitaXmlGenerator::generateCollisionPages() writeStartTag(DT_ul); for (int i=0; i(collisions.at(i)); - Node* p = n->findChildNodeByName(*t); + Node* p = n->findChildNode(*t); if (p) { QString link = linkForNode(p,0); QString label; diff --git a/src/tools/qdoc/ditaxmlgenerator.h b/src/tools/qdoc/ditaxmlgenerator.h index da05bb0274..a2a0bf5020 100644 --- a/src/tools/qdoc/ditaxmlgenerator.h +++ b/src/tools/qdoc/ditaxmlgenerator.h @@ -302,7 +302,7 @@ public: virtual void terminateGenerator(); virtual QString format(); virtual bool canHandleFormat(const QString& format); - virtual void generateTree(); + virtual void generateDocs(); void generateCollisionPages(); QString protectEnc(const QString& string); diff --git a/src/tools/qdoc/doc.cpp b/src/tools/qdoc/doc.cpp index 6e70671d1d..afc21bb518 100644 --- a/src/tools/qdoc/doc.cpp +++ b/src/tools/qdoc/doc.cpp @@ -316,17 +316,17 @@ Q_GLOBAL_STATIC(QHash_QString_Macro, macroHash) class DocPrivateExtra { public: - Doc::Sections granularity; - Doc::Sections section; // ### - QList tableOfContents; - QList tableOfContentsLevels; - QList keywords; - QList targets; - QStringMultiMap metaMap; + Doc::Sections granularity_; + Doc::Sections section_; // ### + QList tableOfContents_; + QList tableOfContentsLevels_; + QList keywords_; + QList targets_; + QStringMultiMap metaMap_; DocPrivateExtra() - : granularity(Doc::Part) - , section(Doc::NoSection) + : granularity_(Doc::Part) + , section_(Doc::NoSection) { } }; @@ -535,7 +535,7 @@ private: int braceDepth; int minIndent; Doc::Sections currentSection; - QMap targetMap; + QMap targetMap_; QMap pendingFormats; QStack openedCommands; QStack openedLists; @@ -880,7 +880,7 @@ void DocParser::parse(const QString& source, break; case CMD_GRANULARITY: priv->constructExtra(); - priv->extra->granularity = getSectioningUnit(); + priv->extra->granularity_ = getSectioningUnit(); break; case CMD_HEADER: if (openedCommands.top() == CMD_TABLE) { @@ -1025,7 +1025,7 @@ void DocParser::parse(const QString& source, case CMD_META: priv->constructExtra(); p1 = getArgument(); - priv->extra->metaMap.insert(p1, getArgument()); + priv->extra->metaMap_.insert(p1, getArgument()); break; case CMD_NEWCODE: location().warning(tr("Unexpected '\\%1'").arg(cmdName(CMD_NEWCODE))); @@ -1626,8 +1626,8 @@ void DocParser::parse(const QString& source, currentSection = Doc::NoSection; } - if (priv->extra && priv->extra->granularity < priv->extra->section) - priv->extra->granularity = priv->extra->section; + if (priv->extra && priv->extra->granularity_ < priv->extra->section_) + priv->extra->granularity_ = priv->extra->section_; priv->text.stripFirstAtom(); } @@ -1668,18 +1668,18 @@ QString DocParser::detailsUnknownCommand(const QSet &metaCommandSet, void DocParser::insertTarget(const QString &target, bool keyword) { - if (targetMap.contains(target)) { + if (targetMap_.contains(target)) { location().warning(tr("Duplicate target name '%1'").arg(target)); - targetMap[target].warning(tr("(The previous occurrence is here)")); + targetMap_[target].warning(tr("(The previous occurrence is here)")); } else { - targetMap.insert(target, location()); + targetMap_.insert(target, location()); append(Atom::Target, target); priv->constructExtra(); if (keyword) - priv->extra->keywords.append(priv->text.lastAtom()); + priv->extra->keywords_.append(priv->text.lastAtom()); else - priv->extra->targets.append(priv->text.lastAtom()); + priv->extra->targets_.append(priv->text.lastAtom()); } } @@ -1883,15 +1883,15 @@ void DocParser::startSection(Doc::Sections unit, int cmd) if (currentSection == Doc::NoSection) { currentSection = (Doc::Sections) (unit); priv->constructExtra(); - priv->extra->section = currentSection; + priv->extra->section_ = currentSection; } else endSection(unit,cmd); append(Atom::SectionLeft, QString::number(unit)); priv->constructExtra(); - priv->extra->tableOfContents.append(priv->text.lastAtom()); - priv->extra->tableOfContentsLevels.append(unit); + priv->extra->tableOfContents_.append(priv->text.lastAtom()); + priv->extra->tableOfContentsLevels_.append(unit); enterPara(Atom::SectionHeadingLeft, Atom::SectionHeadingRight, QString::number(unit)); @@ -2958,10 +2958,10 @@ Text Doc::legaleseText() const Doc::Sections Doc::granularity() const { if (priv == 0 || priv->extra == 0) { - return DocPrivateExtra().granularity; + return DocPrivateExtra().granularity_; } else { - return priv->extra->granularity; + return priv->extra->granularity_; } } @@ -3007,46 +3007,46 @@ const QList &Doc::alsoList() const bool Doc::hasTableOfContents() const { - return priv && priv->extra && !priv->extra->tableOfContents.isEmpty(); + return priv && priv->extra && !priv->extra->tableOfContents_.isEmpty(); } bool Doc::hasKeywords() const { - return priv && priv->extra && !priv->extra->keywords.isEmpty(); + return priv && priv->extra && !priv->extra->keywords_.isEmpty(); } bool Doc::hasTargets() const { - return priv && priv->extra && !priv->extra->targets.isEmpty(); + return priv && priv->extra && !priv->extra->targets_.isEmpty(); } const QList &Doc::tableOfContents() const { priv->constructExtra(); - return priv->extra->tableOfContents; + return priv->extra->tableOfContents_; } const QList &Doc::tableOfContentsLevels() const { priv->constructExtra(); - return priv->extra->tableOfContentsLevels; + return priv->extra->tableOfContentsLevels_; } const QList &Doc::keywords() const { priv->constructExtra(); - return priv->extra->keywords; + return priv->extra->keywords_; } const QList &Doc::targets() const { priv->constructExtra(); - return priv->extra->targets; + return priv->extra->targets_; } const QStringMultiMap &Doc::metaTagMap() const { - return priv && priv->extra ? priv->extra->metaMap : *null_QStringMultiMap(); + return priv && priv->extra ? priv->extra->metaMap_ : *null_QStringMultiMap(); } const Config* Doc::config_ = 0; diff --git a/src/tools/qdoc/generator.cpp b/src/tools/qdoc/generator.cpp index d563546192..ec8eb14aa9 100644 --- a/src/tools/qdoc/generator.cpp +++ b/src/tools/qdoc/generator.cpp @@ -97,23 +97,26 @@ QStringList Generator::styleDirs; QStringList Generator::styleFiles; bool Generator::debugging_ = false; bool Generator::noLinkErrors_ = false; +bool Generator::autolinkErrors_ = false; bool Generator::redirectDocumentationToDevNull_ = false; Generator::Passes Generator::qdocPass_ = Both; bool Generator::useOutputSubdirs_ = true; -void Generator::setDebugSegfaultFlag(bool b) +void Generator::setDebugFlag(bool b) { +#if 0 if (b) qDebug() << "DEBUG: Setting debug flag."; else qDebug() << "DEBUG: Clearing debug flag."; +#endif debugging_ = b; } /*! Prints \a message as an aid to debugging the release version. */ -void Generator::debugSegfault(const QString& message) +void Generator::debug(const QString& message) { if (debugging()) qDebug() << "DEBUG:" << message; @@ -275,7 +278,7 @@ void Generator::beginSubPage(const InnerNode* node, const QString& fileName) node->location().error(tr("HTML file already exists; overwriting %1").arg(outFile->fileName())); if (!outFile->open(QFile::WriteOnly)) node->location().fatal(tr("Cannot open output file '%1'").arg(outFile->fileName())); - Generator::debugSegfault("Writing: " + path); + Generator::debug("Writing: " + path); outFileNames.insert(fileName,fileName); QTextStream* out = new QTextStream(outFile); @@ -1365,11 +1368,11 @@ void Generator::generateThreadSafeness(const Node *node, CodeMarker *marker) } /*! - Traverses the database recursivly to generate all the documentation. + Traverses the current tree to generate all the documentation. */ -void Generator::generateTree() +void Generator::generateDocs() { - generateInnerNode(qdb_->treeRoot()); + generateInnerNode(qdb_->primaryTreeRoot()); } Generator *Generator::generatorForFormat(const QString& format) @@ -1653,6 +1656,7 @@ void Generator::initialize(const Config &config) else outputPrefixes[QLatin1String("QML")] = QLatin1String("qml-"); noLinkErrors_ = config.getBool(CONFIG_NOLINKERRORS); + autolinkErrors_ = config.getBool(CONFIG_AUTOLINKERRORS); } /*! diff --git a/src/tools/qdoc/generator.h b/src/tools/qdoc/generator.h index b464014308..1042105e15 100644 --- a/src/tools/qdoc/generator.h +++ b/src/tools/qdoc/generator.h @@ -77,7 +77,7 @@ public: virtual bool canHandleFormat(const QString &format) { return format == this->format(); } virtual QString format() = 0; - virtual void generateTree(); + virtual void generateDocs(); virtual void initializeGenerator(const Config &config); virtual void terminateGenerator(); @@ -92,10 +92,11 @@ public: static void terminate(); static void writeOutFileNames(); static void augmentImageDirs(QSet& moreImageDirs); - static void debugSegfault(const QString& message); - static void setDebugSegfaultFlag(bool b); + static void debug(const QString& message); + static void setDebugFlag(bool b); static bool debugging() { return debugging_; } static bool noLinkErrors() { return noLinkErrors_; } + static bool autolinkErrors() { return autolinkErrors_; } static void setQDocPass(Passes pass) { qdocPass_ = pass; } static bool runPrepareOnly() { return (qdocPass_ == Prepare); } static bool runGenerateOnly() { return (qdocPass_ == Generate); } @@ -219,6 +220,7 @@ private: static QStringList styleFiles; static bool debugging_; static bool noLinkErrors_; + static bool autolinkErrors_; static bool redirectDocumentationToDevNull_; static Passes qdocPass_; static bool useOutputSubdirs_; diff --git a/src/tools/qdoc/helpprojectwriter.cpp b/src/tools/qdoc/helpprojectwriter.cpp index 2057bcd3bc..1e414578cd 100644 --- a/src/tools/qdoc/helpprojectwriter.cpp +++ b/src/tools/qdoc/helpprojectwriter.cpp @@ -612,7 +612,7 @@ void HelpProjectWriter::generateProject(HelpProject &project) if (!project.indexRoot.isEmpty()) rootNode = qdb_->findDocNodeByTitle(project.indexRoot); else - rootNode = qdb_->treeRoot(); + rootNode = qdb_->primaryTreeRoot(); if (!rootNode) return; diff --git a/src/tools/qdoc/htmlgenerator.cpp b/src/tools/qdoc/htmlgenerator.cpp index ca52d98596..b4ea771fcc 100644 --- a/src/tools/qdoc/htmlgenerator.cpp +++ b/src/tools/qdoc/htmlgenerator.cpp @@ -264,16 +264,16 @@ QString HtmlGenerator::format() } /*! - Traverses the database generating all the HTML documentation. + Traverses the current tree generating all the HTML documentation. */ -void HtmlGenerator::generateTree() +void HtmlGenerator::generateDocs() { qdb_->buildCollections(); Node* qflags = qdb_->findNodeByNameAndType(QStringList("QFlags"), Node::Class, Node::NoSubType); if (qflags) qflagsHref_ = linkForNode(qflags,0); if (!runPrepareOnly()) { - Generator::generateTree(); + Generator::generateDocs(); generateCollisionPages(); } @@ -303,10 +303,11 @@ int HtmlGenerator::generateAtom(const Atom *atom, const Node *relative, CodeMark { int skipAhead = 0; static bool in_para = false; - +#if 0 if (Generator::debugging()) { atom->dump(); } +#endif switch (atom->type()) { case Atom::AbstractLeft: if (relative) @@ -327,6 +328,8 @@ int HtmlGenerator::generateAtom(const Atom *atom, const Node *relative, CodeMark } else { out() << protectEnc(atom->string()); + if (autolinkErrors()) + relative->doc().location().warning(tr("Can't autolink to '%1'").arg(atom->string())); } } else { @@ -1374,7 +1377,7 @@ void HtmlGenerator::generateCollisionPages() int count = 0; for (int i=0; i(collisions.at(i)); - if (n->findChildNodeByName(t.key())) { + if (n->findChildNode(t.key())) { ++count; if (count > 1) { targets.append(t.key()); @@ -1393,7 +1396,7 @@ void HtmlGenerator::generateCollisionPages() out() << "
    \n"; for (int i=0; i(collisions.at(i)); - Node* p = n->findChildNodeByName(*t); + Node* p = n->findChildNode(*t); if (p) { QString link = linkForNode(p,0); QString label; @@ -2798,7 +2801,7 @@ void HtmlGenerator::generateOverviewList(const Node *relative) QMap uncategorizedNodeMap; QRegExp singleDigit("\\b([0-9])\\b"); - const NodeList children = qdb_->treeRoot()->childNodes(); + const NodeList children = qdb_->primaryTreeRoot()->childNodes(); foreach (Node *child, children) { if (child->type() == Node::Document && child != relative) { DocNode *docNode = static_cast(child); @@ -3149,7 +3152,6 @@ QString HtmlGenerator::highlightedCode(const QString& markedCode, if (src.at(i) == charLangle && src.at(i + 1) == charAt) { i += 2; if (parseArg(src, funcTag, &i, srcSize, &arg, &par1)) { - const Node* n = qdb_->resolveTarget(par1.toString(), relative); QString link = linkForNode(n, relative); addLink(link, arg, &html); @@ -3165,7 +3167,7 @@ QString HtmlGenerator::highlightedCode(const QString& markedCode, } } - // replace all "(<@(type|headerfile|func)(?: +[^>]*)?>)(.*)()" tags + // replace all "(<@(type|headerfile)(?: +[^>]*)?>)(.*)()" tags src = html; html = QString(); @@ -3175,7 +3177,7 @@ QString HtmlGenerator::highlightedCode(const QString& markedCode, bool handled = false; if (parseArg(src, typeTag, &i, srcSize, &arg, &par1)) { par1 = QStringRef(); - const Node* n = qdb_->resolveTarget(arg.toString(), relative); + const Node* n = qdb_->resolveType(arg.toString(), relative); html += QLatin1String(""); if (n && n->subType() == Node::QmlBasicType) { if (relative && relative->subType() == Node::QmlClass) @@ -3194,13 +3196,16 @@ QString HtmlGenerator::highlightedCode(const QString& markedCode, addLink(linkForNode(n,relative), arg, &html); handled = true; } +#if 0 + // Apparently, this clause was never used. + // <@func> is taken out above. else if (parseArg(src, funcTag, &i, srcSize, &arg, &par1)) { par1 = QStringRef(); const Node* n = qdb_->resolveTarget(arg.toString(), relative); addLink(linkForNode(n,relative), arg, &html); handled = true; } - +#endif if (!handled) { html += charLangle; html += charAt; @@ -3755,11 +3760,11 @@ QString HtmlGenerator::getLink(const Atom *atom, const Node *relative, const Nod *node = qdb_->findNodeByNameAndType(QStringList(first), Node::Document, Node::NoSubType); } else { - *node = qdb_->resolveTarget(first, relative); - if (!*node) { + if (!(*node)) + *node = qdb_->resolveTarget(first, relative); + if (!(*node)) *node = qdb_->findDocNodeByTitle(first, relative); - } - if (!*node) { + if (!(*node)) { *node = qdb_->findUnambiguousTarget(first, ref, relative); if (*node && !(*node)->url().isEmpty() && !ref.isEmpty()) { QString final = (*node)->url() + "#" + ref; @@ -3768,16 +3773,13 @@ QString HtmlGenerator::getLink(const Atom *atom, const Node *relative, const Nod } } if (*node) { - if (!(*node)->url().isEmpty()) { + if (!(*node)->url().isEmpty()) return (*node)->url(); - } - else { + else path.removeFirst(); - } } - else { + else *node = relative; - } if (*node) { if ((*node)->status() == Node::Obsolete) { @@ -3802,10 +3804,8 @@ QString HtmlGenerator::getLink(const Atom *atom, const Node *relative, const Nod } } } - else { - qDebug() << "Link to Obsolete entity" - << (*node)->name() << "no relative"; - } + else + qDebug() << "Link to Obsolete entity" << (*node)->name() << "no relative"; } } @@ -3833,9 +3833,8 @@ QString HtmlGenerator::getLink(const Atom *atom, const Node *relative, const Nod link = linkForNode(*node, relative); if (*node && (*node)->subType() == Node::Image) link = "images/used-in-examples/" + link; - if (!ref.isEmpty()) { + if (!ref.isEmpty()) link += QLatin1Char('#') + ref; - } } } return link; diff --git a/src/tools/qdoc/htmlgenerator.h b/src/tools/qdoc/htmlgenerator.h index 476bd58452..45b1a327cf 100644 --- a/src/tools/qdoc/htmlgenerator.h +++ b/src/tools/qdoc/htmlgenerator.h @@ -88,7 +88,7 @@ public: virtual void initializeGenerator(const Config& config); virtual void terminateGenerator(); virtual QString format(); - virtual void generateTree(); + virtual void generateDocs(); void generateCollisionPages(); void generateManifestFiles(); diff --git a/src/tools/qdoc/main.cpp b/src/tools/qdoc/main.cpp index a5da6b3edd..9e0aecf393 100644 --- a/src/tools/qdoc/main.cpp +++ b/src/tools/qdoc/main.cpp @@ -78,6 +78,7 @@ static bool highlighting = false; static bool showInternal = false; static bool redirectDocumentationToDevNull = false; static bool noLinkErrors = false; +static bool autolinkErrors = false; static bool obsoleteLinks = false; static QStringList defines; static QStringList dependModules; @@ -282,6 +283,7 @@ static void processQdocconfFile(const QString &fileName) config.setStringList(CONFIG_SHOWINTERNAL, QStringList(showInternal ? "true" : "false")); config.setStringList(CONFIG_REDIRECTDOCUMENTATIONTODEVNULL, QStringList(redirectDocumentationToDevNull ? "true" : "false")); config.setStringList(CONFIG_NOLINKERRORS, QStringList(noLinkErrors ? "true" : "false")); + config.setStringList(CONFIG_AUTOLINKERRORS, QStringList(autolinkErrors ? "true" : "false")); config.setStringList(CONFIG_OBSOLETELINKS, QStringList(obsoleteLinks ? "true" : "false")); /* @@ -371,6 +373,8 @@ static void processQdocconfFile(const QString &fileName) //if (!Generator::runPrepareOnly()) loadIndexFiles(config); + qdb->newPrimaryTree(config.getString(CONFIG_PROJECT)); + qdb->setSearchOrder(); QSet excludedDirs; QSet excludedFiles; @@ -379,7 +383,7 @@ static void processQdocconfFile(const QString &fileName) QStringList excludedDirsList; QStringList excludedFilesList; - Generator::debugSegfault("Reading excludedirs"); + Generator::debug("Reading excludedirs"); excludedDirsList = config.getCanonicalPathList(CONFIG_EXCLUDEDIRS); foreach (const QString &excludeDir, excludedDirsList) { QString p = QDir::fromNativeSeparators(excludeDir); @@ -388,14 +392,14 @@ static void processQdocconfFile(const QString &fileName) excludedDirs.insert(p); } - Generator::debugSegfault("Reading excludefiles"); + Generator::debug("Reading excludefiles"); excludedFilesList = config.getCleanPathList(CONFIG_EXCLUDEFILES); foreach (const QString& excludeFile, excludedFilesList) { QString p = QDir::fromNativeSeparators(excludeFile); excludedFiles.insert(p); } - Generator::debugSegfault("Reading headerdirs"); + Generator::debug("Reading headerdirs"); headerList = config.getAllFiles(CONFIG_HEADERS,CONFIG_HEADERDIRS,excludedDirs,excludedFiles); QMap headers; QMultiMap headerFileNames; @@ -409,7 +413,7 @@ static void processQdocconfFile(const QString &fileName) headerFileNames.insert(t,t); } - Generator::debugSegfault("Reading sourcedirs"); + Generator::debug("Reading sourcedirs"); sourceList = config.getAllFiles(CONFIG_SOURCES,CONFIG_SOURCEDIRS,excludedDirs,excludedFiles); QMap sources; QMultiMap sourceFileNames; @@ -426,7 +430,7 @@ static void processQdocconfFile(const QString &fileName) Find all the qdoc files in the example dirs, and add them to the source files to be parsed. */ - Generator::debugSegfault("Reading exampledirs"); + Generator::debug("Reading exampledirs"); QStringList exampleQdocList = config.getExampleQdocFiles(excludedDirs, excludedFiles); for (int i=0; i exampleImageDirs; QStringList exampleImageList = config.getExampleImageFiles(excludedDirs, excludedFiles); for (int i=0; i usedParsers; + //Config::debug_ = true; - Generator::debugSegfault("Parsing header files"); + Generator::debug("Parsing header files"); int parsed = 0; QMap::ConstIterator h = headers.constBegin(); while (h != headers.constEnd()) { @@ -479,17 +484,21 @@ static void processQdocconfFile(const QString &fileName) add it to the big tree. */ parsed = 0; - Generator::debugSegfault("Parsing source files"); + Generator::debug("Parsing source files"); QMap::ConstIterator s = sources.constBegin(); while (s != sources.constEnd()) { CodeParser *codeParser = CodeParser::parserForSourceFile(s.key()); if (codeParser) { ++parsed; + //Generator::setDebugFlag(true); + Generator::debug(QString("Parsing " + s.key())); codeParser->parseSourceFile(config.location(), s.key()); usedParsers.insert(codeParser); } ++s; } + Generator::debug(QString("Parsing done.")); + //Generator::setDebugFlag(false); foreach (CodeParser *codeParser, usedParsers) codeParser->doneParsingSourceFiles(); @@ -499,7 +508,7 @@ static void processQdocconfFile(const QString &fileName) source files. Resolve all the class names, function names, targets, URLs, links, and other stuff that needs resolving. */ - Generator::debugSegfault("Resolving stuff prior to generating docs"); + Generator::debug("Resolving stuff prior to generating docs"); qdb->resolveIssues(); /* @@ -508,19 +517,19 @@ static void processQdocconfFile(const QString &fileName) documentation output. More than one output format can be requested. The tree is traversed for each one. */ - Generator::debugSegfault("Generating docs"); + Generator::debug("Generating docs"); QSet::ConstIterator of = outputFormats.constBegin(); while (of != outputFormats.constEnd()) { Generator* generator = Generator::generatorForFormat(*of); if (generator == 0) outputFormatsLocation.fatal(QCoreApplication::translate("QDoc", "Unknown output format '%1'").arg(*of)); - generator->generateTree(); + generator->generateDocs(); ++of; } //Generator::writeOutFileNames(); - Generator::debugSegfault("Shutting down qdoc"); + Generator::debug("Shutting down qdoc"); QDocDatabase::qdocDB()->setVersion(QString()); Generator::terminate(); @@ -541,7 +550,7 @@ static void processQdocconfFile(const QString &fileName) #ifdef DEBUG_SHUTDOWN_CRASH qDebug() << "main(): qdoc database deleted"; #endif - Generator::debugSegfault("qdoc finished!"); + Generator::debug("qdoc finished!"); } QT_END_NAMESPACE @@ -641,8 +650,11 @@ int main(int argc, char **argv) else if (opt == "-no-link-errors") { noLinkErrors = true; } + else if (opt == "-autolink-errors") { + autolinkErrors = true; + } else if (opt == "-debug") { - Generator::setDebugSegfaultFlag(true); + Generator::setDebugFlag(true); } else if (opt == "-prepare") { Generator::setQDocPass(Generator::Prepare); diff --git a/src/tools/qdoc/node.cpp b/src/tools/qdoc/node.cpp index 6399616725..fcc79ff11c 100644 --- a/src/tools/qdoc/node.cpp +++ b/src/tools/qdoc/node.cpp @@ -764,7 +764,7 @@ void InnerNode::getMemberClasses(NodeMap& out) sure to also look in the children of its property group nodes. Return the matching node or 0. */ -Node *InnerNode::findChildNodeByName(const QString& name) const +Node *InnerNode::findChildNode(const QString& name) const { Node *node = childMap.value(name); if (node && !node->isQmlPropertyGroup()) @@ -773,7 +773,7 @@ Node *InnerNode::findChildNodeByName(const QString& name) const for (int i=0; iisQmlPropertyGroup()) { - node = static_cast(n)->findChildNodeByName(name); + node = static_cast(n)->findChildNode(name); if (node) return node; } @@ -782,61 +782,6 @@ Node *InnerNode::findChildNodeByName(const QString& name) const return primaryFunctionMap.value(name); } -/*! - */ -void InnerNode::findNodes(const QString& name, QList& n) -{ - n.clear(); - Node* node = 0; - QList nodes = childMap.values(name); - /* - If this node's child map contains no nodes named - name, then if this node is a QML class, search each of its - property group nodes for a node named name. If a match is - found, append it to the output list and return immediately. - */ - if (nodes.isEmpty()) { - if (isQmlType()) { - for (int i=0; iisQmlPropertyGroup()) { - node = static_cast(node)->findChildNodeByName(name); - if (node) { - n.append(node); - return; - } - } - } - } - } - else { - /* - If the childMap does contain one or more nodes named - name, traverse the list of matching nodes. Append each - matching node that is not a property group node to the - output list. Search each property group node for a node - named name and append that node to the output list. - This is overkill, I think, but should produce a useful - list. - */ - for (int i=0; iisQmlPropertyGroup()) - n.append(node); - else { - node = static_cast(node)->findChildNodeByName(name); - if (node) - n.append(node); - } - } - } - if (!n.isEmpty()) - return; - node = primaryFunctionMap.value(name); - if (node) - n.append(node); -} - /*! Find the node in this node's children that has the given \a name. If this node is a QML class node, be sure to also look in the children @@ -847,7 +792,7 @@ void InnerNode::findNodes(const QString& name, QList& n) returns \c true. If \a qml is false, only match a node for which node->isQmlNode() returns \c false. */ -Node* InnerNode::findChildNodeByName(const QString& name, bool qml) const +Node* InnerNode::findChildNode(const QString& name, bool qml) const { QList nodes = childMap.values(name); if (!nodes.isEmpty()) { @@ -865,7 +810,7 @@ Node* InnerNode::findChildNodeByName(const QString& name, bool qml) const for (int i=0; iisQmlPropertyGroup()) { - node = static_cast(node)->findChildNodeByName(name); + node = static_cast(node)->findChildNode(name); if (node) return node; } @@ -875,7 +820,7 @@ Node* InnerNode::findChildNodeByName(const QString& name, bool qml) const } /*! - This function is like findChildNodeByName(), but if a node + This function is like findChildNode(), but if a node with the specified \a name is found but it is not of the specified \a type, 0 is returned. @@ -885,7 +830,7 @@ Node* InnerNode::findChildNodeByName(const QString& name, bool qml) const node because it looks up \a name in the child map, not the list. */ -Node* InnerNode::findChildNodeByNameAndType(const QString& name, Type type) +Node* InnerNode::findChildNode(const QString& name, Type type) { if (type == Function) return primaryFunctionMap.value(name); @@ -900,6 +845,61 @@ Node* InnerNode::findChildNodeByNameAndType(const QString& name, Type type) return 0; } +/*! + */ +void InnerNode::findNodes(const QString& name, QList& n) +{ + n.clear(); + Node* node = 0; + QList nodes = childMap.values(name); + /* + If this node's child map contains no nodes named + name, then if this node is a QML class, search each of its + property group nodes for a node named name. If a match is + found, append it to the output list and return immediately. + */ + if (nodes.isEmpty()) { + if (isQmlType()) { + for (int i=0; iisQmlPropertyGroup()) { + node = static_cast(node)->findChildNode(name); + if (node) { + n.append(node); + return; + } + } + } + } + } + else { + /* + If the childMap does contain one or more nodes named + name, traverse the list of matching nodes. Append each + matching node that is not a property group node to the + output list. Search each property group node for a node + named name and append that node to the output list. + This is overkill, I think, but should produce a useful + list. + */ + for (int i=0; iisQmlPropertyGroup()) + n.append(node); + else { + node = static_cast(node)->findChildNode(name); + if (node) + n.append(node); + } + } + } + if (!n.isEmpty()) + return; + node = primaryFunctionMap.value(name); + if (node) + n.append(node); +} + /*! Find a function node that is a child of this nose, such that the function node has the specified \a name. @@ -1258,6 +1258,17 @@ void InnerNode::addChild(Node *child) } } +/*! + Adds the \a child to this node's child map using \a title + as the key. The \a child is not added to the child list + again, because it is presumed to already be there. We just + want to be able to find the child by its \a title. + */ +void InnerNode::addChild(Node* child, const QString& title) +{ + childMap.insertMulti(title, child); +} + /*! */ void InnerNode::removeChild(Node *child) @@ -1288,6 +1299,16 @@ void InnerNode::removeChild(Node *child) } ++ent; } + if (child->title().isEmpty()) + return; + ent = childMap.find(child->title()); + while (ent != childMap.end() && ent.key() == child->title()) { + if (*ent == child) { + childMap.erase(ent); + break; + } + ++ent; + } } /*! @@ -1533,7 +1554,7 @@ void ClassNode::fixPropertyUsingBaseClasses(PropertyNode* pn) while (bc != baseClasses().constEnd()) { ClassNode* cn = bc->node_; if (cn) { - Node* n = cn->findChildNodeByNameAndType(pn->name(), Node::Property); + Node* n = cn->findChildNode(pn->name(), Node::Property); if (n) { PropertyNode* baseProperty = static_cast(n); cn->fixPropertyUsingBaseClasses(baseProperty); @@ -1552,7 +1573,7 @@ void ClassNode::fixPropertyUsingBaseClasses(PropertyNode* pn) */ PropertyNode* ClassNode::findPropertyNode(const QString& name) { - Node* n = findChildNodeByNameAndType(name, Node::Property); + Node* n = findChildNode(name, Node::Property); if (n) return static_cast(n); @@ -1665,6 +1686,15 @@ QString DocNode::title() const return title_; } +/*! + Sets the document node's \a title. This is used for the page title. + */ +void DocNode::setTitle(const QString &title) +{ + title_ = title; + parent()->addChild(this, title); +} + /*! Returns the document node's full title, which is usually just title(), but for some SubType values is different diff --git a/src/tools/qdoc/node.h b/src/tools/qdoc/node.h index ea21e9dcd9..487da9f10e 100644 --- a/src/tools/qdoc/node.h +++ b/src/tools/qdoc/node.h @@ -339,9 +339,9 @@ class InnerNode : public Node public: virtual ~InnerNode(); - Node* findChildNodeByName(const QString& name) const; - Node* findChildNodeByName(const QString& name, bool qml) const; - Node* findChildNodeByNameAndType(const QString& name, Type type); + Node* findChildNode(const QString& name) const; + Node* findChildNode(const QString& name, bool qml) const; + Node* findChildNode(const QString& name, Type type); void findNodes(const QString& name, QList& n); FunctionNode* findFunctionNode(const QString& name) const; FunctionNode* findFunctionNode(const FunctionNode* clone); @@ -382,6 +382,7 @@ public: virtual void setOutputFileName(const QString& f) { outputFileName_ = f; } virtual QString outputFileName() const { return outputFileName_; } virtual QmlPropertyNode* hasQmlProperty(const QString& ) const; + void addChild(Node* child, const QString& title); void printChildren(const QString& title); void printMembers(const QString& title); @@ -508,7 +509,7 @@ public: virtual ~DocNode() { } void setQtVariable(const QString &variable) { qtVariable_ = variable; } - void setTitle(const QString &title) { title_ = title; } + void setTitle(const QString &title); void setSubTitle(const QString &subTitle) { subtitle_ = subTitle; } QString qtVariable() const { return qtVariable_; } @@ -838,8 +839,6 @@ private: QString def; }; -class PropertyNode; - class FunctionNode : public LeafNode { public: diff --git a/src/tools/qdoc/puredocparser.cpp b/src/tools/qdoc/puredocparser.cpp index 644fe05438..e8292e787e 100644 --- a/src/tools/qdoc/puredocparser.cpp +++ b/src/tools/qdoc/puredocparser.cpp @@ -202,7 +202,7 @@ bool PureDocParser::processQdocComments() } } - Node* treeRoot = QDocDatabase::qdocDB()->treeRoot(); + Node* treeRoot = QDocDatabase::qdocDB()->primaryTreeRoot(); NodeList::Iterator n = nodes.begin(); QList::Iterator d = docs.begin(); while (n != nodes.end()) { diff --git a/src/tools/qdoc/puredocparser.h b/src/tools/qdoc/puredocparser.h index 0174af7a4e..b664367527 100644 --- a/src/tools/qdoc/puredocparser.h +++ b/src/tools/qdoc/puredocparser.h @@ -56,7 +56,6 @@ QT_BEGIN_NAMESPACE class Config; class Node; class QString; -class Tree; class PureDocParser : public CppCodeParser { diff --git a/src/tools/qdoc/qdocdatabase.cpp b/src/tools/qdoc/qdocdatabase.cpp index bbcb92c30d..79e323f0b0 100644 --- a/src/tools/qdoc/qdocdatabase.cpp +++ b/src/tools/qdoc/qdocdatabase.cpp @@ -52,45 +52,378 @@ QT_BEGIN_NAMESPACE static NodeMap emptyNodeMap_; static NodeMultiMap emptyNodeMultiMap_; +/*! \class QDocForest + This class manages a collection of trees. Each tree is an + instance of class Tree, which is a private class. + + The forest is populated as each index file is loaded. + Each index file adds a tree to the forest. Each tree + is named with the name of the module it represents. + + The search order is created by searchOrder(), if it has + not already been created. The search order and module + names arrays have parallel structure, i.e. modulNames_[i] + is the module name of the Tree at searchOrder_[i]. + */ + +/*! + Destroys the qdoc forest. This requires deleting + each Tree in the forest. Note that the forest has + been transferred into the search order array, so + what is really being used to destroy the forest + is the search order array. + */ +QDocForest::~QDocForest() +{ + for (int i=0; iroot() : 0); +} + +/*! + Increments the forest's current tree index. If the current + tree index is still within the forest, the function returns + the root node of the current tree. Otherwise it returns 0. + */ +NamespaceNode* QDocForest::nextRoot() +{ + ++currentIndex_; + return (currentIndex_ < searchOrder_.size() ? searchOrder_[currentIndex_]->root() : 0); +} + +/*! + Initializes the forest prior to a traversal and + returns a pointer to the primary tree. If the + forest is empty, it returns 0. + */ +Tree* QDocForest::firstTree() +{ + currentIndex_ = 0; + return (!searchOrder_.isEmpty() ? searchOrder_[0] : 0); +} + +/*! + Increments the forest's current tree index. If the current + tree index is still within the forest, the function returns + the pointer to the current tree. Otherwise it returns 0. + */ +Tree* QDocForest::nextTree() +{ + ++currentIndex_; + return (currentIndex_ < searchOrder_.size() ? searchOrder_[currentIndex_] : 0); +} + +/*! + \fn Tree* QDocForest::primaryTree() + + Returns the pointer to the primary tree. + */ + +/*! + If the search order array is empty, create the search order. + If the search order array is not empty, do nothing. + */ +void QDocForest::setSearchOrder() +{ + if (!searchOrder_.isEmpty()) + return; + QString primaryName = primaryTree()->moduleName(); + searchOrder_.clear(); + searchOrder_.reserve(forest_.size()+1); + moduleNames_.reserve(forest_.size()+1); + searchOrder_.append(primaryTree_); + moduleNames_.append(primaryName); + QMap::iterator i; + if (primaryName != "QtCore") { + i = forest_.find("QtCore"); + if (i != forest_.end()) { + searchOrder_.append(i.value()); + moduleNames_.append("QtCore"); + forest_.erase(i); + } + } + if (primaryName != "QtGui") { + i = forest_.find("QtGui"); + if (i != forest_.end()) { + searchOrder_.append(i.value()); + moduleNames_.append("QtGui"); + forest_.erase(i); + } + } + if (primaryName != "QtNetwork") { + i = forest_.find("QtNetwork"); + if (i != forest_.end()) { + searchOrder_.append(i.value()); + moduleNames_.append("QtNetwork"); + forest_.erase(i); + } + } + if (primaryName != "QtOpenGL") { + i = forest_.find("QtOpenGL"); + if (i != forest_.end()) { + searchOrder_.append(i.value()); + moduleNames_.append("QtOpenGL"); + forest_.erase(i); + } + } + if (primaryName != "QtWidgets") { + i = forest_.find("QtWidgets"); + if (i != forest_.end()) { + searchOrder_.append(i.value()); + moduleNames_.append("QtWidgets"); + forest_.erase(i); + } + } + if (primaryName != "QtSql") { + i = forest_.find("QtSql"); + if (i != forest_.end()) { + searchOrder_.append(i.value()); + moduleNames_.append("QtSql"); + forest_.erase(i); + } + } + if (primaryName != "QtXml") { + i = forest_.find("QtXml"); + if (i != forest_.end()) { + searchOrder_.append(i.value()); + moduleNames_.append("QtXml"); + forest_.erase(i); + } + } + if (primaryName != "QtSvg") { + i = forest_.find("QtSvg"); + if (i != forest_.end()) { + searchOrder_.append(i.value()); + moduleNames_.append("QtSvg"); + forest_.erase(i); + } + } + if (primaryName != "QtDoc") { + i = forest_.find("QtDoc"); + if (i != forest_.end()) { + searchOrder_.append(i.value()); + moduleNames_.append("QtDoc"); + forest_.erase(i); + } + } + if (primaryName != "QtQuick") { + i = forest_.find("QtQuick"); + if (i != forest_.end()) { + searchOrder_.append(i.value()); + moduleNames_.append("QtQuick"); + forest_.erase(i); + } + } + if (primaryName != "QtQml") { + i = forest_.find("QtQml"); + if (i != forest_.end()) { + searchOrder_.append(i.value()); + moduleNames_.append("QtQml"); + forest_.erase(i); + } + } + if (primaryName != "QtPrintSupport") { + i = forest_.find("QtPrintSupport"); + if (i != forest_.end()) { + searchOrder_.append(i.value()); + moduleNames_.append("QtPrintSupport"); + forest_.erase(i); + } + } + if (primaryName != "QtGraphicalEffects") { + i = forest_.find("QtGraphicalEffects"); + if (i != forest_.end()) { + searchOrder_.append(i.value()); + moduleNames_.append("QtGraphicalEffects"); + forest_.erase(i); + } + } + if (primaryName != "QtConcurrent") { + i = forest_.find("QtConcurrent"); + if (i != forest_.end()) { + searchOrder_.append(i.value()); + moduleNames_.append("QtConcurrent"); + forest_.erase(i); + } + } +#if 0 + if (primaryName != "zzz") { + i = forest_.find("zzz"); + if (i != forest_.end()) { + searchOrder_.append(i.value()); + moduleNames_.append("zzz"); + forest_.erase(i); + } + } +#endif + /* + If any trees remain in the forest, just add them + to the search order sequentially, because we don't + know any better at this point. + */ + if (!forest_.isEmpty()) { + i = forest_.begin(); + while (i != forest_.end()) { + searchOrder_.append(i.value()); + moduleNames_.append(i.key()); + ++i; + } + forest_.clear(); + } +#if 0 + qDebug() << " SEARCH ORDER:"; + for (int i=0; i& QDocForest::searchOrder() +{ + if (searchOrder_.isEmpty()) + setSearchOrder(); + return searchOrder_; +} + +/*! + Create a new Tree for the index file for the specified + \a module and add it to the forest. Return the pointer + to its root. + */ +NamespaceNode* QDocForest::newIndexTree(const QString& module) +{ + primaryTree_ = new Tree(module, qdb_); + forest_.insert(module, primaryTree_); + return primaryTree_->root(); +} + +/*! + Create a new Tree for use as the primary tree. This tree + will represent the primary module. + */ +void QDocForest::newPrimaryTree(const QString& module) +{ + primaryTree_ = new Tree(module, qdb_); +} + +/*! + Searches the Tree \a t for a node named \a target and returns + a pointer to it if found. The \a relative node is the starting + point, but it only makes sense in the primary tree. Therefore, + when this function is called with \a t being an index tree, + \a relative is 0. When relative is 0, the root node of \a t is + the starting point. + */ +const Node* QDocForest::resolveTargetHelper(const QString& target, + const Node* relative, + Tree* t) +{ + const Node* node = 0; + if (target.endsWith("()")) { + QString funcName = target; + funcName.chop(2); + QStringList path = funcName.split("::"); + const FunctionNode* fn = t->findFunctionNode(path, relative, SearchBaseClasses); + if (fn && fn->metaness() != FunctionNode::MacroWithoutParams) + node = fn; + } + else { + QStringList path = target.split("::"); + int flags = SearchBaseClasses | SearchEnumValues | NonFunction; + node = t->findNode(path, relative, flags); + if (!node) { + QStringList path = target.split("::"); + const FunctionNode* fn = t->findFunctionNode(path, relative, SearchBaseClasses); + if (fn && fn->metaness() != FunctionNode::MacroWithoutParams) + node = fn; + } + } + return node; +} + +/*! + Searches the Tree \a t for a type node named by the \a path + and returns a pointer to it if found. The \a relative node + is the starting point, but it only makes sense when searching + the primary tree. Therefore, when this function is called with + \a t being an index tree, \a relative is 0. When relative is 0, + the root node of \a t is the starting point. + */ +const Node* QDocForest::resolveTypeHelper(const QStringList& path, const Node* relative, Tree* t) +{ + int flags = SearchBaseClasses | SearchEnumValues | NonFunction; + return t->findNode(path, relative, flags); +} + /*! \class QDocDatabase + This class provides exclusive access to the qdoc database, + which consists of a forrest of trees and a lot of maps and + other useful data structures. */ QDocDatabase* QDocDatabase::qdocDB_ = NULL; +NodeMap QDocDatabase::typeNodeMap_; /*! - Constructs the singleton qdoc database object. - It constructs a singleton Tree object with this - qdoc database pointer. + Constructs the singleton qdoc database object. The singleton + constructs the \a forest_ object, which is also a singleton. + \a showInternal_ is normally false. If it is true, qdoc will + write documentation for nodes marked \c internal. */ -QDocDatabase::QDocDatabase() : showInternal_(false) +QDocDatabase::QDocDatabase() : showInternal_(false), forest_(this) { - tree_ = new Tree(this); + // nothing } /*! - Destroys the qdoc database object. This requires deleting - the tree of nodes, which deletes each node. + Destroys the qdoc database object. This requires destroying + the forest object, which contains an array of tree pointers. + Each tree is deleted. */ QDocDatabase::~QDocDatabase() { masterMap_.clear(); - delete tree_; } -/*! \fn Tree* QDocDatabase::tree() - Returns the pointer to the tree. This function is for compatibility - with the current qdoc. It will be removed when the QDocDatabase class - replaces the current structures. - */ - /*! Creates the singleton. Allows only one instance of the class to be created. Returns a pointer to the singleton. */ QDocDatabase* QDocDatabase::qdocDB() { - if (!qdocDB_) + if (!qdocDB_) { qdocDB_ = new QDocDatabase; + initializeDB(); + } return qdocDB_; } @@ -105,6 +438,166 @@ void QDocDatabase::destroyQdocDB() } } +/*! + Initialize data structures in the singleton qdoc database. + + In particular, the type node map is initialized with a lot + type names that don't refer to documented types. For example, + the C++ standard types are included. These might be documented + here at some point, but for now they are not. Other examples + include \c array and \c data, which are just generic names + used as place holders in function signatures that appear in + the documentation. + */ +void QDocDatabase::initializeDB() +{ + typeNodeMap_.insert( "accepted", 0); + typeNodeMap_.insert( "actionPerformed", 0); + typeNodeMap_.insert( "activated", 0); + typeNodeMap_.insert( "alias", 0); + typeNodeMap_.insert( "anchors", 0); + typeNodeMap_.insert( "any", 0); + typeNodeMap_.insert( "array", 0); + typeNodeMap_.insert( "autoSearch", 0); + typeNodeMap_.insert( "axis", 0); + typeNodeMap_.insert( "backClicked", 0); + typeNodeMap_.insert( "bool", 0); + typeNodeMap_.insert( "boomTime", 0); + typeNodeMap_.insert( "border", 0); + typeNodeMap_.insert( "buttonClicked", 0); + typeNodeMap_.insert( "callback", 0); + typeNodeMap_.insert( "char", 0); + typeNodeMap_.insert( "clicked", 0); + typeNodeMap_.insert( "close", 0); + typeNodeMap_.insert( "closed", 0); + typeNodeMap_.insert( "color", 0); + typeNodeMap_.insert( "cond", 0); + typeNodeMap_.insert( "data", 0); + typeNodeMap_.insert( "dataReady", 0); + typeNodeMap_.insert( "dateString", 0); + typeNodeMap_.insert( "dateTimeString", 0); + typeNodeMap_.insert( "datetime", 0); + typeNodeMap_.insert( "day", 0); + typeNodeMap_.insert( "deactivated", 0); + typeNodeMap_.insert( "double", 0); + typeNodeMap_.insert( "drag", 0); + typeNodeMap_.insert( "easing", 0); + typeNodeMap_.insert( "enumeration", 0); + typeNodeMap_.insert( "error", 0); + typeNodeMap_.insert( "exposure", 0); + typeNodeMap_.insert( "fatalError", 0); + typeNodeMap_.insert( "fileSelected", 0); + typeNodeMap_.insert( "flags", 0); + typeNodeMap_.insert( "float", 0); + typeNodeMap_.insert( "focus", 0); + typeNodeMap_.insert( "focusZone", 0); + typeNodeMap_.insert( "format", 0); + typeNodeMap_.insert( "framePainted", 0); + typeNodeMap_.insert( "from", 0); + typeNodeMap_.insert( "frontClicked", 0); + typeNodeMap_.insert( "function", 0); + typeNodeMap_.insert( "hasOpened", 0); + typeNodeMap_.insert( "hovered", 0); + typeNodeMap_.insert( "hoveredTitle", 0); + typeNodeMap_.insert( "hoveredUrl", 0); + typeNodeMap_.insert( "imageCapture", 0); + typeNodeMap_.insert( "imageProcessing", 0); + typeNodeMap_.insert( "index", 0); + typeNodeMap_.insert( "initialized", 0); + typeNodeMap_.insert( "int", 0); + typeNodeMap_.insert( "isLoaded", 0); + typeNodeMap_.insert( "item", 0); + typeNodeMap_.insert( "jsdict", 0); + typeNodeMap_.insert( "jsobject", 0); + typeNodeMap_.insert( "key", 0); + typeNodeMap_.insert( "keysequence", 0); + typeNodeMap_.insert( "list", 0); + typeNodeMap_.insert( "listViewClicked", 0); + typeNodeMap_.insert( "loadRequest", 0); + typeNodeMap_.insert( "locale", 0); + typeNodeMap_.insert( "location", 0); + typeNodeMap_.insert( "long", 0); + typeNodeMap_.insert( "message", 0); + typeNodeMap_.insert( "messageReceived", 0); + typeNodeMap_.insert( "mode", 0); + typeNodeMap_.insert( "month", 0); + typeNodeMap_.insert( "name", 0); + typeNodeMap_.insert( "number", 0); + typeNodeMap_.insert( "object", 0); + typeNodeMap_.insert( "offset", 0); + typeNodeMap_.insert( "ok", 0); + typeNodeMap_.insert( "openCamera", 0); + typeNodeMap_.insert( "openImage", 0); + typeNodeMap_.insert( "openVideo", 0); + typeNodeMap_.insert( "padding", 0); + typeNodeMap_.insert( "parent", 0); + typeNodeMap_.insert( "path", 0); + typeNodeMap_.insert( "photoModeSelected", 0); + typeNodeMap_.insert( "position", 0); + typeNodeMap_.insert( "precision", 0); + typeNodeMap_.insert( "presetClicked", 0); + typeNodeMap_.insert( "preview", 0); + typeNodeMap_.insert( "previewSelected", 0); + typeNodeMap_.insert( "progress", 0); + typeNodeMap_.insert( "puzzleLost", 0); + typeNodeMap_.insert( "qmlSignal", 0); + typeNodeMap_.insert( "real", 0); + typeNodeMap_.insert( "rectangle", 0); + typeNodeMap_.insert( "request", 0); + typeNodeMap_.insert( "requestId", 0); + typeNodeMap_.insert( "section", 0); + typeNodeMap_.insert( "selected", 0); + typeNodeMap_.insert( "send", 0); + typeNodeMap_.insert( "settingsClicked", 0); + typeNodeMap_.insert( "shoe", 0); + typeNodeMap_.insert( "short", 0); + typeNodeMap_.insert( "signed", 0); + typeNodeMap_.insert( "sizeChanged", 0); + typeNodeMap_.insert( "size_t", 0); + typeNodeMap_.insert( "sockaddr", 0); + typeNodeMap_.insert( "someOtherSignal", 0); + typeNodeMap_.insert( "sourceSize", 0); + typeNodeMap_.insert( "startButtonClicked", 0); + typeNodeMap_.insert( "state", 0); + typeNodeMap_.insert( "std::initializer_list", 0); + typeNodeMap_.insert( "std::list", 0); + typeNodeMap_.insert( "std::map", 0); + typeNodeMap_.insert( "std::pair", 0); + typeNodeMap_.insert( "std::string", 0); + typeNodeMap_.insert( "std::vector", 0); + typeNodeMap_.insert( "string", 0); + typeNodeMap_.insert( "stringlist", 0); + typeNodeMap_.insert( "swapPlayers", 0); + typeNodeMap_.insert( "symbol", 0); + typeNodeMap_.insert( "t", 0); + typeNodeMap_.insert( "T", 0); + typeNodeMap_.insert( "tagChanged", 0); + typeNodeMap_.insert( "timeString", 0); + typeNodeMap_.insert( "timeout", 0); + typeNodeMap_.insert( "to", 0); + typeNodeMap_.insert( "toggled", 0); + typeNodeMap_.insert( "type", 0); + typeNodeMap_.insert( "unsigned", 0); + typeNodeMap_.insert( "urllist", 0); + typeNodeMap_.insert( "va_list", 0); + typeNodeMap_.insert( "value", 0); + typeNodeMap_.insert( "valueEmitted", 0); + typeNodeMap_.insert( "videoFramePainted", 0); + typeNodeMap_.insert( "videoModeSelected", 0); + typeNodeMap_.insert( "videoRecorder", 0); + typeNodeMap_.insert( "void", 0); + typeNodeMap_.insert( "volatile", 0); + typeNodeMap_.insert( "wchar_t", 0); + typeNodeMap_.insert( "x", 0); + typeNodeMap_.insert( "y", 0); + typeNodeMap_.insert( "zoom", 0); + typeNodeMap_.insert( "zoomTo", 0); +} + +/*! \fn NamespaceNode* QDocDatabase::primaryTreeRoot() + Returns a pointer to the root node of the primary tree. + */ + /*! \fn const DocNodeMap& QDocDatabase::groups() const Returns a const reference to the collection of all @@ -148,7 +641,7 @@ DocNode* QDocDatabase::findGroup(const QString& name) DocNodeMap::const_iterator i = groups_.find(name); if (i != groups_.end()) return i.value(); - DocNode* dn = new DocNode(tree_->root(), name, Node::Group, Node::OverviewPage); + DocNode* dn = new DocNode(primaryTreeRoot(), name, Node::Group, Node::OverviewPage); dn->markNotSeen(); groups_.insert(name,dn); if (!masterMap_.contains(name,dn)) @@ -169,7 +662,7 @@ DocNode* QDocDatabase::findModule(const QString& name) DocNodeMap::const_iterator i = modules_.find(name); if (i != modules_.end()) return i.value(); - DocNode* dn = new DocNode(tree_->root(), name, Node::Module, Node::OverviewPage); + DocNode* dn = new DocNode(primaryTreeRoot(), name, Node::Module, Node::OverviewPage); dn->markNotSeen(); modules_.insert(name,dn); if (!masterMap_.contains(name,dn)) @@ -190,7 +683,7 @@ QmlModuleNode* QDocDatabase::findQmlModule(const QString& name) if (qmlModules_.contains(name)) return static_cast(qmlModules_.value(name)); - QmlModuleNode* qmn = new QmlModuleNode(tree_->root(), name); + QmlModuleNode* qmn = new QmlModuleNode(primaryTreeRoot(), name); qmn->markNotSeen(); qmn->setQmlModuleInfo(name); qmlModules_.insert(name, qmn); @@ -321,13 +814,13 @@ void QDocDatabase::addToQmlModule(const QString& name, Node* node) If the QML module id is empty, it looks up the QML type by \a name only. */ -QmlClassNode* QDocDatabase::findQmlType(const QString& qmid, const QString& name) const +QmlClassNode* QDocDatabase::findQmlType(const QString& qmid, const QString& name) { if (!qmid.isEmpty()) return qmlTypeMap_.value(qmid + "::" + name); QStringList path(name); - Node* n = tree_->findNodeByNameAndType(path, Node::Document, Node::QmlClass, 0, true); + Node* n = forest_.findNodeByNameAndType(path, Node::Document, Node::QmlClass, true); if (n) { if (n->subType() == Node::QmlClass) return static_cast(n); @@ -390,6 +883,18 @@ void QDocDatabase::printQmlModules() const } } +/*! + This function calls \a func for each tree in the forest. + */ +void QDocDatabase::processForest(void (QDocDatabase::*func) (InnerNode*)) +{ + Tree* t = forest_.firstTree(); + while (t) { + (this->*(func))(t->root()); + t = forest_.nextTree(); + } +} + /*! Traverses the database to construct useful data structures for use when outputting certain significant collections of @@ -407,19 +912,27 @@ void QDocDatabase::buildCollections() serviceClasses_.clear(); qmlClasses_.clear(); + /* findAllClasses(treeRoot()); findAllFunctions(treeRoot()); findAllLegaleseTexts(treeRoot()); findAllNamespaces(treeRoot()); findAllSince(treeRoot()); findAllObsoleteThings(treeRoot()); + */ + processForest(&QDocDatabase::findAllClasses); + processForest(&QDocDatabase::findAllFunctions); + processForest(&QDocDatabase::findAllLegaleseTexts); + processForest(&QDocDatabase::findAllNamespaces); + processForest(&QDocDatabase::findAllSince); + processForest(&QDocDatabase::findAllObsoleteThings); } /*! Finds all the C++ class nodes and QML type nodes and sorts them into maps. */ -void QDocDatabase::findAllClasses(const InnerNode* node) +void QDocDatabase::findAllClasses(InnerNode* node) { NodeList::const_iterator c = node->childNodes().constBegin(); while (c != node->childNodes().constEnd()) { @@ -465,13 +978,13 @@ void QDocDatabase::findAllClasses(const InnerNode* node) /*! Finds all the function nodes */ -void QDocDatabase::findAllFunctions(const InnerNode* node) +void QDocDatabase::findAllFunctions(InnerNode* node) { NodeList::ConstIterator c = node->childNodes().constBegin(); while (c != node->childNodes().constEnd()) { if ((*c)->access() != Node::Private) { if ((*c)->isInnerNode()) { - findAllFunctions(static_cast(*c)); + findAllFunctions(static_cast(*c)); } else if ((*c)->type() == Node::Function) { const FunctionNode* func = static_cast(*c); @@ -491,7 +1004,7 @@ void QDocDatabase::findAllFunctions(const InnerNode* node) Finds all the nodes containing legalese text and puts them in a map. */ -void QDocDatabase::findAllLegaleseTexts(const InnerNode* node) +void QDocDatabase::findAllLegaleseTexts(InnerNode* node) { NodeList::ConstIterator c = node->childNodes().constBegin(); while (c != node->childNodes().constEnd()) { @@ -499,7 +1012,7 @@ void QDocDatabase::findAllLegaleseTexts(const InnerNode* node) if (!(*c)->doc().legaleseText().isEmpty()) legaleseTexts_.insertMulti((*c)->doc().legaleseText(), *c); if ((*c)->isInnerNode()) - findAllLegaleseTexts(static_cast(*c)); + findAllLegaleseTexts(static_cast(*c)); } ++c; } @@ -508,13 +1021,13 @@ void QDocDatabase::findAllLegaleseTexts(const InnerNode* node) /*! Finds all the namespace nodes and puts them in an index. */ -void QDocDatabase::findAllNamespaces(const InnerNode* node) +void QDocDatabase::findAllNamespaces(InnerNode* node) { NodeList::ConstIterator c = node->childNodes().constBegin(); while (c != node->childNodes().constEnd()) { if ((*c)->access() != Node::Private) { if ((*c)->isInnerNode()) { - findAllNamespaces(static_cast(*c)); + findAllNamespaces(static_cast(*c)); if ((*c)->type() == Node::Namespace) { // Ensure that the namespace's name is not empty (the root // namespace has no name). @@ -532,7 +1045,7 @@ void QDocDatabase::findAllNamespaces(const InnerNode* node) maps. They can be C++ classes, QML types, or they can be functions, enum types, typedefs, methods, etc. */ -void QDocDatabase::findAllObsoleteThings(const InnerNode* node) +void QDocDatabase::findAllObsoleteThings(InnerNode* node) { NodeList::const_iterator c = node->childNodes().constBegin(); while (c != node->childNodes().constEnd()) { @@ -627,7 +1140,7 @@ void QDocDatabase::findAllObsoleteThings(const InnerNode* node) This function is used for generating the "New Classes... in x.y" section on the \e{What's New in Qt x.y} page. */ -void QDocDatabase::findAllSince(const InnerNode* node) +void QDocDatabase::findAllSince(InnerNode* node) { NodeList::const_iterator child = node->childNodes().constBegin(); while (child != node->childNodes().constEnd()) { @@ -742,46 +1255,37 @@ const NodeMultiMap& QDocDatabase::getSinceMap(const QString& key) const to generating documentation. */ void QDocDatabase::resolveIssues() { - resolveQmlInheritance(treeRoot()); - resolveTargets(treeRoot()); - tree_->resolveCppToQmlLinks(); + resolveQmlInheritance(primaryTreeRoot()); + resolveTargets(); + primaryTree()->resolveCppToQmlLinks(); } /*! - Searches the \a database for a node named \a target and returns - a pointer to it if found. + This function is called for autolinking to a \a type, + which could be a function return type or a parameter + type. The tree node that represents the \a type is + returned. All the trees are searched until a match is + found. When searching the primary tree, the search + begins at \a relative and proceeds up the parent chain. + When searching the index trees, the search begins at the + root. */ -const Node* QDocDatabase::resolveTarget(const QString& target, const Node* relative) +const Node* QDocDatabase::resolveType(const QString& type, const Node* relative) { - const Node* node = 0; - if (target.endsWith("()")) { - QString funcName = target; - funcName.chop(2); - QStringList path = funcName.split("::"); - const FunctionNode* fn = tree_->findFunctionNode(path, relative, SearchBaseClasses); - if (fn) { - /* - Why is this case not accepted? - */ - if (fn->metaness() != FunctionNode::MacroWithoutParams) - node = fn; - } + QStringList path = type.split("::"); + if ((path.size() == 1) && (path.at(0)[0].isLower() || path.at(0) == QString("T"))) { + NodeMap::iterator i = typeNodeMap_.find(path.at(0)); + if (i != typeNodeMap_.end()) + return i.value(); } - else if (target.contains(QLatin1Char('#'))) { - // This error message is never printed; I think we can remove the case. - qDebug() << "qdoc: target case not handled:" << target; - } - else { - QStringList path = target.split("::"); - int flags = SearchBaseClasses | SearchEnumValues | NonFunction; - node = tree_->findNode(path, relative, flags); - } - return node; + return forest_.resolveType(path, relative); } /*! Finds the node that will generate the documentation that contains the \a target and returns a pointer to it. + + Can this be improved by using the target map in Tree? */ const Node* QDocDatabase::findNodeForTarget(const QString& target, const Node* relative) { @@ -799,150 +1303,6 @@ const Node* QDocDatabase::findNodeForTarget(const QString& target, const Node* r 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, TargetRec::Type type, Node* node, int priority) -{ - TargetRec target; - target.type_ = type; - target.node_ = node; - target.priority_ = priority; - Atom a = Atom(Atom::Target, name); - target.ref_ = refForAtom(&a); - targetRecMultiMap_.insert(name, target); -} - -/*! - This function searches for a \a target anchor node. If it - finds one, it sets \a ref and returns the found node. - */ -const Node* -QDocDatabase::findUnambiguousTarget(const QString& target, QString& ref, const Node* relative) -{ - TargetRec bestTarget; - int numBestTargets = 0; - QList bestTargetList; - - QString key = Doc::canonicalTitle(target); - TargetRecMultiMap::iterator i = targetRecMultiMap_.find(key); - while (i != targetRecMultiMap_.end()) { - if (i.key() != key) - break; - const TargetRec& candidate = i.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; - } - ++i; - } - if (numBestTargets > 0) { - if (numBestTargets == 1) { - ref = bestTarget.ref_; - return bestTarget.node_; - } - else if (bestTargetList.size() > 1) { - if (relative && !relative->qmlModuleName().isEmpty()) { - for (int i=0; iqmlModuleName() == n->qmlModuleName()) { - ref = bestTargetList.at(i).ref_; - return n; - } - } - } - } - } - ref.clear(); - 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 -{ - QString key = Doc::canonicalTitle(title); - DocNodeMultiMap::const_iterator i = docNodesByTitle_.constFind(key); - if (i != docNodesByTitle_.constEnd()) { - if (relative && !relative->qmlModuleName().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)->qmlModuleName() == relative->qmlModuleName()) { - /* - By returning here, we avoid printing - all the duplicate header warnings, - which are not really duplicates now, - because of the QML module name 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. - */ - DocNodeMultiMap::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()); - break; // Just report one duplicate for now. - } - ++j; - } - if (internalLocations.size() > 0) { - i.value()->location().warning(tr("This page title 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. If the node it finds is \a node, - it returns the ref from that node. Otherwise it returns an - empty string. - */ -QString QDocDatabase::findTarget(const QString& target, const Node* node) const -{ - QString key = Doc::canonicalTitle(target); - TargetRecMultiMap::const_iterator i = targetRecMultiMap_.constFind(key); - - if (i != targetRecMultiMap_.constEnd()) { - do { - if (i.value().node_ == node) - return i.value().ref_; - ++i; - } while (i != targetRecMultiMap_.constEnd() && i.key() == key); - } - return QString(); -} - /*! For each QML Type node in the tree beginning at \a root, if it has a QML base type name but its QML base type node @@ -952,7 +1312,7 @@ QString QDocDatabase::findTarget(const QString& target, const Node* node) const */ void QDocDatabase::resolveQmlInheritance(InnerNode* root) { - // Dop we need recursion? + // Do we need recursion? foreach (Node* child, root->childNodes()) { if (child->type() == Node::Document && child->subType() == Node::QmlClass) { QmlClassNode* qcn = static_cast(child); @@ -986,80 +1346,6 @@ void QDocDatabase::resolveQmlInheritance(InnerNode* root) } } -/*! - */ -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()) { - QString key = Doc::canonicalTitle(node->title()); - QList nodes = docNodesByTitle_.values(key); - bool alreadyThere = false; - if (!nodes.empty()) { - for (int i=0; i< nodes.size(); ++i) { - if (nodes[i]->subType() == Node::ExternalPage) { - if (node->name() == nodes[i]->name()) { - alreadyThere = true; - break; - } - } - } - } - if (!alreadyThere) { - docNodesByTitle_.insert(key, node); - } - } - if (node->subType() == Node::Collision) { - resolveTargets(node); - } - } - - if (child->doc().hasTableOfContents()) { - const QList& toc = child->doc().tableOfContents(); - TargetRec target; - target.node_ = child; - target.priority_ = 3; - - for (int i = 0; i < toc.size(); ++i) { - target.ref_ = refForAtom(toc.at(i)); - QString title = Text::sectionHeading(toc.at(i)).toString(); - if (!title.isEmpty()) { - QString key = Doc::canonicalTitle(title); - targetRecMultiMap_.insert(key, target); - } - } - } - if (child->doc().hasKeywords()) { - const QList& keywords = child->doc().keywords(); - TargetRec target; - target.node_ = child; - target.priority_ = 1; - - for (int i = 0; i < keywords.size(); ++i) { - target.ref_ = refForAtom(keywords.at(i)); - QString key = Doc::canonicalTitle(keywords.at(i)->string()); - targetRecMultiMap_.insert(key, target); - } - } - if (child->doc().hasTargets()) { - const QList& toc = child->doc().targets(); - TargetRec target; - target.node_ = child; - target.priority_ = 2; - - for (int i = 0; i < toc.size(); ++i) { - target.ref_ = refForAtom(toc.at(i)); - QString key = Doc::canonicalTitle(toc.at(i)->string()); - targetRecMultiMap_.insert(key, target); - } - } - } -} - /*! Generates a tag file and writes it to \a name. */ @@ -1095,17 +1381,6 @@ void QDocDatabase::generateIndex(const QString& fileName, QDocIndexFiles::destroyQDocIndexFiles(); } -QString QDocDatabase::refForAtom(const Atom* atom) -{ - if (atom) { - if (atom->type() == Atom::SectionLeft) - return Doc::canonicalTitle(Text::sectionHeading(atom).toString()); - if (atom->type() == Atom::Target) - return Doc::canonicalTitle(atom->string()); - } - return QString(); -} - /*! If there are open namespaces, search for the function node having the same function name as the \a clone node in each @@ -1135,20 +1410,29 @@ FunctionNode* QDocDatabase::findNodeInOpenNamespace(const QStringList& parentPat /*! Find a node of the specified \a type and \a subtype that is - reached with the specified \a path. If such a node is found - in an open namespace, prefix \a path with the name of the - open namespace and "::" and return a pointer to the node. - Othewrwise return 0. + reached with the specified \a path qualified with the name + of one of the open namespaces (might not be any open ones). + If the node is found in an open namespace, prefix \a path + with the name of the open namespace and "::" and return a + pointer to the node. Othewrwise return 0. + + This function only searches in the current primary tree. */ Node* QDocDatabase::findNodeInOpenNamespace(QStringList& path, Node::Type type, Node::SubType subtype) { + if (path.isEmpty()) + return 0; Node* n = 0; if (!openNamespaces_.isEmpty()) { foreach (const QString& t, openNamespaces_) { - QStringList p = t.split("::") + path; - n = findNodeByNameAndType(p, type, subtype); + QStringList p; + if (t != path[0]) + p = t.split("::") + path; + else + p = path; + n = primaryTree()->findNodeByNameAndType(p, type, subtype); if (n) { path = p; break; diff --git a/src/tools/qdoc/qdocdatabase.h b/src/tools/qdoc/qdocdatabase.h index 4e50396817..8541f03106 100644 --- a/src/tools/qdoc/qdocdatabase.h +++ b/src/tools/qdoc/qdocdatabase.h @@ -45,6 +45,8 @@ #include #include #include "tree.h" +#include "config.h" +#include QT_BEGIN_NAMESPACE @@ -54,10 +56,10 @@ typedef QMap NodeMapMap; typedef QMap NodeMultiMapMap; typedef QMultiMap QDocMultiMap; typedef QMap TextToNodeMap; -typedef QMultiMap DocNodeMultiMap; class Atom; class Generator; +class QDocDatabase; enum FindFlag { SearchBaseClasses = 0x1, @@ -65,20 +67,159 @@ enum FindFlag { NonFunction = 0x4 }; -struct TargetRec +class QDocForest { public: - enum Type { Unknown, Target, Keyword, Contents, Class, Function, Page, Subtitle }; - TargetRec() : node_(0), priority_(INT_MAX), type_(Unknown) { } - bool isEmpty() const { return ref_.isEmpty(); } - //void debug(int idx, const QString& key); - Node* node_; - QString ref_; - int priority_; - Type type_; -}; -typedef QMultiMap TargetRecMultiMap; + friend class QDocDatabase; + QDocForest(QDocDatabase* qdb) + : qdb_(qdb), primaryTree_(0), currentIndex_(0) { } + ~QDocForest(); + + NamespaceNode* firstRoot(); + NamespaceNode* nextRoot(); + Tree* firstTree(); + Tree* nextTree(); + Tree* primaryTree() { return primaryTree_; } + NamespaceNode* primaryTreeRoot() { return (primaryTree_ ? primaryTree_->root() : 0); } + bool isEmpty() const { return searchOrder_.isEmpty(); } + bool done() const { return (currentIndex_ >= searchOrder_.size()); } + const QVector& searchOrder(); + void setSearchOrder(); + + const Node* findNode(const QStringList& path, const Node* relative, int findFlags) { + foreach (Tree* t, searchOrder_) { + const Node* n = t->findNode(path, relative, findFlags); + if (n) { + return n; + } + relative = 0; + } + if (Config::debug_) + qDebug() << "FAILED SEARCH 1" << path; + return 0; + } + Node* findNodeByNameAndType(const QStringList& path, + Node::Type type, + Node::SubType subtype, + bool acceptCollision = false) { + foreach (Tree* t, searchOrder_) { + Node* n = t->findNodeByNameAndType(path, type, subtype, acceptCollision); + if (n) { + return n; + } + } + if (Config::debug_) + qDebug() << "FAILED SEARCH 2" << path << type << subtype; + return 0; + } + + ClassNode* findClassNode(const QStringList& path) { + foreach (Tree* t, searchOrder_) { + ClassNode* n = t->findClassNode(path); + if (n) { + return n; + } + } + if (Config::debug_) + qDebug() << "FAILED SEARCH 3" << path; + return 0; + } + + InnerNode* findRelatesNode(const QStringList& path) { + foreach (Tree* t, searchOrder_) { + InnerNode* n = t->findRelatesNode(path); + if (n) { + return n; + } + } + if (Config::debug_) + qDebug() << "FAILED SEARCH 4" << path; + return 0; + } + + const Node* resolveTarget(const QString& target, const Node* relative) { + const Node* r = relative; + foreach (Tree* t, searchOrder_) { + const Node* n = resolveTargetHelper(target, relative, t); + if (n) { + return n; + } + relative = 0; + } + if (Config::debug_) { + qDebug() << "FAILED SEARCH 6" << target << r; + } + return 0; + } + + const Node* resolveType(const QStringList& path, const Node* relative) + { + foreach (Tree* t, searchOrder_) { + const Node* n = resolveTypeHelper(path, relative, t); + if (n) { + return n; + } + relative = 0; + } + if (Config::debug_) + qDebug() << "FAILED SEARCH 5" << path; + return 0; + } + + QString findTarget(const QString& target, const Node* node) const + { + foreach (Tree* t, searchOrder_) { + QString ref = t->findTarget(target, node); + if (!ref.isEmpty()) { + return ref; + } + } + if (Config::debug_) + qDebug() << "FAILED SEARCH 7" << target; + return QString(); + } + + const Node* findUnambiguousTarget(const QString& target, QString& ref, const Node* relative) + { + foreach (Tree* t, searchOrder_) { + const Node* n = t->findUnambiguousTarget(target, ref, relative); + if (n) { + return n; + } + } + if (Config::debug_) + qDebug() << "FAILED SEARCH 8" << target; + return 0; + } + + const DocNode* findDocNodeByTitle(const QString& title, const Node* relative) const + { + foreach (Tree* t, searchOrder_) { + const DocNode* n = t->findDocNodeByTitle(title, relative); + if (n) { + return n; + } + } + if (Config::debug_) + qDebug() << "FAILED SEARCH 9" << title; + return 0; + } + + private: + void newPrimaryTree(const QString& module); + NamespaceNode* newIndexTree(const QString& module); + const Node* resolveTargetHelper(const QString& target, const Node* relative, Tree* t); + const Node* resolveTypeHelper(const QStringList& path, const Node* relative, Tree* t); + + private: + QDocDatabase* qdb_; + Tree* primaryTree_; + int currentIndex_; + QMap forest_; + QVector searchOrder_; + QVector moduleNames_; +}; class QDocDatabase { @@ -90,8 +231,8 @@ class QDocDatabase ~QDocDatabase(); const DocNodeMap& groups() const { return groups_; } - const DocNodeMap& modules() const { return modules_; } - const DocNodeMap& qmlModules() const { return qmlModules_; } + //const DocNodeMap& modules() const { return modules_; } // not used + //const DocNodeMap& qmlModules() const { return qmlModules_; } // not used DocNode* getGroup(const QString& name); DocNode* findGroup(const QString& name); @@ -106,15 +247,15 @@ class QDocDatabase DocNode* addToModule(const QString& name, Node* node); void addToQmlModule(const QString& name, Node* node); - QmlClassNode* findQmlType(const QString& qmid, const QString& name) const; + QmlClassNode* findQmlType(const QString& qmid, const QString& name); QmlClassNode* findQmlType(const ImportRec& import, const QString& name) const; - void findAllClasses(const InnerNode *node); - void findAllFunctions(const InnerNode *node); - void findAllLegaleseTexts(const InnerNode *node); - void findAllNamespaces(const InnerNode *node); - void findAllObsoleteThings(const InnerNode* node); - void findAllSince(const InnerNode *node); + void findAllClasses(InnerNode *node); + void findAllFunctions(InnerNode *node); + void findAllLegaleseTexts(InnerNode *node); + void findAllNamespaces(InnerNode *node); + void findAllObsoleteThings(InnerNode* node); + void findAllSince(InnerNode *node); void buildCollections(); // special collection access functions @@ -137,57 +278,63 @@ class QDocDatabase /* convenience functions Many of these will be either eliminated or replaced. */ - QString refForAtom(const Atom* atom); - Tree* tree() { return tree_; } - NamespaceNode* treeRoot() { return tree_->root(); } - void resolveInheritance() { tree_->resolveInheritance(); } + void resolveInheritance() { primaryTree()->resolveInheritance(); } void resolveQmlInheritance(InnerNode* root); void resolveIssues(); - void fixInheritance() { tree_->fixInheritance(); } - void resolveProperties() { tree_->resolveProperties(); } + void fixInheritance() { primaryTree()->fixInheritance(); } + void resolveProperties() { primaryTree()->resolveProperties(); } - /******************************************************************* - The functions declared below don't search in the tree(s). - ********************************************************************/ - QString findTarget(const QString &target, const Node *node) const; - void resolveTargets(InnerNode* root); - void insertTarget(const QString& name, TargetRec::Type type, Node* node, int priority); - /*******************************************************************/ + void resolveTargets() { + primaryTree()->resolveTargets(primaryTreeRoot()); + } + void insertTarget(const QString& name, TargetRec::Type type, Node* node, int priority) { + primaryTree()->insertTarget(name, type, node, priority); + } /******************************************************************* The functions declared below are called for the current tree only. ********************************************************************/ FunctionNode* findFunctionNode(const QStringList& parentPath, const FunctionNode* clone) { - return tree_->findFunctionNode(parentPath, clone); + return primaryTree()->findFunctionNode(parentPath, clone); } FunctionNode* findNodeInOpenNamespace(const QStringList& parentPath, const FunctionNode* clone); Node* findNodeInOpenNamespace(QStringList& path, Node::Type type, Node::SubType subtype); - NameCollisionNode* findCollisionNode(const QString& name) const { - return tree_->findCollisionNode(name); + NameCollisionNode* findCollisionNode(const QString& name) { + return primaryTree()->findCollisionNode(name); + } + NameCollisionNode* checkForCollision(const QString& name) { + return primaryTree()->checkForCollision(name); } /*******************************************************************/ /******************************************************************* The functions declared below are called for all trees. ********************************************************************/ - ClassNode* findClassNode(const QStringList& path) { return tree_->findClassNode(path); } - InnerNode* findRelatesNode(const QStringList& path) { return tree_->findRelatesNode(path); } - const Node* resolveTarget(const QString& target, const Node* relative); + ClassNode* findClassNode(const QStringList& path) { return forest_.findClassNode(path); } + InnerNode* findRelatesNode(const QStringList& path) { return forest_.findRelatesNode(path); } + QString findTarget(const QString& target, const Node* node) const { + return forest_.findTarget(target, node); + } + const Node* resolveTarget(const QString& target, const Node* relative) { + return forest_.resolveTarget(target, relative); + } + const Node* resolveType(const QString& type, const Node* relative); const Node* findNodeForTarget(const QString& target, const Node* relative); - const DocNode* findDocNodeByTitle(const QString& title, const Node* relative = 0) const; - const Node* findUnambiguousTarget(const QString& target, QString& ref, const Node* relative); - Node* findNodeByNameAndType(const QStringList& path, Node::Type type, Node::SubType subtype){ - return tree_->findNodeByNameAndType(path, type, subtype, 0); + const DocNode* findDocNodeByTitle(const QString& title, const Node* relative = 0) const { + return forest_.findDocNodeByTitle(title, relative); + } + const Node* findUnambiguousTarget(const QString& target, QString& ref, const Node* relative) { + return forest_.findUnambiguousTarget(target, ref, relative); } - NameCollisionNode* checkForCollision(const QString& name) const { - return tree_->checkForCollision(name); + Node* findNodeByNameAndType(const QStringList& path, Node::Type type, Node::SubType subtype){ + return forest_.findNodeByNameAndType(path, type, subtype, false); } /*******************************************************************/ void addPropertyFunction(PropertyNode* property, const QString& funcName, PropertyNode::FunctionRole funcRole) { - tree_->addPropertyFunction(property, funcName, funcRole); + primaryTree()->addPropertyFunction(property, funcName, funcRole); } void setVersion(const QString& v) { version_ = v; } @@ -205,6 +352,14 @@ class QDocDatabase void insertOpenNamespace(const QString& path) { openNamespaces_.insert(path); } void setShowInternal(bool value) { showInternal_ = value; } + // Try to make this function private. + QDocForest& forest() { return forest_; } + NamespaceNode* primaryTreeRoot() { return forest_.primaryTreeRoot(); } + void newPrimaryTree(const QString& module) { forest_.newPrimaryTree(module); } + NamespaceNode* newIndexTree(const QString& module) { return forest_.newIndexTree(module); } + const QVector& searchOrder() { return forest_.searchOrder(); } + void setSearchOrder() { forest_.setSearchOrder(); } + /* debugging functions */ void printModules() const; void printQmlModules() const; @@ -213,17 +368,25 @@ class QDocDatabase friend class QDocIndexFiles; friend class QDocTagFiles; + const Node* findNode(const QStringList& path, const Node* relative, int findFlags) { + return forest_.findNode(path, relative, findFlags); + } + void processForest(void (QDocDatabase::*) (InnerNode*)); + static void initializeDB(); + private: QDocDatabase(); - QDocDatabase(QDocDatabase const& ) { }; // copy constructor is private - QDocDatabase& operator=(QDocDatabase const& ); // assignment operator is private + QDocDatabase(QDocDatabase const& ) : showInternal_(false), forest_(this) { } + QDocDatabase& operator=(QDocDatabase const& ); + Tree* primaryTree() { return forest_.primaryTree(); } private: static QDocDatabase* qdocDB_; + static NodeMap typeNodeMap_; bool showInternal_; QString version_; QDocMultiMap masterMap_; - Tree* tree_; + QDocForest forest_; DocNodeMap groups_; DocNodeMap modules_; DocNodeMap qmlModules_; @@ -244,8 +407,6 @@ class QDocDatabase NodeMultiMapMap newSinceMaps_; NodeMapMap funcIndex_; TextToNodeMap legaleseTexts_; - DocNodeMultiMap docNodesByTitle_; - TargetRecMultiMap targetRecMultiMap_; QSet openNamespaces_; }; diff --git a/src/tools/qdoc/qdocindexfiles.cpp b/src/tools/qdoc/qdocindexfiles.cpp index 55e580427f..8d33cdcdcd 100644 --- a/src/tools/qdoc/qdocindexfiles.cpp +++ b/src/tools/qdoc/qdocindexfiles.cpp @@ -77,6 +77,7 @@ QDocIndexFiles::QDocIndexFiles() QDocIndexFiles::~QDocIndexFiles() { qdb_ = 0; + gen_ = 0; } /*! @@ -109,10 +110,9 @@ void QDocIndexFiles::readIndexes(const QStringList& indexFiles) foreach (const QString& indexFile, indexFiles) { QString msg = "Loading index file: " + indexFile; Location::logToStdErr(msg); - //qDebug() << "READING INDEX:" << indexFile; + //qDebug() << " LOAD INDEX FILE:" << indexFile; readIndexFile(indexFile); } - //qDebug() << "DONE READING INDEX FILES"; } /*! @@ -146,12 +146,13 @@ void QDocIndexFiles::readIndexFile(const QString& path) basesList_.clear(); relatedList_.clear(); + NamespaceNode* root = qdb_->newIndexTree(project_); + // 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); + readIndexSection(child, root, indexUrl); child = child.nextSiblingElement(); } @@ -523,7 +524,7 @@ void QDocIndexFiles::readIndexSection(const QDomElement& element, QString moduleName = element.attribute("module"); if (!moduleName.isEmpty()) - node->setModuleName(moduleName); + qdb_->addToModule(moduleName, node); if (!href.isEmpty()) { if (node->isExternalPage()) node->setUrl(href); @@ -768,7 +769,7 @@ bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter& writer, QString objName = node->name(); // Special case: only the root node should have an empty name. - if (objName.isEmpty() && node != qdb_->treeRoot()) + if (objName.isEmpty() && node != qdb_->primaryTreeRoot()) return false; writer.writeStartElement(nodeName); @@ -1325,7 +1326,7 @@ void QDocIndexFiles::generateIndex(const QString& fileName, writer.writeAttribute("version", qdb_->version()); writer.writeAttribute("project", g->config()->getString(CONFIG_PROJECT)); - generateIndexSections(writer, qdb_->treeRoot(), generateInternalNodes); + generateIndexSections(writer, qdb_->primaryTreeRoot(), generateInternalNodes); /* We wait until the end of the index file to output the group elements. diff --git a/src/tools/qdoc/qdoctagfiles.cpp b/src/tools/qdoc/qdoctagfiles.cpp index 23c10c01b5..cc2bd3f1f0 100644 --- a/src/tools/qdoc/qdoctagfiles.cpp +++ b/src/tools/qdoc/qdoctagfiles.cpp @@ -147,7 +147,7 @@ void QDocTagFiles::generateTagFileCompounds(QXmlStreamWriter& writer, const Inne QString objName = node->name(); // Special case: only the root node should have an empty name. - if (objName.isEmpty() && node != qdb_->treeRoot()) + if (objName.isEmpty() && node != qdb_->primaryTreeRoot()) continue; // *** Write the starting tag for the element here. *** @@ -244,7 +244,7 @@ void QDocTagFiles::generateTagFileMembers(QXmlStreamWriter& writer, const InnerN QString objName = node->name(); // Special case: only the root node should have an empty name. - if (objName.isEmpty() && node != qdb_->treeRoot()) + if (objName.isEmpty() && node != qdb_->primaryTreeRoot()) continue; // *** Write the starting tag for the element here. *** @@ -373,7 +373,7 @@ void QDocTagFiles::generateTagFile(const QString& fileName, Generator* g) writer.setAutoFormatting(true); writer.writeStartDocument(); writer.writeStartElement("tagfile"); - generateTagFileCompounds(writer, qdb_->treeRoot()); + generateTagFileCompounds(writer, qdb_->primaryTreeRoot()); writer.writeEndElement(); // tagfile writer.writeEndDocument(); file.close(); diff --git a/src/tools/qdoc/qmlcodeparser.h b/src/tools/qdoc/qmlcodeparser.h index 71b4660fe7..7f6f8d1a81 100644 --- a/src/tools/qdoc/qmlcodeparser.h +++ b/src/tools/qdoc/qmlcodeparser.h @@ -58,7 +58,6 @@ QT_BEGIN_NAMESPACE class Config; class Node; class QString; -class Tree; class QmlCodeParser : public CodeParser { diff --git a/src/tools/qdoc/qmlvisitor.cpp b/src/tools/qdoc/qmlvisitor.cpp index ec1ef41256..d16fdfa5d4 100644 --- a/src/tools/qdoc/qmlvisitor.cpp +++ b/src/tools/qdoc/qmlvisitor.cpp @@ -97,7 +97,7 @@ QmlDocVisitor::QmlDocVisitor(const QString &filePath, this->engine = engine; this->commands_ = commands; this->topics_ = topics; - current = QDocDatabase::qdocDB()->treeRoot(); + current = QDocDatabase::qdocDB()->primaryTreeRoot(); } /*! diff --git a/src/tools/qdoc/tree.cpp b/src/tools/qdoc/tree.cpp index 98ac64246e..45faed8e2b 100644 --- a/src/tools/qdoc/tree.cpp +++ b/src/tools/qdoc/tree.cpp @@ -49,8 +49,6 @@ #include #include -bool Tree::debug_ = false; - QT_BEGIN_NAMESPACE /*! @@ -62,23 +60,35 @@ QT_BEGIN_NAMESPACE This class is now private. Only class QDocDatabase has access. Please don't change this. If you must access class Tree, do it though the pointer to the singleton QDocDatabase. + + Tree is being converted to a forest. A static member provides a + map of Tree* values with the module names as the keys. There is + one Tree in the map for each index file read, and there is one + tree that is not in the map for the module whose documentation + is being generated. */ /*! - Constructs the singleton tree. \a qdb is the pointer to the + Constructs a Tree. \a qdb is the pointer to the singleton qdoc database that is constructing the tree. This might not be necessary, and it might be removed later. */ -Tree::Tree(QDocDatabase* qdb) - : qdb_(qdb), root_(0, QString()) +Tree::Tree(const QString& module, QDocDatabase* qdb) + : module_(module), qdb_(qdb), root_(0, QString()) { + root_.setModuleName(module_); } /*! - Destroys the singleton Tree. + Destroys the Tree. The root node is a data member + of this object, so its destructor is called. The + destructor of each child node is called, and these + destructors are recursive. Thus the entire tree is + destroyed. */ Tree::~Tree() { + // nothing } /* API members */ @@ -163,7 +173,7 @@ QmlClassNode* Tree::findQmlTypeNode(const QStringList& path) node as its first child, and return a pointer to the new NameCollisionNode. Otherwise return 0. */ -NameCollisionNode* Tree::checkForCollision(const QString& name) const +NameCollisionNode* Tree::checkForCollision(const QString& name) { Node* n = const_cast(findNode(QStringList(name))); if (n) { @@ -235,7 +245,7 @@ const FunctionNode* Tree::findFunctionNode(const QStringList& path, if (i == path.size() - 1) next = ((InnerNode*) node)->findFunctionNode(path.at(i)); else - next = ((InnerNode*) node)->findChildNodeByName(path.at(i)); + next = ((InnerNode*) node)->findChildNode(path.at(i)); if (!next && node->type() == Node::Class && (findFlags & SearchBaseClasses)) { NodeList baseClasses = allBaseClasses(static_cast(node)); @@ -243,7 +253,7 @@ const FunctionNode* Tree::findFunctionNode(const QStringList& path, if (i == path.size() - 1) next = static_cast(baseClass)->findFunctionNode(path.at(i)); else - next = static_cast(baseClass)->findChildNodeByName(path.at(i)); + next = static_cast(baseClass)->findChildNode(path.at(i)); if (next) break; @@ -539,12 +549,9 @@ NodeList Tree::allBaseClasses(const ClassNode* classNode) const Node* Tree::findNodeByNameAndType(const QStringList& path, Node::Type type, Node::SubType subtype, - Node* start, bool acceptCollision) { - if (!start) - start = const_cast(root()); - Node* result = findNodeRecursive(path, 0, start, type, subtype, acceptCollision); + Node* result = findNodeRecursive(path, 0, root(), type, subtype, acceptCollision); return result; } @@ -745,14 +752,14 @@ const Node* Tree::findNode(const QStringList& path, const Node* start, int findF if (node == 0 || !node->isInnerNode()) break; - const Node* next = static_cast(node)->findChildNodeByName(path.at(i), qml); + const Node* next = static_cast(node)->findChildNode(path.at(i), qml); if (!next && (findFlags & SearchEnumValues) && i == path.size()-1) next = static_cast(node)->findEnumNodeForValue(path.at(i)); if (!next && !qml && node->type() == Node::Class && (findFlags & SearchBaseClasses)) { NodeList baseClasses = allBaseClasses(static_cast(node)); foreach (const Node* baseClass, baseClasses) { - next = static_cast(baseClass)->findChildNodeByName(path.at(i)); + next = static_cast(baseClass)->findChildNode(path.at(i)); if (!next && (findFlags & SearchEnumValues) && i == path.size() - 1) next = static_cast(baseClass)->findEnumNodeForValue(path.at(i)); if (next) @@ -776,4 +783,236 @@ const Node* Tree::findNode(const QStringList& path, const Node* start, int findF return 0; } +/*! + This function searches for a node with a canonical title + constructed from \a target. If the node it finds is \a node, + it returns the ref from that node. Otherwise it returns an + empty string. + */ +QString Tree::findTarget(const QString& target, const Node* node) const +{ + QString key = Doc::canonicalTitle(target); + TargetMap::const_iterator i = nodesByTarget_.constFind(key); + if (i != nodesByTarget_.constEnd()) { + do { + if (i.value().node_ == node) + return i.value().ref_; + ++i; + } while (i != nodesByTarget_.constEnd() && i.key() == key); + } + return QString(); +} + +/*! + Inserts a new target into the target table. \a name is the + key. The target record contains the \a type, a pointer to + the \a node, the \a priority. and a canonicalized form of + the \a name, which is later used. + */ +void Tree::insertTarget(const QString& name, TargetRec::Type type, Node* node, int priority) +{ + TargetRec target; + target.type_ = type; + target.node_ = node; + target.priority_ = priority; + target.ref_ = Doc::canonicalTitle(name); + nodesByTarget_.insert(name, target); +} + +/*! + */ +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()) { + QString key = Doc::canonicalTitle(node->title()); + QList nodes = docNodesByTitle_.values(key); + bool alreadyThere = false; + if (!nodes.empty()) { + for (int i=0; i< nodes.size(); ++i) { + if (nodes[i]->subType() == Node::ExternalPage) { + if (node->name() == nodes[i]->name()) { + alreadyThere = true; + break; + } + } + } + } + if (!alreadyThere) { + docNodesByTitle_.insert(key, node); + } + } + if (node->subType() == Node::Collision) { + resolveTargets(node); + } + } + + if (child->doc().hasTableOfContents()) { + const QList& toc = child->doc().tableOfContents(); + TargetRec target; + target.node_ = child; + target.priority_ = 3; + + for (int i = 0; i < toc.size(); ++i) { + target.ref_ = refForAtom(toc.at(i)); + QString title = Text::sectionHeading(toc.at(i)).toString(); + if (!title.isEmpty()) { + QString key = Doc::canonicalTitle(title); + nodesByTarget_.insert(key, target); + } + } + } + if (child->doc().hasKeywords()) { + const QList& keywords = child->doc().keywords(); + TargetRec target; + target.node_ = child; + target.priority_ = 1; + + for (int i = 0; i < keywords.size(); ++i) { + target.ref_ = refForAtom(keywords.at(i)); + QString key = Doc::canonicalTitle(keywords.at(i)->string()); + nodesByTarget_.insert(key, target); + } + } + if (child->doc().hasTargets()) { + const QList& toc = child->doc().targets(); + TargetRec target; + target.node_ = child; + target.priority_ = 2; + + for (int i = 0; i < toc.size(); ++i) { + target.ref_ = refForAtom(toc.at(i)); + QString key = Doc::canonicalTitle(toc.at(i)->string()); + nodesByTarget_.insert(key, target); + } + } + } +} + +/*! + This function searches for a \a target anchor node. If it + finds one, it sets \a ref and returns the found node. + */ +const Node* +Tree::findUnambiguousTarget(const QString& target, QString& ref, const Node* relative) +{ + TargetRec bestTarget; + int numBestTargets = 0; + QList bestTargetList; + + QString key = Doc::canonicalTitle(target); + TargetMap::iterator i = nodesByTarget_.find(key); + while (i != nodesByTarget_.end()) { + if (i.key() != key) + break; + const TargetRec& candidate = i.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; + } + ++i; + } + if (numBestTargets > 0) { + if (numBestTargets == 1) { + ref = bestTarget.ref_; + return bestTarget.node_; + } + else if (bestTargetList.size() > 1) { + if (relative && !relative->qmlModuleName().isEmpty()) { + for (int i=0; iqmlModuleName() == n->qmlModuleName()) { + ref = bestTargetList.at(i).ref_; + return n; + } + } + } + } + } + ref.clear(); + 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* Tree::findDocNodeByTitle(const QString& title, const Node* relative) const +{ + QString key = Doc::canonicalTitle(title); + DocNodeMultiMap::const_iterator i = docNodesByTitle_.constFind(key); + if (i != docNodesByTitle_.constEnd()) { + if (relative && !relative->qmlModuleName().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)->qmlModuleName() == relative->qmlModuleName()) { + /* + By returning here, we avoid printing + all the duplicate header warnings, + which are not really duplicates now, + because of the QML module name 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. + */ + DocNodeMultiMap::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()); + break; // Just report one duplicate for now. + } + ++j; + } + if (internalLocations.size() > 0) { + i.value()->location().warning("This page title exists in more than one file: " + title); + foreach (const Location &location, internalLocations) + location.warning("[It also exists here]"); + } + } + return i.value(); + } + return 0; +} + +/*! + Returns a canonical title for the \a atom, if the \a atom + is a SectionLeft or a Target. + */ +QString Tree::refForAtom(const Atom* atom) +{ + if (atom) { + if (atom->type() == Atom::SectionLeft) + return Doc::canonicalTitle(Text::sectionHeading(atom).toString()); + if (atom->type() == Atom::Target) + return Doc::canonicalTitle(atom->string()); + } + return QString(); +} + QT_END_NAMESPACE diff --git a/src/tools/qdoc/tree.h b/src/tools/qdoc/tree.h index e1a08dd471..1fe0046ae5 100644 --- a/src/tools/qdoc/tree.h +++ b/src/tools/qdoc/tree.h @@ -46,6 +46,7 @@ #ifndef TREE_H #define TREE_H +#include #include "node.h" QT_BEGIN_NAMESPACE @@ -53,23 +54,36 @@ QT_BEGIN_NAMESPACE class QStringList; class QDocDatabase; +struct TargetRec +{ + public: + enum Type { Unknown, Target, Keyword, Contents, Class, Function, Page, Subtitle }; + TargetRec() : node_(0), priority_(INT_MAX), type_(Unknown) { } + bool isEmpty() const { return ref_.isEmpty(); } + Node* node_; + QString ref_; + int priority_; + Type type_; +}; +typedef QMultiMap TargetMap; +typedef QMultiMap DocNodeMultiMap; + class Tree { private: + friend class QDocForest; friend class QDocDatabase; typedef QMap RoleMap; typedef QMap PropertyMap; - Tree(QDocDatabase* qdb); + Tree(const QString& module, QDocDatabase* qdb); ~Tree(); - /* API members */ ClassNode* findClassNode(const QStringList& path, Node* start = 0) const; NamespaceNode* findNamespaceNode(const QStringList& path) const; FunctionNode* findFunctionNode(const QStringList& parentPath, const FunctionNode* clone); - /* internal members */ Node* findNodeRecursive(const QStringList& path, int pathIndex, Node* start, @@ -90,19 +104,22 @@ class Tree int findFlags, bool qml) const; -// --------------------------------------------------------------------- QmlClassNode* findQmlTypeNode(const QStringList& path); Node* findNodeByNameAndType(const QStringList& path, Node::Type type, Node::SubType subtype, - Node* start, bool acceptCollision = false); InnerNode* findRelatesNode(const QStringList& path); - NameCollisionNode* checkForCollision(const QString& name) const; + NameCollisionNode* checkForCollision(const QString& name); NameCollisionNode* findCollisionNode(const QString& name) const; + QString findTarget(const QString& target, const Node* node) const; + void insertTarget(const QString& name, TargetRec::Type type, Node* node, int priority); + void resolveTargets(InnerNode* root); + const Node* findUnambiguousTarget(const QString& target, QString& ref, const Node* relative); + const DocNode* findDocNodeByTitle(const QString& title, const Node* relative = 0) const; void addPropertyFunction(PropertyNode *property, const QString &funcName, @@ -122,14 +139,20 @@ class Tree FunctionNode *findVirtualFunctionInBaseClasses(ClassNode *classe, FunctionNode *clone); NodeList allBaseClasses(const ClassNode *classe) const; + QString refForAtom(const Atom* atom); public: - static bool debug_; + const QString& moduleName() const { return module_; } private: + QString module_; QDocDatabase* qdb_; NamespaceNode root_; PropertyMap unresolvedPropertyMap; + DocNodeMultiMap docNodesByTitle_; + TargetMap nodesByTarget_; + //NodeMap nodesByName_; + //NodeMap nodesByTitle_; }; QT_END_NAMESPACE -- cgit v1.2.3