Merge branch 'master' of scm.dev.nokia.troll.no:qt/oslo-staging-1 into master-integration
[qt-netbsd.git] / tools / qdoc3 / tree.cpp
blobd46da95f2d114ef3d02fa46bac6c08743dcb806e
1 /****************************************************************************
2 **
3 ** Copyright (C) 2010 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 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
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.
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.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
38 ** $QT_END_LICENSE$
40 ****************************************************************************/
43 tree.cpp
46 #include <QtCore>
47 #include <QDomDocument>
49 #include "atom.h"
50 #include "doc.h"
51 #include "htmlgenerator.h"
52 #include "location.h"
53 #include "node.h"
54 #include "text.h"
55 #include "tree.h"
57 QT_BEGIN_NAMESPACE
59 struct InheritanceBound
61 Node::Access access;
62 QStringList basePath;
63 QString dataTypeWithTemplateArgs;
64 InnerNode *parent;
66 InheritanceBound()
67 : access(Node::Public) { }
68 InheritanceBound(Node::Access access0,
69 const QStringList& basePath0,
70 const QString &dataTypeWithTemplateArgs0,
71 InnerNode *parent)
72 : access(access0), basePath(basePath0),
73 dataTypeWithTemplateArgs(dataTypeWithTemplateArgs0),
74 parent(parent) { }
77 struct Target
79 Node *node;
80 Atom *atom;
81 int priority;
84 typedef QMap<PropertyNode::FunctionRole, QString> RoleMap;
85 typedef QMap<PropertyNode *, RoleMap> PropertyMap;
86 typedef QMultiMap<QString, Node *> GroupMap;
87 typedef QMultiHash<QString, FakeNode *> FakeNodeHash;
88 typedef QMultiHash<QString, Target> TargetHash;
90 class TreePrivate
92 public:
93 QMap<ClassNode *, QList<InheritanceBound> > unresolvedInheritanceMap;
94 PropertyMap unresolvedPropertyMap;
95 GroupMap groupMap;
96 QMultiMap<QString, QString> publicGroupMap;
97 FakeNodeHash fakeNodesByTitle;
98 TargetHash targetHash;
99 QList<QPair<ClassNode*,QString> > basesList;
100 QList<QPair<FunctionNode*,QString> > relatedList;
104 \class Tree
108 The default constructor is the only constructor.
110 Tree::Tree()
111 : roo(0, "")
113 priv = new TreePrivate;
117 The destructor deletes the internal, private tree.
119 Tree::~Tree()
121 delete priv;
126 Node *Tree::findNode(const QStringList &path, Node *relative, int findFlags)
128 return const_cast<Node*>(const_cast<const Tree*>(this)->findNode(path,
129 relative,
130 findFlags));
135 const Node *Tree::findNode(const QStringList &path,
136 const Node *relative,
137 int findFlags) const
139 if (!relative)
140 relative = root();
142 do {
143 const Node *node = relative;
144 int i;
146 for (i = 0; i < path.size(); ++i) {
147 if (node == 0 || !node->isInnerNode())
148 break;
150 const Node *next =
151 static_cast<const InnerNode*>(node)->findNode(path.at(i));
152 if (!next && (findFlags & SearchEnumValues) && i == path.size()-1)
153 next = static_cast<const InnerNode*>(node)->findEnumNodeForValue(path.at(i));
155 if (!next && node->type() == Node::Class && (findFlags & SearchBaseClasses)) {
156 NodeList baseClasses = allBaseClasses(static_cast<const ClassNode *>(node));
157 foreach (const Node *baseClass, baseClasses) {
158 next = static_cast<const InnerNode *>(baseClass)->findNode(path.at(i));
159 if (!next && (findFlags & SearchEnumValues) && i == path.size() - 1)
160 next = static_cast<const InnerNode *>(baseClass)
161 ->findEnumNodeForValue(path.at(i));
162 if (next)
163 break;
166 node = next;
168 if (node && i == path.size()
169 && (!(findFlags & NonFunction) || node->type() != Node::Function
170 || ((FunctionNode *)node)->metaness() == FunctionNode::MacroWithoutParams))
171 return node;
172 relative = relative->parent();
173 } while (relative);
175 return 0;
179 Find the node with the specified \a path name of the
180 specified \a type.
182 Node *Tree::findNode(const QStringList &path,
183 Node::Type type,
184 Node *relative,
185 int findFlags)
187 return const_cast<Node*>(const_cast<const Tree*>(this)->findNode(path,
188 type,
189 relative,
190 findFlags));
194 Find the node with the specified \a path name of the
195 specified \a type.
197 const Node *Tree::findNode(const QStringList &path,
198 Node::Type type,
199 const Node *relative,
200 int findFlags) const
202 const Node *node = findNode(path, relative, findFlags);
203 if (node != 0 && node->type() == type)
204 return node;
205 return 0;
210 FunctionNode *Tree::findFunctionNode(const QStringList& path,
211 Node *relative,
212 int findFlags)
214 return const_cast<FunctionNode *>(
215 const_cast<const Tree *>(this)->findFunctionNode(path,
216 relative,
217 findFlags));
222 const FunctionNode *Tree::findFunctionNode(const QStringList &path,
223 const Node *relative,
224 int findFlags) const
226 if (!relative)
227 relative = root();
229 do {
230 const Node *node = relative;
231 int i;
233 for (i = 0; i < path.size(); ++i) {
234 if (node == 0 || !node->isInnerNode())
235 break;
237 const Node *next;
238 if (i == path.size() - 1)
239 next = ((InnerNode *) node)->findFunctionNode(path.at(i));
240 else
241 next = ((InnerNode *) node)->findNode(path.at(i));
243 if (!next && node->type() == Node::Class &&
244 (findFlags & SearchBaseClasses)) {
245 NodeList baseClasses = allBaseClasses(static_cast<const ClassNode *>(node));
246 foreach (const Node *baseClass, baseClasses) {
247 if (i == path.size() - 1)
248 next = static_cast<const InnerNode *>(baseClass)->findFunctionNode(path.at(i));
249 else
250 next = static_cast<const InnerNode *>(baseClass)->findNode(path.at(i));
252 if (next)
253 break;
257 node = next;
259 if (node && i == path.size() && node->isFunction()) {
260 // CppCodeParser::processOtherMetaCommand ensures that reimplemented
261 // functions are private.
262 const FunctionNode *func = static_cast<const FunctionNode*>(node);
263 while (func->access() == Node::Private) {
264 const FunctionNode *from = func->reimplementedFrom();
265 if (from != 0) {
266 if (from->access() != Node::Private)
267 return from;
268 else
269 func = from;
271 else
272 break;
274 return func;
276 relative = relative->parent();
277 } while (relative);
279 return 0;
284 FunctionNode *Tree::findFunctionNode(const QStringList &parentPath,
285 const FunctionNode *clone,
286 Node *relative,
287 int findFlags)
289 return const_cast<FunctionNode *>(
290 const_cast<const Tree *>(this)->findFunctionNode(parentPath,
291 clone,
292 relative,
293 findFlags));
298 const FunctionNode *Tree::findFunctionNode(const QStringList &parentPath,
299 const FunctionNode *clone,
300 const Node *relative,
301 int findFlags) const
303 const Node *parent = findNode(parentPath, relative, findFlags);
304 if (parent == 0 || !parent->isInnerNode()) {
305 return 0;
307 else {
308 return ((InnerNode *)parent)->findFunctionNode(clone);
312 static const int NumSuffixes = 3;
313 static const char * const suffixes[NumSuffixes] = { "", "s", "es" };
317 const FakeNode *Tree::findFakeNodeByTitle(const QString &title) const
319 for (int pass = 0; pass < NumSuffixes; ++pass) {
320 FakeNodeHash::const_iterator i =
321 priv->fakeNodesByTitle.find(Doc::canonicalTitle(title + suffixes[pass]));
322 if (i != priv->fakeNodesByTitle.constEnd()) {
323 FakeNodeHash::const_iterator j = i;
324 ++j;
325 if (j != priv->fakeNodesByTitle.constEnd() && j.key() == i.key()) {
326 QList<Location> internalLocations;
327 while (j != priv->fakeNodesByTitle.constEnd()) {
328 if (j.key() == i.key() && j.value()->url().isEmpty())
329 internalLocations.append(j.value()->doc().location());
330 ++j;
332 if (internalLocations.size() > 0) {
333 i.value()->doc().location().warning(
334 tr("Page '%1' defined in more than one location:").arg(title));
335 foreach (const Location &location, internalLocations)
336 location.warning(tr("(defined here)"));
339 return i.value();
342 return 0;
347 const Node*
348 Tree::findUnambiguousTarget(const QString &target, Atom *&atom) const
350 Target bestTarget = {0, 0, INT_MAX};
351 int numBestTargets = 0;
353 for (int pass = 0; pass < NumSuffixes; ++pass) {
354 TargetHash::const_iterator i =
355 priv->targetHash.find(Doc::canonicalTitle(target + suffixes[pass]));
356 if (i != priv->targetHash.constEnd()) {
357 TargetHash::const_iterator j = i;
358 do {
359 const Target &candidate = j.value();
360 if (candidate.priority < bestTarget.priority) {
361 bestTarget = candidate;
362 numBestTargets = 1;
363 } else if (candidate.priority == bestTarget.priority) {
364 ++numBestTargets;
366 ++j;
367 } while (j != priv->targetHash.constEnd() && j.key() == i.key());
369 if (numBestTargets == 1) {
370 atom = bestTarget.atom;
371 return bestTarget.node;
375 return 0;
380 Atom *Tree::findTarget(const QString &target, const Node *node) const
382 for (int pass = 0; pass < NumSuffixes; ++pass) {
383 QString key = Doc::canonicalTitle(target + suffixes[pass]);
384 TargetHash::const_iterator i = priv->targetHash.find(key);
386 if (i != priv->targetHash.constEnd()) {
387 do {
388 if (i.value().node == node)
389 return i.value().atom;
390 ++i;
391 } while (i != priv->targetHash.constEnd() && i.key() == key);
394 return 0;
399 void Tree::addBaseClass(ClassNode *subclass, Node::Access access,
400 const QStringList &basePath,
401 const QString &dataTypeWithTemplateArgs,
402 InnerNode *parent)
404 priv->unresolvedInheritanceMap[subclass].append(
405 InheritanceBound(access,
406 basePath,
407 dataTypeWithTemplateArgs,
408 parent)
415 void Tree::addPropertyFunction(PropertyNode *property,
416 const QString &funcName,
417 PropertyNode::FunctionRole funcRole)
419 priv->unresolvedPropertyMap[property].insert(funcRole, funcName);
423 This function adds the \a node to the \a group. The group
424 can be listed anywhere using the \e{annotated list} command.
426 void Tree::addToGroup(Node *node, const QString &group)
428 priv->groupMap.insert(group, node);
433 QMultiMap<QString, Node *> Tree::groups() const
435 return priv->groupMap;
440 void Tree::addToPublicGroup(Node *node, const QString &group)
442 priv->publicGroupMap.insert(node->name(), group);
443 addToGroup(node, group);
448 QMultiMap<QString, QString> Tree::publicGroups() const
450 return priv->publicGroupMap;
455 void Tree::resolveInheritance(NamespaceNode *rootNode)
457 if (!rootNode)
458 rootNode = root();
460 for (int pass = 0; pass < 2; pass++) {
461 NodeList::ConstIterator c = rootNode->childNodes().begin();
462 while (c != rootNode->childNodes().end()) {
463 if ((*c)->type() == Node::Class)
464 resolveInheritance(pass, (ClassNode *) *c);
465 else if ((*c)->type() == Node::Namespace) {
466 NamespaceNode *ns = static_cast<NamespaceNode*>(*c);
467 resolveInheritance(ns);
469 ++c;
471 if (rootNode == root())
472 priv->unresolvedInheritanceMap.clear();
478 void Tree::resolveProperties()
480 PropertyMap::ConstIterator propEntry;
482 propEntry = priv->unresolvedPropertyMap.begin();
483 while (propEntry != priv->unresolvedPropertyMap.end()) {
484 PropertyNode *property = propEntry.key();
485 InnerNode *parent = property->parent();
486 QString getterName = (*propEntry)[PropertyNode::Getter];
487 QString setterName = (*propEntry)[PropertyNode::Setter];
488 QString resetterName = (*propEntry)[PropertyNode::Resetter];
489 QString notifierName = (*propEntry)[PropertyNode::Notifier];
491 NodeList::ConstIterator c = parent->childNodes().begin();
492 while (c != parent->childNodes().end()) {
493 if ((*c)->type() == Node::Function) {
494 FunctionNode *function = static_cast<FunctionNode *>(*c);
495 if (function->access() == property->access() &&
496 (function->status() == property->status() ||
497 function->doc().isEmpty())) {
498 if (function->name() == getterName) {
499 property->addFunction(function, PropertyNode::Getter);
500 } else if (function->name() == setterName) {
501 property->addFunction(function, PropertyNode::Setter);
502 } else if (function->name() == resetterName) {
503 property->addFunction(function, PropertyNode::Resetter);
504 } else if (function->name() == notifierName) {
505 property->addSignal(function, PropertyNode::Notifier);
509 ++c;
511 ++propEntry;
514 propEntry = priv->unresolvedPropertyMap.begin();
515 while (propEntry != priv->unresolvedPropertyMap.end()) {
516 PropertyNode *property = propEntry.key();
517 // redo it to set the property functions
518 if (property->overriddenFrom())
519 property->setOverriddenFrom(property->overriddenFrom());
520 ++propEntry;
523 priv->unresolvedPropertyMap.clear();
528 void Tree::resolveInheritance(int pass, ClassNode *classe)
530 if (pass == 0) {
531 QList<InheritanceBound> bounds = priv->unresolvedInheritanceMap[classe];
532 QList<InheritanceBound>::ConstIterator b = bounds.begin();
533 while (b != bounds.end()) {
534 ClassNode *baseClass = (ClassNode*)findNode((*b).basePath,
535 Node::Class);
536 if (!baseClass && (*b).parent)
537 baseClass = (ClassNode*)findNode((*b).basePath,
538 Node::Class,
539 (*b).parent);
540 if (baseClass)
541 classe->addBaseClass((*b).access,
542 baseClass,
543 (*b).dataTypeWithTemplateArgs);
544 ++b;
547 else {
548 NodeList::ConstIterator c = classe->childNodes().begin();
549 while (c != classe->childNodes().end()) {
550 if ((*c)->type() == Node::Function) {
551 FunctionNode *func = (FunctionNode *) *c;
552 FunctionNode *from = findVirtualFunctionInBaseClasses(classe, func);
553 if (from != 0) {
554 if (func->virtualness() == FunctionNode::NonVirtual)
555 func->setVirtualness(FunctionNode::ImpureVirtual);
556 func->setReimplementedFrom(from);
559 else if ((*c)->type() == Node::Property) {
560 fixPropertyUsingBaseClasses(classe, static_cast<PropertyNode *>(*c));
562 ++c;
569 void Tree::resolveGroups()
571 GroupMap::const_iterator i;
572 QString prevGroup;
573 for (i = priv->groupMap.constBegin(); i != priv->groupMap.constEnd(); ++i) {
574 if (i.value()->access() == Node::Private)
575 continue;
577 FakeNode *fake =
578 static_cast<FakeNode*>(findNode(QStringList(i.key()),Node::Fake));
579 if (fake && fake->subType() == Node::Group) {
580 fake->addGroupMember(i.value());
582 #if 0
583 else {
584 if (prevGroup != i.key())
585 i.value()->doc().location().warning(tr("No such group '%1'").arg(i.key()));
587 #endif
589 prevGroup = i.key();
592 //priv->groupMap.clear();
597 void Tree::resolveTargets()
599 // need recursion
601 foreach (Node *child, roo.childNodes()) {
602 if (child->type() == Node::Fake) {
603 FakeNode *node = static_cast<FakeNode *>(child);
604 priv->fakeNodesByTitle.insert(Doc::canonicalTitle(node->title()), node);
607 if (child->doc().hasTableOfContents()) {
608 const QList<Atom *> &toc = child->doc().tableOfContents();
609 Target target;
610 target.node = child;
611 target.priority = 3;
613 for (int i = 0; i < toc.size(); ++i) {
614 target.atom = toc.at(i);
615 QString title = Text::sectionHeading(target.atom).toString();
616 if (!title.isEmpty())
617 priv->targetHash.insert(Doc::canonicalTitle(title), target);
620 if (child->doc().hasKeywords()) {
621 const QList<Atom *> &keywords = child->doc().keywords();
622 Target target;
623 target.node = child;
624 target.priority = 1;
626 for (int i = 0; i < keywords.size(); ++i) {
627 target.atom = keywords.at(i);
628 priv->targetHash.insert(Doc::canonicalTitle(target.atom->string()), target);
631 if (child->doc().hasTargets()) {
632 const QList<Atom *> &toc = child->doc().targets();
633 Target target;
634 target.node = child;
635 target.priority = 2;
637 for (int i = 0; i < toc.size(); ++i) {
638 target.atom = toc.at(i);
639 priv->targetHash.insert(Doc::canonicalTitle(target.atom->string()), target);
647 void Tree::fixInheritance(NamespaceNode *rootNode)
649 if (!rootNode)
650 rootNode = root();
652 NodeList::ConstIterator c = rootNode->childNodes().begin();
653 while (c != rootNode->childNodes().end()) {
654 if ((*c)->type() == Node::Class)
655 static_cast<ClassNode *>(*c)->fixBaseClasses();
656 else if ((*c)->type() == Node::Namespace) {
657 NamespaceNode *ns = static_cast<NamespaceNode*>(*c);
658 fixInheritance(ns);
660 ++c;
666 FunctionNode *Tree::findVirtualFunctionInBaseClasses(ClassNode *classe,
667 FunctionNode *clone)
669 QList<RelatedClass>::ConstIterator r = classe->baseClasses().begin();
670 while (r != classe->baseClasses().end()) {
671 FunctionNode *func;
672 if (((func = findVirtualFunctionInBaseClasses((*r).node, clone)) != 0 ||
673 (func = (*r).node->findFunctionNode(clone)) != 0)) {
674 if (func->virtualness() != FunctionNode::NonVirtual)
675 return func;
677 ++r;
679 return 0;
684 void Tree::fixPropertyUsingBaseClasses(ClassNode *classe,
685 PropertyNode *property)
687 QList<RelatedClass>::const_iterator r = classe->baseClasses().begin();
688 while (r != classe->baseClasses().end()) {
689 PropertyNode *baseProperty =
690 static_cast<PropertyNode *>(r->node->findNode(property->name(),
691 Node::Property));
692 if (baseProperty) {
693 fixPropertyUsingBaseClasses(r->node, baseProperty);
694 property->setOverriddenFrom(baseProperty);
696 else {
697 fixPropertyUsingBaseClasses(r->node, property);
699 ++r;
705 NodeList Tree::allBaseClasses(const ClassNode *classe) const
707 NodeList result;
708 foreach (const RelatedClass &r, classe->baseClasses()) {
709 result += r.node;
710 result += allBaseClasses(r.node);
712 return result;
717 void Tree::readIndexes(const QStringList &indexFiles)
719 foreach (const QString &indexFile, indexFiles)
720 readIndexFile(indexFile);
724 Read the QDomDocument at \a path and get the index from it.
726 void Tree::readIndexFile(const QString &path)
728 QFile file(path);
729 if (file.open(QFile::ReadOnly)) {
730 QDomDocument document;
731 document.setContent(&file);
732 file.close();
734 QDomElement indexElement = document.documentElement();
735 QString indexUrl = indexElement.attribute("url", "");
736 priv->basesList.clear();
737 priv->relatedList.clear();
739 // Scan all elements in the XML file, constructing a map that contains
740 // base classes for each class found.
742 QDomElement child = indexElement.firstChildElement();
743 while (!child.isNull()) {
744 readIndexSection(child, root(), indexUrl);
745 child = child.nextSiblingElement();
748 // Now that all the base classes have been found for this index,
749 // arrange them into an inheritance hierarchy.
751 resolveIndex();
757 void Tree::readIndexSection(const QDomElement &element,
758 InnerNode *parent,
759 const QString &indexUrl)
761 QString name = element.attribute("name");
762 QString href = element.attribute("href");
764 Node *section;
765 Location location;
767 if (element.nodeName() == "namespace") {
768 section = new NamespaceNode(parent, name);
770 if (!indexUrl.isEmpty())
771 location = Location(indexUrl + "/" + name.toLower() + ".html");
772 else if (!indexUrl.isNull())
773 location = Location(name.toLower() + ".html");
776 else if (element.nodeName() == "class") {
777 section = new ClassNode(parent, name);
778 priv->basesList.append(QPair<ClassNode*,QString>(
779 static_cast<ClassNode*>(section), element.attribute("bases")));
781 if (!indexUrl.isEmpty())
782 location = Location(indexUrl + "/" + name.toLower() + ".html");
783 else if (!indexUrl.isNull())
784 location = Location(name.toLower() + ".html");
787 else if (element.nodeName() == "page") {
788 Node::SubType subtype;
789 if (element.attribute("subtype") == "example")
790 subtype = Node::Example;
791 else if (element.attribute("subtype") == "header")
792 subtype = Node::HeaderFile;
793 else if (element.attribute("subtype") == "file")
794 subtype = Node::File;
795 else if (element.attribute("subtype") == "group")
796 subtype = Node::Group;
797 else if (element.attribute("subtype") == "module")
798 subtype = Node::Module;
799 else if (element.attribute("subtype") == "page")
800 subtype = Node::Page;
801 else if (element.attribute("subtype") == "externalpage")
802 subtype = Node::ExternalPage;
803 else
804 return;
806 FakeNode *fakeNode = new FakeNode(parent, name, subtype);
807 fakeNode->setTitle(element.attribute("title"));
809 if (element.hasAttribute("location"))
810 name = element.attribute("location", "");
812 if (!indexUrl.isEmpty())
813 location = Location(indexUrl + "/" + name);
814 else if (!indexUrl.isNull())
815 location = Location(name);
817 section = fakeNode;
820 else if (element.nodeName() == "enum") {
821 EnumNode *enumNode = new EnumNode(parent, name);
823 if (!indexUrl.isEmpty())
824 location =
825 Location(indexUrl + "/" + parent->name().toLower() + ".html");
826 else if (!indexUrl.isNull())
827 location = Location(parent->name().toLower() + ".html");
829 QDomElement child = element.firstChildElement("value");
830 while (!child.isNull()) {
831 EnumItem item(child.attribute("name"), child.attribute("value"));
832 enumNode->addItem(item);
833 child = child.nextSiblingElement("value");
836 section = enumNode;
838 } else if (element.nodeName() == "typedef") {
839 section = new TypedefNode(parent, name);
841 if (!indexUrl.isEmpty())
842 location =
843 Location(indexUrl + "/" + parent->name().toLower() + ".html");
844 else if (!indexUrl.isNull())
845 location = Location(parent->name().toLower() + ".html");
848 else if (element.nodeName() == "property") {
849 section = new PropertyNode(parent, name);
851 if (!indexUrl.isEmpty())
852 location =
853 Location(indexUrl + "/" + parent->name().toLower() + ".html");
854 else if (!indexUrl.isNull())
855 location = Location(parent->name().toLower() + ".html");
857 } else if (element.nodeName() == "function") {
858 FunctionNode::Virtualness virt;
859 if (element.attribute("virtual") == "non")
860 virt = FunctionNode::NonVirtual;
861 else if (element.attribute("virtual") == "impure")
862 virt = FunctionNode::ImpureVirtual;
863 else if (element.attribute("virtual") == "pure")
864 virt = FunctionNode::PureVirtual;
865 else
866 return;
868 FunctionNode::Metaness meta;
869 if (element.attribute("meta") == "plain")
870 meta = FunctionNode::Plain;
871 else if (element.attribute("meta") == "signal")
872 meta = FunctionNode::Signal;
873 else if (element.attribute("meta") == "slot")
874 meta = FunctionNode::Slot;
875 else if (element.attribute("meta") == "constructor")
876 meta = FunctionNode::Ctor;
877 else if (element.attribute("meta") == "destructor")
878 meta = FunctionNode::Dtor;
879 else if (element.attribute("meta") == "macro")
880 meta = FunctionNode::MacroWithParams;
881 else if (element.attribute("meta") == "macrowithparams")
882 meta = FunctionNode::MacroWithParams;
883 else if (element.attribute("meta") == "macrowithoutparams")
884 meta = FunctionNode::MacroWithoutParams;
885 else
886 return;
888 FunctionNode *functionNode = new FunctionNode(parent, name);
889 functionNode->setReturnType(element.attribute("return"));
890 functionNode->setVirtualness(virt);
891 functionNode->setMetaness(meta);
892 functionNode->setConst(element.attribute("const") == "true");
893 functionNode->setStatic(element.attribute("static") == "true");
894 functionNode->setOverload(element.attribute("overload") == "true");
896 if (element.hasAttribute("relates")
897 && element.attribute("relates") != parent->name()) {
898 priv->relatedList.append(
899 QPair<FunctionNode*,QString>(functionNode,
900 element.attribute("relates")));
903 QDomElement child = element.firstChildElement("parameter");
904 while (!child.isNull()) {
905 // Do not use the default value for the parameter; it is not
906 // required, and has been known to cause problems.
907 Parameter parameter(child.attribute("left"),
908 child.attribute("right"),
909 child.attribute("name"),
910 ""); // child.attribute("default")
911 functionNode->addParameter(parameter);
912 child = child.nextSiblingElement("parameter");
915 section = functionNode;
917 if (!indexUrl.isEmpty())
918 location =
919 Location(indexUrl + "/" + parent->name().toLower() + ".html");
920 else if (!indexUrl.isNull())
921 location = Location(parent->name().toLower() + ".html");
924 else if (element.nodeName() == "variable") {
925 section = new VariableNode(parent, name);
927 if (!indexUrl.isEmpty())
928 location = Location(indexUrl + "/" + parent->name().toLower() + ".html");
929 else if (!indexUrl.isNull())
930 location = Location(parent->name().toLower() + ".html");
933 else if (element.nodeName() == "keyword") {
934 Target target;
935 target.node = parent;
936 target.priority = 1;
937 target.atom = new Atom(Atom::Target, name);
938 priv->targetHash.insert(name, target);
939 return;
942 else if (element.nodeName() == "target") {
943 Target target;
944 target.node = parent;
945 target.priority = 2;
946 target.atom = new Atom(Atom::Target, name);
947 priv->targetHash.insert(name, target);
948 return;
951 else if (element.nodeName() == "contents") {
952 Target target;
953 target.node = parent;
954 target.priority = 3;
955 target.atom = new Atom(Atom::Target, name);
956 priv->targetHash.insert(name, target);
957 return;
960 else
961 return;
963 QString access = element.attribute("access");
964 if (access == "public")
965 section->setAccess(Node::Public);
966 else if (access == "protected")
967 section->setAccess(Node::Protected);
968 else if (access == "private")
969 section->setAccess(Node::Private);
970 else
971 section->setAccess(Node::Public);
973 if (element.nodeName() != "page") {
974 QString threadSafety = element.attribute("threadsafety");
975 if (threadSafety == "non-reentrant")
976 section->setThreadSafeness(Node::NonReentrant);
977 else if (threadSafety == "reentrant")
978 section->setThreadSafeness(Node::Reentrant);
979 else if (threadSafety == "thread safe")
980 section->setThreadSafeness(Node::ThreadSafe);
981 else
982 section->setThreadSafeness(Node::UnspecifiedSafeness);
984 else
985 section->setThreadSafeness(Node::UnspecifiedSafeness);
987 QString status = element.attribute("status");
988 if (status == "compat")
989 section->setStatus(Node::Compat);
990 else if (status == "obsolete")
991 section->setStatus(Node::Obsolete);
992 else if (status == "deprecated")
993 section->setStatus(Node::Deprecated);
994 else if (status == "preliminary")
995 section->setStatus(Node::Preliminary);
996 else if (status == "commendable")
997 section->setStatus(Node::Commendable);
998 else if (status == "internal")
999 section->setStatus(Node::Internal);
1000 else if (status == "main")
1001 section->setStatus(Node::Main);
1002 else
1003 section->setStatus(Node::Commendable);
1005 section->setModuleName(element.attribute("module"));
1006 if (!indexUrl.isEmpty()) {
1007 if (indexUrl.startsWith("."))
1008 section->setUrl(href);
1009 else
1010 section->setUrl(indexUrl + "/" + href);
1013 // Create some content for the node.
1014 QSet<QString> emptySet;
1016 Doc doc(location, location, " ", emptySet); // placeholder
1017 section->setDoc(doc);
1019 if (section->isInnerNode()) {
1020 InnerNode *inner = static_cast<InnerNode*>(section);
1021 if (inner) {
1022 QDomElement child = element.firstChildElement();
1024 while (!child.isNull()) {
1025 if (element.nodeName() == "class")
1026 readIndexSection(child, inner, indexUrl);
1027 else if (element.nodeName() == "page")
1028 readIndexSection(child, inner, indexUrl);
1029 else if (element.nodeName() == "namespace" && !name.isEmpty())
1030 // The root node in the index is a namespace with an empty name.
1031 readIndexSection(child, inner, indexUrl);
1032 else
1033 readIndexSection(child, parent, indexUrl);
1035 child = child.nextSiblingElement();
1043 QString Tree::readIndexText(const QDomElement &element)
1045 QString text;
1046 QDomNode child = element.firstChild();
1047 while (!child.isNull()) {
1048 if (child.isText())
1049 text += child.toText().nodeValue();
1050 child = child.nextSibling();
1052 return text;
1057 void Tree::resolveIndex()
1059 QPair<ClassNode*,QString> pair;
1061 foreach (pair, priv->basesList) {
1062 foreach (const QString &base, pair.second.split(",")) {
1063 Node *baseClass = root()->findNode(base, Node::Class);
1064 if (baseClass) {
1065 pair.first->addBaseClass(Node::Public,
1066 static_cast<ClassNode*>(baseClass));
1071 QPair<FunctionNode*,QString> relatedPair;
1073 foreach (relatedPair, priv->relatedList) {
1074 Node *classNode = root()->findNode(relatedPair.second, Node::Class);
1075 if (classNode)
1076 relatedPair.first->setRelates(static_cast<ClassNode*>(classNode));
1081 Generate the index section with the given \a writer for the \a node
1082 specified, returning true if an element was written; otherwise returns
1083 false.
1085 bool Tree::generateIndexSection(QXmlStreamWriter &writer,
1086 const Node *node,
1087 bool generateInternalNodes) const
1089 if (!node->url().isEmpty())
1090 return false;
1092 QString nodeName;
1093 switch (node->type()) {
1094 case Node::Namespace:
1095 nodeName = "namespace";
1096 break;
1097 case Node::Class:
1098 nodeName = "class";
1099 break;
1100 case Node::Fake:
1101 nodeName = "page";
1102 break;
1103 case Node::Enum:
1104 nodeName = "enum";
1105 break;
1106 case Node::Typedef:
1107 nodeName = "typedef";
1108 break;
1109 case Node::Property:
1110 nodeName = "property";
1111 break;
1112 case Node::Function:
1113 nodeName = "function";
1114 break;
1115 case Node::Variable:
1116 nodeName = "variable";
1117 break;
1118 case Node::Target:
1119 nodeName = "target";
1120 break;
1121 default:
1122 return false;
1125 QString access;
1126 switch (node->access()) {
1127 case Node::Public:
1128 access = "public";
1129 break;
1130 case Node::Protected:
1131 access = "protected";
1132 break;
1133 case Node::Private:
1134 // Do not include private non-internal nodes in the index.
1135 // (Internal public and protected nodes are marked as private
1136 // by qdoc. We can check their internal status to determine
1137 // whether they were really private to begin with.)
1138 if (node->status() == Node::Internal && generateInternalNodes)
1139 access = "internal";
1140 else
1141 return false;
1142 break;
1143 default:
1144 return false;
1147 QString objName = node->name();
1149 // Special case: only the root node should have an empty name.
1150 if (objName.isEmpty() && node != root())
1151 return false;
1153 writer.writeStartElement(nodeName);
1155 QXmlStreamAttributes attributes;
1156 writer.writeAttribute("access", access);
1158 if (node->type() != Node::Fake) {
1159 QString threadSafety;
1160 switch (node->threadSafeness()) {
1161 case Node::NonReentrant:
1162 threadSafety = "non-reentrant";
1163 break;
1164 case Node::Reentrant:
1165 threadSafety = "reentrant";
1166 break;
1167 case Node::ThreadSafe:
1168 threadSafety = "thread safe";
1169 break;
1170 case Node::UnspecifiedSafeness:
1171 default:
1172 threadSafety = "unspecified";
1173 break;
1175 writer.writeAttribute("threadsafety", threadSafety);
1178 QString status;
1179 switch (node->status()) {
1180 case Node::Compat:
1181 status = "compat";
1182 break;
1183 case Node::Obsolete:
1184 status = "obsolete";
1185 break;
1186 case Node::Deprecated:
1187 status = "deprecated";
1188 break;
1189 case Node::Preliminary:
1190 status = "preliminary";
1191 break;
1192 case Node::Commendable:
1193 status = "commendable";
1194 break;
1195 case Node::Internal:
1196 status = "internal";
1197 break;
1198 case Node::Main:
1199 default:
1200 status = "main";
1201 break;
1203 writer.writeAttribute("status", status);
1205 writer.writeAttribute("name", objName);
1206 QString fullName = fullDocumentName(node);
1207 if (fullName != objName)
1208 writer.writeAttribute("fullname", fullName);
1209 writer.writeAttribute("href", fullDocumentLocation(node));
1210 if (node->type() != Node::Fake)
1211 writer.writeAttribute("location", node->location().fileName());
1213 switch (node->type()) {
1215 case Node::Class:
1217 // Classes contain information about their base classes.
1219 const ClassNode *classNode = static_cast<const ClassNode*>(node);
1220 QList<RelatedClass> bases = classNode->baseClasses();
1221 QSet<QString> baseStrings;
1222 foreach (const RelatedClass &related, bases) {
1223 ClassNode *baseClassNode = related.node;
1224 baseStrings.insert(baseClassNode->name());
1226 writer.writeAttribute("bases", QStringList(baseStrings.toList()).join(","));
1227 writer.writeAttribute("module", node->moduleName());
1229 break;
1231 case Node::Namespace:
1232 writer.writeAttribute("module", node->moduleName());
1233 break;
1235 case Node::Fake:
1238 Fake nodes (such as manual pages) contain subtypes,
1239 titles and other attributes.
1242 const FakeNode *fakeNode = static_cast<const FakeNode*>(node);
1243 switch (fakeNode->subType()) {
1244 case Node::Example:
1245 writer.writeAttribute("subtype", "example");
1246 break;
1247 case Node::HeaderFile:
1248 writer.writeAttribute("subtype", "header");
1249 break;
1250 case Node::File:
1251 writer.writeAttribute("subtype", "file");
1252 break;
1253 case Node::Group:
1254 writer.writeAttribute("subtype", "group");
1255 break;
1256 case Node::Module:
1257 writer.writeAttribute("subtype", "module");
1258 break;
1259 case Node::Page:
1260 writer.writeAttribute("subtype", "page");
1261 break;
1262 case Node::ExternalPage:
1263 writer.writeAttribute("subtype", "externalpage");
1264 break;
1265 default:
1266 break;
1268 writer.writeAttribute("title", fakeNode->title());
1269 writer.writeAttribute("fulltitle", fakeNode->fullTitle());
1270 writer.writeAttribute("subtitle", fakeNode->subTitle());
1271 writer.writeAttribute("location", fakeNode->doc().location().fileName());
1273 break;
1275 case Node::Function:
1278 Function nodes contain information about the type of
1279 function being described.
1282 const FunctionNode *functionNode =
1283 static_cast<const FunctionNode*>(node);
1285 switch (functionNode->virtualness()) {
1286 case FunctionNode::NonVirtual:
1287 writer.writeAttribute("virtual", "non");
1288 break;
1289 case FunctionNode::ImpureVirtual:
1290 writer.writeAttribute("virtual", "impure");
1291 break;
1292 case FunctionNode::PureVirtual:
1293 writer.writeAttribute("virtual", "pure");
1294 break;
1295 default:
1296 break;
1298 switch (functionNode->metaness()) {
1299 case FunctionNode::Plain:
1300 writer.writeAttribute("meta", "plain");
1301 break;
1302 case FunctionNode::Signal:
1303 writer.writeAttribute("meta", "signal");
1304 break;
1305 case FunctionNode::Slot:
1306 writer.writeAttribute("meta", "slot");
1307 break;
1308 case FunctionNode::Ctor:
1309 writer.writeAttribute("meta", "constructor");
1310 break;
1311 case FunctionNode::Dtor:
1312 writer.writeAttribute("meta", "destructor");
1313 break;
1314 case FunctionNode::MacroWithParams:
1315 writer.writeAttribute("meta", "macrowithparams");
1316 break;
1317 case FunctionNode::MacroWithoutParams:
1318 writer.writeAttribute("meta", "macrowithoutparams");
1319 break;
1320 default:
1321 break;
1323 writer.writeAttribute("const", functionNode->isConst()?"true":"false");
1324 writer.writeAttribute("static", functionNode->isStatic()?"true":"false");
1325 writer.writeAttribute("overload", functionNode->isOverload()?"true":"false");
1326 if (functionNode->isOverload())
1327 writer.writeAttribute("overload-number", QString::number(functionNode->overloadNumber()));
1328 if (functionNode->relates())
1329 writer.writeAttribute("relates", functionNode->relates()->name());
1330 const PropertyNode *propertyNode = functionNode->associatedProperty();
1331 if (propertyNode)
1332 writer.writeAttribute("associated-property", propertyNode->name());
1333 writer.writeAttribute("type", functionNode->returnType());
1335 break;
1337 case Node::Property:
1339 const PropertyNode *propertyNode = static_cast<const PropertyNode*>(node);
1340 writer.writeAttribute("type", propertyNode->dataType());
1341 foreach (const Node *fnNode, propertyNode->getters()) {
1342 if (fnNode) {
1343 const FunctionNode *functionNode = static_cast<const FunctionNode*>(fnNode);
1344 writer.writeStartElement("getter");
1345 writer.writeAttribute("name", functionNode->name());
1346 writer.writeEndElement(); // getter
1349 foreach (const Node *fnNode, propertyNode->setters()) {
1350 if (fnNode) {
1351 const FunctionNode *functionNode = static_cast<const FunctionNode*>(fnNode);
1352 writer.writeStartElement("setter");
1353 writer.writeAttribute("name", functionNode->name());
1354 writer.writeEndElement(); // getter
1357 foreach (const Node *fnNode, propertyNode->resetters()) {
1358 if (fnNode) {
1359 const FunctionNode *functionNode = static_cast<const FunctionNode*>(fnNode);
1360 writer.writeStartElement("resetter");
1361 writer.writeAttribute("name", functionNode->name());
1362 writer.writeEndElement(); // getter
1366 break;
1368 case Node::Variable:
1370 const VariableNode *variableNode =
1371 static_cast<const VariableNode*>(node);
1372 writer.writeAttribute("type", variableNode->dataType());
1373 writer.writeAttribute("static",
1374 variableNode->isStatic() ? "true" : "false");
1376 break;
1377 default:
1378 break;
1381 // Inner nodes and function nodes contain child nodes of some sort, either
1382 // actual child nodes or function parameters. For these, we close the
1383 // opening tag, create child elements, then add a closing tag for the
1384 // element. Elements for all other nodes are closed in the opening tag.
1386 if (node->isInnerNode()) {
1388 const InnerNode *inner = static_cast<const InnerNode*>(node);
1390 // For internal pages, we canonicalize the target, keyword and content
1391 // item names so that they can be used by qdoc for other sets of
1392 // documentation.
1393 // The reason we do this here is that we don't want to ruin
1394 // externally composed indexes, containing non-qdoc-style target names
1395 // when reading in indexes.
1397 if (inner->doc().hasTargets()) {
1398 bool external = false;
1399 if (inner->type() == Node::Fake) {
1400 const FakeNode *fakeNode = static_cast<const FakeNode *>(inner);
1401 if (fakeNode->subType() == Node::ExternalPage)
1402 external = true;
1405 foreach (const Atom *target, inner->doc().targets()) {
1406 QString targetName = target->string();
1407 if (!external)
1408 targetName = Doc::canonicalTitle(targetName);
1410 writer.writeStartElement("target");
1411 writer.writeAttribute("name", targetName);
1412 writer.writeEndElement(); // target
1415 if (inner->doc().hasKeywords()) {
1416 foreach (const Atom *keyword, inner->doc().keywords()) {
1417 writer.writeStartElement("keyword");
1418 writer.writeAttribute("name",
1419 Doc::canonicalTitle(keyword->string()));
1420 writer.writeEndElement(); // keyword
1423 if (inner->doc().hasTableOfContents()) {
1424 for (int i = 0; i < inner->doc().tableOfContents().size(); ++i) {
1425 Atom *item = inner->doc().tableOfContents()[i];
1426 int level = inner->doc().tableOfContentsLevels()[i];
1428 QString title = Text::sectionHeading(item).toString();
1429 writer.writeStartElement("contents");
1430 writer.writeAttribute("name", Doc::canonicalTitle(title));
1431 writer.writeAttribute("title", title);
1432 writer.writeAttribute("level", QString::number(level));
1433 writer.writeEndElement(); // contents
1438 else if (node->type() == Node::Function) {
1440 const FunctionNode *functionNode = static_cast<const FunctionNode*>(node);
1441 // Write a signature attribute for convenience.
1442 QStringList signatureList;
1443 QStringList resolvedParameters;
1445 foreach (const Parameter &parameter, functionNode->parameters()) {
1446 QString leftType = parameter.leftType();
1447 const Node *leftNode =
1448 const_cast<Tree*>(this)->findNode(parameter.leftType().split("::"),
1449 Node::Typedef, 0, SearchBaseClasses|NonFunction);
1450 if (!leftNode) {
1451 leftNode = const_cast<Tree *>(this)->findNode(
1452 parameter.leftType().split("::"), Node::Typedef,
1453 node->parent(), SearchBaseClasses|NonFunction);
1455 if (leftNode) {
1456 if (leftNode->type() == Node::Typedef) {
1457 const TypedefNode *typedefNode =
1458 static_cast<const TypedefNode *>(leftNode);
1459 if (typedefNode->associatedEnum()) {
1460 leftType = "QFlags<"+fullDocumentName(typedefNode->associatedEnum())+">";
1463 else
1464 leftType = fullDocumentName(leftNode);
1466 resolvedParameters.append(leftType);
1467 signatureList.append(leftType + " " + parameter.name());
1470 QString signature = functionNode->name()+"("+signatureList.join(", ")+")";
1471 if (functionNode->isConst())
1472 signature += " const";
1473 writer.writeAttribute("signature", signature);
1475 for (int i = 0; i < functionNode->parameters().size(); ++i) {
1476 Parameter parameter = functionNode->parameters()[i];
1477 writer.writeStartElement("parameter");
1478 writer.writeAttribute("left", resolvedParameters[i]);
1479 writer.writeAttribute("right", parameter.rightType());
1480 writer.writeAttribute("name", parameter.name());
1481 writer.writeAttribute("default", parameter.defaultValue());
1482 writer.writeEndElement(); // parameter
1486 else if (node->type() == Node::Enum) {
1488 const EnumNode *enumNode = static_cast<const EnumNode*>(node);
1489 if (enumNode->flagsType()) {
1490 writer.writeAttribute("typedef",
1491 fullDocumentName(enumNode->flagsType()));
1493 foreach (const EnumItem &item, enumNode->items()) {
1494 writer.writeStartElement("value");
1495 writer.writeAttribute("name", item.name());
1496 writer.writeAttribute("value", item.value());
1497 writer.writeEndElement(); // value
1501 else if (node->type() == Node::Typedef) {
1503 const TypedefNode *typedefNode = static_cast<const TypedefNode*>(node);
1504 if (typedefNode->associatedEnum()) {
1505 writer.writeAttribute("enum",
1506 fullDocumentName(typedefNode->associatedEnum()));
1510 return true;
1515 void Tree::generateIndexSections(QXmlStreamWriter &writer,
1516 const Node *node,
1517 bool generateInternalNodes) const
1519 if (generateIndexSection(writer, node, generateInternalNodes)) {
1521 if (node->isInnerNode()) {
1522 const InnerNode *inner = static_cast<const InnerNode *>(node);
1524 // Recurse to write an element for this child node and all its children.
1525 foreach (const Node *child, inner->childNodes())
1526 generateIndexSections(writer, child, generateInternalNodes);
1529 foreach (const Node *child, inner->relatedNodes()) {
1530 QDomElement childElement = generateIndexSections(document, child);
1531 element.appendChild(childElement);
1535 writer.writeEndElement();
1540 Outputs an index file.
1542 void Tree::generateIndex(const QString &fileName,
1543 const QString &url,
1544 const QString &title,
1545 bool generateInternalNodes) const
1547 QFile file(fileName);
1548 if (!file.open(QFile::WriteOnly | QFile::Text))
1549 return ;
1551 QXmlStreamWriter writer(&file);
1552 writer.setAutoFormatting(true);
1553 writer.writeStartDocument();
1554 writer.writeDTD("<!DOCTYPE QDOCINDEX>");
1556 writer.writeStartElement("INDEX");
1557 writer.writeAttribute("url", url);
1558 writer.writeAttribute("title", title);
1559 writer.writeAttribute("version", version());
1561 generateIndexSections(writer, root(), generateInternalNodes);
1563 writer.writeEndElement(); // INDEX
1564 writer.writeEndElement(); // QDOCINDEX
1565 writer.writeEndDocument();
1566 file.close();
1570 Generate the tag file section with the given \a writer for the \a node
1571 specified, returning true if an element was written; otherwise returns
1572 false.
1574 void Tree::generateTagFileCompounds(QXmlStreamWriter &writer,
1575 const InnerNode *inner) const
1577 foreach (const Node *node, inner->childNodes()) {
1579 if (!node->url().isEmpty())
1580 continue;
1582 QString kind;
1583 switch (node->type()) {
1584 case Node::Namespace:
1585 kind = "namespace";
1586 break;
1587 case Node::Class:
1588 kind = "class";
1589 break;
1590 case Node::Enum:
1591 case Node::Typedef:
1592 case Node::Property:
1593 case Node::Function:
1594 case Node::Variable:
1595 case Node::Target:
1596 default:
1597 continue;
1600 QString access;
1601 switch (node->access()) {
1602 case Node::Public:
1603 access = "public";
1604 break;
1605 case Node::Protected:
1606 access = "protected";
1607 break;
1608 case Node::Private:
1609 default:
1610 continue;
1613 QString objName = node->name();
1615 // Special case: only the root node should have an empty name.
1616 if (objName.isEmpty() && node != root())
1617 continue;
1619 // *** Write the starting tag for the element here. ***
1620 writer.writeStartElement("compound");
1621 writer.writeAttribute("kind", kind);
1623 if (node->type() == Node::Class) {
1624 writer.writeTextElement("name", fullDocumentName(node));
1625 writer.writeTextElement("filename", fullDocumentLocation(node));
1627 // Classes contain information about their base classes.
1628 const ClassNode *classNode = static_cast<const ClassNode*>(node);
1629 QList<RelatedClass> bases = classNode->baseClasses();
1630 foreach (const RelatedClass &related, bases) {
1631 ClassNode *baseClassNode = related.node;
1632 writer.writeTextElement("base", baseClassNode->name());
1635 // Recurse to write all members.
1636 generateTagFileMembers(writer, static_cast<const InnerNode *>(node));
1637 writer.writeEndElement();
1639 // Recurse to write all compounds.
1640 generateTagFileCompounds(writer, static_cast<const InnerNode *>(node));
1641 } else {
1642 writer.writeTextElement("name", fullDocumentName(node));
1643 writer.writeTextElement("filename", fullDocumentLocation(node));
1645 // Recurse to write all members.
1646 generateTagFileMembers(writer, static_cast<const InnerNode *>(node));
1647 writer.writeEndElement();
1649 // Recurse to write all compounds.
1650 generateTagFileCompounds(writer, static_cast<const InnerNode *>(node));
1657 void Tree::generateTagFileMembers(QXmlStreamWriter &writer,
1658 const InnerNode *inner) const
1660 foreach (const Node *node, inner->childNodes()) {
1662 if (!node->url().isEmpty())
1663 continue;
1665 QString nodeName;
1666 QString kind;
1667 switch (node->type()) {
1668 case Node::Enum:
1669 nodeName = "member";
1670 kind = "enum";
1671 break;
1672 case Node::Typedef:
1673 nodeName = "member";
1674 kind = "typedef";
1675 break;
1676 case Node::Property:
1677 nodeName = "member";
1678 kind = "property";
1679 break;
1680 case Node::Function:
1681 nodeName = "member";
1682 kind = "function";
1683 break;
1684 case Node::Namespace:
1685 nodeName = "namespace";
1686 break;
1687 case Node::Class:
1688 nodeName = "class";
1689 break;
1690 case Node::Variable:
1691 case Node::Target:
1692 default:
1693 continue;
1696 QString access;
1697 switch (node->access()) {
1698 case Node::Public:
1699 access = "public";
1700 break;
1701 case Node::Protected:
1702 access = "protected";
1703 break;
1704 case Node::Private:
1705 default:
1706 continue;
1709 QString objName = node->name();
1711 // Special case: only the root node should have an empty name.
1712 if (objName.isEmpty() && node != root())
1713 continue;
1715 // *** Write the starting tag for the element here. ***
1716 writer.writeStartElement(nodeName);
1717 if (!kind.isEmpty())
1718 writer.writeAttribute("kind", kind);
1720 switch (node->type()) {
1722 case Node::Class:
1723 writer.writeCharacters(fullDocumentName(node));
1724 writer.writeEndElement();
1725 break;
1726 case Node::Namespace:
1727 writer.writeCharacters(fullDocumentName(node));
1728 writer.writeEndElement();
1729 break;
1730 case Node::Function:
1733 Function nodes contain information about
1734 the type of function being described.
1737 const FunctionNode *functionNode =
1738 static_cast<const FunctionNode*>(node);
1739 writer.writeAttribute("protection", access);
1741 switch (functionNode->virtualness()) {
1742 case FunctionNode::NonVirtual:
1743 writer.writeAttribute("virtualness", "non");
1744 break;
1745 case FunctionNode::ImpureVirtual:
1746 writer.writeAttribute("virtualness", "virtual");
1747 break;
1748 case FunctionNode::PureVirtual:
1749 writer.writeAttribute("virtual", "pure");
1750 break;
1751 default:
1752 break;
1754 writer.writeAttribute("static",
1755 functionNode->isStatic() ? "yes" : "no");
1757 if (functionNode->virtualness() == FunctionNode::NonVirtual)
1758 writer.writeTextElement("type", functionNode->returnType());
1759 else
1760 writer.writeTextElement("type",
1761 "virtual " + functionNode->returnType());
1763 writer.writeTextElement("name", objName);
1764 QStringList pieces = fullDocumentLocation(node).split("#");
1765 writer.writeTextElement("anchorfile", pieces[0]);
1766 writer.writeTextElement("anchor", pieces[1]);
1768 // Write a signature attribute for convenience.
1769 QStringList signatureList;
1771 foreach (const Parameter &parameter, functionNode->parameters()) {
1772 QString leftType = parameter.leftType();
1773 const Node *leftNode = const_cast<Tree *>(this)->findNode(parameter.leftType().split("::"),
1774 Node::Typedef, 0, SearchBaseClasses|NonFunction);
1775 if (!leftNode) {
1776 leftNode = const_cast<Tree *>(this)->findNode(
1777 parameter.leftType().split("::"), Node::Typedef,
1778 node->parent(), SearchBaseClasses|NonFunction);
1780 if (leftNode) {
1781 const TypedefNode *typedefNode = static_cast<const TypedefNode *>(leftNode);
1782 if (typedefNode->associatedEnum()) {
1783 leftType = "QFlags<"+fullDocumentName(typedefNode->associatedEnum())+">";
1786 signatureList.append(leftType + " " + parameter.name());
1789 QString signature = "("+signatureList.join(", ")+")";
1790 if (functionNode->isConst())
1791 signature += " const";
1792 if (functionNode->virtualness() == FunctionNode::PureVirtual)
1793 signature += " = 0";
1794 writer.writeTextElement("arglist", signature);
1796 writer.writeEndElement(); // member
1797 break;
1799 case Node::Property:
1801 const PropertyNode *propertyNode = static_cast<const PropertyNode*>(node);
1802 writer.writeAttribute("type", propertyNode->dataType());
1803 writer.writeTextElement("name", objName);
1804 QStringList pieces = fullDocumentLocation(node).split("#");
1805 writer.writeTextElement("anchorfile", pieces[0]);
1806 writer.writeTextElement("anchor", pieces[1]);
1807 writer.writeTextElement("arglist", "");
1809 writer.writeEndElement(); // member
1810 break;
1812 case Node::Enum:
1814 const EnumNode *enumNode = static_cast<const EnumNode*>(node);
1815 writer.writeTextElement("name", objName);
1816 QStringList pieces = fullDocumentLocation(node).split("#");
1817 writer.writeTextElement("anchor", pieces[1]);
1818 writer.writeTextElement("arglist", "");
1819 writer.writeEndElement(); // member
1821 for (int i = 0; i < enumNode->items().size(); ++i) {
1822 EnumItem item = enumNode->items().value(i);
1823 writer.writeStartElement("member");
1824 writer.writeAttribute("name", item.name());
1825 writer.writeTextElement("anchor", pieces[1]);
1826 writer.writeTextElement("arglist", "");
1827 writer.writeEndElement(); // member
1830 break;
1832 case Node::Typedef:
1834 const TypedefNode *typedefNode = static_cast<const TypedefNode*>(node);
1835 if (typedefNode->associatedEnum())
1836 writer.writeAttribute("type", fullDocumentName(typedefNode->associatedEnum()));
1837 else
1838 writer.writeAttribute("type", "");
1839 writer.writeTextElement("name", objName);
1840 QStringList pieces = fullDocumentLocation(node).split("#");
1841 writer.writeTextElement("anchorfile", pieces[0]);
1842 writer.writeTextElement("anchor", pieces[1]);
1843 writer.writeTextElement("arglist", "");
1845 writer.writeEndElement(); // member
1846 break;
1848 case Node::Variable:
1849 case Node::Target:
1850 default:
1851 break;
1858 void Tree::generateTagFile(const QString &fileName) const
1860 QFile file(fileName);
1861 if (!file.open(QFile::WriteOnly | QFile::Text))
1862 return ;
1864 QXmlStreamWriter writer(&file);
1865 writer.setAutoFormatting(true);
1866 writer.writeStartDocument();
1868 writer.writeStartElement("tagfile");
1870 generateTagFileCompounds(writer, root());
1872 writer.writeEndElement(); // tagfile
1873 writer.writeEndDocument();
1874 file.close();
1879 void Tree::addExternalLink(const QString &url, const Node *relative)
1881 FakeNode *fakeNode = new FakeNode(root(), url, Node::ExternalPage);
1882 fakeNode->setAccess(Node::Public);
1884 // Create some content for the node.
1885 QSet<QString> emptySet;
1886 Location location(relative->doc().location());
1887 Doc doc(location, location, " ", emptySet); // placeholder
1888 fakeNode->setDoc(doc);
1892 Returns the full document location for HTML-based documentation.
1893 This should be moved into the HTML generator.
1895 QString Tree::fullDocumentLocation(const Node *node) const
1897 if (!node)
1898 return "";
1899 if (!node->url().isEmpty())
1900 return node->url();
1902 QString parentName;
1903 QString anchorRef;
1905 if (node->type() == Node::Namespace) {
1907 // The root namespace has no name - check for this before creating
1908 // an attribute containing the location of any documentation.
1910 if (!node->fileBase().isEmpty())
1911 parentName = node->fileBase() + ".html";
1912 else
1913 return "";
1915 else if (node->type() == Node::Fake) {
1916 #ifdef QDOC_QML
1917 if (node->subType() == Node::QmlClass)
1918 return "qml-" + node->fileBase() + ".html";
1919 else
1920 #endif
1921 parentName = node->fileBase() + ".html";
1923 else if (node->fileBase().isEmpty())
1924 return "";
1926 Node *parentNode = 0;
1928 if ((parentNode = node->relates()))
1929 parentName = fullDocumentLocation(node->relates());
1930 else if ((parentNode = node->parent()))
1931 parentName = fullDocumentLocation(node->parent());
1933 switch (node->type()) {
1934 case Node::Class:
1935 case Node::Namespace:
1936 if (parentNode && !parentNode->name().isEmpty())
1937 parentName = parentName.replace(".html", "") + "-"
1938 + node->fileBase().toLower() + ".html";
1939 else
1940 parentName = node->fileBase() + ".html";
1941 break;
1942 case Node::Function:
1945 Functions can be destructors, overloaded, or
1946 have associated properties.
1948 const FunctionNode *functionNode =
1949 static_cast<const FunctionNode *>(node);
1951 if (functionNode->metaness() == FunctionNode::Dtor)
1952 anchorRef = "#dtor." + functionNode->name().mid(1);
1954 else if (functionNode->associatedProperty())
1955 return fullDocumentLocation(functionNode->associatedProperty());
1957 else if (functionNode->overloadNumber() > 1)
1958 anchorRef = "#" + functionNode->name()
1959 + "-" + QString::number(functionNode->overloadNumber());
1960 else
1961 anchorRef = "#" + functionNode->name();
1965 Use node->name() instead of node->fileBase() as
1966 the latter returns the name in lower-case. For
1967 HTML anchors, we need to preserve the case.
1969 break;
1970 case Node::Enum:
1971 anchorRef = "#" + node->name() + "-enum";
1972 break;
1973 case Node::Typedef:
1974 anchorRef = "#" + node->name() + "-typedef";
1975 break;
1976 case Node::Property:
1977 anchorRef = "#" + node->name() + "-prop";
1978 break;
1979 case Node::Variable:
1980 anchorRef = "#" + node->name() + "-var";
1981 break;
1982 case Node::Target:
1983 anchorRef = "#" + Doc::canonicalTitle(node->name());
1984 break;
1985 case Node::Fake:
1988 Use node->fileBase() for fake nodes because they are represented
1989 by pages whose file names are lower-case.
1991 parentName = node->fileBase();
1992 parentName.replace("/", "-").replace(".", "-");
1993 parentName += ".html";
1995 break;
1996 default:
1997 break;
2000 // Various objects can be compat (deprecated) or obsolete.
2001 if (node->type() != Node::Class && node->type() != Node::Namespace) {
2002 switch (node->status()) {
2003 case Node::Compat:
2004 parentName.replace(".html", "-qt3.html");
2005 break;
2006 case Node::Obsolete:
2007 parentName.replace(".html", "-obsolete.html");
2008 break;
2009 default:
2014 return parentName.toLower() + anchorRef;
2019 QString Tree::fullDocumentName(const Node *node) const
2021 if (!node)
2022 return "";
2024 QStringList pieces;
2025 const Node *n = node;
2027 do {
2028 if (!n->name().isEmpty())
2029 pieces.insert(0, n->name());
2031 if (n->type() == Node::Fake)
2032 break;
2034 // Examine the parent node if one exists.
2035 if (n->parent())
2036 n = n->parent();
2037 else
2038 break;
2039 } while (true);
2041 // Create a name based on the type of the ancestor node.
2042 if (n->type() == Node::Fake)
2043 return pieces.join("#");
2044 else
2045 return pieces.join("::");
2048 QT_END_NAMESPACE