qdoc: Make links to \target work intermodule
[profile/ivi/qtbase.git] / src / tools / qdoc / qdocindexfiles.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the tools applications of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia.  For licensing terms and
14 ** conditions see http://qt.digia.com/licensing.  For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights.  These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file.  Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qdom.h"
43 #include "qxmlstream.h"
44 #include "qdocindexfiles.h"
45 #include "qdoctagfiles.h"
46 #include "config.h"
47 #include "qdocdatabase.h"
48 #include "location.h"
49 #include "atom.h"
50 #include "generator.h"
51 #include <qdebug.h>
52
53 QT_BEGIN_NAMESPACE
54
55 /*!
56   \class QDocIndexFiles
57
58   This class handles qdoc index files.
59  */
60
61 QDocIndexFiles* QDocIndexFiles::qdocIndexFiles_ = NULL;
62
63 /*!
64   Constructs the singleton QDocIndexFiles.
65  */
66 QDocIndexFiles::QDocIndexFiles()
67 {
68     qdb_ = QDocDatabase::qdocDB();
69 }
70
71 /*!
72   Destroys the singleton QDocIndexFiles.
73  */
74 QDocIndexFiles::~QDocIndexFiles()
75 {
76     qdb_ = 0;
77 }
78
79 /*!
80   Creates the singleton. Allows only one instance of the class
81   to be created. Returns a pointer to the singleton.
82  */
83 QDocIndexFiles* QDocIndexFiles::qdocIndexFiles()
84 {
85    if (!qdocIndexFiles_)
86       qdocIndexFiles_ = new QDocIndexFiles;
87    return qdocIndexFiles_;
88 }
89
90 /*!
91   Destroys the singleton.
92  */
93 void QDocIndexFiles::destroyQDocIndexFiles()
94 {
95     if (qdocIndexFiles_) {
96         delete qdocIndexFiles_;
97         qdocIndexFiles_ = 0;
98     }
99 }
100
101 /*!
102   Reads and parses the list of index files in \a indexFiles.
103  */
104 void QDocIndexFiles::readIndexes(const QStringList& indexFiles)
105 {
106     foreach (const QString& indexFile, indexFiles) {
107         QString msg = "Loading index file: " + indexFile;
108         Location::logToStdErr(msg);
109         readIndexFile(indexFile);
110     }
111 }
112
113 /*!
114   Reads and parses the index file at \a path.
115  */
116 void QDocIndexFiles::readIndexFile(const QString& path)
117 {
118     QFile file(path);
119     if (file.open(QFile::ReadOnly)) {
120         QDomDocument document;
121         document.setContent(&file);
122         file.close();
123
124         QDomElement indexElement = document.documentElement();
125
126         // Generate a relative URL between the install dir and the index file
127         // when the -installdir command line option is set.
128         QString indexUrl;
129         if (Config::installDir.isEmpty()) {
130             indexUrl = indexElement.attribute("url", QString());
131         }
132         else {
133             // Use a fake directory, since we will copy the output to a sub directory of
134             // installDir when using "make install". This is just for a proper relative path.
135             QDir installDir(path.section('/', 0, -3) + "/outputdir");
136             indexUrl = installDir.relativeFilePath(path).section('/', 0, -2);
137         }
138
139         basesList_.clear();
140         relatedList_.clear();
141
142         // Scan all elements in the XML file, constructing a map that contains
143         // base classes for each class found.
144
145         QDomElement child = indexElement.firstChildElement();
146         while (!child.isNull()) {
147             readIndexSection(child, qdb_->treeRoot(), indexUrl);
148             child = child.nextSiblingElement();
149         }
150
151         // Now that all the base classes have been found for this index,
152         // arrange them into an inheritance hierarchy.
153         resolveIndex();
154     }
155 }
156
157 /*!
158   Read a <section> element from the index file and create the
159   appropriate node(s).
160  */
161 void QDocIndexFiles::readIndexSection(const QDomElement& element,
162                                       InnerNode* parent,
163                                       const QString& indexUrl)
164 {
165     QString name = element.attribute("name");
166     QString href = element.attribute("href");
167     Node* node;
168     Location location;
169
170     if (element.nodeName() == "namespace") {
171         node = new NamespaceNode(parent, name);
172
173         if (!indexUrl.isEmpty())
174             location = Location(indexUrl + QLatin1Char('/') + name.toLower() + ".html");
175         else if (!indexUrl.isNull())
176             location = Location(name.toLower() + ".html");
177
178     }
179     else if (element.nodeName() == "class") {
180         node = new ClassNode(parent, name);
181         basesList_.append(QPair<ClassNode*,QString>(static_cast<ClassNode*>(node),
182                                                    element.attribute("bases")));
183
184         if (!indexUrl.isEmpty())
185             location = Location(indexUrl + QLatin1Char('/') + name.toLower() + ".html");
186         else if (!indexUrl.isNull())
187             location = Location(name.toLower() + ".html");
188     }
189     else if ((element.nodeName() == "qmlclass") ||
190              ((element.nodeName() == "page") && (element.attribute("subtype") == "qmlclass"))) {
191         QmlClassNode* qcn = new QmlClassNode(parent, name);
192         qcn->setTitle(element.attribute("title"));
193         if (element.hasAttribute("location"))
194             name = element.attribute("location", QString());
195         if (!indexUrl.isEmpty())
196             location = Location(indexUrl + QLatin1Char('/') + name);
197         else if (!indexUrl.isNull())
198             location = Location(name);
199         node = qcn;
200     }
201     else if (element.nodeName() == "qmlbasictype") {
202         QmlBasicTypeNode* qbtn = new QmlBasicTypeNode(parent, name);
203         qbtn->setTitle(element.attribute("title"));
204         if (element.hasAttribute("location"))
205             name = element.attribute("location", QString());
206         if (!indexUrl.isEmpty())
207             location = Location(indexUrl + QLatin1Char('/') + name);
208         else if (!indexUrl.isNull())
209             location = Location(name);
210         node = qbtn;
211     }
212     else if (element.nodeName() == "page") {
213         Node::SubType subtype;
214         Node::PageType ptype = Node::NoPageType;
215         if (element.attribute("subtype") == "example") {
216             subtype = Node::Example;
217             ptype = Node::ExamplePage;
218         }
219         else if (element.attribute("subtype") == "header") {
220             subtype = Node::HeaderFile;
221             ptype = Node::ApiPage;
222         }
223         else if (element.attribute("subtype") == "file") {
224             subtype = Node::File;
225             ptype = Node::NoPageType;
226         }
227         else if (element.attribute("subtype") == "group") {
228             subtype = Node::Group;
229             ptype = Node::OverviewPage;
230         }
231         else if (element.attribute("subtype") == "module") {
232             subtype = Node::Module;
233             ptype = Node::OverviewPage;
234         }
235         else if (element.attribute("subtype") == "qmlmodule") {
236             subtype = Node::QmlModule;
237             ptype = Node::OverviewPage;
238         }
239         else if (element.attribute("subtype") == "page") {
240             subtype = Node::Page;
241             ptype = Node::ArticlePage;
242         }
243         else if (element.attribute("subtype") == "externalpage") {
244             subtype = Node::ExternalPage;
245             ptype = Node::ArticlePage;
246         }
247         else if (element.attribute("subtype") == "qmlclass") {
248             subtype = Node::QmlClass;
249             ptype = Node::ApiPage;
250         }
251         else if (element.attribute("subtype") == "qmlpropertygroup") {
252             subtype = Node::QmlPropertyGroup;
253             ptype = Node::ApiPage;
254         }
255         else if (element.attribute("subtype") == "qmlbasictype") {
256             subtype = Node::QmlBasicType;
257             ptype = Node::ApiPage;
258         }
259         else
260             return;
261
262         DocNode* docNode = new DocNode(parent, name, subtype, ptype);
263         docNode->setTitle(element.attribute("title"));
264
265         if (element.hasAttribute("location"))
266             name = element.attribute("location", QString());
267
268         if (!indexUrl.isEmpty())
269             location = Location(indexUrl + QLatin1Char('/') + name);
270         else if (!indexUrl.isNull())
271             location = Location(name);
272
273         node = docNode;
274
275     }
276     else if (element.nodeName() == "enum") {
277         EnumNode* enumNode = new EnumNode(parent, name);
278
279         if (!indexUrl.isEmpty())
280             location = Location(indexUrl + QLatin1Char('/') + parent->name().toLower() + ".html");
281         else if (!indexUrl.isNull())
282             location = Location(parent->name().toLower() + ".html");
283
284         QDomElement child = element.firstChildElement("value");
285         while (!child.isNull()) {
286             EnumItem item(child.attribute("name"), child.attribute("value"));
287             enumNode->addItem(item);
288             child = child.nextSiblingElement("value");
289         }
290
291         node = enumNode;
292
293     }
294     else if (element.nodeName() == "typedef") {
295         node = new TypedefNode(parent, name);
296
297         if (!indexUrl.isEmpty())
298             location = Location(indexUrl + QLatin1Char('/') + parent->name().toLower() + ".html");
299         else if (!indexUrl.isNull())
300             location = Location(parent->name().toLower() + ".html");
301
302     }
303     else if (element.nodeName() == "property") {
304         node = new PropertyNode(parent, name);
305
306         if (!indexUrl.isEmpty())
307             location = Location(indexUrl + QLatin1Char('/') + parent->name().toLower() + ".html");
308         else if (!indexUrl.isNull())
309             location = Location(parent->name().toLower() + ".html");
310
311     }
312     else if (element.nodeName() == "function") {
313         FunctionNode::Virtualness virt;
314         if (element.attribute("virtual") == "non")
315             virt = FunctionNode::NonVirtual;
316         else if (element.attribute("virtual") == "impure")
317             virt = FunctionNode::ImpureVirtual;
318         else if (element.attribute("virtual") == "pure")
319             virt = FunctionNode::PureVirtual;
320         else
321             return;
322
323         FunctionNode::Metaness meta;
324         if (element.attribute("meta") == "plain")
325             meta = FunctionNode::Plain;
326         else if (element.attribute("meta") == "signal")
327             meta = FunctionNode::Signal;
328         else if (element.attribute("meta") == "slot")
329             meta = FunctionNode::Slot;
330         else if (element.attribute("meta") == "constructor")
331             meta = FunctionNode::Ctor;
332         else if (element.attribute("meta") == "destructor")
333             meta = FunctionNode::Dtor;
334         else if (element.attribute("meta") == "macro")
335             meta = FunctionNode::MacroWithParams;
336         else if (element.attribute("meta") == "macrowithparams")
337             meta = FunctionNode::MacroWithParams;
338         else if (element.attribute("meta") == "macrowithoutparams")
339             meta = FunctionNode::MacroWithoutParams;
340         else
341             return;
342
343         FunctionNode* functionNode = new FunctionNode(parent, name);
344         functionNode->setReturnType(element.attribute("return"));
345         functionNode->setVirtualness(virt);
346         functionNode->setMetaness(meta);
347         functionNode->setConst(element.attribute("const") == "true");
348         functionNode->setStatic(element.attribute("static") == "true");
349         functionNode->setOverload(element.attribute("overload") == "true");
350
351         if (element.hasAttribute("relates")
352                 && element.attribute("relates") != parent->name()) {
353             relatedList_.append(
354                         QPair<FunctionNode*,QString>(functionNode,
355                                                      element.attribute("relates")));
356         }
357
358         QDomElement child = element.firstChildElement("parameter");
359         while (!child.isNull()) {
360             // Do not use the default value for the parameter; it is not
361             // required, and has been known to cause problems.
362             Parameter parameter(child.attribute("left"),
363                                 child.attribute("right"),
364                                 child.attribute("name"),
365                                 QString()); // child.attribute("default")
366             functionNode->addParameter(parameter);
367             child = child.nextSiblingElement("parameter");
368         }
369
370         node = functionNode;
371         if (!indexUrl.isEmpty())
372             location =  Location(indexUrl + QLatin1Char('/') + parent->name().toLower() + ".html");
373         else if (!indexUrl.isNull())
374             location = Location(parent->name().toLower() + ".html");
375     }
376     else if (element.nodeName() == "variable") {
377         node = new VariableNode(parent, name);
378         if (!indexUrl.isEmpty())
379             location = Location(indexUrl + QLatin1Char('/') + parent->name().toLower() + ".html");
380         else if (!indexUrl.isNull())
381             location = Location(parent->name().toLower() + ".html");
382     }
383     else if (element.nodeName() == "keyword") {
384         qdb_->insertTarget(name, TargetRec::Keyword, parent, 1);
385         return;
386     }
387     else if (element.nodeName() == "target") {
388         qdb_->insertTarget(name, TargetRec::Target, parent, 2);
389         return;
390     }
391     else if (element.nodeName() == "contents") {
392         qdb_->insertTarget(name, TargetRec::Contents, parent, 3);
393         return;
394     }
395     else
396         return;
397
398     QString access = element.attribute("access");
399     if (access == "public")
400         node->setAccess(Node::Public);
401     else if (access == "protected")
402         node->setAccess(Node::Protected);
403     else if (access == "private")
404         node->setAccess(Node::Private);
405     else
406         node->setAccess(Node::Public);
407
408     if ((element.nodeName() != "page") &&
409             (element.nodeName() != "qmlclass") &&
410             (element.nodeName() != "qmlbasictype")) {
411         QString threadSafety = element.attribute("threadsafety");
412         if (threadSafety == "non-reentrant")
413             node->setThreadSafeness(Node::NonReentrant);
414         else if (threadSafety == "reentrant")
415             node->setThreadSafeness(Node::Reentrant);
416         else if (threadSafety == "thread safe")
417             node->setThreadSafeness(Node::ThreadSafe);
418         else
419             node->setThreadSafeness(Node::UnspecifiedSafeness);
420     }
421     else
422         node->setThreadSafeness(Node::UnspecifiedSafeness);
423
424     QString status = element.attribute("status");
425     if (status == "compat")
426         node->setStatus(Node::Compat);
427     else if (status == "obsolete")
428         node->setStatus(Node::Obsolete);
429     else if (status == "deprecated")
430         node->setStatus(Node::Deprecated);
431     else if (status == "preliminary")
432         node->setStatus(Node::Preliminary);
433     else if (status == "commendable")
434         node->setStatus(Node::Commendable);
435     else if (status == "internal")
436         node->setStatus(Node::Internal);
437     else if (status == "main")
438         node->setStatus(Node::Main);
439     else
440         node->setStatus(Node::Commendable);
441
442     QString moduleName = element.attribute("module");
443     if (!moduleName.isEmpty())
444         node->setModuleName(moduleName);
445     if (!indexUrl.isEmpty()) {
446         node->setUrl(indexUrl + QLatin1Char('/') + href);
447     }
448
449     QString since = element.attribute("since");
450     if (!since.isEmpty()) {
451         node->setSince(since);
452     }
453
454     QString groupsAttr = element.attribute("groups");
455     if (!groupsAttr.isEmpty()) {
456         QStringList groupNames = groupsAttr.split(",");
457         for (int i=0; i<groupNames.size(); ++i) {
458             DocNode* dn = qdb_->findGroup(groupNames[i]);
459             if (dn) {
460                 dn->addMember(node);
461             }
462             else {
463                 qDebug() << "NODE:" << node->name() << "GROUPS:" << groupNames;
464                 qDebug() << "DID NOT FIND GROUP:" << dn->name() << "for:" << node->name();
465             }
466         }
467     }
468
469     // Create some content for the node.
470     QSet<QString> emptySet;
471     Doc doc(location, location, " ", emptySet); // placeholder
472     node->setDoc(doc);
473     node->setIndexNodeFlag();
474
475     if (node->isInnerNode()) {
476         InnerNode* inner = static_cast<InnerNode*>(node);
477         QDomElement child = element.firstChildElement();
478         while (!child.isNull()) {
479             if (element.nodeName() == "class")
480                 readIndexSection(child, inner, indexUrl);
481             else if (element.nodeName() == "qmlclass")
482                 readIndexSection(child, inner, indexUrl);
483             else if (element.nodeName() == "page")
484                 readIndexSection(child, inner, indexUrl);
485             else if (element.nodeName() == "namespace" && !name.isEmpty())
486                 // The root node in the index is a namespace with an empty name.
487                 readIndexSection(child, inner, indexUrl);
488             else
489                 readIndexSection(child, parent, indexUrl);
490             child = child.nextSiblingElement();
491         }
492     }
493 }
494
495 /*!
496  */
497 void QDocIndexFiles::resolveIndex()
498 {
499     QPair<ClassNode*,QString> pair;
500     foreach (pair, basesList_) {
501         foreach (const QString& base, pair.second.split(QLatin1Char(','))) {
502             Node* n = qdb_->treeRoot()->findChildNodeByNameAndType(base, Node::Class);
503             if (n) {
504                 pair.first->addBaseClass(Node::Public, static_cast<ClassNode*>(n));
505             }
506         }
507     }
508
509     QPair<FunctionNode*,QString> relatedPair;
510     foreach (relatedPair, relatedList_) {
511         Node* n = qdb_->treeRoot()->findChildNodeByNameAndType(relatedPair.second, Node::Class);
512         if (n)
513             relatedPair.first->setRelates(static_cast<ClassNode*>(n));
514     }
515 }
516
517 /*!
518   Normally this is used for writing the \e groups attribute,
519   but it can be used for writing any attribute with a list
520   value that comes from some subset of the members of \a n.
521
522   \note The members of \a n are \e not the children of \a n.
523
524   The names we want to include are the names of the members
525   of \a n that have node type \a t and node subtype \a st.
526   The attribute name is \a attr. The names are joined with
527   the space character and written with \a writer.
528  */
529 void QDocIndexFiles::writeMembersAttribute(QXmlStreamWriter& writer,
530                                            const InnerNode* n,
531                                            Node::Type t,
532                                            Node::SubType st,
533                                            const QString& attr)
534 {
535     const NodeList& members = n->members();
536     if (!members.isEmpty()) {
537         QStringList names;
538         NodeList::ConstIterator i = members.constBegin();
539         while (i != members.constEnd()) {
540             if ((*i)->type() == t && (*i)->subType() == st)
541                 names.append((*i)->name());
542             ++i;
543         }
544         if (!names.isEmpty())
545             writer.writeAttribute(attr, names.join(","));
546     }
547 }
548
549 /*!
550   Generate the index section with the given \a writer for the \a node
551   specified, returning true if an element was written; otherwise returns
552   false.
553  */
554 bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter& writer,
555                                           Node* node,
556                                           bool generateInternalNodes)
557 {
558     if (node->subType() == Node::DitaMap)
559         return false;
560
561     QString nodeName;
562     switch (node->type()) {
563     case Node::Namespace:
564         nodeName = "namespace";
565         break;
566     case Node::Class:
567         nodeName = "class";
568         break;
569     case Node::Document:
570         nodeName = "page";
571         if (node->subType() == Node::QmlClass)
572             nodeName = "qmlclass";
573         else if (node->subType() == Node::QmlBasicType)
574             nodeName = "qmlbasictype";
575         break;
576     case Node::Enum:
577         nodeName = "enum";
578         break;
579     case Node::Typedef:
580         nodeName = "typedef";
581         break;
582     case Node::Property:
583         nodeName = "property";
584         break;
585     case Node::Function:
586         nodeName = "function";
587         break;
588     case Node::Variable:
589         nodeName = "variable";
590         break;
591     case Node::QmlProperty:
592         nodeName = "qmlproperty";
593         break;
594     case Node::QmlSignal:
595         nodeName = "qmlsignal";
596         break;
597     case Node::QmlSignalHandler:
598         nodeName = "qmlsignalhandler";
599         break;
600     case Node::QmlMethod:
601         nodeName = "qmlmethod";
602         break;
603     default:
604         return false;
605     }
606
607     QString access;
608     switch (node->access()) {
609     case Node::Public:
610         access = "public";
611         break;
612     case Node::Protected:
613         access = "protected";
614         break;
615     case Node::Private:
616         // Do not include private non-internal nodes in the index.
617         // (Internal public and protected nodes are marked as private
618         // by qdoc. We can check their internal status to determine
619         // whether they were really private to begin with.)
620         if (node->status() == Node::Internal && generateInternalNodes)
621             access = "internal";
622         else
623             return false;
624         break;
625     default:
626         return false;
627     }
628
629     QString objName = node->name();
630     // Special case: only the root node should have an empty name.
631     if (objName.isEmpty() && node != qdb_->treeRoot())
632         return false;
633
634     writer.writeStartElement(nodeName);
635
636     QXmlStreamAttributes attributes;
637     writer.writeAttribute("access", access);
638
639     if (node->type() != Node::Document) {
640         QString threadSafety;
641         switch (node->threadSafeness()) {
642         case Node::NonReentrant:
643             threadSafety = "non-reentrant";
644             break;
645         case Node::Reentrant:
646             threadSafety = "reentrant";
647             break;
648         case Node::ThreadSafe:
649             threadSafety = "thread safe";
650             break;
651         case Node::UnspecifiedSafeness:
652         default:
653             threadSafety = "unspecified";
654             break;
655         }
656         writer.writeAttribute("threadsafety", threadSafety);
657     }
658
659     QString status;
660     switch (node->status()) {
661     case Node::Compat:
662         status = "compat";
663         break;
664     case Node::Obsolete:
665         status = "obsolete";
666         break;
667     case Node::Deprecated:
668         status = "deprecated";
669         break;
670     case Node::Preliminary:
671         status = "preliminary";
672         break;
673     case Node::Commendable:
674         status = "commendable";
675         break;
676     case Node::Internal:
677         status = "internal";
678         break;
679     case Node::Main:
680     default:
681         status = "main";
682         break;
683     }
684     writer.writeAttribute("status", status);
685
686     writer.writeAttribute("name", objName);
687     QString fullName = node->fullDocumentName();
688     if (fullName != objName)
689         writer.writeAttribute("fullname", fullName);
690     QString href = node->outputSubdirectory();
691     if (!href.isEmpty())
692         href.append(QLatin1Char('/'));
693     href.append(gen_->fullDocumentLocation(node));
694     writer.writeAttribute("href", href);
695     if ((node->type() != Node::Document) && (!node->isQmlNode()))
696         writer.writeAttribute("location", node->location().fileName());
697
698     if (!node->since().isEmpty()) {
699         writer.writeAttribute("since", node->since());
700     }
701
702     switch (node->type()) {
703     case Node::Class:
704         {
705             // Classes contain information about their base classes.
706             const ClassNode* classNode = static_cast<const ClassNode*>(node);
707             QList<RelatedClass> bases = classNode->baseClasses();
708             QSet<QString> baseStrings;
709             foreach (const RelatedClass& related, bases) {
710                 ClassNode* baseClassNode = related.node;
711                 baseStrings.insert(baseClassNode->name());
712             }
713             writer.writeAttribute("bases", QStringList(baseStrings.toList()).join(","));
714             if (!node->moduleName().isEmpty())
715                 writer.writeAttribute("module", node->moduleName());
716             writeMembersAttribute(writer, classNode, Node::Document, Node::Group, "groups");
717         }
718         break;
719     case Node::Namespace:
720         {
721             const NamespaceNode* namespaceNode = static_cast<const NamespaceNode*>(node);
722             if (!namespaceNode->moduleName().isEmpty())
723                 writer.writeAttribute("module", namespaceNode->moduleName());
724             writeMembersAttribute(writer, namespaceNode, Node::Document, Node::Group, "groups");
725         }
726         break;
727     case Node::Document:
728         {
729             /*
730               Document nodes (such as manual pages) contain subtypes,
731               titles and other attributes.
732             */
733             bool writeModuleName = false;
734             const DocNode* docNode = static_cast<const DocNode*>(node);
735             switch (docNode->subType()) {
736             case Node::Example:
737                 writer.writeAttribute("subtype", "example");
738                 writeModuleName = true;
739                 break;
740             case Node::HeaderFile:
741                 writer.writeAttribute("subtype", "header");
742                 writeModuleName = true;
743                 break;
744             case Node::File:
745                 writer.writeAttribute("subtype", "file");
746                 break;
747             case Node::Group:
748                 {
749                     writer.writeAttribute("subtype", "group");
750                     writer.writeAttribute("seen", docNode->wasSeen() ? "true" : "false");
751                     // Groups contain information about their group members.
752                     const NodeList& members = docNode->members();
753                     QStringList names;
754                     foreach (const Node* member, members) {
755                         names.append(member->name());
756                     }
757                     writer.writeAttribute("members", names.join(","));
758                     writeModuleName = true;
759                 }
760                 break;
761             case Node::Module:
762                 writer.writeAttribute("subtype", "module");
763                 break;
764             case Node::QmlModule:
765                 writer.writeAttribute("subtype", "qmlmodule");
766                 break;
767             case Node::Page:
768                 writer.writeAttribute("subtype", "page");
769                 writeModuleName = true;
770                 break;
771             case Node::ExternalPage:
772                 writer.writeAttribute("subtype", "externalpage");
773                 break;
774             case Node::QmlClass:
775                 //writer.writeAttribute("subtype", "qmlclass");
776                 break;
777             case Node::QmlBasicType:
778                 //writer.writeAttribute("subtype", "qmlbasictype");
779                 break;
780             default:
781                 break;
782             }
783             writer.writeAttribute("title", docNode->title());
784             writer.writeAttribute("fulltitle", docNode->fullTitle());
785             writer.writeAttribute("subtitle", docNode->subTitle());
786             writer.writeAttribute("location", docNode->doc().location().fileName());
787             if (!node->moduleName().isEmpty() && writeModuleName) {
788                 writer.writeAttribute("module", node->moduleName());
789             }
790             writeMembersAttribute(writer, docNode, Node::Document, Node::Group, "groups");
791         }
792         break;
793     case Node::Function:
794         {
795             /*
796               Function nodes contain information about the type of
797               function being described.
798             */
799             const FunctionNode* functionNode = static_cast<const FunctionNode*>(node);
800             switch (functionNode->virtualness()) {
801             case FunctionNode::NonVirtual:
802                 writer.writeAttribute("virtual", "non");
803                 break;
804             case FunctionNode::ImpureVirtual:
805                 writer.writeAttribute("virtual", "impure");
806                 break;
807             case FunctionNode::PureVirtual:
808                 writer.writeAttribute("virtual", "pure");
809                 break;
810             default:
811                 break;
812             }
813
814             switch (functionNode->metaness()) {
815             case FunctionNode::Plain:
816                 writer.writeAttribute("meta", "plain");
817                 break;
818             case FunctionNode::Signal:
819                 writer.writeAttribute("meta", "signal");
820                 break;
821             case FunctionNode::Slot:
822                 writer.writeAttribute("meta", "slot");
823                 break;
824             case FunctionNode::Ctor:
825                 writer.writeAttribute("meta", "constructor");
826                 break;
827             case FunctionNode::Dtor:
828                 writer.writeAttribute("meta", "destructor");
829                 break;
830             case FunctionNode::MacroWithParams:
831                 writer.writeAttribute("meta", "macrowithparams");
832                 break;
833             case FunctionNode::MacroWithoutParams:
834                 writer.writeAttribute("meta", "macrowithoutparams");
835                 break;
836             default:
837                 break;
838             }
839             writer.writeAttribute("const", functionNode->isConst()?"true":"false");
840             writer.writeAttribute("static", functionNode->isStatic()?"true":"false");
841             writer.writeAttribute("overload", functionNode->isOverload()?"true":"false");
842             if (functionNode->isOverload())
843                 writer.writeAttribute("overload-number", QString::number(functionNode->overloadNumber()));
844             if (functionNode->relates())
845                 writer.writeAttribute("relates", functionNode->relates()->name());
846             const PropertyNode* propertyNode = functionNode->associatedProperty();
847             if (propertyNode)
848                 writer.writeAttribute("associated-property", propertyNode->name());
849             writer.writeAttribute("type", functionNode->returnType());
850         }
851         break;
852     case Node::QmlProperty:
853         {
854             QmlPropertyNode* qpn = static_cast<QmlPropertyNode*>(node);
855             writer.writeAttribute("type", qpn->dataType());
856             writer.writeAttribute("attached", qpn->isAttached() ? "true" : "false");
857             writer.writeAttribute("writable", qpn->isWritable(qdb_) ? "true" : "false");
858         }
859         break;
860     case Node::Property:
861         {
862             const PropertyNode* propertyNode = static_cast<const PropertyNode*>(node);
863             writer.writeAttribute("type", propertyNode->dataType());
864             foreach (const Node* fnNode, propertyNode->getters()) {
865                 if (fnNode) {
866                     const FunctionNode* functionNode = static_cast<const FunctionNode*>(fnNode);
867                     writer.writeStartElement("getter");
868                     writer.writeAttribute("name", functionNode->name());
869                     writer.writeEndElement(); // getter
870                 }
871             }
872             foreach (const Node* fnNode, propertyNode->setters()) {
873                 if (fnNode) {
874                     const FunctionNode* functionNode = static_cast<const FunctionNode*>(fnNode);
875                     writer.writeStartElement("setter");
876                     writer.writeAttribute("name", functionNode->name());
877                     writer.writeEndElement(); // setter
878                 }
879             }
880             foreach (const Node* fnNode, propertyNode->resetters()) {
881                 if (fnNode) {
882                     const FunctionNode* functionNode = static_cast<const FunctionNode*>(fnNode);
883                     writer.writeStartElement("resetter");
884                     writer.writeAttribute("name", functionNode->name());
885                     writer.writeEndElement(); // resetter
886                 }
887             }
888             foreach (const Node* fnNode, propertyNode->notifiers()) {
889                 if (fnNode) {
890                     const FunctionNode* functionNode = static_cast<const FunctionNode*>(fnNode);
891                     writer.writeStartElement("notifier");
892                     writer.writeAttribute("name", functionNode->name());
893                     writer.writeEndElement(); // notifier
894                 }
895             }
896         }
897         break;
898     case Node::Variable:
899         {
900             const VariableNode* variableNode = static_cast<const VariableNode*>(node);
901             writer.writeAttribute("type", variableNode->dataType());
902             writer.writeAttribute("static", variableNode->isStatic() ? "true" : "false");
903         }
904         break;
905     default:
906         break;
907     }
908
909     // Inner nodes and function nodes contain child nodes of some sort, either
910     // actual child nodes or function parameters. For these, we close the
911     // opening tag, create child elements, then add a closing tag for the
912     // element. Elements for all other nodes are closed in the opening tag.
913
914     if (node->isInnerNode()) {
915         const InnerNode* inner = static_cast<const InnerNode*>(node);
916
917         // For internal pages, we canonicalize the target, keyword and content
918         // item names so that they can be used by qdoc for other sets of
919         // documentation.
920         // The reason we do this here is that we don't want to ruin
921         // externally composed indexes, containing non-qdoc-style target names
922         // when reading in indexes.
923
924         if (inner->doc().hasTargets()) {
925             bool external = false;
926             if (inner->type() == Node::Document) {
927                 const DocNode* docNode = static_cast<const DocNode*>(inner);
928                 if (docNode->subType() == Node::ExternalPage)
929                     external = true;
930             }
931             foreach (const Atom* target, inner->doc().targets()) {
932                 QString targetName = target->string();
933                 if (!external)
934                     targetName = Doc::canonicalTitle(targetName);
935                 writer.writeStartElement("target");
936                 writer.writeAttribute("name", targetName);
937                 writer.writeEndElement(); // target
938             }
939         }
940         if (inner->doc().hasKeywords()) {
941             foreach (const Atom* keyword, inner->doc().keywords()) {
942                 writer.writeStartElement("keyword");
943                 writer.writeAttribute("name", Doc::canonicalTitle(keyword->string()));
944                 writer.writeEndElement(); // keyword
945             }
946         }
947         if (inner->doc().hasTableOfContents()) {
948             for (int i = 0; i < inner->doc().tableOfContents().size(); ++i) {
949                 Atom* item = inner->doc().tableOfContents()[i];
950                 int level = inner->doc().tableOfContentsLevels()[i];
951                 QString title = Text::sectionHeading(item).toString();
952                 writer.writeStartElement("contents");
953                 writer.writeAttribute("name", Doc::canonicalTitle(title));
954                 writer.writeAttribute("title", title);
955                 writer.writeAttribute("level", QString::number(level));
956                 writer.writeEndElement(); // contents
957             }
958         }
959     }
960     else if (node->type() == Node::Function) {
961         const FunctionNode* functionNode = static_cast<const FunctionNode*>(node);
962         // Write a signature attribute for convenience.
963         QStringList signatureList;
964         QStringList resolvedParameters;
965         foreach (const Parameter& parameter, functionNode->parameters()) {
966             QString leftType = parameter.leftType();
967             const Node* leftNode = qdb_->findNode(parameter.leftType().split("::"),
968                                                   0,
969                                                   SearchBaseClasses|NonFunction);
970             if (!leftNode || leftNode->type() != Node::Typedef) {
971                 leftNode = qdb_->findNode(parameter.leftType().split("::"),
972                                           node->parent(),
973                                           SearchBaseClasses|NonFunction);
974             }
975             if (leftNode && leftNode->type() == Node::Typedef) {
976                 if (leftNode->type() == Node::Typedef) {
977                     const TypedefNode* typedefNode =  static_cast<const TypedefNode*>(leftNode);
978                     if (typedefNode->associatedEnum()) {
979                         leftType = "QFlags<" + typedefNode->associatedEnum()->fullDocumentName() +
980                             QLatin1Char('>');
981                     }
982                 }
983                 else
984                     leftType = leftNode->fullDocumentName();
985             }
986             resolvedParameters.append(leftType);
987             signatureList.append(leftType + QLatin1Char(' ') + parameter.name());
988         }
989
990         QString signature = functionNode->name() + QLatin1Char('(') + signatureList.join(", ") +
991             QLatin1Char(')');
992         if (functionNode->isConst())
993             signature += " const";
994         writer.writeAttribute("signature", signature);
995
996         for (int i = 0; i < functionNode->parameters().size(); ++i) {
997             Parameter parameter = functionNode->parameters()[i];
998             writer.writeStartElement("parameter");
999             writer.writeAttribute("left", resolvedParameters[i]);
1000             writer.writeAttribute("right", parameter.rightType());
1001             writer.writeAttribute("name", parameter.name());
1002             writer.writeAttribute("default", parameter.defaultValue());
1003             writer.writeEndElement(); // parameter
1004         }
1005     }
1006     else if (node->type() == Node::Enum) {
1007         const EnumNode* enumNode = static_cast<const EnumNode*>(node);
1008         if (enumNode->flagsType()) {
1009             writer.writeAttribute("typedef",enumNode->flagsType()->fullDocumentName());
1010         }
1011         foreach (const EnumItem& item, enumNode->items()) {
1012             writer.writeStartElement("value");
1013             writer.writeAttribute("name", item.name());
1014             writer.writeAttribute("value", item.value());
1015             writer.writeEndElement(); // value
1016         }
1017     }
1018     else if (node->type() == Node::Typedef) {
1019         const TypedefNode* typedefNode = static_cast<const TypedefNode*>(node);
1020         if (typedefNode->associatedEnum()) {
1021             writer.writeAttribute("enum",typedefNode->associatedEnum()->fullDocumentName());
1022         }
1023     }
1024     return true;
1025 }
1026
1027 /*!
1028   Returns true if the node \a n1 is less than node \a n2. The
1029   comparison is performed by comparing properties of the nodes
1030   in order of increasing complexity.
1031 */
1032 bool compareNodes(const Node* n1, const Node* n2)
1033 {
1034     // Private nodes can occur in any order since they won't normally be
1035     // written to the index.
1036     if (n1->access() == Node::Private && n2->access() == Node::Private)
1037         return true;
1038
1039     if (n1->location().filePath() < n2->location().filePath())
1040         return true;
1041     else if (n1->location().filePath() > n2->location().filePath())
1042         return false;
1043
1044     if (n1->type() < n2->type())
1045         return true;
1046     else if (n1->type() > n2->type())
1047         return false;
1048
1049     if (n1->name() < n2->name())
1050         return true;
1051     else if (n1->name() > n2->name())
1052         return false;
1053
1054     if (n1->access() < n2->access())
1055         return true;
1056     else if (n1->access() > n2->access())
1057         return false;
1058
1059     if (n1->type() == Node::Function && n2->type() == Node::Function) {
1060         const FunctionNode* f1 = static_cast<const FunctionNode*>(n1);
1061         const FunctionNode* f2 = static_cast<const FunctionNode*>(n2);
1062
1063         if (f1->isConst() < f2->isConst())
1064             return true;
1065         else if (f1->isConst() > f2->isConst())
1066             return false;
1067
1068         if (f1->signature() < f2->signature())
1069             return true;
1070         else if (f1->signature() > f2->signature())
1071             return false;
1072     }
1073
1074     if (n1->type() == Node::Document && n2->type() == Node::Document) {
1075         const DocNode* f1 = static_cast<const DocNode*>(n1);
1076         const DocNode* f2 = static_cast<const DocNode*>(n2);
1077         if (f1->fullTitle() < f2->fullTitle())
1078             return true;
1079         else if (f1->fullTitle() > f2->fullTitle())
1080             return false;
1081     }
1082
1083     return false;
1084 }
1085
1086 /*!
1087   Generate index sections for the child nodes of the given \a node
1088   using the \a writer specified. If \a generateInternalNodes is true,
1089   nodes marked as internal will be included in the index; otherwise,
1090   they will be omitted.
1091 */
1092 void QDocIndexFiles::generateIndexSections(QXmlStreamWriter& writer,
1093                                            Node* node,
1094                                            bool generateInternalNodes)
1095 {
1096     /*
1097       Note that the groups are written after all the other nodes.
1098      */
1099     if (!node->isGroup() && generateIndexSection(writer, node, generateInternalNodes)) {
1100         if (node->isInnerNode()) {
1101             const InnerNode* inner = static_cast<const InnerNode*>(node);
1102
1103             NodeList cnodes = inner->childNodes();
1104             qSort(cnodes.begin(), cnodes.end(), compareNodes);
1105
1106             foreach (Node* child, cnodes) {
1107                 /*
1108                   Don't generate anything for a QML property group node.
1109                   It is just a place holder for a collection of QML property
1110                   nodes. Recurse to its children, which are the QML property
1111                   nodes.
1112                  */
1113                 if (child->subType() == Node::QmlPropertyGroup) {
1114                     const InnerNode* pgn = static_cast<const InnerNode*>(child);
1115                     foreach (Node* c, pgn->childNodes()) {
1116                         generateIndexSections(writer, c, generateInternalNodes);
1117                     }
1118                 }
1119                 else
1120                     generateIndexSections(writer, child, generateInternalNodes);
1121             }
1122         }
1123         writer.writeEndElement();
1124     }
1125 }
1126
1127 /*!
1128   Outputs an index file.
1129  */
1130 void QDocIndexFiles::generateIndex(const QString& fileName,
1131                                    const QString& url,
1132                                    const QString& title,
1133                                    Generator* g,
1134                                    bool generateInternalNodes)
1135 {
1136     QFile file(fileName);
1137     if (!file.open(QFile::WriteOnly | QFile::Text))
1138         return;
1139
1140     QString msg = "Writing index file: " + fileName;
1141     Location::logToStdErr(msg);
1142
1143     gen_ = g;
1144     QXmlStreamWriter writer(&file);
1145     writer.setAutoFormatting(true);
1146     writer.writeStartDocument();
1147     writer.writeDTD("<!DOCTYPE QDOCINDEX>");
1148
1149     writer.writeStartElement("INDEX");
1150     writer.writeAttribute("url", url);
1151     writer.writeAttribute("title", title);
1152     writer.writeAttribute("version", qdb_->version());
1153
1154     generateIndexSections(writer, qdb_->treeRoot(), generateInternalNodes);
1155
1156     /*
1157       We wait until the end of the index file to output the group elements.
1158       By waiting until the end, when we read each group element, its members
1159       will have already been created. It is then only necessary to create
1160       the group page and add each member to its member list.
1161      */
1162     const DocNodeMap& groups = qdb_->groups();
1163     if (!groups.isEmpty()) {
1164         DocNodeMap::ConstIterator g = groups.constBegin();
1165         while (g != groups.constEnd()) {
1166             if (generateIndexSection(writer, g.value(), generateInternalNodes))
1167                 writer.writeEndElement();
1168             ++g;
1169         }
1170     }
1171
1172     writer.writeEndElement(); // INDEX
1173     writer.writeEndElement(); // QDOCINDEX
1174     writer.writeEndDocument();
1175     file.close();
1176 }
1177
1178 QT_END_NAMESPACE