Warning: That file was not part of the compilation database. It may have many parsing errors.

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

Warning: That file was not part of the compilation database. It may have many parsing errors.