1 /****************************************************************************
2 **
3 ** Copyright (C) 2019 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the tools applications of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21 ** included in the packaging of this file. Please review the following
22 ** information to ensure the GNU General Public License requirements will
23 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24 **
25 ** $QT_END_LICENSE$
26 **
27 ****************************************************************************/
28 
29 #include "tree.h"
30 
31 #include "doc.h"
32 #include "htmlgenerator.h"
33 #include "location.h"
34 #include "node.h"
35 #include "qdocdatabase.h"
36 #include "text.h"
37 
38 #include <QtCore/qdebug.h>
39 
40 #include <limits.h>
41 
42 QT_BEGIN_NAMESPACE
43 
44 /*!
45   \class Tree
46 
47   This class constructs and maintains a tree of instances of
48   the subclasses of Node.
49 
50   This class is now private. Only class QDocDatabase has access.
51   Please don't change this. If you must access class Tree, do it
52   though the pointer to the singleton QDocDatabase.
53 
54   Tree is being converted to a forest. A static member provides a
55   map of Tree *values with the module names as the keys. There is
56   one Tree in the map for each index file read, and there is one
57   tree that is not in the map for the module whose documentation
58   is being generated.
59  */
60 
61 /*!
62   Constructs a Tree. \a qdb is the pointer to the singleton
63   qdoc database that is constructing the tree. This might not
64   be necessary, and it might be removed later.
65 
66   \a camelCaseModuleName is the project name for this tree
67   as it appears in the qdocconf file.
68  */
Tree(const QString & camelCaseModuleName,QDocDatabase * qdb)69 Tree::Tree(const QString &camelCaseModuleName, QDocDatabase *qdb)
70     : treeHasBeenAnalyzed_(false),
71       docsHaveBeenGenerated_(false),
72       linkCount_(0),
73       camelCaseModuleName_(camelCaseModuleName),
74       physicalModuleName_(camelCaseModuleName.toLower()),
75       qdb_(qdb),
76       root_(nullptr, QString()),
77       targetListMap_(nullptr)
78 {
79     root_.setPhysicalModuleName(physicalModuleName_);
80     root_.setTree(this);
81     const auto &config = Config::instance();
82     if (config.getBool(CONFIG_WRITEQAPAGES))
83         targetListMap_ = new TargetListMap;
84 }
85 
86 /*!
87   Destroys the Tree. The root node is a data member
88   of this object, so its destructor is called. The
89   destructor of each child node is called, and these
90   destructors are recursive. Thus the entire tree is
91   destroyed.
92 
93   There are two maps of targets, keywords, and contents.
94   One map is indexed by ref, the other by title. The ref
95   is just the canonical form of the title. Both maps
96   use the same set of TargetRec objects as the values,
97   so the destructor only deletes the values from one of
98   the maps. Then it clears both maps.
99  */
~Tree()100 Tree::~Tree()
101 {
102     for (auto i = nodesByTargetRef_.begin(); i != nodesByTargetRef_.end(); ++i) {
103         delete i.value();
104     }
105     nodesByTargetRef_.clear();
106     nodesByTargetTitle_.clear();
107     const auto &config = Config::instance();
108     if (config.getBool(CONFIG_WRITEQAPAGES) && targetListMap_) {
109         for (auto target = targetListMap_->begin(); target != targetListMap_->end(); ++target) {
110             TargetList *tlist = target.value();
111             if (tlist) {
112                 for (auto *location : qAsConst(*tlist))
113                     delete location;
114             }
115             delete tlist;
116         }
117     }
118 }
119 
120 /* API members */
121 
122 /*!
123   Calls findClassNode() first with \a path and \a start. If
124   it finds a node, the node is returned. If not, it calls
125   findNamespaceNode() with the same parameters. The result
126   is returned.
127  */
findNodeForInclude(const QStringList & path) const128 Node *Tree::findNodeForInclude(const QStringList &path) const
129 {
130     Node *n = findClassNode(path);
131     if (n == nullptr)
132         n = findNamespaceNode(path);
133     return n;
134 }
135 
136 /*!
137   This function searches this tree for an Aggregate node with
138   the specified \a name. It returns the pointer to that node
139   or nullptr.
140 
141   We might need to split the name on '::' but we assume the
142   name is a single word at the moment.
143  */
findAggregate(const QString & name)144 Aggregate *Tree::findAggregate(const QString &name)
145 {
146     QStringList path = name.split(QLatin1String("::"));
147     return static_cast<Aggregate *>(findNodeRecursive(path, 0, const_cast<NamespaceNode *>(root()),
148                                                       &Node::isFirstClassAggregate));
149 }
150 
151 /*!
152   Find the C++ class node named \a path. Begin the search at the
153   \a start node. If the \a start node is 0, begin the search
154   at the root of the tree. Only a C++ class node named \a path is
155   acceptible. If one is not found, 0 is returned.
156  */
findClassNode(const QStringList & path,const Node * start) const157 ClassNode *Tree::findClassNode(const QStringList &path, const Node *start) const
158 {
159     if (start == nullptr)
160         start = const_cast<NamespaceNode *>(root());
161     return static_cast<ClassNode *>(findNodeRecursive(path, 0, start, &Node::isClassNode));
162 }
163 
164 /*!
165   Find the Namespace node named \a path. Begin the search at
166   the root of the tree. Only a Namespace node named \a path
167   is acceptible. If one is not found, 0 is returned.
168  */
findNamespaceNode(const QStringList & path) const169 NamespaceNode *Tree::findNamespaceNode(const QStringList &path) const
170 {
171     Node *start = const_cast<NamespaceNode *>(root());
172     return static_cast<NamespaceNode *>(findNodeRecursive(path, 0, start, &Node::isNamespace));
173 }
174 
175 /*!
176   Find the Qml type node named \a path. Begin the search at the
177   \a start node. If the \a start node is 0, begin the search
178   at the root of the tree. Only a Qml type node named <\a path is
179   acceptible. If one is not found, 0 is returned.
180  */
findQmlTypeNode(const QStringList & path)181 QmlTypeNode *Tree::findQmlTypeNode(const QStringList &path)
182 {
183     /*
184       If the path contains one or two double colons ("::"),
185       check first to see if the first two path strings refer
186       to a QML element. If they do, path[0] will be the QML
187       module identifier, and path[1] will be the QML type.
188       If the anser is yes, the reference identifies a QML
189       class node.
190     */
191     if (path.size() >= 2 && !path[0].isEmpty()) {
192         QmlTypeNode *qcn = qdb_->findQmlType(path[0], path[1]);
193         if (qcn != nullptr)
194             return qcn;
195     }
196     return static_cast<QmlTypeNode *>(findNodeRecursive(path, 0, root(), &Node::isQmlType));
197 }
198 
199 /*!
200   This function searches for the node specified by \a path.
201   The matching node can be one of several different types
202   including a C++ class, a C++ namespace, or a C++ header
203   file.
204 
205   I'm not sure if it can be a QML type, but if that is a
206   possibility, the code can easily accommodate it.
207 
208   If a matching node is found, a pointer to it is returned.
209   Otherwise 0 is returned.
210  */
findRelatesNode(const QStringList & path)211 Aggregate *Tree::findRelatesNode(const QStringList &path)
212 {
213     Node *n = findNodeRecursive(path, 0, root(), &Node::isRelatableType);
214     return (((n != nullptr) && n->isAggregate()) ? static_cast<Aggregate *>(n) : nullptr);
215 }
216 
217 /*!
218   Inserts function name \a funcName and function role \a funcRole into
219   the property function map for the specified \a property.
220  */
addPropertyFunction(PropertyNode * property,const QString & funcName,PropertyNode::FunctionRole funcRole)221 void Tree::addPropertyFunction(PropertyNode *property, const QString &funcName,
222                                PropertyNode::FunctionRole funcRole)
223 {
224     unresolvedPropertyMap[property].insert(funcRole, funcName);
225 }
226 
227 /*!
228   This function resolves C++ inheritance and reimplementation
229   settings for each C++ class node found in the tree beginning
230   at \a n. It also calls itself recursively for each C++ class
231   node or namespace node it encounters.
232 
233   This function does not resolve QML inheritance.
234  */
resolveBaseClasses(Aggregate * n)235 void Tree::resolveBaseClasses(Aggregate *n)
236 {
237     for (auto it = n->constBegin(); it != n->constEnd(); ++it) {
238         if ((*it)->isClassNode()) {
239             ClassNode *cn = static_cast<ClassNode *>(*it);
240             QVector<RelatedClass> &bases = cn->baseClasses();
241             for (auto &base : bases) {
242                 if (base.node_ == nullptr) {
243                     Node *n = qdb_->findClassNode(base.path_);
244                     /*
245                       If the node for the base class was not found,
246                       the reason might be that the subclass is in a
247                       namespace and the base class is in the same
248                       namespace, but the base class name was not
249                       qualified with the namespace name. That is the
250                       case most of the time. Then restart the search
251                       at the parent of the subclass node (the namespace
252                       node) using the unqualified base class name.
253                     */
254                     if (n == nullptr) {
255                         Aggregate *parent = cn->parent();
256                         if (parent != nullptr)
257                             // Exclude the root namespace
258                             if (parent->isNamespace() && !parent->name().isEmpty())
259                                 n = findClassNode(base.path_, parent);
260                     }
261                     if (n != nullptr) {
262                         ClassNode *bcn = static_cast<ClassNode *>(n);
263                         base.node_ = bcn;
264                         bcn->addDerivedClass(base.access_, cn);
265                     }
266                 }
267             }
268             resolveBaseClasses(cn);
269         } else if ((*it)->isNamespace()) {
270             resolveBaseClasses(static_cast<NamespaceNode *>(*it));
271         }
272     }
273 }
274 
275 /*!
276  */
resolvePropertyOverriddenFromPtrs(Aggregate * n)277 void Tree::resolvePropertyOverriddenFromPtrs(Aggregate *n)
278 {
279     for (auto node = n->constBegin(); node != n->constEnd(); ++node) {
280         if ((*node)->isClassNode()) {
281             ClassNode *cn = static_cast<ClassNode *>(*node);
282             for (auto property = cn->constBegin(); property != cn->constEnd(); ++property) {
283                 if ((*property)->isProperty())
284                     cn->resolvePropertyOverriddenFromPtrs(static_cast<PropertyNode *>(*property));
285             }
286             resolvePropertyOverriddenFromPtrs(cn);
287         } else if ((*node)->isNamespace()) {
288             resolvePropertyOverriddenFromPtrs(static_cast<NamespaceNode *>(*node));
289         }
290     }
291 }
292 
293 /*!
294  */
resolveProperties()295 void Tree::resolveProperties()
296 {
297     for (auto propEntry = unresolvedPropertyMap.constBegin();
298          propEntry != unresolvedPropertyMap.constEnd(); ++propEntry) {
299         PropertyNode *property = propEntry.key();
300         Aggregate *parent = property->parent();
301         QString getterName = (*propEntry)[PropertyNode::Getter];
302         QString setterName = (*propEntry)[PropertyNode::Setter];
303         QString resetterName = (*propEntry)[PropertyNode::Resetter];
304         QString notifierName = (*propEntry)[PropertyNode::Notifier];
305 
306         for (auto it = parent->constBegin(); it != parent->constEnd(); ++it) {
307             if ((*it)->isFunction()) {
308                 FunctionNode *function = static_cast<FunctionNode *>(*it);
309                 if (function->access() == property->access()
310                     && (function->status() == property->status() || function->doc().isEmpty())) {
311                     if (function->name() == getterName) {
312                         property->addFunction(function, PropertyNode::Getter);
313                     } else if (function->name() == setterName) {
314                         property->addFunction(function, PropertyNode::Setter);
315                     } else if (function->name() == resetterName) {
316                         property->addFunction(function, PropertyNode::Resetter);
317                     } else if (function->name() == notifierName) {
318                         property->addSignal(function, PropertyNode::Notifier);
319                     }
320                 }
321             }
322         }
323     }
324 
325     for (auto propEntry = unresolvedPropertyMap.constBegin();
326          propEntry != unresolvedPropertyMap.constEnd(); ++propEntry) {
327         PropertyNode *property = propEntry.key();
328         // redo it to set the property functions
329         if (property->overriddenFrom())
330             property->setOverriddenFrom(property->overriddenFrom());
331     }
332 
333     unresolvedPropertyMap.clear();
334 }
335 
336 /*!
337   For each QML class node that points to a C++ class node,
338   follow its C++ class node pointer and set the C++ class
339   node's QML class node pointer back to the QML class node.
340  */
resolveCppToQmlLinks()341 void Tree::resolveCppToQmlLinks()
342 {
343 
344     const NodeList &children = root_.childNodes();
345     for (auto *child : children) {
346         if (child->isQmlType() || child->isJsType()) {
347             QmlTypeNode *qcn = static_cast<QmlTypeNode *>(child);
348             ClassNode *cn = const_cast<ClassNode *>(qcn->classNode());
349             if (cn)
350                 cn->setQmlElement(qcn);
351         }
352     }
353 }
354 
355 /*!
356   For each C++ class node, resolve any \c using clauses
357   that appeared in the class declaration.
358 
359   For type aliases, resolve the aliased node.
360  */
resolveUsingClauses(Aggregate * parent)361 void Tree::resolveUsingClauses(Aggregate *parent)
362 {
363     if (!parent)
364         parent = &root_;
365     for (auto *child : parent->childNodes()) {
366         if (child->isClassNode()) {
367             ClassNode *cn = static_cast<ClassNode *>(child);
368             QVector<UsingClause> &usingClauses = cn->usingClauses();
369             for (auto &usingClause : usingClauses) {
370                 if (usingClause.node() == nullptr) {
371                     const Node *n = qdb_->findFunctionNode(usingClause.signature(), cn, Node::CPP);
372                     if (n != nullptr)
373                         usingClause.setNode(n);
374                 }
375             }
376         } else if (child->isTypeAlias()) {
377             TypeAliasNode *ta = static_cast<TypeAliasNode *>(child);
378             ta->setAliasedNode(qdb_->findNodeForTarget(ta->aliasedType(), child->parent()));
379         }
380 
381     if (child->genus() == Node::CPP && child->isAggregate())
382         resolveUsingClauses(static_cast<Aggregate *>(child));
383     }
384 }
385 
386 /*!
387   Traverse this Tree and for each ClassNode found, remove
388   from its list of base classes any that are marked private
389   or internal. When a class is removed from a base class
390   list, promote its public pase classes to be base classes
391   of the class where the base class was removed. This is
392   done for documentation purposes. The function is recursive
393   on namespace nodes.
394  */
removePrivateAndInternalBases(NamespaceNode * rootNode)395 void Tree::removePrivateAndInternalBases(NamespaceNode *rootNode)
396 {
397     if (rootNode == nullptr)
398         rootNode = root();
399 
400     for (auto node = rootNode->constBegin(); node != rootNode->constEnd(); ++node) {
401         if ((*node)->isClassNode())
402             static_cast<ClassNode *>(*node)->removePrivateAndInternalBases();
403         else if ((*node)->isNamespace())
404             removePrivateAndInternalBases(static_cast<NamespaceNode *>(*node));
405     }
406 }
407 
408 /*!
409  */
allBaseClasses(const ClassNode * classNode) const410 ClassList Tree::allBaseClasses(const ClassNode *classNode) const
411 {
412     ClassList result;
413     const auto &baseClasses = classNode->baseClasses();
414     for (const auto &relatedClass : baseClasses) {
415         if (relatedClass.node_ != nullptr) {
416             result += relatedClass.node_;
417             result += allBaseClasses(relatedClass.node_);
418         }
419     }
420     return result;
421 }
422 
423 /*!
424   Find the node with the specified \a path name that is of
425   the specified \a type and \a subtype. Begin the search at
426   the \a start node. If the \a start node is 0, begin the
427   search at the tree root. \a subtype is not used unless
428   \a type is \c{Page}.
429  */
findNodeByNameAndType(const QStringList & path,bool (Node::* isMatch)()const) const430 Node *Tree::findNodeByNameAndType(const QStringList &path, bool (Node::*isMatch)() const) const
431 {
432     return findNodeRecursive(path, 0, root(), isMatch);
433 }
434 
435 /*!
436   Recursive search for a node identified by \a path. Each
437   path element is a name. \a pathIndex specifies the index
438   of the name in \a path to try to match. \a start is the
439   node whose children shoulod be searched for one that has
440   that name. Each time a match is found, increment the
441   \a pathIndex and call this function recursively.
442 
443   If the end of the path is reached (i.e. if a matching
444   node is found for each name in the \a path), the \a type
445   must match the type of the last matching node, and if the
446   type is \e{Page}, the \a subtype must match as well.
447 
448   If the algorithm is successful, the pointer to the final
449   node is returned. Otherwise 0 is returned.
450  */
findNodeRecursive(const QStringList & path,int pathIndex,const Node * start,bool (Node::* isMatch)()const) const451 Node *Tree::findNodeRecursive(const QStringList &path, int pathIndex, const Node *start,
452                               bool (Node::*isMatch)() const) const
453 {
454     if (start == nullptr || path.isEmpty())
455         return nullptr;
456     Node *node = const_cast<Node *>(start);
457     if (!node->isAggregate())
458         return ((pathIndex >= path.size()) ? node : nullptr);
459     Aggregate *current = static_cast<Aggregate *>(node);
460     const NodeList &children = current->childNodes();
461     const QString &name = path.at(pathIndex);
462     for (auto *node : children) {
463         if (node == nullptr)
464             continue;
465         if (node->name() == name) {
466             if (pathIndex + 1 >= path.size()) {
467                 if ((node->*(isMatch))())
468                     return node;
469                 continue;
470             } else { // Search the children of n for the next name in the path.
471                 node = findNodeRecursive(path, pathIndex + 1, node, isMatch);
472                 if (node != nullptr)
473                     return node;
474             }
475         }
476     }
477     return nullptr;
478 }
479 
480 /*!
481   Searches the tree for a node that matches the \a path plus
482   the \a target. The search begins at \a start and moves up
483   the parent chain from there, or, if \a start is 0, the search
484   begins at the root.
485 
486   The \a flags can indicate whether to search base classes and/or
487   the enum values in enum types. \a genus can be a further restriction
488   on what kind of node is an acceptible match, i.e. CPP or QML.
489 
490   If a matching node is found, \a ref is an output parameter that
491   is set to the HTML reference to use for the link.
492  */
findNodeForTarget(const QStringList & path,const QString & target,const Node * start,int flags,Node::Genus genus,QString & ref) const493 const Node *Tree::findNodeForTarget(const QStringList &path, const QString &target,
494                                     const Node *start, int flags, Node::Genus genus,
495                                     QString &ref) const
496 {
497     const Node *node = nullptr;
498     if ((genus == Node::DontCare) || (genus == Node::DOC)) {
499         node = findPageNodeByTitle(path.at(0));
500         if (node) {
501             if (!target.isEmpty()) {
502                 ref = getRef(target, node);
503                 if (ref.isEmpty())
504                     node = nullptr;
505             }
506             if (node)
507                 return node;
508         }
509     }
510 
511     node = findUnambiguousTarget(path.join(QLatin1String("::")), genus, ref);
512     if (node) {
513         if (!target.isEmpty()) {
514             ref = getRef(target, node);
515             if (ref.isEmpty())
516                 node = nullptr;
517         }
518         if (node)
519             return node;
520     }
521 
522     const Node *current = start;
523     if (current == nullptr)
524         current = root();
525 
526     /*
527       If the path contains one or two double colons ("::"),
528       check first to see if the first two path strings refer
529       to a QML element. If they do, path[0] will be the QML
530       module identifier, and path[1] will be the QML type.
531       If the answer is yes, the reference identifies a QML
532       type node.
533     */
534     int path_idx = 0;
535     if (((genus == Node::QML) || (genus == Node::DontCare)) && (path.size() >= 2)
536         && !path[0].isEmpty()) {
537         QmlTypeNode *qcn = lookupQmlType(QString(path[0] + "::" + path[1]));
538         if (qcn) {
539             current = qcn;
540             if (path.size() == 2) {
541                 if (!target.isEmpty()) {
542                     ref = getRef(target, current);
543                     if (!ref.isEmpty())
544                         return current;
545                     return nullptr;
546                 } else
547                     return current;
548             }
549             path_idx = 2;
550         }
551     }
552 
553     while (current != nullptr) {
554         if (current->isAggregate()) { // Should this be isPageNode() ???
555             const Node *node =
556                     matchPathAndTarget(path, path_idx, target, current, flags, genus, ref);
557             if (node)
558                 return node;
559         }
560         current = current->parent();
561         path_idx = 0;
562     }
563     return nullptr;
564 }
565 
566 /*!
567   First, the \a path is used to find a node. The \a path
568   matches some part of the node's fully quallified name.
569   If the \a target is not empty, it must match a target
570   in the matching node. If the matching of the \a path
571   and the \a target (if present) is successful, \a ref
572   is set from the \a target, and the pointer to the
573   matching node is returned. \a idx is the index into the
574   \a path where to begin the matching. The function is
575   recursive with idx being incremented for each recursive
576   call.
577 
578   The matching node must be of the correct \a genus, i.e.
579   either QML or C++, but \a genus can be set to \c DontCare.
580   \a flags indicates whether to search base classes and
581   whether to search for an enum value. \a node points to
582   the node where the search should begin, assuming the
583   \a path is a not a fully-qualified name. \a node is
584   most often the root of this Tree.
585  */
matchPathAndTarget(const QStringList & path,int idx,const QString & target,const Node * node,int flags,Node::Genus genus,QString & ref) const586 const Node *Tree::matchPathAndTarget(const QStringList &path, int idx, const QString &target,
587                                      const Node *node, int flags, Node::Genus genus,
588                                      QString &ref) const
589 {
590     /*
591       If the path has been matched, then if there is a target,
592       try to match the target. If there is a target, but you
593       can't match it at the end of the path, give up; return 0.
594      */
595     if (idx == path.size()) {
596         if (!target.isEmpty()) {
597             ref = getRef(target, node);
598             if (ref.isEmpty())
599                 return nullptr;
600         }
601         if (node->isFunction() && node->name() == node->parent()->name())
602             node = node->parent();
603         return node;
604     }
605 
606     QString name = path.at(idx);
607     if (node->isAggregate()) {
608         NodeVector nodes;
609         static_cast<const Aggregate *>(node)->findChildren(name, nodes);
610         for (const auto *node : qAsConst(nodes)) {
611             if (genus != Node::DontCare && node->genus() != genus)
612                 continue;
613             const Node *t = matchPathAndTarget(path, idx + 1, target, node, flags, genus, ref);
614             if (t && !t->isPrivate())
615                 return t;
616         }
617     }
618     if (target.isEmpty()) {
619         if ((idx) == (path.size() - 1) && node->isAggregate() && (flags & SearchEnumValues)) {
620             const Node *t =
621                     static_cast<const Aggregate *>(node)->findEnumNodeForValue(path.at(idx));
622             if (t)
623                 return t;
624         }
625     }
626     if (((genus == Node::CPP) || (genus == Node::DontCare)) && node->isClassNode()
627         && (flags & SearchBaseClasses)) {
628         const ClassList bases = allBaseClasses(static_cast<const ClassNode *>(node));
629         for (const auto *base : bases) {
630             const Node *t = matchPathAndTarget(path, idx, target, base, flags, genus, ref);
631             if (t && !t->isPrivate())
632                 return t;
633             if (target.isEmpty()) {
634                 if ((idx) == (path.size() - 1) && (flags & SearchEnumValues)) {
635                     t = base->findEnumNodeForValue(path.at(idx));
636                     if (t)
637                         return t;
638                 }
639             }
640         }
641     }
642     return nullptr;
643 }
644 
645 /*!
646   Searches the tree for a node that matches the \a path. The
647   search begins at \a start but can move up the parent chain
648   recursively if no match is found. The \a flags are used to
649   restrict the search.
650  */
findNode(const QStringList & path,const Node * start,int flags,Node::Genus genus) const651 const Node *Tree::findNode(const QStringList &path, const Node *start, int flags,
652                            Node::Genus genus) const
653 {
654     const Node *current = start;
655     if (current == nullptr)
656         current = root();
657 
658     do {
659         const Node *node = current;
660         int i;
661         int start_idx = 0;
662 
663         /*
664           If the path contains one or two double colons ("::"),
665           check first to see if the first two path strings refer
666           to a QML element. If they do, path[0] will be the QML
667           module identifier, and path[1] will be the QML type.
668           If the answer is yes, the reference identifies a QML
669           type node.
670         */
671         if (((genus == Node::QML) || (genus == Node::DontCare)) && (path.size() >= 2)
672             && !path[0].isEmpty()) {
673             QmlTypeNode *qcn = lookupQmlType(QString(path[0] + "::" + path[1]));
674             if (qcn != nullptr) {
675                 node = qcn;
676                 if (path.size() == 2)
677                     return node;
678                 start_idx = 2;
679             }
680         }
681 
682         for (i = start_idx; i < path.size(); ++i) {
683             if (node == nullptr || !node->isAggregate())
684                 break;
685 
686             // Clear the TypesOnly flag until the last path segment, as e.g. namespaces are not
687             // types. We also ignore module nodes as they are not aggregates and thus have no
688             // children.
689             int tmpFlags = (i < path.size() - 1) ? (flags & ~TypesOnly) | IgnoreModules : flags;
690 
691             const Node *next = static_cast<const Aggregate *>(node)->findChildNode(path.at(i),
692                                                                                    genus, tmpFlags);
693             if ((next == nullptr) && (flags & SearchEnumValues) && i == path.size() - 1) {
694                 next = static_cast<const Aggregate *>(node)->findEnumNodeForValue(path.at(i));
695             }
696             if ((next == nullptr) && ((genus == Node::CPP) || (genus == Node::DontCare))
697                 && node->isClassNode() && (flags & SearchBaseClasses)) {
698                 const ClassList bases = allBaseClasses(static_cast<const ClassNode *>(node));
699                 for (const auto *base : bases) {
700                     next = base->findChildNode(path.at(i), genus, tmpFlags);
701                     if ((next == nullptr) && (flags & SearchEnumValues) && i == path.size() - 1)
702                         next = base->findEnumNodeForValue(path.at(i));
703                     if (next != nullptr)
704                         break;
705                 }
706             }
707             node = next;
708         }
709         if ((node != nullptr) && i == path.size())
710             return node;
711         current = current->parent();
712     } while (current != nullptr);
713 
714     return nullptr;
715 }
716 
717 /*!
718   This function searches for a node with a canonical title
719   constructed from \a target. If the node it finds is \a node,
720   it returns the ref from that node. Otherwise it returns an
721   empty string.
722  */
getRef(const QString & target,const Node * node) const723 QString Tree::getRef(const QString &target, const Node *node) const
724 {
725     auto it = nodesByTargetTitle_.constFind(target);
726     if (it != nodesByTargetTitle_.constEnd()) {
727         do {
728             if (it.value()->node_ == node)
729                 return it.value()->ref_;
730             ++it;
731         } while (it != nodesByTargetTitle_.constEnd() && it.key() == target);
732     }
733     QString key = Doc::canonicalTitle(target);
734     it = nodesByTargetRef_.constFind(key);
735     if (it != nodesByTargetRef_.constEnd()) {
736         do {
737             if (it.value()->node_ == node)
738                 return it.value()->ref_;
739             ++it;
740         } while (it != nodesByTargetRef_.constEnd() && it.key() == key);
741     }
742     return QString();
743 }
744 
745 /*!
746   Inserts a new target into the target table. \a name is the
747   key. The target record contains the \a type, a pointer to
748   the \a node, the \a priority. and a canonicalized form of
749   the \a name, which is later used.
750  */
insertTarget(const QString & name,const QString & title,TargetRec::TargetType type,Node * node,int priority)751 void Tree::insertTarget(const QString &name, const QString &title, TargetRec::TargetType type,
752                         Node *node, int priority)
753 {
754     TargetRec *target = new TargetRec(name, title, type, node, priority);
755     nodesByTargetRef_.insert(name, target);
756     nodesByTargetTitle_.insert(title, target);
757 }
758 
759 /*!
760  */
resolveTargets(Aggregate * root)761 void Tree::resolveTargets(Aggregate *root)
762 {
763     for (auto *child : root->childNodes()) {
764         if (child->isTextPageNode()) {
765             PageNode *node = static_cast<PageNode *>(child);
766             QString key = node->title();
767             if (!key.isEmpty()) {
768                 if (key.contains(QChar(' ')))
769                     key = Doc::canonicalTitle(key);
770                 QList<PageNode *> nodes = pageNodesByTitle_.values(key);
771                 bool alreadyThere = false;
772                 if (!nodes.empty()) {
773                     for (const auto &node_ : nodes) {
774                         if (node_->isExternalPage()) {
775                             if (node->name() == node_->name()) {
776                                 alreadyThere = true;
777                                 break;
778                             }
779                         }
780                     }
781                 }
782                 if (!alreadyThere)
783                     pageNodesByTitle_.insert(key, node);
784             }
785         }
786 
787         if (child->doc().hasTableOfContents()) {
788             const QVector<Atom *> &toc = child->doc().tableOfContents();
789             for (int i = 0; i < toc.size(); ++i) {
790                 QString ref = refForAtom(toc.at(i));
791                 QString title = Text::sectionHeading(toc.at(i)).toString();
792                 if (!ref.isEmpty() && !title.isEmpty()) {
793                     QString key = Doc::canonicalTitle(title);
794                     TargetRec *target = new TargetRec(ref, title, TargetRec::Contents, child, 3);
795                     nodesByTargetRef_.insert(key, target);
796                     nodesByTargetTitle_.insert(title, target);
797                 }
798             }
799         }
800         if (child->doc().hasKeywords()) {
801             const QVector<Atom *> &keywords = child->doc().keywords();
802             for (int i = 0; i < keywords.size(); ++i) {
803                 QString ref = refForAtom(keywords.at(i));
804                 QString title = keywords.at(i)->string();
805                 if (!ref.isEmpty() && !title.isEmpty()) {
806                     TargetRec *target = new TargetRec(ref, title, TargetRec::Keyword, child, 1);
807                     nodesByTargetRef_.insert(Doc::canonicalTitle(title), target);
808                     nodesByTargetTitle_.insert(title, target);
809                 }
810             }
811         }
812         if (child->doc().hasTargets()) {
813             const QVector<Atom *> &targets = child->doc().targets();
814             for (int i = 0; i < targets.size(); ++i) {
815                 QString ref = refForAtom(targets.at(i));
816                 QString title = targets.at(i)->string();
817                 if (!ref.isEmpty() && !title.isEmpty()) {
818                     QString key = Doc::canonicalTitle(title);
819                     TargetRec *target = new TargetRec(ref, title, TargetRec::Target, child, 2);
820                     nodesByTargetRef_.insert(key, target);
821                     nodesByTargetTitle_.insert(title, target);
822                 }
823             }
824         }
825         if (child->isAggregate())
826             resolveTargets(static_cast<Aggregate *>(child));
827     }
828 }
829 
830 /*!
831   This function searches for a \a target anchor node. If it
832   finds one, it sets \a ref and returns the found node.
833  */
findUnambiguousTarget(const QString & target,Node::Genus genus,QString & ref) const834 const Node *Tree::findUnambiguousTarget(const QString &target, Node::Genus genus,
835                                         QString &ref) const
836 {
837     int numBestTargets = 0;
838     TargetRec *bestTarget = nullptr;
839     QVector<TargetRec *> bestTargetList;
840 
841     QString key = target;
842     for (auto it = nodesByTargetTitle_.find(key); it != nodesByTargetTitle_.constEnd(); ++it) {
843         if (it.key() != key)
844             break;
845         TargetRec *candidate = it.value();
846         if ((genus == Node::DontCare) || (genus == candidate->genus())) {
847             if (!bestTarget || (candidate->priority_ < bestTarget->priority_)) {
848                 bestTarget = candidate;
849                 bestTargetList.clear();
850                 bestTargetList.append(candidate);
851                 numBestTargets = 1;
852             } else if (candidate->priority_ == bestTarget->priority_) {
853                 bestTargetList.append(candidate);
854                 ++numBestTargets;
855             }
856         }
857     }
858     if (bestTarget) {
859         ref = bestTarget->ref_;
860         return bestTarget->node_;
861     }
862 
863     numBestTargets = 0;
864     bestTarget = nullptr;
865     key = Doc::canonicalTitle(target);
866     for (auto it = nodesByTargetRef_.find(key); it != nodesByTargetRef_.constEnd(); ++it) {
867         if (it.key() != key)
868             break;
869         TargetRec *candidate = it.value();
870         if ((genus == Node::DontCare) || (genus == candidate->genus())) {
871             if (!bestTarget || (candidate->priority_ < bestTarget->priority_)) {
872                 bestTarget = candidate;
873                 bestTargetList.clear();
874                 bestTargetList.append(candidate);
875                 numBestTargets = 1;
876             } else if (candidate->priority_ == bestTarget->priority_) {
877                 bestTargetList.append(candidate);
878                 ++numBestTargets;
879             }
880         }
881     }
882     if (bestTarget) {
883         ref = bestTarget->ref_;
884         return bestTarget->node_;
885     }
886 
887     ref.clear();
888     return nullptr;
889 }
890 
891 /*!
892   This function searches for a node with the specified \a title.
893  */
findPageNodeByTitle(const QString & title) const894 const PageNode *Tree::findPageNodeByTitle(const QString &title) const
895 {
896     PageNodeMultiMap::const_iterator it;
897     if (title.contains(QChar(' ')))
898         it = pageNodesByTitle_.constFind(Doc::canonicalTitle(title));
899     else
900         it = pageNodesByTitle_.constFind(title);
901     if (it != pageNodesByTitle_.constEnd()) {
902         /*
903           Reporting all these duplicate section titles is probably
904           overkill. We should report the duplicate file and let
905           that suffice.
906         */
907         PageNodeMultiMap::const_iterator j = it;
908         ++j;
909         if (j != pageNodesByTitle_.constEnd() && j.key() == it.key()) {
910             while (j != pageNodesByTitle_.constEnd()) {
911                 if (j.key() == it.key() && j.value()->url().isEmpty()) {
912                     break; // Just report one duplicate for now.
913                 }
914                 ++j;
915             }
916             if (j != pageNodesByTitle_.cend()) {
917                 it.value()->location().warning("This page title exists in more than one file: "
918                                                + title);
919                 j.value()->location().warning("[It also exists here]");
920             }
921         }
922         return it.value();
923     }
924     return nullptr;
925 }
926 
927 /*!
928   Returns a canonical title for the \a atom, if the \a atom
929   is a SectionLeft or a Target.
930  */
refForAtom(const Atom * atom)931 QString Tree::refForAtom(const Atom *atom)
932 {
933     if (atom) {
934         if (atom->type() == Atom::SectionLeft)
935             return Doc::canonicalTitle(Text::sectionHeading(atom).toString());
936         if ((atom->type() == Atom::Target) || (atom->type() == Atom::Keyword))
937             return Doc::canonicalTitle(atom->string());
938     }
939     return QString();
940 }
941 
942 /*!
943   \fn const CNMap &Tree::groups() const
944   Returns a const reference to the collection of all
945   group nodes.
946 */
947 
948 /*!
949   \fn const ModuleMap &Tree::modules() const
950   Returns a const reference to the collection of all
951   module nodes.
952 */
953 
954 /*!
955   \fn const QmlModuleMap &Tree::qmlModules() const
956   Returns a const reference to the collection of all
957   QML module nodes.
958 */
959 
960 /*!
961   Returns a pointer to the collection map specified by \a type.
962   Returns null if \a type is not specified.
963  */
getCollectionMap(Node::NodeType type)964 CNMap *Tree::getCollectionMap(Node::NodeType type)
965 {
966     switch (type) {
967     case Node::Group:
968         return &groups_;
969     case Node::Module:
970         return &modules_;
971     case Node::QmlModule:
972         return &qmlModules_;
973     case Node::JsModule:
974         return &jsModules_;
975     default:
976         break;
977     }
978     return nullptr;
979 }
980 
981 /*!
982   Searches this tree for a collection named \a name with the
983   specified \a type. If the collection is found, a pointer
984   to it is returned. If a collection is not found, null is
985   returned.
986  */
getCollection(const QString & name,Node::NodeType type)987 CollectionNode *Tree::getCollection(const QString &name, Node::NodeType type)
988 {
989     CNMap *map = getCollectionMap(type);
990     if (map) {
991         auto it = map->constFind(name);
992         if (it != map->cend())
993             return it.value();
994     }
995     return nullptr;
996 }
997 
998 /*!
999   Find the group, module, QML module, or JavaScript module
1000   named \a name and return a pointer to that collection node.
1001   \a type specifies which kind of collection node you want.
1002   If a collection node with the specified \a name and \a type
1003   is not found, a new one is created, and the pointer to the
1004   new one is returned.
1005 
1006   If a new collection node is created, its parent is the tree
1007   root, and the new collection node is marked \e{not seen}.
1008 
1009   \a genus must be specified, i.e. it must not be \c{DontCare}.
1010   If it is \c{DontCare}, 0 is returned, which is a programming
1011   error.
1012  */
findCollection(const QString & name,Node::NodeType type)1013 CollectionNode *Tree::findCollection(const QString &name, Node::NodeType type)
1014 {
1015     CNMap *m = getCollectionMap(type);
1016     if (!m) // error
1017         return nullptr;
1018     auto it = m->constFind(name);
1019     if (it != m->cend())
1020         return it.value();
1021     CollectionNode *cn = new CollectionNode(type, root(), name);
1022     cn->markNotSeen();
1023     m->insert(name, cn);
1024     return cn;
1025 }
1026 
1027 /*! \fn CollectionNode *Tree::findGroup(const QString &name)
1028   Find the group node named \a name and return a pointer
1029   to it. If the group node is not found, add a new group
1030   node named \a name and return a pointer to the new one.
1031 
1032   If a new group node is added, its parent is the tree root,
1033   and the new group node is marked \e{not seen}.
1034  */
1035 
1036 /*! \fn CollectionNode *Tree::findModule(const QString &name)
1037   Find the module node named \a name and return a pointer
1038   to it. If a matching node is not found, add a new module
1039   node named \a name and return a pointer to that one.
1040 
1041   If a new module node is added, its parent is the tree root,
1042   and the new module node is marked \e{not seen}.
1043  */
1044 
1045 /*! \fn CollectionNode *Tree::findQmlModule(const QString &name)
1046   Find the QML module node named \a name and return a pointer
1047   to it. If a matching node is not found, add a new QML module
1048   node named \a name and return a pointer to that one.
1049 
1050   If a new QML module node is added, its parent is the tree root,
1051   and the new node is marked \e{not seen}.
1052  */
1053 
1054 /*! \fn CollectionNode *Tree::findJsModule(const QString &name)
1055   Find the JavaScript module named \a name and return a pointer
1056   to it. If a matching node is not found, add a new JavaScript
1057   module node named \a name and return a pointer to that one.
1058 
1059   If a new JavaScript module node is added, its parent is the
1060   tree root, and the new node is marked \e{not seen}.
1061  */
1062 
1063 /*! \fn CollectionNode *Tree::addGroup(const QString &name)
1064   Looks up the group node named \a name in the collection
1065   of all group nodes. If a match is found, a pointer to the
1066   node is returned. Otherwise, a new group node named \a name
1067   is created and inserted into the collection, and the pointer
1068   to that node is returned.
1069  */
1070 
1071 /*! \fn CollectionNode *Tree::addModule(const QString &name)
1072   Looks up the module node named \a name in the collection
1073   of all module nodes. If a match is found, a pointer to the
1074   node is returned. Otherwise, a new module node named \a name
1075   is created and inserted into the collection, and the pointer
1076   to that node is returned.
1077  */
1078 
1079 /*! \fn CollectionNode *Tree::addQmlModule(const QString &name)
1080   Looks up the QML module node named \a name in the collection
1081   of all QML module nodes. If a match is found, a pointer to the
1082   node is returned. Otherwise, a new QML module node named \a name
1083   is created and inserted into the collection, and the pointer
1084   to that node is returned.
1085  */
1086 
1087 /*! \fn CollectionNode *Tree::addJsModule(const QString &name)
1088   Looks up the JavaScript module node named \a name in the collection
1089   of all JavaScript module nodes. If a match is found, a pointer to the
1090   node is returned. Otherwise, a new JavaScrpt module node named \a name
1091   is created and inserted into the collection, and the pointer
1092   to that node is returned.
1093  */
1094 
1095 /*!
1096   Looks up the group node named \a name in the collection
1097   of all group nodes. If a match is not found, a new group
1098   node named \a name is created and inserted into the collection.
1099   Then append \a node to the group's members list, and append the
1100   group name to the list of group names in \a node. The parent of
1101   \a node is not changed by this function. Returns a pointer to
1102   the group node.
1103  */
addToGroup(const QString & name,Node * node)1104 CollectionNode *Tree::addToGroup(const QString &name, Node *node)
1105 {
1106     CollectionNode *cn = findGroup(name);
1107     if (!node->isInternal()) {
1108         cn->addMember(node);
1109         node->appendGroupName(name);
1110     }
1111     return cn;
1112 }
1113 
1114 /*!
1115   Looks up the module node named \a name in the collection
1116   of all module nodes. If a match is not found, a new module
1117   node named \a name is created and inserted into the collection.
1118   Then append \a node to the module's members list. The parent of
1119   \a node is not changed by this function. Returns the module node.
1120  */
addToModule(const QString & name,Node * node)1121 CollectionNode *Tree::addToModule(const QString &name, Node *node)
1122 {
1123     CollectionNode *cn = findModule(name);
1124     cn->addMember(node);
1125     node->setPhysicalModuleName(name);
1126     return cn;
1127 }
1128 
1129 /*!
1130   Looks up the QML module named \a name. If it isn't there,
1131   create it. Then append \a node to the QML module's member
1132   list. The parent of \a node is not changed by this function.
1133   Returns the pointer to the QML module node.
1134  */
addToQmlModule(const QString & name,Node * node)1135 CollectionNode *Tree::addToQmlModule(const QString &name, Node *node)
1136 {
1137     QStringList qmid;
1138     QStringList dotSplit;
1139     QStringList blankSplit = name.split(QLatin1Char(' '));
1140     qmid.append(blankSplit[0]);
1141     if (blankSplit.size() > 1) {
1142         qmid.append(blankSplit[0] + blankSplit[1]);
1143         dotSplit = blankSplit[1].split(QLatin1Char('.'));
1144         qmid.append(blankSplit[0] + dotSplit[0]);
1145     }
1146 
1147     CollectionNode *cn = findQmlModule(blankSplit[0]);
1148     cn->addMember(node);
1149     node->setQmlModule(cn);
1150     if (node->isQmlType()) {
1151         QmlTypeNode *n = static_cast<QmlTypeNode *>(node);
1152         for (int i = 0; i < qmid.size(); ++i) {
1153             QString key = qmid[i] + "::" + node->name();
1154             insertQmlType(key, n);
1155         }
1156     }
1157     return cn;
1158 }
1159 
1160 /*!
1161   Looks up the QML module named \a name. If it isn't there,
1162   create it. Then append \a node to the QML module's member
1163   list. The parent of \a node is not changed by this function.
1164   Returns the pointer to the QML module node.
1165  */
addToJsModule(const QString & name,Node * node)1166 CollectionNode *Tree::addToJsModule(const QString &name, Node *node)
1167 {
1168     QStringList qmid;
1169     QStringList dotSplit;
1170     QStringList blankSplit = name.split(QLatin1Char(' '));
1171     qmid.append(blankSplit[0]);
1172     if (blankSplit.size() > 1) {
1173         qmid.append(blankSplit[0] + blankSplit[1]);
1174         dotSplit = blankSplit[1].split(QLatin1Char('.'));
1175         qmid.append(blankSplit[0] + dotSplit[0]);
1176     }
1177 
1178     CollectionNode *cn = findJsModule(blankSplit[0]);
1179     cn->addMember(node);
1180     node->setQmlModule(cn);
1181     if (node->isJsType()) {
1182         QmlTypeNode *n = static_cast<QmlTypeNode *>(node);
1183         for (int i = 0; i < qmid.size(); ++i) {
1184             QString key = qmid[i] + "::" + node->name();
1185             insertQmlType(key, n);
1186         }
1187     }
1188     return cn;
1189 }
1190 
1191 /*!
1192   If the QML type map does not contain \a key, insert node
1193   \a n with the specified \a key.
1194  */
insertQmlType(const QString & key,QmlTypeNode * n)1195 void Tree::insertQmlType(const QString &key, QmlTypeNode *n)
1196 {
1197     if (!qmlTypeMap_.contains(key))
1198         qmlTypeMap_.insert(key, n);
1199 }
1200 
1201 /*!
1202   Finds the function node with the specifried name \a path that
1203   also has the specified \a parameters and returns a pointer to
1204   the the first matching function node if one is found.
1205 
1206   This function begins searching the tree at \a relative for
1207   the \l {FunctionNode} {function node} identified by \a path
1208   that has the specified \a parameters. The \a flags are
1209   used to restrict the search. If a matching node is found, a
1210   pointer to it is returned. Otherwise, nullis returned. If
1211   \a relative is ull, the search begins at the tree root.
1212  */
findFunctionNode(const QStringList & path,const Parameters & parameters,const Node * relative,Node::Genus genus) const1213 const FunctionNode *Tree::findFunctionNode(const QStringList &path, const Parameters &parameters,
1214                                            const Node *relative, Node::Genus genus) const
1215 {
1216     if (path.size() == 3 && !path[0].isEmpty()
1217         && ((genus == Node::QML) || (genus == Node::DontCare))) {
1218         QmlTypeNode *qcn = lookupQmlType(QString(path[0] + "::" + path[1]));
1219         if (qcn == nullptr) {
1220             QStringList p(path[1]);
1221             Node *n = findNodeByNameAndType(p, &Node::isQmlType);
1222             if ((n != nullptr) && (n->isQmlType() || n->isJsType()))
1223                 qcn = static_cast<QmlTypeNode *>(n);
1224         }
1225         if (qcn != nullptr)
1226             return static_cast<const FunctionNode *>(qcn->findFunctionChild(path[2], parameters));
1227     }
1228 
1229     if (relative == nullptr)
1230         relative = root();
1231     else if (genus != Node::DontCare) {
1232         if (genus != relative->genus())
1233             relative = root();
1234     }
1235 
1236     do {
1237         Node *node = const_cast<Node *>(relative);
1238         int i;
1239 
1240         for (i = 0; i < path.size(); ++i) {
1241             if (node == nullptr || !node->isAggregate())
1242                 break;
1243 
1244             Aggregate *aggregate = static_cast<Aggregate *>(node);
1245             Node *next = nullptr;
1246             if (i == path.size() - 1)
1247                 next = aggregate->findFunctionChild(path.at(i), parameters);
1248             else
1249                 next = aggregate->findChildNode(path.at(i), genus);
1250 
1251             if ((next == nullptr) && aggregate->isClassNode()) {
1252                 const ClassList bases = allBaseClasses(static_cast<const ClassNode *>(aggregate));
1253                 for (auto *base : bases) {
1254                     if (i == path.size() - 1)
1255                         next = base->findFunctionChild(path.at(i), parameters);
1256                     else
1257                         next = base->findChildNode(path.at(i), genus);
1258 
1259                     if (next != nullptr)
1260                         break;
1261                 }
1262             }
1263 
1264             node = next;
1265         } // for (i = 0; i < path.size(); ++i)
1266 
1267         if (node && i == path.size() && node->isFunction()) {
1268             // A function node was found at the end of the path.
1269             // If it is not marked private, return it. If it is
1270             // marked private, then if it overrides a function,
1271             // find that function instead because it might not
1272             // be marked private. If all the overloads are
1273             // marked private, return the original function node.
1274             // This should be replace with findOverriddenFunctionNode().
1275             const FunctionNode *fn = static_cast<const FunctionNode *>(node);
1276             const FunctionNode *FN = fn;
1277             while (FN->isPrivate() && !FN->overridesThis().isEmpty()) {
1278                 QStringList path = FN->overridesThis().split("::");
1279                 FN = qdb_->findFunctionNode(path, parameters, relative, genus);
1280                 if (FN == nullptr)
1281                     break;
1282                 if (!FN->isPrivate())
1283                     return FN;
1284             }
1285             return fn;
1286         }
1287         relative = relative->parent();
1288     } while (relative);
1289     return nullptr;
1290 }
1291 
1292 /*!
1293   Generate a target of the form link-nnn, where the nnn is
1294   the current link count for this tree. This target string
1295   is returned. It will be output as an HTML anchor just before
1296   an HTML link to the node \a t.
1297 
1298   The node \a t
1299  */
getNewLinkTarget(const Node * locNode,const Node * t,const QString & fileName,QString & text,bool broken)1300 QString Tree::getNewLinkTarget(const Node *locNode, const Node *t, const QString &fileName,
1301                                QString &text, bool broken)
1302 {
1303     QString physicalModuleName;
1304     if (t != nullptr && !broken) {
1305         Tree *tree = t->tree();
1306         if (tree != this)
1307             tree->incrementLinkCount();
1308         physicalModuleName = tree->physicalModuleName();
1309     } else
1310         physicalModuleName = "broken";
1311     incrementLinkCount();
1312     QString target = QString("qa-target-%1").arg(-(linkCount()));
1313     TargetLoc *tloc = new TargetLoc(locNode, target, fileName, text, broken);
1314     TargetList *tList = nullptr;
1315     auto it = targetListMap_->find(physicalModuleName);
1316     if (it == targetListMap_->end()) {
1317         tList = new TargetList;
1318         it = targetListMap_->insert(physicalModuleName, tList);
1319     } else
1320         tList = it.value();
1321     tList->append(tloc);
1322     return target;
1323 }
1324 
1325 /*!
1326   Look up the target list for the specified \a module
1327   and return a pointer to it.
1328  */
getTargetList(const QString & module)1329 TargetList *Tree::getTargetList(const QString &module)
1330 {
1331     return targetListMap_->value(module);
1332 }
1333 
1334 /*!
1335   Search this tree recursively from \a parent to find a function
1336   node with the specified \a tag. If no function node is found
1337   with the required \a tag, return 0.
1338  */
findFunctionNodeForTag(const QString & tag,Aggregate * parent)1339 FunctionNode *Tree::findFunctionNodeForTag(const QString &tag, Aggregate *parent)
1340 {
1341     if (parent == nullptr)
1342         parent = root();
1343     const NodeList &children = parent->childNodes();
1344     for (Node *n : children) {
1345         if (n != nullptr && n->isFunction() && n->hasTag(tag))
1346             return static_cast<FunctionNode *>(n);
1347     }
1348     for (Node *n : children) {
1349         if (n != nullptr && n->isAggregate()) {
1350             n = findFunctionNodeForTag(tag, static_cast<Aggregate *>(n));
1351             if (n != nullptr)
1352                 return static_cast<FunctionNode *>(n);
1353         }
1354     }
1355     return nullptr;
1356 }
1357 
1358 /*!
1359   There should only be one macro node for macro name \a t.
1360   The macro node is not built until the \macro command is seen.
1361  */
findMacroNode(const QString & t,const Aggregate * parent)1362 FunctionNode *Tree::findMacroNode(const QString &t, const Aggregate *parent)
1363 {
1364     if (parent == nullptr)
1365         parent = root();
1366     const NodeList &children = parent->childNodes();
1367     for (Node *n : children) {
1368         if (n != nullptr && (n->isMacro() || n->isFunction()) && n->name() == t)
1369             return static_cast<FunctionNode *>(n);
1370     }
1371     for (Node *n : children) {
1372         if (n != nullptr && n->isAggregate()) {
1373             FunctionNode *fn = findMacroNode(t, static_cast<Aggregate *>(n));
1374             if (fn != nullptr)
1375                 return fn;
1376         }
1377     }
1378     return nullptr;
1379 }
1380 
1381 /*!
1382   Add the class and struct names in \a arg to the \e {don't document}
1383   map.
1384  */
addToDontDocumentMap(QString & arg)1385 void Tree::addToDontDocumentMap(QString &arg)
1386 {
1387     arg.remove(QChar('('));
1388     arg.remove(QChar(')'));
1389     QString t = arg.simplified();
1390     QStringList sl = t.split(QChar(' '));
1391     if (sl.isEmpty())
1392         return;
1393     for (const QString &s : sl) {
1394         if (!dontDocumentMap_.contains(s))
1395             dontDocumentMap_.insert(s, nullptr);
1396     }
1397 }
1398 
1399 /*!
1400   The \e {don't document} map has been loaded with the names
1401   of classes and structs in the current module that are not
1402   documented and should not be documented. Now traverse the
1403   map, and for each class or struct name, find the class node
1404   that represents that class or struct and mark it with the
1405   \C DontDocument status.
1406 
1407   This results in a map of the class and struct nodes in the
1408   module that are in the public API but are not meant to be
1409   used by anyone. They are only used internally, but for one
1410   reason or another, they must have public visibility.
1411  */
markDontDocumentNodes()1412 void Tree::markDontDocumentNodes()
1413 {
1414     for (auto it = dontDocumentMap_.begin(); it != dontDocumentMap_.end(); ++it) {
1415         Aggregate *node = findAggregate(it.key());
1416         if (node != nullptr)
1417             node->setStatus(Node::DontDocument);
1418     }
1419 }
1420 
1421 QT_END_NAMESPACE
1422