qdoc: Teach qdoc to use multiple trees (part 2)

qdoc now knows how to search the forrest of node
trees in an optimal order. But there remain some
problems with specific searches that cross module
boundaries. These include group membership and C++
and QML module membership, as well ass C++ base
class resolution. Part 3 will be concerned with
fixing these remaining bugs.

With this update, qdoc now takes less time to
generate the docs for Qt 5. Testing indicates
that qdoc run time has dropped from about 14
minutes to about 7.5 minutes on an iMac.

Task-number: QTBUG-35377
Change-Id: I6bded6ef54124b4f6e5914cad4548f0b600209b0
Reviewed-by: Martin Smith <martin.smith@digia.com>
bb10
Martin Smith 2014-01-31 11:56:10 +01:00 committed by The Qt Project
parent e9954e3162
commit 5fecc6512f
26 changed files with 1385 additions and 636 deletions

View File

@ -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

View File

@ -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;
}

View File

@ -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;

View File

@ -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

View File

@ -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)

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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);
}
/*!

View File

@ -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_;

View File

@ -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;

View File

@ -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;

View File

@ -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();

View File

@ -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);

View File

@ -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;
}
@ -782,6 +782,69 @@ Node *InnerNode::findChildNodeByName(const QString& name) const
return primaryFunctionMap.value(name);
}
/*!
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
not a recearsive search.
If \a qml is true, only match a node for which node->isQmlNode()
returns \c true. If \a qml is false, only match a node for which
node->isQmlNode() returns \c false.
*/
Node* InnerNode::findChildNode(const QString& name, bool qml) const
{
QList<Node*> nodes = childMap.values(name);
if (!nodes.isEmpty()) {
for (int i=0; i<nodes.size(); ++i) {
Node* node = nodes.at(i);
if (!qml) {
if (!node->isQmlNode())
return node;
}
else if (node->isQmlNode() && !node->isQmlPropertyGroup())
return node;
}
}
if (qml && isQmlType()) {
for (int i=0; i<children_.size(); ++i) {
Node* node = children_.at(i);
if (node->isQmlPropertyGroup()) {
node = static_cast<InnerNode*>(node)->findChildNode(name);
if (node)
return node;
}
}
}
return primaryFunctionMap.value(name);
}
/*!
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.
This function is not recursive and therefore can't handle
collisions. If it finds a collision node named \a name, it
will return that node. But it might not find the collision
node because it looks up \a name in the child map, not the
list.
*/
Node* InnerNode::findChildNode(const QString& name, Type type)
{
if (type == Function)
return primaryFunctionMap.value(name);
else {
QList<Node*> nodes = childMap.values(name);
for (int i=0; i<nodes.size(); ++i) {
Node* node = nodes.at(i);
if (node->type() == type)
return node;
}
}
return 0;
}
/*!
*/
void InnerNode::findNodes(const QString& name, QList<Node*>& n)
@ -800,7 +863,7 @@ void InnerNode::findNodes(const QString& name, QList<Node*>& n)
for (int i=0; i<children_.size(); ++i) {
node = children_.at(i);
if (node->isQmlPropertyGroup()) {
node = static_cast<InnerNode*>(node)->findChildNodeByName(name);
node = static_cast<InnerNode*>(node)->findChildNode(name);
if (node) {
n.append(node);
return;
@ -824,7 +887,7 @@ void InnerNode::findNodes(const QString& name, QList<Node*>& n)
if (!node->isQmlPropertyGroup())
n.append(node);
else {
node = static_cast<InnerNode*>(node)->findChildNodeByName(name);
node = static_cast<InnerNode*>(node)->findChildNode(name);
if (node)
n.append(node);
}
@ -837,69 +900,6 @@ void InnerNode::findNodes(const QString& name, QList<Node*>& n)
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
not a recearsive search.
If \a qml is true, only match a node for which node->isQmlNode()
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
{
QList<Node*> nodes = childMap.values(name);
if (!nodes.isEmpty()) {
for (int i=0; i<nodes.size(); ++i) {
Node* node = nodes.at(i);
if (!qml) {
if (!node->isQmlNode())
return node;
}
else if (node->isQmlNode() && !node->isQmlPropertyGroup())
return node;
}
}
if (qml && isQmlType()) {
for (int i=0; i<children_.size(); ++i) {
Node* node = children_.at(i);
if (node->isQmlPropertyGroup()) {
node = static_cast<InnerNode*>(node)->findChildNodeByName(name);
if (node)
return node;
}
}
}
return primaryFunctionMap.value(name);
}
/*!
This function is like findChildNodeByName(), but if a node
with the specified \a name is found but it is not of the
specified \a type, 0 is returned.
This function is not recursive and therefore can't handle
collisions. If it finds a collision node named \a name, it
will return that node. But it might not find the collision
node because it looks up \a name in the child map, not the
list.
*/
Node* InnerNode::findChildNodeByNameAndType(const QString& name, Type type)
{
if (type == Function)
return primaryFunctionMap.value(name);
else {
QList<Node*> nodes = childMap.values(name);
for (int i=0; i<nodes.size(); ++i) {
Node* node = nodes.at(i);
if (node->type() == type)
return node;
}
}
return 0;
}
/*!
Find a function node that is a child of this nose, such
that the function node has the specified \a name.
@ -1258,6 +1258,17 @@ void InnerNode::addChild(Node *child)
}
}
/*!
Adds the \a child to this node's child map using \a title
as the key. The \a child is not added to the child list
again, because it is presumed to already be there. We just
want to be able to find the child by its \a title.
*/
void InnerNode::addChild(Node* child, const QString& title)
{
childMap.insertMulti(title, child);
}
/*!
*/
void InnerNode::removeChild(Node *child)
@ -1288,6 +1299,16 @@ void InnerNode::removeChild(Node *child)
}
++ent;
}
if (child->title().isEmpty())
return;
ent = childMap.find(child->title());
while (ent != childMap.end() && ent.key() == child->title()) {
if (*ent == child) {
childMap.erase(ent);
break;
}
++ent;
}
}
/*!
@ -1533,7 +1554,7 @@ void ClassNode::fixPropertyUsingBaseClasses(PropertyNode* pn)
while (bc != baseClasses().constEnd()) {
ClassNode* cn = bc->node_;
if (cn) {
Node* n = cn->findChildNodeByNameAndType(pn->name(), Node::Property);
Node* n = cn->findChildNode(pn->name(), Node::Property);
if (n) {
PropertyNode* baseProperty = static_cast<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);
@ -1665,6 +1686,15 @@ QString DocNode::title() const
return title_;
}
/*!
Sets the document node's \a title. This is used for the page title.
*/
void DocNode::setTitle(const QString &title)
{
title_ = title;
parent()->addChild(this, title);
}
/*!
Returns the document node's full title, which is usually
just title(), but for some SubType values is different

View File

@ -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:

View File

@ -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()) {

View File

@ -56,7 +56,6 @@ QT_BEGIN_NAMESPACE
class Config;
class Node;
class QString;
class Tree;
class PureDocParser : public CppCodeParser
{

File diff suppressed because it is too large Load Diff

View File

@ -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);
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);
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);
}
NameCollisionNode* checkForCollision(const QString& name) const {
return tree_->checkForCollision(name);
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 {
return forest_.findDocNodeByTitle(title, relative);
}
const Node* findUnambiguousTarget(const QString& target, QString& ref, const Node* relative) {
return forest_.findUnambiguousTarget(target, ref, relative);
}
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_;
};

View File

@ -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.

View File

@ -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();

View File

@ -58,7 +58,6 @@ QT_BEGIN_NAMESPACE
class Config;
class Node;
class QString;
class Tree;
class QmlCodeParser : public CodeParser
{

View File

@ -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();
}
/*!

View File

@ -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

View File

@ -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