diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/tools/qdoc/atom.cpp | 24 | ||||
| -rw-r--r-- | src/tools/qdoc/atom.h | 8 | ||||
| -rw-r--r-- | src/tools/qdoc/ditaxmlgenerator.cpp | 147 | ||||
| -rw-r--r-- | src/tools/qdoc/htmlgenerator.cpp | 75 | ||||
| -rw-r--r-- | src/tools/qdoc/node.cpp | 89 | ||||
| -rw-r--r-- | src/tools/qdoc/node.h | 33 | ||||
| -rw-r--r-- | src/tools/qdoc/qdocdatabase.cpp | 158 | ||||
| -rw-r--r-- | src/tools/qdoc/qdocdatabase.h | 79 | ||||
| -rw-r--r-- | src/tools/qdoc/qdocindexfiles.cpp | 27 | ||||
| -rw-r--r-- | src/tools/qdoc/qmlvisitor.cpp | 2 | ||||
| -rw-r--r-- | src/tools/qdoc/tree.cpp | 463 | ||||
| -rw-r--r-- | src/tools/qdoc/tree.h | 59 |
12 files changed, 697 insertions, 467 deletions
diff --git a/src/tools/qdoc/atom.cpp b/src/tools/qdoc/atom.cpp index 5c699b05469..0c17a38e51e 100644 --- a/src/tools/qdoc/atom.cpp +++ b/src/tools/qdoc/atom.cpp @@ -369,12 +369,15 @@ void Atom::dump() const } /*! - The only constructor for LinkAtom. It only create an Atom - of type Atom::Link with \a p1 being the link text. \a p2 - contains some search parameters. + The only constructor for LinkAtom. It creates an Atom of + type Atom::Link. \a p1 being the link target. \a p2 is the + 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) - : 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(' ')); foreach (const QString& p, params) { @@ -388,10 +391,15 @@ LinkAtom::LinkAtom(const QString& p1, const QString& p2) if (goal_ != Node::NoType) continue; } - if (p == "qml") - genus_ = QML; - else if (p == "cpp") - genus_ = CPP; + if (p == "qml") { + genus_ = Node::QML; + continue; + } + if (p == "cpp") { + genus_ = Node::CPP; + continue; + } + break; } } diff --git a/src/tools/qdoc/atom.h b/src/tools/qdoc/atom.h index 36a7390ae21..65ba2a9b5cf 100644 --- a/src/tools/qdoc/atom.h +++ b/src/tools/qdoc/atom.h @@ -140,8 +140,6 @@ public: Last = UnknownCommand }; - enum NodeGenus { DontCare, CPP, QML }; - friend class LinkAtom; Atom(const QString& string) @@ -201,7 +199,7 @@ public: const QStringList& strings() const { return strs; } 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 Tree* domain() const { return 0; } virtual Node::Type goal() const { return Node::NoType; } @@ -221,13 +219,13 @@ class LinkAtom : public Atom virtual ~LinkAtom() { } 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 Tree* domain() const { return domain_; } virtual Node::Type goal() const { return goal_; } protected: - NodeGenus genus_; + Node::Genus genus_; Node::Type goal_; Tree* domain_; }; diff --git a/src/tools/qdoc/ditaxmlgenerator.cpp b/src/tools/qdoc/ditaxmlgenerator.cpp index c2a5cdb8b8f..83d89d01873 100644 --- a/src/tools/qdoc/ditaxmlgenerator.cpp +++ b/src/tools/qdoc/ditaxmlgenerator.cpp @@ -3444,7 +3444,7 @@ void DitaXmlGenerator::writeText(const QString& markedCode, const Node* relative text.clear(); } par1 = QStringRef(); - n = qdb_->resolveFunctionTarget(arg.toString(), relative); + n = qdb_->findFunctionNode(arg.toString(), relative, Node::DontCare); addLink(linkForNode(n, relative), arg); break; case 1: @@ -3455,7 +3455,7 @@ void DitaXmlGenerator::writeText(const QString& markedCode, const Node* relative text.clear(); } par1 = QStringRef(); - n = qdb_->resolveType(arg.toString(), relative); + n = qdb_->findTypeNode(arg.toString(), relative); if (n && n->isQmlBasicType()) { if (relative && relative->isQmlType()) 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) { - if (atom->string().contains(QLatin1Char(':')) && (atom->string().startsWith("file:") || - atom->string().startsWith("http:") || - atom->string().startsWith("https:") || - atom->string().startsWith("ftp:") || - atom->string().startsWith("mailto:"))) { - return atom->string(); // It's some kind of protocol. + const QString& t = atom->string(); + if (t.at(0) == QChar('h')) { + if (t.startsWith("http:") || t.startsWith("https:")) + return t; + } + 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 link; - QStringList path = atom->string().split("#"); - QString first = path.first().trimmed(); - *node = 0; - 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; - } - } - } - } + *node = qdb_->findNodeForAtom(atom, relative, ref); if (!(*node)) - return link; // empty - - if (!(*node)->url().isEmpty()) - return (*node)->url(); + return QString(); - if (!path.isEmpty()) { - ref = qdb_->findTarget(path.first(), *node); + QString url = (*node)->url(); + if (!url.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 to the page that *node represents, and then if we found a target on that page, we connect the target to the link with '#'. */ - link = linkForNode(*node, relative); + QString link = linkForNode(*node, relative); if (*node && (*node)->subType() == Node::Image) 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; + } return link; } @@ -3810,31 +3795,20 @@ QString DitaXmlGenerator::getAutoLink(const Atom *atom, const Node *relative, co { QString ref; 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)) - return link; // empty + return QString(); - if (!(*node)->url().isEmpty()) - return (*node)->url(); + QString url = (*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); if (!ref.isEmpty()) @@ -3963,40 +3937,7 @@ void DitaXmlGenerator::generateStatus(const Node* node, CodeMarker* marker) Generator::generateStatus(node, marker); break; case Node::Compat: - if (node->isInnerNode()) { - 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); + // Porting to Qt 4 no longer supported break; default: Generator::generateStatus(node, marker); @@ -4652,7 +4593,7 @@ void DitaXmlGenerator::replaceTypesWithLinks(const Node* n, const InnerNode* par } i += 2; 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) { //Do not generate a link from a C++ function to a QML Basic Type (such as int) if (n->isFunction() && tn->isQmlBasicType()) @@ -6159,7 +6100,7 @@ void DitaXmlGenerator::generateCollisionPages() int count = 0; for (int i=0; i<collisions.size(); ++i) { InnerNode* n = static_cast<InnerNode*>(collisions.at(i)); - if (n->findChildNode(t.key())) { + if (n->findChildNode(t.key(), Node::DontCare)) { ++count; if (count > 1) { targets.append(t.key()); @@ -6181,7 +6122,7 @@ void DitaXmlGenerator::generateCollisionPages() writeStartTag(DT_ul); for (int i=0; i<collisions.size(); ++i) { InnerNode* n = static_cast<InnerNode*>(collisions.at(i)); - Node* p = n->findChildNode(*t); + Node* p = n->findChildNode(*t, Node::DontCare); if (p) { QString link = linkForNode(p,0); QString label; diff --git a/src/tools/qdoc/htmlgenerator.cpp b/src/tools/qdoc/htmlgenerator.cpp index dd665ea8854..44de7e11540 100644 --- a/src/tools/qdoc/htmlgenerator.cpp +++ b/src/tools/qdoc/htmlgenerator.cpp @@ -816,7 +816,7 @@ int HtmlGenerator::generateAtom(const Atom *atom, const Node *relative, CodeMark inObsoleteLink = false; const Node *node = 0; 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())); } else { @@ -1493,7 +1493,7 @@ void HtmlGenerator::generateCollisionPages() int count = 0; for (int i=0; i<collisions.size(); ++i) { InnerNode* n = static_cast<InnerNode*>(collisions.at(i)); - if (n->findChildNode(t.key())) { + if (n->findChildNode(t.key(), Node::DontCare)) { ++count; if (count > 1) { targets.append(t.key()); @@ -1512,7 +1512,7 @@ void HtmlGenerator::generateCollisionPages() out() << "<ul>\n"; for (int i=0; i<collisions.size(); ++i) { InnerNode* n = static_cast<InnerNode*>(collisions.at(i)); - Node* p = n->findChildNode(*t); + Node* p = n->findChildNode(*t, Node::DontCare); if (p) { QString link = linkForNode(p,0); QString label; @@ -3287,7 +3287,7 @@ QString HtmlGenerator::highlightedCode(const QString& markedCode, if (src.at(i) == charLangle && src.at(i + 1) == charAt) { i += 2; if (parseArg(src, funcTag, &i, srcSize, &arg, &par1)) { - const Node* n = qdb_->resolveFunctionTarget(par1.toString(), relative); + const Node* n = qdb_->findFunctionNode(par1.toString(), relative, Node::DontCare); QString link = linkForNode(n, relative); addLink(link, arg, &html); par1 = QStringRef(); @@ -3312,7 +3312,7 @@ QString HtmlGenerator::highlightedCode(const QString& markedCode, bool handled = false; if (parseArg(src, typeTag, &i, srcSize, &arg, &par1)) { par1 = QStringRef(); - const Node* n = qdb_->resolveType(arg.toString(), relative); + const Node* n = qdb_->findTypeNode(arg.toString(), relative); html += QLatin1String("<span class=\"type\">"); if (n && n->isQmlBasicType()) { if (relative && relative->isQmlType()) @@ -3330,9 +3330,8 @@ QString HtmlGenerator::highlightedCode(const QString& markedCode, if (arg.at(0) == QChar('&')) html += arg; else { - // zzz resolveClassTarget() - const Node* n = qdb_->resolveTarget(arg.toString(), relative); - if (n) + const Node* n = qdb_->findNodeForInclude(QStringList(arg.toString())); + if (n && n != relative) addLink(linkForNode(n,relative), arg, &html); else html += arg; @@ -3659,8 +3658,6 @@ QString HtmlGenerator::refForNode(const Node *node) return registerRef(ref); } -#define DEBUG_ABSTRACT 0 - /*! This function is called for links, i.e. for words that 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; - *node = qdb_->findNode(atom, relative, ref); + *node = qdb_->findNodeForAtom(atom, relative, ref); if (!(*node)) return QString(); @@ -3734,31 +3731,20 @@ QString HtmlGenerator::getAutoLink(const Atom *atom, const Node *relative, const { QString ref; 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)) - return link; // empty + return QString(); - if (!(*node)->url().isEmpty()) - return (*node)->url(); + QString url = (*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); 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) { - if (node == 0 || node == relative) + if (node == 0) return QString(); if (!node->url().isEmpty()) return node->url(); @@ -3977,26 +3963,7 @@ void HtmlGenerator::generateStatus(const Node *node, CodeMarker *marker) Generator::generateStatus(node, marker); break; case Node::Compat: - if (node->isInnerNode()) { - 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); + // Porting to Qt 4 no longer supported break; default: Generator::generateStatus(node, marker); diff --git a/src/tools/qdoc/node.cpp b/src/tools/qdoc/node.cpp index 87a055ae0fb..fa91d67f1c4 100644 --- a/src/tools/qdoc/node.cpp +++ b/src/tools/qdoc/node.cpp @@ -46,6 +46,7 @@ #include <quuid.h> #include "qdocdatabase.h" #include <qdebug.h> +#include "generator.h" QT_BEGIN_NAMESPACE @@ -717,22 +718,38 @@ InnerNode::~InnerNode() } /*! - Find the node in this node's children that has the - given \a name. If this node is a QML class node, be - sure to also look in the children of its property - group nodes. Return the matching node or 0. + If \a genus is \c{Node::DontCare}, find the first node in + this node's child list that has the given \a name. If this + node is a QML type, be sure to also look in the children + 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 (node && !node->isQmlPropertyGroup()) - return node; - if (isQmlType()) { - for (int i=0; i<children_.size(); ++i) { - Node* n = children_.at(i); - if (n->isQmlPropertyGroup()) { - node = static_cast<InnerNode*>(n)->findChildNode(name); - if (node) + if (genus == Node::DontCare) { + Node *node = childMap.value(name); + if (node && !node->isQmlPropertyGroup()) // mws asks: Why not property group? + return node; + if (isQmlType()) { + for (int i=0; i<children_.size(); ++i) { + Node* n = children_.at(i); + if (n->isQmlPropertyGroup()) { + 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; } } @@ -741,6 +758,39 @@ Node *InnerNode::findChildNode(const QString& name) const } /*! + 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 this node is a QML class node, be sure to also look in the children of its property group nodes. Return the matching node or 0. This is @@ -752,7 +802,7 @@ Node *InnerNode::findChildNode(const QString& name) const */ Node* InnerNode::findChildNode(const QString& name, bool qml) const { - QList<Node*> nodes = childMap.values(name); + NodeList nodes = childMap.values(name); if (!nodes.isEmpty()) { for (int i=0; i<nodes.size(); ++i) { Node* node = nodes.at(i); @@ -776,6 +826,7 @@ Node* InnerNode::findChildNode(const QString& name, bool qml) const } return primaryFunctionMap.value(name); } +#endif /*! This function is like findChildNode(), but if a node @@ -793,7 +844,7 @@ Node* InnerNode::findChildNode(const QString& name, Type type) if (type == Function) return primaryFunctionMap.value(name); else { - QList<Node*> nodes = childMap.values(name); + NodeList nodes = childMap.values(name); for (int i=0; i<nodes.size(); ++i) { Node* node = nodes.at(i); if (node->type() == type) @@ -803,13 +854,14 @@ Node* InnerNode::findChildNode(const QString& name, Type type) return 0; } +#if 0 /*! */ -void InnerNode::findNodes(const QString& name, QList<Node*>& n) +void InnerNode::findNodes(const QString& name, NodeList& n) { n.clear(); 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 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) n.append(node); } +#endif /*! Find a function node that is a child of this nose, such diff --git a/src/tools/qdoc/node.h b/src/tools/qdoc/node.h index 37fbe482b03..67ad006e2cb 100644 --- a/src/tools/qdoc/node.h +++ b/src/tools/qdoc/node.h @@ -117,6 +117,8 @@ public: LastSubtype }; + enum Genus { DontCare, CPP, QML }; + enum Access { Public, Protected, Private }; enum Status { @@ -217,6 +219,7 @@ public: virtual bool isNamespace() const { return false; } virtual bool isClass() const { return false; } virtual bool isQmlNode() const { return false; } + virtual bool isCppNode() const { return false; } virtual bool isQtQuickNode() const { return false; } virtual bool isAbstract() const { return false; } virtual bool isQmlPropertyGroup() const { return false; } @@ -233,6 +236,7 @@ public: virtual bool hasClasses() const { return false; } virtual void setAbstract(bool ) { } virtual void setWrapper() { } + virtual Node::Genus genus() const { return DontCare; } virtual QString title() const { return name(); } virtual QString fullTitle() const { return name(); } virtual QString subTitle() const { return QString(); } @@ -250,6 +254,7 @@ public: virtual void appendGroupName(const QString& ) { } virtual QString element() const { return QString(); } virtual Tree* tree() const; + virtual void findChildren(const QString& , NodeList& nodes) const { nodes.clear(); } bool isIndexNode() const { return indexNodeFlag_; } Type type() const { return nodeType_; } virtual SubType subType() const { return NoSubType; } @@ -271,6 +276,7 @@ public: void setLink(LinkType linkType, const QString &link, const QString &desc); Access access() const { return access_; } + bool isPrivate() const { return access_ == Private; } QString accessString() const; const Location& location() const { return loc_; } const Doc& doc() const { return doc_; } @@ -360,10 +366,11 @@ class InnerNode : public Node public: virtual ~InnerNode(); - Node* findChildNode(const QString& name) const; - Node* findChildNode(const QString& name, bool qml) const; + Node* findChildNode(const QString& name, Node::Genus genus) const; + //Node* findChildNode(const QString& name, bool qml) const; 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 FunctionNode* clone); void addInclude(const QString &include); @@ -443,6 +450,8 @@ public: virtual ~NamespaceNode() { } virtual bool isNamespace() const { return true; } 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; } private: @@ -473,7 +482,9 @@ public: ClassNode(InnerNode* parent, const QString& name); virtual ~ClassNode() { } virtual bool isClass() const { return true; } + virtual bool isCppNode() const { return true; } virtual bool isWrapper() const { return wrapper_; } + virtual Node::Genus genus() const { return Node::CPP; } virtual QString obsoleteLink() const { return obsoleteLink_; } virtual void setObsoleteLink(const QString& t) { obsoleteLink_ = t; } virtual void setWrapper() { wrapper_ = true; } @@ -618,6 +629,7 @@ public: virtual QString qmlModuleIdentifier() const; virtual QmlModuleNode* qmlModule() const { return qmlModule_; } virtual void setQmlModule(QmlModuleNode* t) { qmlModule_ = t; } + virtual Node::Genus genus() const { return Node::QML; } const ImportList& importList() const { return importList_; } void setImportList(const ImportList& il) { importList_ = il; } const QString& qmlBaseName() const { return qmlBaseName_; } @@ -655,6 +667,7 @@ public: virtual ~QmlBasicTypeNode() { } virtual bool isQmlNode() const { return true; } virtual bool isQmlBasicType() const { return true; } + virtual Node::Genus genus() const { return Node::QML; } }; class QmlPropertyGroupNode : public InnerNode @@ -670,6 +683,7 @@ public: virtual QString qmlModuleIdentifier() const { return parent()->qmlModuleIdentifier(); } virtual QString idNumber(); virtual bool isQmlPropertyGroup() const { return true; } + virtual Node::Genus genus() const { return Node::QML; } virtual QString element() const { return parent()->name(); } @@ -688,6 +702,7 @@ public: bool attached); virtual ~QmlPropertyNode() { } + virtual Node::Genus genus() const { return Node::QML; } virtual void setDataType(const QString& dataType) { type_ = dataType; } void setStored(bool stored) { stored_ = toFlagValue(stored); } void setDesignable(bool designable) { designable_ = toFlagValue(designable); } @@ -746,6 +761,8 @@ public: EnumNode(InnerNode* parent, const QString& name); virtual ~EnumNode() { } + virtual Node::Genus genus() const { return Node::CPP; } + virtual bool isCppNode() const { return true; } void addItem(const EnumItem& item); void setFlagsType(TypedefNode* typedeff); bool hasItem(const QString &name) const { return names.contains(name); } @@ -767,6 +784,8 @@ public: TypedefNode(InnerNode* parent, const QString& name); virtual ~TypedefNode() { } + virtual Node::Genus genus() const { return Node::CPP; } + virtual bool isCppNode() const { return true; } const EnumNode* associatedEnum() const { return ae; } private: @@ -873,6 +892,8 @@ public: (type() == QmlMethod) || (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 QString qmlTypeName() const { return parent()->qmlTypeName(); } virtual QString qmlModuleName() const { return parent()->qmlModuleName(); } @@ -911,6 +932,8 @@ public: PropertyNode(InnerNode* parent, const QString& name); virtual ~PropertyNode() { } + virtual Node::Genus genus() const { return Node::CPP; } + virtual bool isCppNode() const { return true; } virtual void setDataType(const QString& dataType) { type_ = dataType; } void addFunction(FunctionNode* function, FunctionRole role); void addSignal(FunctionNode* function, FunctionRole role); @@ -998,6 +1021,8 @@ public: VariableNode(InnerNode* parent, const QString &name); virtual ~VariableNode() { } + virtual Node::Genus genus() const { return Node::CPP; } + virtual bool isCppNode() const { return true; } void setLeftType(const QString &leftType) { lt = leftType; } void setRightType(const QString &rightType) { rt = rightType; } void setStatic(bool statique) { sta = statique; } @@ -1084,6 +1109,7 @@ class ModuleNode : public CollectionNode virtual ~ModuleNode() { } virtual bool isModule() const { return true; } + virtual bool isCppNode() const { return true; } virtual void setQtVariable(const QString& v) { qtVariable_ = v; } virtual QString qtVariable() const { return qtVariable_; } @@ -1098,6 +1124,7 @@ class QmlModuleNode : public CollectionNode : CollectionNode(Node::QmlModule, parent, name) { } virtual ~QmlModuleNode() { } + virtual bool isQmlNode() const { return true; } virtual bool isQmlModule() const { return true; } virtual QString qmlModuleName() const { return qmlModuleName_; } virtual QString qmlModuleVersion() const { diff --git a/src/tools/qdoc/qdocdatabase.cpp b/src/tools/qdoc/qdocdatabase.cpp index d43fdf49705..e9e62fb7f3c 100644 --- a/src/tools/qdoc/qdocdatabase.cpp +++ b/src/tools/qdoc/qdocdatabase.cpp @@ -379,20 +379,34 @@ void QDocForest::newPrimaryTree(const QString& module) } /*! - Searches the trees for a node named \a target and returns - a pointer to it if found. The \a relative node is the starting - point, but it only makes sense in the primary tree, which is - searched first. After the primary tree is searched, \a relative - is set to 0 for searching the index trees. When relative is 0, - the root nodes of the index trees are the starting points. - */ -const Node* QDocForest::resolveTarget(const QString& target, const Node* relative) + Searches through the forest for a node named \a targetPath + and returns a pointer to it if found. The \a relative node + is the starting point. It only makes sense for the primary + tree, which is searched first. After the primary tree has + been searched, \a relative is set to 0 for searching the + 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::findNodeForTarget(QStringList& targetPath, + const Node* relative, + Node::Genus genus, + QString& ref) { - QStringList path = target.split("::"); - int flags = SearchBaseClasses | SearchEnumValues | NonFunction; + int flags = SearchBaseClasses | SearchEnumValues; + + 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()) { - const Node* n = t->findNode(path, relative, flags); + const Node* n = t->findNodeForTarget(entityPath, target, relative, flags, genus, ref); if (n) return n; relative = 0; @@ -401,20 +415,6 @@ const Node* QDocForest::resolveTarget(const QString& target, const Node* relativ } /*! - 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 nodes of node type \a t into the collection multimap \a cnmm, which is cleared before starting. @@ -1343,7 +1343,7 @@ void QDocDatabase::resolveIssues() { When searching the index trees, the search begins at the 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("::"); 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()) 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); } else { - node = resolveTarget(target, relative); - if (!node) - node = findDocNodeByTitle(target); + QStringList path = target.split("::"); + int flags = SearchBaseClasses | SearchEnumValues; // | NonFunction; + 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; } @@ -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 \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 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; - QStringList path = atom->string().split("#"); - QString first = path.first().trimmed(); - path.removeFirst(); + + QStringList targetPath = atom->string().split("#"); + 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()) node = relative; // search for a target on the current page. - else if (atom->specifiesDomain()) { - qDebug() << "Processing LinkAtom"; - if (first.endsWith(".html")) { // The target is an html file. - node = atom->domain()->findNodeByNameAndType(QStringList(first), Node::Document); - } - else if (first.endsWith("()")) { // The target is a C++ function or QML method. - node = atom->domain()->resolveFunctionTarget(first, 0); //relative); - } + else if (domain) { + if (first.endsWith(".html")) + node = domain->findNodeByNameAndType(QStringList(first), Node::Document); + else if (first.endsWith("()")) + node = domain->findFunctionNode(first, 0, genus); else { - node = atom->domain()->resolveTarget(first, 0); // relative); - if (!node) - node = atom->domain()->findUnambiguousTarget(first, ref); // ref - if (!node && path.isEmpty()) - node = atom->domain()->findDocNodeByTitle(first); + int flags = SearchBaseClasses | SearchEnumValues; + QStringList nodePath = first.split("::"); + QString target; + targetPath.removeFirst(); + if (!targetPath.isEmpty()) { + target = targetPath.at(0); + targetPath.removeFirst(); + } + node = domain->findNodeForTarget(nodePath, target, relative, flags, genus, ref); + return node; } } else { - if (first.endsWith(".html")) { // The target is an html file. - node = findNodeByNameAndType(QStringList(first), Node::Document); // ref - } - else if (first.endsWith("()")) { // The target is a C++ function or QML method. - node = resolveFunctionTarget(first, relative); - } + if (first.endsWith(".html")) + node = findNodeByNameAndType(QStringList(first), Node::Document); + else if (first.endsWith("()")) + node = findFunctionNode(first, relative, genus); else { - node = resolveTarget(first, relative); // ref - if (!node) - node = findUnambiguousTarget(first, ref); // ref - if (!node && path.isEmpty()) - node = findDocNodeByTitle(first); + node = findNodeForTarget(targetPath, relative, genus, ref); + return node; } } + if (node && ref.isEmpty()) { if (!node->url().isEmpty()) return node; - if (!path.isEmpty()) { - ref = findTarget(path.first(), node); + targetPath.removeFirst(); + if (!targetPath.isEmpty()) { + ref = node->root()->tree()->getRef(targetPath.first(), node); if (ref.isEmpty()) node = 0; } diff --git a/src/tools/qdoc/qdocdatabase.h b/src/tools/qdoc/qdocdatabase.h index 99d1c46ca29..12105842b8f 100644 --- a/src/tools/qdoc/qdocdatabase.h +++ b/src/tools/qdoc/qdocdatabase.h @@ -88,9 +88,12 @@ class QDocForest const QVector<Tree*>& indexSearchOrder(); 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()) { - const Node* n = t->findNode(path, relative, findFlags); + const Node* n = t->findNode(path, relative, findFlags, genus); if (n) return n; relative = 0; @@ -116,43 +119,48 @@ class QDocForest return 0; } - InnerNode* findRelatesNode(const QStringList& path) { + Node* findNodeForInclude(const QStringList& path) { foreach (Tree* t, searchOrder()) { - InnerNode* n = t->findRelatesNode(path); + Node* n = t->findNodeForInclude(path); if (n) return n; } return 0; } - const Node* resolveFunctionTarget(const QString& target, const Node* relative) { + InnerNode* findRelatesNode(const QStringList& path) { foreach (Tree* t, searchOrder()) { - const Node* n = t->resolveFunctionTarget(target, relative); + InnerNode* n = t->findRelatesNode(path); if (n) return n; - relative = 0; } return 0; } - const Node* resolveTarget(const QString& target, const Node* relative); - const Node* resolveType(const QStringList& path, const Node* relative) - { + const Node* findFunctionNode(const QString& target, + const Node* relative, + Node::Genus genus) { foreach (Tree* t, searchOrder()) { - const Node* n = resolveTypeHelper(path, relative, t); + const Node* n = t->findFunctionNode(target, relative, genus); if (n) return n; relative = 0; } return 0; } + const Node* findNodeForTarget(QStringList& targetPath, + const Node* relative, + Node::Genus genus, + QString& ref); - const Node* findUnambiguousTarget(const QString& target, QString& ref) + const Node* findTypeNode(const QStringList& path, const Node* relative) { foreach (Tree* t, searchOrder()) { - const Node* n = t->findUnambiguousTarget(target, ref); + int flags = SearchBaseClasses | SearchEnumValues | NonFunction; + const Node* n = t->findNode(path, relative, flags, Node::DontCare); if (n) return n; + relative = 0; } return 0; } @@ -189,7 +197,6 @@ class QDocForest private: void newPrimaryTree(const QString& module); NamespaceNode* newIndexTree(const QString& module); - const Node* resolveTypeHelper(const QStringList& path, const Node* relative, Tree* t); private: QDocDatabase* qdb_; @@ -281,8 +288,12 @@ class QDocDatabase void resolveTargets() { primaryTree()->resolveTargets(primaryTreeRoot()); } - void insertTarget(const QString& name, TargetRec::Type type, Node* node, int priority) { - primaryTree()->insertTarget(name, type, node, priority); + void insertTarget(const QString& name, + 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 '[' ']'. ********************************************************************/ - Node* findNode(const Atom* atom); - const Node* findNode(const Atom* atom, const Node* relative, QString& ref); - const DocNode* findDocNodeByTitle(const Atom* atom); + const Node* findNodeForAtom(const Atom* atom, const Node* relative, QString& ref); /*******************************************************************/ /******************************************************************* The functions declared below are called for all trees. ********************************************************************/ 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); } - const Node* resolveTarget(const QString& target, const Node* relative) { - return forest_.resolveTarget(target, relative); + const Node* findFunctionNode(const QString& target, const Node* relative, Node::Genus genus) { + return forest_.findFunctionNode(target, relative, genus); } - const Node* resolveFunctionTarget(const QString& target, const Node* relative) { - return forest_.resolveFunctionTarget(target, relative); - } - const Node* resolveType(const QString& type, const Node* relative); + const Node* findTypeNode(const QString& type, const Node* relative); const Node* findNodeForTarget(const QString& target, const Node* relative); const DocNode* findDocNodeByTitle(const QString& 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) { return forest_.findNodeByNameAndType(path, type); } - /*******************************************************************/ - QString findTarget(const QString& target, const Node* node) { - return node->root()->tree()->findTarget(target, node); + private: + 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, const QString& funcName, PropertyNode::FunctionRole funcRole) { @@ -371,8 +381,11 @@ class QDocDatabase friend class QDocIndexFiles; friend class QDocTagFiles; - const Node* findNode(const QStringList& path, const Node* relative, int findFlags) { - return forest_.findNode(path, relative, findFlags); + const Node* findNode(const QStringList& path, + const Node* relative, + int findFlags, + Node::Genus genus) { + return forest_.findNode(path, relative, findFlags, genus); } void processForest(void (QDocDatabase::*) (InnerNode*)); static void initializeDB(); diff --git a/src/tools/qdoc/qdocindexfiles.cpp b/src/tools/qdoc/qdocindexfiles.cpp index 4531ce8eeaf..ba09bfea3b5 100644 --- a/src/tools/qdoc/qdocindexfiles.cpp +++ b/src/tools/qdoc/qdocindexfiles.cpp @@ -471,15 +471,18 @@ void QDocIndexFiles::readIndexSection(const QDomElement& element, location = Location(parent->name().toLower() + ".html"); } 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; } 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; } 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; } else @@ -1202,18 +1205,26 @@ bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter& writer, external = true; } foreach (const Atom* target, node->doc().targets()) { - QString targetName = target->string(); - if (!external) - targetName = Doc::canonicalTitle(targetName); + QString title = target->string(); + QString name = Doc::canonicalTitle(title); 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 } } if (node->doc().hasKeywords()) { foreach (const Atom* keyword, node->doc().keywords()) { + QString title = keyword->string(); + QString name = Doc::canonicalTitle(title); writer.writeStartElement("keyword"); - writer.writeAttribute("name", Doc::canonicalTitle(keyword->string())); + writer.writeAttribute("name", name); + if (name != title) + writer.writeAttribute("title", title); writer.writeEndElement(); // keyword } } diff --git a/src/tools/qdoc/qmlvisitor.cpp b/src/tools/qdoc/qmlvisitor.cpp index fbe4940c19f..026f3bd0a2a 100644 --- a/src/tools/qdoc/qmlvisitor.cpp +++ b/src/tools/qdoc/qmlvisitor.cpp @@ -249,7 +249,7 @@ bool QmlDocVisitor::applyDocumentation(QQmlJS::AST::SourceLocation location, Nod nodes.append(node); if (topicsUsed.size() > 0) { 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_; break; } diff --git a/src/tools/qdoc/tree.cpp b/src/tools/qdoc/tree.cpp index e689227bf18..d43f82949a6 100644 --- a/src/tools/qdoc/tree.cpp +++ b/src/tools/qdoc/tree.cpp @@ -86,21 +86,48 @@ Tree::Tree(const QString& module, QDocDatabase* qdb) destructor of each child node is called, and these destructors are recursive. Thus the entire tree is 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() { - // nothing + TargetMap::iterator i = nodesByTargetRef_.begin(); + while (i != nodesByTargetRef_.end()) { + delete i.value(); + ++i; + } + nodesByTargetRef_.clear(); + nodesByTargetTitle_.clear(); } /* 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 \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 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) 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 like the \a clone, it returns a pointer to the found node. - There should be a way to avoid creating the clone in the - first place. Investigate when time allows. + Apparently the search order is important here. Don't change + it unless you know what you are doing, or you will introduce + qdoc warnings. */ FunctionNode* Tree::findFunctionNode(const QStringList& parentPath, const FunctionNode* clone) { @@ -134,7 +162,7 @@ FunctionNode* Tree::findFunctionNode(const QStringList& parentPath, const Functi if (parent == 0) parent = findClassNode(parentPath, 0); if (parent == 0) - parent = findNode(parentPath); + parent = findNode(parentPath, 0, 0, Node::DontCare); if (parent == 0 || !parent->isInnerNode()) return 0; return ((InnerNode*)parent)->findFunctionNode(clone); @@ -176,7 +204,7 @@ QmlClassNode* Tree::findQmlTypeNode(const QStringList& path) */ 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->subType() == Node::Collision) { NameCollisionNode* ncn = static_cast<NameCollisionNode*>(n); @@ -196,7 +224,7 @@ NameCollisionNode* Tree::checkForCollision(const QString& name) */ 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->subType() == Node::Collision) { 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 Node* relative, - int findFlags) const + int findFlags, + Node::Genus genus) const { - if (!relative) - relative = root(); - - if (path.size() == 3 && !path[0].isEmpty()) { + if (path.size() == 3 && !path[0].isEmpty() && (genus != Node::CPP)) { QmlClassNode* qcn = lookupQmlType(QString(path[0] + "::" + path[1])); if (!qcn) { QStringList p(path[1]); @@ -240,6 +266,13 @@ const FunctionNode* Tree::findFunctionNode(const QStringList& path, 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 { const Node* node = relative; int i; @@ -252,7 +285,7 @@ const FunctionNode* Tree::findFunctionNode(const QStringList& path, if (i == path.size() - 1) next = ((InnerNode*) node)->findFunctionNode(path.at(i)); else - next = ((InnerNode*) node)->findChildNode(path.at(i)); + next = ((InnerNode*) node)->findChildNode(path.at(i), genus); if (!next && node->type() == Node::Class && (findFlags & SearchBaseClasses)) { NodeList baseClasses = allBaseClasses(static_cast<const ClassNode*>(node)); @@ -260,7 +293,7 @@ const FunctionNode* Tree::findFunctionNode(const QStringList& path, if (i == path.size() - 1) next = static_cast<const InnerNode*>(baseClass)->findFunctionNode(path.at(i)); else - next = static_cast<const InnerNode*>(baseClass)->findChildNode(path.at(i)); + next = static_cast<const InnerNode*>(baseClass)->findChildNode(path.at(i), genus); if (next) break; @@ -670,44 +703,197 @@ Node* Tree::findNodeRecursive(const QStringList& path, } /*! - Searches the tree for a node that matches the \a path. The - search begins at \a start but can move up the parent chain - recursively if no match is found. + 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. - This findNode() callse the other findNode(), which is not - called anywhere else. + 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::findNode(const QStringList& path, const Node* start, int findFlags) const +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(); /* - 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. + 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. */ - const Node* n = findNode(path, current, findFlags, false); - if (n) - return n; - return findNode(path, current, findFlags, true); + 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; } /*! - 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 + Searches the tree for a node that matches the \a path. The + search begins at \a start but can move up the parent chain + recursively if no match is found. + + This findNode() callse the other findNode(), which is not + called anywhere else. + */ +const Node* Tree::findNode(const QStringList& path, + const Node* start, + int findFlags, + Node::Genus genus) const { const Node* current = start; + if (!current) + current = root(); + do { const Node* node = current; 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 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 anser is yes, the reference identifies a QML - class node. + If the answer is yes, the reference identifies a QML + 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])); if (qcn) { node = qcn; @@ -735,14 +921,14 @@ const Node* Tree::findNode(const QStringList& path, const Node* start, int findF if (node == 0 || !node->isInnerNode()) break; - const Node* next = static_cast<const InnerNode*>(node)->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) { 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)); 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) next = static_cast<const InnerNode*>(baseClass)->findEnumNodeForValue(path.at(i)); if (next) { @@ -752,13 +938,8 @@ const Node* Tree::findNode(const QStringList& path, const Node* start, int findF } node = next; } - if (node && i == path.size() - && (!(findFlags & NonFunction) || node->type() != Node::Function - || ((FunctionNode*)node)->metaness() == FunctionNode::MacroWithoutParams)) { - if (node->isCollisionNode()) - node = node->applyModuleName(start); - return node; - } + if (node && i == path.size()) + return node; current = current->parent(); } 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 empty string. */ -QString Tree::findTarget(const QString& target, const Node* node) const +QString Tree::getRef(const QString& target, const Node* node) const { + TargetMap::const_iterator i = nodesByTargetTitle_.constFind(target); + if (i != nodesByTargetTitle_.constEnd()) { + do { + if (i.value()->node_ == node) + return i.value()->ref_; + ++i; + } while (i != nodesByTargetTitle_.constEnd() && i.key() == target); + } QString key = Doc::canonicalTitle(target); - TargetMap::const_iterator i = nodesByTarget_.constFind(key); - if (i != nodesByTarget_.constEnd()) { + i = nodesByTargetRef_.constFind(key); + if (i != nodesByTargetRef_.constEnd()) { do { - if (i.value().node_ == node) - return i.value().ref_; + if (i.value()->node_ == node) + return i.value()->ref_; ++i; - } while (i != nodesByTarget_.constEnd() && i.key() == key); + } while (i != nodesByTargetRef_.constEnd() && i.key() == key); } 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 name, which is later used. */ -void Tree::insertTarget(const QString& name, TargetRec::Type type, Node* node, int priority) -{ - TargetRec target; - target.type_ = type; - target.node_ = node; - target.priority_ = priority; - target.ref_ = Doc::canonicalTitle(name); - nodesByTarget_.insert(name, target); -} - -/*! - 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) +void Tree::insertTarget(const QString& name, + const QString& title, + TargetRec::Type type, + Node* node, + int priority) { - QStringList path = target.split("::"); - int flags = SearchBaseClasses | SearchEnumValues | NonFunction; - if (start && start->tree() != this) - start = 0; - return findNode(path, start, flags); + TargetRec* target = new TargetRec(name, title, type, node, priority); + nodesByTargetRef_.insert(name, target); + nodesByTargetTitle_.insert(title, target); } /*! @@ -826,8 +999,10 @@ void Tree::resolveTargets(InnerNode* root) foreach (Node* child, root->childNodes()) { if (child->type() == Node::Document) { DocNode* node = static_cast<DocNode*>(child); - if (!node->title().isEmpty()) { - QString key = Doc::canonicalTitle(node->title()); + QString key = node->title(); + if (!key.isEmpty()) { + if (key.contains(QChar(' '))) + key = Doc::canonicalTitle(key); QList<DocNode*> nodes = docNodesByTitle_.values(key); bool alreadyThere = false; if (!nodes.empty()) { @@ -840,9 +1015,8 @@ void Tree::resolveTargets(InnerNode* root) } } } - if (!alreadyThere) { + if (!alreadyThere) docNodesByTitle_.insert(key, node); - } } if (node->subType() == Node::Collision) { resolveTargets(node); @@ -851,41 +1025,41 @@ void Tree::resolveTargets(InnerNode* root) if (child->doc().hasTableOfContents()) { const QList<Atom*>& toc = child->doc().tableOfContents(); - TargetRec target; - target.node_ = child; - target.priority_ = 3; - for (int i = 0; i < toc.size(); ++i) { - target.ref_ = refForAtom(toc.at(i)); + QString ref = refForAtom(toc.at(i)); QString title = Text::sectionHeading(toc.at(i)).toString(); - if (!title.isEmpty()) { + if (!ref.isEmpty() && !title.isEmpty()) { 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()) { const QList<Atom*>& keywords = child->doc().keywords(); - TargetRec target; - target.node_ = child; - target.priority_ = 1; - for (int i = 0; i < keywords.size(); ++i) { - target.ref_ = refForAtom(keywords.at(i)); - QString key = Doc::canonicalTitle(keywords.at(i)->string()); - nodesByTarget_.insert(key, target); + QString ref = refForAtom(keywords.at(i)); + QString title = keywords.at(i)->string(); + 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()) { - const QList<Atom*>& toc = child->doc().targets(); - TargetRec target; - target.node_ = child; - target.priority_ = 2; - - for (int i = 0; i < toc.size(); ++i) { - target.ref_ = refForAtom(toc.at(i)); - QString key = Doc::canonicalTitle(toc.at(i)->string()); - nodesByTarget_.insert(key, target); + const QList<Atom*>& targets = child->doc().targets(); + for (int i = 0; i < targets.size(); ++i) { + QString ref = refForAtom(targets.at(i)); + QString title = targets.at(i)->string(); + if (!ref.isEmpty() && !title.isEmpty()) { + QString key = Doc::canonicalTitle(title); + TargetRec* target = new TargetRec(ref, title, TargetRec::Target, child, 2); + nodesByTargetRef_.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. */ const Node* -Tree::findUnambiguousTarget(const QString& target, QString& ref) +Tree::findUnambiguousTarget(const QString& target, QString& ref) const { - TargetRec bestTarget; int numBestTargets = 0; - QList<TargetRec> bestTargetList; + TargetRec* bestTarget = 0; + QList<TargetRec*> bestTargetList; - QString key = Doc::canonicalTitle(target); - TargetMap::iterator i = nodesByTarget_.find(key); - while (i != nodesByTarget_.end()) { + QString key = target; + TargetMap::const_iterator i = nodesByTargetTitle_.find(key); + while (i != nodesByTargetTitle_.constEnd()) { if (i.key() != key) break; - const TargetRec& candidate = i.value(); - if (candidate.priority_ < bestTarget.priority_) { + 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_) { + } else if (candidate->priority_ == bestTarget->priority_) { bestTargetList.append(candidate); ++numBestTargets; } ++i; } - if (numBestTargets > 0) { - if (numBestTargets == 1) { - ref = bestTarget.ref_; - return bestTarget.node_; - } - else if (bestTargetList.size() > 1) { -#if 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_; + if (bestTarget) { + ref = bestTarget->ref_; + return bestTarget->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(); return 0; } @@ -945,8 +1131,11 @@ Tree::findUnambiguousTarget(const QString& target, QString& ref) */ const DocNode* Tree::findDocNodeByTitle(const QString& title) const { - QString key = Doc::canonicalTitle(title); - DocNodeMultiMap::const_iterator i = docNodesByTitle_.constFind(key); + DocNodeMultiMap::const_iterator i; + if (title.contains(QChar(' '))) + i = docNodesByTitle_.constFind(Doc::canonicalTitle(title)); + else + i = docNodesByTitle_.constFind(title); if (i != docNodesByTitle_.constEnd()) { /* 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 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; t.chop(2); 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) return fn; return 0; diff --git a/src/tools/qdoc/tree.h b/src/tools/qdoc/tree.h index a9537519684..c9c695d119d 100644 --- a/src/tools/qdoc/tree.h +++ b/src/tools/qdoc/tree.h @@ -58,15 +58,24 @@ struct TargetRec { public: 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(); } + Node* node_; QString ref_; + QString title_; int priority_; Type type_; }; -typedef QMultiMap<QString, TargetRec> TargetMap; +typedef QMultiMap<QString, TargetRec*> TargetMap; typedef QMultiMap<QString, DocNode*> DocNodeMultiMap; typedef QMap<QString, QmlClassNode*> QmlTypeMap; typedef QMultiMap<QString, const ExampleNode*> ExampleNodeMap; @@ -83,10 +92,11 @@ class Tree Tree(const QString& module, QDocDatabase* qdb); ~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; 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, int pathIndex, @@ -97,14 +107,24 @@ class Tree Node* start, const NodeTypeList& types) const; - const Node* findNode(const QStringList &path, - const Node* relative = 0, - int findFlags = 0) const; + const Node* findNodeForTarget(const QStringList& path, + const QString& target, + 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* start, - int findFlags, - bool qml) const; + const Node* findNode(const QStringList &path, + const Node* relative, // = 0, + int findFlags, // = 0, + Node::Genus genus) const; // = Node::DontCare) const; QmlClassNode* findQmlTypeNode(const QStringList& path); @@ -112,11 +132,14 @@ class Tree InnerNode* findRelatesNode(const QStringList& path); NameCollisionNode* checkForCollision(const QString& name); NameCollisionNode* findCollisionNode(const QString& name) const; - QString findTarget(const QString& target, const Node* node) const; - void insertTarget(const QString& name, TargetRec::Type type, Node* node, int priority); - const Node* resolveTarget(const QString& target, const Node* start); + QString getRef(const QString& target, const Node* node) const; + void insertTarget(const QString& name, + const QString& title, + TargetRec::Type type, + Node* node, + int priority); 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; void addPropertyFunction(PropertyNode *property, @@ -131,7 +154,8 @@ class Tree const FunctionNode *findFunctionNode(const QStringList &path, const Node *relative = 0, - int findFlags = 0) const; + int findFlags = 0, + Node::Genus genus = Node::DontCare) const; const NamespaceNode *root() const { return &root_; } FunctionNode *findVirtualFunctionInBaseClasses(ClassNode *classe, @@ -182,7 +206,8 @@ private: NamespaceNode root_; PropertyMap unresolvedPropertyMap; DocNodeMultiMap docNodesByTitle_; - TargetMap nodesByTarget_; + TargetMap nodesByTargetRef_; + TargetMap nodesByTargetTitle_; CNMap groups_; CNMap modules_; CNMap qmlModules_; |
