From 9b0c0823c067eb0c072a6f35caec5cb31809a1b4 Mon Sep 17 00:00:00 2001
From: Martin Smith
Date: Fri, 30 Mar 2012 13:43:46 +0200
Subject: [PATCH] qdoc: Allow documenting a C++ class as a QML type.
Now qdoc can handle the case where a C++ class is
documented as a QML type of the same name, or as
both a C++ class and a QML type of the same name.
And collisions pages are created for both the HTML
and the DITA XML output. A collision page is created
when two items have the same name. The collision
pages will be augmented later to include the list
of pages where ambiguous links to one of the items
listed on the collision page are actually located,
so the writer can go back to those links and add
the appropriate qualifier.
Change-Id: I5a9632b2d2209e0784392047056bed8962005624
Reviewed-by: Martin Smith
Reviewed-by: Casper van Donderen
---
src/tools/qdoc/codemarker.cpp | 1 -
src/tools/qdoc/cppcodemarker.cpp | 57 ++---
src/tools/qdoc/cppcodeparser.cpp | 280 +++++++++++-----------
src/tools/qdoc/cppcodeparser.h | 4 +-
src/tools/qdoc/ditaxmlgenerator.cpp | 244 +++++++++++++------
src/tools/qdoc/ditaxmlgenerator.h | 20 +-
src/tools/qdoc/generator.cpp | 71 ++++--
src/tools/qdoc/generator.h | 12 +-
src/tools/qdoc/helpprojectwriter.cpp | 5 -
src/tools/qdoc/htmlgenerator.cpp | 234 +++++++++---------
src/tools/qdoc/htmlgenerator.h | 16 +-
src/tools/qdoc/node.cpp | 206 ++++++++--------
src/tools/qdoc/node.h | 68 +++---
src/tools/qdoc/tree.cpp | 448 +++++++++++++++++++++++------------
src/tools/qdoc/tree.h | 61 +++--
15 files changed, 998 insertions(+), 729 deletions(-)
diff --git a/src/tools/qdoc/codemarker.cpp b/src/tools/qdoc/codemarker.cpp
index 9f22a28..fe0b366 100644
--- a/src/tools/qdoc/codemarker.cpp
+++ b/src/tools/qdoc/codemarker.cpp
@@ -649,7 +649,6 @@ QStringList CodeMarker::macRefsForNode(Node *node)
}
case Node::Namespace:
case Node::Fake:
- case Node::Target:
default:
return QStringList();
}
diff --git a/src/tools/qdoc/cppcodemarker.cpp b/src/tools/qdoc/cppcodemarker.cpp
index 3e7d190..aea8ed2 100644
--- a/src/tools/qdoc/cppcodemarker.cpp
+++ b/src/tools/qdoc/cppcodemarker.cpp
@@ -826,61 +826,38 @@ QList CppCodeMarker::sections(const InnerNode *inner,
return sections;
}
+/*!
+ Search the \a tree for a node named \a target
+ */
const Node *CppCodeMarker::resolveTarget(const QString& target,
const Tree* tree,
const Node* relative,
const Node* self)
{
+ const Node* node = 0;
if (target.endsWith("()")) {
- const FunctionNode *func;
QString funcName = target;
funcName.chop(2);
-
QStringList path = funcName.split("::");
- if ((func = tree->findFunctionNode(path,
- relative,
- Tree::SearchBaseClasses))
- && func->metaness() != FunctionNode::MacroWithoutParams)
- return func;
+ const FunctionNode* fn = tree->findFunctionNode(path, relative, Tree::SearchBaseClasses);
+ if (fn) {
+ /*
+ Why is this case not accepted?
+ */
+ if (fn->metaness() != FunctionNode::MacroWithoutParams)
+ node = fn;
+ }
}
else if (target.contains(QLatin1Char('#'))) {
- // ### this doesn't belong here; get rid of TargetNode hack
- int hashAt = target.indexOf(QLatin1Char('#'));
- QString link = target.left(hashAt);
- QString ref = target.mid(hashAt + 1);
- const Node *node;
- if (link.isEmpty()) {
- node = relative;
- }
- else {
- QStringList path(link);
- node = tree->findNode(path, tree->root(), Tree::SearchBaseClasses);
- }
- if (node && node->isInnerNode()) {
- const Atom *atom = node->doc().body().firstAtom();
- while (atom) {
- if (atom->type() == Atom::Target && atom->string() == ref) {
- Node *parentNode = const_cast(node);
- return new TargetNode(static_cast(parentNode),
- ref);
- }
- atom = atom->next();
- }
- }
+ // 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("::");
- const Node *node;
- int flags = Tree::SearchBaseClasses |
- Tree::SearchEnumValues |
- Tree::NonFunction;
- if ((node = tree->findNode(path,
- relative,
- flags,
- self)))
- return node;
+ int flags = Tree::SearchBaseClasses | Tree::SearchEnumValues | Tree::NonFunction;
+ node = tree->findNode(path, relative, flags, self);
}
- return 0;
+ return node;
}
static const char * const typeTable[] = {
diff --git a/src/tools/qdoc/cppcodeparser.cpp b/src/tools/qdoc/cppcodeparser.cpp
index a308da1..7dfb32b 100644
--- a/src/tools/qdoc/cppcodeparser.cpp
+++ b/src/tools/qdoc/cppcodeparser.cpp
@@ -79,7 +79,6 @@ QT_BEGIN_NAMESPACE
#define COMMAND_PROPERTY Doc::alias("property")
#define COMMAND_REIMP Doc::alias("reimp")
#define COMMAND_RELATES Doc::alias("relates")
-#define COMMAND_SERVICE Doc::alias("service")
#define COMMAND_STARTPAGE Doc::alias("startpage")
#define COMMAND_TYPEDEF Doc::alias("typedef")
#define COMMAND_VARIABLE Doc::alias("variable")
@@ -119,7 +118,7 @@ QStringList CppCodeParser::exampleDirs;
This is used for fuzzy matching only, which in turn is only used
for Qt Jambi.
*/
-static QString cleanType(const QString &type, const Tree *tree)
+static QString cleanType(const QString &type, Tree* tree)
{
QString result = type;
result.replace("qlonglong", "long long");
@@ -145,14 +144,13 @@ static QString cleanType(const QString &type, const Tree *tree)
while ((pos = result.indexOf(regExp, pos)) != -1) {
// we assume that the path for the associated enum
// is the same as for the flag typedef
- QStringList path = regExp.cap(2).split("::",
- QString::SkipEmptyParts);
- const EnumNode *enume = static_cast(
- tree->findNode(QStringList(path) << regExp.cap(3),
- Node::Enum));
- if (enume && enume->flagsType())
- result.replace(pos, regExp.matchedLength(),
- (QStringList(path) << enume->flagsType()->name()).join("::"));
+ QStringList path = regExp.cap(2).split("::", QString::SkipEmptyParts);
+ QStringList tmpPath = QStringList(path) << regExp.cap(3);
+ EnumNode* en = tree->findEnumNode(tmpPath);
+ if (en && en->flagsType()) {
+ tmpPath = QStringList(path) << en->flagsType()->name();
+ result.replace(pos, regExp.matchedLength(), tmpPath.join("::"));
+ }
++pos;
}
}
@@ -191,9 +189,12 @@ void CppCodeParser::initializeParser(const Config &config)
{
CodeParser::initializeParser(config);
+ /*
+ All these can appear in a C++ namespace. Don't add
+ anything that can't be in a C++ namespace.
+ */
nodeTypeMap.insert(COMMAND_NAMESPACE, Node::Namespace);
nodeTypeMap.insert(COMMAND_CLASS, Node::Class);
- nodeTypeMap.insert(COMMAND_SERVICE, Node::Class);
nodeTypeMap.insert(COMMAND_ENUM, Node::Enum);
nodeTypeMap.insert(COMMAND_TYPEDEF, Node::Typedef);
nodeTypeMap.insert(COMMAND_PROPERTY, Node::Property);
@@ -306,7 +307,13 @@ void CppCodeParser::parseSourceFile(const Location& location,
Tokenizer fileTokenizer(fileLocation, in);
tokenizer = &fileTokenizer;
readToken();
- usedNamespaces.clear();
+
+ /*
+ The set of active namespaces is cleared before parsing
+ each source file. The word "source" here means cpp file.
+ */
+ activeNamespaces_.clear();
+
matchDocsAndStuff();
in.close();
}
@@ -403,14 +410,14 @@ const FunctionNode *CppCodeParser::findFunctionNode(const QString& synopsis,
This is necessary because Roberto's parser resolves typedefs.
*/
if (!func && fuzzy) {
- func = tre->findFunctionNode(parentPath +
+ func = tree_->findFunctionNode(parentPath +
QStringList(clone->name()),
relative,
flags);
if (!func && clone->name().contains('_')) {
QStringList path = parentPath;
path << clone->name().split('_');
- func = tre->findFunctionNode(path, relative, flags);
+ func = tree_->findFunctionNode(path, relative, flags);
}
if (func) {
@@ -512,7 +519,6 @@ QSet CppCodeParser::topicCommands()
<< COMMAND_NAMESPACE
<< COMMAND_PAGE
<< COMMAND_PROPERTY
- << COMMAND_SERVICE
<< COMMAND_TYPEDEF
<< COMMAND_VARIABLE
<< COMMAND_QMLCLASS
@@ -543,21 +549,21 @@ Node* CppCodeParser::processTopicCommand(const Doc& doc,
doc.location().warning(tr("Invalid syntax in '\\%1'").arg(COMMAND_FN));
}
else {
- if (!usedNamespaces.isEmpty()) {
- foreach (const QString &usedNamespace, usedNamespaces) {
- QStringList newPath = usedNamespace.split("::") + parentPath;
- func = tre->findFunctionNode(newPath, clone);
+ if (!activeNamespaces_.isEmpty()) {
+ foreach (const QString& usedNamespace_, activeNamespaces_) {
+ QStringList newPath = usedNamespace_.split("::") + parentPath;
+ func = tree_->findFunctionNode(newPath, clone);
if (func)
break;
}
}
// Search the root namespace if no match was found.
if (func == 0)
- func = tre->findFunctionNode(parentPath, clone);
+ func = tree_->findFunctionNode(parentPath, clone);
if (func == 0) {
if (parentPath.isEmpty() && !lastPath.isEmpty())
- func = tre->findFunctionNode(lastPath, clone);
+ func = tree_->findFunctionNode(lastPath, clone);
if (func == 0) {
doc.location().warning(tr("Cannot find '%1' in '\\%2'")
.arg(clone->name() + "(...)")
@@ -589,7 +595,7 @@ Node* CppCodeParser::processTopicCommand(const Doc& doc,
QStringList parentPath;
FunctionNode *func = 0;
- if (makeFunctionNode(arg, &parentPath, &func, tre->root())) {
+ if (makeFunctionNode(arg, &parentPath, &func, tree_->root())) {
if (!parentPath.isEmpty()) {
doc.location().warning(tr("Invalid syntax in '\\%1'")
.arg(COMMAND_MACRO));
@@ -610,7 +616,7 @@ Node* CppCodeParser::processTopicCommand(const Doc& doc,
return func;
}
else if (QRegExp("[A-Za-z_][A-Za-z0-9_]+").exactMatch(arg)) {
- func = new FunctionNode(tre->root(), arg);
+ func = new FunctionNode(tree_->root(), arg);
func->setAccess(Node::Public);
func->setLocation(doc.location());
func->setMetaness(FunctionNode::MacroWithoutParams);
@@ -624,25 +630,45 @@ Node* CppCodeParser::processTopicCommand(const Doc& doc,
}
else if (nodeTypeMap.contains(command)) {
/*
- The command was neither "fn" nor "macro" .
+ We should only get in here if the command refers to
+ something that can appear in a C++ namespace,
+ i.e. a class, another namespace, an enum, a typedef,
+ a property or a variable. I think these are handled
+ this way to allow the writer to refer to the entity
+ without including the namespace qualifier.
*/
- // ### split(QLatin1Char(' ')) hack is there to support header file syntax
+ Node::Type type = nodeTypeMap[command];
+ Node::SubType subtype = Node::NoSubType;
+ if (type == Node::Fake)
+ subtype = Node::QmlClass;
+
QStringList paths = arg.split(QLatin1Char(' '));
QStringList path = paths[0].split("::");
Node *node = 0;
- if (!usedNamespaces.isEmpty()) {
- foreach (const QString &usedNamespace, usedNamespaces) {
- QStringList newPath = usedNamespace.split("::") + path;
- node = tre->findNode(newPath, nodeTypeMap[command]);
+
+ /*
+ If the command refers to something that can be in a
+ C++ namespace, search for it first in all the known
+ C++ namespaces.
+ */
+ if (!activeNamespaces_.isEmpty()) {
+ foreach (const QString& usedNamespace_, activeNamespaces_) {
+ QStringList newPath = usedNamespace_.split("::") + path;
+ node = tree_->findNodeByNameAndType(newPath, type, subtype, 0);
if (node) {
path = newPath;
break;
}
}
}
- // Search the root namespace if no match was found.
- if (node == 0)
- node = tre->findNode(path, nodeTypeMap[command]);
+
+ /*
+ If the node was not found in a C++ namespace, search
+ for it in the root namespace.
+ */
+ if (node == 0) {
+ node = tree_->findNodeByNameAndType(path, type, subtype, 0);
+ }
if (node == 0) {
doc.location().warning(tr("Cannot find '%1' specified with '\\%2' in any header file")
@@ -650,63 +676,40 @@ Node* CppCodeParser::processTopicCommand(const Doc& doc,
lastPath = path;
}
- else if (command == COMMAND_SERVICE) {
- // If the command is "\service", then we need to tag the
- // class with the actual service name.
- QStringList args = arg.split(QLatin1Char(' '));
- if (args.size() > 1) {
- ClassNode *cnode = static_cast(node);
- cnode->setServiceName(args[1]);
- cnode->setHideFromMainList(true);
- }
- }
else if (node->isInnerNode()) {
+ /*
+ This treets a class as a namespace.
+ */
if (path.size() > 1) {
path.pop_back();
- usedNamespaces.insert(path.join("::"));
+ QString ns = path.join("::");
+ activeNamespaces_.insert(ns);
}
}
-#if 0
- /*
- This code apparently does nothing. After further
- investigation to verify it is useless, it will
- be removed.
- */
- if (command == COMMAND_CLASS) {
- if (paths.size() > 1) {
- if (!paths[1].endsWith(".h")) {
- ClassNode* cnode = static_cast(node);
- cnode->setQmlElement(paths[1]);
- }
- }
- }
-#endif
return node;
}
else if (command == COMMAND_EXAMPLE) {
- if (Config::generateExamples) {
- ExampleNode* en = new ExampleNode(tre->root(), arg);
- createExampleFileNodes(en);
- return en;
- }
+ ExampleNode* en = new ExampleNode(tree_->root(), arg);
+ createExampleFileNodes(en);
+ return en;
}
else if (command == COMMAND_EXTERNALPAGE) {
- return new FakeNode(tre->root(), arg, Node::ExternalPage, Node::ArticlePage);
+ return new FakeNode(tree_->root(), arg, Node::ExternalPage, Node::ArticlePage);
}
else if (command == COMMAND_FILE) {
- return new FakeNode(tre->root(), arg, Node::File, Node::NoPageType);
+ return new FakeNode(tree_->root(), arg, Node::File, Node::NoPageType);
}
else if (command == COMMAND_GROUP) {
- return new FakeNode(tre->root(), arg, Node::Group, Node::OverviewPage);
+ return new FakeNode(tree_->root(), arg, Node::Group, Node::OverviewPage);
}
else if (command == COMMAND_HEADERFILE) {
- return new FakeNode(tre->root(), arg, Node::HeaderFile, Node::ApiPage);
+ return new FakeNode(tree_->root(), arg, Node::HeaderFile, Node::ApiPage);
}
else if (command == COMMAND_MODULE) {
- return new FakeNode(tre->root(), arg, Node::Module, Node::OverviewPage);
+ return new FakeNode(tree_->root(), arg, Node::Module, Node::OverviewPage);
}
else if (command == COMMAND_QMLMODULE) {
- return new FakeNode(tre->root(), arg, Node::QmlModule, Node::OverviewPage);
+ return new FakeNode(tree_->root(), arg, Node::QmlModule, Node::OverviewPage);
}
else if (command == COMMAND_PAGE) {
Node::PageType ptype = Node::ArticlePage;
@@ -740,30 +743,27 @@ Node* CppCodeParser::processTopicCommand(const Doc& doc,
If there is no collision, just create a new Page
node and return that one.
*/
- NameCollisionNode* ncn = tre->checkForCollision(args[0]);
+ NameCollisionNode* ncn = tree_->checkForCollision(args[0]);
FakeNode* fn = 0;
if (ptype == Node::DitaMapPage)
- fn = new DitaMapNode(tre->root(), args[0]);
+ fn = new DitaMapNode(tree_->root(), args[0]);
else
- fn = new FakeNode(tre->root(), args[0], Node::Page, ptype);
+ fn = new FakeNode(tree_->root(), args[0], Node::Page, ptype);
if (ncn) {
ncn->addCollision(fn);
}
return fn;
}
else if (command == COMMAND_DITAMAP) {
- FakeNode* fn = new DitaMapNode(tre->root(), arg);
+ FakeNode* fn = new DitaMapNode(tree_->root(), arg);
return fn;
}
else if (command == COMMAND_QMLCLASS) {
- const ClassNode* classNode = 0;
+ ClassNode* classNode = 0;
QStringList names = arg.split(QLatin1Char(' '));
- if (names.size() > 1) {
- Node* n = tre->findNode(names[1].split("::"),Node::Class);
- if (n) {
- classNode = static_cast(n);
- }
- }
+ if (names.size() > 1)
+ classNode = tree_->findClassNode(names[1].split("::"));
+
/*
Search for a node with the same name. If there is one,
then there is a collision, so create a collision node
@@ -775,15 +775,14 @@ Node* CppCodeParser::processTopicCommand(const Doc& doc,
If there is no collision, just create a new QML class
node and return that one.
*/
- NameCollisionNode* ncn = tre->checkForCollision(names[0]);
- QmlClassNode* qcn = new QmlClassNode(tre->root(), names[0], classNode);
- if (ncn) {
+ NameCollisionNode* ncn = tree_->checkForCollision(names[0]);
+ QmlClassNode* qcn = new QmlClassNode(tree_->root(), names[0], classNode);
+ if (ncn)
ncn->addCollision(qcn);
- }
return qcn;
}
else if (command == COMMAND_QMLBASICTYPE) {
- return new QmlBasicTypeNode(tre->root(), arg);
+ return new QmlBasicTypeNode(tree_->root(), arg);
}
else if ((command == COMMAND_QMLSIGNAL) ||
(command == COMMAND_QMLMETHOD) ||
@@ -793,7 +792,7 @@ Node* CppCodeParser::processTopicCommand(const Doc& doc,
QString element;
QString type;
if (splitQmlMethodArg(doc,arg,type,module,element)) {
- QmlClassNode* qmlClass = tre->findQmlClassNode(module,element);
+ QmlClassNode* qmlClass = tree_->findQmlClassNode(module,element);
if (qmlClass) {
if (command == COMMAND_QMLSIGNAL)
return makeFunctionNode(doc,arg,qmlClass,Node::QmlSignal,false,COMMAND_QMLSIGNAL);
@@ -936,18 +935,18 @@ Node *CppCodeParser::processTopicCommandGroup(const Doc& doc,
bool attached = (command == COMMAND_QMLATTACHEDPROPERTY);
QStringList::ConstIterator arg = args.begin();
if (splitQmlPropertyArg(doc,(*arg),type,module,element,property)) {
- QmlClassNode* qmlClass = tre->findQmlClassNode(module,element);
+ QmlClassNode* qmlClass = tree_->findQmlClassNode(module,element);
if (qmlClass) {
qmlPropGroup = new QmlPropGroupNode(qmlClass,property,attached);
}
}
if (qmlPropGroup) {
- const ClassNode *correspondingClass = static_cast(qmlPropGroup->parent())->classNode();
+ ClassNode *correspondingClass = static_cast(qmlPropGroup->parent())->classNode();
QmlPropertyNode *qmlPropNode = new QmlPropertyNode(qmlPropGroup,property,type,attached);
const PropertyNode *correspondingProperty = 0;
if (correspondingClass) {
- correspondingProperty = qmlPropNode->correspondingProperty(tre);
+ correspondingProperty = qmlPropNode->correspondingProperty(tree_);
}
if (correspondingProperty) {
bool writableList = type.startsWith("list") && correspondingProperty->dataType().endsWith('*');
@@ -1053,28 +1052,37 @@ void CppCodeParser::processOtherMetaCommand(const Doc& doc,
}
}
else if (command == COMMAND_RELATES) {
- InnerNode *pseudoParent;
+ /*
+ Find the node that this node relates to.
+ */
+ Node* n = 0;
if (arg.startsWith(QLatin1Char('<')) || arg.startsWith('"')) {
- pseudoParent =
- static_cast(tre->findNode(QStringList(arg),
- Node::Fake));
+ /*
+ It should be a header file, I think.
+ */
+ n = tree_->findNodeByNameAndType(QStringList(arg), Node::Fake, Node::NoSubType, 0);
}
else {
+ /*
+ If it wasn't a file, it should be either a class or a namespace.
+ */
QStringList newPath = arg.split("::");
- pseudoParent =
- static_cast(tre->findNode(QStringList(newPath),
- Node::Class));
- if (!pseudoParent)
- pseudoParent =
- static_cast(tre->findNode(QStringList(newPath),
- Node::Namespace));
- }
- if (!pseudoParent) {
- doc.location().warning(tr("Cannot find '%1' in '\\%2'")
- .arg(arg).arg(COMMAND_RELATES));
+ n = tree_->findClassNode(QStringList(newPath));
+ if (!n)
+ n = tree_->findNamespaceNode(QStringList(newPath));
+ }
+
+ if (!n) {
+ /*
+ Didn't ind it. Error...
+ */
+ doc.location().warning(tr("Cannot find '%1' in '\\%2'").arg(arg).arg(COMMAND_RELATES));
}
else {
- node->setRelates(pseudoParent);
+ /*
+ Found it. This node relates to it.
+ */
+ node->setRelates(static_cast(n));
}
}
else if (command == COMMAND_CONTENTSPAGE) {
@@ -1136,7 +1144,7 @@ void CppCodeParser::processOtherMetaCommand(const Doc& doc,
}
}
else {
- processCommonMetaCommand(doc.location(),command,arg,node,tre);
+ processCommonMetaCommand(doc.location(),command,arg,node,tree_);
}
}
@@ -1165,7 +1173,7 @@ void CppCodeParser::processOtherMetaCommands(const Doc& doc, Node *node)
*/
void CppCodeParser::reset(Tree *tree)
{
- tre = tree;
+ tree_ = tree;
tokenizer = 0;
tok = 0;
access = Node::Public;
@@ -1691,7 +1699,7 @@ bool CppCodeParser::matchBaseSpecifier(ClassNode *classe, bool isClass)
if (!matchDataType(&baseClass))
return false;
- tre->addBaseClass(classe,
+ tree_->addBaseClass(classe,
access,
baseClass.toPath(),
baseClass.toString(),
@@ -1773,18 +1781,18 @@ bool CppCodeParser::matchNamespaceDecl(InnerNode *parent)
So far, so good. We have 'namespace Foo {'.
*/
QString namespaceName = previousLexeme();
- NamespaceNode *namespasse = 0;
+ NamespaceNode* ns = 0;
if (parent) {
- namespasse = static_cast(parent->findNode(namespaceName, Node::Namespace));
+ ns = static_cast(parent->findChildNodeByNameAndType(namespaceName, Node::Namespace));
}
- if (!namespasse) {
- namespasse = new NamespaceNode(parent, namespaceName);
- namespasse->setAccess(access);
- namespasse->setLocation(location());
+ if (!ns) {
+ ns = new NamespaceNode(parent, namespaceName);
+ ns->setAccess(access);
+ ns->setLocation(location());
}
readToken(); // skip '{'
- bool matched = matchDeclList(namespasse);
+ bool matched = matchDeclList(ns);
return matched && match(Tok_RightBrace);
}
@@ -1817,7 +1825,7 @@ bool CppCodeParser::matchUsingDecl()
/*
So far, so good. We have 'using namespace Foo;'.
*/
- usedNamespaces.insert(name);
+ activeNamespaces_.insert(name);
return true;
}
@@ -1915,10 +1923,10 @@ bool CppCodeParser::matchTypedefDecl(InnerNode *parent)
if (!match(Tok_Semicolon))
return false;
- if (parent && !parent->findNode(name, Node::Typedef)) {
- TypedefNode *typedeffe = new TypedefNode(parent, name);
- typedeffe->setAccess(access);
- typedeffe->setLocation(location());
+ if (parent && !parent->findChildNodeByNameAndType(name, Node::Typedef)) {
+ TypedefNode* td = new TypedefNode(parent, name);
+ td->setAccess(access);
+ td->setLocation(location());
}
return true;
}
@@ -1977,9 +1985,9 @@ bool CppCodeParser::matchProperty(InnerNode *parent)
}
if (key == "READ")
- tre->addPropertyFunction(property, value, PropertyNode::Getter);
+ tree_->addPropertyFunction(property, value, PropertyNode::Getter);
else if (key == "WRITE") {
- tre->addPropertyFunction(property, value, PropertyNode::Setter);
+ tree_->addPropertyFunction(property, value, PropertyNode::Setter);
property->setWritable(true);
}
else if (key == "STORED")
@@ -1996,9 +2004,9 @@ bool CppCodeParser::matchProperty(InnerNode *parent)
}
}
else if (key == "RESET")
- tre->addPropertyFunction(property, value, PropertyNode::Resetter);
+ tree_->addPropertyFunction(property, value, PropertyNode::Resetter);
else if (key == "NOTIFY") {
- tre->addPropertyFunction(property, value, PropertyNode::Notifier);
+ tree_->addPropertyFunction(property, value, PropertyNode::Notifier);
} else if (key == "REVISION") {
int revision;
bool ok;
@@ -2130,15 +2138,13 @@ bool CppCodeParser::matchDeclList(InnerNode *parent)
if (match(Tok_LeftParen) && match(Tok_Ident)) {
QString flagsType = previousLexeme();
if (match(Tok_Comma) && match(Tok_Ident)) {
- QString enumType = previousLexeme();
+ QString name = previousLexeme();
TypedefNode *flagsNode = new TypedefNode(parent, flagsType);
flagsNode->setAccess(access);
flagsNode->setLocation(location());
- EnumNode *enumNode =
- static_cast(parent->findNode(enumType,
- Node::Enum));
- if (enumNode)
- enumNode->setFlagsType(flagsNode);
+ EnumNode* en = static_cast(parent->findChildNodeByNameAndType(name, Node::Enum));
+ if (en)
+ en->setFlagsType(flagsNode);
}
}
match(Tok_RightParen);
@@ -2219,14 +2225,14 @@ bool CppCodeParser::matchDocsAndStuff()
FunctionNode *func = 0;
if (matchFunctionDecl(0, &parentPath, &clone)) {
- foreach (const QString &usedNamespace, usedNamespaces) {
- QStringList newPath = usedNamespace.split("::") + parentPath;
- func = tre->findFunctionNode(newPath, clone);
+ foreach (const QString& usedNamespace_, activeNamespaces_) {
+ QStringList newPath = usedNamespace_.split("::") + parentPath;
+ func = tree_->findFunctionNode(newPath, clone);
if (func)
break;
}
if (func == 0)
- func = tre->findFunctionNode(parentPath, clone);
+ func = tree_->findFunctionNode(parentPath, clone);
if (func) {
func->borrowParameterNames(clone);
@@ -2280,7 +2286,7 @@ bool CppCodeParser::matchDocsAndStuff()
if ((*n)->isInnerNode() &&
((InnerNode *)*n)->includes().isEmpty()) {
InnerNode *m = static_cast(*n);
- while (m->parent() != tre->root())
+ while (m->parent() != tree_->root())
m = m->parent();
if (m == *n)
((InnerNode *)*n)->addInclude((*n)->name());
@@ -2309,7 +2315,7 @@ bool CppCodeParser::matchDocsAndStuff()
Signals are implemented in uninteresting files
generated by moc.
*/
- node = tre->findFunctionNode(parentPath, clone);
+ node = tree_->findFunctionNode(parentPath, clone);
if (node != 0 && node->metaness() != FunctionNode::Signal)
node->setLocation(clone->location());
delete clone;
@@ -2421,7 +2427,7 @@ void CppCodeParser::instantiateIteratorMacro(const QString &container,
Tokenizer stringTokenizer(loc, latin1);
tokenizer = &stringTokenizer;
readToken();
- matchDeclList(tre->root());
+ matchDeclList(tree_->root());
}
void CppCodeParser::createExampleFileNodes(FakeNode *fake)
diff --git a/src/tools/qdoc/cppcodeparser.h b/src/tools/qdoc/cppcodeparser.h
index f33ff64..74abb99 100644
--- a/src/tools/qdoc/cppcodeparser.h
+++ b/src/tools/qdoc/cppcodeparser.h
@@ -165,7 +165,7 @@ private:
void createExampleFileNodes(FakeNode *fake);
QMap nodeTypeMap;
- Tree *tre;
+ Tree* tree_;
Tokenizer *tokenizer;
int tok;
Node::Access access;
@@ -179,7 +179,7 @@ private:
QString mutableSequentialIteratorDefinition;
QString associativeIteratorDefinition;
QString mutableAssociativeIteratorDefinition;
- QSet usedNamespaces;
+ QSet activeNamespaces_;
QMap sequentialIteratorClasses;
QMap mutableSequentialIteratorClasses;
QMap associativeIteratorClasses;
diff --git a/src/tools/qdoc/ditaxmlgenerator.cpp b/src/tools/qdoc/ditaxmlgenerator.cpp
index 8fa7f93..1458b64 100644
--- a/src/tools/qdoc/ditaxmlgenerator.cpp
+++ b/src/tools/qdoc/ditaxmlgenerator.cpp
@@ -418,7 +418,6 @@ DitaXmlGenerator::DitaXmlGenerator()
sectionNestingLevel(0),
tableColumnCount(0),
funcLeftParen("\\S(\\()"),
- tree_(0),
nodeTypeMaps(Node::LastType,0),
nodeSubtypeMaps(Node::LastSubtype,0),
pageTypeMaps(Node::OnBeyondZebra,0)
@@ -618,7 +617,7 @@ GuidMap* DitaXmlGenerator::lookupGuidMap(const QString& fileName)
This is where the DITA XML files are written.
\note The file is created in PageGenerator::generateTree().
*/
-void DitaXmlGenerator::generateTree(const Tree *tree)
+void DitaXmlGenerator::generateTree(Tree *tree)
{
tree_ = tree;
nonCompatClasses.clear();
@@ -638,6 +637,7 @@ void DitaXmlGenerator::generateTree(const Tree *tree)
findAllSince(tree->root());
Generator::generateTree(tree);
+ generateCollisionPages();
writeDitaMap(tree);
}
@@ -1269,19 +1269,15 @@ int DitaXmlGenerator::generateAtom(const Atom *atom,
const Node *node = 0;
QString myLink = getLink(atom, relative, marker, &node);
if (myLink.isEmpty()) {
- relative->doc().location().warning(tr("Can't link to '%1' in %2")
- .arg(atom->string())
- .arg(marker->plainFullName(relative)));
+ myLink = getCollisionLink(atom);
+ }
+ if (myLink.isEmpty()) {
+ relative->doc().location().warning(tr("Can't link to '%1'")
+ .arg(atom->string()));
}
else if (!inSectionHeading) {
beginLink(myLink);
}
-#if 0
- else {
- //xmlWriter().writeCharacters(atom->string());
- //qDebug() << "MYLINK:" << myLink << outFileName() << atom->string();
- }
-#endif
skipAhead = 1;
}
break;
@@ -1744,7 +1740,7 @@ int DitaXmlGenerator::generateAtom(const Atom *atom,
for marking up the code. I don't know what that means exactly.
*/
void
-DitaXmlGenerator::generateClassLikeNode(const InnerNode* inner, CodeMarker* marker)
+DitaXmlGenerator::generateClassLikeNode(InnerNode* inner, CodeMarker* marker)
{
QList::ConstIterator s;
@@ -2143,8 +2139,8 @@ DitaXmlGenerator::generateClassLikeNode(const InnerNode* inner, CodeMarker* mark
writeEndTag(); //
}
else if ((inner->type() == Node::Fake) && (inner->subType() == Node::QmlClass)) {
- const QmlClassNode* qcn = const_cast(static_cast(inner));
- const ClassNode* cn = qcn->classNode();
+ QmlClassNode* qcn = const_cast(static_cast(inner));
+ ClassNode* cn = qcn->classNode();
rawTitle = marker->plainName(inner);
fullTitle = marker->plainFullName(inner);
title = rawTitle + " Element";
@@ -2248,7 +2244,7 @@ void DitaXmlGenerator::writeXrefListItem(const QString& link, const QString& tex
Generate the DITA page for a qdoc file that doesn't map
to an underlying c++ file.
*/
-void DitaXmlGenerator::generateFakeNode(const FakeNode* fake, CodeMarker* marker)
+void DitaXmlGenerator::generateFakeNode(FakeNode* fake, CodeMarker* marker)
{
/*
If the fake node is a page node, and if the page type
@@ -2499,6 +2495,9 @@ void DitaXmlGenerator::generateHeader(const Node* node,
case Node::ExternalPage: // not used
outputclass = "externalpage";
break;
+ case Node::Collision:
+ outputclass = "collision";
+ break;
default:
outputclass = "page";
}
@@ -3302,16 +3301,11 @@ void DitaXmlGenerator::generateOverviewList(const Node* relative, CodeMarker* /*
else if (!isGroupPage) {
// If we encounter a page that belongs to a group then
// we add that page to the list for that group.
- const FakeNode* groupNode =
- static_cast(tree_->root()->findNode(group, Node::Fake));
- if (groupNode)
- fakeNodeMap[groupNode].insert(sortKey, fakeNode);
- //else
- // uncategorizedNodeMap.insert(sortKey, fakeNode);
- }// else
- // uncategorizedNodeMap.insert(sortKey, fakeNode);
- }// else
- // uncategorizedNodeMap.insert(sortKey, fakeNode);
+ const FakeNode* gn = tree_->findGroupNode(QStringList(group));
+ if (gn)
+ fakeNodeMap[gn].insert(sortKey, fakeNode);
+ }
+ }
}
}
@@ -3877,8 +3871,6 @@ QString DitaXmlGenerator::guidForNode(const Node* node)
return node->guid();
case Node::Variable:
return node->guid();
- case Node::Target:
- return node->guid();
}
return QString();
}
@@ -4143,7 +4135,7 @@ const Node* DitaXmlGenerator::findNodeForTarget(const QString& target,
node = relative;
}
else if (target.endsWith(".html")) {
- node = tree_->root()->findNode(target, Node::Fake);
+ node = tree_->root()->findChildNodeByNameAndType(target, Node::Fake);
}
else if (marker) {
node = marker->resolveTarget(target, tree_, relative);
@@ -4204,7 +4196,7 @@ QString DitaXmlGenerator::getLink(const Atom* atom,
*node = relative;
}
else if (first.endsWith(".html")) {
- *node = tree_->root()->findNode(first, Node::Fake);
+ *node = tree_->root()->findChildNodeByNameAndType(first, Node::Fake);
}
else {
*node = marker->resolveTarget(first, tree_, relative);
@@ -4292,17 +4284,6 @@ QString DitaXmlGenerator::getLink(const Atom* atom,
return link;
}
-/*!
- This function can be called if getLink() returns an empty
- string.
- */
-QString DitaXmlGenerator::getDisambiguationLink(const Atom *, CodeMarker *)
-{
- qDebug() << "Unimplemented function called: "
- << "QString DitaXmlGenerator::getDisambiguationLink()";
- return QString();
-}
-
void DitaXmlGenerator::generateIndex(const QString& fileBase,
const QString& url,
const QString& title)
@@ -4419,19 +4400,19 @@ void DitaXmlGenerator::generateQmlSummary(const Section& section,
Outputs the DITA detailed documentation for a section
on a QML element reference page.
*/
-void DitaXmlGenerator::generateDetailedQmlMember(const Node* node,
+void DitaXmlGenerator::generateDetailedQmlMember(Node* node,
const InnerNode* relative,
CodeMarker* marker)
{
QString marked;
- const QmlPropertyNode* qpn = 0;
+ QmlPropertyNode* qpn = 0;
if (node->subType() == Node::QmlPropertyGroup) {
const QmlPropGroupNode* qpgn = static_cast(node);
NodeList::ConstIterator p = qpgn->childNodes().begin();
writeStartTag(DT_ul);
while (p != qpgn->childNodes().end()) {
if ((*p)->type() == Node::QmlProperty) {
- qpn = static_cast(*p);
+ qpn = static_cast(*p);
writeStartTag(DT_li);
writeGuidAttribute((Node*)qpn);
QString attr;
@@ -4457,7 +4438,7 @@ void DitaXmlGenerator::generateDetailedQmlMember(const Node* node,
writeEndTag(); //
}
else if (node->type() == Node::QmlProperty) {
- qpn = static_cast(node);
+ qpn = static_cast(node);
/*
If the QML property node has a single subproperty,
override, replace qpn with that override node and
@@ -4466,7 +4447,7 @@ void DitaXmlGenerator::generateDetailedQmlMember(const Node* node,
if (qpn->qmlPropNodes().size() == 1) {
Node* n = qpn->qmlPropNodes().at(0);
if (n->type() == Node::QmlProperty)
- qpn = static_cast(n);
+ qpn = static_cast(n);
}
/*
Now qpn either has no overrides, or it has more
@@ -4600,10 +4581,9 @@ void DitaXmlGenerator::generateQmlInherits(const QmlClassNode* qcn, CodeMarker*
If there is no class node, or if the class node status
is set to Node::Internal, do nothing.
*/
-void DitaXmlGenerator::generateQmlInstantiates(const QmlClassNode* qcn,
- CodeMarker* marker)
+void DitaXmlGenerator::generateQmlInstantiates(QmlClassNode* qcn, CodeMarker* marker)
{
- const ClassNode* cn = qcn->classNode();
+ ClassNode* cn = qcn->classNode();
if (cn && (cn->status() != Node::Internal)) {
writeStartTag(DT_p);
xmlWriter().writeAttribute("outputclass","instantiates");
@@ -4631,8 +4611,7 @@ void DitaXmlGenerator::generateQmlInstantiates(const QmlClassNode* qcn,
If there is no QML element, or if the class node status
is set to Node::Internal, do nothing.
*/
-void DitaXmlGenerator::generateInstantiatedBy(const ClassNode* cn,
- CodeMarker* marker)
+void DitaXmlGenerator::generateInstantiatedBy(ClassNode* cn, CodeMarker* marker)
{
if (cn && cn->status() != Node::Internal && cn->qmlElement() != 0) {
const QmlClassNode* qcn = cn->qmlElement();
@@ -5628,13 +5607,13 @@ void DitaXmlGenerator::writeNestedClasses(const Section& s,
Recursive writing of DITA XML files from the root \a node.
*/
void
-DitaXmlGenerator::generateInnerNode(const InnerNode* node)
+DitaXmlGenerator::generateInnerNode(InnerNode* node)
{
if (!node->url().isNull())
return;
if (node->type() == Node::Fake) {
- const FakeNode *fakeNode = static_cast(node);
+ FakeNode* fakeNode = static_cast(node);
if (fakeNode->subType() == Node::ExternalPage)
return;
if (fakeNode->subType() == Node::Image)
@@ -5653,27 +5632,38 @@ DitaXmlGenerator::generateInnerNode(const InnerNode* node)
CodeMarker *marker = CodeMarker::markerForFileName(node->location().filePath());
if (node->parent() != 0) {
- if (!node->name().endsWith(".ditamap"))
- beginSubPage(node, fileName(node));
- if (node->type() == Node::Namespace || node->type() == Node::Class) {
- generateClassLikeNode(node, marker);
+ /*
+ Skip name collision nodes here and process them
+ later in generateCollisionPages(). Each one is
+ appended to a list for later.
+ */
+ if ((node->type() == Node::Fake) && (node->subType() == Node::Collision)) {
+ NameCollisionNode* ncn = static_cast(node);
+ collisionNodes.append(const_cast(ncn));
}
- else if (node->type() == Node::Fake) {
- if (node->subType() == Node::HeaderFile)
- generateClassLikeNode(node, marker);
- else if (node->subType() == Node::QmlClass)
+ else {
+ if (!node->name().endsWith(".ditamap"))
+ beginSubPage(node, fileName(node));
+ if (node->type() == Node::Namespace || node->type() == Node::Class) {
generateClassLikeNode(node, marker);
- else
- generateFakeNode(static_cast(node), marker);
+ }
+ else if (node->type() == Node::Fake) {
+ if (node->subType() == Node::HeaderFile)
+ generateClassLikeNode(node, marker);
+ else if (node->subType() == Node::QmlClass)
+ generateClassLikeNode(node, marker);
+ else
+ generateFakeNode(static_cast(node), marker);
+ }
+ if (!node->name().endsWith(".ditamap"))
+ endSubPage();
}
- if (!node->name().endsWith(".ditamap"))
- endSubPage();
}
NodeList::ConstIterator c = node->childNodes().begin();
while (c != node->childNodes().end()) {
if ((*c)->isInnerNode() && (*c)->access() != Node::Private)
- generateInnerNode((const InnerNode*) *c);
+ generateInnerNode((InnerNode*)*c);
++c;
}
}
@@ -5819,8 +5809,6 @@ Node* DitaXmlGenerator::collectNodesByTypeAndSubtype(const InnerNode* parent)
break;
case Node::Variable:
break;
- case Node::Target:
- break;
case Node::QmlProperty:
break;
case Node::QmlSignal:
@@ -5840,7 +5828,7 @@ Node* DitaXmlGenerator::collectNodesByTypeAndSubtype(const InnerNode* parent)
Creates the DITA map for the qdoc run. The map is written
to the file \e{qt.ditamap" in the DITA XML output directory.
*/
-void DitaXmlGenerator::writeDitaMap(const Tree *tree)
+void DitaXmlGenerator::writeDitaMap(Tree *tree)
{
QString doctype;
@@ -6374,5 +6362,125 @@ QString DitaXmlGenerator::stripMarkup(const QString& src) const
return text;
}
+/*!
+ We delayed generation of the collision pages until now, after
+ all the other pages have been generated. We do this because we might
+ encounter a link command that tries to link to a target on a QML
+ type page, but the link doesn't specify the module identifer
+ for the QML type, and the QML type name without a module
+ identifier is ambiguous. When such a link is found, qdoc can't find
+ the target, so it appends the target to the NameCollisionNode. After
+ the tree has been traversed and all these ambiguous links have been
+ added to the name collision nodes, this function is called. The list
+ of collision nodes is traversed here, and the collision page for
+ each collision is generated. The collision page will not only
+ disambiguate links to the QML type pages, but it will also disambiguate
+ links to properties, section headers, etc.
+ */
+void DitaXmlGenerator::generateCollisionPages()
+{
+ if (collisionNodes.isEmpty())
+ return;
+
+ for (int i=0; ichildNodes();
+ if (!nl.isEmpty()) {
+ NodeList::ConstIterator it = nl.begin();
+ while (it != nl.end()) {
+ if (!(*it)->isInternal())
+ collisions.append(*it);
+ ++it;
+ }
+ }
+ if (collisions.size() <= 1)
+ continue;
+
+ ncn->clearCurrentChild();
+ beginSubPage(ncn, Generator::fileName(ncn));
+ QString fullTitle = ncn->fullTitle();
+ QString ditaTitle = fullTitle;
+ CodeMarker* marker = CodeMarker::markerForFileName(ncn->location().filePath());
+ if (ncn->isQmlNode()) {
+ // Replace the marker with a QML code marker.
+ if (ncn->isQmlNode())
+ marker = CodeMarker::markerForLanguage(QLatin1String("QML"));
+ }
+
+ generateHeader(ncn, ditaTitle);
+ writeProlog(ncn);
+ writeStartTag(DT_body);
+ enterSection(QString(),QString());
+
+ NodeMap nm;
+ for (int i=0; iqmlModuleIdentifier().isEmpty())
+ t = n->qmlModuleIdentifier() + " ";
+ t += protectEnc(fullTitle);
+ nm.insertMulti(t,n);
+ }
+ generateAnnotatedList(ncn, marker, nm);
+
+ QList targets;
+ if (!ncn->linkTargets().isEmpty()) {
+ QMap::ConstIterator t = ncn->linkTargets().begin();
+ while (t != ncn->linkTargets().end()) {
+ int count = 0;
+ for (int i=0; i(collisions.at(i));
+ if (n->findChildNodeByName(t.key())) {
+ ++count;
+ if (count > 1) {
+ targets.append(t.key());
+ break;
+ }
+ }
+ }
+ ++t;
+ }
+ }
+ if (!targets.isEmpty()) {
+ QList::ConstIterator t = targets.begin();
+ while (t != targets.end()) {
+ writeStartTag(DT_p);
+ writeGuidAttribute(Doc::canonicalTitle(*t));
+ xmlWriter().writeAttribute("outputclass","h2");
+ writeCharacters(protectEnc(*t));
+ writeEndTag(); //
+ writeStartTag(DT_ul);
+ for (int i=0; i(collisions.at(i));
+ Node* p = n->findChildNodeByName(*t);
+ if (p) {
+ QString link = linkForNode(p,0);
+ QString label;
+ if (!n->qmlModuleIdentifier().isEmpty())
+ label = n->qmlModuleIdentifier() + "::";
+ label += n->name() + "::" + p->name();
+ writeStartTag(DT_li);
+ writeStartTag(DT_xref);
+ xmlWriter().writeAttribute("href", link);
+ writeCharacters(protectEnc(label));
+ writeEndTag(); //
+ writeEndTag(); //
+ }
+ }
+ writeEndTag(); //
+ ++t;
+ }
+ }
+ leaveSection(); //
+ writeEndTag(); //