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

Last change on this file since 374 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

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