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 ¶meters,
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