qdoc: Allow choice of linking to QML or CPP

This update enables using QML or CPP as the parameter
in square brackets for the \l command. You will use this
when, for example, there exist both a C++ class named
QWidget and a QML type named QWidget and your \l {QWidget}
links to the wrong one.

Suppose you write \l {QWidget} expecting it to link
to the QML type named QWidget, but it links to the C++
class named QWidget. Then write this instead:

\l [QML] {QWidget}

Or if you wrote \l {QWidget} expecting it to link to
the C++ class, but it links to the QML type, write this
instead:

\l [CPP] {QWidget}

A qdoc warning is printed if qdoc can not recognize the
parameter in square brackets.

There will be a further update to complete this task for
implementing the other type of parameter that can be in
the square brackets.

Task-number: QTBUG-39221
Change-Id: I5dd85478f968025ecbe337a8aabcc31d8b12a86d
Reviewed-by: Topi Reiniö <topi.reinio@digia.com>
This commit is contained in:
Martin Smith 2014-07-28 14:21:37 +02:00
parent 0da4ddfcc5
commit a2c432e978
12 changed files with 704 additions and 474 deletions

View File

@ -369,12 +369,15 @@ void Atom::dump() const
} }
/*! /*!
The only constructor for LinkAtom. It only create an Atom The only constructor for LinkAtom. It creates an Atom of
of type Atom::Link with \a p1 being the link text. \a p2 type Atom::Link. \a p1 being the link target. \a p2 is the
contains some search parameters. parameters in square brackets. Normally there is just one
word in the square brackets, but there can be up to three
words separated by spaces. The constructor splits \a p2 on
the space character.
*/ */
LinkAtom::LinkAtom(const QString& p1, const QString& p2) LinkAtom::LinkAtom(const QString& p1, const QString& p2)
: Atom(p1), genus_(DontCare), goal_(Node::NoType), domain_(0) : Atom(p1), genus_(Node::DontCare), goal_(Node::NoType), domain_(0)
{ {
QStringList params = p2.toLower().split(QLatin1Char(' ')); QStringList params = p2.toLower().split(QLatin1Char(' '));
foreach (const QString& p, params) { foreach (const QString& p, params) {
@ -388,10 +391,15 @@ LinkAtom::LinkAtom(const QString& p1, const QString& p2)
if (goal_ != Node::NoType) if (goal_ != Node::NoType)
continue; continue;
} }
if (p == "qml") if (p == "qml") {
genus_ = QML; genus_ = Node::QML;
else if (p == "cpp") continue;
genus_ = CPP; }
if (p == "cpp") {
genus_ = Node::CPP;
continue;
}
break;
} }
} }

View File

@ -140,8 +140,6 @@ public:
Last = UnknownCommand Last = UnknownCommand
}; };
enum NodeGenus { DontCare, CPP, QML };
friend class LinkAtom; friend class LinkAtom;
Atom(const QString& string) Atom(const QString& string)
@ -201,7 +199,7 @@ public:
const QStringList& strings() const { return strs; } const QStringList& strings() const { return strs; }
virtual bool isLinkAtom() const { return false; } virtual bool isLinkAtom() const { return false; }
virtual NodeGenus genus() const { return DontCare; } virtual Node::Genus genus() const { return Node::DontCare; }
virtual bool specifiesDomain() const { return false; } virtual bool specifiesDomain() const { return false; }
virtual Tree* domain() const { return 0; } virtual Tree* domain() const { return 0; }
virtual Node::Type goal() const { return Node::NoType; } virtual Node::Type goal() const { return Node::NoType; }
@ -221,13 +219,13 @@ class LinkAtom : public Atom
virtual ~LinkAtom() { } virtual ~LinkAtom() { }
virtual bool isLinkAtom() const { return true; } virtual bool isLinkAtom() const { return true; }
virtual NodeGenus genus() const { return genus_; } virtual Node::Genus genus() const { return genus_; }
virtual bool specifiesDomain() const { return (domain_ != 0); } virtual bool specifiesDomain() const { return (domain_ != 0); }
virtual Tree* domain() const { return domain_; } virtual Tree* domain() const { return domain_; }
virtual Node::Type goal() const { return goal_; } virtual Node::Type goal() const { return goal_; }
protected: protected:
NodeGenus genus_; Node::Genus genus_;
Node::Type goal_; Node::Type goal_;
Tree* domain_; Tree* domain_;
}; };

View File

@ -3444,7 +3444,7 @@ void DitaXmlGenerator::writeText(const QString& markedCode, const Node* relative
text.clear(); text.clear();
} }
par1 = QStringRef(); par1 = QStringRef();
n = qdb_->resolveFunctionTarget(arg.toString(), relative); n = qdb_->findFunctionNode(arg.toString(), relative, Node::DontCare);
addLink(linkForNode(n, relative), arg); addLink(linkForNode(n, relative), arg);
break; break;
case 1: case 1:
@ -3455,7 +3455,7 @@ void DitaXmlGenerator::writeText(const QString& markedCode, const Node* relative
text.clear(); text.clear();
} }
par1 = QStringRef(); par1 = QStringRef();
n = qdb_->resolveType(arg.toString(), relative); n = qdb_->findTypeNode(arg.toString(), relative);
if (n && n->isQmlBasicType()) { if (n && n->isQmlBasicType()) {
if (relative && relative->isQmlType()) if (relative && relative->isQmlType())
addLink(linkForNode(n, relative), arg); addLink(linkForNode(n, relative), arg);
@ -3733,65 +3733,50 @@ QString DitaXmlGenerator::fileName(const Node* node)
*/ */
QString DitaXmlGenerator::getLink(const Atom *atom, const Node *relative, const Node** node) QString DitaXmlGenerator::getLink(const Atom *atom, const Node *relative, const Node** node)
{ {
if (atom->string().contains(QLatin1Char(':')) && (atom->string().startsWith("file:") || const QString& t = atom->string();
atom->string().startsWith("http:") || if (t.at(0) == QChar('h')) {
atom->string().startsWith("https:") || if (t.startsWith("http:") || t.startsWith("https:"))
atom->string().startsWith("ftp:") || return t;
atom->string().startsWith("mailto:"))) { }
return atom->string(); // It's some kind of protocol. else if (t.at(0) == QChar('f')) {
if (t.startsWith("file:") || t.startsWith("ftp:"))
return t;
}
else if (t.at(0) == QChar('m')) {
if (t.startsWith("mailto:"))
return t;
} }
QString ref; QString ref;
QString link;
QStringList path = atom->string().split("#");
QString first = path.first().trimmed();
*node = 0; *node = qdb_->findNodeForAtom(atom, relative, ref);
if (first.isEmpty())
*node = relative; // search for a target on the current page.
else {
if (first.endsWith(".html")) { // The target is an html file.
*node = qdb_->findNodeByNameAndType(QStringList(first), Node::Document);
}
else if (first.endsWith("()")) { // The target is a C++ function or QML method.
*node = qdb_->resolveFunctionTarget(first, relative);
}
else {
*node = qdb_->resolveTarget(first, relative);
if (!(*node))
*node = qdb_->findDocNodeByTitle(first);
if (!(*node)) {
*node = qdb_->findUnambiguousTarget(first, ref);
if (*node && !(*node)->url().isEmpty() && !ref.isEmpty()) {
QString final = (*node)->url() + "#" + ref;
return final;
}
}
}
}
if (!(*node)) if (!(*node))
return link; // empty return QString();
if (!(*node)->url().isEmpty()) QString url = (*node)->url();
return (*node)->url(); if (!url.isEmpty()) {
if (!path.isEmpty()) {
ref = qdb_->findTarget(path.first(), *node);
if (ref.isEmpty()) if (ref.isEmpty())
return link; // empty return url;
int hashtag = url.lastIndexOf(QChar('#'));
if (hashtag != -1)
url.truncate(hashtag);
return url + "#" + ref;
} }
/* /*
Given that *node is not null, we now cconstruct a link Given that *node is not null, we now cconstruct a link
to the page that *node represents, and then if we found to the page that *node represents, and then if we found
a target on that page, we connect the target to the link a target on that page, we connect the target to the link
with '#'. with '#'.
*/ */
link = linkForNode(*node, relative); QString link = linkForNode(*node, relative);
if (*node && (*node)->subType() == Node::Image) if (*node && (*node)->subType() == Node::Image)
link = "images/used-in-examples/" + link; link = "images/used-in-examples/" + link;
if (!ref.isEmpty()) if (!ref.isEmpty()) {
int hashtag = link.lastIndexOf(QChar('#'));
if (hashtag != -1)
link.truncate(hashtag);
link += QLatin1Char('#') + ref; link += QLatin1Char('#') + ref;
}
return link; return link;
} }
@ -3810,31 +3795,20 @@ QString DitaXmlGenerator::getAutoLink(const Atom *atom, const Node *relative, co
{ {
QString ref; QString ref;
QString link; QString link;
QString target = atom->string().trimmed();
*node = 0;
if (target.endsWith("()")) { // The target is a C++ function or QML method.
*node = qdb_->resolveFunctionTarget(target, relative);
}
else {
*node = qdb_->resolveTarget(target, relative);
if (!(*node)) {
*node = qdb_->findDocNodeByTitle(target);
}
if (!(*node)) {
*node = qdb_->findUnambiguousTarget(target, ref);
if (*node && !(*node)->url().isEmpty() && !ref.isEmpty()) {
QString final = (*node)->url() + "#" + ref;
return final;
}
}
}
*node = qdb_->findNodeForAtom(atom, relative, ref);
if (!(*node)) if (!(*node))
return link; // empty return QString();
if (!(*node)->url().isEmpty()) QString url = (*node)->url();
return (*node)->url(); if (!url.isEmpty()) {
if (ref.isEmpty())
return url;
int hashtag = url.lastIndexOf(QChar('#'));
if (hashtag != -1)
url.truncate(hashtag);
return url + "#" + ref;
}
link = linkForNode(*node, relative); link = linkForNode(*node, relative);
if (!ref.isEmpty()) if (!ref.isEmpty())
@ -3963,40 +3937,7 @@ void DitaXmlGenerator::generateStatus(const Node* node, CodeMarker* marker)
Generator::generateStatus(node, marker); Generator::generateStatus(node, marker);
break; break;
case Node::Compat: case Node::Compat:
if (node->isInnerNode()) { // Porting to Qt 4 no longer supported
text << Atom::ParaLeft
<< Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD)
<< "This "
<< typeString(node)
<< " is part of the Qt 3 support library."
<< Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD)
<< " It is provided to keep old source code working. "
<< "We strongly advise against "
<< "using it in new code. See ";
const DocNode *docNode = qdb_->findDocNodeByTitle("Porting To Qt 4");
QString ref;
if (docNode && node->type() == Node::Class) {
QString oldName(node->name());
oldName.remove(QLatin1Char('3'));
ref = qdb_->findTarget(oldName,docNode);
}
if (!ref.isEmpty()) {
QString fn = fileName(docNode);
QString guid = lookupGuid(fn, ref);
text << Atom(Atom::GuidLink, fn + QLatin1Char('#') + guid);
}
else
text << Atom(Atom::Link, "Porting to Qt 4");
text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
<< Atom(Atom::String, "Porting to Qt 4")
<< Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
<< " for more information."
<< Atom::ParaRight;
}
generateText(text, node, marker);
break; break;
default: default:
Generator::generateStatus(node, marker); Generator::generateStatus(node, marker);
@ -4652,7 +4593,7 @@ void DitaXmlGenerator::replaceTypesWithLinks(const Node* n, const InnerNode* par
} }
i += 2; i += 2;
if (parseArg(src, typeTag, &i, srcSize, &arg, &par1)) { if (parseArg(src, typeTag, &i, srcSize, &arg, &par1)) {
const Node* tn = qdb_->resolveType(arg.toString(), parent); const Node* tn = qdb_->findTypeNode(arg.toString(), parent);
if (tn) { if (tn) {
//Do not generate a link from a C++ function to a QML Basic Type (such as int) //Do not generate a link from a C++ function to a QML Basic Type (such as int)
if (n->isFunction() && tn->isQmlBasicType()) if (n->isFunction() && tn->isQmlBasicType())
@ -6159,7 +6100,7 @@ void DitaXmlGenerator::generateCollisionPages()
int count = 0; int count = 0;
for (int i=0; i<collisions.size(); ++i) { for (int i=0; i<collisions.size(); ++i) {
InnerNode* n = static_cast<InnerNode*>(collisions.at(i)); InnerNode* n = static_cast<InnerNode*>(collisions.at(i));
if (n->findChildNode(t.key())) { if (n->findChildNode(t.key(), Node::DontCare)) {
++count; ++count;
if (count > 1) { if (count > 1) {
targets.append(t.key()); targets.append(t.key());
@ -6181,7 +6122,7 @@ void DitaXmlGenerator::generateCollisionPages()
writeStartTag(DT_ul); writeStartTag(DT_ul);
for (int i=0; i<collisions.size(); ++i) { for (int i=0; i<collisions.size(); ++i) {
InnerNode* n = static_cast<InnerNode*>(collisions.at(i)); InnerNode* n = static_cast<InnerNode*>(collisions.at(i));
Node* p = n->findChildNode(*t); Node* p = n->findChildNode(*t, Node::DontCare);
if (p) { if (p) {
QString link = linkForNode(p,0); QString link = linkForNode(p,0);
QString label; QString label;

View File

@ -816,7 +816,7 @@ int HtmlGenerator::generateAtom(const Atom *atom, const Node *relative, CodeMark
inObsoleteLink = false; inObsoleteLink = false;
const Node *node = 0; const Node *node = 0;
QString link = getLink(atom, relative, &node); QString link = getLink(atom, relative, &node);
if (link.isEmpty() && !noLinkErrors()) { if (link.isEmpty() && (node != relative) && !noLinkErrors()) {
relative->doc().location().warning(tr("Can't link to '%1'").arg(atom->string())); relative->doc().location().warning(tr("Can't link to '%1'").arg(atom->string()));
} }
else { else {
@ -1493,7 +1493,7 @@ void HtmlGenerator::generateCollisionPages()
int count = 0; int count = 0;
for (int i=0; i<collisions.size(); ++i) { for (int i=0; i<collisions.size(); ++i) {
InnerNode* n = static_cast<InnerNode*>(collisions.at(i)); InnerNode* n = static_cast<InnerNode*>(collisions.at(i));
if (n->findChildNode(t.key())) { if (n->findChildNode(t.key(), Node::DontCare)) {
++count; ++count;
if (count > 1) { if (count > 1) {
targets.append(t.key()); targets.append(t.key());
@ -1512,7 +1512,7 @@ void HtmlGenerator::generateCollisionPages()
out() << "<ul>\n"; out() << "<ul>\n";
for (int i=0; i<collisions.size(); ++i) { for (int i=0; i<collisions.size(); ++i) {
InnerNode* n = static_cast<InnerNode*>(collisions.at(i)); InnerNode* n = static_cast<InnerNode*>(collisions.at(i));
Node* p = n->findChildNode(*t); Node* p = n->findChildNode(*t, Node::DontCare);
if (p) { if (p) {
QString link = linkForNode(p,0); QString link = linkForNode(p,0);
QString label; QString label;
@ -3287,7 +3287,7 @@ QString HtmlGenerator::highlightedCode(const QString& markedCode,
if (src.at(i) == charLangle && src.at(i + 1) == charAt) { if (src.at(i) == charLangle && src.at(i + 1) == charAt) {
i += 2; i += 2;
if (parseArg(src, funcTag, &i, srcSize, &arg, &par1)) { if (parseArg(src, funcTag, &i, srcSize, &arg, &par1)) {
const Node* n = qdb_->resolveFunctionTarget(par1.toString(), relative); const Node* n = qdb_->findFunctionNode(par1.toString(), relative, Node::DontCare);
QString link = linkForNode(n, relative); QString link = linkForNode(n, relative);
addLink(link, arg, &html); addLink(link, arg, &html);
par1 = QStringRef(); par1 = QStringRef();
@ -3312,7 +3312,7 @@ QString HtmlGenerator::highlightedCode(const QString& markedCode,
bool handled = false; bool handled = false;
if (parseArg(src, typeTag, &i, srcSize, &arg, &par1)) { if (parseArg(src, typeTag, &i, srcSize, &arg, &par1)) {
par1 = QStringRef(); par1 = QStringRef();
const Node* n = qdb_->resolveType(arg.toString(), relative); const Node* n = qdb_->findTypeNode(arg.toString(), relative);
html += QLatin1String("<span class=\"type\">"); html += QLatin1String("<span class=\"type\">");
if (n && n->isQmlBasicType()) { if (n && n->isQmlBasicType()) {
if (relative && relative->isQmlType()) if (relative && relative->isQmlType())
@ -3330,9 +3330,8 @@ QString HtmlGenerator::highlightedCode(const QString& markedCode,
if (arg.at(0) == QChar('&')) if (arg.at(0) == QChar('&'))
html += arg; html += arg;
else { else {
// zzz resolveClassTarget() const Node* n = qdb_->findNodeForInclude(QStringList(arg.toString()));
const Node* n = qdb_->resolveTarget(arg.toString(), relative); if (n && n != relative)
if (n)
addLink(linkForNode(n,relative), arg, &html); addLink(linkForNode(n,relative), arg, &html);
else else
html += arg; html += arg;
@ -3659,8 +3658,6 @@ QString HtmlGenerator::refForNode(const Node *node)
return registerRef(ref); return registerRef(ref);
} }
#define DEBUG_ABSTRACT 0
/*! /*!
This function is called for links, i.e. for words that This function is called for links, i.e. for words that
are marked with the qdoc link command. For autolinks are marked with the qdoc link command. For autolinks
@ -3691,7 +3688,7 @@ QString HtmlGenerator::getLink(const Atom *atom, const Node *relative, const Nod
QString ref; QString ref;
*node = qdb_->findNode(atom, relative, ref); *node = qdb_->findNodeForAtom(atom, relative, ref);
if (!(*node)) if (!(*node))
return QString(); return QString();
@ -3734,31 +3731,20 @@ QString HtmlGenerator::getAutoLink(const Atom *atom, const Node *relative, const
{ {
QString ref; QString ref;
QString link; QString link;
QString target = atom->string().trimmed();
*node = 0;
if (target.endsWith("()")) { // The target is a C++ function or QML method.
*node = qdb_->resolveFunctionTarget(target, relative);
}
else {
*node = qdb_->resolveTarget(target, relative);
if (!(*node)) {
*node = qdb_->findDocNodeByTitle(target);
}
if (!(*node)) {
*node = qdb_->findUnambiguousTarget(target, ref);
if (*node && !(*node)->url().isEmpty() && !ref.isEmpty()) {
QString final = (*node)->url() + "#" + ref;
return final;
}
}
}
*node = qdb_->findNodeForAtom(atom, relative, ref);
if (!(*node)) if (!(*node))
return link; // empty return QString();
if (!(*node)->url().isEmpty()) QString url = (*node)->url();
return (*node)->url(); if (!url.isEmpty()) {
if (ref.isEmpty())
return url;
int hashtag = url.lastIndexOf(QChar('#'));
if (hashtag != -1)
url.truncate(hashtag);
return url + "#" + ref;
}
link = linkForNode(*node, relative); link = linkForNode(*node, relative);
if (!ref.isEmpty()) if (!ref.isEmpty())
@ -3776,7 +3762,7 @@ QString HtmlGenerator::getAutoLink(const Atom *atom, const Node *relative, const
*/ */
QString HtmlGenerator::linkForNode(const Node *node, const Node *relative) QString HtmlGenerator::linkForNode(const Node *node, const Node *relative)
{ {
if (node == 0 || node == relative) if (node == 0)
return QString(); return QString();
if (!node->url().isEmpty()) if (!node->url().isEmpty())
return node->url(); return node->url();
@ -3977,26 +3963,7 @@ void HtmlGenerator::generateStatus(const Node *node, CodeMarker *marker)
Generator::generateStatus(node, marker); Generator::generateStatus(node, marker);
break; break;
case Node::Compat: case Node::Compat:
if (node->isInnerNode()) { // Porting to Qt 4 no longer supported
text << Atom::ParaLeft
<< Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD)
<< "This "
<< typeString(node)
<< " is part of the Qt 3 support library."
<< Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD)
<< " It is provided to keep old source code working. "
<< "We strongly advise against "
<< "using it in new code. See ";
text << Atom(Atom::Link, "Porting to Qt 4");
text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
<< Atom(Atom::String, "Porting to Qt 4")
<< Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
<< " for more information."
<< Atom::ParaRight;
}
generateText(text, node, marker);
break; break;
default: default:
Generator::generateStatus(node, marker); Generator::generateStatus(node, marker);

View File

@ -46,6 +46,7 @@
#include <quuid.h> #include <quuid.h>
#include "qdocdatabase.h" #include "qdocdatabase.h"
#include <qdebug.h> #include <qdebug.h>
#include "generator.h"
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@ -717,22 +718,38 @@ InnerNode::~InnerNode()
} }
/*! /*!
Find the node in this node's children that has the If \a genus is \c{Node::DontCare}, find the first node in
given \a name. If this node is a QML class node, be this node's child list that has the given \a name. If this
sure to also look in the children of its property node is a QML type, be sure to also look in the children
group nodes. Return the matching node or 0. of its property group nodes. Return the matching node or 0.
If \a genus is either \c{Node::CPP} or \c {Node::QML}, then
find all this node's children that have the given \a name,
and return the one that satisfies the \a genus requirement.
*/ */
Node *InnerNode::findChildNode(const QString& name) const Node *InnerNode::findChildNode(const QString& name, Node::Genus genus) const
{ {
Node *node = childMap.value(name); if (genus == Node::DontCare) {
if (node && !node->isQmlPropertyGroup()) Node *node = childMap.value(name);
return node; if (node && !node->isQmlPropertyGroup()) // mws asks: Why not property group?
if (isQmlType()) { return node;
for (int i=0; i<children_.size(); ++i) { if (isQmlType()) {
Node* n = children_.at(i); for (int i=0; i<children_.size(); ++i) {
if (n->isQmlPropertyGroup()) { Node* n = children_.at(i);
node = static_cast<InnerNode*>(n)->findChildNode(name); if (n->isQmlPropertyGroup()) {
if (node) node = static_cast<InnerNode*>(n)->findChildNode(name, genus);
if (node)
return node;
}
}
}
}
else {
NodeList nodes = childMap.values(name);
if (!nodes.isEmpty()) {
for (int i=0; i<nodes.size(); ++i) {
Node* node = nodes.at(i);
if (genus == node->genus() || genus == Node::DontCare)
return node; return node;
} }
} }
@ -740,6 +757,39 @@ Node *InnerNode::findChildNode(const QString& name) const
return primaryFunctionMap.value(name); return primaryFunctionMap.value(name);
} }
/*!
Find all the child nodes of this node that are named
\a name and return them in \a nodes.
*/
void InnerNode::findChildren(const QString& name, NodeList& nodes) const
{
nodes = childMap.values(name);
Node* n = primaryFunctionMap.value(name);
if (n) {
nodes.append(n);
NodeList t = secondaryFunctionMap.value(name);
if (!t.isEmpty())
nodes.append(t);
}
if (!nodes.isEmpty() || !isQmlNode())
return;
int i = name.indexOf(QChar('.'));
if (i < 0)
return;
QString qmlPropGroup = name.left(i);
NodeList t = childMap.values(qmlPropGroup);
if (t.isEmpty())
return;
foreach (Node* n, t) {
if (n->isQmlPropertyGroup()) {
n->findChildren(name, nodes);
if (!nodes.isEmpty())
break;
}
}
}
#if 0
/*! /*!
Find the node in this node's children that has the given \a name. If 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 this node is a QML class node, be sure to also look in the children
@ -752,7 +802,7 @@ Node *InnerNode::findChildNode(const QString& name) const
*/ */
Node* InnerNode::findChildNode(const QString& name, bool qml) const Node* InnerNode::findChildNode(const QString& name, bool qml) const
{ {
QList<Node*> nodes = childMap.values(name); NodeList nodes = childMap.values(name);
if (!nodes.isEmpty()) { if (!nodes.isEmpty()) {
for (int i=0; i<nodes.size(); ++i) { for (int i=0; i<nodes.size(); ++i) {
Node* node = nodes.at(i); Node* node = nodes.at(i);
@ -776,6 +826,7 @@ Node* InnerNode::findChildNode(const QString& name, bool qml) const
} }
return primaryFunctionMap.value(name); return primaryFunctionMap.value(name);
} }
#endif
/*! /*!
This function is like findChildNode(), but if a node This function is like findChildNode(), but if a node
@ -793,7 +844,7 @@ Node* InnerNode::findChildNode(const QString& name, Type type)
if (type == Function) if (type == Function)
return primaryFunctionMap.value(name); return primaryFunctionMap.value(name);
else { else {
QList<Node*> nodes = childMap.values(name); NodeList nodes = childMap.values(name);
for (int i=0; i<nodes.size(); ++i) { for (int i=0; i<nodes.size(); ++i) {
Node* node = nodes.at(i); Node* node = nodes.at(i);
if (node->type() == type) if (node->type() == type)
@ -803,13 +854,14 @@ Node* InnerNode::findChildNode(const QString& name, Type type)
return 0; return 0;
} }
#if 0
/*! /*!
*/ */
void InnerNode::findNodes(const QString& name, QList<Node*>& n) void InnerNode::findNodes(const QString& name, NodeList& n)
{ {
n.clear(); n.clear();
Node* node = 0; Node* node = 0;
QList<Node*> nodes = childMap.values(name); NodeList nodes = childMap.values(name);
/* /*
<sigh> If this node's child map contains no nodes named <sigh> If this node's child map contains no nodes named
name, then if this node is a QML class, search each of its name, then if this node is a QML class, search each of its
@ -857,6 +909,7 @@ void InnerNode::findNodes(const QString& name, QList<Node*>& n)
if (node) if (node)
n.append(node); n.append(node);
} }
#endif
/*! /*!
Find a function node that is a child of this nose, such Find a function node that is a child of this nose, such

View File

@ -117,6 +117,8 @@ public:
LastSubtype LastSubtype
}; };
enum Genus { DontCare, CPP, QML };
enum Access { Public, Protected, Private }; enum Access { Public, Protected, Private };
enum Status { enum Status {
@ -217,6 +219,7 @@ public:
virtual bool isNamespace() const { return false; } virtual bool isNamespace() const { return false; }
virtual bool isClass() const { return false; } virtual bool isClass() const { return false; }
virtual bool isQmlNode() const { return false; } virtual bool isQmlNode() const { return false; }
virtual bool isCppNode() const { return false; }
virtual bool isQtQuickNode() const { return false; } virtual bool isQtQuickNode() const { return false; }
virtual bool isAbstract() const { return false; } virtual bool isAbstract() const { return false; }
virtual bool isQmlPropertyGroup() const { return false; } virtual bool isQmlPropertyGroup() const { return false; }
@ -233,6 +236,7 @@ public:
virtual bool hasClasses() const { return false; } virtual bool hasClasses() const { return false; }
virtual void setAbstract(bool ) { } virtual void setAbstract(bool ) { }
virtual void setWrapper() { } virtual void setWrapper() { }
virtual Node::Genus genus() const { return DontCare; }
virtual QString title() const { return name(); } virtual QString title() const { return name(); }
virtual QString fullTitle() const { return name(); } virtual QString fullTitle() const { return name(); }
virtual QString subTitle() const { return QString(); } virtual QString subTitle() const { return QString(); }
@ -250,6 +254,7 @@ public:
virtual void appendGroupName(const QString& ) { } virtual void appendGroupName(const QString& ) { }
virtual QString element() const { return QString(); } virtual QString element() const { return QString(); }
virtual Tree* tree() const; virtual Tree* tree() const;
virtual void findChildren(const QString& , NodeList& nodes) const { nodes.clear(); }
bool isIndexNode() const { return indexNodeFlag_; } bool isIndexNode() const { return indexNodeFlag_; }
Type type() const { return nodeType_; } Type type() const { return nodeType_; }
virtual SubType subType() const { return NoSubType; } virtual SubType subType() const { return NoSubType; }
@ -271,6 +276,7 @@ public:
void setLink(LinkType linkType, const QString &link, const QString &desc); void setLink(LinkType linkType, const QString &link, const QString &desc);
Access access() const { return access_; } Access access() const { return access_; }
bool isPrivate() const { return access_ == Private; }
QString accessString() const; QString accessString() const;
const Location& location() const { return loc_; } const Location& location() const { return loc_; }
const Doc& doc() const { return doc_; } const Doc& doc() const { return doc_; }
@ -360,10 +366,11 @@ class InnerNode : public Node
public: public:
virtual ~InnerNode(); virtual ~InnerNode();
Node* findChildNode(const QString& name) const; Node* findChildNode(const QString& name, Node::Genus genus) const;
Node* findChildNode(const QString& name, bool qml) const; //Node* findChildNode(const QString& name, bool qml) const;
Node* findChildNode(const QString& name, Type type); Node* findChildNode(const QString& name, Type type);
void findNodes(const QString& name, QList<Node*>& n); //void findNodes(const QString& name, NodeList& n);
virtual void findChildren(const QString& name, NodeList& nodes) const;
FunctionNode* findFunctionNode(const QString& name) const; FunctionNode* findFunctionNode(const QString& name) const;
FunctionNode* findFunctionNode(const FunctionNode* clone); FunctionNode* findFunctionNode(const FunctionNode* clone);
void addInclude(const QString &include); void addInclude(const QString &include);
@ -443,6 +450,8 @@ public:
virtual ~NamespaceNode() { } virtual ~NamespaceNode() { }
virtual bool isNamespace() const { return true; } virtual bool isNamespace() const { return true; }
virtual Tree* tree() const { return (parent() ? parent()->tree() : tree_); } virtual Tree* tree() const { return (parent() ? parent()->tree() : tree_); }
virtual bool isCppNode() const { return true; }
virtual Node::Genus genus() const { return Node::CPP; }
void setTree(Tree* t) { tree_ = t; } void setTree(Tree* t) { tree_ = t; }
private: private:
@ -473,7 +482,9 @@ public:
ClassNode(InnerNode* parent, const QString& name); ClassNode(InnerNode* parent, const QString& name);
virtual ~ClassNode() { } virtual ~ClassNode() { }
virtual bool isClass() const { return true; } virtual bool isClass() const { return true; }
virtual bool isCppNode() const { return true; }
virtual bool isWrapper() const { return wrapper_; } virtual bool isWrapper() const { return wrapper_; }
virtual Node::Genus genus() const { return Node::CPP; }
virtual QString obsoleteLink() const { return obsoleteLink_; } virtual QString obsoleteLink() const { return obsoleteLink_; }
virtual void setObsoleteLink(const QString& t) { obsoleteLink_ = t; } virtual void setObsoleteLink(const QString& t) { obsoleteLink_ = t; }
virtual void setWrapper() { wrapper_ = true; } virtual void setWrapper() { wrapper_ = true; }
@ -618,6 +629,7 @@ public:
virtual QString qmlModuleIdentifier() const; virtual QString qmlModuleIdentifier() const;
virtual QmlModuleNode* qmlModule() const { return qmlModule_; } virtual QmlModuleNode* qmlModule() const { return qmlModule_; }
virtual void setQmlModule(QmlModuleNode* t) { qmlModule_ = t; } virtual void setQmlModule(QmlModuleNode* t) { qmlModule_ = t; }
virtual Node::Genus genus() const { return Node::QML; }
const ImportList& importList() const { return importList_; } const ImportList& importList() const { return importList_; }
void setImportList(const ImportList& il) { importList_ = il; } void setImportList(const ImportList& il) { importList_ = il; }
const QString& qmlBaseName() const { return qmlBaseName_; } const QString& qmlBaseName() const { return qmlBaseName_; }
@ -655,6 +667,7 @@ public:
virtual ~QmlBasicTypeNode() { } virtual ~QmlBasicTypeNode() { }
virtual bool isQmlNode() const { return true; } virtual bool isQmlNode() const { return true; }
virtual bool isQmlBasicType() const { return true; } virtual bool isQmlBasicType() const { return true; }
virtual Node::Genus genus() const { return Node::QML; }
}; };
class QmlPropertyGroupNode : public InnerNode class QmlPropertyGroupNode : public InnerNode
@ -670,6 +683,7 @@ public:
virtual QString qmlModuleIdentifier() const { return parent()->qmlModuleIdentifier(); } virtual QString qmlModuleIdentifier() const { return parent()->qmlModuleIdentifier(); }
virtual QString idNumber(); virtual QString idNumber();
virtual bool isQmlPropertyGroup() const { return true; } virtual bool isQmlPropertyGroup() const { return true; }
virtual Node::Genus genus() const { return Node::QML; }
virtual QString element() const { return parent()->name(); } virtual QString element() const { return parent()->name(); }
@ -688,6 +702,7 @@ public:
bool attached); bool attached);
virtual ~QmlPropertyNode() { } virtual ~QmlPropertyNode() { }
virtual Node::Genus genus() const { return Node::QML; }
virtual void setDataType(const QString& dataType) { type_ = dataType; } virtual void setDataType(const QString& dataType) { type_ = dataType; }
void setStored(bool stored) { stored_ = toFlagValue(stored); } void setStored(bool stored) { stored_ = toFlagValue(stored); }
void setDesignable(bool designable) { designable_ = toFlagValue(designable); } void setDesignable(bool designable) { designable_ = toFlagValue(designable); }
@ -746,6 +761,8 @@ public:
EnumNode(InnerNode* parent, const QString& name); EnumNode(InnerNode* parent, const QString& name);
virtual ~EnumNode() { } virtual ~EnumNode() { }
virtual Node::Genus genus() const { return Node::CPP; }
virtual bool isCppNode() const { return true; }
void addItem(const EnumItem& item); void addItem(const EnumItem& item);
void setFlagsType(TypedefNode* typedeff); void setFlagsType(TypedefNode* typedeff);
bool hasItem(const QString &name) const { return names.contains(name); } bool hasItem(const QString &name) const { return names.contains(name); }
@ -767,6 +784,8 @@ public:
TypedefNode(InnerNode* parent, const QString& name); TypedefNode(InnerNode* parent, const QString& name);
virtual ~TypedefNode() { } virtual ~TypedefNode() { }
virtual Node::Genus genus() const { return Node::CPP; }
virtual bool isCppNode() const { return true; }
const EnumNode* associatedEnum() const { return ae; } const EnumNode* associatedEnum() const { return ae; }
private: private:
@ -873,6 +892,8 @@ public:
(type() == QmlMethod) || (type() == QmlMethod) ||
(type() == QmlSignalHandler)); (type() == QmlSignalHandler));
} }
virtual bool isCppNode() const { return !isQmlNode(); }
virtual Node::Genus genus() const { return (isQmlNode() ? Node::QML : Node::CPP); }
virtual bool isQtQuickNode() const { return parent()->isQtQuickNode(); } virtual bool isQtQuickNode() const { return parent()->isQtQuickNode(); }
virtual QString qmlTypeName() const { return parent()->qmlTypeName(); } virtual QString qmlTypeName() const { return parent()->qmlTypeName(); }
virtual QString qmlModuleName() const { return parent()->qmlModuleName(); } virtual QString qmlModuleName() const { return parent()->qmlModuleName(); }
@ -911,6 +932,8 @@ public:
PropertyNode(InnerNode* parent, const QString& name); PropertyNode(InnerNode* parent, const QString& name);
virtual ~PropertyNode() { } virtual ~PropertyNode() { }
virtual Node::Genus genus() const { return Node::CPP; }
virtual bool isCppNode() const { return true; }
virtual void setDataType(const QString& dataType) { type_ = dataType; } virtual void setDataType(const QString& dataType) { type_ = dataType; }
void addFunction(FunctionNode* function, FunctionRole role); void addFunction(FunctionNode* function, FunctionRole role);
void addSignal(FunctionNode* function, FunctionRole role); void addSignal(FunctionNode* function, FunctionRole role);
@ -998,6 +1021,8 @@ public:
VariableNode(InnerNode* parent, const QString &name); VariableNode(InnerNode* parent, const QString &name);
virtual ~VariableNode() { } virtual ~VariableNode() { }
virtual Node::Genus genus() const { return Node::CPP; }
virtual bool isCppNode() const { return true; }
void setLeftType(const QString &leftType) { lt = leftType; } void setLeftType(const QString &leftType) { lt = leftType; }
void setRightType(const QString &rightType) { rt = rightType; } void setRightType(const QString &rightType) { rt = rightType; }
void setStatic(bool statique) { sta = statique; } void setStatic(bool statique) { sta = statique; }
@ -1084,6 +1109,7 @@ class ModuleNode : public CollectionNode
virtual ~ModuleNode() { } virtual ~ModuleNode() { }
virtual bool isModule() const { return true; } virtual bool isModule() const { return true; }
virtual bool isCppNode() const { return true; }
virtual void setQtVariable(const QString& v) { qtVariable_ = v; } virtual void setQtVariable(const QString& v) { qtVariable_ = v; }
virtual QString qtVariable() const { return qtVariable_; } virtual QString qtVariable() const { return qtVariable_; }
@ -1098,6 +1124,7 @@ class QmlModuleNode : public CollectionNode
: CollectionNode(Node::QmlModule, parent, name) { } : CollectionNode(Node::QmlModule, parent, name) { }
virtual ~QmlModuleNode() { } virtual ~QmlModuleNode() { }
virtual bool isQmlNode() const { return true; }
virtual bool isQmlModule() const { return true; } virtual bool isQmlModule() const { return true; }
virtual QString qmlModuleName() const { return qmlModuleName_; } virtual QString qmlModuleName() const { return qmlModuleName_; }
virtual QString qmlModuleVersion() const { virtual QString qmlModuleVersion() const {

View File

@ -379,20 +379,34 @@ void QDocForest::newPrimaryTree(const QString& module)
} }
/*! /*!
Searches the trees for a node named \a target and returns Searches through the forest for a node named \a targetPath
a pointer to it if found. The \a relative node is the starting and returns a pointer to it if found. The \a relative node
point, but it only makes sense in the primary tree, which is is the starting point. It only makes sense for the primary
searched first. After the primary tree is searched, \a relative tree, which is searched first. After the primary tree has
is set to 0 for searching the index trees. When relative is 0, been searched, \a relative is set to 0 for searching the
the root nodes of the index trees are the starting points. other trees, which are all index trees. With relative set
to 0, the starting point for each index tree is the root
of the index tree.
*/ */
const Node* QDocForest::resolveTarget(const QString& target, const Node* relative) const Node* QDocForest::findNodeForTarget(QStringList& targetPath,
const Node* relative,
Node::Genus genus,
QString& ref)
{ {
QStringList path = target.split("::"); int flags = SearchBaseClasses | SearchEnumValues;
int flags = SearchBaseClasses | SearchEnumValues | NonFunction;
QString entity = targetPath.at(0);
targetPath.removeFirst();
QStringList entityPath = entity.split("::");
QString target;
if (!targetPath.isEmpty()) {
target = targetPath.at(0);
targetPath.removeFirst();
}
foreach (Tree* t, searchOrder()) { foreach (Tree* t, searchOrder()) {
const Node* n = t->findNode(path, relative, flags); const Node* n = t->findNodeForTarget(entityPath, target, relative, flags, genus, ref);
if (n) if (n)
return n; return n;
relative = 0; relative = 0;
@ -400,20 +414,6 @@ const Node* QDocForest::resolveTarget(const QString& target, const Node* relativ
return 0; return 0;
} }
/*!
Searches the Tree \a t for a type node named by the \a path
and returns a pointer to it if found. The \a relative node
is the starting point, but it only makes sense when searching
the primary tree. Therefore, when this function is called with
\a t being an index tree, \a relative is 0. When relative is 0,
the root node of \a t is the starting point.
*/
const Node* QDocForest::resolveTypeHelper(const QStringList& path, const Node* relative, Tree* t)
{
int flags = SearchBaseClasses | SearchEnumValues | NonFunction;
return t->findNode(path, relative, flags);
}
/*! /*!
This function merges all the collection maps for collection This function merges all the collection maps for collection
nodes of node type \a t into the collection multimap \a cnmm, nodes of node type \a t into the collection multimap \a cnmm,
@ -1343,7 +1343,7 @@ void QDocDatabase::resolveIssues() {
When searching the index trees, the search begins at the When searching the index trees, the search begins at the
root. root.
*/ */
const Node* QDocDatabase::resolveType(const QString& type, const Node* relative) const Node* QDocDatabase::findTypeNode(const QString& type, const Node* relative)
{ {
QStringList path = type.split("::"); QStringList path = type.split("::");
if ((path.size() == 1) && (path.at(0)[0].isLower() || path.at(0) == QString("T"))) { if ((path.size() == 1) && (path.at(0)[0].isLower() || path.at(0) == QString("T"))) {
@ -1351,7 +1351,7 @@ const Node* QDocDatabase::resolveType(const QString& type, const Node* relative)
if (i != typeNodeMap_.end()) if (i != typeNodeMap_.end())
return i.value(); return i.value();
} }
return forest_.resolveType(path, relative); return forest_.findTypeNode(path, relative);
} }
/*! /*!
@ -1369,9 +1369,15 @@ const Node* QDocDatabase::findNodeForTarget(const QString& target, const Node* r
node = findNodeByNameAndType(QStringList(target), Node::Document); node = findNodeByNameAndType(QStringList(target), Node::Document);
} }
else { else {
node = resolveTarget(target, relative); QStringList path = target.split("::");
if (!node) int flags = SearchBaseClasses | SearchEnumValues; // | NonFunction;
node = findDocNodeByTitle(target); foreach (Tree* t, searchOrder()) {
const Node* n = t->findNode(path, relative, flags, Node::DontCare);
if (n)
return n;
relative = 0;
}
node = findDocNodeByTitle(target);
} }
return node; return node;
} }
@ -1578,27 +1584,6 @@ void QDocDatabase::mergeCollections(CollectionNode* cn)
} }
} }
/*!
This function is called when the \a{atom} might be a link
atom. It handles the optional, square bracket parameters
for the link command.
*/
Node* QDocDatabase::findNode(const Atom* atom)
{
QStringList path(atom->string());
if (atom->specifiesDomain()) {
return atom->domain()->findNodeByNameAndType(path, atom->goal());
}
qDebug() << "FINDNODE:" << path << atom->goal();
return forest_.findNodeByNameAndType(path, atom->goal());
}
const DocNode* QDocDatabase::findDocNodeByTitle(const Atom* atom)
{
return forest_.findDocNodeByTitle(atom->string());
}
/*! /*!
Searches for the node that matches the path in \a atom. The Searches for the node that matches the path in \a atom. The
\a relative node is used if the first leg of the path is \a relative node is used if the first leg of the path is
@ -1608,51 +1593,62 @@ const DocNode* QDocDatabase::findDocNodeByTitle(const Atom* atom)
\a ref. If the returned node pointer is null, \a ref is not \a ref. If the returned node pointer is null, \a ref is not
valid. valid.
*/ */
const Node* QDocDatabase::findNode(const Atom* atom, const Node* relative, QString& ref) const Node* QDocDatabase::findNodeForAtom(const Atom* atom, const Node* relative, QString& ref)
{ {
const Node* node = 0; const Node* node = 0;
QStringList path = atom->string().split("#");
QString first = path.first().trimmed(); QStringList targetPath = atom->string().split("#");
path.removeFirst(); QString first = targetPath.first().trimmed();
Tree* domain = 0;
Node::Genus genus = Node::DontCare;
// Reserved for future use
//Node::Type goal = Node::NoType;
if (atom->isLinkAtom()) {
domain = atom->domain();
genus = atom->genus();
// Reserved for future use
//goal = atom->goal();
}
if (first.isEmpty()) if (first.isEmpty())
node = relative; // search for a target on the current page. node = relative; // search for a target on the current page.
else if (atom->specifiesDomain()) { else if (domain) {
qDebug() << "Processing LinkAtom"; if (first.endsWith(".html"))
if (first.endsWith(".html")) { // The target is an html file. node = domain->findNodeByNameAndType(QStringList(first), Node::Document);
node = atom->domain()->findNodeByNameAndType(QStringList(first), Node::Document); else if (first.endsWith("()"))
} node = domain->findFunctionNode(first, 0, genus);
else if (first.endsWith("()")) { // The target is a C++ function or QML method.
node = atom->domain()->resolveFunctionTarget(first, 0); //relative);
}
else { else {
node = atom->domain()->resolveTarget(first, 0); // relative); int flags = SearchBaseClasses | SearchEnumValues;
if (!node) QStringList nodePath = first.split("::");
node = atom->domain()->findUnambiguousTarget(first, ref); // ref QString target;
if (!node && path.isEmpty()) targetPath.removeFirst();
node = atom->domain()->findDocNodeByTitle(first); if (!targetPath.isEmpty()) {
target = targetPath.at(0);
targetPath.removeFirst();
}
node = domain->findNodeForTarget(nodePath, target, relative, flags, genus, ref);
return node;
} }
} }
else { else {
if (first.endsWith(".html")) { // The target is an html file. if (first.endsWith(".html"))
node = findNodeByNameAndType(QStringList(first), Node::Document); // ref node = findNodeByNameAndType(QStringList(first), Node::Document);
} else if (first.endsWith("()"))
else if (first.endsWith("()")) { // The target is a C++ function or QML method. node = findFunctionNode(first, relative, genus);
node = resolveFunctionTarget(first, relative);
}
else { else {
node = resolveTarget(first, relative); // ref node = findNodeForTarget(targetPath, relative, genus, ref);
if (!node) return node;
node = findUnambiguousTarget(first, ref); // ref
if (!node && path.isEmpty())
node = findDocNodeByTitle(first);
} }
} }
if (node && ref.isEmpty()) { if (node && ref.isEmpty()) {
if (!node->url().isEmpty()) if (!node->url().isEmpty())
return node; return node;
if (!path.isEmpty()) { targetPath.removeFirst();
ref = findTarget(path.first(), node); if (!targetPath.isEmpty()) {
ref = node->root()->tree()->getRef(targetPath.first(), node);
if (ref.isEmpty()) if (ref.isEmpty())
node = 0; node = 0;
} }

View File

@ -88,9 +88,12 @@ class QDocForest
const QVector<Tree*>& indexSearchOrder(); const QVector<Tree*>& indexSearchOrder();
void setSearchOrder(); void setSearchOrder();
const Node* findNode(const QStringList& path, const Node* relative, int findFlags) { const Node* findNode(const QStringList& path,
const Node* relative,
int findFlags,
Node::Genus genus) {
foreach (Tree* t, searchOrder()) { foreach (Tree* t, searchOrder()) {
const Node* n = t->findNode(path, relative, findFlags); const Node* n = t->findNode(path, relative, findFlags, genus);
if (n) if (n)
return n; return n;
relative = 0; relative = 0;
@ -116,6 +119,15 @@ class QDocForest
return 0; return 0;
} }
Node* findNodeForInclude(const QStringList& path) {
foreach (Tree* t, searchOrder()) {
Node* n = t->findNodeForInclude(path);
if (n)
return n;
}
return 0;
}
InnerNode* findRelatesNode(const QStringList& path) { InnerNode* findRelatesNode(const QStringList& path) {
foreach (Tree* t, searchOrder()) { foreach (Tree* t, searchOrder()) {
InnerNode* n = t->findRelatesNode(path); InnerNode* n = t->findRelatesNode(path);
@ -125,21 +137,27 @@ class QDocForest
return 0; return 0;
} }
const Node* resolveFunctionTarget(const QString& target, const Node* relative) { const Node* findFunctionNode(const QString& target,
const Node* relative,
Node::Genus genus) {
foreach (Tree* t, searchOrder()) { foreach (Tree* t, searchOrder()) {
const Node* n = t->resolveFunctionTarget(target, relative); const Node* n = t->findFunctionNode(target, relative, genus);
if (n) if (n)
return n; return n;
relative = 0; relative = 0;
} }
return 0; return 0;
} }
const Node* resolveTarget(const QString& target, const Node* relative); const Node* findNodeForTarget(QStringList& targetPath,
const Node* relative,
Node::Genus genus,
QString& ref);
const Node* resolveType(const QStringList& path, const Node* relative) const Node* findTypeNode(const QStringList& path, const Node* relative)
{ {
foreach (Tree* t, searchOrder()) { foreach (Tree* t, searchOrder()) {
const Node* n = resolveTypeHelper(path, relative, t); int flags = SearchBaseClasses | SearchEnumValues | NonFunction;
const Node* n = t->findNode(path, relative, flags, Node::DontCare);
if (n) if (n)
return n; return n;
relative = 0; relative = 0;
@ -147,16 +165,6 @@ class QDocForest
return 0; return 0;
} }
const Node* findUnambiguousTarget(const QString& target, QString& ref)
{
foreach (Tree* t, searchOrder()) {
const Node* n = t->findUnambiguousTarget(target, ref);
if (n)
return n;
}
return 0;
}
const DocNode* findDocNodeByTitle(const QString& title) const DocNode* findDocNodeByTitle(const QString& title)
{ {
foreach (Tree* t, searchOrder()) { foreach (Tree* t, searchOrder()) {
@ -189,7 +197,6 @@ class QDocForest
private: private:
void newPrimaryTree(const QString& module); void newPrimaryTree(const QString& module);
NamespaceNode* newIndexTree(const QString& module); NamespaceNode* newIndexTree(const QString& module);
const Node* resolveTypeHelper(const QStringList& path, const Node* relative, Tree* t);
private: private:
QDocDatabase* qdb_; QDocDatabase* qdb_;
@ -281,8 +288,12 @@ class QDocDatabase
void resolveTargets() { void resolveTargets() {
primaryTree()->resolveTargets(primaryTreeRoot()); primaryTree()->resolveTargets(primaryTreeRoot());
} }
void insertTarget(const QString& name, TargetRec::Type type, Node* node, int priority) { void insertTarget(const QString& name,
primaryTree()->insertTarget(name, type, node, priority); const QString& title,
TargetRec::Type type,
Node* node,
int priority) {
primaryTree()->insertTarget(name, title, type, node, priority);
} }
/******************************************************************* /*******************************************************************
@ -304,38 +315,37 @@ class QDocDatabase
/******************************************************************* /*******************************************************************
The functions declared below handle the parameters in '[' ']'. The functions declared below handle the parameters in '[' ']'.
********************************************************************/ ********************************************************************/
Node* findNode(const Atom* atom); const Node* findNodeForAtom(const Atom* atom, const Node* relative, QString& ref);
const Node* findNode(const Atom* atom, const Node* relative, QString& ref);
const DocNode* findDocNodeByTitle(const Atom* atom);
/*******************************************************************/ /*******************************************************************/
/******************************************************************* /*******************************************************************
The functions declared below are called for all trees. The functions declared below are called for all trees.
********************************************************************/ ********************************************************************/
ClassNode* findClassNode(const QStringList& path) { return forest_.findClassNode(path); } ClassNode* findClassNode(const QStringList& path) { return forest_.findClassNode(path); }
Node* findNodeForInclude(const QStringList& path) { return forest_.findNodeForInclude(path); }
InnerNode* findRelatesNode(const QStringList& path) { return forest_.findRelatesNode(path); } InnerNode* findRelatesNode(const QStringList& path) { return forest_.findRelatesNode(path); }
const Node* resolveTarget(const QString& target, const Node* relative) { const Node* findFunctionNode(const QString& target, const Node* relative, Node::Genus genus) {
return forest_.resolveTarget(target, relative); return forest_.findFunctionNode(target, relative, genus);
} }
const Node* resolveFunctionTarget(const QString& target, const Node* relative) { const Node* findTypeNode(const QString& type, const Node* relative);
return forest_.resolveFunctionTarget(target, relative);
}
const Node* resolveType(const QString& type, const Node* relative);
const Node* findNodeForTarget(const QString& target, const Node* relative); const Node* findNodeForTarget(const QString& target, const Node* relative);
const DocNode* findDocNodeByTitle(const QString& title) { const DocNode* findDocNodeByTitle(const QString& title) {
return forest_.findDocNodeByTitle(title); return forest_.findDocNodeByTitle(title);
} }
const Node* findUnambiguousTarget(const QString& target, QString& ref) {
return forest_.findUnambiguousTarget(target, ref);
}
Node* findNodeByNameAndType(const QStringList& path, Node::Type type) { Node* findNodeByNameAndType(const QStringList& path, Node::Type type) {
return forest_.findNodeByNameAndType(path, type); return forest_.findNodeByNameAndType(path, type);
} }
/*******************************************************************/
QString findTarget(const QString& target, const Node* node) { private:
return node->root()->tree()->findTarget(target, node); const Node* findNodeForTarget(QStringList& targetPath,
const Node* relative,
Node::Genus genus,
QString& ref) {
return forest_.findNodeForTarget(targetPath, relative, genus, ref);
} }
/*******************************************************************/
public:
void addPropertyFunction(PropertyNode* property, void addPropertyFunction(PropertyNode* property,
const QString& funcName, const QString& funcName,
PropertyNode::FunctionRole funcRole) { PropertyNode::FunctionRole funcRole) {
@ -371,8 +381,11 @@ class QDocDatabase
friend class QDocIndexFiles; friend class QDocIndexFiles;
friend class QDocTagFiles; friend class QDocTagFiles;
const Node* findNode(const QStringList& path, const Node* relative, int findFlags) { const Node* findNode(const QStringList& path,
return forest_.findNode(path, relative, findFlags); const Node* relative,
int findFlags,
Node::Genus genus) {
return forest_.findNode(path, relative, findFlags, genus);
} }
void processForest(void (QDocDatabase::*) (InnerNode*)); void processForest(void (QDocDatabase::*) (InnerNode*));
static void initializeDB(); static void initializeDB();

View File

@ -471,15 +471,18 @@ void QDocIndexFiles::readIndexSection(const QDomElement& element,
location = Location(parent->name().toLower() + ".html"); location = Location(parent->name().toLower() + ".html");
} }
else if (element.nodeName() == "keyword") { else if (element.nodeName() == "keyword") {
qdb_->insertTarget(name, TargetRec::Keyword, current, 1); QString title = element.attribute("title");
qdb_->insertTarget(name, title, TargetRec::Keyword, current, 1);
return; return;
} }
else if (element.nodeName() == "target") { else if (element.nodeName() == "target") {
qdb_->insertTarget(name, TargetRec::Target, current, 2); QString title = element.attribute("title");
qdb_->insertTarget(name, title, TargetRec::Target, current, 2);
return; return;
} }
else if (element.nodeName() == "contents") { else if (element.nodeName() == "contents") {
qdb_->insertTarget(name, TargetRec::Contents, current, 3); QString title = element.attribute("title");
qdb_->insertTarget(name, title, TargetRec::Contents, current, 3);
return; return;
} }
else else
@ -1202,18 +1205,26 @@ bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter& writer,
external = true; external = true;
} }
foreach (const Atom* target, node->doc().targets()) { foreach (const Atom* target, node->doc().targets()) {
QString targetName = target->string(); QString title = target->string();
if (!external) QString name = Doc::canonicalTitle(title);
targetName = Doc::canonicalTitle(targetName);
writer.writeStartElement("target"); writer.writeStartElement("target");
writer.writeAttribute("name", targetName); if (!external)
writer.writeAttribute("name", name);
else
writer.writeAttribute("name", title);
if (name != title)
writer.writeAttribute("title", title);
writer.writeEndElement(); // target writer.writeEndElement(); // target
} }
} }
if (node->doc().hasKeywords()) { if (node->doc().hasKeywords()) {
foreach (const Atom* keyword, node->doc().keywords()) { foreach (const Atom* keyword, node->doc().keywords()) {
QString title = keyword->string();
QString name = Doc::canonicalTitle(title);
writer.writeStartElement("keyword"); writer.writeStartElement("keyword");
writer.writeAttribute("name", Doc::canonicalTitle(keyword->string())); writer.writeAttribute("name", name);
if (name != title)
writer.writeAttribute("title", title);
writer.writeEndElement(); // keyword writer.writeEndElement(); // keyword
} }
} }

View File

@ -249,7 +249,7 @@ bool QmlDocVisitor::applyDocumentation(QQmlJS::AST::SourceLocation location, Nod
nodes.append(node); nodes.append(node);
if (topicsUsed.size() > 0) { if (topicsUsed.size() > 0) {
for (int i=0; i<topicsUsed.size(); ++i) { for (int i=0; i<topicsUsed.size(); ++i) {
if (topicsUsed.at(i).topic == QString("qmlpropertygroup")) { if (topicsUsed.at(i).topic == COMMAND_QMLPROPERTYGROUP) {
qDebug() << "PROPERTY GROUP COMMAND SEEN:" << topicsUsed.at(i).args << filePath_; qDebug() << "PROPERTY GROUP COMMAND SEEN:" << topicsUsed.at(i).args << filePath_;
break; break;
} }

View File

@ -86,21 +86,48 @@ Tree::Tree(const QString& module, QDocDatabase* qdb)
destructor of each child node is called, and these destructor of each child node is called, and these
destructors are recursive. Thus the entire tree is destructors are recursive. Thus the entire tree is
destroyed. destroyed.
There are two maps of targets, keywords, and contents.
One map is indexed by ref, the other by title. The ref
is just the canonical form of the title. Both maps
use the same set of TargetRec objects as the values,
so the destructor only deletes the values from one of
the maps. Then it clears both maps.
*/ */
Tree::~Tree() Tree::~Tree()
{ {
// nothing TargetMap::iterator i = nodesByTargetRef_.begin();
while (i != nodesByTargetRef_.end()) {
delete i.value();
++i;
}
nodesByTargetRef_.clear();
nodesByTargetTitle_.clear();
} }
/* API members */ /* API members */
/*!
Calls findClassNode() first with \a path and \a start. If
it finds a node, the node is returned. If not, it calls
findNamespaceNode() with the same parameters. The result
is returned.
*/
Node* Tree::findNodeForInclude(const QStringList& path) const
{
Node* n = findClassNode(path);
if (!n)
n = findNamespaceNode(path);
return n;
}
/*! /*!
Find the C++ class node named \a path. Begin the search at the Find the C++ class node named \a path. Begin the search at the
\a start node. If the \a start node is 0, begin the search \a start node. If the \a start node is 0, begin the search
at the root of the tree. Only a C++ class node named \a path is at the root of the tree. Only a C++ class node named \a path is
acceptible. If one is not found, 0 is returned. acceptible. If one is not found, 0 is returned.
*/ */
ClassNode* Tree::findClassNode(const QStringList& path, Node* start) const ClassNode* Tree::findClassNode(const QStringList& path, const Node* start) const
{ {
if (!start) if (!start)
start = const_cast<NamespaceNode*>(root()); start = const_cast<NamespaceNode*>(root());
@ -125,8 +152,9 @@ NamespaceNode* Tree::findNamespaceNode(const QStringList& path) const
matches the \a clone node. If it finds a node that is just matches the \a clone node. If it finds a node that is just
like the \a clone, it returns a pointer to the found node. like the \a clone, it returns a pointer to the found node.
There should be a way to avoid creating the clone in the Apparently the search order is important here. Don't change
first place. Investigate when time allows. it unless you know what you are doing, or you will introduce
qdoc warnings.
*/ */
FunctionNode* Tree::findFunctionNode(const QStringList& parentPath, const FunctionNode* clone) FunctionNode* Tree::findFunctionNode(const QStringList& parentPath, const FunctionNode* clone)
{ {
@ -134,7 +162,7 @@ FunctionNode* Tree::findFunctionNode(const QStringList& parentPath, const Functi
if (parent == 0) if (parent == 0)
parent = findClassNode(parentPath, 0); parent = findClassNode(parentPath, 0);
if (parent == 0) if (parent == 0)
parent = findNode(parentPath); parent = findNode(parentPath, 0, 0, Node::DontCare);
if (parent == 0 || !parent->isInnerNode()) if (parent == 0 || !parent->isInnerNode())
return 0; return 0;
return ((InnerNode*)parent)->findFunctionNode(clone); return ((InnerNode*)parent)->findFunctionNode(clone);
@ -176,7 +204,7 @@ QmlClassNode* Tree::findQmlTypeNode(const QStringList& path)
*/ */
NameCollisionNode* Tree::checkForCollision(const QString& name) NameCollisionNode* Tree::checkForCollision(const QString& name)
{ {
Node* n = const_cast<Node*>(findNode(QStringList(name))); Node* n = const_cast<Node*>(findNode(QStringList(name), 0, 0, Node::DontCare));
if (n) { if (n) {
if (n->subType() == Node::Collision) { if (n->subType() == Node::Collision) {
NameCollisionNode* ncn = static_cast<NameCollisionNode*>(n); NameCollisionNode* ncn = static_cast<NameCollisionNode*>(n);
@ -196,7 +224,7 @@ NameCollisionNode* Tree::checkForCollision(const QString& name)
*/ */
NameCollisionNode* Tree::findCollisionNode(const QString& name) const NameCollisionNode* Tree::findCollisionNode(const QString& name) const
{ {
Node* n = const_cast<Node*>(findNode(QStringList(name))); Node* n = const_cast<Node*>(findNode(QStringList(name), 0, 0, Node::DontCare));
if (n) { if (n) {
if (n->subType() == Node::Collision) { if (n->subType() == Node::Collision) {
NameCollisionNode* ncn = static_cast<NameCollisionNode*>(n); NameCollisionNode* ncn = static_cast<NameCollisionNode*>(n);
@ -216,12 +244,10 @@ NameCollisionNode* Tree::findCollisionNode(const QString& name) const
*/ */
const FunctionNode* Tree::findFunctionNode(const QStringList& path, const FunctionNode* Tree::findFunctionNode(const QStringList& path,
const Node* relative, const Node* relative,
int findFlags) const int findFlags,
Node::Genus genus) const
{ {
if (!relative) if (path.size() == 3 && !path[0].isEmpty() && (genus != Node::CPP)) {
relative = root();
if (path.size() == 3 && !path[0].isEmpty()) {
QmlClassNode* qcn = lookupQmlType(QString(path[0] + "::" + path[1])); QmlClassNode* qcn = lookupQmlType(QString(path[0] + "::" + path[1]));
if (!qcn) { if (!qcn) {
QStringList p(path[1]); QStringList p(path[1]);
@ -240,6 +266,13 @@ const FunctionNode* Tree::findFunctionNode(const QStringList& path,
return static_cast<const FunctionNode*>(qcn->findFunctionNode(path[2])); return static_cast<const FunctionNode*>(qcn->findFunctionNode(path[2]));
} }
if (!relative)
relative = root();
else if (genus != Node::DontCare) {
if (genus != relative->genus())
relative = root();
}
do { do {
const Node* node = relative; const Node* node = relative;
int i; int i;
@ -252,7 +285,7 @@ const FunctionNode* Tree::findFunctionNode(const QStringList& path,
if (i == path.size() - 1) if (i == path.size() - 1)
next = ((InnerNode*) node)->findFunctionNode(path.at(i)); next = ((InnerNode*) node)->findFunctionNode(path.at(i));
else else
next = ((InnerNode*) node)->findChildNode(path.at(i)); next = ((InnerNode*) node)->findChildNode(path.at(i), genus);
if (!next && node->type() == Node::Class && (findFlags & SearchBaseClasses)) { if (!next && node->type() == Node::Class && (findFlags & SearchBaseClasses)) {
NodeList baseClasses = allBaseClasses(static_cast<const ClassNode*>(node)); NodeList baseClasses = allBaseClasses(static_cast<const ClassNode*>(node));
@ -260,7 +293,7 @@ const FunctionNode* Tree::findFunctionNode(const QStringList& path,
if (i == path.size() - 1) if (i == path.size() - 1)
next = static_cast<const InnerNode*>(baseClass)->findFunctionNode(path.at(i)); next = static_cast<const InnerNode*>(baseClass)->findFunctionNode(path.at(i));
else else
next = static_cast<const InnerNode*>(baseClass)->findChildNode(path.at(i)); next = static_cast<const InnerNode*>(baseClass)->findChildNode(path.at(i), genus);
if (next) if (next)
break; break;
@ -669,6 +702,181 @@ Node* Tree::findNodeRecursive(const QStringList& path,
return 0; return 0;
} }
/*!
Searches the tree for a node that matches the \a path plus
the \a target. The search begins at \a start and moves up
the parent chain from there, or, if \a start is 0, the search
begins at the root.
The \a flags can indicate whether to search base classes and/or
the enum values in enum types. \a genus can be a further restriction
on what kind of node is an acceptible match, i.e. CPP or QML.
If a matching node is found, \a ref is an output parameter that
is set to the HTML reference to use for the link.
*/
const Node* Tree::findNodeForTarget(const QStringList& path,
const QString& target,
const Node* start,
int flags,
Node::Genus genus,
QString& ref) const
{
const Node* node = 0;
QString p;
if (path.size() > 1)
p = path.join(QString("::"));
else {
p = path.at(0);
node = findDocNodeByTitle(p);
if (node) {
if (!target.isEmpty()) {
ref = getRef(target, node);
if (ref.isEmpty())
node = 0;
}
if (node)
return node;
}
}
node = findUnambiguousTarget(p, ref);
if (node) {
if (!target.isEmpty()) {
ref = getRef(target, node);
if (ref.isEmpty())
node = 0;
}
if (node)
return node;
}
const Node* current = start;
if (!current)
current = root();
/*
If the path contains one or two double colons ("::"),
check first to see if the first two path strings refer
to a QML element. If they do, path[0] will be the QML
module identifier, and path[1] will be the QML type.
If the answer is yes, the reference identifies a QML
type node.
*/
int path_idx = 0;
if ((genus != Node::CPP) && (path.size() >= 2) && !path[0].isEmpty()) {
QmlClassNode* qcn = lookupQmlType(QString(path[0] + "::" + path[1]));
if (qcn) {
current = qcn;
if (path.size() == 2) {
if (!target.isEmpty()) {
ref = getRef(target, current);
if (!ref.isEmpty())
return current;
else if (genus == Node::QML)
return 0;
}
else
return current;
}
path_idx = 2;
}
}
while (current) {
if (current->isInnerNode()) {
const Node* node = matchPathAndTarget(path, path_idx, target, current, flags, genus, ref);
if (node)
return node;
}
current = current->parent();
path_idx = 0;
}
return 0;
}
/*!
First, the \a path is used to find a node. The \a path
matches some part of the node's fully quallified name.
If the \a target is not empty, it must match a target
in the matching node. If the matching of the \a path
and the \a target (if present) is successful, \a ref
is set from the \a target, and the pointer to the
matching node is returned. \a idx is the index into the
\a path where to begin the matching. The function is
recursive with idx being incremented for each recursive
call.
The matching node must be of the correct \a genus, i.e.
either QML or C++, but \a genus can be set to \c DontCare.
\a flags indicates whether to search base classes and
whether to search for an enum value. \a node points to
the node where the search should begin, assuming the
\a path is a not a fully-qualified name. \a node is
most often the root of this Tree.
*/
const Node* Tree::matchPathAndTarget(const QStringList& path,
int idx,
const QString& target,
const Node* node,
int flags,
Node::Genus genus,
QString& ref) const
{
/*
If the path has been matched, then if there is a target,
try to match the target. If there is a target, but you
can't match it at the end of the path, give up; return 0.
*/
if (idx == path.size()) {
if (!target.isEmpty()) {
ref = getRef(target, node);
if (ref.isEmpty())
return 0;
}
if (node->isFunction() && node->name() == node->parent()->name())
node = node->parent();
return node;
}
const Node* t = 0;
QString name = path.at(idx);
QList<Node*> nodes;
node->findChildren(name, nodes);
foreach (const Node* n, nodes) {
if (genus != Node::DontCare) {
if (n->genus() != genus)
continue;
}
t = matchPathAndTarget(path, idx+1, target, n, flags, genus, ref);
if (t && !t->isPrivate())
return t;
}
if (target.isEmpty()) {
if ((idx) == (path.size()-1) && node->isInnerNode() && (flags & SearchEnumValues)) {
t = static_cast<const InnerNode*>(node)->findEnumNodeForValue(path.at(idx));
if (t)
return t;
}
}
if ((genus != Node::QML) && node->isClass() && (flags & SearchBaseClasses)) {
NodeList baseClasses = allBaseClasses(static_cast<const ClassNode*>(node));
foreach (const Node* bc, baseClasses) {
t = matchPathAndTarget(path, idx, target, bc, flags, genus, ref);
if (t && ! t->isPrivate())
return t;
if (target.isEmpty()) {
if ((idx) == (path.size()-1) && (flags & SearchEnumValues)) {
t = static_cast<const InnerNode*>(bc)->findEnumNodeForValue(path.at(idx));
if (t)
return t;
}
}
}
}
return 0;
}
/*! /*!
Searches the tree for a node that matches the \a path. The Searches the tree for a node that matches the \a path. The
search begins at \a start but can move up the parent chain search begins at \a start but can move up the parent chain
@ -677,37 +885,15 @@ Node* Tree::findNodeRecursive(const QStringList& path,
This findNode() callse the other findNode(), which is not This findNode() callse the other findNode(), which is not
called anywhere else. called anywhere else.
*/ */
const Node* Tree::findNode(const QStringList& path, const Node* start, int findFlags) const const Node* Tree::findNode(const QStringList& path,
const Node* start,
int findFlags,
Node::Genus genus) const
{ {
const Node* current = start; const Node* current = start;
if (!current) if (!current)
current = root(); current = root();
/*
First, search for a node assuming we don't want a QML node.
If that search fails, search again assuming we do want a
QML node.
*/
const Node* n = findNode(path, current, findFlags, false);
if (n)
return n;
return findNode(path, current, findFlags, true);
}
/*!
This overload function was extracted from the one above that has the
same signature without the last bool parameter, \a qml. This version
is called only by that other one. It is therefore private. It can
be called a second time by that other version, if the first call
returns null. If \a qml is false, the search will only match a node
that is not a QML node. If \a qml is true, the search will only
match a node that is a QML node.
This findNode() is only called by the other findNode().
*/
const Node* Tree::findNode(const QStringList& path, const Node* start, int findFlags, bool qml) const
{
const Node* current = start;
do { do {
const Node* node = current; const Node* node = current;
int i; int i;
@ -718,10 +904,10 @@ const Node* Tree::findNode(const QStringList& path, const Node* start, int findF
check first to see if the first two path strings refer check first to see if the first two path strings refer
to a QML element. If they do, path[0] will be the QML to a QML element. If they do, path[0] will be the QML
module identifier, and path[1] will be the QML type. module identifier, and path[1] will be the QML type.
If the anser is yes, the reference identifies a QML If the answer is yes, the reference identifies a QML
class node. type node.
*/ */
if (qml && path.size() >= 2 && !path[0].isEmpty()) { if ((genus != Node::CPP) && (path.size() >= 2) && !path[0].isEmpty()) {
QmlClassNode* qcn = lookupQmlType(QString(path[0] + "::" + path[1])); QmlClassNode* qcn = lookupQmlType(QString(path[0] + "::" + path[1]));
if (qcn) { if (qcn) {
node = qcn; node = qcn;
@ -735,14 +921,14 @@ const Node* Tree::findNode(const QStringList& path, const Node* start, int findF
if (node == 0 || !node->isInnerNode()) if (node == 0 || !node->isInnerNode())
break; break;
const Node* next = static_cast<const InnerNode*>(node)->findChildNode(path.at(i), qml); const Node* next = static_cast<const InnerNode*>(node)->findChildNode(path.at(i), genus);
if (!next && (findFlags & SearchEnumValues) && i == path.size()-1) { if (!next && (findFlags & SearchEnumValues) && i == path.size()-1) {
next = static_cast<const InnerNode*>(node)->findEnumNodeForValue(path.at(i)); next = static_cast<const InnerNode*>(node)->findEnumNodeForValue(path.at(i));
} }
if (!next && !qml && node->type() == Node::Class && (findFlags & SearchBaseClasses)) { if (!next && (genus != Node::QML) && node->isClass() && (findFlags & SearchBaseClasses)) {
NodeList baseClasses = allBaseClasses(static_cast<const ClassNode*>(node)); NodeList baseClasses = allBaseClasses(static_cast<const ClassNode*>(node));
foreach (const Node* baseClass, baseClasses) { foreach (const Node* baseClass, baseClasses) {
next = static_cast<const InnerNode*>(baseClass)->findChildNode(path.at(i)); next = static_cast<const InnerNode*>(baseClass)->findChildNode(path.at(i), genus);
if (!next && (findFlags & SearchEnumValues) && i == path.size() - 1) if (!next && (findFlags & SearchEnumValues) && i == path.size() - 1)
next = static_cast<const InnerNode*>(baseClass)->findEnumNodeForValue(path.at(i)); next = static_cast<const InnerNode*>(baseClass)->findEnumNodeForValue(path.at(i));
if (next) { if (next) {
@ -752,13 +938,8 @@ const Node* Tree::findNode(const QStringList& path, const Node* start, int findF
} }
node = next; node = next;
} }
if (node && i == path.size() if (node && i == path.size())
&& (!(findFlags & NonFunction) || node->type() != Node::Function return node;
|| ((FunctionNode*)node)->metaness() == FunctionNode::MacroWithoutParams)) {
if (node->isCollisionNode())
node = node->applyModuleName(start);
return node;
}
current = current->parent(); current = current->parent();
} while (current); } while (current);
@ -771,16 +952,24 @@ const Node* Tree::findNode(const QStringList& path, const Node* start, int findF
it returns the ref from that node. Otherwise it returns an it returns the ref from that node. Otherwise it returns an
empty string. empty string.
*/ */
QString Tree::findTarget(const QString& target, const Node* node) const QString Tree::getRef(const QString& target, const Node* node) const
{ {
QString key = Doc::canonicalTitle(target); TargetMap::const_iterator i = nodesByTargetTitle_.constFind(target);
TargetMap::const_iterator i = nodesByTarget_.constFind(key); if (i != nodesByTargetTitle_.constEnd()) {
if (i != nodesByTarget_.constEnd()) {
do { do {
if (i.value().node_ == node) if (i.value()->node_ == node)
return i.value().ref_; return i.value()->ref_;
++i; ++i;
} while (i != nodesByTarget_.constEnd() && i.key() == key); } while (i != nodesByTargetTitle_.constEnd() && i.key() == target);
}
QString key = Doc::canonicalTitle(target);
i = nodesByTargetRef_.constFind(key);
if (i != nodesByTargetRef_.constEnd()) {
do {
if (i.value()->node_ == node)
return i.value()->ref_;
++i;
} while (i != nodesByTargetRef_.constEnd() && i.key() == key);
} }
return QString(); return QString();
} }
@ -791,31 +980,15 @@ QString Tree::findTarget(const QString& target, const Node* node) const
the \a node, the \a priority. and a canonicalized form of the \a node, the \a priority. and a canonicalized form of
the \a name, which is later used. the \a name, which is later used.
*/ */
void Tree::insertTarget(const QString& name, TargetRec::Type type, Node* node, int priority) void Tree::insertTarget(const QString& name,
const QString& title,
TargetRec::Type type,
Node* node,
int priority)
{ {
TargetRec target; TargetRec* target = new TargetRec(name, title, type, node, priority);
target.type_ = type; nodesByTargetRef_.insert(name, target);
target.node_ = node; nodesByTargetTitle_.insert(title, target);
target.priority_ = priority;
target.ref_ = Doc::canonicalTitle(name);
nodesByTarget_.insert(name, target);
}
/*!
Searches this tree for a node named \a target and returns
a pointer to it if found. The \a start node is the starting
point, but it only makes sense if \a start is in this tree.
If \a start is not in this tree, \a start is set to 0 before
beginning the search to ensure that the search starts at the
root.
*/
const Node* Tree::resolveTarget(const QString& target, const Node* start)
{
QStringList path = target.split("::");
int flags = SearchBaseClasses | SearchEnumValues | NonFunction;
if (start && start->tree() != this)
start = 0;
return findNode(path, start, flags);
} }
/*! /*!
@ -826,8 +999,10 @@ void Tree::resolveTargets(InnerNode* root)
foreach (Node* child, root->childNodes()) { foreach (Node* child, root->childNodes()) {
if (child->type() == Node::Document) { if (child->type() == Node::Document) {
DocNode* node = static_cast<DocNode*>(child); DocNode* node = static_cast<DocNode*>(child);
if (!node->title().isEmpty()) { QString key = node->title();
QString key = Doc::canonicalTitle(node->title()); if (!key.isEmpty()) {
if (key.contains(QChar(' ')))
key = Doc::canonicalTitle(key);
QList<DocNode*> nodes = docNodesByTitle_.values(key); QList<DocNode*> nodes = docNodesByTitle_.values(key);
bool alreadyThere = false; bool alreadyThere = false;
if (!nodes.empty()) { if (!nodes.empty()) {
@ -840,9 +1015,8 @@ void Tree::resolveTargets(InnerNode* root)
} }
} }
} }
if (!alreadyThere) { if (!alreadyThere)
docNodesByTitle_.insert(key, node); docNodesByTitle_.insert(key, node);
}
} }
if (node->subType() == Node::Collision) { if (node->subType() == Node::Collision) {
resolveTargets(node); resolveTargets(node);
@ -851,41 +1025,41 @@ void Tree::resolveTargets(InnerNode* root)
if (child->doc().hasTableOfContents()) { if (child->doc().hasTableOfContents()) {
const QList<Atom*>& toc = child->doc().tableOfContents(); const QList<Atom*>& toc = child->doc().tableOfContents();
TargetRec target;
target.node_ = child;
target.priority_ = 3;
for (int i = 0; i < toc.size(); ++i) { for (int i = 0; i < toc.size(); ++i) {
target.ref_ = refForAtom(toc.at(i)); QString ref = refForAtom(toc.at(i));
QString title = Text::sectionHeading(toc.at(i)).toString(); QString title = Text::sectionHeading(toc.at(i)).toString();
if (!title.isEmpty()) { if (!ref.isEmpty() && !title.isEmpty()) {
QString key = Doc::canonicalTitle(title); QString key = Doc::canonicalTitle(title);
nodesByTarget_.insert(key, target); TargetRec* target = new TargetRec(ref, title, TargetRec::Contents, child, 3);
nodesByTargetRef_.insert(key, target);
nodesByTargetTitle_.insert(title, target);
} }
} }
} }
if (child->doc().hasKeywords()) { if (child->doc().hasKeywords()) {
const QList<Atom*>& keywords = child->doc().keywords(); const QList<Atom*>& keywords = child->doc().keywords();
TargetRec target;
target.node_ = child;
target.priority_ = 1;
for (int i = 0; i < keywords.size(); ++i) { for (int i = 0; i < keywords.size(); ++i) {
target.ref_ = refForAtom(keywords.at(i)); QString ref = refForAtom(keywords.at(i));
QString key = Doc::canonicalTitle(keywords.at(i)->string()); QString title = keywords.at(i)->string();
nodesByTarget_.insert(key, target); if (!ref.isEmpty() && !title.isEmpty()) {
QString key = Doc::canonicalTitle(title);
TargetRec* target = new TargetRec(ref, title, TargetRec::Keyword, child, 1);
nodesByTargetRef_.insert(key, target);
nodesByTargetTitle_.insert(title, target);
}
} }
} }
if (child->doc().hasTargets()) { if (child->doc().hasTargets()) {
const QList<Atom*>& toc = child->doc().targets(); const QList<Atom*>& targets = child->doc().targets();
TargetRec target; for (int i = 0; i < targets.size(); ++i) {
target.node_ = child; QString ref = refForAtom(targets.at(i));
target.priority_ = 2; QString title = targets.at(i)->string();
if (!ref.isEmpty() && !title.isEmpty()) {
for (int i = 0; i < toc.size(); ++i) { QString key = Doc::canonicalTitle(title);
target.ref_ = refForAtom(toc.at(i)); TargetRec* target = new TargetRec(ref, title, TargetRec::Target, child, 2);
QString key = Doc::canonicalTitle(toc.at(i)->string()); nodesByTargetRef_.insert(key, target);
nodesByTarget_.insert(key, target); nodesByTargetTitle_.insert(title, target);
}
} }
} }
} }
@ -896,46 +1070,58 @@ void Tree::resolveTargets(InnerNode* root)
finds one, it sets \a ref and returns the found node. finds one, it sets \a ref and returns the found node.
*/ */
const Node* const Node*
Tree::findUnambiguousTarget(const QString& target, QString& ref) Tree::findUnambiguousTarget(const QString& target, QString& ref) const
{ {
TargetRec bestTarget;
int numBestTargets = 0; int numBestTargets = 0;
QList<TargetRec> bestTargetList; TargetRec* bestTarget = 0;
QList<TargetRec*> bestTargetList;
QString key = Doc::canonicalTitle(target); QString key = target;
TargetMap::iterator i = nodesByTarget_.find(key); TargetMap::const_iterator i = nodesByTargetTitle_.find(key);
while (i != nodesByTarget_.end()) { while (i != nodesByTargetTitle_.constEnd()) {
if (i.key() != key) if (i.key() != key)
break; break;
const TargetRec& candidate = i.value(); TargetRec* candidate = i.value();
if (candidate.priority_ < bestTarget.priority_) { if (!bestTarget || (candidate->priority_ < bestTarget->priority_)) {
bestTarget = candidate; bestTarget = candidate;
bestTargetList.clear(); bestTargetList.clear();
bestTargetList.append(candidate); bestTargetList.append(candidate);
numBestTargets = 1; numBestTargets = 1;
} else if (candidate.priority_ == bestTarget.priority_) { } else if (candidate->priority_ == bestTarget->priority_) {
bestTargetList.append(candidate); bestTargetList.append(candidate);
++numBestTargets; ++numBestTargets;
} }
++i; ++i;
} }
if (numBestTargets > 0) { if (bestTarget) {
if (numBestTargets == 1) { ref = bestTarget->ref_;
ref = bestTarget.ref_; return bestTarget->node_;
return bestTarget.node_;
}
else if (bestTargetList.size() > 1) {
#if 0
qDebug() << "TARGET:" << target << numBestTargets;
for (int i=0; i<bestTargetList.size(); ++i) {
const Node* n = bestTargetList.at(i).node_;
qDebug() << " " << n->name() << n->title();
}
#endif
ref = bestTargetList.at(0).ref_;
return bestTargetList.at(0).node_;
}
} }
numBestTargets = 0;
bestTarget = 0;
key = Doc::canonicalTitle(target);
i = nodesByTargetRef_.find(key);
while (i != nodesByTargetRef_.constEnd()) {
if (i.key() != key)
break;
TargetRec* candidate = i.value();
if (!bestTarget || (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 (bestTarget) {
ref = bestTarget->ref_;
return bestTarget->node_;
}
ref.clear(); ref.clear();
return 0; return 0;
} }
@ -945,8 +1131,11 @@ Tree::findUnambiguousTarget(const QString& target, QString& ref)
*/ */
const DocNode* Tree::findDocNodeByTitle(const QString& title) const const DocNode* Tree::findDocNodeByTitle(const QString& title) const
{ {
QString key = Doc::canonicalTitle(title); DocNodeMultiMap::const_iterator i;
DocNodeMultiMap::const_iterator i = docNodesByTitle_.constFind(key); if (title.contains(QChar(' ')))
i = docNodesByTitle_.constFind(Doc::canonicalTitle(title));
else
i = docNodesByTitle_.constFind(title);
if (i != docNodesByTitle_.constEnd()) { if (i != docNodesByTitle_.constEnd()) {
/* /*
Reporting all these duplicate section titles is probably Reporting all these duplicate section titles is probably
@ -1241,13 +1430,15 @@ void Tree::insertQmlType(const QString& key, QmlClassNode* n)
/*! /*!
Split \a target on "::" and find the function node with that Split \a target on "::" and find the function node with that
path. path.
Called in HtmlGenerator, DitaXmlGenerator, and QdocDatabase.
*/ */
const Node* Tree::resolveFunctionTarget(const QString& target, const Node* relative) const Node* Tree::findFunctionNode(const QString& target, const Node* relative, Node::Genus genus)
{ {
QString t = target; QString t = target;
t.chop(2); t.chop(2);
QStringList path = t.split("::"); QStringList path = t.split("::");
const FunctionNode* fn = findFunctionNode(path, relative, SearchBaseClasses); const FunctionNode* fn = findFunctionNode(path, relative, SearchBaseClasses, genus);
if (fn && fn->metaness() != FunctionNode::MacroWithoutParams) if (fn && fn->metaness() != FunctionNode::MacroWithoutParams)
return fn; return fn;
return 0; return 0;

View File

@ -58,15 +58,24 @@ struct TargetRec
{ {
public: public:
enum Type { Unknown, Target, Keyword, Contents, Class, Function, Page, Subtitle }; enum Type { Unknown, Target, Keyword, Contents, Class, Function, Page, Subtitle };
TargetRec() : node_(0), priority_(INT_MAX), type_(Unknown) { }
TargetRec(const QString& name,
const QString& title,
TargetRec::Type type,
Node* node,
int priority)
: node_(node), ref_(name), title_(title), priority_(priority), type_(type) { }
bool isEmpty() const { return ref_.isEmpty(); } bool isEmpty() const { return ref_.isEmpty(); }
Node* node_; Node* node_;
QString ref_; QString ref_;
QString title_;
int priority_; int priority_;
Type type_; Type type_;
}; };
typedef QMultiMap<QString, TargetRec> TargetMap; typedef QMultiMap<QString, TargetRec*> TargetMap;
typedef QMultiMap<QString, DocNode*> DocNodeMultiMap; typedef QMultiMap<QString, DocNode*> DocNodeMultiMap;
typedef QMap<QString, QmlClassNode*> QmlTypeMap; typedef QMap<QString, QmlClassNode*> QmlTypeMap;
typedef QMultiMap<QString, const ExampleNode*> ExampleNodeMap; typedef QMultiMap<QString, const ExampleNode*> ExampleNodeMap;
@ -83,10 +92,11 @@ class Tree
Tree(const QString& module, QDocDatabase* qdb); Tree(const QString& module, QDocDatabase* qdb);
~Tree(); ~Tree();
ClassNode* findClassNode(const QStringList& path, Node* start = 0) const; Node* findNodeForInclude(const QStringList& path) const;
ClassNode* findClassNode(const QStringList& path, const Node* start = 0) const;
NamespaceNode* findNamespaceNode(const QStringList& path) const; NamespaceNode* findNamespaceNode(const QStringList& path) const;
FunctionNode* findFunctionNode(const QStringList& parentPath, const FunctionNode* clone); FunctionNode* findFunctionNode(const QStringList& parentPath, const FunctionNode* clone);
const Node* resolveFunctionTarget(const QString& target, const Node* relative); const Node* findFunctionNode(const QString& target, const Node* relative, Node::Genus genus);
Node* findNodeRecursive(const QStringList& path, Node* findNodeRecursive(const QStringList& path,
int pathIndex, int pathIndex,
@ -97,14 +107,24 @@ class Tree
Node* start, Node* start,
const NodeTypeList& types) const; const NodeTypeList& types) const;
const Node* findNode(const QStringList &path, const Node* findNodeForTarget(const QStringList& path,
const Node* relative = 0, const QString& target,
int findFlags = 0) const; const Node* node,
int flags,
Node::Genus genus,
QString& ref) const;
const Node* matchPathAndTarget(const QStringList& path,
int idx,
const QString& target,
const Node* node,
int flags,
Node::Genus genus,
QString& ref) const;
const Node* findNode(const QStringList& path, const Node* findNode(const QStringList &path,
const Node* start, const Node* relative, // = 0,
int findFlags, int findFlags, // = 0,
bool qml) const; Node::Genus genus) const; // = Node::DontCare) const;
QmlClassNode* findQmlTypeNode(const QStringList& path); QmlClassNode* findQmlTypeNode(const QStringList& path);
@ -112,11 +132,14 @@ class Tree
InnerNode* findRelatesNode(const QStringList& path); InnerNode* findRelatesNode(const QStringList& path);
NameCollisionNode* checkForCollision(const QString& name); NameCollisionNode* checkForCollision(const QString& name);
NameCollisionNode* findCollisionNode(const QString& name) const; NameCollisionNode* findCollisionNode(const QString& name) const;
QString findTarget(const QString& target, const Node* node) const; QString getRef(const QString& target, const Node* node) const;
void insertTarget(const QString& name, TargetRec::Type type, Node* node, int priority); void insertTarget(const QString& name,
const Node* resolveTarget(const QString& target, const Node* start); const QString& title,
TargetRec::Type type,
Node* node,
int priority);
void resolveTargets(InnerNode* root); void resolveTargets(InnerNode* root);
const Node* findUnambiguousTarget(const QString& target, QString& ref); const Node* findUnambiguousTarget(const QString& target, QString& ref) const;
const DocNode* findDocNodeByTitle(const QString& title) const; const DocNode* findDocNodeByTitle(const QString& title) const;
void addPropertyFunction(PropertyNode *property, void addPropertyFunction(PropertyNode *property,
@ -131,7 +154,8 @@ class Tree
const FunctionNode *findFunctionNode(const QStringList &path, const FunctionNode *findFunctionNode(const QStringList &path,
const Node *relative = 0, const Node *relative = 0,
int findFlags = 0) const; int findFlags = 0,
Node::Genus genus = Node::DontCare) const;
const NamespaceNode *root() const { return &root_; } const NamespaceNode *root() const { return &root_; }
FunctionNode *findVirtualFunctionInBaseClasses(ClassNode *classe, FunctionNode *findVirtualFunctionInBaseClasses(ClassNode *classe,
@ -182,7 +206,8 @@ private:
NamespaceNode root_; NamespaceNode root_;
PropertyMap unresolvedPropertyMap; PropertyMap unresolvedPropertyMap;
DocNodeMultiMap docNodesByTitle_; DocNodeMultiMap docNodesByTitle_;
TargetMap nodesByTarget_; TargetMap nodesByTargetRef_;
TargetMap nodesByTargetTitle_;
CNMap groups_; CNMap groups_;
CNMap modules_; CNMap modules_;
CNMap qmlModules_; CNMap qmlModules_;