diff options
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<QString, Node*> MemberMap; // the string is the member signature typedef QPair<const QmlClassNode*, MemberMap> 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 <qdebug.h> +#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<QString> 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<InnerNode*>(n); if (parent) { @@ -1704,7 +1691,7 @@ bool CppCodeParser::matchNamespaceDecl(InnerNode *parent) QString namespaceName = previousLexeme(); NamespaceNode* ns = 0; if (parent) - ns = static_cast<NamespaceNode*>(parent->findChildNodeByNameAndType(namespaceName, Node::Namespace)); + ns = static_cast<NamespaceNode*>(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<EnumNode*>(parent->findChildNodeByNameAndType(name, Node::Enum)); + EnumNode* en = static_cast<EnumNode*>(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<InnerNode *>(*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<QString, DocNode*> 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<DocNode*>(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 = "<!DOCTYPE map PUBLIC \"-//OASIS//DTD DITA Map//EN\" \"map.dtd\">"; xmlWriter().writeDTD(doctype); writeStartTag(DT_map); @@ -5537,9 +5537,9 @@ void DitaXmlGenerator::writeDitaMap() nodeSubtypeMaps[i] = new NodeMultiMap; for (unsigned i=0; i<Node::OnBeyondZebra; ++i) pageTypeMaps[i] = new NodeMultiMap; - Node* rootPageNode = collectNodesByTypeAndSubtype(qdb_->treeRoot()); + Node* rootPageNode = collectNodesByTypeAndSubtype(qdb_->primaryTreeRoot()); - beginSubPage(qdb_->treeRoot(),"qt.ditamap"); + beginSubPage(qdb_->primaryTreeRoot(),"qt.ditamap"); doctype = "<!DOCTYPE map PUBLIC \"-//OASIS//DTD DITA Map//EN\" \"map.dtd\">"; xmlWriter().writeDTD(doctype); @@ -6132,7 +6132,7 @@ void DitaXmlGenerator::generateCollisionPages() int count = 0; for (int i=0; i<collisions.size(); ++i) { InnerNode* n = static_cast<InnerNode*>(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.size(); ++i) { InnerNode* n = static_cast<InnerNode*>(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<Atom*> tableOfContents; - QList<int> tableOfContentsLevels; - QList<Atom*> keywords; - QList<Atom*> targets; - QStringMultiMap metaMap; + Doc::Sections granularity_; + Doc::Sections section_; // ### + QList<Atom*> tableOfContents_; + QList<int> tableOfContentsLevels_; + QList<Atom*> keywords_; + QList<Atom*> 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<QString, Location> targetMap; + QMap<QString, Location> targetMap_; QMap<int, QString> pendingFormats; QStack<int> openedCommands; QStack<OpenedList> 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<QString> &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<Text> &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<Atom *> &Doc::tableOfContents() const { priv->constructExtra(); - return priv->extra->tableOfContents; + return priv->extra->tableOfContents_; } const QList<int> &Doc::tableOfContentsLevels() const { priv->constructExtra(); - return priv->extra->tableOfContentsLevels; + return priv->extra->tableOfContentsLevels_; } const QList<Atom *> &Doc::keywords() const { priv->constructExtra(); - return priv->extra->keywords; + return priv->extra->keywords_; } const QList<Atom *> &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<QString>& 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.size(); ++i) { InnerNode* n = static_cast<InnerNode*>(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() << "<ul>\n"; for (int i=0; i<collisions.size(); ++i) { InnerNode* n = static_cast<InnerNode*>(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<QString, DocNode *> 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<DocNode *>(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)(?: +[^>]*)?>)(.*)(</@\\2>)" tags + // replace all "(<@(type|headerfile)(?: +[^>]*)?>)(.*)(</@\\2>)" 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("<span class=\"type\">"); 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<QString> excludedDirs; QSet<QString> 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<QString,QString> headers; QMultiMap<QString,QString> 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<QString,QString> sources; QMultiMap<QString,QString> 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<exampleQdocList.size(); ++i) { if (!sources.contains(exampleQdocList[i])) { @@ -436,7 +440,7 @@ static void processQdocconfFile(const QString &fileName) } } - Generator::debugSegfault("Adding doc/image dirs found in exampledirs to imagedirs"); + Generator::debug("Adding doc/image dirs found in exampledirs to imagedirs"); QSet<QString> exampleImageDirs; QStringList exampleImageList = config.getExampleImageFiles(excludedDirs, excludedFiles); for (int i=0; i<exampleImageList.size(); ++i) { @@ -454,8 +458,9 @@ static void processQdocconfFile(const QString &fileName) to the big tree. */ QSet<CodeParser *> usedParsers; + //Config::debug_ = true; - Generator::debugSegfault("Parsing header files"); + Generator::debug("Parsing header files"); int parsed = 0; QMap<QString,QString>::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<QString,QString>::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<QString>::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; i<children_.size(); ++i) { Node* n = children_.at(i); if (n->isQmlPropertyGroup()) { - node = static_cast<InnerNode*>(n)->findChildNodeByName(name); + node = static_cast<InnerNode*>(n)->findChildNode(name); if (node) return node; } @@ -783,61 +783,6 @@ Node *InnerNode::findChildNodeByName(const QString& name) const } /*! - */ -void InnerNode::findNodes(const QString& name, QList<Node*>& n) -{ - n.clear(); - Node* node = 0; - QList<Node*> nodes = childMap.values(name); - /* - <sigh> 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; i<children_.size(); ++i) { - node = children_.at(i); - if (node->isQmlPropertyGroup()) { - node = static_cast<InnerNode*>(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; i<nodes.size(); ++i) { - node = nodes.at(i); - if (!node->isQmlPropertyGroup()) - n.append(node); - else { - node = static_cast<InnerNode*>(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 of its property group nodes. Return the matching node or 0. This is @@ -847,7 +792,7 @@ void InnerNode::findNodes(const QString& name, QList<Node*>& 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<Node*> nodes = childMap.values(name); if (!nodes.isEmpty()) { @@ -865,7 +810,7 @@ Node* InnerNode::findChildNodeByName(const QString& name, bool qml) const for (int i=0; i<children_.size(); ++i) { Node* node = children_.at(i); if (node->isQmlPropertyGroup()) { - node = static_cast<InnerNode*>(node)->findChildNodeByName(name); + node = static_cast<InnerNode*>(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); @@ -901,6 +846,61 @@ Node* InnerNode::findChildNodeByNameAndType(const QString& name, Type type) } /*! + */ +void InnerNode::findNodes(const QString& name, QList<Node*>& n) +{ + n.clear(); + Node* node = 0; + QList<Node*> nodes = childMap.values(name); + /* + <sigh> 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; i<children_.size(); ++i) { + node = children_.at(i); + if (node->isQmlPropertyGroup()) { + node = static_cast<InnerNode*>(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; i<nodes.size(); ++i) { + node = nodes.at(i); + if (!node->isQmlPropertyGroup()) + n.append(node); + else { + node = static_cast<InnerNode*>(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. */ @@ -1259,6 +1259,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<PropertyNode*>(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<PropertyNode*>(n); @@ -1666,6 +1687,15 @@ QString DocNode::title() const } /*! + 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 from title() 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<Node*>& 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<Doc>::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; i<searchOrder_.size(); ++i) + delete searchOrder_.at(i); + forest_.clear(); + searchOrder_.clear(); + moduleNames_.clear(); + primaryTree_ = 0; +} + +/*! + Initializes the forest prior to a traversal and + returns a pointer to the root node of the primary + tree. If the forest is empty, it return 0 + */ +NamespaceNode* QDocForest::firstRoot() +{ + currentIndex_ = 0; + return (!searchOrder_.isEmpty() ? searchOrder_[0]->root() : 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<QString, Tree*>::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<moduleNames_.size(); ++i) + qDebug() << " " << i+1 << "." << moduleNames_.at(i); +#endif +} + +/*! + Returns an ordered array of Tree pointers that represents + the order in which the trees should be searched. The first + Tree in the array is the tree for the current module, i.e. + the module for which qdoc is generating documentation. + + The other Tree pointers in the array represent the index + files that were loaded in preparation for generating this + module's documentation. Each Tree pointer represents one + index file. The index file Tree points have been ordered + heuristically to, hopefully, minimize searching. Thr order + will probably be changed. + + If the search order array is empty, this function calls + setSearchOrder() to create the search order. + */ +const QVector<Tree*>& 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_; } @@ -106,6 +439,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 group nodes. @@ -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<QmlModuleNode*>(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<QmlClassNode*>(n); @@ -391,6 +884,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 things, C++ classes, QML types, "since" lists, and other @@ -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<const InnerNode*>(*c)); + findAllFunctions(static_cast<InnerNode*>(*c)); } else if ((*c)->type() == Node::Function) { const FunctionNode* func = static_cast<const FunctionNode*>(*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<const InnerNode *>(*c)); + findAllLegaleseTexts(static_cast<InnerNode *>(*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<const InnerNode *>(*c)); + findAllNamespaces(static_cast<InnerNode *>(*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) { @@ -800,150 +1304,6 @@ const Node* QDocDatabase::findNodeForTarget(const QString& target, const Node* r } /*! - 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<TargetRec> 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; i<bestTargetList.size(); ++i) { - const Node* n = bestTargetList.at(i).node_; - if (n && relative->qmlModuleName() == 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<const DocNode*>(*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<Location> 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 pointer is 0, use the QML base type name to look up the @@ -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<QmlClassNode*>(child); @@ -987,80 +1347,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<DocNode*>(child); - if (!node->title().isEmpty()) { - QString key = Doc::canonicalTitle(node->title()); - QList<DocNode*> 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<Atom*>& 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<Atom*>& 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<Atom*>& 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. */ void QDocDatabase::generateTagFile(const QString& name, Generator* g) @@ -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 <qstring.h> #include <qmap.h> #include "tree.h" +#include "config.h" +#include <qdebug.h> QT_BEGIN_NAMESPACE @@ -54,10 +56,10 @@ typedef QMap<QString, NodeMap> NodeMapMap; typedef QMap<QString, NodeMultiMap> NodeMultiMapMap; typedef QMultiMap<QString, Node*> QDocMultiMap; typedef QMap<Text, const Node*> TextToNodeMap; -typedef QMultiMap<QString, DocNode*> 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<QString, TargetRec> 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<Tree*>& 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<QString, Tree*> forest_; + QVector<Tree*> searchOrder_; + QVector<QString> 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<Tree*>& 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<QString> 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 <limits.h> #include <qdebug.h> -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<Node*>(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<const ClassNode*>(node)); @@ -243,7 +253,7 @@ const FunctionNode* Tree::findFunctionNode(const QStringList& path, if (i == path.size() - 1) next = static_cast<const InnerNode*>(baseClass)->findFunctionNode(path.at(i)); else - next = static_cast<const InnerNode*>(baseClass)->findChildNodeByName(path.at(i)); + next = static_cast<const InnerNode*>(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<NamespaceNode*>(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<const InnerNode*>(node)->findChildNodeByName(path.at(i), qml); + const Node* next = static_cast<const InnerNode*>(node)->findChildNode(path.at(i), qml); if (!next && (findFlags & SearchEnumValues) && i == path.size()-1) next = static_cast<const InnerNode*>(node)->findEnumNodeForValue(path.at(i)); if (!next && !qml && node->type() == Node::Class && (findFlags & SearchBaseClasses)) { NodeList baseClasses = allBaseClasses(static_cast<const ClassNode*>(node)); foreach (const Node* baseClass, baseClasses) { - next = static_cast<const InnerNode*>(baseClass)->findChildNodeByName(path.at(i)); + next = static_cast<const InnerNode*>(baseClass)->findChildNode(path.at(i)); if (!next && (findFlags & SearchEnumValues) && i == path.size() - 1) next = static_cast<const InnerNode*>(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<DocNode*>(child); + if (!node->title().isEmpty()) { + QString key = Doc::canonicalTitle(node->title()); + QList<DocNode*> 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<Atom*>& 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<Atom*>& 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<Atom*>& 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<TargetRec> 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; i<bestTargetList.size(); ++i) { + const Node* n = bestTargetList.at(i).node_; + if (n && relative->qmlModuleName() == 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<const DocNode*>(*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<Location> 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 <QtCore/qstack.h> #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<QString, TargetRec> TargetMap; +typedef QMultiMap<QString, DocNode*> DocNodeMultiMap; + class Tree { private: + friend class QDocForest; friend class QDocDatabase; typedef QMap<PropertyNode::FunctionRole, QString> RoleMap; typedef QMap<PropertyNode*, RoleMap> 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 |