source: trunk/tools/qdoc3/tree.cpp@ 674

Last change on this file since 674 was 651, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.2 sources.

File size: 68.5 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the tools applications of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
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** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
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
57QT_BEGIN_NAMESPACE
58
59struct 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
77struct Target
78{
79 Node *node;
80 Atom *atom;
81 int priority;
82};
83
84typedef QMap<PropertyNode::FunctionRole, QString> RoleMap;
85typedef QMap<PropertyNode *, RoleMap> PropertyMap;
86typedef QMultiMap<QString, Node *> GroupMap;
87typedef QMultiHash<QString, FakeNode *> FakeNodeHash;
88typedef QMultiHash<QString, Target> TargetHash;
89
90class TreePrivate
91{
92public:
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 */
110Tree::Tree()
111 : roo(0, "")
112{
113 priv = new TreePrivate;
114}
115
116/*!
117 The destructor deletes the internal, private tree.
118 */
119Tree::~Tree()
120{
121 delete priv;
122}
123
124/*!
125 */
126Node *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 */
135const 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 */
182Node *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 */
197const 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 */
210FunctionNode *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 */
222const 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 */
284FunctionNode *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 */
298const 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
312static const int NumSuffixes = 3;
313static const char * const suffixes[NumSuffixes] = { "", "s", "es" };
314
315/*!
316 */
317const 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 */
347const Node*
348Tree::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 */
380Atom *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 */
399void 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 */
415void 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 */
426void Tree::addToGroup(Node *node, const QString &group)
427{
428 priv->groupMap.insert(group, node);
429}
430
431/*!
432 */
433QMultiMap<QString, Node *> Tree::groups() const
434{
435 return priv->groupMap;
436}
437
438/*!
439 */
440void Tree::addToPublicGroup(Node *node, const QString &group)
441{
442 priv->publicGroupMap.insert(node->name(), group);
443 addToGroup(node, group);
444}
445
446/*!
447 */
448QMultiMap<QString, QString> Tree::publicGroups() const
449{
450 return priv->publicGroupMap;
451}
452
453/*!
454 */
455void 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 */
478void 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 */
528void 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 */
569void 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 */
597void 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 */
647void 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 */
666FunctionNode *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 */
684void 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 */
705NodeList 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 */
717void 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 */
726void 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 */
757void 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 */
1043QString 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 */
1057void 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 */
1085bool 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 */
1515void 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 */
1542void 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 */
1574void 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 */
1657void 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 */
1858void 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 */
1879void 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 */
1895QString 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 */
2019QString 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
2048QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.