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