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 &parameters, 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