qdoc: findNodeRecursive() was called with a null start node
[profile/ivi/qtbase.git] / src / tools / qdoc / tree.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the tools applications of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 /*
43   tree.cpp
44 */
45
46 #include <QDomDocument>
47 #include "atom.h"
48 #include "doc.h"
49 #include "htmlgenerator.h"
50 #include "location.h"
51 #include "node.h"
52 #include "text.h"
53 #include "tree.h"
54 #include <limits.h>
55 #include <qdebug.h>
56
57 QT_BEGIN_NAMESPACE
58
59 struct InheritanceBound
60 {
61     Node::Access access;
62     QStringList basePath;
63     QString dataTypeWithTemplateArgs;
64     InnerNode* parent;
65
66     InheritanceBound()
67         : access(Node::Public) { }
68     InheritanceBound(Node::Access access0,
69                      const QStringList& basePath0,
70                      const QString& dataTypeWithTemplateArgs0,
71                      InnerNode* parent)
72         : access(access0), basePath(basePath0),
73           dataTypeWithTemplateArgs(dataTypeWithTemplateArgs0),
74           parent(parent) { }
75 };
76
77 struct Target
78 {
79     Node* node;
80     Atom* atom;
81     int priority;
82 };
83
84 typedef QMap<PropertyNode::FunctionRole, QString> RoleMap;
85 typedef QMap<PropertyNode*, RoleMap> PropertyMap;
86 typedef QMultiHash<QString, FakeNode*> FakeNodeHash;
87 typedef QMultiHash<QString, Target> TargetHash;
88
89 class TreePrivate
90 {
91 public:
92     QMap<ClassNode* , QList<InheritanceBound> > unresolvedInheritanceMap;
93     PropertyMap unresolvedPropertyMap;
94     NodeMultiMap groupMap;
95     NodeMultiMap qmlModuleMap;
96     QMultiMap<QString, QString> publicGroupMap;
97     FakeNodeHash fakeNodesByTitle;
98     TargetHash targetHash;
99     QList<QPair<ClassNode*,QString> > basesList;
100     QList<QPair<FunctionNode*,QString> > relatedList;
101 };
102
103 /*!
104   \class Tree
105
106   This class constructs and maintains a tree of instances of
107   Node and its many subclasses.
108  */
109
110 /*!
111   The default constructor is the only constructor.
112  */
113 Tree::Tree()
114     : roo(0, "")
115 {
116     priv = new TreePrivate;
117 }
118
119 /*!
120   The destructor deletes the internal, private tree.
121  */
122 Tree::~Tree()
123 {
124     delete priv;
125 }
126
127 // 1 calls 2
128 /*!
129   Searches the tree for a node that matches the \a path. The
130   search begins at \a start but can move up the parent chain
131   recursively if no match is found.
132  */
133 const Node* Tree::findNode(const QStringList& path,
134                            const Node* start,
135                            int findFlags,
136                            const Node* self) const
137 {
138     const Node* current = start;
139     if (!current)
140         current = root();
141
142     /*
143       First, search for a node assuming we don't want a QML node.
144       If that search fails, search again assuming we do want a
145       QML node.
146      */
147     const Node* n = findNode(path,current,findFlags,self,false);
148     if (!n) {
149         n = findNode(path,current,findFlags,self,true);
150     }
151     return n;
152 }
153
154 // 2 is private; it is only called by 1.
155 /*!
156   This overload function was extracted from the one above that has the
157   same signature without the last bool parameter, \a qml. This version
158   is called only by that other one. It is therefore private.  It can
159   be called a second time by that other version, if the first call
160   returns null. If \a qml is false, the search will only match a node
161   that is not a QML node.  If \a qml is true, the search will only
162   match a node that is a QML node.
163 */
164 const Node* Tree::findNode(const QStringList& path,
165                            const Node* start,
166                            int findFlags,
167                            const Node* self,
168                            bool qml) const
169 {
170     const Node* current = start;
171     do {
172         const Node* node = current;
173         int i;
174         int start_idx = 0;
175
176         /*
177           If the path contains one or two double colons ("::"),
178           check first to see if the first two path strings refer
179           to a QML element. If yes, that reference identifies a
180           QML class node.
181         */
182         if (qml && path.size() >= 2) {
183             QmlClassNode* qcn = QmlClassNode::moduleMap.value(path[0]+ "::" +path[1]);
184             if (qcn) {
185                 node = qcn;
186                 if (path.size() == 2)
187                     return node;
188                 start_idx = 2;
189             }
190         }
191
192         for (i = start_idx; i < path.size(); ++i) {
193             if (node == 0 || !node->isInnerNode())
194                 break;
195
196             const Node* next = static_cast<const InnerNode*>(node)->findChildNodeByName(path.at(i), qml);
197
198             if (!next && (findFlags & SearchEnumValues) && i == path.size()-1)
199                 next = static_cast<const InnerNode*>(node)->findEnumNodeForValue(path.at(i));
200
201             if (!next && !qml && node->type() == Node::Class && (findFlags & SearchBaseClasses)) {
202                 NodeList baseClasses = allBaseClasses(static_cast<const ClassNode*>(node));
203                 foreach (const Node* baseClass, baseClasses) {
204                     next = static_cast<const InnerNode*>(baseClass)->findChildNodeByName(path.at(i));
205                     if (!next && (findFlags & SearchEnumValues) && i == path.size() - 1)
206                         next = static_cast<const InnerNode*>(baseClass)->findEnumNodeForValue(path.at(i));
207                     if (next)
208                         break;
209                 }
210             }
211             node = next;
212         }
213         if (node && i == path.size()
214                 && (!(findFlags & NonFunction) || node->type() != Node::Function
215                     || ((FunctionNode*)node)->metaness() == FunctionNode::MacroWithoutParams)) {
216             if ((node != self) && (node->subType() != Node::QmlPropertyGroup)) {
217                 if (node->subType() == Node::Collision) {
218                     node = node->applyModuleIdentifier(start);
219                 }
220                 return node;
221             }
222         }
223         current = current->parent();
224     } while (current);
225
226     return 0;
227 }
228
229 /*!
230   Find the QML class node for the specified \a module and \a name
231   identifiers. The \a module identifier may be empty. If the module
232   identifier is empty, then begin by finding the FakeNode that has
233   the specified \a name. If that FakeNode is a QML class, return it.
234   If it is a collision node, return its current child, if the current
235   child is a QML class. If the collision node does not have a child
236   that is a QML class node, return 0.
237  */
238 QmlClassNode* Tree::findQmlClassNode(const QString& module, const QString& name)
239 {
240     if (module.isEmpty()) {
241         Node* n = findQmlClassNode(QStringList(name));
242         if (n) {
243             if (n->subType() == Node::QmlClass)
244                 return static_cast<QmlClassNode*>(n);
245             else if (n->subType() == Node::Collision) {
246                 NameCollisionNode* ncn;
247                 ncn = static_cast<NameCollisionNode*>(n);
248                 return static_cast<QmlClassNode*>(ncn->findAny(Node::Fake,Node::QmlClass));
249             }
250         }
251         return 0;
252     }
253     return QmlClassNode::moduleMap.value(module + "::" + name);
254 }
255
256 /*!
257   First, search for a node with the specified \a name. If a matching
258   node is found, if it is a collision node, another collision with
259   this name has been found, so return the collision node. If the
260   matching node is not a collision node, the first collision for this
261   name has been found, so create a NameCollisionNode with the matching
262   node as its first child, and return a pointer to the new
263   NameCollisionNode. Otherwise return 0.
264  */
265 NameCollisionNode* Tree::checkForCollision(const QString& name) const
266 {
267     Node* n = const_cast<Node*>(findNode(QStringList(name)));
268     if (n) {
269         if (n->subType() == Node::Collision) {
270             NameCollisionNode* ncn = static_cast<NameCollisionNode*>(n);
271             return ncn;
272         }
273         if (n->isInnerNode())
274             return new NameCollisionNode(static_cast<InnerNode*>(n));
275     }
276     return 0;
277 }
278
279 /*!
280   This function is like checkForCollision() in that it searches
281   for a collision node with the specified \a name. But it doesn't
282   create anything. If it finds a match, it returns the pointer.
283   Otherwise it returns 0.
284  */
285 NameCollisionNode* Tree::findCollisionNode(const QString& name) const
286 {
287     Node* n = const_cast<Node*>(findNode(QStringList(name)));
288     if (n) {
289         if (n->subType() == Node::Collision) {
290             NameCollisionNode* ncn = static_cast<NameCollisionNode*>(n);
291             return ncn;
292         }
293     }
294     return 0;
295 }
296
297 /*!
298   This function just calls the const version of the same function
299   and returns the function node.
300  */
301 FunctionNode* Tree::findFunctionNode(const QStringList& path,
302                                      Node* relative,
303                                      int findFlags)
304 {
305     return const_cast<FunctionNode*>
306             (const_cast<const Tree*>(this)->findFunctionNode(path,relative,findFlags));
307 }
308
309 /*!
310   This function begins searching the tree at \a relative for
311   the \l {FunctionNode} {function node} identified by \a path.
312   The \a findFlags are used to restrict the search. If a node
313   that matches the \a path is found, it is returned. Otherwise,
314   0 is returned. If \a relative is 0, the root of the tree is
315   used as the starting point.
316  */
317 const FunctionNode* Tree::findFunctionNode(const QStringList& path,
318                                            const Node* relative,
319                                            int findFlags) const
320 {
321     if (!relative)
322         relative = root();
323
324     /*
325       If the path contains two double colons ("::"), check
326       first to see if it is a reference to a QML method. If
327       it is a reference to a QML method, first look up the
328       QML class node in the QML module map.
329      */
330     if (path.size() == 3) {
331         QmlClassNode* qcn = QmlClassNode::moduleMap.value(path[0]+ "::" +path[1]);
332         if (qcn) {
333             return static_cast<const FunctionNode*>(qcn->findFunctionNode(path[2]));
334         }
335     }
336
337     do {
338         const Node* node = relative;
339         int i;
340
341         for (i = 0; i < path.size(); ++i) {
342             if (node == 0 || !node->isInnerNode())
343                 break;
344
345             const Node* next;
346             if (i == path.size() - 1)
347                 next = ((InnerNode*) node)->findFunctionNode(path.at(i));
348             else
349                 next = ((InnerNode*) node)->findChildNodeByName(path.at(i));
350
351             if (!next && node->type() == Node::Class && (findFlags & SearchBaseClasses)) {
352                 NodeList baseClasses = allBaseClasses(static_cast<const ClassNode*>(node));
353                 foreach (const Node* baseClass, baseClasses) {
354                     if (i == path.size() - 1)
355                         next = static_cast<const InnerNode*>(baseClass)->findFunctionNode(path.at(i));
356                     else
357                         next = static_cast<const InnerNode*>(baseClass)->findChildNodeByName(path.at(i));
358
359                     if (next)
360                         break;
361                 }
362             }
363
364             node = next;
365         }
366         if (node && i == path.size() && node->isFunction()) {
367             // CppCodeParser::processOtherMetaCommand ensures that reimplemented
368             // functions are private.
369             const FunctionNode* func = static_cast<const FunctionNode*>(node);
370             while (func->access() == Node::Private) {
371                 const FunctionNode* from = func->reimplementedFrom();
372                 if (from != 0) {
373                     if (from->access() != Node::Private)
374                         return from;
375                     else
376                         func = from;
377                 }
378                 else
379                     break;
380             }
381             return func;
382         }
383         relative = relative->parent();
384     } while (relative);
385
386     return 0;
387 }
388
389 /*!
390   This function just calls the const version of itself and
391   returns the result.
392  */
393 FunctionNode* Tree::findFunctionNode(const QStringList& parentPath,
394                                      const FunctionNode* clone,
395                                      Node* relative,
396                                      int findFlags)
397 {
398     return const_cast<FunctionNode*>(
399                 const_cast<const Tree*>(this)->findFunctionNode(parentPath,
400                                                                 clone,
401                                                                 relative,
402                                                                 findFlags));
403 }
404
405 /*!
406   This function first ignores the \a clone node and searches
407   for the node having the \a parentPath by calling the main
408   findFunction(\a {parentPath}, \a {relative}, \a {findFlags}).
409   If that search is successful, then it searches for the \a clone
410   in the found parent node.
411  */
412 const FunctionNode* Tree::findFunctionNode(const QStringList& parentPath,
413                                            const FunctionNode* clone,
414                                            const Node* relative,
415                                            int findFlags) const
416 {
417     const Node* parent = findNode(parentPath, relative, findFlags);
418     if (parent == 0 || !parent->isInnerNode()) {
419         return 0;
420     }
421     else {
422         return ((InnerNode*)parent)->findFunctionNode(clone);
423     }
424 }
425 //findNode(parameter.leftType().split("::"), 0, SearchBaseClasses|NonFunction);
426
427 static const int NumSuffixes = 3;
428 static const char*  const suffixes[NumSuffixes] = { "", "s", "es" };
429
430 /*!
431   This function searches for a node with the specified \a title.
432   If \a relative is provided, use it to disambiguate if it has a
433   QML module identifier.
434  */
435 const FakeNode* Tree::findFakeNodeByTitle(const QString& title, const Node* relative ) const
436 {
437     for (int pass = 0; pass < NumSuffixes; ++pass) {
438         FakeNodeHash::const_iterator i = priv->fakeNodesByTitle.find(Doc::canonicalTitle(title + suffixes[pass]));
439         if (i != priv->fakeNodesByTitle.constEnd()) {
440             if (relative && !relative->qmlModuleIdentifier().isEmpty()) {
441                 const FakeNode* fn = i.value();
442                 InnerNode* parent = fn->parent();
443                 if (parent && parent->type() == Node::Fake && parent->subType() == Node::Collision) {
444                     const NodeList& nl = parent->childNodes();
445                     NodeList::ConstIterator it = nl.begin();
446                     while (it != nl.end()) {
447                         if ((*it)->qmlModuleIdentifier() == relative->qmlModuleIdentifier()) {
448                             /*
449                               By returning here, we avoid printing all the duplicate
450                               header warnings, which are not really duplicates now,
451                               because of the QML module identifier being used as a
452                               namespace qualifier.
453                              */
454                             fn = static_cast<const FakeNode*>(*it);
455                             return fn;
456                         }
457                         ++it;
458                     }
459                 }
460             }
461             /*
462               Reporting all these duplicate section titles is probably
463               overkill. We should report the duplicate file and let
464               that suffice.
465              */
466             FakeNodeHash::const_iterator j = i;
467             ++j;
468             if (j != priv->fakeNodesByTitle.constEnd() && j.key() == i.key()) {
469                 QList<Location> internalLocations;
470                 while (j != priv->fakeNodesByTitle.constEnd()) {
471                     if (j.key() == i.key() && j.value()->url().isEmpty())
472                         internalLocations.append(j.value()->doc().location());
473                     ++j;
474                 }
475                 if (internalLocations.size() > 0) {
476                     i.value()->doc().location().warning(
477                                 tr("Page '%1' defined in more than one location:").arg(title));
478                     foreach (const Location &location, internalLocations)
479                         location.warning(tr("(defined here)"));
480                 }
481             }
482             return i.value();
483         }
484     }
485     return 0;
486 }
487
488 /*!
489   This function searches for a \a target anchor node. If it
490   finds one, it sets \a atom from the found node and returns
491   the found node.
492  */
493 const Node*
494 Tree::findUnambiguousTarget(const QString& target, Atom *&atom, const Node* relative) const
495 {
496     Target bestTarget = {0, 0, INT_MAX};
497     int numBestTargets = 0;
498     QList<Target> bestTargetList;
499
500     for (int pass = 0; pass < NumSuffixes; ++pass) {
501         TargetHash::const_iterator i = priv->targetHash.find(Doc::canonicalTitle(target + suffixes[pass]));
502         if (i != priv->targetHash.constEnd()) {
503             TargetHash::const_iterator j = i;
504             do {
505                 const Target& candidate = j.value();
506                 if (candidate.priority < bestTarget.priority) {
507                     bestTarget = candidate;
508                     bestTargetList.clear();
509                     bestTargetList.append(candidate);
510                     numBestTargets = 1;
511                 } else if (candidate.priority == bestTarget.priority) {
512                     bestTargetList.append(candidate);
513                     ++numBestTargets;
514                 }
515                 ++j;
516             } while (j != priv->targetHash.constEnd() && j.key() == i.key());
517
518             if (numBestTargets == 1) {
519                 atom = bestTarget.atom;
520                 return bestTarget.node;
521             }
522             else if (bestTargetList.size() > 1) {
523                 if (relative && !relative->qmlModuleIdentifier().isEmpty()) {
524                     for (int i=0; i<bestTargetList.size(); ++i) {
525                         const Node* n = bestTargetList.at(i).node;
526                         if (relative->qmlModuleIdentifier() == n->qmlModuleIdentifier()) {
527                             atom = bestTargetList.at(i).atom;
528                             return n;
529                         }
530                     }
531                 }
532             }
533         }
534     }
535     return 0;
536 }
537
538 /*!
539   This function searches for a node with a canonical title
540   constructed from \a target and each of the possible suffixes.
541   If the node it finds is \a node, it returns the Atom from that
542   node. Otherwise it returns null.
543  */
544 Atom* Tree::findTarget(const QString& target, const Node* node) const
545 {
546     for (int pass = 0; pass < NumSuffixes; ++pass) {
547         QString key = Doc::canonicalTitle(target + suffixes[pass]);
548         TargetHash::const_iterator i = priv->targetHash.find(key);
549
550         if (i != priv->targetHash.constEnd()) {
551             do {
552                 if (i.value().node == node)
553                     return i.value().atom;
554                 ++i;
555             } while (i != priv->targetHash.constEnd() && i.key() == key);
556         }
557     }
558     return 0;
559 }
560
561 /*!
562  */
563 void Tree::addBaseClass(ClassNode* subclass, Node::Access access,
564                         const QStringList& basePath,
565                         const QString& dataTypeWithTemplateArgs,
566                         InnerNode* parent)
567 {
568     priv->unresolvedInheritanceMap[subclass].append(
569                 InheritanceBound(access,
570                                  basePath,
571                                  dataTypeWithTemplateArgs,
572                                  parent)
573                 );
574 }
575
576 /*!
577  */
578 void Tree::addPropertyFunction(PropertyNode* property,
579                                const QString& funcName,
580                                PropertyNode::FunctionRole funcRole)
581 {
582     priv->unresolvedPropertyMap[property].insert(funcRole, funcName);
583 }
584
585 /*!
586   This function adds the \a node to the \a group. The group
587   can be listed anywhere using the \e{annotated list} command.
588  */
589 void Tree::addToGroup(Node* node, const QString& group)
590 {
591     priv->groupMap.insert(group, node);
592 }
593
594 /*!
595   This function adds the \a node to the QML \a module. The QML
596   module can be listed anywhere using the \e{annotated list}
597   command.
598  */
599 void Tree::addToQmlModule(Node* node, const QString& module)
600 {
601     priv->qmlModuleMap.insert(module, node);
602 }
603
604 /*!
605   Returns the group map.
606  */
607 NodeMultiMap Tree::groups() const
608 {
609     return priv->groupMap;
610 }
611
612 /*!
613   Returns the QML module map.
614  */
615 NodeMultiMap Tree::qmlModules() const
616 {
617     return priv->qmlModuleMap;
618 }
619
620 /*!
621  */
622 void Tree::addToPublicGroup(Node* node, const QString& group)
623 {
624     priv->publicGroupMap.insert(node->name(), group);
625     addToGroup(node, group);
626 }
627
628 /*!
629  */
630 QMultiMap<QString, QString> Tree::publicGroups() const
631 {
632     return priv->publicGroupMap;
633 }
634
635 /*!
636  */
637 void Tree::resolveInheritance(NamespaceNode* rootNode)
638 {
639     if (!rootNode)
640         rootNode = root();
641
642     for (int pass = 0; pass < 2; pass++) {
643         NodeList::ConstIterator c = rootNode->childNodes().begin();
644         while (c != rootNode->childNodes().end()) {
645             if ((*c)->type() == Node::Class) {
646                 resolveInheritance(pass, (ClassNode*)* c);
647             }
648             else if ((*c)->type() == Node::Namespace) {
649                 NamespaceNode* ns = static_cast<NamespaceNode*>(*c);
650                 resolveInheritance(ns);
651             }
652             ++c;
653         }
654         if (rootNode == root())
655             priv->unresolvedInheritanceMap.clear();
656     }
657 }
658
659 /*!
660  */
661 void Tree::resolveProperties()
662 {
663     PropertyMap::ConstIterator propEntry;
664
665     propEntry = priv->unresolvedPropertyMap.begin();
666     while (propEntry != priv->unresolvedPropertyMap.end()) {
667         PropertyNode* property = propEntry.key();
668         InnerNode* parent = property->parent();
669         QString getterName = (*propEntry)[PropertyNode::Getter];
670         QString setterName = (*propEntry)[PropertyNode::Setter];
671         QString resetterName = (*propEntry)[PropertyNode::Resetter];
672         QString notifierName = (*propEntry)[PropertyNode::Notifier];
673
674         NodeList::ConstIterator c = parent->childNodes().begin();
675         while (c != parent->childNodes().end()) {
676             if ((*c)->type() == Node::Function) {
677                 FunctionNode* function = static_cast<FunctionNode*>(*c);
678                 if (function->access() == property->access() &&
679                         (function->status() == property->status() ||
680                          function->doc().isEmpty())) {
681                     if (function->name() == getterName) {
682                         property->addFunction(function, PropertyNode::Getter);
683                     }
684                     else if (function->name() == setterName) {
685                         property->addFunction(function, PropertyNode::Setter);
686                     }
687                     else if (function->name() == resetterName) {
688                         property->addFunction(function, PropertyNode::Resetter);
689                     }
690                     else if (function->name() == notifierName) {
691                         property->addSignal(function, PropertyNode::Notifier);
692                     }
693                 }
694             }
695             ++c;
696         }
697         ++propEntry;
698     }
699
700     propEntry = priv->unresolvedPropertyMap.begin();
701     while (propEntry != priv->unresolvedPropertyMap.end()) {
702         PropertyNode* property = propEntry.key();
703         // redo it to set the property functions
704         if (property->overriddenFrom())
705             property->setOverriddenFrom(property->overriddenFrom());
706         ++propEntry;
707     }
708
709     priv->unresolvedPropertyMap.clear();
710 }
711
712 /*!
713  */
714 void Tree::resolveInheritance(int pass, ClassNode* classe)
715 {
716     if (pass == 0) {
717         QList<InheritanceBound> bounds = priv->unresolvedInheritanceMap[classe];
718         QList<InheritanceBound>::ConstIterator b = bounds.begin();
719         while (b != bounds.end()) {
720             Node* n = findClassNode((*b).basePath);
721             if (!n && (*b).parent) {
722                 n = findClassNode((*b).basePath, (*b).parent);
723             }
724             if (n) {
725                 classe->addBaseClass((*b).access, static_cast<ClassNode*>(n), (*b).dataTypeWithTemplateArgs);
726             }
727             ++b;
728         }
729     }
730     else {
731         NodeList::ConstIterator c = classe->childNodes().begin();
732         while (c != classe->childNodes().end()) {
733             if ((*c)->type() == Node::Function) {
734                 FunctionNode* func = (FunctionNode*)* c;
735                 FunctionNode* from = findVirtualFunctionInBaseClasses(classe, func);
736                 if (from != 0) {
737                     if (func->virtualness() == FunctionNode::NonVirtual)
738                         func->setVirtualness(FunctionNode::ImpureVirtual);
739                     func->setReimplementedFrom(from);
740                 }
741             }
742             else if ((*c)->type() == Node::Property) {
743                 fixPropertyUsingBaseClasses(classe, static_cast<PropertyNode*>(*c));
744             }
745             ++c;
746         }
747     }
748 }
749
750 /*!
751   For each node in the group map, add the node to the appropriate
752   group node.
753  */
754 void Tree::resolveGroups()
755 {
756     NodeMultiMap::const_iterator i;
757     for (i = priv->groupMap.constBegin(); i != priv->groupMap.constEnd(); ++i) {
758         if (i.value()->access() == Node::Private)
759             continue;
760
761         Node* n = findGroupNode(QStringList(i.key()));
762         if (n)
763             n->addGroupMember(i.value());
764     }
765 }
766
767 /*!
768   For each node in the QML module map, add the node to the
769   appropriate QML module node.
770  */
771 void Tree::resolveQmlModules()
772 {
773     NodeMultiMap::const_iterator i;
774     for (i = priv->qmlModuleMap.constBegin(); i != priv->qmlModuleMap.constEnd(); ++i) {
775         Node* n = findQmlModuleNode(QStringList(i.key()));
776         if (n)
777             n->addQmlModuleMember(i.value());
778     }
779 }
780
781 /*!
782  */
783 void Tree::resolveTargets(InnerNode* root)
784 {
785     // need recursion
786
787     foreach (Node* child, root->childNodes()) {
788         if (child->type() == Node::Fake) {
789             FakeNode* node = static_cast<FakeNode*>(child);
790             if (!node->title().isEmpty())
791                 priv->fakeNodesByTitle.insert(Doc::canonicalTitle(node->title()), node);
792             if (node->subType() == Node::Collision) {
793                 resolveTargets(node);
794             }
795         }
796
797         if (child->doc().hasTableOfContents()) {
798             const QList<Atom*>& toc = child->doc().tableOfContents();
799             Target target;
800             target.node = child;
801             target.priority = 3;
802
803             for (int i = 0; i < toc.size(); ++i) {
804                 target.atom = toc.at(i);
805                 QString title = Text::sectionHeading(target.atom).toString();
806                 if (!title.isEmpty())
807                     priv->targetHash.insert(Doc::canonicalTitle(title), target);
808             }
809         }
810         if (child->doc().hasKeywords()) {
811             const QList<Atom*>& keywords = child->doc().keywords();
812             Target target;
813             target.node = child;
814             target.priority = 1;
815
816             for (int i = 0; i < keywords.size(); ++i) {
817                 target.atom = keywords.at(i);
818                 priv->targetHash.insert(Doc::canonicalTitle(target.atom->string()), 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.atom = toc.at(i);
829                 priv->targetHash.insert(Doc::canonicalTitle(target.atom->string()), target);
830             }
831         }
832     }
833 }
834
835 /*!
836   For each QML class node that points to a C++ class node,
837   follow its C++ class node pointer and set the C++ class
838   node's QML class node pointer back to the QML class node.
839  */
840 void Tree::resolveCppToQmlLinks()
841 {
842
843     foreach (Node* child, roo.childNodes()) {
844         if (child->type() == Node::Fake && child->subType() == Node::QmlClass) {
845             QmlClassNode* qcn = static_cast<QmlClassNode*>(child);
846             ClassNode* cn = const_cast<ClassNode*>(qcn->classNode());
847             if (cn)
848                 cn->setQmlElement(qcn);
849         }
850     }
851 }
852
853 /*!
854   For each QML class node in the tree, determine whether
855   it inherits a QML base class and, if so, which one, and
856   store that pointer in the QML class node's state.
857  */
858 void Tree::resolveQmlInheritance()
859 {
860
861     foreach (Node* child, roo.childNodes()) {
862         if (child->type() == Node::Fake) {
863             if (child->subType() == Node::QmlClass) {
864                 QmlClassNode* qcn = static_cast<QmlClassNode*>(child);
865                 qcn->resolveInheritance(this);
866             }
867             else if (child->subType() == Node::Collision) {
868                 NameCollisionNode* ncn = static_cast<NameCollisionNode*>(child);
869                 foreach (Node* child, ncn->childNodes()) {
870                     if (child->type() == Node::Fake) {
871                         if (child->subType() == Node::QmlClass) {
872                             QmlClassNode* qcn = static_cast<QmlClassNode*>(child);
873                             qcn->resolveInheritance(this);
874                         }
875                     }
876                 }
877             }
878         }
879     }
880 }
881
882 /*!
883  */
884 void Tree::fixInheritance(NamespaceNode* rootNode)
885 {
886     if (!rootNode)
887         rootNode = root();
888
889     NodeList::ConstIterator c = rootNode->childNodes().begin();
890     while (c != rootNode->childNodes().end()) {
891         if ((*c)->type() == Node::Class)
892             static_cast<ClassNode*>(*c)->fixBaseClasses();
893         else if ((*c)->type() == Node::Namespace) {
894             NamespaceNode* ns = static_cast<NamespaceNode*>(*c);
895             fixInheritance(ns);
896         }
897         ++c;
898     }
899 }
900
901 /*!
902  */
903 FunctionNode* Tree::findVirtualFunctionInBaseClasses(ClassNode* classe,
904                                                      FunctionNode* clone)
905 {
906     QList<RelatedClass>::ConstIterator r = classe->baseClasses().begin();
907     while (r != classe->baseClasses().end()) {
908         FunctionNode* func;
909         if (((func = findVirtualFunctionInBaseClasses((*r).node, clone)) != 0 ||
910              (func = (*r).node->findFunctionNode(clone)) != 0)) {
911             if (func->virtualness() != FunctionNode::NonVirtual)
912                 return func;
913         }
914         ++r;
915     }
916     return 0;
917 }
918
919 /*!
920  */
921 void Tree::fixPropertyUsingBaseClasses(ClassNode* classe, PropertyNode* property)
922 {
923     QList<RelatedClass>::const_iterator r = classe->baseClasses().begin();
924     while (r != classe->baseClasses().end()) {
925         Node* n = r->node->findChildNodeByNameAndType(property->name(), Node::Property);
926         if (n) {
927             PropertyNode* baseProperty = static_cast<PropertyNode*>(n);
928             fixPropertyUsingBaseClasses(r->node, baseProperty);
929             property->setOverriddenFrom(baseProperty);
930         }
931         else {
932             fixPropertyUsingBaseClasses(r->node, property);
933         }
934         ++r;
935     }
936 }
937
938 /*!
939  */
940 NodeList Tree::allBaseClasses(const ClassNode* classe) const
941 {
942     NodeList result;
943     foreach (const RelatedClass& r, classe->baseClasses()) {
944         result += r.node;
945         result += allBaseClasses(r.node);
946     }
947     return result;
948 }
949
950 /*!
951  */
952 void Tree::readIndexes(const QStringList& indexFiles)
953 {
954     foreach (const QString& indexFile, indexFiles)
955         readIndexFile(indexFile);
956 }
957
958 /*!
959   Read the QDomDocument at \a path and get the index from it.
960  */
961 void Tree::readIndexFile(const QString& path)
962 {
963     QFile file(path);
964     if (file.open(QFile::ReadOnly)) {
965         QDomDocument document;
966         document.setContent(&file);
967         file.close();
968
969         QDomElement indexElement = document.documentElement();
970         QString indexUrl = indexElement.attribute("url", "");
971         priv->basesList.clear();
972         priv->relatedList.clear();
973
974         // Scan all elements in the XML file, constructing a map that contains
975         // base classes for each class found.
976
977         QDomElement child = indexElement.firstChildElement();
978         while (!child.isNull()) {
979             readIndexSection(child, root(), indexUrl);
980             child = child.nextSiblingElement();
981         }
982
983         // Now that all the base classes have been found for this index,
984         // arrange them into an inheritance hierarchy.
985
986         resolveIndex();
987     }
988 }
989
990 /*!
991   Read a <section> element from the index file and create the
992   appropriate node(s).
993  */
994 void Tree::readIndexSection(const QDomElement& element,
995                             InnerNode* parent,
996                             const QString& indexUrl)
997 {
998     QString name = element.attribute("name");
999     QString href = element.attribute("href");
1000
1001     Node* section;
1002     Location location;
1003
1004     if (element.nodeName() == "namespace") {
1005         section = new NamespaceNode(parent, name);
1006
1007         if (!indexUrl.isEmpty())
1008             location = Location(indexUrl + QLatin1Char('/') + name.toLower() + ".html");
1009         else if (!indexUrl.isNull())
1010             location = Location(name.toLower() + ".html");
1011
1012     }
1013     else if (element.nodeName() == "class") {
1014         section = new ClassNode(parent, name);
1015         priv->basesList.append(QPair<ClassNode*,QString>(
1016                                    static_cast<ClassNode*>(section), element.attribute("bases")));
1017
1018         if (!indexUrl.isEmpty())
1019             location = Location(indexUrl + QLatin1Char('/') + name.toLower() + ".html");
1020         else if (!indexUrl.isNull())
1021             location = Location(name.toLower() + ".html");
1022
1023     }
1024     else if ((element.nodeName() == "qmlclass") ||
1025              ((element.nodeName() == "page") && (element.attribute("subtype") == "qmlclass"))) {
1026         QmlClassNode* qcn = new QmlClassNode(parent, name, 0);
1027         qcn->setTitle(element.attribute("title"));
1028         if (element.hasAttribute("location"))
1029             name = element.attribute("location", "");
1030         if (!indexUrl.isEmpty())
1031             location = Location(indexUrl + QLatin1Char('/') + name);
1032         else if (!indexUrl.isNull())
1033             location = Location(name);
1034         section = qcn;
1035     }
1036     else if (element.nodeName() == "qmlbasictype") {
1037         QmlBasicTypeNode* qbtn = new QmlBasicTypeNode(parent, name);
1038         qbtn->setTitle(element.attribute("title"));
1039         if (element.hasAttribute("location"))
1040             name = element.attribute("location", "");
1041         if (!indexUrl.isEmpty())
1042             location = Location(indexUrl + QLatin1Char('/') + name);
1043         else if (!indexUrl.isNull())
1044             location = Location(name);
1045         section = qbtn;
1046     }
1047     else if (element.nodeName() == "page") {
1048         Node::SubType subtype;
1049         Node::PageType ptype = Node::NoPageType;
1050         if (element.attribute("subtype") == "example") {
1051             subtype = Node::Example;
1052             ptype = Node::ExamplePage;
1053         }
1054         else if (element.attribute("subtype") == "header") {
1055             subtype = Node::HeaderFile;
1056             ptype = Node::ApiPage;
1057         }
1058         else if (element.attribute("subtype") == "file") {
1059             subtype = Node::File;
1060             ptype = Node::NoPageType;
1061         }
1062         else if (element.attribute("subtype") == "group") {
1063             subtype = Node::Group;
1064             ptype = Node::OverviewPage;
1065         }
1066         else if (element.attribute("subtype") == "module") {
1067             subtype = Node::Module;
1068             ptype = Node::OverviewPage;
1069         }
1070         else if (element.attribute("subtype") == "page") {
1071             subtype = Node::Page;
1072             ptype = Node::ArticlePage;
1073         }
1074         else if (element.attribute("subtype") == "externalpage") {
1075             subtype = Node::ExternalPage;
1076             ptype = Node::ArticlePage;
1077         }
1078         else if (element.attribute("subtype") == "qmlclass") {
1079             subtype = Node::QmlClass;
1080             ptype = Node::ApiPage;
1081         }
1082         else if (element.attribute("subtype") == "qmlpropertygroup") {
1083             subtype = Node::QmlPropertyGroup;
1084             ptype = Node::ApiPage;
1085         }
1086         else if (element.attribute("subtype") == "qmlbasictype") {
1087             subtype = Node::QmlBasicType;
1088             ptype = Node::ApiPage;
1089         }
1090         else
1091             return;
1092
1093         FakeNode* fakeNode = new FakeNode(parent, name, subtype, ptype);
1094         fakeNode->setTitle(element.attribute("title"));
1095
1096         if (element.hasAttribute("location"))
1097             name = element.attribute("location", "");
1098
1099         if (!indexUrl.isEmpty())
1100             location = Location(indexUrl + QLatin1Char('/') + name);
1101         else if (!indexUrl.isNull())
1102             location = Location(name);
1103
1104         section = fakeNode;
1105
1106     }
1107     else if (element.nodeName() == "enum") {
1108         EnumNode* enumNode = new EnumNode(parent, name);
1109
1110         if (!indexUrl.isEmpty())
1111             location =
1112                     Location(indexUrl + QLatin1Char('/') + parent->name().toLower() + ".html");
1113         else if (!indexUrl.isNull())
1114             location = Location(parent->name().toLower() + ".html");
1115
1116         QDomElement child = element.firstChildElement("value");
1117         while (!child.isNull()) {
1118             EnumItem item(child.attribute("name"), child.attribute("value"));
1119             enumNode->addItem(item);
1120             child = child.nextSiblingElement("value");
1121         }
1122
1123         section = enumNode;
1124
1125     } else if (element.nodeName() == "typedef") {
1126         section = new TypedefNode(parent, name);
1127
1128         if (!indexUrl.isEmpty())
1129             location =
1130                     Location(indexUrl + QLatin1Char('/') + parent->name().toLower() + ".html");
1131         else if (!indexUrl.isNull())
1132             location = Location(parent->name().toLower() + ".html");
1133
1134     }
1135     else if (element.nodeName() == "property") {
1136         section = new PropertyNode(parent, name);
1137
1138         if (!indexUrl.isEmpty())
1139             location =
1140                     Location(indexUrl + QLatin1Char('/') + parent->name().toLower() + ".html");
1141         else if (!indexUrl.isNull())
1142             location = Location(parent->name().toLower() + ".html");
1143
1144     } else if (element.nodeName() == "function") {
1145         FunctionNode::Virtualness virt;
1146         if (element.attribute("virtual") == "non")
1147             virt = FunctionNode::NonVirtual;
1148         else if (element.attribute("virtual") == "impure")
1149             virt = FunctionNode::ImpureVirtual;
1150         else if (element.attribute("virtual") == "pure")
1151             virt = FunctionNode::PureVirtual;
1152         else
1153             return;
1154
1155         FunctionNode::Metaness meta;
1156         if (element.attribute("meta") == "plain")
1157             meta = FunctionNode::Plain;
1158         else if (element.attribute("meta") == "signal")
1159             meta = FunctionNode::Signal;
1160         else if (element.attribute("meta") == "slot")
1161             meta = FunctionNode::Slot;
1162         else if (element.attribute("meta") == "constructor")
1163             meta = FunctionNode::Ctor;
1164         else if (element.attribute("meta") == "destructor")
1165             meta = FunctionNode::Dtor;
1166         else if (element.attribute("meta") == "macro")
1167             meta = FunctionNode::MacroWithParams;
1168         else if (element.attribute("meta") == "macrowithparams")
1169             meta = FunctionNode::MacroWithParams;
1170         else if (element.attribute("meta") == "macrowithoutparams")
1171             meta = FunctionNode::MacroWithoutParams;
1172         else
1173             return;
1174
1175         FunctionNode* functionNode = new FunctionNode(parent, name);
1176         functionNode->setReturnType(element.attribute("return"));
1177         functionNode->setVirtualness(virt);
1178         functionNode->setMetaness(meta);
1179         functionNode->setConst(element.attribute("const") == "true");
1180         functionNode->setStatic(element.attribute("static") == "true");
1181         functionNode->setOverload(element.attribute("overload") == "true");
1182
1183         if (element.hasAttribute("relates")
1184                 && element.attribute("relates") != parent->name()) {
1185             priv->relatedList.append(
1186                         QPair<FunctionNode*,QString>(functionNode,
1187                                                      element.attribute("relates")));
1188         }
1189
1190         QDomElement child = element.firstChildElement("parameter");
1191         while (!child.isNull()) {
1192             // Do not use the default value for the parameter; it is not
1193             // required, and has been known to cause problems.
1194             Parameter parameter(child.attribute("left"),
1195                                 child.attribute("right"),
1196                                 child.attribute("name"),
1197                                 ""); // child.attribute("default")
1198             functionNode->addParameter(parameter);
1199             child = child.nextSiblingElement("parameter");
1200         }
1201
1202         section = functionNode;
1203
1204         if (!indexUrl.isEmpty())
1205             location =
1206                     Location(indexUrl + QLatin1Char('/') + parent->name().toLower() + ".html");
1207         else if (!indexUrl.isNull())
1208             location = Location(parent->name().toLower() + ".html");
1209
1210     }
1211     else if (element.nodeName() == "variable") {
1212         section = new VariableNode(parent, name);
1213
1214         if (!indexUrl.isEmpty())
1215             location = Location(indexUrl + QLatin1Char('/') + parent->name().toLower() + ".html");
1216         else if (!indexUrl.isNull())
1217             location = Location(parent->name().toLower() + ".html");
1218
1219     }
1220     else if (element.nodeName() == "keyword") {
1221         Target target;
1222         target.node = parent;
1223         target.priority = 1;
1224         target.atom = new Atom(Atom::Target, name);
1225         priv->targetHash.insert(name, target);
1226         return;
1227
1228     }
1229     else if (element.nodeName() == "target") {
1230         Target target;
1231         target.node = parent;
1232         target.priority = 2;
1233         target.atom = new Atom(Atom::Target, name);
1234         priv->targetHash.insert(name, target);
1235         return;
1236
1237     }
1238     else if (element.nodeName() == "contents") {
1239         Target target;
1240         target.node = parent;
1241         target.priority = 3;
1242         target.atom = new Atom(Atom::Target, name);
1243         priv->targetHash.insert(name, target);
1244         return;
1245
1246     }
1247     else
1248         return;
1249
1250     QString access = element.attribute("access");
1251     if (access == "public")
1252         section->setAccess(Node::Public);
1253     else if (access == "protected")
1254         section->setAccess(Node::Protected);
1255     else if (access == "private")
1256         section->setAccess(Node::Private);
1257     else
1258         section->setAccess(Node::Public);
1259
1260     if ((element.nodeName() != "page") &&
1261             (element.nodeName() != "qmlclass") &&
1262             (element.nodeName() != "qmlbasictype")) {
1263         QString threadSafety = element.attribute("threadsafety");
1264         if (threadSafety == "non-reentrant")
1265             section->setThreadSafeness(Node::NonReentrant);
1266         else if (threadSafety == "reentrant")
1267             section->setThreadSafeness(Node::Reentrant);
1268         else if (threadSafety == "thread safe")
1269             section->setThreadSafeness(Node::ThreadSafe);
1270         else
1271             section->setThreadSafeness(Node::UnspecifiedSafeness);
1272     }
1273     else
1274         section->setThreadSafeness(Node::UnspecifiedSafeness);
1275
1276     QString status = element.attribute("status");
1277     if (status == "compat")
1278         section->setStatus(Node::Compat);
1279     else if (status == "obsolete")
1280         section->setStatus(Node::Obsolete);
1281     else if (status == "deprecated")
1282         section->setStatus(Node::Deprecated);
1283     else if (status == "preliminary")
1284         section->setStatus(Node::Preliminary);
1285     else if (status == "commendable")
1286         section->setStatus(Node::Commendable);
1287     else if (status == "internal")
1288         section->setStatus(Node::Internal);
1289     else if (status == "main")
1290         section->setStatus(Node::Main);
1291     else
1292         section->setStatus(Node::Commendable);
1293
1294     section->setModuleName(element.attribute("module"));
1295     if (!indexUrl.isEmpty()) {
1296         if (indexUrl.startsWith(QLatin1Char('.')))
1297             section->setUrl(href);
1298         else
1299             section->setUrl(indexUrl + QLatin1Char('/') + href);
1300     }
1301
1302     // Create some content for the node.
1303     QSet<QString> emptySet;
1304
1305     Doc doc(location, location, " ", emptySet); // placeholder
1306     section->setDoc(doc);
1307     section->setIndexNodeFlag();
1308
1309     if (section->isInnerNode()) {
1310         InnerNode* inner = static_cast<InnerNode*>(section);
1311         if (inner) {
1312             QDomElement child = element.firstChildElement();
1313
1314             while (!child.isNull()) {
1315                 if (element.nodeName() == "class")
1316                     readIndexSection(child, inner, indexUrl);
1317                 else if (element.nodeName() == "qmlclass")
1318                     readIndexSection(child, inner, indexUrl);
1319                 else if (element.nodeName() == "page")
1320                     readIndexSection(child, inner, indexUrl);
1321                 else if (element.nodeName() == "namespace" && !name.isEmpty())
1322                     // The root node in the index is a namespace with an empty name.
1323                     readIndexSection(child, inner, indexUrl);
1324                 else
1325                     readIndexSection(child, parent, indexUrl);
1326
1327                 child = child.nextSiblingElement();
1328             }
1329         }
1330     }
1331 }
1332
1333 /*!
1334  */
1335 QString Tree::readIndexText(const QDomElement& element)
1336 {
1337     QString text;
1338     QDomNode child = element.firstChild();
1339     while (!child.isNull()) {
1340         if (child.isText())
1341             text += child.toText().nodeValue();
1342         child = child.nextSibling();
1343     }
1344     return text;
1345 }
1346
1347 /*!
1348  */
1349 void Tree::resolveIndex()
1350 {
1351     QPair<ClassNode*,QString> pair;
1352
1353     foreach (pair, priv->basesList) {
1354         foreach (const QString& base, pair.second.split(QLatin1Char(','))) {
1355             Node* n = root()->findChildNodeByNameAndType(base, Node::Class);
1356             if (n) {
1357                 pair.first->addBaseClass(Node::Public, static_cast<ClassNode*>(n));
1358             }
1359         }
1360     }
1361
1362     QPair<FunctionNode*,QString> relatedPair;
1363
1364     foreach (relatedPair, priv->relatedList) {
1365         Node* n = root()->findChildNodeByNameAndType(relatedPair.second, Node::Class);
1366         if (n)
1367             relatedPair.first->setRelates(static_cast<ClassNode*>(n));
1368     }
1369 }
1370
1371 /*!
1372   Generate the index section with the given \a writer for the \a node
1373   specified, returning true if an element was written; otherwise returns
1374   false.
1375  */
1376 bool Tree::generateIndexSection(QXmlStreamWriter& writer,
1377                                 Node* node,
1378                                 bool generateInternalNodes)
1379 {
1380     if (!node->url().isEmpty())
1381         return false;
1382
1383     QString nodeName;
1384     switch (node->type()) {
1385     case Node::Namespace:
1386         nodeName = "namespace";
1387         break;
1388     case Node::Class:
1389         nodeName = "class";
1390         break;
1391     case Node::Fake:
1392         nodeName = "page";
1393         if (node->subType() == Node::QmlClass)
1394             nodeName = "qmlclass";
1395         else if (node->subType() == Node::QmlBasicType)
1396             nodeName = "qmlbasictype";
1397         break;
1398     case Node::Enum:
1399         nodeName = "enum";
1400         break;
1401     case Node::Typedef:
1402         nodeName = "typedef";
1403         break;
1404     case Node::Property:
1405         nodeName = "property";
1406         break;
1407     case Node::Function:
1408         nodeName = "function";
1409         break;
1410     case Node::Variable:
1411         nodeName = "variable";
1412         break;
1413     case Node::QmlProperty:
1414         nodeName = "qmlproperty";
1415         break;
1416     case Node::QmlSignal:
1417         nodeName = "qmlsignal";
1418         break;
1419     case Node::QmlSignalHandler:
1420         nodeName = "qmlsignalhandler";
1421         break;
1422     case Node::QmlMethod:
1423         nodeName = "qmlmethod";
1424         break;
1425     default:
1426         return false;
1427     }
1428
1429     QString access;
1430     switch (node->access()) {
1431     case Node::Public:
1432         access = "public";
1433         break;
1434     case Node::Protected:
1435         access = "protected";
1436         break;
1437     case Node::Private:
1438         // Do not include private non-internal nodes in the index.
1439         // (Internal public and protected nodes are marked as private
1440         // by qdoc. We can check their internal status to determine
1441         // whether they were really private to begin with.)
1442         if (node->status() == Node::Internal && generateInternalNodes)
1443             access = "internal";
1444         else
1445             return false;
1446         break;
1447     default:
1448         return false;
1449     }
1450
1451     QString objName = node->name();
1452
1453     // Special case: only the root node should have an empty name.
1454     if (objName.isEmpty() && node != root())
1455         return false;
1456
1457     writer.writeStartElement(nodeName);
1458
1459     QXmlStreamAttributes attributes;
1460     writer.writeAttribute("access", access);
1461
1462     if (node->type() != Node::Fake) {
1463         QString threadSafety;
1464         switch (node->threadSafeness()) {
1465         case Node::NonReentrant:
1466             threadSafety = "non-reentrant";
1467             break;
1468         case Node::Reentrant:
1469             threadSafety = "reentrant";
1470             break;
1471         case Node::ThreadSafe:
1472             threadSafety = "thread safe";
1473             break;
1474         case Node::UnspecifiedSafeness:
1475         default:
1476             threadSafety = "unspecified";
1477             break;
1478         }
1479         writer.writeAttribute("threadsafety", threadSafety);
1480     }
1481
1482     QString status;
1483     switch (node->status()) {
1484     case Node::Compat:
1485         status = "compat";
1486         break;
1487     case Node::Obsolete:
1488         status = "obsolete";
1489         break;
1490     case Node::Deprecated:
1491         status = "deprecated";
1492         break;
1493     case Node::Preliminary:
1494         status = "preliminary";
1495         break;
1496     case Node::Commendable:
1497         status = "commendable";
1498         break;
1499     case Node::Internal:
1500         status = "internal";
1501         break;
1502     case Node::Main:
1503     default:
1504         status = "main";
1505         break;
1506     }
1507     writer.writeAttribute("status", status);
1508
1509     writer.writeAttribute("name", objName);
1510     QString fullName = node->fullDocumentName();
1511     if (fullName != objName)
1512         writer.writeAttribute("fullname", fullName);
1513     QString href = node->outputSubdirectory();
1514     if (!href.isEmpty())
1515         href.append(QLatin1Char('/'));
1516     href.append(HtmlGenerator::fullDocumentLocation(node));
1517     writer.writeAttribute("href", href);
1518     if ((node->type() != Node::Fake) && (!node->isQmlNode()))
1519         writer.writeAttribute("location", node->location().fileName());
1520
1521     switch (node->type()) {
1522
1523     case Node::Class:
1524     {
1525         // Classes contain information about their base classes.
1526
1527         const ClassNode* classNode = static_cast<const ClassNode*>(node);
1528         QList<RelatedClass> bases = classNode->baseClasses();
1529         QSet<QString> baseStrings;
1530         foreach (const RelatedClass& related, bases) {
1531             ClassNode* baseClassNode = related.node;
1532             baseStrings.insert(baseClassNode->name());
1533         }
1534         writer.writeAttribute("bases", QStringList(baseStrings.toList()).join(","));
1535         writer.writeAttribute("module", node->moduleName());
1536     }
1537         break;
1538
1539     case Node::Namespace:
1540         writer.writeAttribute("module", node->moduleName());
1541         break;
1542
1543     case Node::Fake:
1544     {
1545         /*
1546               Fake nodes (such as manual pages) contain subtypes,
1547               titles and other attributes.
1548             */
1549
1550         const FakeNode* fakeNode = static_cast<const FakeNode*>(node);
1551         switch (fakeNode->subType()) {
1552         case Node::Example:
1553             writer.writeAttribute("subtype", "example");
1554             break;
1555         case Node::HeaderFile:
1556             writer.writeAttribute("subtype", "header");
1557             break;
1558         case Node::File:
1559             writer.writeAttribute("subtype", "file");
1560             break;
1561         case Node::Group:
1562             writer.writeAttribute("subtype", "group");
1563             break;
1564         case Node::Module:
1565             writer.writeAttribute("subtype", "module");
1566             break;
1567         case Node::Page:
1568             writer.writeAttribute("subtype", "page");
1569             break;
1570         case Node::ExternalPage:
1571             writer.writeAttribute("subtype", "externalpage");
1572             break;
1573         case Node::QmlClass:
1574             //writer.writeAttribute("subtype", "qmlclass");
1575             break;
1576         case Node::QmlBasicType:
1577             //writer.writeAttribute("subtype", "qmlbasictype");
1578             break;
1579         default:
1580             break;
1581         }
1582         writer.writeAttribute("title", fakeNode->title());
1583         writer.writeAttribute("fulltitle", fakeNode->fullTitle());
1584         writer.writeAttribute("subtitle", fakeNode->subTitle());
1585         writer.writeAttribute("location", fakeNode->doc().location().fileName());
1586     }
1587         break;
1588
1589     case Node::Function:
1590     {
1591         /*
1592               Function nodes contain information about the type of
1593               function being described.
1594             */
1595
1596         const FunctionNode* functionNode =
1597                 static_cast<const FunctionNode*>(node);
1598
1599         switch (functionNode->virtualness()) {
1600         case FunctionNode::NonVirtual:
1601             writer.writeAttribute("virtual", "non");
1602             break;
1603         case FunctionNode::ImpureVirtual:
1604             writer.writeAttribute("virtual", "impure");
1605             break;
1606         case FunctionNode::PureVirtual:
1607             writer.writeAttribute("virtual", "pure");
1608             break;
1609         default:
1610             break;
1611         }
1612         switch (functionNode->metaness()) {
1613         case FunctionNode::Plain:
1614             writer.writeAttribute("meta", "plain");
1615             break;
1616         case FunctionNode::Signal:
1617             writer.writeAttribute("meta", "signal");
1618             break;
1619         case FunctionNode::Slot:
1620             writer.writeAttribute("meta", "slot");
1621             break;
1622         case FunctionNode::Ctor:
1623             writer.writeAttribute("meta", "constructor");
1624             break;
1625         case FunctionNode::Dtor:
1626             writer.writeAttribute("meta", "destructor");
1627             break;
1628         case FunctionNode::MacroWithParams:
1629             writer.writeAttribute("meta", "macrowithparams");
1630             break;
1631         case FunctionNode::MacroWithoutParams:
1632             writer.writeAttribute("meta", "macrowithoutparams");
1633             break;
1634         default:
1635             break;
1636         }
1637         writer.writeAttribute("const", functionNode->isConst()?"true":"false");
1638         writer.writeAttribute("static", functionNode->isStatic()?"true":"false");
1639         writer.writeAttribute("overload", functionNode->isOverload()?"true":"false");
1640         if (functionNode->isOverload())
1641             writer.writeAttribute("overload-number", QString::number(functionNode->overloadNumber()));
1642         if (functionNode->relates())
1643             writer.writeAttribute("relates", functionNode->relates()->name());
1644         const PropertyNode* propertyNode = functionNode->associatedProperty();
1645         if (propertyNode)
1646             writer.writeAttribute("associated-property", propertyNode->name());
1647         writer.writeAttribute("type", functionNode->returnType());
1648     }
1649         break;
1650
1651     case Node::QmlProperty:
1652     {
1653         QmlPropertyNode* qpn = static_cast<QmlPropertyNode*>(node);
1654         writer.writeAttribute("type", qpn->dataType());
1655         writer.writeAttribute("attached", qpn->isAttached() ? "true" : "false");
1656         writer.writeAttribute("writable", qpn->isWritable(this) ? "true" : "false");
1657     }
1658         break;
1659     case Node::Property:
1660     {
1661         const PropertyNode* propertyNode = static_cast<const PropertyNode*>(node);
1662         writer.writeAttribute("type", propertyNode->dataType());
1663         foreach (const Node* fnNode, propertyNode->getters()) {
1664             if (fnNode) {
1665                 const FunctionNode* functionNode = static_cast<const FunctionNode*>(fnNode);
1666                 writer.writeStartElement("getter");
1667                 writer.writeAttribute("name", functionNode->name());
1668                 writer.writeEndElement(); // getter
1669             }
1670         }
1671         foreach (const Node* fnNode, propertyNode->setters()) {
1672             if (fnNode) {
1673                 const FunctionNode* functionNode = static_cast<const FunctionNode*>(fnNode);
1674                 writer.writeStartElement("setter");
1675                 writer.writeAttribute("name", functionNode->name());
1676                 writer.writeEndElement(); // setter
1677             }
1678         }
1679         foreach (const Node* fnNode, propertyNode->resetters()) {
1680             if (fnNode) {
1681                 const FunctionNode* functionNode = static_cast<const FunctionNode*>(fnNode);
1682                 writer.writeStartElement("resetter");
1683                 writer.writeAttribute("name", functionNode->name());
1684                 writer.writeEndElement(); // resetter
1685             }
1686         }
1687         foreach (const Node* fnNode, propertyNode->notifiers()) {
1688             if (fnNode) {
1689                 const FunctionNode* functionNode = static_cast<const FunctionNode*>(fnNode);
1690                 writer.writeStartElement("notifier");
1691                 writer.writeAttribute("name", functionNode->name());
1692                 writer.writeEndElement(); // notifier
1693             }
1694         }
1695     }
1696         break;
1697
1698     case Node::Variable:
1699     {
1700         const VariableNode* variableNode =
1701                 static_cast<const VariableNode*>(node);
1702         writer.writeAttribute("type", variableNode->dataType());
1703         writer.writeAttribute("static",
1704                               variableNode->isStatic() ? "true" : "false");
1705     }
1706         break;
1707     default:
1708         break;
1709     }
1710
1711     // Inner nodes and function nodes contain child nodes of some sort, either
1712     // actual child nodes or function parameters. For these, we close the
1713     // opening tag, create child elements, then add a closing tag for the
1714     // element. Elements for all other nodes are closed in the opening tag.
1715
1716     if (node->isInnerNode()) {
1717
1718         const InnerNode* inner = static_cast<const InnerNode*>(node);
1719
1720         // For internal pages, we canonicalize the target, keyword and content
1721         // item names so that they can be used by qdoc for other sets of
1722         // documentation.
1723         // The reason we do this here is that we don't want to ruin
1724         // externally composed indexes, containing non-qdoc-style target names
1725         // when reading in indexes.
1726
1727         if (inner->doc().hasTargets()) {
1728             bool external = false;
1729             if (inner->type() == Node::Fake) {
1730                 const FakeNode* fakeNode = static_cast<const FakeNode*>(inner);
1731                 if (fakeNode->subType() == Node::ExternalPage)
1732                     external = true;
1733             }
1734
1735             foreach (const Atom* target, inner->doc().targets()) {
1736                 QString targetName = target->string();
1737                 if (!external)
1738                     targetName = Doc::canonicalTitle(targetName);
1739
1740                 writer.writeStartElement("target");
1741                 writer.writeAttribute("name", targetName);
1742                 writer.writeEndElement(); // target
1743             }
1744         }
1745         if (inner->doc().hasKeywords()) {
1746             foreach (const Atom* keyword, inner->doc().keywords()) {
1747                 writer.writeStartElement("keyword");
1748                 writer.writeAttribute("name",
1749                                       Doc::canonicalTitle(keyword->string()));
1750                 writer.writeEndElement(); // keyword
1751             }
1752         }
1753         if (inner->doc().hasTableOfContents()) {
1754             for (int i = 0; i < inner->doc().tableOfContents().size(); ++i) {
1755                 Atom* item = inner->doc().tableOfContents()[i];
1756                 int level = inner->doc().tableOfContentsLevels()[i];
1757
1758                 QString title = Text::sectionHeading(item).toString();
1759                 writer.writeStartElement("contents");
1760                 writer.writeAttribute("name", Doc::canonicalTitle(title));
1761                 writer.writeAttribute("title", title);
1762                 writer.writeAttribute("level", QString::number(level));
1763                 writer.writeEndElement(); // contents
1764             }
1765         }
1766
1767     }
1768     else if (node->type() == Node::Function) {
1769
1770         const FunctionNode* functionNode = static_cast<const FunctionNode*>(node);
1771         // Write a signature attribute for convenience.
1772         QStringList signatureList;
1773         QStringList resolvedParameters;
1774
1775         foreach (const Parameter& parameter, functionNode->parameters()) {
1776             QString leftType = parameter.leftType();
1777             const Node* leftNode = const_cast<Tree*>(this)->findNode(parameter.leftType().split("::"),
1778                                                       0, SearchBaseClasses|NonFunction);
1779             if (!leftNode || leftNode->type() != Node::Typedef) {
1780                 leftNode = const_cast<Tree*>(this)->findNode(parameter.leftType().split("::"),
1781                             node->parent(), SearchBaseClasses|NonFunction);
1782             }
1783             if (leftNode && leftNode->type() == Node::Typedef) {
1784                 if (leftNode->type() == Node::Typedef) {
1785                     const TypedefNode* typedefNode =
1786                             static_cast<const TypedefNode*>(leftNode);
1787                     if (typedefNode->associatedEnum()) {
1788                         leftType = "QFlags<" + typedefNode->associatedEnum()->fullDocumentName() + QLatin1Char('>');
1789                     }
1790                 }
1791                 else
1792                     leftType = leftNode->fullDocumentName();
1793             }
1794             resolvedParameters.append(leftType);
1795             signatureList.append(leftType + QLatin1Char(' ') + parameter.name());
1796         }
1797
1798         QString signature = functionNode->name()+QLatin1Char('(')+signatureList.join(", ")+QLatin1Char(')');
1799         if (functionNode->isConst())
1800             signature += " const";
1801         writer.writeAttribute("signature", signature);
1802
1803         for (int i = 0; i < functionNode->parameters().size(); ++i) {
1804             Parameter parameter = functionNode->parameters()[i];
1805             writer.writeStartElement("parameter");
1806             writer.writeAttribute("left", resolvedParameters[i]);
1807             writer.writeAttribute("right", parameter.rightType());
1808             writer.writeAttribute("name", parameter.name());
1809             writer.writeAttribute("default", parameter.defaultValue());
1810             writer.writeEndElement(); // parameter
1811         }
1812
1813     }
1814     else if (node->type() == Node::Enum) {
1815
1816         const EnumNode* enumNode = static_cast<const EnumNode*>(node);
1817         if (enumNode->flagsType()) {
1818             writer.writeAttribute("typedef",enumNode->flagsType()->fullDocumentName());
1819         }
1820         foreach (const EnumItem& item, enumNode->items()) {
1821             writer.writeStartElement("value");
1822             writer.writeAttribute("name", item.name());
1823             writer.writeAttribute("value", item.value());
1824             writer.writeEndElement(); // value
1825         }
1826
1827     }
1828     else if (node->type() == Node::Typedef) {
1829
1830         const TypedefNode* typedefNode = static_cast<const TypedefNode*>(node);
1831         if (typedefNode->associatedEnum()) {
1832             writer.writeAttribute("enum",typedefNode->associatedEnum()->fullDocumentName());
1833         }
1834     }
1835
1836     return true;
1837 }
1838
1839
1840 /*!
1841     Returns true if the node \a n1 is less than node \a n2.
1842     The comparison is performed by comparing properties of the nodes in order
1843     of increasing complexity.
1844 */
1845 bool compareNodes(const Node* n1, const Node* n2)
1846 {
1847     // Private nodes can occur in any order since they won't normally be
1848     // written to the index.
1849     if (n1->access() == Node::Private && n2->access() == Node::Private)
1850         return true;
1851
1852     if (n1->location().filePath() < n2->location().filePath())
1853         return true;
1854     else if (n1->location().filePath() > n2->location().filePath())
1855         return false;
1856
1857     if (n1->type() < n2->type())
1858         return true;
1859     else if (n1->type() > n2->type())
1860         return false;
1861
1862     if (n1->name() < n2->name())
1863         return true;
1864     else if (n1->name() > n2->name())
1865         return false;
1866
1867     if (n1->access() < n2->access())
1868         return true;
1869     else if (n1->access() > n2->access())
1870         return false;
1871
1872     if (n1->type() == Node::Function && n2->type() == Node::Function) {
1873         const FunctionNode* f1 = static_cast<const FunctionNode*>(n1);
1874         const FunctionNode* f2 = static_cast<const FunctionNode*>(n2);
1875
1876         if (f1->isConst() < f2->isConst())
1877             return true;
1878         else if (f1->isConst() > f2->isConst())
1879             return false;
1880
1881         if (f1->signature() < f2->signature())
1882             return true;
1883         else if (f1->signature() > f2->signature())
1884             return false;
1885     }
1886
1887     if (n1->type() == Node::Fake && n2->type() == Node::Fake) {
1888         const FakeNode* f1 = static_cast<const FakeNode*>(n1);
1889         const FakeNode* f2 = static_cast<const FakeNode*>(n2);
1890         if (f1->fullTitle() < f2->fullTitle())
1891             return true;
1892         else if (f1->fullTitle() > f2->fullTitle())
1893             return false;
1894     }
1895
1896     return false;
1897 }
1898
1899 /*!
1900     Generate index sections for the child nodes of the given \a node
1901     using the \a writer specified. If \a generateInternalNodes is true,
1902     nodes marked as internal will be included in the index; otherwise,
1903     they will be omitted.
1904 */
1905 void Tree::generateIndexSections(QXmlStreamWriter& writer,
1906                                  Node* node,
1907                                  bool generateInternalNodes)
1908 {
1909     if (generateIndexSection(writer, node, generateInternalNodes)) {
1910
1911         if (node->isInnerNode()) {
1912             const InnerNode* inner = static_cast<const InnerNode*>(node);
1913
1914             NodeList cnodes = inner->childNodes();
1915             qSort(cnodes.begin(), cnodes.end(), compareNodes);
1916
1917             foreach (Node* child, cnodes) {
1918                 /*
1919                   Don't generate anything for a QML property group node.
1920                   It is just a place holder for a collection of QML property
1921                   nodes. Recurse to its children, which are the QML property
1922                   nodes.
1923                  */
1924                 if (child->subType() == Node::QmlPropertyGroup) {
1925                     const InnerNode* pgn = static_cast<const InnerNode*>(child);
1926                     foreach (Node* c, pgn->childNodes()) {
1927                         generateIndexSections(writer, c, generateInternalNodes);
1928                     }
1929                 }
1930                 else
1931                     generateIndexSections(writer, child, generateInternalNodes);
1932             }
1933
1934             /*
1935             foreach (const Node* child, inner->relatedNodes()) {
1936                 QDomElement childElement = generateIndexSections(document, child);
1937                 element.appendChild(childElement);
1938             }
1939 */
1940         }
1941         writer.writeEndElement();
1942     }
1943 }
1944
1945 /*!
1946   Outputs an index file.
1947  */
1948 void Tree::generateIndex(const QString& fileName,
1949                          const QString& url,
1950                          const QString& title,
1951                          bool generateInternalNodes)
1952 {
1953     QFile file(fileName);
1954     if (!file.open(QFile::WriteOnly | QFile::Text))
1955         return ;
1956
1957     QXmlStreamWriter writer(&file);
1958     writer.setAutoFormatting(true);
1959     writer.writeStartDocument();
1960     writer.writeDTD("<!DOCTYPE QDOCINDEX>");
1961
1962     writer.writeStartElement("INDEX");
1963     writer.writeAttribute("url", url);
1964     writer.writeAttribute("title", title);
1965     writer.writeAttribute("version", version());
1966
1967     generateIndexSections(writer, root(), generateInternalNodes);
1968
1969     writer.writeEndElement(); // INDEX
1970     writer.writeEndElement(); // QDOCINDEX
1971     writer.writeEndDocument();
1972     file.close();
1973 }
1974
1975 /*!
1976   Generate the tag file section with the given \a writer for the \a node
1977   specified, returning true if an element was written; otherwise returns
1978   false.
1979  */
1980 void Tree::generateTagFileCompounds(QXmlStreamWriter& writer, const InnerNode* inner)
1981 {
1982     foreach (const Node* node, inner->childNodes()) {
1983
1984         if (!node->url().isEmpty())
1985             continue;
1986
1987         QString kind;
1988         switch (node->type()) {
1989         case Node::Namespace:
1990             kind = "namespace";
1991             break;
1992         case Node::Class:
1993             kind = "class";
1994             break;
1995         case Node::Enum:
1996         case Node::Typedef:
1997         case Node::Property:
1998         case Node::Function:
1999         case Node::Variable:
2000         default:
2001             continue;
2002         }
2003
2004         QString access;
2005         switch (node->access()) {
2006         case Node::Public:
2007             access = "public";
2008             break;
2009         case Node::Protected:
2010             access = "protected";
2011             break;
2012         case Node::Private:
2013         default:
2014             continue;
2015         }
2016
2017         QString objName = node->name();
2018
2019         // Special case: only the root node should have an empty name.
2020         if (objName.isEmpty() && node != root())
2021             continue;
2022
2023         // *** Write the starting tag for the element here. ***
2024         writer.writeStartElement("compound");
2025         writer.writeAttribute("kind", kind);
2026
2027         if (node->type() == Node::Class) {
2028             writer.writeTextElement("name", node->fullDocumentName());
2029             writer.writeTextElement("filename", HtmlGenerator::fullDocumentLocation(node,true));
2030
2031             // Classes contain information about their base classes.
2032             const ClassNode* classNode = static_cast<const ClassNode*>(node);
2033             QList<RelatedClass> bases = classNode->baseClasses();
2034             foreach (const RelatedClass& related, bases) {
2035                 ClassNode* baseClassNode = related.node;
2036                 writer.writeTextElement("base", baseClassNode->name());
2037             }
2038
2039             // Recurse to write all members.
2040             generateTagFileMembers(writer, static_cast<const InnerNode*>(node));
2041             writer.writeEndElement();
2042
2043             // Recurse to write all compounds.
2044             generateTagFileCompounds(writer, static_cast<const InnerNode*>(node));
2045         } else {
2046             writer.writeTextElement("name", node->fullDocumentName());
2047             writer.writeTextElement("filename", HtmlGenerator::fullDocumentLocation(node,true));
2048
2049             // Recurse to write all members.
2050             generateTagFileMembers(writer, static_cast<const InnerNode*>(node));
2051             writer.writeEndElement();
2052
2053             // Recurse to write all compounds.
2054             generateTagFileCompounds(writer, static_cast<const InnerNode*>(node));
2055         }
2056     }
2057 }
2058
2059 /*!
2060  */
2061 void Tree::generateTagFileMembers(QXmlStreamWriter& writer, const InnerNode* inner)
2062 {
2063     foreach (const Node* node, inner->childNodes()) {
2064
2065         if (!node->url().isEmpty())
2066             continue;
2067
2068         QString nodeName;
2069         QString kind;
2070         switch (node->type()) {
2071         case Node::Enum:
2072             nodeName = "member";
2073             kind = "enum";
2074             break;
2075         case Node::Typedef:
2076             nodeName = "member";
2077             kind = "typedef";
2078             break;
2079         case Node::Property:
2080             nodeName = "member";
2081             kind = "property";
2082             break;
2083         case Node::Function:
2084             nodeName = "member";
2085             kind = "function";
2086             break;
2087         case Node::Namespace:
2088             nodeName = "namespace";
2089             break;
2090         case Node::Class:
2091             nodeName = "class";
2092             break;
2093         case Node::Variable:
2094         default:
2095             continue;
2096         }
2097
2098         QString access;
2099         switch (node->access()) {
2100         case Node::Public:
2101             access = "public";
2102             break;
2103         case Node::Protected:
2104             access = "protected";
2105             break;
2106         case Node::Private:
2107         default:
2108             continue;
2109         }
2110
2111         QString objName = node->name();
2112
2113         // Special case: only the root node should have an empty name.
2114         if (objName.isEmpty() && node != root())
2115             continue;
2116
2117         // *** Write the starting tag for the element here. ***
2118         writer.writeStartElement(nodeName);
2119         if (!kind.isEmpty())
2120             writer.writeAttribute("kind", kind);
2121
2122         switch (node->type()) {
2123
2124         case Node::Class:
2125             writer.writeCharacters(node->fullDocumentName());
2126             writer.writeEndElement();
2127             break;
2128         case Node::Namespace:
2129             writer.writeCharacters(node->fullDocumentName());
2130             writer.writeEndElement();
2131             break;
2132         case Node::Function:
2133         {
2134             /*
2135                   Function nodes contain information about
2136                   the type of function being described.
2137                 */
2138
2139             const FunctionNode* functionNode =
2140                     static_cast<const FunctionNode*>(node);
2141             writer.writeAttribute("protection", access);
2142
2143             switch (functionNode->virtualness()) {
2144             case FunctionNode::NonVirtual:
2145                 writer.writeAttribute("virtualness", "non");
2146                 break;
2147             case FunctionNode::ImpureVirtual:
2148                 writer.writeAttribute("virtualness", "virtual");
2149                 break;
2150             case FunctionNode::PureVirtual:
2151                 writer.writeAttribute("virtual", "pure");
2152                 break;
2153             default:
2154                 break;
2155             }
2156             writer.writeAttribute("static",
2157                                   functionNode->isStatic() ? "yes" : "no");
2158
2159             if (functionNode->virtualness() == FunctionNode::NonVirtual)
2160                 writer.writeTextElement("type", functionNode->returnType());
2161             else
2162                 writer.writeTextElement("type",
2163                                         "virtual " + functionNode->returnType());
2164
2165             writer.writeTextElement("name", objName);
2166             QStringList pieces = HtmlGenerator::fullDocumentLocation(node,true).split(QLatin1Char('#'));
2167             writer.writeTextElement("anchorfile", pieces[0]);
2168             writer.writeTextElement("anchor", pieces[1]);
2169
2170             // Write a signature attribute for convenience.
2171             QStringList signatureList;
2172
2173             foreach (const Parameter& parameter, functionNode->parameters()) {
2174                 QString leftType = parameter.leftType();
2175                 const Node* leftNode = const_cast<Tree*>(this)->findNode(parameter.leftType().split("::"),
2176                                                                          0, SearchBaseClasses|NonFunction);
2177                 if (!leftNode || leftNode->type() != Node::Typedef) {
2178                     leftNode = const_cast<Tree*>(this)->findNode(parameter.leftType().split("::"),
2179                                                       node->parent(), SearchBaseClasses|NonFunction);
2180                 }
2181                 if (leftNode && leftNode->type() == Node::Typedef) {
2182                     const TypedefNode* typedefNode = static_cast<const TypedefNode*>(leftNode);
2183                     if (typedefNode->associatedEnum()) {
2184                         leftType = "QFlags<" + typedefNode->associatedEnum()->fullDocumentName() + QLatin1Char('>');
2185                     }
2186                 }
2187                 signatureList.append(leftType + QLatin1Char(' ') + parameter.name());
2188             }
2189
2190             QString signature = QLatin1Char('(')+signatureList.join(", ")+QLatin1Char(')');
2191             if (functionNode->isConst())
2192                 signature += " const";
2193             if (functionNode->virtualness() == FunctionNode::PureVirtual)
2194                 signature += " = 0";
2195             writer.writeTextElement("arglist", signature);
2196         }
2197             writer.writeEndElement(); // member
2198             break;
2199
2200         case Node::Property:
2201         {
2202             const PropertyNode* propertyNode = static_cast<const PropertyNode*>(node);
2203             writer.writeAttribute("type", propertyNode->dataType());
2204             writer.writeTextElement("name", objName);
2205             QStringList pieces = HtmlGenerator::fullDocumentLocation(node,true).split(QLatin1Char('#'));
2206             writer.writeTextElement("anchorfile", pieces[0]);
2207             writer.writeTextElement("anchor", pieces[1]);
2208             writer.writeTextElement("arglist", "");
2209         }
2210             writer.writeEndElement(); // member
2211             break;
2212
2213         case Node::Enum:
2214         {
2215             const EnumNode* enumNode = static_cast<const EnumNode*>(node);
2216             writer.writeTextElement("name", objName);
2217             QStringList pieces = HtmlGenerator::fullDocumentLocation(node).split(QLatin1Char('#'));
2218             writer.writeTextElement("anchor", pieces[1]);
2219             writer.writeTextElement("arglist", "");
2220             writer.writeEndElement(); // member
2221
2222             for (int i = 0; i < enumNode->items().size(); ++i) {
2223                 EnumItem item = enumNode->items().value(i);
2224                 writer.writeStartElement("member");
2225                 writer.writeAttribute("name", item.name());
2226                 writer.writeTextElement("anchor", pieces[1]);
2227                 writer.writeTextElement("arglist", "");
2228                 writer.writeEndElement(); // member
2229             }
2230         }
2231             break;
2232
2233         case Node::Typedef:
2234         {
2235             const TypedefNode* typedefNode = static_cast<const TypedefNode*>(node);
2236             if (typedefNode->associatedEnum())
2237                 writer.writeAttribute("type", typedefNode->associatedEnum()->fullDocumentName());
2238             else
2239                 writer.writeAttribute("type", "");
2240             writer.writeTextElement("name", objName);
2241             QStringList pieces = HtmlGenerator::fullDocumentLocation(node,true).split(QLatin1Char('#'));
2242             writer.writeTextElement("anchorfile", pieces[0]);
2243             writer.writeTextElement("anchor", pieces[1]);
2244             writer.writeTextElement("arglist", "");
2245         }
2246             writer.writeEndElement(); // member
2247             break;
2248
2249         case Node::Variable:
2250         default:
2251             break;
2252         }
2253     }
2254 }
2255
2256 /*!
2257   Writes a tag file named \a fileName.
2258  */
2259 void Tree::generateTagFile(const QString& fileName)
2260 {
2261     QFile file(fileName);
2262     if (!file.open(QFile::WriteOnly | QFile::Text))
2263         return ;
2264
2265     QXmlStreamWriter writer(&file);
2266     writer.setAutoFormatting(true);
2267     writer.writeStartDocument();
2268
2269     writer.writeStartElement("tagfile");
2270
2271     generateTagFileCompounds(writer, root());
2272
2273     writer.writeEndElement(); // tagfile
2274     writer.writeEndDocument();
2275     file.close();
2276 }
2277
2278 /*!
2279  */
2280 void Tree::addExternalLink(const QString& url, const Node* relative)
2281 {
2282     FakeNode* fakeNode = new FakeNode(root(), url, Node::ExternalPage, Node::ArticlePage);
2283     fakeNode->setAccess(Node::Public);
2284
2285     // Create some content for the node.
2286     QSet<QString> emptySet;
2287     Location location(relative->doc().location());
2288     Doc doc(location, location, " ", emptySet); // placeholder
2289     fakeNode->setDoc(doc);
2290 }
2291
2292 /*!
2293   Find the node with the specified \a path name that is of
2294   the specified \a type and \a subtype. Begin the search at
2295   the \a start node. If the \a start node is 0, begin the
2296   search at the tree root. \a subtype is not used unless
2297   \a type is \c{Fake}.
2298  */
2299 Node* Tree::findNodeByNameAndType(const QStringList& path,
2300                                   Node::Type type,
2301                                   Node::SubType subtype,
2302                                   Node* start,
2303                                   bool acceptCollision)
2304 {
2305     if (!start)
2306         start = const_cast<NamespaceNode*>(root());
2307     Node* result = findNodeRecursive(path, 0, start, type, subtype, acceptCollision);
2308     return result;
2309 }
2310
2311 #if 0
2312     if (result)
2313         qDebug() << "FOUND:" << path << Node::nodeTypeString(type)
2314                  << Node::nodeSubtypeString(subtype);
2315     else
2316         qDebug() << "NOT FOUND:" << path << Node::nodeTypeString(type)
2317                  << Node::nodeSubtypeString(subtype);
2318 #endif
2319
2320 /*!
2321   Recursive search for a node identified by \a path. Each
2322   path element is a name. \a pathIndex specifies the index
2323   of the name in \a path to try to match. \a start is the
2324   node whose children shoulod be searched for one that has
2325   that name. Each time a match is found, increment the
2326   \a pathIndex and call this function recursively.
2327
2328   If the end of the path is reached (i.e. if a matching
2329   node is found for each name in the \a path), the \a type
2330   must match the type of the last matching node, and if the
2331   type is \e{Fake}, the \a subtype must match as well.
2332
2333   If the algorithm is successful, the pointer to the final
2334   node is returned. Otherwise 0 is returned.
2335  */
2336 Node* Tree::findNodeRecursive(const QStringList& path,
2337                               int pathIndex,
2338                               Node* start,
2339                               Node::Type type,
2340                               Node::SubType subtype,
2341                               bool acceptCollision)
2342 {
2343     if (!start || path.isEmpty())
2344         return 0; // no place to start, or nothing to search for.
2345     if (start->isLeaf()) {
2346         if (pathIndex >= path.size())
2347             return start; // found a match.
2348         return 0; // premature leaf
2349     }
2350     if (pathIndex >= path.size())
2351         return 0; // end of search path.
2352
2353     InnerNode* current = static_cast<InnerNode*>(start);
2354     const NodeList& children = current->childNodes();
2355     const QString& name = path.at(pathIndex);
2356     for (int i=0; i<children.size(); ++i) {
2357         Node* n = children.at(i);
2358         if (!n)
2359             continue;
2360         if (n->isQmlPropertyGroup()) {
2361             if (type == Node::QmlProperty) {
2362                 n = findNodeRecursive(path, pathIndex, n, type, subtype);
2363                 if (n)
2364                     return n;
2365             }
2366         }
2367         else if (n->name() == name) {
2368             if (pathIndex+1 >= path.size()) {
2369                 if (n->type() == type) {
2370                     if (type == Node::Fake) {
2371                         if (n->subType() == subtype)
2372                             return n;
2373                         else if (n->subType() == Node::Collision && acceptCollision)
2374                             return n;
2375                         else if (subtype == Node::NoSubType)
2376                             return n; // don't care what subtype is.
2377                         return 0;
2378                     }
2379                     else
2380                         return n;
2381                 }
2382                 else if (n->isCollisionNode()) {
2383                     if (acceptCollision)
2384                         return n;
2385                     return n = findNodeRecursive(path, pathIndex, n, type, subtype);
2386                     if (n)
2387                         return n;
2388                 }
2389                 else
2390                     return 0;
2391             }
2392             else { // Not at the end of the path.
2393                 n = findNodeRecursive(path, pathIndex+1, n, type, subtype);
2394                 if (n)
2395                     return n;
2396             }
2397         }
2398     }
2399     return 0;
2400 }
2401
2402 /*!
2403   Find the Enum type node named \a path. Begin the search at the
2404   \a start node. If the \a start node is 0, begin the search
2405   at the root of the tree. Only an Enum type node named \a path is
2406   acceptible. If one is not found, 0 is returned.
2407  */
2408 EnumNode* Tree::findEnumNode(const QStringList& path, Node* start)
2409 {
2410     if (!start)
2411         start = const_cast<NamespaceNode*>(root());
2412     return static_cast<EnumNode*>(findNodeRecursive(path, 0, start, Node::Enum, Node::NoSubType));
2413 }
2414
2415 /*!
2416   Find the C++ class node named \a path. Begin the search at the
2417   \a start node. If the \a start node is 0, begin the search
2418   at the root of the tree. Only a C++ class node named \a path is
2419   acceptible. If one is not found, 0 is returned.
2420  */
2421 ClassNode* Tree::findClassNode(const QStringList& path, Node* start)
2422 {
2423     if (!start)
2424         start = const_cast<NamespaceNode*>(root());
2425     return static_cast<ClassNode*>(findNodeRecursive(path, 0, start, Node::Class, Node::NoSubType));
2426 }
2427
2428 /*!
2429   Find the Qml class node named \a path. Begin the search at the
2430   \a start node. If the \a start node is 0, begin the search
2431   at the root of the tree. Only a Qml class node named \a path is
2432   acceptible. If one is not found, 0 is returned.
2433  */
2434 QmlClassNode* Tree::findQmlClassNode(const QStringList& path, Node* start)
2435 {
2436     /*
2437       If the path contains one or two double colons ("::"),
2438       check first to see if the first two path strings refer
2439       to a QML element. If yes, that reference identifies a
2440       QML class node.
2441     */
2442     if (path.size() >= 2) {
2443         QmlClassNode* qcn = QmlClassNode::moduleMap.value(path[0]+ "::" +path[1]);
2444         if (qcn)
2445             return qcn;
2446     }
2447
2448     if (!start)
2449         start = const_cast<NamespaceNode*>(root());
2450     return static_cast<QmlClassNode*>(findNodeRecursive(path, 0, start, Node::Fake, Node::QmlClass));
2451 }
2452
2453 /*!
2454   Find the Namespace node named \a path. Begin the search at the
2455   \a start node. If the \a start node is 0, begin the search
2456   at the root of the tree. Only a Namespace node named \a path is
2457   acceptible. If one is not found, 0 is returned.
2458  */
2459 NamespaceNode* Tree::findNamespaceNode(const QStringList& path, Node* start)
2460 {
2461     if (!start)
2462         start = const_cast<NamespaceNode*>(root());
2463     return static_cast<NamespaceNode*>(findNodeRecursive(path, 0, start, Node::Namespace, Node::NoSubType));
2464 }
2465
2466 /*!
2467   Find the Group node named \a path. Begin the search at the
2468   \a start node. If the \a start node is 0, begin the search
2469   at the root of the tree. Only a Group node named \a path is
2470   acceptible. If one is not found, 0 is returned.
2471  */
2472 FakeNode* Tree::findGroupNode(const QStringList& path, Node* start)
2473 {
2474     if (!start)
2475         start = const_cast<NamespaceNode*>(root());
2476     return static_cast<FakeNode*>(findNodeRecursive(path, 0, start, Node::Fake, Node::Group));
2477 }
2478
2479 /*!
2480   Find the Qml module node named \a path. Begin the search at the
2481   \a start node. If the \a start node is 0, begin the search
2482   at the root of the tree. Only a Qml module node named \a path is
2483   acceptible. If one is not found, 0 is returned.
2484  */
2485 FakeNode* Tree::findQmlModuleNode(const QStringList& path, Node* start)
2486 {
2487     if (!start)
2488         start = const_cast<NamespaceNode*>(root());
2489     return static_cast<FakeNode*>(findNodeRecursive(path, 0, start, Node::Fake, Node::QmlModule));
2490 }
2491
2492 QT_END_NAMESPACE
2493
2494 #if 0
2495 const Node* Tree::findNodeXXX(const QStringList& path, bool qml) const
2496 {
2497     const Node* current = root();
2498     do {
2499         const Node* node = current;
2500         int i;
2501         int start_idx = 0;
2502
2503         /*
2504           If the path contains one or two double colons ("::"),
2505           check first to see if the first two path strings refer
2506           to a QML element. If yes, that reference identifies a
2507           QML class node.
2508         */
2509         if (qml && path.size() >= 2) {
2510             QmlClassNode* qcn = QmlClassNode::moduleMap.value(path[0]+ "::" +path[1]);
2511             if (qcn) {
2512                 node = qcn;
2513                 if (path.size() == 2)
2514                     return node;
2515                 start_idx = 2;
2516             }
2517         }
2518
2519         for (i = start_idx; i < path.size(); ++i) {
2520             if (node == 0 || !node->isInnerNode())
2521                 break;
2522
2523             const Node* next = static_cast<const InnerNode*>(node)->findChildNodeByName(path.at(i), qml);
2524             node = next;
2525         }
2526         if (node && i == path.size()) {
2527             if (node->subType() != Node::QmlPropertyGroup) {
2528                 if (node->subType() == Node::Collision) {
2529                     node = node->applyModuleIdentifier(start);
2530                 }
2531                 return node;
2532             }
2533         }
2534         current = current->parent();
2535     } while (current);
2536
2537     return 0;
2538 }
2539 #endif