source: trunk/tools/qdoc3/tree.cpp

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

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