tools/qdoc3/tree.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the tools applications of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 /*
       
    43   tree.cpp
       
    44 */
       
    45 
       
    46 #include <QtCore>
       
    47 #include <QDomDocument>
       
    48 
       
    49 #include "atom.h"
       
    50 #include "doc.h"
       
    51 #include "htmlgenerator.h"
       
    52 #include "location.h"
       
    53 #include "node.h"
       
    54 #include "text.h"
       
    55 #include "tree.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 QMultiMap<QString, Node *> GroupMap;
       
    87 typedef QMultiHash<QString, FakeNode *> FakeNodeHash;
       
    88 typedef QMultiHash<QString, Target> TargetHash;
       
    89 
       
    90 class TreePrivate
       
    91 {
       
    92 public:
       
    93     QMap<ClassNode *, QList<InheritanceBound> > unresolvedInheritanceMap;
       
    94     PropertyMap unresolvedPropertyMap;
       
    95     GroupMap groupMap;
       
    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 
       
   107 /*!
       
   108   The default constructor is the only constructor.
       
   109  */
       
   110 Tree::Tree()
       
   111     : roo(0, "")
       
   112 {
       
   113     priv = new TreePrivate;
       
   114 }
       
   115 
       
   116 /*!
       
   117   The destructor deletes the internal, private tree.
       
   118  */
       
   119 Tree::~Tree()
       
   120 {
       
   121     delete priv;
       
   122 }
       
   123 
       
   124 /*!
       
   125  */
       
   126 Node *Tree::findNode(const QStringList &path, Node *relative, int findFlags)
       
   127 {
       
   128     return const_cast<Node*>(const_cast<const Tree*>(this)->findNode(path,
       
   129                                                                      relative,
       
   130                                                                      findFlags));
       
   131 }
       
   132 
       
   133 /*!
       
   134  */
       
   135 const Node *Tree::findNode(const QStringList &path,
       
   136                            const Node *relative,
       
   137                            int findFlags) const
       
   138 {
       
   139     if (!relative)
       
   140         relative = root();
       
   141 
       
   142     do {
       
   143         const Node *node = relative;
       
   144         int i;
       
   145 
       
   146         for (i = 0; i < path.size(); ++i) {
       
   147             if (node == 0 || !node->isInnerNode())
       
   148                 break;
       
   149 
       
   150             const Node *next =
       
   151                 static_cast<const InnerNode*>(node)->findNode(path.at(i));
       
   152             if (!next && (findFlags & SearchEnumValues) && i == path.size()-1)
       
   153                 next = static_cast<const InnerNode*>(node)->findEnumNodeForValue(path.at(i));
       
   154 
       
   155             if (!next && node->type() == Node::Class && (findFlags & SearchBaseClasses)) {
       
   156                 NodeList baseClasses = allBaseClasses(static_cast<const ClassNode *>(node));
       
   157                 foreach (const Node *baseClass, baseClasses) {
       
   158                     next = static_cast<const InnerNode *>(baseClass)->findNode(path.at(i));
       
   159                     if (!next && (findFlags & SearchEnumValues) && i == path.size() - 1)
       
   160                         next = static_cast<const InnerNode *>(baseClass)
       
   161                                         ->findEnumNodeForValue(path.at(i));
       
   162                     if (next)
       
   163                         break;
       
   164                 }
       
   165             }
       
   166             node = next;
       
   167         }
       
   168         if (node && i == path.size()
       
   169                 && (!(findFlags & NonFunction) || node->type() != Node::Function
       
   170                     || ((FunctionNode *)node)->metaness() == FunctionNode::MacroWithoutParams))
       
   171             return node;
       
   172         relative = relative->parent();
       
   173     } while (relative);
       
   174 
       
   175     return 0;
       
   176 }
       
   177 
       
   178 /*!
       
   179   Find the node with the specified \a path name of the
       
   180   specified \a type.
       
   181  */
       
   182 Node *Tree::findNode(const QStringList &path,
       
   183                      Node::Type type,
       
   184                      Node *relative,
       
   185                      int findFlags)
       
   186 {
       
   187     return const_cast<Node*>(const_cast<const Tree*>(this)->findNode(path,
       
   188                                                                      type,
       
   189                                                                      relative,
       
   190                                                                      findFlags));
       
   191 }
       
   192 
       
   193 /*!
       
   194   Find the node with the specified \a path name of the
       
   195   specified \a type.
       
   196  */
       
   197 const Node *Tree::findNode(const QStringList &path,
       
   198                            Node::Type type,
       
   199                            const Node *relative,
       
   200                            int findFlags) const
       
   201 {
       
   202     const Node *node = findNode(path, relative, findFlags);
       
   203     if (node != 0 && node->type() == type)
       
   204         return node;
       
   205     return 0;
       
   206 }
       
   207 
       
   208 /*!
       
   209  */
       
   210 FunctionNode *Tree::findFunctionNode(const QStringList& path,
       
   211                                      Node *relative,
       
   212                                      int findFlags)
       
   213 {
       
   214     return const_cast<FunctionNode *>(
       
   215                 const_cast<const Tree *>(this)->findFunctionNode(path,
       
   216                                                                  relative,
       
   217                                                                  findFlags));
       
   218 }
       
   219 
       
   220 /*!
       
   221  */
       
   222 const FunctionNode *Tree::findFunctionNode(const QStringList &path,
       
   223                                            const Node *relative,
       
   224                                            int findFlags) const
       
   225 {
       
   226     if (!relative)
       
   227         relative = root();
       
   228     do {
       
   229         const Node *node = relative;
       
   230         int i;
       
   231 
       
   232         for (i = 0; i < path.size(); ++i) {
       
   233             if (node == 0 || !node->isInnerNode())
       
   234                 break;
       
   235 
       
   236             const Node *next;
       
   237             if (i == path.size() - 1)
       
   238                 next = ((InnerNode *) node)->findFunctionNode(path.at(i));
       
   239             else
       
   240                 next = ((InnerNode *) node)->findNode(path.at(i));
       
   241 
       
   242             if (!next && node->type() == Node::Class &&
       
   243                 (findFlags & SearchBaseClasses)) {
       
   244                 NodeList baseClasses = allBaseClasses(static_cast<const ClassNode *>(node));
       
   245                 foreach (const Node *baseClass, baseClasses) {
       
   246                     if (i == path.size() - 1)
       
   247                         next = static_cast<const InnerNode *>(baseClass)->
       
   248                                 findFunctionNode(path.at(i));
       
   249                     else
       
   250                         next = static_cast<const InnerNode *>(baseClass)->findNode(path.at(i));
       
   251 
       
   252                     if (next)
       
   253                         break;
       
   254                 }
       
   255             }
       
   256 
       
   257             node = next;
       
   258         }
       
   259         if (node && i == path.size() && node->type() == Node::Function) {
       
   260             // CppCodeParser::processOtherMetaCommand ensures that reimplemented
       
   261             // functions are private.
       
   262             const FunctionNode *func = static_cast<const FunctionNode*>(node);
       
   263 
       
   264             while (func->access() == Node::Private) {
       
   265                 const FunctionNode *from = func->reimplementedFrom();
       
   266                 if (from != 0) {
       
   267                     if (from->access() != Node::Private)
       
   268                         return from;
       
   269                     else
       
   270                         func = from;
       
   271                 } else
       
   272                     break;
       
   273             }
       
   274             return func;
       
   275         }
       
   276         relative = relative->parent();
       
   277     } while (relative);
       
   278 
       
   279     return 0;
       
   280 }
       
   281 
       
   282 /*!
       
   283  */
       
   284 FunctionNode *Tree::findFunctionNode(const QStringList &parentPath,
       
   285                                      const FunctionNode *clone,
       
   286                                      Node *relative,
       
   287                                      int findFlags)
       
   288 {
       
   289     return const_cast<FunctionNode *>(
       
   290 		const_cast<const Tree *>(this)->findFunctionNode(parentPath,
       
   291                                                                  clone,
       
   292                                       				 relative,
       
   293                                                                  findFlags));
       
   294 }
       
   295 
       
   296 /*!
       
   297  */
       
   298 const FunctionNode *Tree::findFunctionNode(const QStringList &parentPath,
       
   299                                            const FunctionNode *clone,
       
   300                                            const Node *relative,
       
   301                                            int findFlags) const
       
   302 {
       
   303     const Node *parent = findNode(parentPath, relative, findFlags);
       
   304     if (parent == 0 || !parent->isInnerNode()) {
       
   305         return 0;
       
   306     } else {
       
   307         return ((InnerNode *)parent)->findFunctionNode(clone);
       
   308     }
       
   309 }
       
   310 
       
   311 static const int NumSuffixes = 3;
       
   312 static const char * const suffixes[NumSuffixes] = { "", "s", "es" };
       
   313 
       
   314 /*!
       
   315  */
       
   316 const FakeNode *Tree::findFakeNodeByTitle(const QString &title) const
       
   317 {
       
   318     for (int pass = 0; pass < NumSuffixes; ++pass) {
       
   319         FakeNodeHash::const_iterator i =
       
   320                 priv->fakeNodesByTitle.find(Doc::canonicalTitle(title + suffixes[pass]));
       
   321         if (i != priv->fakeNodesByTitle.constEnd()) {
       
   322             FakeNodeHash::const_iterator j = i;
       
   323             ++j;
       
   324             if (j != priv->fakeNodesByTitle.constEnd() && j.key() == i.key()) {
       
   325                 QList<Location> internalLocations;
       
   326                 while (j != priv->fakeNodesByTitle.constEnd()) {
       
   327                     if (j.key() == i.key() && j.value()->url().isEmpty())
       
   328                         internalLocations.append(j.value()->doc().location());
       
   329                     ++j;
       
   330                 }
       
   331                 if (internalLocations.size() > 0) {
       
   332                     i.value()->doc().location().warning(
       
   333                         tr("Page '%1' defined in more than one location:").arg(title));
       
   334                     foreach (const Location &location, internalLocations)
       
   335                         location.warning(tr("(defined here)"));
       
   336                 }
       
   337             }
       
   338             return i.value();
       
   339         }
       
   340     }
       
   341     return 0;
       
   342 }
       
   343 
       
   344 /*!
       
   345  */
       
   346 const Node*
       
   347 Tree::findUnambiguousTarget(const QString &target, Atom *&atom) const
       
   348 {
       
   349     Target bestTarget = {0, 0, INT_MAX};
       
   350     int numBestTargets = 0;
       
   351 
       
   352     for (int pass = 0; pass < NumSuffixes; ++pass) {
       
   353         TargetHash::const_iterator i =
       
   354                 priv->targetHash.find(Doc::canonicalTitle(target + suffixes[pass]));
       
   355         if (i != priv->targetHash.constEnd()) {
       
   356             TargetHash::const_iterator j = i;
       
   357             do {
       
   358                 const Target &candidate = j.value();
       
   359                 if (candidate.priority < bestTarget.priority) {
       
   360                     bestTarget = candidate;
       
   361                     numBestTargets = 1;
       
   362                 } else if (candidate.priority == bestTarget.priority) {
       
   363                     ++numBestTargets;
       
   364                 }
       
   365                 ++j;
       
   366             } while (j != priv->targetHash.constEnd() && j.key() == i.key());
       
   367 
       
   368             if (numBestTargets == 1) {
       
   369                 atom = bestTarget.atom;
       
   370                 return bestTarget.node;
       
   371             }
       
   372         }
       
   373     }
       
   374     return 0;
       
   375 }
       
   376 
       
   377 /*!
       
   378  */
       
   379 Atom *Tree::findTarget(const QString &target, const Node *node) const
       
   380 {
       
   381     for (int pass = 0; pass < NumSuffixes; ++pass) {
       
   382         QString key = Doc::canonicalTitle(target + suffixes[pass]);
       
   383         TargetHash::const_iterator i = priv->targetHash.find(key);
       
   384 
       
   385         if (i != priv->targetHash.constEnd()) {
       
   386             do {
       
   387                 if (i.value().node == node)
       
   388                     return i.value().atom;
       
   389                 ++i;
       
   390             } while (i != priv->targetHash.constEnd() && i.key() == key);
       
   391         }
       
   392     }
       
   393     return 0;
       
   394 }
       
   395 
       
   396 /*!
       
   397  */
       
   398 void Tree::addBaseClass(ClassNode *subclass, Node::Access access,
       
   399                         const QStringList &basePath,
       
   400                         const QString &dataTypeWithTemplateArgs,
       
   401                         InnerNode *parent)
       
   402 {
       
   403     priv->unresolvedInheritanceMap[subclass].append(
       
   404 	    InheritanceBound(access,
       
   405                              basePath,
       
   406                              dataTypeWithTemplateArgs,
       
   407                              parent)
       
   408                                                    );
       
   409 }
       
   410 
       
   411 
       
   412 /*!
       
   413  */
       
   414 void Tree::addPropertyFunction(PropertyNode *property,
       
   415                                const QString &funcName,
       
   416                                PropertyNode::FunctionRole funcRole)
       
   417 {
       
   418     priv->unresolvedPropertyMap[property].insert(funcRole, funcName);
       
   419 }
       
   420 
       
   421 /*!
       
   422   This function adds the \a node to the \a group. The group
       
   423   can be listed anywhere using the \e{annotated list} command.
       
   424  */
       
   425 void Tree::addToGroup(Node *node, const QString &group)
       
   426 {
       
   427     priv->groupMap.insert(group, node);
       
   428 }
       
   429 
       
   430 /*!
       
   431  */
       
   432 QMultiMap<QString, Node *> Tree::groups() const
       
   433 {
       
   434     return priv->groupMap;
       
   435 }
       
   436 
       
   437 /*!
       
   438  */
       
   439 void Tree::addToPublicGroup(Node *node, const QString &group)
       
   440 {
       
   441     priv->publicGroupMap.insert(node->name(), group);
       
   442     addToGroup(node, group);
       
   443 }
       
   444 
       
   445 /*!
       
   446  */
       
   447 QMultiMap<QString, QString> Tree::publicGroups() const
       
   448 {
       
   449     return priv->publicGroupMap;
       
   450 }
       
   451 
       
   452 /*!
       
   453  */
       
   454 void Tree::resolveInheritance(NamespaceNode *rootNode)
       
   455 {
       
   456     if (!rootNode)
       
   457         rootNode = root();
       
   458 
       
   459     for (int pass = 0; pass < 2; pass++) {
       
   460         NodeList::ConstIterator c = rootNode->childNodes().begin();
       
   461         while (c != rootNode->childNodes().end()) {
       
   462             if ((*c)->type() == Node::Class)
       
   463                 resolveInheritance(pass, (ClassNode *) *c);
       
   464             else if ((*c)->type() == Node::Namespace) {
       
   465                 NamespaceNode *ns = static_cast<NamespaceNode*>(*c);
       
   466                 resolveInheritance(ns);
       
   467             }
       
   468             ++c;
       
   469         }
       
   470         if (rootNode == root())
       
   471             priv->unresolvedInheritanceMap.clear();
       
   472     }
       
   473 }
       
   474 
       
   475 /*!
       
   476  */
       
   477 void Tree::resolveProperties()
       
   478 {
       
   479     PropertyMap::ConstIterator propEntry;
       
   480 
       
   481     propEntry = priv->unresolvedPropertyMap.begin();
       
   482     while (propEntry != priv->unresolvedPropertyMap.end()) {
       
   483         PropertyNode *property = propEntry.key();
       
   484         InnerNode *parent = property->parent();
       
   485         QString getterName = (*propEntry)[PropertyNode::Getter];
       
   486         QString setterName = (*propEntry)[PropertyNode::Setter];
       
   487         QString resetterName = (*propEntry)[PropertyNode::Resetter];
       
   488         QString notifierName = (*propEntry)[PropertyNode::Notifier];
       
   489 
       
   490         NodeList::ConstIterator c = parent->childNodes().begin();
       
   491         while (c != parent->childNodes().end()) {
       
   492             if ((*c)->type() == Node::Function) {
       
   493                 FunctionNode *function = static_cast<FunctionNode *>(*c);
       
   494                 if (function->access() == property->access() &&
       
   495                     (function->status() == property->status() ||
       
   496                      function->doc().isEmpty())) {
       
   497                     if (function->name() == getterName) {
       
   498                         property->addFunction(function, PropertyNode::Getter);
       
   499                     } else if (function->name() == setterName) {
       
   500                         property->addFunction(function, PropertyNode::Setter);
       
   501                     } else if (function->name() == resetterName) {
       
   502                         property->addFunction(function, PropertyNode::Resetter);
       
   503                     } else if (function->name() == notifierName) {
       
   504                         property->addSignal(function, PropertyNode::Notifier);
       
   505                     }
       
   506                 }
       
   507             }
       
   508             ++c;
       
   509         }
       
   510         ++propEntry;
       
   511     }
       
   512 
       
   513     propEntry = priv->unresolvedPropertyMap.begin();
       
   514     while (propEntry != priv->unresolvedPropertyMap.end()) {
       
   515         PropertyNode *property = propEntry.key();
       
   516         // redo it to set the property functions
       
   517         if (property->overriddenFrom())
       
   518             property->setOverriddenFrom(property->overriddenFrom());
       
   519         ++propEntry;
       
   520     }
       
   521 
       
   522     priv->unresolvedPropertyMap.clear();
       
   523 }
       
   524 
       
   525 /*!
       
   526  */
       
   527 void Tree::resolveInheritance(int pass, ClassNode *classe)
       
   528 {
       
   529     if (pass == 0) {
       
   530 	QList<InheritanceBound> bounds = priv->unresolvedInheritanceMap[classe];
       
   531 	QList<InheritanceBound>::ConstIterator b = bounds.begin();
       
   532 	while (b != bounds.end()) {
       
   533 	    ClassNode *baseClass = (ClassNode*)findNode((*b).basePath,
       
   534                                                         Node::Class);
       
   535             if (!baseClass && (*b).parent)
       
   536                 baseClass = (ClassNode*)findNode((*b).basePath,
       
   537                                                  Node::Class,
       
   538                                                  (*b).parent);
       
   539 	    if (baseClass)
       
   540 		classe->addBaseClass((*b).access,
       
   541                                      baseClass,
       
   542                                      (*b).dataTypeWithTemplateArgs);
       
   543 	    ++b;
       
   544 	}
       
   545     }
       
   546     else {
       
   547 	NodeList::ConstIterator c = classe->childNodes().begin();
       
   548 	while (c != classe->childNodes().end()) {
       
   549 	    if ((*c)->type() == Node::Function) {
       
   550 		FunctionNode *func = (FunctionNode *) *c;
       
   551 		FunctionNode *from = findVirtualFunctionInBaseClasses(classe, func);
       
   552 		if (from != 0) {
       
   553 		    if (func->virtualness() == FunctionNode::NonVirtual)
       
   554 			func->setVirtualness(FunctionNode::ImpureVirtual);
       
   555 		    func->setReimplementedFrom(from);
       
   556 		}
       
   557 	    }
       
   558             else if ((*c)->type() == Node::Property) {
       
   559                 fixPropertyUsingBaseClasses(classe, static_cast<PropertyNode *>(*c));
       
   560             }
       
   561             ++c;
       
   562         }
       
   563     }
       
   564 }
       
   565 
       
   566 /*!
       
   567  */
       
   568 void Tree::resolveGroups()
       
   569 {
       
   570     GroupMap::const_iterator i;
       
   571     QString prevGroup;
       
   572     for (i = priv->groupMap.constBegin(); i != priv->groupMap.constEnd(); ++i) {
       
   573         if (i.value()->access() == Node::Private)
       
   574             continue;
       
   575 
       
   576         FakeNode *fake =
       
   577             static_cast<FakeNode*>(findNode(QStringList(i.key()),Node::Fake));
       
   578         if (fake && fake->subType() == Node::Group) {
       
   579             fake->addGroupMember(i.value());
       
   580         }
       
   581 #if 0        
       
   582         else {
       
   583             if (prevGroup != i.key())
       
   584                 i.value()->doc().location().warning(tr("No such group '%1'").arg(i.key()));
       
   585         }
       
   586 #endif        
       
   587 
       
   588         prevGroup = i.key();
       
   589     }
       
   590 
       
   591     //priv->groupMap.clear();
       
   592 }
       
   593 
       
   594 /*!
       
   595  */
       
   596 void Tree::resolveTargets()
       
   597 {
       
   598     // need recursion
       
   599 
       
   600     foreach (Node *child, roo.childNodes()) {
       
   601         if (child->type() == Node::Fake) {
       
   602             FakeNode *node = static_cast<FakeNode *>(child);
       
   603             priv->fakeNodesByTitle.insert(Doc::canonicalTitle(node->title()), node);
       
   604         }
       
   605 
       
   606         if (child->doc().hasTableOfContents()) {
       
   607             const QList<Atom *> &toc = child->doc().tableOfContents();
       
   608             Target target;
       
   609             target.node = child;
       
   610             target.priority = 3;
       
   611 
       
   612             for (int i = 0; i < toc.size(); ++i) {
       
   613                 target.atom = toc.at(i);
       
   614                 QString title = Text::sectionHeading(target.atom).toString();
       
   615                 if (!title.isEmpty())
       
   616                     priv->targetHash.insert(Doc::canonicalTitle(title), target);
       
   617             }
       
   618         }
       
   619         if (child->doc().hasKeywords()) {
       
   620             const QList<Atom *> &keywords = child->doc().keywords();
       
   621             Target target;
       
   622             target.node = child;
       
   623             target.priority = 1;
       
   624 
       
   625             for (int i = 0; i < keywords.size(); ++i) {
       
   626                 target.atom = keywords.at(i);
       
   627                 priv->targetHash.insert(Doc::canonicalTitle(target.atom->string()), target);
       
   628             }
       
   629         }
       
   630         if (child->doc().hasTargets()) {
       
   631             const QList<Atom *> &toc = child->doc().targets();
       
   632             Target target;
       
   633             target.node = child;
       
   634             target.priority = 2;
       
   635 
       
   636             for (int i = 0; i < toc.size(); ++i) {
       
   637                 target.atom = toc.at(i);
       
   638                 priv->targetHash.insert(Doc::canonicalTitle(target.atom->string()), target);
       
   639             }
       
   640         }
       
   641     }
       
   642 }
       
   643 
       
   644 /*!
       
   645  */
       
   646 void Tree::fixInheritance(NamespaceNode *rootNode)
       
   647 {
       
   648     if (!rootNode)
       
   649         rootNode = root();
       
   650 
       
   651     NodeList::ConstIterator c = rootNode->childNodes().begin();
       
   652     while (c != rootNode->childNodes().end()) {
       
   653         if ((*c)->type() == Node::Class)
       
   654             static_cast<ClassNode *>(*c)->fixBaseClasses();
       
   655         else if ((*c)->type() == Node::Namespace) {
       
   656             NamespaceNode *ns = static_cast<NamespaceNode*>(*c);
       
   657             fixInheritance(ns);
       
   658         }
       
   659         ++c;
       
   660     }
       
   661 }
       
   662 
       
   663 /*!
       
   664  */
       
   665 FunctionNode *Tree::findVirtualFunctionInBaseClasses(ClassNode *classe,
       
   666                                                      FunctionNode *clone)
       
   667 {
       
   668     QList<RelatedClass>::ConstIterator r = classe->baseClasses().begin();
       
   669     while (r != classe->baseClasses().end()) {
       
   670         FunctionNode *func;
       
   671         if (((func = findVirtualFunctionInBaseClasses((*r).node, clone)) != 0 ||
       
   672               (func = (*r).node->findFunctionNode(clone)) != 0)) {
       
   673             if (func->virtualness() != FunctionNode::NonVirtual)
       
   674                 return func;
       
   675         }
       
   676          ++r;
       
   677     }
       
   678     return 0;
       
   679 }
       
   680 
       
   681 /*!
       
   682  */
       
   683 void Tree::fixPropertyUsingBaseClasses(ClassNode *classe,
       
   684                                        PropertyNode *property)
       
   685 {
       
   686     QList<RelatedClass>::const_iterator r = classe->baseClasses().begin();
       
   687     while (r != classe->baseClasses().end()) {
       
   688 	PropertyNode *baseProperty =
       
   689             static_cast<PropertyNode *>(r->node->findNode(property->name(),
       
   690                                                           Node::Property));
       
   691         if (baseProperty) {
       
   692             fixPropertyUsingBaseClasses(r->node, baseProperty);
       
   693             property->setOverriddenFrom(baseProperty);
       
   694         }
       
   695         else {
       
   696             fixPropertyUsingBaseClasses(r->node, property);
       
   697         }
       
   698          ++r;
       
   699     }
       
   700 }
       
   701 
       
   702 /*!
       
   703  */
       
   704 NodeList Tree::allBaseClasses(const ClassNode *classe) const
       
   705 {
       
   706     NodeList result;
       
   707     foreach (const RelatedClass &r, classe->baseClasses()) {
       
   708         result += r.node;
       
   709         result += allBaseClasses(r.node);
       
   710     }
       
   711     return result;
       
   712 }
       
   713 
       
   714 /*!
       
   715  */
       
   716 void Tree::readIndexes(const QStringList &indexFiles)
       
   717 {
       
   718     foreach (const QString &indexFile, indexFiles)
       
   719         readIndexFile(indexFile);
       
   720 }
       
   721 
       
   722 /*!
       
   723   Read the QDomDocument at \a path and get the index from it.
       
   724  */
       
   725 void Tree::readIndexFile(const QString &path)
       
   726 {
       
   727     QFile file(path);
       
   728     if (file.open(QFile::ReadOnly)) {
       
   729         QDomDocument document;
       
   730         document.setContent(&file);
       
   731         file.close();
       
   732 
       
   733         QDomElement indexElement = document.documentElement();
       
   734         QString indexUrl = indexElement.attribute("url", "");
       
   735         priv->basesList.clear();
       
   736         priv->relatedList.clear();
       
   737 
       
   738         // Scan all elements in the XML file, constructing a map that contains
       
   739         // base classes for each class found.
       
   740 
       
   741         QDomElement child = indexElement.firstChildElement();
       
   742         while (!child.isNull()) {
       
   743             readIndexSection(child, root(), indexUrl);
       
   744             child = child.nextSiblingElement();
       
   745         }
       
   746 
       
   747         // Now that all the base classes have been found for this index,
       
   748         // arrange them into an inheritance hierarchy.
       
   749 
       
   750         resolveIndex();
       
   751     }
       
   752 }
       
   753 
       
   754 /*!
       
   755  */
       
   756 void Tree::readIndexSection(const QDomElement &element,
       
   757                             InnerNode *parent,
       
   758                             const QString &indexUrl)
       
   759 {
       
   760     QString name = element.attribute("name");
       
   761     QString href = element.attribute("href");
       
   762 
       
   763     Node *section;
       
   764     Location location;
       
   765 
       
   766     if (element.nodeName() == "namespace") {
       
   767         section = new NamespaceNode(parent, name);
       
   768 
       
   769         if (!indexUrl.isEmpty())
       
   770             location = Location(indexUrl + "/" + name.toLower() + ".html");
       
   771         else if (!indexUrl.isNull())
       
   772             location = Location(name.toLower() + ".html");
       
   773 
       
   774     }
       
   775     else if (element.nodeName() == "class") {
       
   776         section = new ClassNode(parent, name);
       
   777         priv->basesList.append(QPair<ClassNode*,QString>(
       
   778             static_cast<ClassNode*>(section), element.attribute("bases")));
       
   779 
       
   780         if (!indexUrl.isEmpty())
       
   781             location = Location(indexUrl + "/" + name.toLower() + ".html");
       
   782         else if (!indexUrl.isNull())
       
   783             location = Location(name.toLower() + ".html");
       
   784 
       
   785     }
       
   786     else if (element.nodeName() == "page") {
       
   787         Node::SubType subtype;
       
   788         if (element.attribute("subtype") == "example")
       
   789             subtype = Node::Example;
       
   790         else if (element.attribute("subtype") == "header")
       
   791             subtype = Node::HeaderFile;
       
   792         else if (element.attribute("subtype") == "file")
       
   793             subtype = Node::File;
       
   794         else if (element.attribute("subtype") == "group")
       
   795             subtype = Node::Group;
       
   796         else if (element.attribute("subtype") == "module")
       
   797             subtype = Node::Module;
       
   798         else if (element.attribute("subtype") == "page")
       
   799             subtype = Node::Page;
       
   800         else if (element.attribute("subtype") == "externalpage")
       
   801             subtype = Node::ExternalPage;
       
   802         else
       
   803             return;
       
   804 
       
   805         FakeNode *fakeNode = new FakeNode(parent, name, subtype);
       
   806         fakeNode->setTitle(element.attribute("title"));
       
   807 
       
   808         if (element.hasAttribute("location"))
       
   809             name = element.attribute("location", "");
       
   810 
       
   811         if (!indexUrl.isEmpty())
       
   812             location = Location(indexUrl + "/" + name);
       
   813         else if (!indexUrl.isNull())
       
   814             location = Location(name);
       
   815 
       
   816         section = fakeNode;
       
   817 
       
   818     }
       
   819     else if (element.nodeName() == "enum") {
       
   820         EnumNode *enumNode = new EnumNode(parent, name);
       
   821 
       
   822         if (!indexUrl.isEmpty())
       
   823             location =
       
   824                 Location(indexUrl + "/" + parent->name().toLower() + ".html");
       
   825         else if (!indexUrl.isNull())
       
   826             location = Location(parent->name().toLower() + ".html");
       
   827 
       
   828         QDomElement child = element.firstChildElement("value");
       
   829         while (!child.isNull()) {
       
   830             EnumItem item(child.attribute("name"), child.attribute("value"));
       
   831             enumNode->addItem(item);
       
   832             child = child.nextSiblingElement("value");
       
   833         }
       
   834 
       
   835         section = enumNode;
       
   836 
       
   837     } else if (element.nodeName() == "typedef") {
       
   838         section = new TypedefNode(parent, name);
       
   839 
       
   840         if (!indexUrl.isEmpty())
       
   841             location =
       
   842                 Location(indexUrl + "/" + parent->name().toLower() + ".html");
       
   843         else if (!indexUrl.isNull())
       
   844             location = Location(parent->name().toLower() + ".html");
       
   845 
       
   846     }
       
   847     else if (element.nodeName() == "property") {
       
   848         section = new PropertyNode(parent, name);
       
   849 
       
   850         if (!indexUrl.isEmpty())
       
   851             location =
       
   852                 Location(indexUrl + "/" + parent->name().toLower() + ".html");
       
   853         else if (!indexUrl.isNull())
       
   854             location = Location(parent->name().toLower() + ".html");
       
   855 
       
   856     } else if (element.nodeName() == "function") {
       
   857         FunctionNode::Virtualness virt;
       
   858         if (element.attribute("virtual") == "non")
       
   859             virt = FunctionNode::NonVirtual;
       
   860         else if (element.attribute("virtual") == "impure")
       
   861             virt = FunctionNode::ImpureVirtual;
       
   862         else if (element.attribute("virtual") == "pure")
       
   863             virt = FunctionNode::PureVirtual;
       
   864         else
       
   865             return;
       
   866 
       
   867         FunctionNode::Metaness meta;
       
   868         if (element.attribute("meta") == "plain")
       
   869             meta = FunctionNode::Plain;
       
   870         else if (element.attribute("meta") == "signal")
       
   871             meta = FunctionNode::Signal;
       
   872         else if (element.attribute("meta") == "slot")
       
   873             meta = FunctionNode::Slot;
       
   874         else if (element.attribute("meta") == "constructor")
       
   875             meta = FunctionNode::Ctor;
       
   876         else if (element.attribute("meta") == "destructor")
       
   877             meta = FunctionNode::Dtor;
       
   878         else if (element.attribute("meta") == "macro")
       
   879             meta = FunctionNode::MacroWithParams;
       
   880         else if (element.attribute("meta") == "macrowithparams")
       
   881             meta = FunctionNode::MacroWithParams;
       
   882         else if (element.attribute("meta") == "macrowithoutparams")
       
   883             meta = FunctionNode::MacroWithoutParams;
       
   884         else
       
   885             return;
       
   886 
       
   887         FunctionNode *functionNode = new FunctionNode(parent, name);
       
   888         functionNode->setReturnType(element.attribute("return"));
       
   889         functionNode->setVirtualness(virt);
       
   890         functionNode->setMetaness(meta);
       
   891         functionNode->setConst(element.attribute("const") == "true");
       
   892         functionNode->setStatic(element.attribute("static") == "true");
       
   893         functionNode->setOverload(element.attribute("overload") == "true");
       
   894 
       
   895         if (element.hasAttribute("relates")
       
   896             && element.attribute("relates") != parent->name()) {
       
   897             priv->relatedList.append(
       
   898                 QPair<FunctionNode*,QString>(functionNode,
       
   899                                              element.attribute("relates")));
       
   900         }
       
   901 
       
   902         QDomElement child = element.firstChildElement("parameter");
       
   903         while (!child.isNull()) {
       
   904             // Do not use the default value for the parameter; it is not
       
   905             // required, and has been known to cause problems.
       
   906             Parameter parameter(child.attribute("left"),
       
   907                                 child.attribute("right"),
       
   908                                 child.attribute("name"),
       
   909                                 ""); // child.attribute("default")
       
   910             functionNode->addParameter(parameter);
       
   911             child = child.nextSiblingElement("parameter");
       
   912         }
       
   913 
       
   914         section = functionNode;
       
   915 
       
   916         if (!indexUrl.isEmpty())
       
   917             location =
       
   918                 Location(indexUrl + "/" + parent->name().toLower() + ".html");
       
   919         else if (!indexUrl.isNull())
       
   920             location = Location(parent->name().toLower() + ".html");
       
   921 
       
   922     }
       
   923     else if (element.nodeName() == "variable") {
       
   924         section = new VariableNode(parent, name);
       
   925 
       
   926         if (!indexUrl.isEmpty())
       
   927             location = Location(indexUrl + "/" + parent->name().toLower() + ".html");
       
   928         else if (!indexUrl.isNull())
       
   929             location = Location(parent->name().toLower() + ".html");
       
   930 
       
   931     }
       
   932     else if (element.nodeName() == "keyword") {
       
   933         Target target;
       
   934         target.node = parent;
       
   935         target.priority = 1;
       
   936         target.atom = new Atom(Atom::Target, name);
       
   937         priv->targetHash.insert(name, target);
       
   938         return;
       
   939 
       
   940     }
       
   941     else if (element.nodeName() == "target") {
       
   942         Target target;
       
   943         target.node = parent;
       
   944         target.priority = 2;
       
   945         target.atom = new Atom(Atom::Target, name);
       
   946         priv->targetHash.insert(name, target);
       
   947         return;
       
   948 
       
   949     }
       
   950     else if (element.nodeName() == "contents") {
       
   951         Target target;
       
   952         target.node = parent;
       
   953         target.priority = 3;
       
   954         target.atom = new Atom(Atom::Target, name);
       
   955         priv->targetHash.insert(name, target);
       
   956         return;
       
   957 
       
   958     }
       
   959     else
       
   960         return;
       
   961 
       
   962     QString access = element.attribute("access");
       
   963     if (access == "public")
       
   964         section->setAccess(Node::Public);
       
   965     else if (access == "protected")
       
   966         section->setAccess(Node::Protected);
       
   967     else if (access == "private")
       
   968         section->setAccess(Node::Private);
       
   969     else
       
   970         section->setAccess(Node::Public);
       
   971 
       
   972     if (element.nodeName() != "page") {
       
   973         QString threadSafety = element.attribute("threadsafety");
       
   974         if (threadSafety == "non-reentrant")
       
   975             section->setThreadSafeness(Node::NonReentrant);
       
   976         else if (threadSafety == "reentrant")
       
   977             section->setThreadSafeness(Node::Reentrant);
       
   978         else if (threadSafety == "thread safe")
       
   979             section->setThreadSafeness(Node::ThreadSafe);
       
   980         else
       
   981             section->setThreadSafeness(Node::UnspecifiedSafeness);
       
   982     }
       
   983     else
       
   984         section->setThreadSafeness(Node::UnspecifiedSafeness);
       
   985 
       
   986     QString status = element.attribute("status");
       
   987     if (status == "compat")
       
   988         section->setStatus(Node::Compat);
       
   989     else if (status == "obsolete")
       
   990         section->setStatus(Node::Obsolete);
       
   991     else if (status == "deprecated")
       
   992         section->setStatus(Node::Deprecated);
       
   993     else if (status == "preliminary")
       
   994         section->setStatus(Node::Preliminary);
       
   995     else if (status == "commendable")
       
   996         section->setStatus(Node::Commendable);
       
   997     else if (status == "internal")
       
   998         section->setStatus(Node::Internal);
       
   999     else if (status == "main")
       
  1000         section->setStatus(Node::Main);
       
  1001     else
       
  1002         section->setStatus(Node::Commendable);
       
  1003 
       
  1004     section->setModuleName(element.attribute("module"));
       
  1005     if (!indexUrl.isEmpty()) {
       
  1006         if (indexUrl.startsWith("."))
       
  1007             section->setUrl(href);
       
  1008         else
       
  1009             section->setUrl(indexUrl + "/" + href);
       
  1010     }
       
  1011 
       
  1012     // Create some content for the node.
       
  1013     QSet<QString> emptySet;
       
  1014 
       
  1015     Doc doc(location, location, " ", emptySet); // placeholder
       
  1016     section->setDoc(doc);
       
  1017 
       
  1018     if (section->isInnerNode()) {
       
  1019         InnerNode *inner = static_cast<InnerNode*>(section);
       
  1020         if (inner) {
       
  1021             QDomElement child = element.firstChildElement();
       
  1022 
       
  1023             while (!child.isNull()) {
       
  1024                 if (element.nodeName() == "class")
       
  1025                     readIndexSection(child, inner, indexUrl);
       
  1026                 else if (element.nodeName() == "page")
       
  1027                     readIndexSection(child, inner, indexUrl);
       
  1028                 else if (element.nodeName() == "namespace" && !name.isEmpty())
       
  1029                     // The root node in the index is a namespace with an empty name.
       
  1030                     readIndexSection(child, inner, indexUrl);
       
  1031                 else
       
  1032                     readIndexSection(child, parent, indexUrl);
       
  1033 
       
  1034                 child = child.nextSiblingElement();
       
  1035             }
       
  1036         }
       
  1037     }
       
  1038 }
       
  1039 
       
  1040 /*!
       
  1041  */
       
  1042 QString Tree::readIndexText(const QDomElement &element)
       
  1043 {
       
  1044     QString text;
       
  1045     QDomNode child = element.firstChild();
       
  1046     while (!child.isNull()) {
       
  1047         if (child.isText())
       
  1048             text += child.toText().nodeValue();
       
  1049         child = child.nextSibling();
       
  1050     }
       
  1051     return text;
       
  1052 }
       
  1053 
       
  1054 /*!
       
  1055  */
       
  1056 void Tree::resolveIndex()
       
  1057 {
       
  1058     QPair<ClassNode*,QString> pair;
       
  1059 
       
  1060     foreach (pair, priv->basesList) {
       
  1061         foreach (const QString &base, pair.second.split(",")) {
       
  1062             Node *baseClass = root()->findNode(base, Node::Class);
       
  1063             if (baseClass) {
       
  1064                 pair.first->addBaseClass(Node::Public,
       
  1065                                          static_cast<ClassNode*>(baseClass));
       
  1066             }
       
  1067         }
       
  1068     }
       
  1069 
       
  1070     QPair<FunctionNode*,QString> relatedPair;
       
  1071 
       
  1072     foreach (relatedPair, priv->relatedList) {
       
  1073         Node *classNode = root()->findNode(relatedPair.second, Node::Class);
       
  1074         if (classNode)
       
  1075             relatedPair.first->setRelates(static_cast<ClassNode*>(classNode));
       
  1076     }
       
  1077 }
       
  1078 
       
  1079 /*!
       
  1080   Generate the index section with the given \a writer for the \a node
       
  1081   specified, returning true if an element was written; otherwise returns
       
  1082   false.
       
  1083  */
       
  1084 bool Tree::generateIndexSection(QXmlStreamWriter &writer,
       
  1085                                 const Node *node,
       
  1086                                 bool generateInternalNodes) const
       
  1087 {
       
  1088     if (!node->url().isEmpty())
       
  1089         return false;
       
  1090 
       
  1091     QString nodeName;
       
  1092     switch (node->type()) {
       
  1093         case Node::Namespace:
       
  1094             nodeName = "namespace";
       
  1095             break;
       
  1096         case Node::Class:
       
  1097             nodeName = "class";
       
  1098             break;
       
  1099         case Node::Fake:
       
  1100             nodeName = "page";
       
  1101             break;
       
  1102         case Node::Enum:
       
  1103             nodeName = "enum";
       
  1104             break;
       
  1105         case Node::Typedef:
       
  1106             nodeName = "typedef";
       
  1107             break;
       
  1108         case Node::Property:
       
  1109             nodeName = "property";
       
  1110             break;
       
  1111         case Node::Function:
       
  1112             nodeName = "function";
       
  1113             break;
       
  1114         case Node::Variable:
       
  1115             nodeName = "variable";
       
  1116             break;
       
  1117         case Node::Target:
       
  1118             nodeName = "target";
       
  1119             break;
       
  1120         default:
       
  1121             return false;
       
  1122     }
       
  1123 
       
  1124     QString access;
       
  1125     switch (node->access()) {
       
  1126         case Node::Public:
       
  1127             access = "public";
       
  1128             break;
       
  1129         case Node::Protected:
       
  1130             access = "protected";
       
  1131             break;
       
  1132         case Node::Private:
       
  1133             // Do not include private non-internal nodes in the index.
       
  1134             // (Internal public and protected nodes are marked as private
       
  1135             // by qdoc. We can check their internal status to determine
       
  1136             // whether they were really private to begin with.)
       
  1137             if (node->status() == Node::Internal && generateInternalNodes)
       
  1138                 access = "internal";
       
  1139             else
       
  1140                 return false;
       
  1141             break;
       
  1142         default:
       
  1143             return false;
       
  1144     }
       
  1145 
       
  1146     QString objName = node->name();
       
  1147 
       
  1148     // Special case: only the root node should have an empty name.
       
  1149     if (objName.isEmpty() && node != root())
       
  1150         return false;
       
  1151 
       
  1152     writer.writeStartElement(nodeName);
       
  1153 
       
  1154     QXmlStreamAttributes attributes;
       
  1155     writer.writeAttribute("access", access);
       
  1156 
       
  1157     if (node->type() != Node::Fake) {
       
  1158         QString threadSafety;
       
  1159         switch (node->threadSafeness()) {
       
  1160             case Node::NonReentrant:
       
  1161                 threadSafety = "non-reentrant";
       
  1162                 break;
       
  1163             case Node::Reentrant:
       
  1164                 threadSafety = "reentrant";
       
  1165                 break;
       
  1166             case Node::ThreadSafe:
       
  1167                 threadSafety = "thread safe";
       
  1168                 break;
       
  1169             case Node::UnspecifiedSafeness:
       
  1170             default:
       
  1171                 threadSafety = "unspecified";
       
  1172                 break;
       
  1173         }
       
  1174         writer.writeAttribute("threadsafety", threadSafety);
       
  1175     }
       
  1176 
       
  1177     QString status;
       
  1178     switch (node->status()) {
       
  1179         case Node::Compat:
       
  1180             status = "compat";
       
  1181             break;
       
  1182         case Node::Obsolete:
       
  1183             status = "obsolete";
       
  1184             break;
       
  1185         case Node::Deprecated:
       
  1186             status = "deprecated";
       
  1187             break;
       
  1188         case Node::Preliminary:
       
  1189             status = "preliminary";
       
  1190             break;
       
  1191         case Node::Commendable:
       
  1192             status = "commendable";
       
  1193             break;
       
  1194         case Node::Internal:
       
  1195             status = "internal";
       
  1196             break;
       
  1197         case Node::Main:
       
  1198         default:
       
  1199             status = "main";
       
  1200             break;
       
  1201     }
       
  1202     writer.writeAttribute("status", status);
       
  1203 
       
  1204     writer.writeAttribute("name", objName);
       
  1205     QString fullName = fullDocumentName(node);
       
  1206     if (fullName != objName)
       
  1207         writer.writeAttribute("fullname", fullName);
       
  1208     writer.writeAttribute("href", fullDocumentLocation(node));
       
  1209     if (node->type() != Node::Fake)
       
  1210         writer.writeAttribute("location", node->location().fileName());
       
  1211 
       
  1212     switch (node->type()) {
       
  1213 
       
  1214     case Node::Class:
       
  1215         {
       
  1216             // Classes contain information about their base classes.
       
  1217 
       
  1218             const ClassNode *classNode = static_cast<const ClassNode*>(node);
       
  1219             QList<RelatedClass> bases = classNode->baseClasses();
       
  1220             QSet<QString> baseStrings;
       
  1221             foreach (const RelatedClass &related, bases) {
       
  1222                 ClassNode *baseClassNode = related.node;
       
  1223                 baseStrings.insert(baseClassNode->name());
       
  1224             }
       
  1225             writer.writeAttribute("bases", QStringList(baseStrings.toList()).join(","));
       
  1226             writer.writeAttribute("module", node->moduleName());
       
  1227         }
       
  1228         break;
       
  1229 
       
  1230     case Node::Namespace:
       
  1231         writer.writeAttribute("module", node->moduleName());
       
  1232         break;
       
  1233 
       
  1234     case Node::Fake:
       
  1235         {
       
  1236             /*
       
  1237               Fake nodes (such as manual pages) contain subtypes,
       
  1238               titles and other attributes.
       
  1239             */
       
  1240 
       
  1241             const FakeNode *fakeNode = static_cast<const FakeNode*>(node);
       
  1242             switch (fakeNode->subType()) {
       
  1243                 case Node::Example:
       
  1244                     writer.writeAttribute("subtype", "example");
       
  1245                     break;
       
  1246                 case Node::HeaderFile:
       
  1247                     writer.writeAttribute("subtype", "header");
       
  1248                     break;
       
  1249                 case Node::File:
       
  1250                     writer.writeAttribute("subtype", "file");
       
  1251                     break;
       
  1252                 case Node::Group:
       
  1253                     writer.writeAttribute("subtype", "group");
       
  1254                     break;
       
  1255                 case Node::Module:
       
  1256                     writer.writeAttribute("subtype", "module");
       
  1257                     break;
       
  1258                 case Node::Page:
       
  1259                     writer.writeAttribute("subtype", "page");
       
  1260                     break;
       
  1261                 case Node::ExternalPage:
       
  1262                     writer.writeAttribute("subtype", "externalpage");
       
  1263                     break;
       
  1264                 default:
       
  1265                     break;
       
  1266             }
       
  1267             writer.writeAttribute("title", fakeNode->title());
       
  1268             writer.writeAttribute("fulltitle", fakeNode->fullTitle());
       
  1269             writer.writeAttribute("subtitle", fakeNode->subTitle());
       
  1270             writer.writeAttribute("location", fakeNode->doc().location().fileName());
       
  1271         }
       
  1272         break;
       
  1273 
       
  1274     case Node::Function:
       
  1275         {
       
  1276             /*
       
  1277               Function nodes contain information about the type of
       
  1278               function being described.
       
  1279             */
       
  1280 
       
  1281             const FunctionNode *functionNode =
       
  1282                 static_cast<const FunctionNode*>(node);
       
  1283 
       
  1284             switch (functionNode->virtualness()) {
       
  1285                 case FunctionNode::NonVirtual:
       
  1286                     writer.writeAttribute("virtual", "non");
       
  1287                     break;
       
  1288                 case FunctionNode::ImpureVirtual:
       
  1289                     writer.writeAttribute("virtual", "impure");
       
  1290                     break;
       
  1291                 case FunctionNode::PureVirtual:
       
  1292                     writer.writeAttribute("virtual", "pure");
       
  1293                     break;
       
  1294                 default:
       
  1295                     break;
       
  1296             }
       
  1297             switch (functionNode->metaness()) {
       
  1298                 case FunctionNode::Plain:
       
  1299                     writer.writeAttribute("meta", "plain");
       
  1300                     break;
       
  1301                 case FunctionNode::Signal:
       
  1302                     writer.writeAttribute("meta", "signal");
       
  1303                     break;
       
  1304                 case FunctionNode::Slot:
       
  1305                     writer.writeAttribute("meta", "slot");
       
  1306                     break;
       
  1307                 case FunctionNode::Ctor:
       
  1308                     writer.writeAttribute("meta", "constructor");
       
  1309                     break;
       
  1310                 case FunctionNode::Dtor:
       
  1311                     writer.writeAttribute("meta", "destructor");
       
  1312                     break;
       
  1313                 case FunctionNode::MacroWithParams:
       
  1314                     writer.writeAttribute("meta", "macrowithparams");
       
  1315                     break;
       
  1316                 case FunctionNode::MacroWithoutParams:
       
  1317                     writer.writeAttribute("meta", "macrowithoutparams");
       
  1318                     break;
       
  1319                 default:
       
  1320                     break;
       
  1321             }
       
  1322             writer.writeAttribute("const", functionNode->isConst()?"true":"false");
       
  1323             writer.writeAttribute("static", functionNode->isStatic()?"true":"false");
       
  1324             writer.writeAttribute("overload", functionNode->isOverload()?"true":"false");
       
  1325             if (functionNode->isOverload())
       
  1326                 writer.writeAttribute("overload-number", QString::number(functionNode->overloadNumber()));
       
  1327             if (functionNode->relates())
       
  1328                 writer.writeAttribute("relates", functionNode->relates()->name());
       
  1329             const PropertyNode *propertyNode = functionNode->associatedProperty();
       
  1330             if (propertyNode)
       
  1331                 writer.writeAttribute("associated-property", propertyNode->name());
       
  1332             writer.writeAttribute("type", functionNode->returnType());
       
  1333         }
       
  1334         break;
       
  1335 
       
  1336     case Node::Property:
       
  1337         {
       
  1338             const PropertyNode *propertyNode = static_cast<const PropertyNode*>(node);
       
  1339             writer.writeAttribute("type", propertyNode->dataType());
       
  1340             foreach (const Node *fnNode, propertyNode->getters()) {
       
  1341                 if (fnNode) {
       
  1342                     const FunctionNode *functionNode = static_cast<const FunctionNode*>(fnNode);
       
  1343                     writer.writeStartElement("getter");
       
  1344                     writer.writeAttribute("name", functionNode->name());
       
  1345                     writer.writeEndElement(); // getter
       
  1346                 }
       
  1347             }
       
  1348             foreach (const Node *fnNode, propertyNode->setters()) {
       
  1349                 if (fnNode) {
       
  1350                     const FunctionNode *functionNode = static_cast<const FunctionNode*>(fnNode);
       
  1351                     writer.writeStartElement("setter");
       
  1352                     writer.writeAttribute("name", functionNode->name());
       
  1353                     writer.writeEndElement(); // getter
       
  1354                 }
       
  1355             }
       
  1356             foreach (const Node *fnNode, propertyNode->resetters()) {
       
  1357                 if (fnNode) {
       
  1358                     const FunctionNode *functionNode = static_cast<const FunctionNode*>(fnNode);
       
  1359                     writer.writeStartElement("resetter");
       
  1360                     writer.writeAttribute("name", functionNode->name());
       
  1361                     writer.writeEndElement(); // getter
       
  1362                 }
       
  1363             }
       
  1364         }
       
  1365         break;
       
  1366 
       
  1367     case Node::Variable:
       
  1368         {
       
  1369             const VariableNode *variableNode =
       
  1370                 static_cast<const VariableNode*>(node);
       
  1371             writer.writeAttribute("type", variableNode->dataType());
       
  1372             writer.writeAttribute("static",
       
  1373                                   variableNode->isStatic() ? "true" : "false");
       
  1374         }
       
  1375         break;
       
  1376     default:
       
  1377         break;
       
  1378     }
       
  1379 
       
  1380     // Inner nodes and function nodes contain child nodes of some sort, either
       
  1381     // actual child nodes or function parameters. For these, we close the
       
  1382     // opening tag, create child elements, then add a closing tag for the
       
  1383     // element. Elements for all other nodes are closed in the opening tag.
       
  1384 
       
  1385     if (node->isInnerNode()) {
       
  1386 
       
  1387         const InnerNode *inner = static_cast<const InnerNode*>(node);
       
  1388 
       
  1389         // For internal pages, we canonicalize the target, keyword and content
       
  1390         // item names so that they can be used by qdoc for other sets of
       
  1391         // documentation.
       
  1392         // The reason we do this here is that we don't want to ruin
       
  1393         // externally composed indexes, containing non-qdoc-style target names
       
  1394         // when reading in indexes.
       
  1395 
       
  1396         if (inner->doc().hasTargets()) {
       
  1397             bool external = false;
       
  1398             if (inner->type() == Node::Fake) {
       
  1399                 const FakeNode *fakeNode = static_cast<const FakeNode *>(inner);
       
  1400                 if (fakeNode->subType() == Node::ExternalPage)
       
  1401                     external = true;
       
  1402             }
       
  1403 
       
  1404             foreach (const Atom *target, inner->doc().targets()) {
       
  1405                 QString targetName = target->string();
       
  1406                 if (!external)
       
  1407                     targetName = Doc::canonicalTitle(targetName);
       
  1408 
       
  1409                 writer.writeStartElement("target");
       
  1410                 writer.writeAttribute("name", targetName);
       
  1411                 writer.writeEndElement(); // target
       
  1412             }
       
  1413         }
       
  1414         if (inner->doc().hasKeywords()) {
       
  1415             foreach (const Atom *keyword, inner->doc().keywords()) {
       
  1416                 writer.writeStartElement("keyword");
       
  1417                 writer.writeAttribute("name",
       
  1418                                       Doc::canonicalTitle(keyword->string()));
       
  1419                 writer.writeEndElement(); // keyword
       
  1420             }
       
  1421         }
       
  1422         if (inner->doc().hasTableOfContents()) {
       
  1423             for (int i = 0; i < inner->doc().tableOfContents().size(); ++i) {
       
  1424                 Atom *item = inner->doc().tableOfContents()[i];
       
  1425                 int level = inner->doc().tableOfContentsLevels()[i];
       
  1426 
       
  1427                 QString title = Text::sectionHeading(item).toString();
       
  1428                 writer.writeStartElement("contents");
       
  1429                 writer.writeAttribute("name", Doc::canonicalTitle(title));
       
  1430                 writer.writeAttribute("title", title);
       
  1431                 writer.writeAttribute("level", QString::number(level));
       
  1432                 writer.writeEndElement(); // contents
       
  1433             }
       
  1434         }
       
  1435 
       
  1436     }
       
  1437     else if (node->type() == Node::Function) {
       
  1438 
       
  1439         const FunctionNode *functionNode = static_cast<const FunctionNode*>(node);
       
  1440         // Write a signature attribute for convenience.
       
  1441         QStringList signatureList;
       
  1442         QStringList resolvedParameters;
       
  1443 
       
  1444         foreach (const Parameter &parameter, functionNode->parameters()) {
       
  1445             QString leftType = parameter.leftType();
       
  1446             const Node *leftNode =
       
  1447                 const_cast<Tree*>(this)->findNode(parameter.leftType().split("::"),
       
  1448                 Node::Typedef, 0, SearchBaseClasses|NonFunction);
       
  1449             if (!leftNode) {
       
  1450                 leftNode = const_cast<Tree *>(this)->findNode(
       
  1451                     parameter.leftType().split("::"), Node::Typedef,
       
  1452                     node->parent(), SearchBaseClasses|NonFunction);
       
  1453             }
       
  1454             if (leftNode) {
       
  1455                 if (leftNode->type() == Node::Typedef) {
       
  1456                     const TypedefNode *typedefNode =
       
  1457                         static_cast<const TypedefNode *>(leftNode);
       
  1458                     if (typedefNode->associatedEnum()) {
       
  1459                         leftType = "QFlags<"+fullDocumentName(typedefNode->associatedEnum())+">";
       
  1460                     }
       
  1461                 }
       
  1462                 else
       
  1463                     leftType = fullDocumentName(leftNode);
       
  1464             }
       
  1465             resolvedParameters.append(leftType);
       
  1466             signatureList.append(leftType + " " + parameter.name());
       
  1467         }
       
  1468 
       
  1469         QString signature = functionNode->name()+"("+signatureList.join(", ")+")";
       
  1470         if (functionNode->isConst())
       
  1471             signature += " const";
       
  1472         writer.writeAttribute("signature", signature);
       
  1473 
       
  1474         for (int i = 0; i < functionNode->parameters().size(); ++i) {
       
  1475             Parameter parameter = functionNode->parameters()[i];
       
  1476             writer.writeStartElement("parameter");
       
  1477             writer.writeAttribute("left", resolvedParameters[i]);
       
  1478             writer.writeAttribute("right", parameter.rightType());
       
  1479             writer.writeAttribute("name", parameter.name());
       
  1480             writer.writeAttribute("default", parameter.defaultValue());
       
  1481             writer.writeEndElement(); // parameter
       
  1482         }
       
  1483 
       
  1484     }
       
  1485     else if (node->type() == Node::Enum) {
       
  1486 
       
  1487         const EnumNode *enumNode = static_cast<const EnumNode*>(node);
       
  1488         if (enumNode->flagsType()) {
       
  1489             writer.writeAttribute("typedef",
       
  1490                 fullDocumentName(enumNode->flagsType()));
       
  1491         }
       
  1492         foreach (const EnumItem &item, enumNode->items()) {
       
  1493             writer.writeStartElement("value");
       
  1494             writer.writeAttribute("name", item.name());
       
  1495             writer.writeAttribute("value", item.value());
       
  1496             writer.writeEndElement(); // value
       
  1497         }
       
  1498 
       
  1499     }
       
  1500     else if (node->type() == Node::Typedef) {
       
  1501 
       
  1502         const TypedefNode *typedefNode = static_cast<const TypedefNode*>(node);
       
  1503         if (typedefNode->associatedEnum()) {
       
  1504             writer.writeAttribute("enum",
       
  1505                 fullDocumentName(typedefNode->associatedEnum()));
       
  1506         }
       
  1507     }
       
  1508 
       
  1509     return true;
       
  1510 }
       
  1511 
       
  1512 /*!
       
  1513  */
       
  1514 void Tree::generateIndexSections(QXmlStreamWriter &writer,
       
  1515                                  const Node *node,
       
  1516                                  bool generateInternalNodes) const
       
  1517 {
       
  1518     if (generateIndexSection(writer, node, generateInternalNodes)) {
       
  1519 
       
  1520         if (node->isInnerNode()) {
       
  1521             const InnerNode *inner = static_cast<const InnerNode *>(node);
       
  1522 
       
  1523             // Recurse to write an element for this child node and all its children.
       
  1524             foreach (const Node *child, inner->childNodes())
       
  1525                 generateIndexSections(writer, child, generateInternalNodes);
       
  1526 
       
  1527 /*
       
  1528             foreach (const Node *child, inner->relatedNodes()) {
       
  1529                 QDomElement childElement = generateIndexSections(document, child);
       
  1530                 element.appendChild(childElement);
       
  1531             }
       
  1532 */
       
  1533         }
       
  1534         writer.writeEndElement();
       
  1535     }
       
  1536 }
       
  1537 
       
  1538 /*!
       
  1539   Outputs an index file.
       
  1540  */
       
  1541 void Tree::generateIndex(const QString &fileName,
       
  1542                          const QString &url,
       
  1543                          const QString &title,
       
  1544                          bool generateInternalNodes) const
       
  1545 {
       
  1546     QFile file(fileName);
       
  1547     if (!file.open(QFile::WriteOnly | QFile::Text))
       
  1548         return ;
       
  1549 
       
  1550     QXmlStreamWriter writer(&file);
       
  1551     writer.setAutoFormatting(true);
       
  1552     writer.writeStartDocument();
       
  1553     writer.writeDTD("<!DOCTYPE QDOCINDEX>");
       
  1554 
       
  1555     writer.writeStartElement("INDEX");
       
  1556     writer.writeAttribute("url", url);
       
  1557     writer.writeAttribute("title", title);
       
  1558     writer.writeAttribute("version", version());
       
  1559 
       
  1560     generateIndexSections(writer, root(), generateInternalNodes);
       
  1561 
       
  1562     writer.writeEndElement(); // INDEX
       
  1563     writer.writeEndElement(); // QDOCINDEX
       
  1564     writer.writeEndDocument();
       
  1565     file.close();
       
  1566 }
       
  1567 
       
  1568 /*!
       
  1569   Generate the tag file section with the given \a writer for the \a node
       
  1570   specified, returning true if an element was written; otherwise returns
       
  1571   false.
       
  1572  */
       
  1573 void Tree::generateTagFileCompounds(QXmlStreamWriter &writer,
       
  1574                                     const InnerNode *inner) const
       
  1575 {
       
  1576     foreach (const Node *node, inner->childNodes()) {
       
  1577 
       
  1578         if (!node->url().isEmpty())
       
  1579             continue;
       
  1580 
       
  1581         QString kind;
       
  1582         switch (node->type()) {
       
  1583             case Node::Namespace:
       
  1584                 kind = "namespace";
       
  1585                 break;
       
  1586             case Node::Class:
       
  1587                 kind = "class";
       
  1588                 break;
       
  1589             case Node::Enum:
       
  1590             case Node::Typedef:
       
  1591             case Node::Property:
       
  1592             case Node::Function:
       
  1593             case Node::Variable:
       
  1594             case Node::Target:
       
  1595             default:
       
  1596                 continue;
       
  1597         }
       
  1598 
       
  1599         QString access;
       
  1600         switch (node->access()) {
       
  1601             case Node::Public:
       
  1602                 access = "public";
       
  1603                 break;
       
  1604             case Node::Protected:
       
  1605                 access = "protected";
       
  1606                 break;
       
  1607             case Node::Private:
       
  1608             default:
       
  1609                 continue;
       
  1610         }
       
  1611 
       
  1612         QString objName = node->name();
       
  1613 
       
  1614         // Special case: only the root node should have an empty name.
       
  1615         if (objName.isEmpty() && node != root())
       
  1616             continue;
       
  1617 
       
  1618         // *** Write the starting tag for the element here. ***
       
  1619         writer.writeStartElement("compound");
       
  1620         writer.writeAttribute("kind", kind);
       
  1621 
       
  1622         if (node->type() == Node::Class) {
       
  1623             writer.writeTextElement("name", fullDocumentName(node));
       
  1624             writer.writeTextElement("filename", fullDocumentLocation(node));
       
  1625 
       
  1626             // Classes contain information about their base classes.
       
  1627             const ClassNode *classNode = static_cast<const ClassNode*>(node);
       
  1628             QList<RelatedClass> bases = classNode->baseClasses();
       
  1629             foreach (const RelatedClass &related, bases) {
       
  1630                 ClassNode *baseClassNode = related.node;
       
  1631                 writer.writeTextElement("base", baseClassNode->name());
       
  1632             }
       
  1633 
       
  1634             // Recurse to write all members.
       
  1635             generateTagFileMembers(writer, static_cast<const InnerNode *>(node));
       
  1636             writer.writeEndElement();
       
  1637 
       
  1638             // Recurse to write all compounds.
       
  1639             generateTagFileCompounds(writer, static_cast<const InnerNode *>(node));
       
  1640         } else {
       
  1641             writer.writeTextElement("name", fullDocumentName(node));
       
  1642             writer.writeTextElement("filename", fullDocumentLocation(node));
       
  1643 
       
  1644             // Recurse to write all members.
       
  1645             generateTagFileMembers(writer, static_cast<const InnerNode *>(node));
       
  1646             writer.writeEndElement();
       
  1647 
       
  1648             // Recurse to write all compounds.
       
  1649             generateTagFileCompounds(writer, static_cast<const InnerNode *>(node));
       
  1650         }
       
  1651     }
       
  1652 }
       
  1653 
       
  1654 /*!
       
  1655  */
       
  1656 void Tree::generateTagFileMembers(QXmlStreamWriter &writer,
       
  1657                                   const InnerNode *inner) const
       
  1658 {
       
  1659     foreach (const Node *node, inner->childNodes()) {
       
  1660 
       
  1661         if (!node->url().isEmpty())
       
  1662             continue;
       
  1663 
       
  1664         QString nodeName;
       
  1665         QString kind;
       
  1666         switch (node->type()) {
       
  1667             case Node::Enum:
       
  1668                 nodeName = "member";
       
  1669                 kind = "enum";
       
  1670                 break;
       
  1671             case Node::Typedef:
       
  1672                 nodeName = "member";
       
  1673                 kind = "typedef";
       
  1674                 break;
       
  1675             case Node::Property:
       
  1676                 nodeName = "member";
       
  1677                 kind = "property";
       
  1678                 break;
       
  1679             case Node::Function:
       
  1680                 nodeName = "member";
       
  1681                 kind = "function";
       
  1682                 break;
       
  1683             case Node::Namespace:
       
  1684                 nodeName = "namespace";
       
  1685                 break;
       
  1686             case Node::Class:
       
  1687                 nodeName = "class";
       
  1688                 break;
       
  1689             case Node::Variable:
       
  1690             case Node::Target:
       
  1691             default:
       
  1692                 continue;
       
  1693         }
       
  1694 
       
  1695         QString access;
       
  1696         switch (node->access()) {
       
  1697             case Node::Public:
       
  1698                 access = "public";
       
  1699                 break;
       
  1700             case Node::Protected:
       
  1701                 access = "protected";
       
  1702                 break;
       
  1703             case Node::Private:
       
  1704             default:
       
  1705                 continue;
       
  1706         }
       
  1707 
       
  1708         QString objName = node->name();
       
  1709 
       
  1710         // Special case: only the root node should have an empty name.
       
  1711         if (objName.isEmpty() && node != root())
       
  1712             continue;
       
  1713 
       
  1714         // *** Write the starting tag for the element here. ***
       
  1715         writer.writeStartElement(nodeName);
       
  1716         if (!kind.isEmpty())
       
  1717             writer.writeAttribute("kind", kind);
       
  1718 
       
  1719         switch (node->type()) {
       
  1720 
       
  1721         case Node::Class:
       
  1722             writer.writeCharacters(fullDocumentName(node));
       
  1723             writer.writeEndElement();
       
  1724             break;
       
  1725         case Node::Namespace:
       
  1726             writer.writeCharacters(fullDocumentName(node));
       
  1727             writer.writeEndElement();
       
  1728             break;
       
  1729         case Node::Function:
       
  1730             {
       
  1731                 /*
       
  1732                   Function nodes contain information about
       
  1733                   the type of function being described.
       
  1734                 */
       
  1735 
       
  1736                 const FunctionNode *functionNode =
       
  1737                     static_cast<const FunctionNode*>(node);
       
  1738                 writer.writeAttribute("protection", access);
       
  1739 
       
  1740                 switch (functionNode->virtualness()) {
       
  1741                     case FunctionNode::NonVirtual:
       
  1742                         writer.writeAttribute("virtualness", "non");
       
  1743                         break;
       
  1744                     case FunctionNode::ImpureVirtual:
       
  1745                         writer.writeAttribute("virtualness", "virtual");
       
  1746                         break;
       
  1747                     case FunctionNode::PureVirtual:
       
  1748                         writer.writeAttribute("virtual", "pure");
       
  1749                         break;
       
  1750                     default:
       
  1751                         break;
       
  1752                 }
       
  1753                 writer.writeAttribute("static",
       
  1754                                       functionNode->isStatic() ? "yes" : "no");
       
  1755 
       
  1756                 if (functionNode->virtualness() == FunctionNode::NonVirtual)
       
  1757                     writer.writeTextElement("type", functionNode->returnType());
       
  1758                 else
       
  1759                     writer.writeTextElement("type",
       
  1760                                             "virtual " + functionNode->returnType());
       
  1761 
       
  1762                 writer.writeTextElement("name", objName);
       
  1763                 QStringList pieces = fullDocumentLocation(node).split("#");
       
  1764                 writer.writeTextElement("anchorfile", pieces[0]);
       
  1765                 writer.writeTextElement("anchor", pieces[1]);
       
  1766 
       
  1767                 // Write a signature attribute for convenience.
       
  1768                 QStringList signatureList;
       
  1769 
       
  1770                 foreach (const Parameter &parameter, functionNode->parameters()) {
       
  1771                     QString leftType = parameter.leftType();
       
  1772                     const Node *leftNode = const_cast<Tree *>(this)->findNode(parameter.leftType().split("::"),
       
  1773                         Node::Typedef, 0, SearchBaseClasses|NonFunction);
       
  1774                     if (!leftNode) {
       
  1775                         leftNode = const_cast<Tree *>(this)->findNode(
       
  1776                             parameter.leftType().split("::"), Node::Typedef,
       
  1777                             node->parent(), SearchBaseClasses|NonFunction);
       
  1778                     }
       
  1779                     if (leftNode) {
       
  1780                         const TypedefNode *typedefNode = static_cast<const TypedefNode *>(leftNode);
       
  1781                         if (typedefNode->associatedEnum()) {
       
  1782                             leftType = "QFlags<"+fullDocumentName(typedefNode->associatedEnum())+">";
       
  1783                         }
       
  1784                     }
       
  1785                     signatureList.append(leftType + " " + parameter.name());
       
  1786                 }
       
  1787 
       
  1788                 QString signature = "("+signatureList.join(", ")+")";
       
  1789                 if (functionNode->isConst())
       
  1790                     signature += " const";
       
  1791                 if (functionNode->virtualness() == FunctionNode::PureVirtual)
       
  1792                     signature += " = 0";
       
  1793                 writer.writeTextElement("arglist", signature);
       
  1794             }
       
  1795             writer.writeEndElement(); // member
       
  1796             break;
       
  1797 
       
  1798         case Node::Property:
       
  1799             {
       
  1800                 const PropertyNode *propertyNode = static_cast<const PropertyNode*>(node);
       
  1801                 writer.writeAttribute("type", propertyNode->dataType());
       
  1802                 writer.writeTextElement("name", objName);
       
  1803                 QStringList pieces = fullDocumentLocation(node).split("#");
       
  1804                 writer.writeTextElement("anchorfile", pieces[0]);
       
  1805                 writer.writeTextElement("anchor", pieces[1]);
       
  1806                 writer.writeTextElement("arglist", "");
       
  1807             }
       
  1808             writer.writeEndElement(); // member
       
  1809             break;
       
  1810 
       
  1811         case Node::Enum:
       
  1812             {
       
  1813                 const EnumNode *enumNode = static_cast<const EnumNode*>(node);
       
  1814                 writer.writeTextElement("name", objName);
       
  1815                 QStringList pieces = fullDocumentLocation(node).split("#");
       
  1816                 writer.writeTextElement("anchor", pieces[1]);
       
  1817                 writer.writeTextElement("arglist", "");
       
  1818                 writer.writeEndElement(); // member
       
  1819 
       
  1820                 for (int i = 0; i < enumNode->items().size(); ++i) {
       
  1821                     EnumItem item = enumNode->items().value(i);
       
  1822                     writer.writeStartElement("member");
       
  1823                     writer.writeAttribute("name", item.name());
       
  1824                     writer.writeTextElement("anchor", pieces[1]);
       
  1825                     writer.writeTextElement("arglist", "");
       
  1826                     writer.writeEndElement(); // member
       
  1827                 }
       
  1828             }
       
  1829             break;
       
  1830 
       
  1831         case Node::Typedef:
       
  1832             {
       
  1833                 const TypedefNode *typedefNode = static_cast<const TypedefNode*>(node);
       
  1834                 if (typedefNode->associatedEnum())
       
  1835                     writer.writeAttribute("type", fullDocumentName(typedefNode->associatedEnum()));
       
  1836                 else
       
  1837                     writer.writeAttribute("type", "");
       
  1838                 writer.writeTextElement("name", objName);
       
  1839                 QStringList pieces = fullDocumentLocation(node).split("#");
       
  1840                 writer.writeTextElement("anchorfile", pieces[0]);
       
  1841                 writer.writeTextElement("anchor", pieces[1]);
       
  1842                 writer.writeTextElement("arglist", "");
       
  1843             }
       
  1844             writer.writeEndElement(); // member
       
  1845             break;
       
  1846 
       
  1847         case Node::Variable:
       
  1848         case Node::Target:
       
  1849         default:
       
  1850             break;
       
  1851         }
       
  1852     }
       
  1853 }
       
  1854 
       
  1855 /*!
       
  1856  */
       
  1857 void Tree::generateTagFile(const QString &fileName) const
       
  1858 {
       
  1859     QFile file(fileName);
       
  1860     if (!file.open(QFile::WriteOnly | QFile::Text))
       
  1861         return ;
       
  1862 
       
  1863     QXmlStreamWriter writer(&file);
       
  1864     writer.setAutoFormatting(true);
       
  1865     writer.writeStartDocument();
       
  1866 
       
  1867     writer.writeStartElement("tagfile");
       
  1868 
       
  1869     generateTagFileCompounds(writer, root());
       
  1870 
       
  1871     writer.writeEndElement(); // tagfile
       
  1872     writer.writeEndDocument();
       
  1873     file.close();
       
  1874 }
       
  1875 
       
  1876 /*!
       
  1877  */
       
  1878 void Tree::addExternalLink(const QString &url, const Node *relative)
       
  1879 {
       
  1880     FakeNode *fakeNode = new FakeNode(root(), url, Node::ExternalPage);
       
  1881     fakeNode->setAccess(Node::Public);
       
  1882 
       
  1883     // Create some content for the node.
       
  1884     QSet<QString> emptySet;
       
  1885     Location location(relative->doc().location());
       
  1886     Doc doc(location, location, " ", emptySet); // placeholder
       
  1887     fakeNode->setDoc(doc);
       
  1888 }
       
  1889 
       
  1890 /*!
       
  1891   Returns the full document location for HTML-based documentation.
       
  1892   This should be moved into the HTML generator.
       
  1893  */
       
  1894 QString Tree::fullDocumentLocation(const Node *node) const
       
  1895 {
       
  1896     if (!node)
       
  1897         return "";
       
  1898     if (!node->url().isEmpty())
       
  1899         return node->url();
       
  1900 
       
  1901     QString parentName;
       
  1902     QString anchorRef;
       
  1903 
       
  1904     if (node->type() == Node::Namespace) {
       
  1905 
       
  1906         // The root namespace has no name - check for this before creating
       
  1907         // an attribute containing the location of any documentation.
       
  1908 
       
  1909         if (!node->fileBase().isEmpty())
       
  1910             parentName = node->fileBase() + ".html";
       
  1911         else
       
  1912             return "";
       
  1913     }
       
  1914     else if (node->type() == Node::Fake) {
       
  1915 #ifdef QDOC_QML
       
  1916         if (node->subType() == Node::QmlClass)
       
  1917             return "qml-" + node->fileBase() + ".html";
       
  1918         else
       
  1919 #endif
       
  1920         parentName = node->fileBase() + ".html";
       
  1921     }
       
  1922     else if (node->fileBase().isEmpty())
       
  1923         return "";
       
  1924 
       
  1925     Node *parentNode = 0;
       
  1926 
       
  1927     if ((parentNode = node->relates()))
       
  1928         parentName = fullDocumentLocation(node->relates());
       
  1929     else if ((parentNode = node->parent()))
       
  1930         parentName = fullDocumentLocation(node->parent());
       
  1931 
       
  1932     switch (node->type()) {
       
  1933         case Node::Class:
       
  1934         case Node::Namespace:
       
  1935             if (parentNode && !parentNode->name().isEmpty())
       
  1936                 parentName = parentName.replace(".html", "") + "-"
       
  1937                            + node->fileBase().toLower() + ".html";
       
  1938             else
       
  1939                 parentName = node->fileBase() + ".html";
       
  1940             break;
       
  1941         case Node::Function:
       
  1942             {
       
  1943                 /*
       
  1944                   Functions can be destructors, overloaded, or
       
  1945                   have associated properties.
       
  1946                 */
       
  1947                 const FunctionNode *functionNode =
       
  1948                     static_cast<const FunctionNode *>(node);
       
  1949 
       
  1950                 if (functionNode->metaness() == FunctionNode::Dtor)
       
  1951                     anchorRef = "#dtor." + functionNode->name().mid(1);
       
  1952 
       
  1953                 else if (functionNode->associatedProperty())
       
  1954                     return fullDocumentLocation(functionNode->associatedProperty());
       
  1955 
       
  1956                 else if (functionNode->overloadNumber() > 1)
       
  1957                     anchorRef = "#" + functionNode->name()
       
  1958                               + "-" + QString::number(functionNode->overloadNumber());
       
  1959                 else
       
  1960                     anchorRef = "#" + functionNode->name();
       
  1961             }
       
  1962 
       
  1963             /*
       
  1964               Use node->name() instead of node->fileBase() as
       
  1965               the latter returns the name in lower-case. For
       
  1966               HTML anchors, we need to preserve the case.
       
  1967             */
       
  1968             break;
       
  1969         case Node::Enum:
       
  1970             anchorRef = "#" + node->name() + "-enum";
       
  1971             break;
       
  1972         case Node::Typedef:
       
  1973             anchorRef = "#" + node->name() + "-typedef";
       
  1974             break;
       
  1975         case Node::Property:
       
  1976             anchorRef = "#" + node->name() + "-prop";
       
  1977             break;
       
  1978         case Node::Variable:
       
  1979             anchorRef = "#" + node->name() + "-var";
       
  1980             break;
       
  1981         case Node::Target:
       
  1982             anchorRef = "#" + Doc::canonicalTitle(node->name());
       
  1983             break;
       
  1984         case Node::Fake:
       
  1985             {
       
  1986             /*
       
  1987               Use node->fileBase() for fake nodes because they are represented
       
  1988               by pages whose file names are lower-case.
       
  1989             */
       
  1990             parentName = node->fileBase();
       
  1991             parentName.replace("/", "-").replace(".", "-");
       
  1992             parentName += ".html";
       
  1993             }
       
  1994             break;
       
  1995         default:
       
  1996             break;
       
  1997     }
       
  1998 
       
  1999     // Various objects can be compat (deprecated) or obsolete.
       
  2000     if (node->type() != Node::Class && node->type() != Node::Namespace) {
       
  2001         switch (node->status()) {
       
  2002         case Node::Compat:
       
  2003             parentName.replace(".html", "-qt3.html");
       
  2004             break;
       
  2005         case Node::Obsolete:
       
  2006             parentName.replace(".html", "-obsolete.html");
       
  2007             break;
       
  2008         default:
       
  2009             ;
       
  2010         }
       
  2011     }
       
  2012 
       
  2013     return parentName.toLower() + anchorRef;
       
  2014 }
       
  2015 
       
  2016 /*!
       
  2017  */
       
  2018 QString Tree::fullDocumentName(const Node *node) const
       
  2019 {
       
  2020     if (!node)
       
  2021         return "";
       
  2022 
       
  2023     QStringList pieces;
       
  2024     const Node *n = node;
       
  2025 
       
  2026     do {
       
  2027         if (!n->name().isEmpty())
       
  2028             pieces.insert(0, n->name());
       
  2029 
       
  2030         if (n->type() == Node::Fake)
       
  2031             break;
       
  2032 
       
  2033         // Examine the parent node if one exists.
       
  2034         if (n->parent())
       
  2035             n = n->parent();
       
  2036         else
       
  2037             break;
       
  2038     } while (true);
       
  2039 
       
  2040     // Create a name based on the type of the ancestor node.
       
  2041     if (n->type() == Node::Fake)
       
  2042         return pieces.join("#");
       
  2043     else
       
  2044         return pieces.join("::");
       
  2045 }
       
  2046 
       
  2047 QT_END_NAMESPACE