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 "qdocdatabase.h"
30
31 #include "atom.h"
32 #include "generator.h"
33 #include "qdocindexfiles.h"
34 #include "qdoctagfiles.h"
35 #include "tree.h"
36
37 #include <QtCore/qdebug.h>
38
39 QT_BEGIN_NAMESPACE
40
41 static NodeMap emptyNodeMap_;
42 static NodeMultiMap emptyNodeMultiMap_;
43 bool QDocDatabase::debug = false;
44
45 /*!
46 \class QDocForest
47
48 A class representing a forest of Tree objects.
49
50 This private class manages a collection of Tree objects (a
51 forest) for the singleton QDocDatabase object. It is only
52 accessed by that singleton QDocDatabase object, which is a
53 friend. Each tree in the forest is an instance of class
54 Tree, which is a mostly private class. Both QDocForest and
55 QDocDatabase are friends of Tree and have full access.
56
57 There are two kinds of trees in the forest, differing not
58 in structure but in use. One Tree is the primary tree. It
59 is the tree representing the module being documented. All
60 the other trees in the forest are called index trees. Each
61 one represents the contents of the index file for one of
62 the modules the current module must be able to link to.
63
64 The instances of subclasses of Node in the primary tree
65 will contain documentation in an instance of Doc. The
66 index trees contain no documentation, and each Node in
67 an index tree is marked as an index node.
68
69 Each tree is named with the name of its module.
70
71 The search order is created by searchOrder(), if it has
72 not already been created. The search order and module
73 names arrays have parallel structure, i.e. modulNames_[i]
74 is the module name of the Tree at searchOrder_[i].
75
76 The primary tree is always the first tree in the search
77 order. i.e., when the database is searched, the primary
78 tree is always searched first, unless a specific tree is
79 being searched.
80 */
81
82 /*!
83 Destroys the qdoc forest. This requires deleting
84 each Tree in the forest. Note that the forest has
85 been transferred into the search order array, so
86 what is really being used to destroy the forest
87 is the search order array.
88 */
~QDocForest()89 QDocForest::~QDocForest()
90 {
91 for (int i = 0; i < searchOrder_.size(); ++i)
92 delete searchOrder_.at(i);
93 forest_.clear();
94 searchOrder_.clear();
95 indexSearchOrder_.clear();
96 moduleNames_.clear();
97 primaryTree_ = nullptr;
98 }
99
100 /*!
101 Initializes the forest prior to a traversal and
102 returns a pointer to the root node of the primary
103 tree. If the forest is empty, it return 0
104 */
firstRoot()105 NamespaceNode *QDocForest::firstRoot()
106 {
107 currentIndex_ = 0;
108 return (!searchOrder().isEmpty() ? searchOrder()[0]->root() : nullptr);
109 }
110
111 /*!
112 Increments the forest's current tree index. If the current
113 tree index is still within the forest, the function returns
114 the root node of the current tree. Otherwise it returns 0.
115 */
nextRoot()116 NamespaceNode *QDocForest::nextRoot()
117 {
118 ++currentIndex_;
119 return (currentIndex_ < searchOrder().size() ? searchOrder()[currentIndex_]->root() : nullptr);
120 }
121
122 /*!
123 Initializes the forest prior to a traversal and
124 returns a pointer to the primary tree. If the
125 forest is empty, it returns 0.
126 */
firstTree()127 Tree *QDocForest::firstTree()
128 {
129 currentIndex_ = 0;
130 return (!searchOrder().isEmpty() ? searchOrder()[0] : nullptr);
131 }
132
133 /*!
134 Increments the forest's current tree index. If the current
135 tree index is still within the forest, the function returns
136 the pointer to the current tree. Otherwise it returns 0.
137 */
nextTree()138 Tree *QDocForest::nextTree()
139 {
140 ++currentIndex_;
141 return (currentIndex_ < searchOrder().size() ? searchOrder()[currentIndex_] : nullptr);
142 }
143
144 /*!
145 \fn Tree *QDocForest::primaryTree()
146
147 Returns the pointer to the primary tree.
148 */
149
150 /*!
151 Finds the tree for module \a t in the forest and
152 sets the primary tree to be that tree. After the
153 primary tree is set, that tree is removed from the
154 forest.
155
156 \node It gets re-inserted into the forest after the
157 search order is built.
158 */
setPrimaryTree(const QString & t)159 void QDocForest::setPrimaryTree(const QString &t)
160 {
161 QString T = t.toLower();
162 primaryTree_ = findTree(T);
163 forest_.remove(T);
164 if (primaryTree_ == nullptr)
165 qDebug() << "ERROR: Could not set primary tree to:" << t;
166 }
167
168 /*!
169 If the search order array is empty, create the search order.
170 If the search order array is not empty, do nothing.
171 */
setSearchOrder(const QStringList & t)172 void QDocForest::setSearchOrder(const QStringList &t)
173 {
174 if (!searchOrder_.isEmpty())
175 return;
176
177 /* Allocate space for the search order. */
178 searchOrder_.reserve(forest_.size() + 1);
179 searchOrder_.clear();
180 moduleNames_.reserve(forest_.size() + 1);
181 moduleNames_.clear();
182
183 /* The primary tree is always first in the search order. */
184 QString primaryName = primaryTree()->physicalModuleName();
185 searchOrder_.append(primaryTree_);
186 moduleNames_.append(primaryName);
187 forest_.remove(primaryName);
188
189 for (const QString &m : t) {
190 if (primaryName != m) {
191 auto it = forest_.find(m);
192 if (it != forest_.end()) {
193 searchOrder_.append(it.value());
194 moduleNames_.append(m);
195 forest_.remove(m);
196 }
197 }
198 }
199 /*
200 If any trees remain in the forest, just add them
201 to the search order sequentially, because we don't
202 know any better at this point.
203 */
204 if (!forest_.isEmpty()) {
205 for (auto it = forest_.begin(); it != forest_.end(); ++it) {
206 searchOrder_.append(it.value());
207 moduleNames_.append(it.key());
208 }
209 forest_.clear();
210 }
211
212 /*
213 Rebuild the forest after constructing the search order.
214 It was destroyed during construction of the search order,
215 but it is needed for module-specific searches.
216
217 Note that this loop also inserts the primary tree into the
218 forrest. That is a requirement.
219 */
220 for (int i = 0; i < searchOrder_.size(); ++i) {
221 if (!forest_.contains(moduleNames_.at(i))) {
222 forest_.insert(moduleNames_.at(i), searchOrder_.at(i));
223 }
224 }
225 }
226
227 /*!
228 Returns an ordered array of Tree pointers that represents
229 the order in which the trees should be searched. The first
230 Tree in the array is the tree for the current module, i.e.
231 the module for which qdoc is generating documentation.
232
233 The other Tree pointers in the array represent the index
234 files that were loaded in preparation for generating this
235 module's documentation. Each Tree pointer represents one
236 index file. The index file Tree points have been ordered
237 heuristically to, hopefully, minimize searching. Thr order
238 will probably be changed.
239
240 If the search order array is empty, this function calls
241 indexSearchOrder(). The search order array is empty while
242 the index files are being loaded, but some searches must
243 be performed during this time, notably searches for base
244 class nodes. These searches require a temporary search
245 order. The temporary order changes throughout the loading
246 of the index files, but it is always the tree for the
247 current index file first, followed by the trees for the
248 index files that have already been loaded. The only
249 ordering required in this temporary search order is that
250 the current tree must be searched first.
251 */
searchOrder()252 const QVector<Tree *> &QDocForest::searchOrder()
253 {
254 if (searchOrder_.isEmpty())
255 return indexSearchOrder();
256 return searchOrder_;
257 }
258
259 /*!
260 There are two search orders used by qdoc when searching for
261 things. The normal search order is returned by searchOrder(),
262 but this normal search order is not known until all the index
263 files have been read. At that point, setSearchOrder() is
264 called.
265
266 During the reading of the index files, the vector holding
267 the normal search order remains empty. Whenever the search
268 order is requested, if that vector is empty, this function
269 is called to return a temporary search order, which includes
270 all the index files that have been read so far, plus the
271 one being read now. That one is prepended to the front of
272 the vector.
273 */
indexSearchOrder()274 const QVector<Tree *> &QDocForest::indexSearchOrder()
275 {
276 if (forest_.size() > indexSearchOrder_.size())
277 indexSearchOrder_.prepend(primaryTree_);
278 return indexSearchOrder_;
279 }
280
281 /*!
282 Create a new Tree for the index file for the specified
283 \a module and add it to the forest. Return the pointer
284 to its root.
285 */
newIndexTree(const QString & module)286 NamespaceNode *QDocForest::newIndexTree(const QString &module)
287 {
288 primaryTree_ = new Tree(module, qdb_);
289 forest_.insert(module.toLower(), primaryTree_);
290 return primaryTree_->root();
291 }
292
293 /*!
294 Create a new Tree for use as the primary tree. This tree
295 will represent the primary module. \a module is camel case.
296 */
newPrimaryTree(const QString & module)297 void QDocForest::newPrimaryTree(const QString &module)
298 {
299 primaryTree_ = new Tree(module, qdb_);
300 }
301
302 /*!
303 Searches through the forest for a node named \a targetPath
304 and returns a pointer to it if found. The \a relative node
305 is the starting point. It only makes sense for the primary
306 tree, which is searched first. After the primary tree has
307 been searched, \a relative is set to 0 for searching the
308 other trees, which are all index trees. With relative set
309 to 0, the starting point for each index tree is the root
310 of the index tree.
311 */
findNodeForTarget(QStringList & targetPath,const Node * relative,Node::Genus genus,QString & ref)312 const Node *QDocForest::findNodeForTarget(QStringList &targetPath, const Node *relative,
313 Node::Genus genus, QString &ref)
314 {
315 int flags = SearchBaseClasses | SearchEnumValues;
316
317 QString entity = targetPath.takeFirst();
318 QStringList entityPath = entity.split("::");
319
320 QString target;
321 if (!targetPath.isEmpty())
322 target = targetPath.takeFirst();
323
324 for (const auto *tree : searchOrder()) {
325 const Node *n = tree->findNodeForTarget(entityPath, target, relative, flags, genus, ref);
326 if (n)
327 return n;
328 relative = nullptr;
329 }
330 return nullptr;
331 }
332
333 /*!
334 Print the list of module names ordered according
335 to how many successful searches each tree had.
336 */
printLinkCounts(const QString & project)337 void QDocForest::printLinkCounts(const QString &project)
338 {
339 Location().report(QString("%1: Link Counts").arg(project));
340 QMultiMap<int, QString> m;
341 for (const auto *tree : searchOrder()) {
342 if (tree->linkCount() < 0)
343 m.insert(tree->linkCount(), tree->physicalModuleName());
344 }
345 QString depends = "depends +=";
346 QString module = project.toLower();
347 for (auto it = m.begin(); it != m.end(); ++it) {
348 QString line = " " + it.value();
349 if (it.value() != module)
350 depends += QLatin1Char(' ') + it.value();
351 int pad = 30 - line.length();
352 for (int k = 0; k < pad; ++k)
353 line += QLatin1Char(' ');
354 line += "%1";
355 Location().report(line.arg(-(it.key())));
356 }
357 Location().report("Optimal depends variable:");
358 Location().report(depends);
359 }
360
361 /*!
362 Print the list of module names ordered according
363 to how many successful searches each tree had.
364 */
getLinkCounts(QStringList & strings,QVector<int> & counts)365 QString QDocForest::getLinkCounts(QStringList &strings, QVector<int> &counts)
366 {
367 QMultiMap<int, QString> m;
368 for (const auto *tree : searchOrder()) {
369 if (tree->linkCount() < 0)
370 m.insert(tree->linkCount(), tree->physicalModuleName());
371 }
372 QString depends = "depends +=";
373 QString module = Generator::defaultModuleName().toLower();
374 for (auto it = m.begin(); it != m.end(); ++it) {
375 if (it.value() != module) {
376 counts.append(-(it.key()));
377 strings.append(it.value());
378 depends += QLatin1Char(' ') + it.value();
379 }
380 }
381 return depends;
382 }
383
384 /*!
385 Finds the FunctionNode for the qualified function name
386 in \a path, that also has the specified \a parameters.
387 Returns a pointer to the first matching function.
388
389 \a relative is a node in the primary tree where the search
390 should begin. It is only used when searching the primary
391 tree. \a genus can be used to force the search to find a
392 C++ function or a QML function.
393 */
findFunctionNode(const QStringList & path,const Parameters & parameters,const Node * relative,Node::Genus genus)394 const FunctionNode *QDocForest::findFunctionNode(const QStringList &path,
395 const Parameters ¶meters, const Node *relative,
396 Node::Genus genus)
397 {
398 for (const auto *tree : searchOrder()) {
399 const FunctionNode *fn = tree->findFunctionNode(path, parameters, relative, genus);
400 if (fn)
401 return fn;
402 relative = nullptr;
403 }
404 return nullptr;
405 }
406
407 /*! \class QDocDatabase
408 This class provides exclusive access to the qdoc database,
409 which consists of a forrest of trees and a lot of maps and
410 other useful data structures.
411 */
412
413 QDocDatabase *QDocDatabase::qdocDB_ = nullptr;
414 NodeMap QDocDatabase::typeNodeMap_;
415 NodeMultiMap QDocDatabase::obsoleteClasses_;
416 NodeMultiMap QDocDatabase::classesWithObsoleteMembers_;
417 NodeMultiMap QDocDatabase::obsoleteQmlTypes_;
418 NodeMultiMap QDocDatabase::qmlTypesWithObsoleteMembers_;
419 NodeMultiMap QDocDatabase::cppClasses_;
420 NodeMultiMap QDocDatabase::qmlBasicTypes_;
421 NodeMultiMap QDocDatabase::qmlTypes_;
422 NodeMultiMap QDocDatabase::examples_;
423 NodeMapMap QDocDatabase::newClassMaps_;
424 NodeMapMap QDocDatabase::newQmlTypeMaps_;
425 NodeMultiMapMap QDocDatabase::newSinceMaps_;
426
427 /*!
428 Constructs the singleton qdoc database object. The singleton
429 constructs the \a forest_ object, which is also a singleton.
430 \a showInternal_ is normally false. If it is true, qdoc will
431 write documentation for nodes marked \c internal.
432
433 \a singleExec_ is false when qdoc is being used in the standard
434 way of running qdoc twices for each module, first with -prepare
435 and then with -generate. First the -prepare phase is run for
436 each module, then the -generate phase is run for each module.
437
438 When \a singleExec_ is true, qdoc is run only once. During the
439 single execution, qdoc processes the qdocconf files for all the
440 modules sequentially in a loop. Each source file for each module
441 is read exactly once.
442 */
QDocDatabase()443 QDocDatabase::QDocDatabase() : showInternal_(false), singleExec_(false), forest_(this)
444 {
445 // nothing
446 }
447
448 /*!
449 Destroys the qdoc database object. This requires destroying
450 the forest object, which contains an array of tree pointers.
451 Each tree is deleted.
452 */
~QDocDatabase()453 QDocDatabase::~QDocDatabase()
454 {
455 // nothing.
456 }
457
458 /*!
459 Creates the singleton. Allows only one instance of the class
460 to be created. Returns a pointer to the singleton.
461 */
qdocDB()462 QDocDatabase *QDocDatabase::qdocDB()
463 {
464 if (qdocDB_ == nullptr) {
465 qdocDB_ = new QDocDatabase;
466 initializeDB();
467 }
468 return qdocDB_;
469 }
470
471 /*!
472 Destroys the singleton.
473 */
destroyQdocDB()474 void QDocDatabase::destroyQdocDB()
475 {
476 if (qdocDB_ != nullptr) {
477 delete qdocDB_;
478 qdocDB_ = nullptr;
479 }
480 }
481
482 /*!
483 Initialize data structures in the singleton qdoc database.
484
485 In particular, the type node map is initialized with a lot
486 type names that don't refer to documented types. For example,
487 many C++ standard types are included. These might be documented
488 here at some point, but for now they are not. Other examples
489 include \c array and \c data, which are just generic names
490 used as place holders in function signatures that appear in
491 the documentation.
492
493 \note Do not add QML basic types into this list as it will
494 break linking to those types.
495
496 Also calls Node::initialize() to initialize the search goal map.
497 */
initializeDB()498 void QDocDatabase::initializeDB()
499 {
500 Node::initialize();
501 typeNodeMap_.insert("accepted", nullptr);
502 typeNodeMap_.insert("actionPerformed", nullptr);
503 typeNodeMap_.insert("activated", nullptr);
504 typeNodeMap_.insert("alias", nullptr);
505 typeNodeMap_.insert("anchors", nullptr);
506 typeNodeMap_.insert("any", nullptr);
507 typeNodeMap_.insert("array", nullptr);
508 typeNodeMap_.insert("autoSearch", nullptr);
509 typeNodeMap_.insert("axis", nullptr);
510 typeNodeMap_.insert("backClicked", nullptr);
511 typeNodeMap_.insert("boomTime", nullptr);
512 typeNodeMap_.insert("border", nullptr);
513 typeNodeMap_.insert("buttonClicked", nullptr);
514 typeNodeMap_.insert("callback", nullptr);
515 typeNodeMap_.insert("char", nullptr);
516 typeNodeMap_.insert("clicked", nullptr);
517 typeNodeMap_.insert("close", nullptr);
518 typeNodeMap_.insert("closed", nullptr);
519 typeNodeMap_.insert("cond", nullptr);
520 typeNodeMap_.insert("data", nullptr);
521 typeNodeMap_.insert("dataReady", nullptr);
522 typeNodeMap_.insert("dateString", nullptr);
523 typeNodeMap_.insert("dateTimeString", nullptr);
524 typeNodeMap_.insert("datetime", nullptr);
525 typeNodeMap_.insert("day", nullptr);
526 typeNodeMap_.insert("deactivated", nullptr);
527 typeNodeMap_.insert("drag", nullptr);
528 typeNodeMap_.insert("easing", nullptr);
529 typeNodeMap_.insert("error", nullptr);
530 typeNodeMap_.insert("exposure", nullptr);
531 typeNodeMap_.insert("fatalError", nullptr);
532 typeNodeMap_.insert("fileSelected", nullptr);
533 typeNodeMap_.insert("flags", nullptr);
534 typeNodeMap_.insert("float", nullptr);
535 typeNodeMap_.insert("focus", nullptr);
536 typeNodeMap_.insert("focusZone", nullptr);
537 typeNodeMap_.insert("format", nullptr);
538 typeNodeMap_.insert("framePainted", nullptr);
539 typeNodeMap_.insert("from", nullptr);
540 typeNodeMap_.insert("frontClicked", nullptr);
541 typeNodeMap_.insert("function", nullptr);
542 typeNodeMap_.insert("hasOpened", nullptr);
543 typeNodeMap_.insert("hovered", nullptr);
544 typeNodeMap_.insert("hoveredTitle", nullptr);
545 typeNodeMap_.insert("hoveredUrl", nullptr);
546 typeNodeMap_.insert("imageCapture", nullptr);
547 typeNodeMap_.insert("imageProcessing", nullptr);
548 typeNodeMap_.insert("index", nullptr);
549 typeNodeMap_.insert("initialized", nullptr);
550 typeNodeMap_.insert("isLoaded", nullptr);
551 typeNodeMap_.insert("item", nullptr);
552 typeNodeMap_.insert("jsdict", nullptr);
553 typeNodeMap_.insert("jsobject", nullptr);
554 typeNodeMap_.insert("key", nullptr);
555 typeNodeMap_.insert("keysequence", nullptr);
556 typeNodeMap_.insert("listViewClicked", nullptr);
557 typeNodeMap_.insert("loadRequest", nullptr);
558 typeNodeMap_.insert("locale", nullptr);
559 typeNodeMap_.insert("location", nullptr);
560 typeNodeMap_.insert("long", nullptr);
561 typeNodeMap_.insert("message", nullptr);
562 typeNodeMap_.insert("messageReceived", nullptr);
563 typeNodeMap_.insert("mode", nullptr);
564 typeNodeMap_.insert("month", nullptr);
565 typeNodeMap_.insert("name", nullptr);
566 typeNodeMap_.insert("number", nullptr);
567 typeNodeMap_.insert("object", nullptr);
568 typeNodeMap_.insert("offset", nullptr);
569 typeNodeMap_.insert("ok", nullptr);
570 typeNodeMap_.insert("openCamera", nullptr);
571 typeNodeMap_.insert("openImage", nullptr);
572 typeNodeMap_.insert("openVideo", nullptr);
573 typeNodeMap_.insert("padding", nullptr);
574 typeNodeMap_.insert("parent", nullptr);
575 typeNodeMap_.insert("path", nullptr);
576 typeNodeMap_.insert("photoModeSelected", nullptr);
577 typeNodeMap_.insert("position", nullptr);
578 typeNodeMap_.insert("precision", nullptr);
579 typeNodeMap_.insert("presetClicked", nullptr);
580 typeNodeMap_.insert("preview", nullptr);
581 typeNodeMap_.insert("previewSelected", nullptr);
582 typeNodeMap_.insert("progress", nullptr);
583 typeNodeMap_.insert("puzzleLost", nullptr);
584 typeNodeMap_.insert("qmlSignal", nullptr);
585 typeNodeMap_.insert("rectangle", nullptr);
586 typeNodeMap_.insert("request", nullptr);
587 typeNodeMap_.insert("requestId", nullptr);
588 typeNodeMap_.insert("section", nullptr);
589 typeNodeMap_.insert("selected", nullptr);
590 typeNodeMap_.insert("send", nullptr);
591 typeNodeMap_.insert("settingsClicked", nullptr);
592 typeNodeMap_.insert("shoe", nullptr);
593 typeNodeMap_.insert("short", nullptr);
594 typeNodeMap_.insert("signed", nullptr);
595 typeNodeMap_.insert("sizeChanged", nullptr);
596 typeNodeMap_.insert("size_t", nullptr);
597 typeNodeMap_.insert("sockaddr", nullptr);
598 typeNodeMap_.insert("someOtherSignal", nullptr);
599 typeNodeMap_.insert("sourceSize", nullptr);
600 typeNodeMap_.insert("startButtonClicked", nullptr);
601 typeNodeMap_.insert("state", nullptr);
602 typeNodeMap_.insert("std::initializer_list", nullptr);
603 typeNodeMap_.insert("std::list", nullptr);
604 typeNodeMap_.insert("std::map", nullptr);
605 typeNodeMap_.insert("std::pair", nullptr);
606 typeNodeMap_.insert("std::string", nullptr);
607 typeNodeMap_.insert("std::vector", nullptr);
608 typeNodeMap_.insert("stringlist", nullptr);
609 typeNodeMap_.insert("swapPlayers", nullptr);
610 typeNodeMap_.insert("symbol", nullptr);
611 typeNodeMap_.insert("t", nullptr);
612 typeNodeMap_.insert("T", nullptr);
613 typeNodeMap_.insert("tagChanged", nullptr);
614 typeNodeMap_.insert("timeString", nullptr);
615 typeNodeMap_.insert("timeout", nullptr);
616 typeNodeMap_.insert("to", nullptr);
617 typeNodeMap_.insert("toggled", nullptr);
618 typeNodeMap_.insert("type", nullptr);
619 typeNodeMap_.insert("unsigned", nullptr);
620 typeNodeMap_.insert("urllist", nullptr);
621 typeNodeMap_.insert("va_list", nullptr);
622 typeNodeMap_.insert("value", nullptr);
623 typeNodeMap_.insert("valueEmitted", nullptr);
624 typeNodeMap_.insert("videoFramePainted", nullptr);
625 typeNodeMap_.insert("videoModeSelected", nullptr);
626 typeNodeMap_.insert("videoRecorder", nullptr);
627 typeNodeMap_.insert("void", nullptr);
628 typeNodeMap_.insert("volatile", nullptr);
629 typeNodeMap_.insert("wchar_t", nullptr);
630 typeNodeMap_.insert("x", nullptr);
631 typeNodeMap_.insert("y", nullptr);
632 typeNodeMap_.insert("zoom", nullptr);
633 typeNodeMap_.insert("zoomTo", nullptr);
634 }
635
636 /*! \fn NamespaceNode *QDocDatabase::primaryTreeRoot()
637 Returns a pointer to the root node of the primary tree.
638 */
639
640 /*!
641 \fn const CNMap &QDocDatabase::groups()
642 Returns a const reference to the collection of all
643 group nodes in the primary tree.
644 */
645
646 /*!
647 \fn const CNMap &QDocDatabase::modules()
648 Returns a const reference to the collection of all
649 module nodes in the primary tree.
650 */
651
652 /*!
653 \fn const CNMap &QDocDatabase::qmlModules()
654 Returns a const reference to the collection of all
655 QML module nodes in the primary tree.
656 */
657
658 /*!
659 \fn const CNMap &QDocDatabase::jsModules()
660 Returns a const reference to the collection of all
661 JovaScript module nodes in the primary tree.
662 */
663
664 /*! \fn CollectionNode *QDocDatabase::findGroup(const QString &name)
665 Find the group node named \a name and return a pointer
666 to it. If a matching node is not found, add a new group
667 node named \a name and return a pointer to that one.
668
669 If a new group node is added, its parent is the tree root,
670 and the new group node is marked \e{not seen}.
671 */
672
673 /*! \fn CollectionNode *QDocDatabase::findModule(const QString &name)
674 Find the module node named \a name and return a pointer
675 to it. If a matching node is not found, add a new module
676 node named \a name and return a pointer to that one.
677
678 If a new module node is added, its parent is the tree root,
679 and the new module node is marked \e{not seen}.
680 */
681
682 /*! \fn CollectionNode *QDocDatabase::findQmlModule(const QString &name, bool javaScript)
683 Find the QML module node named \a name and return a pointer
684 to it. If a matching node is not found, add a new QML module
685 node named \a name and return a pointer to that one.
686
687 If \a javaScript is set, the return collection must be a
688 JavaScript module.
689
690 If a new QML or JavaScript module node is added, its parent
691 is the tree root, and the new node is marked \e{not seen}.
692 */
693
694 /*! \fn CollectionNode *QDocDatabase::addGroup(const QString &name)
695 Looks up the group named \a name in the primary tree. If
696 a match is found, a pointer to the node is returned.
697 Otherwise, a new group node named \a name is created and
698 inserted into the collection, and the pointer to that node
699 is returned.
700 */
701
702 /*! \fn CollectionNode *QDocDatabase::addModule(const QString &name)
703 Looks up the module named \a name in the primary tree. If
704 a match is found, a pointer to the node is returned.
705 Otherwise, a new module node named \a name is created and
706 inserted into the collection, and the pointer to that node
707 is returned.
708 */
709
710 /*! \fn CollectionNode *QDocDatabase::addQmlModule(const QString &name)
711 Looks up the QML module named \a name in the primary tree.
712 If a match is found, a pointer to the node is returned.
713 Otherwise, a new QML module node named \a name is created
714 and inserted into the collection, and the pointer to that
715 node is returned.
716 */
717
718 /*! \fn CollectionNode *QDocDatabase::addJsModule(const QString &name)
719 Looks up the JavaScript module named \a name in the primary
720 tree. If a match is found, a pointer to the node is returned.
721 Otherwise, a new JavaScript module node named \a name is
722 created and inserted into the collection, and the pointer to
723 that node is returned.
724 */
725
726 /*! \fn CollectionNode *QDocDatabase::addToGroup(const QString &name, Node *node)
727 Looks up the group node named \a name in the collection
728 of all group nodes. If a match is not found, a new group
729 node named \a name is created and inserted into the collection.
730 Then append \a node to the group's members list, and append the
731 group node to the member list of the \a node. The parent of the
732 \a node is not changed by this function. Returns a pointer to
733 the group node.
734 */
735
736 /*! \fn CollectionNode *QDocDatabase::addToModule(const QString &name, Node *node)
737 Looks up the module node named \a name in the collection
738 of all module nodes. If a match is not found, a new module
739 node named \a name is created and inserted into the collection.
740 Then append \a node to the module's members list. The parent of
741 \a node is not changed by this function. Returns the module node.
742 */
743
744 /*! \fn Collection *QDocDatabase::addToQmlModule(const QString &name, Node *node)
745 Looks up the QML module named \a name. If it isn't there,
746 create it. Then append \a node to the QML module's member
747 list. The parent of \a node is not changed by this function.
748 */
749
750 /*! \fn Collection *QDocDatabase::addToJsModule(const QString &name, Node *node)
751 Looks up the JavaScript module named \a name. If it isn't there,
752 create it. Then append \a node to the JavaScript module's member
753 list. The parent of \a node is not changed by this function.
754 */
755
756 /*!
757 Looks up the QML type node identified by the qualified Qml
758 type \a name and returns a pointer to the QML type node.
759 */
findQmlType(const QString & name)760 QmlTypeNode *QDocDatabase::findQmlType(const QString &name)
761 {
762 QmlTypeNode *qcn = forest_.lookupQmlType(name);
763 if (qcn)
764 return qcn;
765 return nullptr;
766 }
767
768 /*!
769 Looks up the QML type node identified by the Qml module id
770 \a qmid and QML type \a name and returns a pointer to the
771 QML type node. The key is \a qmid + "::" + \a name.
772
773 If the QML module id is empty, it looks up the QML type by
774 \a name only.
775 */
findQmlType(const QString & qmid,const QString & name)776 QmlTypeNode *QDocDatabase::findQmlType(const QString &qmid, const QString &name)
777 {
778 if (!qmid.isEmpty()) {
779 QString t = qmid + "::" + name;
780 QmlTypeNode *qcn = forest_.lookupQmlType(t);
781 if (qcn)
782 return qcn;
783 }
784
785 QStringList path(name);
786 Node *n = forest_.findNodeByNameAndType(path, &Node::isQmlType);
787 if (n && (n->isQmlType() || n->isJsType()))
788 return static_cast<QmlTypeNode *>(n);
789 return nullptr;
790 }
791
792 /*!
793 Looks up the QML basic type node identified by the Qml module id
794 \a qmid and QML basic type \a name and returns a pointer to the
795 QML basic type node. The key is \a qmid + "::" + \a name.
796
797 If the QML module id is empty, it looks up the QML basic type by
798 \a name only.
799 */
findQmlBasicType(const QString & qmid,const QString & name)800 Aggregate *QDocDatabase::findQmlBasicType(const QString &qmid, const QString &name)
801 {
802 if (!qmid.isEmpty()) {
803 QString t = qmid + "::" + name;
804 Aggregate *a = forest_.lookupQmlBasicType(t);
805 if (a)
806 return a;
807 }
808
809 QStringList path(name);
810 Node *n = forest_.findNodeByNameAndType(path, &Node::isQmlBasicType);
811 if (n && n->isQmlBasicType())
812 return static_cast<Aggregate *>(n);
813 return nullptr;
814 }
815
816 /*!
817 Looks up the QML type node identified by the Qml module id
818 constructed from the strings in the \a import record and the
819 QML type \a name and returns a pointer to the QML type node.
820 If a QML type node is not found, 0 is returned.
821 */
findQmlType(const ImportRec & import,const QString & name)822 QmlTypeNode *QDocDatabase::findQmlType(const ImportRec &import, const QString &name)
823 {
824 if (!import.isEmpty()) {
825 QStringList dotSplit;
826 dotSplit = name.split(QLatin1Char('.'));
827 QString qmName;
828 if (import.importUri_.isEmpty())
829 qmName = import.name_;
830 else
831 qmName = import.importUri_;
832 for (int i = 0; i < dotSplit.size(); ++i) {
833 QString qualifiedName = qmName + "::" + dotSplit[i];
834 QmlTypeNode *qcn = forest_.lookupQmlType(qualifiedName);
835 if (qcn)
836 return qcn;
837 }
838 }
839 return nullptr;
840 }
841
842 /*!
843 This function calls a set of functions for each tree in the
844 forest that has not already been analyzed. In this way, when
845 running qdoc in \e singleExec mode, each tree is analyzed in
846 turn, and its classes and types are added to the appropriate
847 node maps.
848 */
processForest()849 void QDocDatabase::processForest()
850 {
851 Tree *t = forest_.firstTree();
852 while (t) {
853 findAllClasses(t->root());
854 findAllFunctions(t->root());
855 findAllObsoleteThings(t->root());
856 findAllLegaleseTexts(t->root());
857 findAllSince(t->root());
858 findAllAttributions(t->root());
859 t->setTreeHasBeenAnalyzed();
860 t = forest_.nextTree();
861 }
862 resolveNamespaces();
863 }
864
865 /*!
866 This function calls \a func for each tree in the forest,
867 but only if Tree::treeHasBeenAnalyzed() returns false for
868 the tree. In this way, when running qdoc in \e singleExec
869 mode, each tree is analyzed in turn, and its classes and
870 types are added to the appropriate node maps.
871 */
processForest(void (QDocDatabase::* func)(Aggregate *))872 void QDocDatabase::processForest(void (QDocDatabase::*func)(Aggregate *))
873 {
874 Tree *t = forest_.firstTree();
875 while (t) {
876 if (!t->treeHasBeenAnalyzed()) {
877 (this->*(func))(t->root());
878 }
879 t = forest_.nextTree();
880 }
881 }
882
883 /*!
884 Constructs the collection of legalese texts, if it has not
885 already been constructed, and returns a reference to it.
886 */
getLegaleseTexts()887 TextToNodeMap &QDocDatabase::getLegaleseTexts()
888 {
889 if (legaleseTexts_.isEmpty())
890 processForest(&QDocDatabase::findAllLegaleseTexts);
891 return legaleseTexts_;
892 }
893
894 /*!
895 Construct the data structures for obsolete things, if they
896 have not already been constructed. Returns a reference to
897 the map of C++ classes with obsolete members.
898 */
getClassesWithObsoleteMembers()899 NodeMultiMap &QDocDatabase::getClassesWithObsoleteMembers()
900 {
901 if (obsoleteClasses_.isEmpty() && obsoleteQmlTypes_.isEmpty())
902 processForest(&QDocDatabase::findAllObsoleteThings);
903 return classesWithObsoleteMembers_;
904 }
905
906 /*!
907 Construct the data structures for obsolete things, if they
908 have not already been constructed. Returns a reference to
909 the map of obsolete QML types.
910 */
getObsoleteQmlTypes()911 NodeMultiMap &QDocDatabase::getObsoleteQmlTypes()
912 {
913 if (obsoleteClasses_.isEmpty() && obsoleteQmlTypes_.isEmpty())
914 processForest(&QDocDatabase::findAllObsoleteThings);
915 return obsoleteQmlTypes_;
916 }
917
918 /*!
919 Construct the data structures for obsolete things, if they
920 have not already been constructed. Returns a reference to
921 the map of QML types with obsolete members.
922 */
getQmlTypesWithObsoleteMembers()923 NodeMultiMap &QDocDatabase::getQmlTypesWithObsoleteMembers()
924 {
925 if (obsoleteClasses_.isEmpty() && obsoleteQmlTypes_.isEmpty())
926 processForest(&QDocDatabase::findAllObsoleteThings);
927 return qmlTypesWithObsoleteMembers_;
928 }
929
930 /*! \fn NodeMultiMap &QDocDatabase::getNamespaces()
931 Returns a reference to the map of all namespace nodes.
932 This function must not be called in the -prepare phase.
933 */
934
935 /*!
936 Construct the data structures for QML basic types, if they
937 have not already been constructed. Returns a reference to
938 the map of QML basic types.
939 */
getQmlBasicTypes()940 NodeMultiMap &QDocDatabase::getQmlBasicTypes()
941 {
942 if (cppClasses_.isEmpty() && qmlBasicTypes_.isEmpty())
943 processForest(&QDocDatabase::findAllClasses);
944 return qmlBasicTypes_;
945 }
946
947 /*!
948 Construct the data structures for QML types, if they
949 have not already been constructed. Returns a reference to
950 the multimap of QML types.
951 */
getQmlTypes()952 NodeMultiMap &QDocDatabase::getQmlTypes()
953 {
954 if (cppClasses_.isEmpty() && qmlTypes_.isEmpty())
955 processForest(&QDocDatabase::findAllClasses);
956 return qmlTypes_;
957 }
958
959 /*!
960 Construct the data structures for examples, if they
961 have not already been constructed. Returns a reference to
962 the multimap of example nodes.
963 */
getExamples()964 NodeMultiMap &QDocDatabase::getExamples()
965 {
966 if (cppClasses_.isEmpty() && examples_.isEmpty())
967 processForest(&QDocDatabase::findAllClasses);
968 return examples_;
969 }
970
971 /*!
972 Construct the data structures for attributions, if they
973 have not already been constructed. Returns a reference to
974 the multimap of attribution nodes.
975 */
getAttributions()976 NodeMultiMap &QDocDatabase::getAttributions()
977 {
978 if (attributions_.isEmpty())
979 processForest(&QDocDatabase::findAllAttributions);
980 return attributions_;
981 }
982
983 /*!
984 Construct the data structures for obsolete things, if they
985 have not already been constructed. Returns a reference to
986 the map of obsolete C++ clases.
987 */
getObsoleteClasses()988 NodeMultiMap &QDocDatabase::getObsoleteClasses()
989 {
990 if (obsoleteClasses_.isEmpty() && obsoleteQmlTypes_.isEmpty())
991 processForest(&QDocDatabase::findAllObsoleteThings);
992 return obsoleteClasses_;
993 }
994
995 /*!
996 Construct the C++ class data structures, if they have not
997 already been constructed. Returns a reference to the map
998 of all C++ classes.
999 */
getCppClasses()1000 NodeMultiMap &QDocDatabase::getCppClasses()
1001 {
1002 if (cppClasses_.isEmpty() && qmlTypes_.isEmpty())
1003 processForest(&QDocDatabase::findAllClasses);
1004 return cppClasses_;
1005 }
1006
1007 /*!
1008 Construct the function index data structure and return it.
1009 This data structure is used to output the function index page.
1010 */
getFunctionIndex()1011 NodeMapMap &QDocDatabase::getFunctionIndex()
1012 {
1013 if (functionIndex_.isEmpty())
1014 processForest(&QDocDatabase::findAllFunctions);
1015 return functionIndex_;
1016 }
1017
1018 /*!
1019 Finds all the nodes containing legalese text and puts them
1020 in a map.
1021 */
findAllLegaleseTexts(Aggregate * node)1022 void QDocDatabase::findAllLegaleseTexts(Aggregate *node)
1023 {
1024 for (auto it = node->constBegin(); it != node->constEnd(); ++it) {
1025 if (!(*it)->isPrivate()) {
1026 if (!(*it)->doc().legaleseText().isEmpty())
1027 legaleseTexts_.insert((*it)->doc().legaleseText(), *it);
1028 if ((*it)->isAggregate())
1029 findAllLegaleseTexts(static_cast<Aggregate *>(*it));
1030 }
1031 }
1032 }
1033
1034 /*!
1035 \fn void QDocDatabase::findAllObsoleteThings(Aggregate *node)
1036
1037 Finds all nodes with status = Obsolete and sorts them into
1038 maps. They can be C++ classes, QML types, or they can be
1039 functions, enum types, typedefs, methods, etc.
1040 */
1041
1042 /*!
1043 \fn void QDocDatabase::findAllSince(Aggregate *node)
1044
1045 Finds all the nodes in \a node where a \e{since} command appeared
1046 in the qdoc comment and sorts them into maps according to the kind
1047 of node.
1048
1049 This function is used for generating the "New Classes... in x.y"
1050 section on the \e{What's New in Qt x.y} page.
1051 */
1052
1053 /*!
1054 Find the \a key in the map of new class maps, and return a
1055 reference to the value, which is a NodeMap. If \a key is not
1056 found, return a reference to an empty NodeMap.
1057 */
getClassMap(const QString & key)1058 const NodeMap &QDocDatabase::getClassMap(const QString &key)
1059 {
1060 if (newSinceMaps_.isEmpty() && newClassMaps_.isEmpty() && newQmlTypeMaps_.isEmpty())
1061 processForest(&QDocDatabase::findAllSince);
1062 auto it = newClassMaps_.constFind(key);
1063 if (it != newClassMaps_.constEnd())
1064 return it.value();
1065 return emptyNodeMap_;
1066 }
1067
1068 /*!
1069 Find the \a key in the map of new QML type maps, and return a
1070 reference to the value, which is a NodeMap. If the \a key is not
1071 found, return a reference to an empty NodeMap.
1072 */
getQmlTypeMap(const QString & key)1073 const NodeMap &QDocDatabase::getQmlTypeMap(const QString &key)
1074 {
1075 if (newSinceMaps_.isEmpty() && newClassMaps_.isEmpty() && newQmlTypeMaps_.isEmpty())
1076 processForest(&QDocDatabase::findAllSince);
1077 auto it = newQmlTypeMaps_.constFind(key);
1078 if (it != newQmlTypeMaps_.constEnd())
1079 return it.value();
1080 return emptyNodeMap_;
1081 }
1082
1083 /*!
1084 Find the \a key in the map of new \e {since} maps, and return
1085 a reference to the value, which is a NodeMultiMap. If \a key
1086 is not found, return a reference to an empty NodeMultiMap.
1087 */
getSinceMap(const QString & key)1088 const NodeMap &QDocDatabase::getSinceMap(const QString &key)
1089 {
1090 if (newSinceMaps_.isEmpty() && newClassMaps_.isEmpty() && newQmlTypeMaps_.isEmpty())
1091 processForest(&QDocDatabase::findAllSince);
1092 auto it = newSinceMaps_.constFind(key);
1093 if (it != newSinceMaps_.constEnd())
1094 return it.value();
1095 return emptyNodeMultiMap_;
1096 }
1097
1098 /*!
1099 Performs several housekeeping tasks prior to generating the
1100 documentation. These tasks create required data structures
1101 and resolve links.
1102 */
resolveStuff()1103 void QDocDatabase::resolveStuff()
1104 {
1105 const auto &config = Config::instance();
1106 if (config.dualExec() || config.preparing()) {
1107 // order matters
1108 primaryTree()->resolveBaseClasses(primaryTreeRoot());
1109 primaryTree()->resolvePropertyOverriddenFromPtrs(primaryTreeRoot());
1110 primaryTreeRoot()->normalizeOverloads();
1111 primaryTree()->markDontDocumentNodes();
1112 primaryTree()->removePrivateAndInternalBases(primaryTreeRoot());
1113 primaryTree()->resolveProperties();
1114 primaryTreeRoot()->markUndocumentedChildrenInternal();
1115 primaryTreeRoot()->resolveQmlInheritance();
1116 primaryTree()->resolveTargets(primaryTreeRoot());
1117 primaryTree()->resolveCppToQmlLinks();
1118 primaryTree()->resolveUsingClauses();
1119 }
1120 if (config.singleExec() && config.generating()) {
1121 primaryTree()->resolveBaseClasses(primaryTreeRoot());
1122 primaryTree()->resolvePropertyOverriddenFromPtrs(primaryTreeRoot());
1123 primaryTreeRoot()->resolveQmlInheritance();
1124 primaryTree()->resolveCppToQmlLinks();
1125 primaryTree()->resolveUsingClauses();
1126 }
1127 if (config.generating()) {
1128 resolveNamespaces();
1129 resolveProxies();
1130 resolveBaseClasses();
1131 }
1132 if (config.dualExec())
1133 QDocIndexFiles::destroyQDocIndexFiles();
1134 }
1135
resolveBaseClasses()1136 void QDocDatabase::resolveBaseClasses()
1137 {
1138 Tree *t = forest_.firstTree();
1139 while (t) {
1140 t->resolveBaseClasses(t->root());
1141 t = forest_.nextTree();
1142 }
1143 }
1144
1145 /*!
1146 Returns a reference to the namespace map. Constructs the
1147 namespace map if it hasn't been constructed yet.
1148 */
getNamespaces()1149 NodeMultiMap &QDocDatabase::getNamespaces()
1150 {
1151 resolveNamespaces();
1152 return namespaceIndex_;
1153 }
1154
1155 /*!
1156 Multiple namespace nodes for namespace X can exist in the
1157 qdoc database in different trees. This function first finds
1158 all namespace nodes in all the trees and inserts them into
1159 a multimap. Then it combines all the namespace nodes that
1160 have the same name into a single namespace node of that
1161 name and inserts that combined namespace node into an index.
1162 */
resolveNamespaces()1163 void QDocDatabase::resolveNamespaces()
1164 {
1165 if (!namespaceIndex_.isEmpty())
1166 return;
1167 NodeMultiMap namespaceMultimap;
1168 Tree *t = forest_.firstTree();
1169 while (t) {
1170 t->root()->findAllNamespaces(namespaceMultimap);
1171 t = forest_.nextTree();
1172 }
1173 const QList<QString> keys = namespaceMultimap.uniqueKeys();
1174 for (const QString &key : keys) {
1175 NamespaceNode *ns = nullptr;
1176 NamespaceNode *somewhere = nullptr;
1177 const NodeList namespaces = namespaceMultimap.values(key);
1178 int count = namespaceMultimap.remove(key);
1179 if (count > 0) {
1180 for (auto *node : namespaces) {
1181 ns = static_cast<NamespaceNode *>(node);
1182 if (ns->isDocumentedHere())
1183 break;
1184 else if (ns->hadDoc())
1185 somewhere = ns;
1186 ns = nullptr;
1187 }
1188 if (ns) {
1189 for (auto *node : namespaces) {
1190 NamespaceNode *NS = static_cast<NamespaceNode *>(node);
1191 if (NS->hadDoc() && NS != ns) {
1192 ns->doc().location().warning(
1193 tr("Namespace %1 documented more than once").arg(NS->name()));
1194 NS->doc().location().warning(tr("...also seen here"));
1195 }
1196 }
1197
1198 } else if (somewhere == nullptr) {
1199 for (auto *node : namespaces) {
1200 NamespaceNode *NS = static_cast<NamespaceNode *>(node);
1201 NS->reportDocumentedChildrenInUndocumentedNamespace();
1202 }
1203 }
1204 if (somewhere) {
1205 for (auto *node : namespaces) {
1206 NamespaceNode *NS = static_cast<NamespaceNode *>(node);
1207 if (NS != somewhere)
1208 NS->setDocNode(somewhere);
1209 }
1210 }
1211 }
1212 /*
1213 If there are multiple namespace nodes with the same
1214 name and one of them will be the reference page for
1215 the namespace, include all the nodes in the public
1216 API of the namespace in the single namespace node
1217 that will generate the namespace reference page for
1218 the namespace.
1219 */
1220 if (ns && count > 1) {
1221 for (auto *node : namespaces) {
1222 auto *nameSpaceNode = static_cast<NamespaceNode *>(node);
1223 if (nameSpaceNode != ns) {
1224 for (auto it = nameSpaceNode->constBegin(); it != nameSpaceNode->constEnd();
1225 ++it) {
1226 Node *N = *it;
1227 if (N && N->isPublic() && !N->isInternal())
1228 ns->includeChild(N);
1229 }
1230 }
1231 }
1232 }
1233 if (ns == nullptr)
1234 ns = static_cast<NamespaceNode *>(namespaces.at(0));
1235 namespaceIndex_.insert(ns->name(), ns);
1236 }
1237 }
1238
1239 /*!
1240 Each instance of class Tree that represents an index file
1241 must be traversed to find all instances of class ProxyNode.
1242 For each ProxyNode found, look up the ProxyNode's name in
1243 the primary Tree. If it is found, it means that the proxy
1244 node contains elements (normally just functions) that are
1245 documented in the module represented by the Tree containing
1246 the proxy node but that are related to the node we found in
1247 the primary tree.
1248 */
resolveProxies()1249 void QDocDatabase::resolveProxies()
1250 {
1251 // The first tree is the primary tree.
1252 // Skip the primary tree.
1253 Tree *t = forest_.firstTree();
1254 t = forest_.nextTree();
1255 while (t) {
1256 const NodeList &proxies = t->proxies();
1257 if (!proxies.isEmpty()) {
1258 for (auto *node : proxies) {
1259 ProxyNode *pn = static_cast<ProxyNode *>(node);
1260 if (pn->count() > 0) {
1261 Aggregate *aggregate = primaryTree()->findAggregate(pn->name());
1262 if (aggregate != nullptr)
1263 aggregate->appendToRelatedByProxy(pn->childNodes());
1264 }
1265 }
1266 }
1267 t = forest_.nextTree();
1268 }
1269 }
1270
1271 /*!
1272 Finds the function node for the qualified function path in
1273 \a target and returns a pointer to it. The \a target is a
1274 function signature with or without parameters but without
1275 the return type.
1276
1277 \a relative is the node in the primary tree where the search
1278 begins. It is not used in the other trees, if the node is not
1279 found in the primary tree. \a genus can be used to force the
1280 search to find a C++ function or a QML function.
1281
1282 The entire forest is searched, but the first match is accepted.
1283 */
findFunctionNode(const QString & target,const Node * relative,Node::Genus genus)1284 const FunctionNode *QDocDatabase::findFunctionNode(const QString &target, const Node *relative,
1285 Node::Genus genus)
1286 {
1287 QString signature;
1288 QString function = target;
1289 int length = target.length();
1290 if (function.endsWith("()"))
1291 function.chop(2);
1292 if (function.endsWith(QChar(')'))) {
1293 int position = function.lastIndexOf(QChar('('));
1294 signature = function.mid(position + 1, length - position - 2);
1295 function = function.left(position);
1296 }
1297 QStringList path = function.split("::");
1298 return forest_.findFunctionNode(path, Parameters(signature), relative, genus);
1299 }
1300
1301 /*!
1302 This function is called for autolinking to a \a type,
1303 which could be a function return type or a parameter
1304 type. The tree node that represents the \a type is
1305 returned. All the trees are searched until a match is
1306 found. When searching the primary tree, the search
1307 begins at \a relative and proceeds up the parent chain.
1308 When searching the index trees, the search begins at the
1309 root.
1310 */
findTypeNode(const QString & type,const Node * relative,Node::Genus genus)1311 const Node *QDocDatabase::findTypeNode(const QString &type, const Node *relative, Node::Genus genus)
1312 {
1313 QStringList path = type.split("::");
1314 if ((path.size() == 1) && (path.at(0)[0].isLower() || path.at(0) == QString("T"))) {
1315 auto it = typeNodeMap_.find(path.at(0));
1316 if (it != typeNodeMap_.end())
1317 return it.value();
1318 }
1319 return forest_.findTypeNode(path, relative, genus);
1320 }
1321
1322 /*!
1323 Finds the node that will generate the documentation that
1324 contains the \a target and returns a pointer to it.
1325
1326 Can this be improved by using the target map in Tree?
1327 */
findNodeForTarget(const QString & target,const Node * relative)1328 const Node *QDocDatabase::findNodeForTarget(const QString &target, const Node *relative)
1329 {
1330 const Node *node = nullptr;
1331 if (target.isEmpty())
1332 node = relative;
1333 else if (target.endsWith(".html"))
1334 node = findNodeByNameAndType(QStringList(target), &Node::isPageNode);
1335 else {
1336 QStringList path = target.split("::");
1337 int flags = SearchBaseClasses | SearchEnumValues;
1338 for (const auto *tree : searchOrder()) {
1339 const Node *n = tree->findNode(path, relative, flags, Node::DontCare);
1340 if (n)
1341 return n;
1342 relative = nullptr;
1343 }
1344 node = findPageNodeByTitle(target);
1345 }
1346 return node;
1347 }
1348
1349 /*!
1350 Generates a tag file and writes it to \a name.
1351 */
generateTagFile(const QString & name,Generator * g)1352 void QDocDatabase::generateTagFile(const QString &name, Generator *g)
1353 {
1354 if (!name.isEmpty()) {
1355 QDocTagFiles::qdocTagFiles()->generateTagFile(name, g);
1356 QDocTagFiles::destroyQDocTagFiles();
1357 }
1358 }
1359
1360 /*!
1361 Reads and parses the qdoc index files listed in \a indexFiles.
1362 */
readIndexes(const QStringList & indexFiles)1363 void QDocDatabase::readIndexes(const QStringList &indexFiles)
1364 {
1365 QStringList filesToRead;
1366 for (const QString &file : indexFiles) {
1367 QString fn = file.mid(file.lastIndexOf(QChar('/')) + 1);
1368 if (!isLoaded(fn))
1369 filesToRead << file;
1370 else
1371 qDebug() << "This index file is already in memory:" << file;
1372 }
1373 QDocIndexFiles::qdocIndexFiles()->readIndexes(filesToRead);
1374 }
1375
1376 /*!
1377 Generates a qdoc index file and write it to \a fileName. The
1378 index file is generated with the parameters \a url and \a title,
1379 using the generator \a g.
1380 */
generateIndex(const QString & fileName,const QString & url,const QString & title,Generator * g)1381 void QDocDatabase::generateIndex(const QString &fileName, const QString &url, const QString &title,
1382 Generator *g)
1383 {
1384 QString t = fileName.mid(fileName.lastIndexOf(QChar('/')) + 1);
1385 primaryTree()->setIndexFileName(t);
1386 QDocIndexFiles::qdocIndexFiles()->generateIndex(fileName, url, title, g);
1387 QDocIndexFiles::destroyQDocIndexFiles();
1388 }
1389
1390 /*!
1391 Find a node of the specified \a type that is reached with
1392 the specified \a path qualified with the name of one of the
1393 open namespaces (might not be any open ones). If the node
1394 is found in an open namespace, prefix \a path with the name
1395 of the open namespace and "::" and return a pointer to the
1396 node. Othewrwise return 0.
1397
1398 This function only searches in the current primary tree.
1399 */
findNodeInOpenNamespace(QStringList & path,bool (Node::* isMatch)()const)1400 Node *QDocDatabase::findNodeInOpenNamespace(QStringList &path, bool (Node::*isMatch)() const)
1401 {
1402 if (path.isEmpty())
1403 return nullptr;
1404 Node *n = nullptr;
1405 if (!openNamespaces_.isEmpty()) {
1406 const auto &openNamespaces = openNamespaces_;
1407 for (const QString &t : openNamespaces) {
1408 QStringList p;
1409 if (t != path[0])
1410 p = t.split("::") + path;
1411 else
1412 p = path;
1413 n = primaryTree()->findNodeByNameAndType(p, isMatch);
1414 if (n) {
1415 path = p;
1416 break;
1417 }
1418 }
1419 }
1420 return n;
1421 }
1422
1423 /*!
1424 Finds all the collection nodes of the specified \a type
1425 and merges them into the collection node map \a cnm. Nodes
1426 that match the \a relative node are not included.
1427 */
mergeCollections(Node::NodeType type,CNMap & cnm,const Node * relative)1428 void QDocDatabase::mergeCollections(Node::NodeType type, CNMap &cnm, const Node *relative)
1429 {
1430 cnm.clear();
1431 CNMultiMap cnmm;
1432 for (auto *tree : searchOrder()) {
1433 CNMap *m = tree->getCollectionMap(type);
1434 if (m && !m->isEmpty()) {
1435 for (auto it = m->cbegin(); it != m->cend(); ++it) {
1436 if (!it.value()->isInternal())
1437 cnmm.insert(it.key(), it.value());
1438 }
1439 }
1440 }
1441 if (cnmm.isEmpty())
1442 return;
1443 QRegExp singleDigit("\\b([0-9])\\b");
1444 const QStringList keys = cnmm.uniqueKeys();
1445 for (const auto &key : keys) {
1446 const QList<CollectionNode *> values = cnmm.values(key);
1447 CollectionNode *n = nullptr;
1448 for (auto *value : values) {
1449 if (value && value->wasSeen() && value != relative) {
1450 n = value;
1451 break;
1452 }
1453 }
1454 if (n) {
1455 if (values.size() > 1) {
1456 for (CollectionNode *value : values) {
1457 if (value != n) {
1458 // Allow multiple (major) versions of QML/JS modules
1459 if ((n->isQmlModule() || n->isJsModule())
1460 && n->logicalModuleIdentifier() != value->logicalModuleIdentifier()) {
1461 if (value->wasSeen() && value != relative
1462 && !value->members().isEmpty())
1463 cnm.insert(value->fullTitle().toLower(), value);
1464 continue;
1465 }
1466 for (Node *t : value->members())
1467 n->addMember(t);
1468 }
1469 }
1470 }
1471 if (!n->members().isEmpty()) {
1472 QString sortKey = n->fullTitle().toLower();
1473 if (sortKey.startsWith("the "))
1474 sortKey.remove(0, 4);
1475 sortKey.replace(singleDigit, "0\\1");
1476 cnm.insert(sortKey, n);
1477 }
1478 }
1479 }
1480 }
1481
1482 /*!
1483 Finds all the collection nodes with the same name
1484 and type as \a c and merges their members into the
1485 members list of \a c.
1486
1487 For QML and JS modules, only nodes with matching
1488 module identifiers are merged to avoid merging
1489 modules with different (major) versions.
1490 */
mergeCollections(CollectionNode * c)1491 void QDocDatabase::mergeCollections(CollectionNode *c)
1492 {
1493 for (auto *tree : searchOrder()) {
1494 CollectionNode *cn = tree->getCollection(c->name(), c->nodeType());
1495 if (cn && cn != c) {
1496 if ((cn->isQmlModule() || cn->isJsModule())
1497 && cn->logicalModuleIdentifier() != c->logicalModuleIdentifier())
1498 continue;
1499 for (auto *node : cn->members())
1500 c->addMember(node);
1501 }
1502 }
1503 }
1504
1505 /*!
1506 Searches for the node that matches the path in \a atom. The
1507 \a relative node is used if the first leg of the path is
1508 empty, i.e. if the path begins with a hashtag. The function
1509 also sets \a ref if there remains an unused leg in the path
1510 after the node is found. The node is returned as well as the
1511 \a ref. If the returned node pointer is null, \a ref is not
1512 valid.
1513 */
findNodeForAtom(const Atom * a,const Node * relative,QString & ref)1514 const Node *QDocDatabase::findNodeForAtom(const Atom *a, const Node *relative, QString &ref)
1515 {
1516 const Node *node = nullptr;
1517
1518 Atom *atom = const_cast<Atom *>(a);
1519 QStringList targetPath = atom->string().split(QLatin1Char('#'));
1520 QString first = targetPath.first().trimmed();
1521
1522 Tree *domain = nullptr;
1523 Node::Genus genus = Node::DontCare;
1524 // Reserved for future use
1525 // Node::NodeType goal = Node::NoType;
1526
1527 if (atom->isLinkAtom()) {
1528 domain = atom->domain();
1529 genus = atom->genus();
1530 // Reserved for future use
1531 // goal = atom->goal();
1532 }
1533
1534 if (first.isEmpty())
1535 node = relative; // search for a target on the current page.
1536 else if (domain) {
1537 if (first.endsWith(".html"))
1538 node = domain->findNodeByNameAndType(QStringList(first), &Node::isPageNode);
1539 else if (first.endsWith(QChar(')'))) {
1540 QString signature;
1541 QString function = first;
1542 int length = first.length();
1543 if (function.endsWith("()"))
1544 function.chop(2);
1545 if (function.endsWith(QChar(')'))) {
1546 int position = function.lastIndexOf(QChar('('));
1547 signature = function.mid(position + 1, length - position - 2);
1548 function = function.left(position);
1549 }
1550 QStringList path = function.split("::");
1551 node = domain->findFunctionNode(path, Parameters(signature), nullptr, genus);
1552 }
1553 if (node == nullptr) {
1554 int flags = SearchBaseClasses | SearchEnumValues;
1555 QStringList nodePath = first.split("::");
1556 QString target;
1557 targetPath.removeFirst();
1558 if (!targetPath.isEmpty())
1559 target = targetPath.takeFirst();
1560 if (relative && relative->tree()->physicalModuleName() != domain->physicalModuleName())
1561 relative = nullptr;
1562 return domain->findNodeForTarget(nodePath, target, relative, flags, genus, ref);
1563 }
1564 } else {
1565 if (first.endsWith(".html"))
1566 node = findNodeByNameAndType(QStringList(first), &Node::isPageNode);
1567 else if (first.endsWith(QChar(')')))
1568 node = findFunctionNode(first, relative, genus);
1569 if (node == nullptr)
1570 return findNodeForTarget(targetPath, relative, genus, ref);
1571 }
1572
1573 if (node != nullptr && ref.isEmpty()) {
1574 if (!node->url().isEmpty())
1575 return node;
1576 targetPath.removeFirst();
1577 if (!targetPath.isEmpty()) {
1578 ref = node->root()->tree()->getRef(targetPath.first(), node);
1579 if (ref.isEmpty())
1580 node = nullptr;
1581 }
1582 }
1583 return node;
1584 }
1585
1586 QT_END_NAMESPACE
1587