qdoc: Implements the -no-link-errors option
[profile/ivi/qtbase.git] / src / tools / qdoc / qdocdatabase.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the tools applications of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
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 Digia.  For licensing terms and
14 ** conditions see http://qt.digia.com/licensing.  For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights.  These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file.  Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "generator.h"
43 #include "atom.h"
44 #include "tree.h"
45 #include "qdocdatabase.h"
46 #include "qdoctagfiles.h"
47 #include "qdocindexfiles.h"
48 #include <qdebug.h>
49
50 QT_BEGIN_NAMESPACE
51
52 static NodeMap emptyNodeMap_;
53 static NodeMultiMap emptyNodeMultiMap_;
54
55 /*! \class QDocDatabase
56  */
57
58 QDocDatabase* QDocDatabase::qdocDB_ = NULL;
59
60 /*!
61   Constructs the singleton qdoc database object.
62   It constructs a singleton Tree object with this
63   qdoc database pointer.
64  */
65 QDocDatabase::QDocDatabase()
66 {
67     tree_ = new Tree(this);
68 }
69
70 /*!
71   Destroys the qdoc database object. This requires deleting
72   the tree of nodes, which deletes each node.
73  */
74 QDocDatabase::~QDocDatabase()
75 {
76     masterMap_.clear();
77     delete tree_;
78 }
79
80 /*! \fn Tree* QDocDatabase::tree()
81   Returns the pointer to the tree. This function is for compatibility
82   with the current qdoc. It will be removed when the QDocDatabase class
83   replaces the current structures.
84  */
85
86 /*!
87   Creates the singleton. Allows only one instance of the class
88   to be created. Returns a pointer to the singleton.
89 */
90 QDocDatabase* QDocDatabase::qdocDB()
91 {
92    if (!qdocDB_)
93       qdocDB_ = new QDocDatabase;
94    return qdocDB_;
95 }
96
97 /*!
98   Destroys the singleton.
99  */
100 void QDocDatabase::destroyQdocDB()
101 {
102     if (qdocDB_) {
103         delete qdocDB_;
104         qdocDB_ = 0;
105     }
106 }
107
108 /*!
109   \fn const DocNodeMap& QDocDatabase::modules() const
110   Returns a const reference to the collection of all
111   module nodes.
112 */
113
114 /*!
115   \fn const DocNodeMap& QDocDatabase::qmlModules() const
116   Returns a const reference to the collection of all
117   QML module nodes.
118 */
119
120 /*!
121   Looks up the module node named \a name in the collection
122   of all module nodes. If a match is found, a pointer to the
123   node is returned. Otherwise, a new module node named \a name
124   is created and inserted into the collection, and the pointer
125   to that node is returned.
126  */
127 DocNode* QDocDatabase::addModule(const QString& name)
128 {
129     return findModule(name,true);
130 }
131
132 /*!
133   Looks up the QML module node named \a name in the collection
134   of all QML module nodes. If a match is found, a pointer to the
135   node is returned. Otherwise, a new QML module node named \a name
136   is created and inserted into the collection, and the pointer
137   to that node is returned.
138  */
139 DocNode* QDocDatabase::addQmlModule(const QString& name)
140 {
141     return findQmlModule(name,true);
142 }
143
144 /*!
145   Looks up the C++ module named \a moduleName. If it isn't
146   there, create it. Then append \a node to the module's child
147   list. The parent of \a node is not changed by this function.
148   Returns the module node.
149  */
150 DocNode* QDocDatabase::addToModule(const QString& moduleName, Node* node)
151 {
152     DocNode* dn = findModule(moduleName,true);
153     dn->addMember(node);
154     node->setModuleName(moduleName);
155     return dn;
156 }
157
158 /*!
159   Looks up the QML module named \a qmlModuleName. If it isn't
160   there, create it. Then append \a node to the module's child
161   list. The parent of \a node is not changed by this function.
162   Returns a pointer to the QML module node.
163  */
164 DocNode* QDocDatabase::addToQmlModule(const QString& qmlModuleName, Node* node)
165 {
166     DocNode* dn = findQmlModule(qmlModuleName,true);
167     dn->addMember(node);
168     node->setQmlModuleInfo(qmlModuleName);
169     if (node->subType() == Node::QmlClass) {
170         QString t = node->qmlModuleIdentifier() + "::" + node->name();
171         QmlClassNode* n = static_cast<QmlClassNode*>(node);
172         if (!qmlTypeMap_.contains(t))
173             qmlTypeMap_.insert(t,n);
174         if (!masterMap_.contains(t))
175             masterMap_.insert(t,node);
176         if (!masterMap_.contains(node->name(),node))
177             masterMap_.insert(node->name(),node);
178     }
179     return dn;
180 }
181
182 /*!
183   Find the module node named \a name and return a pointer
184   to it. If a matching node is not found and \a addIfNotFound
185   is true, add a new module node named \a name and return
186   a pointer to that one. Otherwise, return 0.
187
188   If a new module node is added, its parent is the tree root,
189   but the new module node is not added to the child list of the
190   tree root.
191  */
192 DocNode* QDocDatabase::findModule(const QString& name, bool addIfNotFound)
193 {
194     DocNodeMap::const_iterator i = modules_.find(name);
195     if (i != modules_.end()) {
196         return i.value();
197     }
198     if (addIfNotFound) {
199         DocNode* dn = new DocNode(tree_->root(), name, Node::Module, Node::OverviewPage);
200         modules_.insert(name,dn);
201         if (!masterMap_.contains(name,dn))
202             masterMap_.insert(name,dn);
203         return dn;
204     }
205     return 0;
206 }
207
208 /*!
209   Find the QML module node named \a name and return a pointer
210   to it. If a matching node is not found and \a addIfNotFound
211   is true, add a new QML module node named \a name and return
212   a pointer to that one. Otherwise, return 0.
213
214   If a new QML module node is added, its parent is the tree root,
215   but the new QML module node is not added to the child list of
216   the tree root.
217  */
218 DocNode* QDocDatabase::findQmlModule(const QString& name, bool addIfNotFound)
219 {
220     QStringList dotSplit;
221     QStringList blankSplit = name.split(QLatin1Char(' '));
222     QString qmid = blankSplit[0];
223     if (blankSplit.size() > 1) {
224         dotSplit = blankSplit[1].split(QLatin1Char('.'));
225         qmid += dotSplit[0];
226     }
227     DocNode* dn = 0;
228     if (qmlModules_.contains(qmid))
229         dn = qmlModules_.value(qmid);
230     else if (addIfNotFound) {
231         dn = new DocNode(tree_->root(), name, Node::QmlModule, Node::OverviewPage);
232         dn->setQmlModuleInfo(name);
233         qmlModules_.insert(qmid,dn);
234         masterMap_.insert(qmid,dn);
235         masterMap_.insert(dn->name(),dn);
236     }
237     return dn;
238 }
239
240 /*!
241   Looks up the QML type node identified by the Qml module id
242   \a qmid and QML type \a name and returns a pointer to the
243   QML type node. The key is \a qmid + "::" + \a name.
244
245   If the QML module id is empty, it looks up the QML type by
246   \a name only.
247  */
248 QmlClassNode* QDocDatabase::findQmlType(const QString& qmid, const QString& name) const
249 {
250     if (!qmid.isEmpty())
251         return qmlTypeMap_.value(qmid + "::" + name);
252
253     QStringList path(name);
254     Node* n = tree_->findNodeByNameAndType(path, Node::Document, Node::QmlClass, 0, true);
255     if (n) {
256         if (n->subType() == Node::QmlClass)
257             return static_cast<QmlClassNode*>(n);
258         else if (n->subType() == Node::Collision) {
259             NameCollisionNode* ncn;
260             ncn = static_cast<NameCollisionNode*>(n);
261             return static_cast<QmlClassNode*>(ncn->findAny(Node::Document,Node::QmlClass));
262         }
263     }
264     return 0;
265
266 }
267
268 /*!
269   For debugging only.
270  */
271 void QDocDatabase::printModules() const
272 {
273     DocNodeMap::const_iterator i = modules_.begin();
274     while (i != modules_.end()) {
275         qDebug() << "  " << i.key();
276         ++i;
277     }
278 }
279
280 /*!
281   For debugging only.
282  */
283 void QDocDatabase::printQmlModules() const
284 {
285     DocNodeMap::const_iterator i = qmlModules_.begin();
286     while (i != qmlModules_.end()) {
287         qDebug() << "  " << i.key();
288         ++i;
289     }
290 }
291
292 /*!
293   Traverses the database to construct useful data structures
294   for use when outputting certain significant collections of
295   things, C++ classes, QML types, "since" lists, and other
296   stuff.
297  */
298 void QDocDatabase::buildCollections()
299 {
300     nonCompatClasses_.clear();
301     mainClasses_.clear();
302     compatClasses_.clear();
303     obsoleteClasses_.clear();
304     funcIndex_.clear();
305     legaleseTexts_.clear();
306     serviceClasses_.clear();
307     qmlClasses_.clear();
308
309     findAllClasses(treeRoot());
310     findAllFunctions(treeRoot());
311     findAllLegaleseTexts(treeRoot());
312     findAllNamespaces(treeRoot());
313     findAllSince(treeRoot());
314 }
315
316 /*!
317   Finds all the C++ class nodes and QML type nodes and
318   sorts them into maps.
319  */
320 void QDocDatabase::findAllClasses(const InnerNode* node)
321 {
322     NodeList::const_iterator c = node->childNodes().constBegin();
323     while (c != node->childNodes().constEnd()) {
324         if ((*c)->access() != Node::Private && (*c)->url().isEmpty()) {
325             if ((*c)->type() == Node::Class && !(*c)->doc().isEmpty()) {
326                 QString className = (*c)->name();
327                 if ((*c)->parent() &&
328                         (*c)->parent()->type() == Node::Namespace &&
329                         !(*c)->parent()->name().isEmpty())
330                     className = (*c)->parent()->name()+"::"+className;
331
332                 if (!(static_cast<const ClassNode *>(*c))->hideFromMainList()) {
333                     if ((*c)->status() == Node::Compat) {
334                         compatClasses_.insert(className, *c);
335                     }
336                     else if ((*c)->status() == Node::Obsolete) {
337                         obsoleteClasses_.insert(className, *c);
338                     }
339                     else {
340                         nonCompatClasses_.insert(className, *c);
341                         if ((*c)->status() == Node::Main)
342                             mainClasses_.insert(className, *c);
343                     }
344                 }
345
346                 QString serviceName = (static_cast<const ClassNode *>(*c))->serviceName();
347                 if (!serviceName.isEmpty())
348                     serviceClasses_.insert(serviceName, *c);
349             }
350             else if ((*c)->type() == Node::Document &&
351                      (*c)->subType() == Node::QmlClass &&
352                      !(*c)->doc().isEmpty()) {
353                 QString qmlTypeName = (*c)->name();
354                 if (qmlTypeName.startsWith(QLatin1String("QML:")))
355                     qmlClasses_.insert(qmlTypeName.mid(4),*c);
356                 else
357                     qmlClasses_.insert(qmlTypeName,*c);
358             }
359             else if ((*c)->isInnerNode()) {
360                 findAllClasses(static_cast<InnerNode*>(*c));
361             }
362         }
363         ++c;
364     }
365 }
366
367 /*!
368   Finds all the function nodes
369  */
370 void QDocDatabase::findAllFunctions(const InnerNode* node)
371 {
372     NodeList::ConstIterator c = node->childNodes().constBegin();
373     while (c != node->childNodes().constEnd()) {
374         if ((*c)->access() != Node::Private) {
375             if ((*c)->isInnerNode() && (*c)->url().isEmpty()) {
376                 findAllFunctions(static_cast<const InnerNode*>(*c));
377             }
378             else if ((*c)->type() == Node::Function) {
379                 const FunctionNode* func = static_cast<const FunctionNode*>(*c);
380                 if ((func->status() > Node::Obsolete) &&
381                         !func->isInternal() &&
382                         (func->metaness() != FunctionNode::Ctor) &&
383                         (func->metaness() != FunctionNode::Dtor)) {
384                     funcIndex_[(*c)->name()].insert((*c)->parent()->fullDocumentName(), *c);
385                 }
386             }
387         }
388         ++c;
389     }
390 }
391
392 /*!
393   Finds all the nodes containing legalese text and puts them
394   in a map.
395  */
396 void QDocDatabase::findAllLegaleseTexts(const InnerNode* node)
397 {
398     NodeList::ConstIterator c = node->childNodes().constBegin();
399     while (c != node->childNodes().constEnd()) {
400         if ((*c)->access() != Node::Private) {
401             if (!(*c)->doc().legaleseText().isEmpty())
402                 legaleseTexts_.insertMulti((*c)->doc().legaleseText(), *c);
403             if ((*c)->isInnerNode())
404                 findAllLegaleseTexts(static_cast<const InnerNode *>(*c));
405         }
406         ++c;
407     }
408 }
409
410 /*!
411   Finds all the namespace nodes and puts them in an index.
412  */
413 void QDocDatabase::findAllNamespaces(const InnerNode* node)
414 {
415     NodeList::ConstIterator c = node->childNodes().constBegin();
416     while (c != node->childNodes().constEnd()) {
417         if ((*c)->access() != Node::Private) {
418             if ((*c)->isInnerNode() && (*c)->url().isEmpty()) {
419                 findAllNamespaces(static_cast<const InnerNode *>(*c));
420                 if ((*c)->type() == Node::Namespace) {
421                     const NamespaceNode* nspace = static_cast<const NamespaceNode *>(*c);
422                     // Ensure that the namespace's name is not empty (the root
423                     // namespace has no name).
424                     if (!nspace->name().isEmpty()) {
425                         namespaceIndex_.insert(nspace->name(), *c);
426                     }
427                 }
428             }
429         }
430         ++c;
431     }
432 }
433
434 /*!
435   Finds all the nodes where a \e{since} command appeared in the
436   qdoc comment and sorts them into maps according to the kind of
437   node.
438
439   This function is used for generating the "New Classes... in x.y"
440   section on the \e{What's New in Qt x.y} page.
441  */
442 void QDocDatabase::findAllSince(const InnerNode* node)
443 {
444     NodeList::const_iterator child = node->childNodes().constBegin();
445     while (child != node->childNodes().constEnd()) {
446         QString sinceString = (*child)->since();
447         // Insert a new entry into each map for each new since string found.
448         if (((*child)->access() != Node::Private) && !sinceString.isEmpty()) {
449             NodeMultiMapMap::iterator nsmap = newSinceMaps_.find(sinceString);
450             if (nsmap == newSinceMaps_.end())
451                 nsmap = newSinceMaps_.insert(sinceString,NodeMultiMap());
452
453             NodeMapMap::iterator ncmap = newClassMaps_.find(sinceString);
454             if (ncmap == newClassMaps_.end())
455                 ncmap = newClassMaps_.insert(sinceString,NodeMap());
456
457             NodeMapMap::iterator nqcmap = newQmlTypeMaps_.find(sinceString);
458             if (nqcmap == newQmlTypeMaps_.end())
459                 nqcmap = newQmlTypeMaps_.insert(sinceString,NodeMap());
460
461             if ((*child)->type() == Node::Function) {
462                 // Insert functions into the general since map.
463                 FunctionNode *func = static_cast<FunctionNode *>(*child);
464                 if ((func->status() > Node::Obsolete) &&
465                     (func->metaness() != FunctionNode::Ctor) &&
466                     (func->metaness() != FunctionNode::Dtor)) {
467                     nsmap.value().insert(func->name(),(*child));
468                 }
469             }
470             else if ((*child)->url().isEmpty()) {
471                 if ((*child)->type() == Node::Class && !(*child)->doc().isEmpty()) {
472                     // Insert classes into the since and class maps.
473                     QString className = (*child)->name();
474                     if ((*child)->parent() && (*child)->parent()->type() == Node::Namespace &&
475                         !(*child)->parent()->name().isEmpty()) {
476                         className = (*child)->parent()->name()+"::"+className;
477                     }
478                     nsmap.value().insert(className,(*child));
479                     ncmap.value().insert(className,(*child));
480                 }
481                 else if ((*child)->subType() == Node::QmlClass) {
482                     // Insert QML elements into the since and element maps.
483                     QString className = (*child)->name();
484                     if ((*child)->parent() && (*child)->parent()->type() == Node::Namespace &&
485                         !(*child)->parent()->name().isEmpty()) {
486                         className = (*child)->parent()->name()+"::"+className;
487                     }
488                     nsmap.value().insert(className,(*child));
489                     nqcmap.value().insert(className,(*child));
490                 }
491                 else if ((*child)->type() == Node::QmlProperty) {
492                     // Insert QML properties into the since map.
493                     QString propertyName = (*child)->name();
494                     nsmap.value().insert(propertyName,(*child));
495                 }
496             }
497             else {
498                 // Insert external documents into the general since map.
499                 QString name = (*child)->name();
500                 if ((*child)->parent() && (*child)->parent()->type() == Node::Namespace &&
501                     !(*child)->parent()->name().isEmpty()) {
502                     name = (*child)->parent()->name()+"::"+name;
503                 }
504                 nsmap.value().insert(name,(*child));
505             }
506
507             // Recursively find child nodes with since commands.
508             if ((*child)->isInnerNode()) {
509                 findAllSince(static_cast<InnerNode *>(*child));
510             }
511         }
512         ++child;
513     }
514 }
515
516 /*!
517   Find the \a key in the map of new class maps, and return a
518   reference to the value, which is a NodeMap. If \a key is not
519   found, return a reference to an empty NodeMap.
520  */
521 const NodeMap& QDocDatabase::getClassMap(const QString& key) const
522 {
523     NodeMapMap::const_iterator i = newClassMaps_.constFind(key);
524     if (i != newClassMaps_.constEnd())
525         return i.value();
526     return emptyNodeMap_;
527 }
528
529 /*!
530   Find the \a key in the map of new QML type maps, and return a
531   reference to the value, which is a NodeMap. If the \a key is not
532   found, return a reference to an empty NodeMap.
533  */
534 const NodeMap& QDocDatabase::getQmlTypeMap(const QString& key) const
535 {
536     NodeMapMap::const_iterator i = newQmlTypeMaps_.constFind(key);
537     if (i != newQmlTypeMaps_.constEnd())
538         return i.value();
539     return emptyNodeMap_;
540 }
541
542 /*!
543   Find the \a key in the map of new \e {since} maps, and return
544   a reference to the value, which is a NodeMultiMap. If \a key
545   is not found, return a reference to an empty NodeMultiMap.
546  */
547 const NodeMultiMap& QDocDatabase::getSinceMap(const QString& key) const
548 {
549     NodeMultiMapMap::const_iterator i = newSinceMaps_.constFind(key);
550     if (i != newSinceMaps_.constEnd())
551         return i.value();
552     return emptyNodeMultiMap_;
553 }
554
555 /*!
556   Performs several housekeeping algorithms that create
557   certain data structures and resolve lots of links, prior
558   to generating documentation.
559  */
560 void QDocDatabase::resolveIssues() {
561     tree_->resolveGroups();
562     resolveTargets(treeRoot());
563     tree_->resolveCppToQmlLinks();
564 }
565
566 /*!
567   Look up group \a name in the map of groups. If found, populate
568   the node map \a group with the classes in the group that are
569   not marked internal or private.
570  */
571 void QDocDatabase::getGroup(const QString& name, NodeMap& group) const
572 {
573     group.clear();
574     NodeList values = tree_->groups().values(name);
575     for (int i=0; i<values.size(); ++i) {
576         const Node* n = values.at(i);
577         if ((n->status() != Node::Internal) && (n->access() != Node::Private)) {
578             group.insert(n->nameForLists(),n);
579         }
580     }
581 }
582
583 /*!
584   Searches the \a database for a node named \a target and returns
585   a pointer to it if found.
586  */
587 const Node* QDocDatabase::resolveTarget(const QString& target,
588                                         const Node* relative,
589                                         const Node* self)
590 {
591     const Node* node = 0;
592     if (target.endsWith("()")) {
593         QString funcName = target;
594         funcName.chop(2);
595         QStringList path = funcName.split("::");
596         const FunctionNode* fn = tree_->findFunctionNode(path, relative, SearchBaseClasses);
597         if (fn) {
598             /*
599               Why is this case not accepted?
600              */
601             if (fn->metaness() != FunctionNode::MacroWithoutParams)
602                 node = fn;
603         }
604     }
605     else if (target.contains(QLatin1Char('#'))) {
606         // This error message is never printed; I think we can remove the case.
607         qDebug() << "qdoc: target case not handled:" << target;
608     }
609     else {
610         QStringList path = target.split("::");
611         int flags = SearchBaseClasses | SearchEnumValues | NonFunction;
612         node = tree_->findNode(path, relative, flags, self);
613     }
614     return node;
615 }
616
617 /*!
618   Finds the node that will generate the documentation that
619   contains the \a target and returns a pointer to it.
620  */
621 const Node* QDocDatabase::findNodeForTarget(const QString& target, const Node* relative)
622 {
623     const Node* node = 0;
624     if (target.isEmpty())
625         node = relative;
626     else if (target.endsWith(".html"))
627         node = tree_->root()->findChildNodeByNameAndType(target, Node::Document);
628     else {
629         node = resolveTarget(target, relative);
630         if (!node)
631             node = findDocNodeByTitle(target, relative);
632     }
633     return node;
634 }
635
636 /*!
637   Inserts a new target into the target table with the specified
638   \a name, \a node, and \a priority.
639  */
640 void QDocDatabase::insertTarget(const QString& name, Node* node, int priority)
641 {
642     Target target;
643     target.node_ = node;
644     target.priority_ = priority;
645     Atom a = Atom(Atom::Target, name);
646     target.ref_ = refForAtom(&a);
647     targetMultiMap_.insert(name, target);
648 }
649
650 /*!
651   This function searches for a \a target anchor node. If it
652   finds one, it sets \a ref and returns the found node.
653  */
654 const Node*
655 QDocDatabase::findUnambiguousTarget(const QString& target, QString& ref, const Node* relative)
656 {
657     Target bestTarget;
658     int numBestTargets = 0;
659     QList<Target> bestTargetList;
660
661     QString key = Doc::canonicalTitle(target);
662     TargetMultiMap::iterator i = targetMultiMap_.find(key);
663     while (i != targetMultiMap_.end()) {
664         if (i.key() != key)
665             break;
666         const Target& candidate = i.value();
667         if (candidate.priority_ < bestTarget.priority_) {
668             bestTarget = candidate;
669             bestTargetList.clear();
670             bestTargetList.append(candidate);
671             numBestTargets = 1;
672         } else if (candidate.priority_ == bestTarget.priority_) {
673             bestTargetList.append(candidate);
674             ++numBestTargets;
675         }
676         ++i;
677     }
678     if (numBestTargets > 0) {
679         if (numBestTargets == 1) {
680             ref = bestTarget.ref_;
681             return bestTarget.node_;
682         }
683         else if (bestTargetList.size() > 1) {
684             if (relative && !relative->qmlModuleIdentifier().isEmpty()) {
685                 for (int i=0; i<bestTargetList.size(); ++i) {
686                     const Node* n = bestTargetList.at(i).node_;
687                     if (n && relative->qmlModuleIdentifier() == n->qmlModuleIdentifier()) {
688                         ref = bestTargetList.at(i).ref_;
689                         return n;
690                     }
691                 }
692             }
693         }
694     }
695     ref.clear();
696     return 0;
697 }
698
699 /*!
700   This function searches for a node with the specified \a title.
701   If \a relative node is provided, it is used to disambiguate if
702   it has a QML module identifier.
703  */
704 const DocNode* QDocDatabase::findDocNodeByTitle(const QString& title, const Node* relative) const
705 {
706     QString key = Doc::canonicalTitle(title);
707     DocNodeMultiMap::const_iterator i = docNodesByTitle_.constFind(key);
708     if (i != docNodesByTitle_.constEnd()) {
709         if (relative && !relative->qmlModuleIdentifier().isEmpty()) {
710             const DocNode* dn = i.value();
711             InnerNode* parent = dn->parent();
712             if (parent && parent->type() == Node::Document && parent->subType() == Node::Collision) {
713                 const NodeList& nl = parent->childNodes();
714                 NodeList::ConstIterator it = nl.constBegin();
715                 while (it != nl.constEnd()) {
716                     if ((*it)->qmlModuleIdentifier() == relative->qmlModuleIdentifier()) {
717                         /*
718                           By returning here, we avoid printing all the duplicate
719                           header warnings, which are not really duplicates now,
720                           because of the QML module identifier being used as a
721                           namespace qualifier.
722                         */
723                         dn = static_cast<const DocNode*>(*it);
724                         return dn;
725                     }
726                     ++it;
727                 }
728             }
729         }
730         /*
731           Reporting all these duplicate section titles is probably
732           overkill. We should report the duplicate file and let
733           that suffice.
734         */
735         DocNodeMultiMap::const_iterator j = i;
736         ++j;
737         if (j != docNodesByTitle_.constEnd() && j.key() == i.key()) {
738             QList<Location> internalLocations;
739             while (j != docNodesByTitle_.constEnd()) {
740                 if (j.key() == i.key() && j.value()->url().isEmpty())
741                     internalLocations.append(j.value()->location());
742                 ++j;
743             }
744             if (internalLocations.size() > 0) {
745                 i.value()->location().warning(tr("This page exists in more than one file: \"%1\"").arg(title));
746                 foreach (const Location &location, internalLocations)
747                     location.warning(tr("[It also exists here]"));
748             }
749         }
750         return i.value();
751     }
752     return 0;
753 }
754
755 /*!
756   This function searches for a node with a canonical title
757   constructed from \a target. If the node it finds is \a node,
758   it returns the ref from that node. Otherwise it returns an
759   empty string.
760  */
761 QString QDocDatabase::findTarget(const QString& target, const Node* node) const
762 {
763     QString key = Doc::canonicalTitle(target);
764     TargetMultiMap::const_iterator i = targetMultiMap_.constFind(key);
765
766     if (i != targetMultiMap_.constEnd()) {
767         do {
768             if (i.value().node_ == node)
769                 return i.value().ref_;
770             ++i;
771         } while (i != targetMultiMap_.constEnd() && i.key() == key);
772     }
773     return QString();
774 }
775
776 /*!
777  */
778 void QDocDatabase::resolveTargets(InnerNode* root)
779 {
780     // need recursion
781
782     foreach (Node* child, root->childNodes()) {
783         if (child->type() == Node::Document) {
784             DocNode* node = static_cast<DocNode*>(child);
785             if (!node->title().isEmpty()) {
786                 QString key = Doc::canonicalTitle(node->title());
787                 docNodesByTitle_.insert(key, node);
788             }
789             if (node->subType() == Node::Collision) {
790                 resolveTargets(node);
791             }
792         }
793
794         if (child->doc().hasTableOfContents()) {
795             const QList<Atom*>& toc = child->doc().tableOfContents();
796             Target target;
797             target.node_ = child;
798             target.priority_ = 3;
799
800             for (int i = 0; i < toc.size(); ++i) {
801                 target.ref_ = refForAtom(toc.at(i));
802                 QString title = Text::sectionHeading(toc.at(i)).toString();
803                 if (!title.isEmpty()) {
804                     QString key = Doc::canonicalTitle(title);
805                     targetMultiMap_.insert(key, target);
806                 }
807             }
808         }
809         if (child->doc().hasKeywords()) {
810             const QList<Atom*>& keywords = child->doc().keywords();
811             Target target;
812             target.node_ = child;
813             target.priority_ = 1;
814
815             for (int i = 0; i < keywords.size(); ++i) {
816                 target.ref_ = refForAtom(keywords.at(i));
817                 QString key = Doc::canonicalTitle(keywords.at(i)->string());
818                 targetMultiMap_.insert(key, target);
819             }
820         }
821         if (child->doc().hasTargets()) {
822             const QList<Atom*>& toc = child->doc().targets();
823             Target target;
824             target.node_ = child;
825             target.priority_ = 2;
826
827             for (int i = 0; i < toc.size(); ++i) {
828                 target.ref_ = refForAtom(toc.at(i));
829                 QString key = Doc::canonicalTitle(toc.at(i)->string());
830                 targetMultiMap_.insert(key, target);
831             }
832         }
833     }
834 }
835
836 /*!
837   Generates a tag file and writes it to \a name.
838  */
839 void QDocDatabase::generateTagFile(const QString& name, Generator* g)
840 {
841     if (!name.isEmpty()) {
842         QDocTagFiles::qdocTagFiles()->generateTagFile(name, g);
843         QDocTagFiles::destroyQDocTagFiles();
844     }
845 }
846
847 /*!
848   Reads and parses the qdoc index files listed in \a indexFiles.
849  */
850 void QDocDatabase::readIndexes(const QStringList& indexFiles)
851 {
852     QDocIndexFiles::qdocIndexFiles()->readIndexes(indexFiles);
853     QDocIndexFiles::destroyQDocIndexFiles();
854 }
855
856 /*!
857   Generates a qdoc index file and write it to \a fileName. The
858   index file is generated with the parameters \a url, \a title,
859   \a g, and \a generateInternalNodes.
860  */
861 void QDocDatabase::generateIndex(const QString& fileName,
862                                  const QString& url,
863                                  const QString& title,
864                                  Generator* g,
865                                  bool generateInternalNodes)
866 {
867     QDocIndexFiles::qdocIndexFiles()->generateIndex(fileName, url, title, g, generateInternalNodes);
868     QDocIndexFiles::destroyQDocIndexFiles();
869 }
870
871 QString QDocDatabase::refForAtom(const Atom* atom)
872 {
873     if (atom) {
874         if (atom->type() == Atom::SectionLeft)
875             return Doc::canonicalTitle(Text::sectionHeading(atom).toString());
876         if (atom->type() == Atom::Target)
877             return Doc::canonicalTitle(atom->string());
878     }
879     return QString();
880 }
881
882 QT_END_NAMESPACE