1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the tools applications of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
46 #include <QDomDocument>
49 #include "htmlgenerator.h"
59 struct InheritanceBound
63 QString dataTypeWithTemplateArgs;
67 : access(Node::Public) { }
68 InheritanceBound(Node::Access access0,
69 const QStringList& basePath0,
70 const QString& dataTypeWithTemplateArgs0,
72 : access(access0), basePath(basePath0),
73 dataTypeWithTemplateArgs(dataTypeWithTemplateArgs0),
84 typedef QMap<PropertyNode::FunctionRole, QString> RoleMap;
85 typedef QMap<PropertyNode*, RoleMap> PropertyMap;
86 typedef QMultiHash<QString, FakeNode*> FakeNodeHash;
87 typedef QMultiHash<QString, Target> TargetHash;
92 QMap<ClassNode* , QList<InheritanceBound> > unresolvedInheritanceMap;
93 PropertyMap unresolvedPropertyMap;
94 NodeMultiMap groupMap;
95 NodeMultiMap qmlModuleMap;
96 QMultiMap<QString, QString> publicGroupMap;
97 FakeNodeHash fakeNodesByTitle;
98 TargetHash targetHash;
99 QList<QPair<ClassNode*,QString> > basesList;
100 QList<QPair<FunctionNode*,QString> > relatedList;
106 This class constructs and maintains a tree of instances of
107 Node and its many subclasses.
111 The default constructor is the only constructor.
116 priv = new TreePrivate;
120 The destructor deletes the internal, private tree.
129 Searches the tree for a node that matches the \a path. The
130 search begins at \a start but can move up the parent chain
131 recursively if no match is found.
133 const Node* Tree::findNode(const QStringList& path,
136 const Node* self) const
138 const Node* current = start;
143 First, search for a node assuming we don't want a QML node.
144 If that search fails, search again assuming we do want a
147 const Node* n = findNode(path,current,findFlags,self,false);
149 n = findNode(path,current,findFlags,self,true);
154 // 2 is private; it is only called by 1.
156 This overload function was extracted from the one above that has the
157 same signature without the last bool parameter, \a qml. This version
158 is called only by that other one. It is therefore private. It can
159 be called a second time by that other version, if the first call
160 returns null. If \a qml is false, the search will only match a node
161 that is not a QML node. If \a qml is true, the search will only
162 match a node that is a QML node.
164 const Node* Tree::findNode(const QStringList& path,
170 const Node* current = start;
172 const Node* node = current;
177 If the path contains one or two double colons ("::"),
178 check first to see if the first two path strings refer
179 to a QML element. If yes, that reference identifies a
182 if (qml && path.size() >= 2) {
183 QmlClassNode* qcn = QmlClassNode::moduleMap.value(path[0]+ "::" +path[1]);
186 if (path.size() == 2)
192 for (i = start_idx; i < path.size(); ++i) {
193 if (node == 0 || !node->isInnerNode())
196 const Node* next = static_cast<const InnerNode*>(node)->findChildNodeByName(path.at(i), qml);
198 if (!next && (findFlags & SearchEnumValues) && i == path.size()-1)
199 next = static_cast<const InnerNode*>(node)->findEnumNodeForValue(path.at(i));
201 if (!next && !qml && node->type() == Node::Class && (findFlags & SearchBaseClasses)) {
202 NodeList baseClasses = allBaseClasses(static_cast<const ClassNode*>(node));
203 foreach (const Node* baseClass, baseClasses) {
204 next = static_cast<const InnerNode*>(baseClass)->findChildNodeByName(path.at(i));
205 if (!next && (findFlags & SearchEnumValues) && i == path.size() - 1)
206 next = static_cast<const InnerNode*>(baseClass)->findEnumNodeForValue(path.at(i));
213 if (node && i == path.size()
214 && (!(findFlags & NonFunction) || node->type() != Node::Function
215 || ((FunctionNode*)node)->metaness() == FunctionNode::MacroWithoutParams)) {
216 if ((node != self) && (node->subType() != Node::QmlPropertyGroup)) {
217 if (node->subType() == Node::Collision) {
218 node = node->applyModuleIdentifier(start);
223 current = current->parent();
230 Find the QML class node for the specified \a module and \a name
231 identifiers. The \a module identifier may be empty. If the module
232 identifier is empty, then begin by finding the FakeNode that has
233 the specified \a name. If that FakeNode is a QML class, return it.
234 If it is a collision node, return its current child, if the current
235 child is a QML class. If the collision node does not have a child
236 that is a QML class node, return 0.
238 QmlClassNode* Tree::findQmlClassNode(const QString& module, const QString& name)
240 if (module.isEmpty()) {
241 Node* n = findQmlClassNode(QStringList(name));
243 if (n->subType() == Node::QmlClass)
244 return static_cast<QmlClassNode*>(n);
245 else if (n->subType() == Node::Collision) {
246 NameCollisionNode* ncn;
247 ncn = static_cast<NameCollisionNode*>(n);
248 return static_cast<QmlClassNode*>(ncn->findAny(Node::Fake,Node::QmlClass));
253 return QmlClassNode::moduleMap.value(module + "::" + name);
257 First, search for a node with the specified \a name. If a matching
258 node is found, if it is a collision node, another collision with
259 this name has been found, so return the collision node. If the
260 matching node is not a collision node, the first collision for this
261 name has been found, so create a NameCollisionNode with the matching
262 node as its first child, and return a pointer to the new
263 NameCollisionNode. Otherwise return 0.
265 NameCollisionNode* Tree::checkForCollision(const QString& name) const
267 Node* n = const_cast<Node*>(findNode(QStringList(name)));
269 if (n->subType() == Node::Collision) {
270 NameCollisionNode* ncn = static_cast<NameCollisionNode*>(n);
273 if (n->isInnerNode())
274 return new NameCollisionNode(static_cast<InnerNode*>(n));
280 This function is like checkForCollision() in that it searches
281 for a collision node with the specified \a name. But it doesn't
282 create anything. If it finds a match, it returns the pointer.
283 Otherwise it returns 0.
285 NameCollisionNode* Tree::findCollisionNode(const QString& name) const
287 Node* n = const_cast<Node*>(findNode(QStringList(name)));
289 if (n->subType() == Node::Collision) {
290 NameCollisionNode* ncn = static_cast<NameCollisionNode*>(n);
298 This function just calls the const version of the same function
299 and returns the function node.
301 FunctionNode* Tree::findFunctionNode(const QStringList& path,
305 return const_cast<FunctionNode*>
306 (const_cast<const Tree*>(this)->findFunctionNode(path,relative,findFlags));
310 This function begins searching the tree at \a relative for
311 the \l {FunctionNode} {function node} identified by \a path.
312 The \a findFlags are used to restrict the search. If a node
313 that matches the \a path is found, it is returned. Otherwise,
314 0 is returned. If \a relative is 0, the root of the tree is
315 used as the starting point.
317 const FunctionNode* Tree::findFunctionNode(const QStringList& path,
318 const Node* relative,
325 If the path contains two double colons ("::"), check
326 first to see if it is a reference to a QML method. If
327 it is a reference to a QML method, first look up the
328 QML class node in the QML module map.
330 if (path.size() == 3) {
331 QmlClassNode* qcn = QmlClassNode::moduleMap.value(path[0]+ "::" +path[1]);
333 return static_cast<const FunctionNode*>(qcn->findFunctionNode(path[2]));
338 const Node* node = relative;
341 for (i = 0; i < path.size(); ++i) {
342 if (node == 0 || !node->isInnerNode())
346 if (i == path.size() - 1)
347 next = ((InnerNode*) node)->findFunctionNode(path.at(i));
349 next = ((InnerNode*) node)->findChildNodeByName(path.at(i));
351 if (!next && node->type() == Node::Class && (findFlags & SearchBaseClasses)) {
352 NodeList baseClasses = allBaseClasses(static_cast<const ClassNode*>(node));
353 foreach (const Node* baseClass, baseClasses) {
354 if (i == path.size() - 1)
355 next = static_cast<const InnerNode*>(baseClass)->findFunctionNode(path.at(i));
357 next = static_cast<const InnerNode*>(baseClass)->findChildNodeByName(path.at(i));
366 if (node && i == path.size() && node->isFunction()) {
367 // CppCodeParser::processOtherMetaCommand ensures that reimplemented
368 // functions are private.
369 const FunctionNode* func = static_cast<const FunctionNode*>(node);
370 while (func->access() == Node::Private) {
371 const FunctionNode* from = func->reimplementedFrom();
373 if (from->access() != Node::Private)
383 relative = relative->parent();
390 This function just calls the const version of itself and
393 FunctionNode* Tree::findFunctionNode(const QStringList& parentPath,
394 const FunctionNode* clone,
398 return const_cast<FunctionNode*>(
399 const_cast<const Tree*>(this)->findFunctionNode(parentPath,
406 This function first ignores the \a clone node and searches
407 for the node having the \a parentPath by calling the main
408 findFunction(\a {parentPath}, \a {relative}, \a {findFlags}).
409 If that search is successful, then it searches for the \a clone
410 in the found parent node.
412 const FunctionNode* Tree::findFunctionNode(const QStringList& parentPath,
413 const FunctionNode* clone,
414 const Node* relative,
417 const Node* parent = findNode(parentPath, relative, findFlags);
418 if (parent == 0 || !parent->isInnerNode()) {
422 return ((InnerNode*)parent)->findFunctionNode(clone);
425 //findNode(parameter.leftType().split("::"), 0, SearchBaseClasses|NonFunction);
427 static const int NumSuffixes = 3;
428 static const char* const suffixes[NumSuffixes] = { "", "s", "es" };
431 This function searches for a node with the specified \a title.
432 If \a relative is provided, use it to disambiguate if it has a
433 QML module identifier.
435 const FakeNode* Tree::findFakeNodeByTitle(const QString& title, const Node* relative ) const
437 for (int pass = 0; pass < NumSuffixes; ++pass) {
438 FakeNodeHash::const_iterator i = priv->fakeNodesByTitle.find(Doc::canonicalTitle(title + suffixes[pass]));
439 if (i != priv->fakeNodesByTitle.constEnd()) {
440 if (relative && !relative->qmlModuleIdentifier().isEmpty()) {
441 const FakeNode* fn = i.value();
442 InnerNode* parent = fn->parent();
443 if (parent && parent->type() == Node::Fake && parent->subType() == Node::Collision) {
444 const NodeList& nl = parent->childNodes();
445 NodeList::ConstIterator it = nl.begin();
446 while (it != nl.end()) {
447 if ((*it)->qmlModuleIdentifier() == relative->qmlModuleIdentifier()) {
449 By returning here, we avoid printing all the duplicate
450 header warnings, which are not really duplicates now,
451 because of the QML module identifier being used as a
454 fn = static_cast<const FakeNode*>(*it);
462 Reporting all these duplicate section titles is probably
463 overkill. We should report the duplicate file and let
466 FakeNodeHash::const_iterator j = i;
468 if (j != priv->fakeNodesByTitle.constEnd() && j.key() == i.key()) {
469 QList<Location> internalLocations;
470 while (j != priv->fakeNodesByTitle.constEnd()) {
471 if (j.key() == i.key() && j.value()->url().isEmpty())
472 internalLocations.append(j.value()->doc().location());
475 if (internalLocations.size() > 0) {
476 i.value()->doc().location().warning(
477 tr("Page '%1' defined in more than one location:").arg(title));
478 foreach (const Location &location, internalLocations)
479 location.warning(tr("(defined here)"));
489 This function searches for a \a target anchor node. If it
490 finds one, it sets \a atom from the found node and returns
494 Tree::findUnambiguousTarget(const QString& target, Atom *&atom, const Node* relative) const
496 Target bestTarget = {0, 0, INT_MAX};
497 int numBestTargets = 0;
498 QList<Target> bestTargetList;
500 for (int pass = 0; pass < NumSuffixes; ++pass) {
501 TargetHash::const_iterator i = priv->targetHash.find(Doc::canonicalTitle(target + suffixes[pass]));
502 if (i != priv->targetHash.constEnd()) {
503 TargetHash::const_iterator j = i;
505 const Target& candidate = j.value();
506 if (candidate.priority < bestTarget.priority) {
507 bestTarget = candidate;
508 bestTargetList.clear();
509 bestTargetList.append(candidate);
511 } else if (candidate.priority == bestTarget.priority) {
512 bestTargetList.append(candidate);
516 } while (j != priv->targetHash.constEnd() && j.key() == i.key());
518 if (numBestTargets == 1) {
519 atom = bestTarget.atom;
520 return bestTarget.node;
522 else if (bestTargetList.size() > 1) {
523 if (relative && !relative->qmlModuleIdentifier().isEmpty()) {
524 for (int i=0; i<bestTargetList.size(); ++i) {
525 const Node* n = bestTargetList.at(i).node;
526 if (relative->qmlModuleIdentifier() == n->qmlModuleIdentifier()) {
527 atom = bestTargetList.at(i).atom;
539 This function searches for a node with a canonical title
540 constructed from \a target and each of the possible suffixes.
541 If the node it finds is \a node, it returns the Atom from that
542 node. Otherwise it returns null.
544 Atom* Tree::findTarget(const QString& target, const Node* node) const
546 for (int pass = 0; pass < NumSuffixes; ++pass) {
547 QString key = Doc::canonicalTitle(target + suffixes[pass]);
548 TargetHash::const_iterator i = priv->targetHash.find(key);
550 if (i != priv->targetHash.constEnd()) {
552 if (i.value().node == node)
553 return i.value().atom;
555 } while (i != priv->targetHash.constEnd() && i.key() == key);
563 void Tree::addBaseClass(ClassNode* subclass, Node::Access access,
564 const QStringList& basePath,
565 const QString& dataTypeWithTemplateArgs,
568 priv->unresolvedInheritanceMap[subclass].append(
569 InheritanceBound(access,
571 dataTypeWithTemplateArgs,
578 void Tree::addPropertyFunction(PropertyNode* property,
579 const QString& funcName,
580 PropertyNode::FunctionRole funcRole)
582 priv->unresolvedPropertyMap[property].insert(funcRole, funcName);
586 This function adds the \a node to the \a group. The group
587 can be listed anywhere using the \e{annotated list} command.
589 void Tree::addToGroup(Node* node, const QString& group)
591 priv->groupMap.insert(group, node);
595 This function adds the \a node to the QML \a module. The QML
596 module can be listed anywhere using the \e{annotated list}
599 void Tree::addToQmlModule(Node* node, const QString& module)
601 priv->qmlModuleMap.insert(module, node);
605 Returns the group map.
607 NodeMultiMap Tree::groups() const
609 return priv->groupMap;
613 Returns the QML module map.
615 NodeMultiMap Tree::qmlModules() const
617 return priv->qmlModuleMap;
622 void Tree::addToPublicGroup(Node* node, const QString& group)
624 priv->publicGroupMap.insert(node->name(), group);
625 addToGroup(node, group);
630 QMultiMap<QString, QString> Tree::publicGroups() const
632 return priv->publicGroupMap;
637 void Tree::resolveInheritance(NamespaceNode* rootNode)
642 for (int pass = 0; pass < 2; pass++) {
643 NodeList::ConstIterator c = rootNode->childNodes().begin();
644 while (c != rootNode->childNodes().end()) {
645 if ((*c)->type() == Node::Class) {
646 resolveInheritance(pass, (ClassNode*)* c);
648 else if ((*c)->type() == Node::Namespace) {
649 NamespaceNode* ns = static_cast<NamespaceNode*>(*c);
650 resolveInheritance(ns);
654 if (rootNode == root())
655 priv->unresolvedInheritanceMap.clear();
661 void Tree::resolveProperties()
663 PropertyMap::ConstIterator propEntry;
665 propEntry = priv->unresolvedPropertyMap.begin();
666 while (propEntry != priv->unresolvedPropertyMap.end()) {
667 PropertyNode* property = propEntry.key();
668 InnerNode* parent = property->parent();
669 QString getterName = (*propEntry)[PropertyNode::Getter];
670 QString setterName = (*propEntry)[PropertyNode::Setter];
671 QString resetterName = (*propEntry)[PropertyNode::Resetter];
672 QString notifierName = (*propEntry)[PropertyNode::Notifier];
674 NodeList::ConstIterator c = parent->childNodes().begin();
675 while (c != parent->childNodes().end()) {
676 if ((*c)->type() == Node::Function) {
677 FunctionNode* function = static_cast<FunctionNode*>(*c);
678 if (function->access() == property->access() &&
679 (function->status() == property->status() ||
680 function->doc().isEmpty())) {
681 if (function->name() == getterName) {
682 property->addFunction(function, PropertyNode::Getter);
684 else if (function->name() == setterName) {
685 property->addFunction(function, PropertyNode::Setter);
687 else if (function->name() == resetterName) {
688 property->addFunction(function, PropertyNode::Resetter);
690 else if (function->name() == notifierName) {
691 property->addSignal(function, PropertyNode::Notifier);
700 propEntry = priv->unresolvedPropertyMap.begin();
701 while (propEntry != priv->unresolvedPropertyMap.end()) {
702 PropertyNode* property = propEntry.key();
703 // redo it to set the property functions
704 if (property->overriddenFrom())
705 property->setOverriddenFrom(property->overriddenFrom());
709 priv->unresolvedPropertyMap.clear();
714 void Tree::resolveInheritance(int pass, ClassNode* classe)
717 QList<InheritanceBound> bounds = priv->unresolvedInheritanceMap[classe];
718 QList<InheritanceBound>::ConstIterator b = bounds.begin();
719 while (b != bounds.end()) {
720 Node* n = findClassNode((*b).basePath);
721 if (!n && (*b).parent) {
722 n = findClassNode((*b).basePath, (*b).parent);
725 classe->addBaseClass((*b).access, static_cast<ClassNode*>(n), (*b).dataTypeWithTemplateArgs);
731 NodeList::ConstIterator c = classe->childNodes().begin();
732 while (c != classe->childNodes().end()) {
733 if ((*c)->type() == Node::Function) {
734 FunctionNode* func = (FunctionNode*)* c;
735 FunctionNode* from = findVirtualFunctionInBaseClasses(classe, func);
737 if (func->virtualness() == FunctionNode::NonVirtual)
738 func->setVirtualness(FunctionNode::ImpureVirtual);
739 func->setReimplementedFrom(from);
742 else if ((*c)->type() == Node::Property) {
743 fixPropertyUsingBaseClasses(classe, static_cast<PropertyNode*>(*c));
751 For each node in the group map, add the node to the appropriate
754 void Tree::resolveGroups()
756 NodeMultiMap::const_iterator i;
757 for (i = priv->groupMap.constBegin(); i != priv->groupMap.constEnd(); ++i) {
758 if (i.value()->access() == Node::Private)
761 Node* n = findGroupNode(QStringList(i.key()));
763 n->addGroupMember(i.value());
768 For each node in the QML module map, add the node to the
769 appropriate QML module node.
771 void Tree::resolveQmlModules()
773 NodeMultiMap::const_iterator i;
774 for (i = priv->qmlModuleMap.constBegin(); i != priv->qmlModuleMap.constEnd(); ++i) {
775 Node* n = findQmlModuleNode(QStringList(i.key()));
777 n->addQmlModuleMember(i.value());
783 void Tree::resolveTargets(InnerNode* root)
787 foreach (Node* child, root->childNodes()) {
788 if (child->type() == Node::Fake) {
789 FakeNode* node = static_cast<FakeNode*>(child);
790 if (!node->title().isEmpty())
791 priv->fakeNodesByTitle.insert(Doc::canonicalTitle(node->title()), node);
792 if (node->subType() == Node::Collision) {
793 resolveTargets(node);
797 if (child->doc().hasTableOfContents()) {
798 const QList<Atom*>& toc = child->doc().tableOfContents();
803 for (int i = 0; i < toc.size(); ++i) {
804 target.atom = toc.at(i);
805 QString title = Text::sectionHeading(target.atom).toString();
806 if (!title.isEmpty())
807 priv->targetHash.insert(Doc::canonicalTitle(title), target);
810 if (child->doc().hasKeywords()) {
811 const QList<Atom*>& keywords = child->doc().keywords();
816 for (int i = 0; i < keywords.size(); ++i) {
817 target.atom = keywords.at(i);
818 priv->targetHash.insert(Doc::canonicalTitle(target.atom->string()), target);
821 if (child->doc().hasTargets()) {
822 const QList<Atom*>& toc = child->doc().targets();
827 for (int i = 0; i < toc.size(); ++i) {
828 target.atom = toc.at(i);
829 priv->targetHash.insert(Doc::canonicalTitle(target.atom->string()), target);
836 For each QML class node that points to a C++ class node,
837 follow its C++ class node pointer and set the C++ class
838 node's QML class node pointer back to the QML class node.
840 void Tree::resolveCppToQmlLinks()
843 foreach (Node* child, roo.childNodes()) {
844 if (child->type() == Node::Fake && child->subType() == Node::QmlClass) {
845 QmlClassNode* qcn = static_cast<QmlClassNode*>(child);
846 ClassNode* cn = const_cast<ClassNode*>(qcn->classNode());
848 cn->setQmlElement(qcn);
854 For each QML class node in the tree, determine whether
855 it inherits a QML base class and, if so, which one, and
856 store that pointer in the QML class node's state.
858 void Tree::resolveQmlInheritance()
861 foreach (Node* child, roo.childNodes()) {
862 if (child->type() == Node::Fake) {
863 if (child->subType() == Node::QmlClass) {
864 QmlClassNode* qcn = static_cast<QmlClassNode*>(child);
865 qcn->resolveInheritance(this);
867 else if (child->subType() == Node::Collision) {
868 NameCollisionNode* ncn = static_cast<NameCollisionNode*>(child);
869 foreach (Node* child, ncn->childNodes()) {
870 if (child->type() == Node::Fake) {
871 if (child->subType() == Node::QmlClass) {
872 QmlClassNode* qcn = static_cast<QmlClassNode*>(child);
873 qcn->resolveInheritance(this);
884 void Tree::fixInheritance(NamespaceNode* rootNode)
889 NodeList::ConstIterator c = rootNode->childNodes().begin();
890 while (c != rootNode->childNodes().end()) {
891 if ((*c)->type() == Node::Class)
892 static_cast<ClassNode*>(*c)->fixBaseClasses();
893 else if ((*c)->type() == Node::Namespace) {
894 NamespaceNode* ns = static_cast<NamespaceNode*>(*c);
903 FunctionNode* Tree::findVirtualFunctionInBaseClasses(ClassNode* classe,
906 QList<RelatedClass>::ConstIterator r = classe->baseClasses().begin();
907 while (r != classe->baseClasses().end()) {
909 if (((func = findVirtualFunctionInBaseClasses((*r).node, clone)) != 0 ||
910 (func = (*r).node->findFunctionNode(clone)) != 0)) {
911 if (func->virtualness() != FunctionNode::NonVirtual)
921 void Tree::fixPropertyUsingBaseClasses(ClassNode* classe, PropertyNode* property)
923 QList<RelatedClass>::const_iterator r = classe->baseClasses().begin();
924 while (r != classe->baseClasses().end()) {
925 Node* n = r->node->findChildNodeByNameAndType(property->name(), Node::Property);
927 PropertyNode* baseProperty = static_cast<PropertyNode*>(n);
928 fixPropertyUsingBaseClasses(r->node, baseProperty);
929 property->setOverriddenFrom(baseProperty);
932 fixPropertyUsingBaseClasses(r->node, property);
940 NodeList Tree::allBaseClasses(const ClassNode* classe) const
943 foreach (const RelatedClass& r, classe->baseClasses()) {
945 result += allBaseClasses(r.node);
952 void Tree::readIndexes(const QStringList& indexFiles)
954 foreach (const QString& indexFile, indexFiles)
955 readIndexFile(indexFile);
959 Read the QDomDocument at \a path and get the index from it.
961 void Tree::readIndexFile(const QString& path)
964 if (file.open(QFile::ReadOnly)) {
965 QDomDocument document;
966 document.setContent(&file);
969 QDomElement indexElement = document.documentElement();
970 QString indexUrl = indexElement.attribute("url", "");
971 priv->basesList.clear();
972 priv->relatedList.clear();
974 // Scan all elements in the XML file, constructing a map that contains
975 // base classes for each class found.
977 QDomElement child = indexElement.firstChildElement();
978 while (!child.isNull()) {
979 readIndexSection(child, root(), indexUrl);
980 child = child.nextSiblingElement();
983 // Now that all the base classes have been found for this index,
984 // arrange them into an inheritance hierarchy.
991 Read a <section> element from the index file and create the
994 void Tree::readIndexSection(const QDomElement& element,
996 const QString& indexUrl)
998 QString name = element.attribute("name");
999 QString href = element.attribute("href");
1004 if (element.nodeName() == "namespace") {
1005 section = new NamespaceNode(parent, name);
1007 if (!indexUrl.isEmpty())
1008 location = Location(indexUrl + QLatin1Char('/') + name.toLower() + ".html");
1009 else if (!indexUrl.isNull())
1010 location = Location(name.toLower() + ".html");
1013 else if (element.nodeName() == "class") {
1014 section = new ClassNode(parent, name);
1015 priv->basesList.append(QPair<ClassNode*,QString>(
1016 static_cast<ClassNode*>(section), element.attribute("bases")));
1018 if (!indexUrl.isEmpty())
1019 location = Location(indexUrl + QLatin1Char('/') + name.toLower() + ".html");
1020 else if (!indexUrl.isNull())
1021 location = Location(name.toLower() + ".html");
1024 else if ((element.nodeName() == "qmlclass") ||
1025 ((element.nodeName() == "page") && (element.attribute("subtype") == "qmlclass"))) {
1026 QmlClassNode* qcn = new QmlClassNode(parent, name, 0);
1027 qcn->setTitle(element.attribute("title"));
1028 if (element.hasAttribute("location"))
1029 name = element.attribute("location", "");
1030 if (!indexUrl.isEmpty())
1031 location = Location(indexUrl + QLatin1Char('/') + name);
1032 else if (!indexUrl.isNull())
1033 location = Location(name);
1036 else if (element.nodeName() == "qmlbasictype") {
1037 QmlBasicTypeNode* qbtn = new QmlBasicTypeNode(parent, name);
1038 qbtn->setTitle(element.attribute("title"));
1039 if (element.hasAttribute("location"))
1040 name = element.attribute("location", "");
1041 if (!indexUrl.isEmpty())
1042 location = Location(indexUrl + QLatin1Char('/') + name);
1043 else if (!indexUrl.isNull())
1044 location = Location(name);
1047 else if (element.nodeName() == "page") {
1048 Node::SubType subtype;
1049 Node::PageType ptype = Node::NoPageType;
1050 if (element.attribute("subtype") == "example") {
1051 subtype = Node::Example;
1052 ptype = Node::ExamplePage;
1054 else if (element.attribute("subtype") == "header") {
1055 subtype = Node::HeaderFile;
1056 ptype = Node::ApiPage;
1058 else if (element.attribute("subtype") == "file") {
1059 subtype = Node::File;
1060 ptype = Node::NoPageType;
1062 else if (element.attribute("subtype") == "group") {
1063 subtype = Node::Group;
1064 ptype = Node::OverviewPage;
1066 else if (element.attribute("subtype") == "module") {
1067 subtype = Node::Module;
1068 ptype = Node::OverviewPage;
1070 else if (element.attribute("subtype") == "page") {
1071 subtype = Node::Page;
1072 ptype = Node::ArticlePage;
1074 else if (element.attribute("subtype") == "externalpage") {
1075 subtype = Node::ExternalPage;
1076 ptype = Node::ArticlePage;
1078 else if (element.attribute("subtype") == "qmlclass") {
1079 subtype = Node::QmlClass;
1080 ptype = Node::ApiPage;
1082 else if (element.attribute("subtype") == "qmlpropertygroup") {
1083 subtype = Node::QmlPropertyGroup;
1084 ptype = Node::ApiPage;
1086 else if (element.attribute("subtype") == "qmlbasictype") {
1087 subtype = Node::QmlBasicType;
1088 ptype = Node::ApiPage;
1093 FakeNode* fakeNode = new FakeNode(parent, name, subtype, ptype);
1094 fakeNode->setTitle(element.attribute("title"));
1096 if (element.hasAttribute("location"))
1097 name = element.attribute("location", "");
1099 if (!indexUrl.isEmpty())
1100 location = Location(indexUrl + QLatin1Char('/') + name);
1101 else if (!indexUrl.isNull())
1102 location = Location(name);
1107 else if (element.nodeName() == "enum") {
1108 EnumNode* enumNode = new EnumNode(parent, name);
1110 if (!indexUrl.isEmpty())
1112 Location(indexUrl + QLatin1Char('/') + parent->name().toLower() + ".html");
1113 else if (!indexUrl.isNull())
1114 location = Location(parent->name().toLower() + ".html");
1116 QDomElement child = element.firstChildElement("value");
1117 while (!child.isNull()) {
1118 EnumItem item(child.attribute("name"), child.attribute("value"));
1119 enumNode->addItem(item);
1120 child = child.nextSiblingElement("value");
1125 } else if (element.nodeName() == "typedef") {
1126 section = new TypedefNode(parent, name);
1128 if (!indexUrl.isEmpty())
1130 Location(indexUrl + QLatin1Char('/') + parent->name().toLower() + ".html");
1131 else if (!indexUrl.isNull())
1132 location = Location(parent->name().toLower() + ".html");
1135 else if (element.nodeName() == "property") {
1136 section = new PropertyNode(parent, name);
1138 if (!indexUrl.isEmpty())
1140 Location(indexUrl + QLatin1Char('/') + parent->name().toLower() + ".html");
1141 else if (!indexUrl.isNull())
1142 location = Location(parent->name().toLower() + ".html");
1144 } else if (element.nodeName() == "function") {
1145 FunctionNode::Virtualness virt;
1146 if (element.attribute("virtual") == "non")
1147 virt = FunctionNode::NonVirtual;
1148 else if (element.attribute("virtual") == "impure")
1149 virt = FunctionNode::ImpureVirtual;
1150 else if (element.attribute("virtual") == "pure")
1151 virt = FunctionNode::PureVirtual;
1155 FunctionNode::Metaness meta;
1156 if (element.attribute("meta") == "plain")
1157 meta = FunctionNode::Plain;
1158 else if (element.attribute("meta") == "signal")
1159 meta = FunctionNode::Signal;
1160 else if (element.attribute("meta") == "slot")
1161 meta = FunctionNode::Slot;
1162 else if (element.attribute("meta") == "constructor")
1163 meta = FunctionNode::Ctor;
1164 else if (element.attribute("meta") == "destructor")
1165 meta = FunctionNode::Dtor;
1166 else if (element.attribute("meta") == "macro")
1167 meta = FunctionNode::MacroWithParams;
1168 else if (element.attribute("meta") == "macrowithparams")
1169 meta = FunctionNode::MacroWithParams;
1170 else if (element.attribute("meta") == "macrowithoutparams")
1171 meta = FunctionNode::MacroWithoutParams;
1175 FunctionNode* functionNode = new FunctionNode(parent, name);
1176 functionNode->setReturnType(element.attribute("return"));
1177 functionNode->setVirtualness(virt);
1178 functionNode->setMetaness(meta);
1179 functionNode->setConst(element.attribute("const") == "true");
1180 functionNode->setStatic(element.attribute("static") == "true");
1181 functionNode->setOverload(element.attribute("overload") == "true");
1183 if (element.hasAttribute("relates")
1184 && element.attribute("relates") != parent->name()) {
1185 priv->relatedList.append(
1186 QPair<FunctionNode*,QString>(functionNode,
1187 element.attribute("relates")));
1190 QDomElement child = element.firstChildElement("parameter");
1191 while (!child.isNull()) {
1192 // Do not use the default value for the parameter; it is not
1193 // required, and has been known to cause problems.
1194 Parameter parameter(child.attribute("left"),
1195 child.attribute("right"),
1196 child.attribute("name"),
1197 ""); // child.attribute("default")
1198 functionNode->addParameter(parameter);
1199 child = child.nextSiblingElement("parameter");
1202 section = functionNode;
1204 if (!indexUrl.isEmpty())
1206 Location(indexUrl + QLatin1Char('/') + parent->name().toLower() + ".html");
1207 else if (!indexUrl.isNull())
1208 location = Location(parent->name().toLower() + ".html");
1211 else if (element.nodeName() == "variable") {
1212 section = new VariableNode(parent, name);
1214 if (!indexUrl.isEmpty())
1215 location = Location(indexUrl + QLatin1Char('/') + parent->name().toLower() + ".html");
1216 else if (!indexUrl.isNull())
1217 location = Location(parent->name().toLower() + ".html");
1220 else if (element.nodeName() == "keyword") {
1222 target.node = parent;
1223 target.priority = 1;
1224 target.atom = new Atom(Atom::Target, name);
1225 priv->targetHash.insert(name, target);
1229 else if (element.nodeName() == "target") {
1231 target.node = parent;
1232 target.priority = 2;
1233 target.atom = new Atom(Atom::Target, name);
1234 priv->targetHash.insert(name, target);
1238 else if (element.nodeName() == "contents") {
1240 target.node = parent;
1241 target.priority = 3;
1242 target.atom = new Atom(Atom::Target, name);
1243 priv->targetHash.insert(name, target);
1250 QString access = element.attribute("access");
1251 if (access == "public")
1252 section->setAccess(Node::Public);
1253 else if (access == "protected")
1254 section->setAccess(Node::Protected);
1255 else if (access == "private")
1256 section->setAccess(Node::Private);
1258 section->setAccess(Node::Public);
1260 if ((element.nodeName() != "page") &&
1261 (element.nodeName() != "qmlclass") &&
1262 (element.nodeName() != "qmlbasictype")) {
1263 QString threadSafety = element.attribute("threadsafety");
1264 if (threadSafety == "non-reentrant")
1265 section->setThreadSafeness(Node::NonReentrant);
1266 else if (threadSafety == "reentrant")
1267 section->setThreadSafeness(Node::Reentrant);
1268 else if (threadSafety == "thread safe")
1269 section->setThreadSafeness(Node::ThreadSafe);
1271 section->setThreadSafeness(Node::UnspecifiedSafeness);
1274 section->setThreadSafeness(Node::UnspecifiedSafeness);
1276 QString status = element.attribute("status");
1277 if (status == "compat")
1278 section->setStatus(Node::Compat);
1279 else if (status == "obsolete")
1280 section->setStatus(Node::Obsolete);
1281 else if (status == "deprecated")
1282 section->setStatus(Node::Deprecated);
1283 else if (status == "preliminary")
1284 section->setStatus(Node::Preliminary);
1285 else if (status == "commendable")
1286 section->setStatus(Node::Commendable);
1287 else if (status == "internal")
1288 section->setStatus(Node::Internal);
1289 else if (status == "main")
1290 section->setStatus(Node::Main);
1292 section->setStatus(Node::Commendable);
1294 section->setModuleName(element.attribute("module"));
1295 if (!indexUrl.isEmpty()) {
1296 if (indexUrl.startsWith(QLatin1Char('.')))
1297 section->setUrl(href);
1299 section->setUrl(indexUrl + QLatin1Char('/') + href);
1302 // Create some content for the node.
1303 QSet<QString> emptySet;
1305 Doc doc(location, location, " ", emptySet); // placeholder
1306 section->setDoc(doc);
1307 section->setIndexNodeFlag();
1309 if (section->isInnerNode()) {
1310 InnerNode* inner = static_cast<InnerNode*>(section);
1312 QDomElement child = element.firstChildElement();
1314 while (!child.isNull()) {
1315 if (element.nodeName() == "class")
1316 readIndexSection(child, inner, indexUrl);
1317 else if (element.nodeName() == "qmlclass")
1318 readIndexSection(child, inner, indexUrl);
1319 else if (element.nodeName() == "page")
1320 readIndexSection(child, inner, indexUrl);
1321 else if (element.nodeName() == "namespace" && !name.isEmpty())
1322 // The root node in the index is a namespace with an empty name.
1323 readIndexSection(child, inner, indexUrl);
1325 readIndexSection(child, parent, indexUrl);
1327 child = child.nextSiblingElement();
1335 QString Tree::readIndexText(const QDomElement& element)
1338 QDomNode child = element.firstChild();
1339 while (!child.isNull()) {
1341 text += child.toText().nodeValue();
1342 child = child.nextSibling();
1349 void Tree::resolveIndex()
1351 QPair<ClassNode*,QString> pair;
1353 foreach (pair, priv->basesList) {
1354 foreach (const QString& base, pair.second.split(QLatin1Char(','))) {
1355 Node* n = root()->findChildNodeByNameAndType(base, Node::Class);
1357 pair.first->addBaseClass(Node::Public, static_cast<ClassNode*>(n));
1362 QPair<FunctionNode*,QString> relatedPair;
1364 foreach (relatedPair, priv->relatedList) {
1365 Node* n = root()->findChildNodeByNameAndType(relatedPair.second, Node::Class);
1367 relatedPair.first->setRelates(static_cast<ClassNode*>(n));
1372 Generate the index section with the given \a writer for the \a node
1373 specified, returning true if an element was written; otherwise returns
1376 bool Tree::generateIndexSection(QXmlStreamWriter& writer,
1378 bool generateInternalNodes)
1380 if (!node->url().isEmpty())
1384 switch (node->type()) {
1385 case Node::Namespace:
1386 nodeName = "namespace";
1393 if (node->subType() == Node::QmlClass)
1394 nodeName = "qmlclass";
1395 else if (node->subType() == Node::QmlBasicType)
1396 nodeName = "qmlbasictype";
1402 nodeName = "typedef";
1404 case Node::Property:
1405 nodeName = "property";
1407 case Node::Function:
1408 nodeName = "function";
1410 case Node::Variable:
1411 nodeName = "variable";
1413 case Node::QmlProperty:
1414 nodeName = "qmlproperty";
1416 case Node::QmlSignal:
1417 nodeName = "qmlsignal";
1419 case Node::QmlSignalHandler:
1420 nodeName = "qmlsignalhandler";
1422 case Node::QmlMethod:
1423 nodeName = "qmlmethod";
1430 switch (node->access()) {
1434 case Node::Protected:
1435 access = "protected";
1438 // Do not include private non-internal nodes in the index.
1439 // (Internal public and protected nodes are marked as private
1440 // by qdoc. We can check their internal status to determine
1441 // whether they were really private to begin with.)
1442 if (node->status() == Node::Internal && generateInternalNodes)
1443 access = "internal";
1451 QString objName = node->name();
1453 // Special case: only the root node should have an empty name.
1454 if (objName.isEmpty() && node != root())
1457 writer.writeStartElement(nodeName);
1459 QXmlStreamAttributes attributes;
1460 writer.writeAttribute("access", access);
1462 if (node->type() != Node::Fake) {
1463 QString threadSafety;
1464 switch (node->threadSafeness()) {
1465 case Node::NonReentrant:
1466 threadSafety = "non-reentrant";
1468 case Node::Reentrant:
1469 threadSafety = "reentrant";
1471 case Node::ThreadSafe:
1472 threadSafety = "thread safe";
1474 case Node::UnspecifiedSafeness:
1476 threadSafety = "unspecified";
1479 writer.writeAttribute("threadsafety", threadSafety);
1483 switch (node->status()) {
1487 case Node::Obsolete:
1488 status = "obsolete";
1490 case Node::Deprecated:
1491 status = "deprecated";
1493 case Node::Preliminary:
1494 status = "preliminary";
1496 case Node::Commendable:
1497 status = "commendable";
1499 case Node::Internal:
1500 status = "internal";
1507 writer.writeAttribute("status", status);
1509 writer.writeAttribute("name", objName);
1510 QString fullName = node->fullDocumentName();
1511 if (fullName != objName)
1512 writer.writeAttribute("fullname", fullName);
1513 QString href = node->outputSubdirectory();
1514 if (!href.isEmpty())
1515 href.append(QLatin1Char('/'));
1516 href.append(HtmlGenerator::fullDocumentLocation(node));
1517 writer.writeAttribute("href", href);
1518 if ((node->type() != Node::Fake) && (!node->isQmlNode()))
1519 writer.writeAttribute("location", node->location().fileName());
1521 switch (node->type()) {
1525 // Classes contain information about their base classes.
1527 const ClassNode* classNode = static_cast<const ClassNode*>(node);
1528 QList<RelatedClass> bases = classNode->baseClasses();
1529 QSet<QString> baseStrings;
1530 foreach (const RelatedClass& related, bases) {
1531 ClassNode* baseClassNode = related.node;
1532 baseStrings.insert(baseClassNode->name());
1534 writer.writeAttribute("bases", QStringList(baseStrings.toList()).join(","));
1535 writer.writeAttribute("module", node->moduleName());
1539 case Node::Namespace:
1540 writer.writeAttribute("module", node->moduleName());
1546 Fake nodes (such as manual pages) contain subtypes,
1547 titles and other attributes.
1550 const FakeNode* fakeNode = static_cast<const FakeNode*>(node);
1551 switch (fakeNode->subType()) {
1553 writer.writeAttribute("subtype", "example");
1555 case Node::HeaderFile:
1556 writer.writeAttribute("subtype", "header");
1559 writer.writeAttribute("subtype", "file");
1562 writer.writeAttribute("subtype", "group");
1565 writer.writeAttribute("subtype", "module");
1568 writer.writeAttribute("subtype", "page");
1570 case Node::ExternalPage:
1571 writer.writeAttribute("subtype", "externalpage");
1573 case Node::QmlClass:
1574 //writer.writeAttribute("subtype", "qmlclass");
1576 case Node::QmlBasicType:
1577 //writer.writeAttribute("subtype", "qmlbasictype");
1582 writer.writeAttribute("title", fakeNode->title());
1583 writer.writeAttribute("fulltitle", fakeNode->fullTitle());
1584 writer.writeAttribute("subtitle", fakeNode->subTitle());
1585 writer.writeAttribute("location", fakeNode->doc().location().fileName());
1589 case Node::Function:
1592 Function nodes contain information about the type of
1593 function being described.
1596 const FunctionNode* functionNode =
1597 static_cast<const FunctionNode*>(node);
1599 switch (functionNode->virtualness()) {
1600 case FunctionNode::NonVirtual:
1601 writer.writeAttribute("virtual", "non");
1603 case FunctionNode::ImpureVirtual:
1604 writer.writeAttribute("virtual", "impure");
1606 case FunctionNode::PureVirtual:
1607 writer.writeAttribute("virtual", "pure");
1612 switch (functionNode->metaness()) {
1613 case FunctionNode::Plain:
1614 writer.writeAttribute("meta", "plain");
1616 case FunctionNode::Signal:
1617 writer.writeAttribute("meta", "signal");
1619 case FunctionNode::Slot:
1620 writer.writeAttribute("meta", "slot");
1622 case FunctionNode::Ctor:
1623 writer.writeAttribute("meta", "constructor");
1625 case FunctionNode::Dtor:
1626 writer.writeAttribute("meta", "destructor");
1628 case FunctionNode::MacroWithParams:
1629 writer.writeAttribute("meta", "macrowithparams");
1631 case FunctionNode::MacroWithoutParams:
1632 writer.writeAttribute("meta", "macrowithoutparams");
1637 writer.writeAttribute("const", functionNode->isConst()?"true":"false");
1638 writer.writeAttribute("static", functionNode->isStatic()?"true":"false");
1639 writer.writeAttribute("overload", functionNode->isOverload()?"true":"false");
1640 if (functionNode->isOverload())
1641 writer.writeAttribute("overload-number", QString::number(functionNode->overloadNumber()));
1642 if (functionNode->relates())
1643 writer.writeAttribute("relates", functionNode->relates()->name());
1644 const PropertyNode* propertyNode = functionNode->associatedProperty();
1646 writer.writeAttribute("associated-property", propertyNode->name());
1647 writer.writeAttribute("type", functionNode->returnType());
1651 case Node::QmlProperty:
1653 QmlPropertyNode* qpn = static_cast<QmlPropertyNode*>(node);
1654 writer.writeAttribute("type", qpn->dataType());
1655 writer.writeAttribute("attached", qpn->isAttached() ? "true" : "false");
1656 writer.writeAttribute("writable", qpn->isWritable(this) ? "true" : "false");
1659 case Node::Property:
1661 const PropertyNode* propertyNode = static_cast<const PropertyNode*>(node);
1662 writer.writeAttribute("type", propertyNode->dataType());
1663 foreach (const Node* fnNode, propertyNode->getters()) {
1665 const FunctionNode* functionNode = static_cast<const FunctionNode*>(fnNode);
1666 writer.writeStartElement("getter");
1667 writer.writeAttribute("name", functionNode->name());
1668 writer.writeEndElement(); // getter
1671 foreach (const Node* fnNode, propertyNode->setters()) {
1673 const FunctionNode* functionNode = static_cast<const FunctionNode*>(fnNode);
1674 writer.writeStartElement("setter");
1675 writer.writeAttribute("name", functionNode->name());
1676 writer.writeEndElement(); // setter
1679 foreach (const Node* fnNode, propertyNode->resetters()) {
1681 const FunctionNode* functionNode = static_cast<const FunctionNode*>(fnNode);
1682 writer.writeStartElement("resetter");
1683 writer.writeAttribute("name", functionNode->name());
1684 writer.writeEndElement(); // resetter
1687 foreach (const Node* fnNode, propertyNode->notifiers()) {
1689 const FunctionNode* functionNode = static_cast<const FunctionNode*>(fnNode);
1690 writer.writeStartElement("notifier");
1691 writer.writeAttribute("name", functionNode->name());
1692 writer.writeEndElement(); // notifier
1698 case Node::Variable:
1700 const VariableNode* variableNode =
1701 static_cast<const VariableNode*>(node);
1702 writer.writeAttribute("type", variableNode->dataType());
1703 writer.writeAttribute("static",
1704 variableNode->isStatic() ? "true" : "false");
1711 // Inner nodes and function nodes contain child nodes of some sort, either
1712 // actual child nodes or function parameters. For these, we close the
1713 // opening tag, create child elements, then add a closing tag for the
1714 // element. Elements for all other nodes are closed in the opening tag.
1716 if (node->isInnerNode()) {
1718 const InnerNode* inner = static_cast<const InnerNode*>(node);
1720 // For internal pages, we canonicalize the target, keyword and content
1721 // item names so that they can be used by qdoc for other sets of
1723 // The reason we do this here is that we don't want to ruin
1724 // externally composed indexes, containing non-qdoc-style target names
1725 // when reading in indexes.
1727 if (inner->doc().hasTargets()) {
1728 bool external = false;
1729 if (inner->type() == Node::Fake) {
1730 const FakeNode* fakeNode = static_cast<const FakeNode*>(inner);
1731 if (fakeNode->subType() == Node::ExternalPage)
1735 foreach (const Atom* target, inner->doc().targets()) {
1736 QString targetName = target->string();
1738 targetName = Doc::canonicalTitle(targetName);
1740 writer.writeStartElement("target");
1741 writer.writeAttribute("name", targetName);
1742 writer.writeEndElement(); // target
1745 if (inner->doc().hasKeywords()) {
1746 foreach (const Atom* keyword, inner->doc().keywords()) {
1747 writer.writeStartElement("keyword");
1748 writer.writeAttribute("name",
1749 Doc::canonicalTitle(keyword->string()));
1750 writer.writeEndElement(); // keyword
1753 if (inner->doc().hasTableOfContents()) {
1754 for (int i = 0; i < inner->doc().tableOfContents().size(); ++i) {
1755 Atom* item = inner->doc().tableOfContents()[i];
1756 int level = inner->doc().tableOfContentsLevels()[i];
1758 QString title = Text::sectionHeading(item).toString();
1759 writer.writeStartElement("contents");
1760 writer.writeAttribute("name", Doc::canonicalTitle(title));
1761 writer.writeAttribute("title", title);
1762 writer.writeAttribute("level", QString::number(level));
1763 writer.writeEndElement(); // contents
1768 else if (node->type() == Node::Function) {
1770 const FunctionNode* functionNode = static_cast<const FunctionNode*>(node);
1771 // Write a signature attribute for convenience.
1772 QStringList signatureList;
1773 QStringList resolvedParameters;
1775 foreach (const Parameter& parameter, functionNode->parameters()) {
1776 QString leftType = parameter.leftType();
1777 const Node* leftNode = const_cast<Tree*>(this)->findNode(parameter.leftType().split("::"),
1778 0, SearchBaseClasses|NonFunction);
1779 if (!leftNode || leftNode->type() != Node::Typedef) {
1780 leftNode = const_cast<Tree*>(this)->findNode(parameter.leftType().split("::"),
1781 node->parent(), SearchBaseClasses|NonFunction);
1783 if (leftNode && leftNode->type() == Node::Typedef) {
1784 if (leftNode->type() == Node::Typedef) {
1785 const TypedefNode* typedefNode =
1786 static_cast<const TypedefNode*>(leftNode);
1787 if (typedefNode->associatedEnum()) {
1788 leftType = "QFlags<" + typedefNode->associatedEnum()->fullDocumentName() + QLatin1Char('>');
1792 leftType = leftNode->fullDocumentName();
1794 resolvedParameters.append(leftType);
1795 signatureList.append(leftType + QLatin1Char(' ') + parameter.name());
1798 QString signature = functionNode->name()+QLatin1Char('(')+signatureList.join(", ")+QLatin1Char(')');
1799 if (functionNode->isConst())
1800 signature += " const";
1801 writer.writeAttribute("signature", signature);
1803 for (int i = 0; i < functionNode->parameters().size(); ++i) {
1804 Parameter parameter = functionNode->parameters()[i];
1805 writer.writeStartElement("parameter");
1806 writer.writeAttribute("left", resolvedParameters[i]);
1807 writer.writeAttribute("right", parameter.rightType());
1808 writer.writeAttribute("name", parameter.name());
1809 writer.writeAttribute("default", parameter.defaultValue());
1810 writer.writeEndElement(); // parameter
1814 else if (node->type() == Node::Enum) {
1816 const EnumNode* enumNode = static_cast<const EnumNode*>(node);
1817 if (enumNode->flagsType()) {
1818 writer.writeAttribute("typedef",enumNode->flagsType()->fullDocumentName());
1820 foreach (const EnumItem& item, enumNode->items()) {
1821 writer.writeStartElement("value");
1822 writer.writeAttribute("name", item.name());
1823 writer.writeAttribute("value", item.value());
1824 writer.writeEndElement(); // value
1828 else if (node->type() == Node::Typedef) {
1830 const TypedefNode* typedefNode = static_cast<const TypedefNode*>(node);
1831 if (typedefNode->associatedEnum()) {
1832 writer.writeAttribute("enum",typedefNode->associatedEnum()->fullDocumentName());
1841 Returns true if the node \a n1 is less than node \a n2.
1842 The comparison is performed by comparing properties of the nodes in order
1843 of increasing complexity.
1845 bool compareNodes(const Node* n1, const Node* n2)
1847 // Private nodes can occur in any order since they won't normally be
1848 // written to the index.
1849 if (n1->access() == Node::Private && n2->access() == Node::Private)
1852 if (n1->location().filePath() < n2->location().filePath())
1854 else if (n1->location().filePath() > n2->location().filePath())
1857 if (n1->type() < n2->type())
1859 else if (n1->type() > n2->type())
1862 if (n1->name() < n2->name())
1864 else if (n1->name() > n2->name())
1867 if (n1->access() < n2->access())
1869 else if (n1->access() > n2->access())
1872 if (n1->type() == Node::Function && n2->type() == Node::Function) {
1873 const FunctionNode* f1 = static_cast<const FunctionNode*>(n1);
1874 const FunctionNode* f2 = static_cast<const FunctionNode*>(n2);
1876 if (f1->isConst() < f2->isConst())
1878 else if (f1->isConst() > f2->isConst())
1881 if (f1->signature() < f2->signature())
1883 else if (f1->signature() > f2->signature())
1887 if (n1->type() == Node::Fake && n2->type() == Node::Fake) {
1888 const FakeNode* f1 = static_cast<const FakeNode*>(n1);
1889 const FakeNode* f2 = static_cast<const FakeNode*>(n2);
1890 if (f1->fullTitle() < f2->fullTitle())
1892 else if (f1->fullTitle() > f2->fullTitle())
1900 Generate index sections for the child nodes of the given \a node
1901 using the \a writer specified. If \a generateInternalNodes is true,
1902 nodes marked as internal will be included in the index; otherwise,
1903 they will be omitted.
1905 void Tree::generateIndexSections(QXmlStreamWriter& writer,
1907 bool generateInternalNodes)
1909 if (generateIndexSection(writer, node, generateInternalNodes)) {
1911 if (node->isInnerNode()) {
1912 const InnerNode* inner = static_cast<const InnerNode*>(node);
1914 NodeList cnodes = inner->childNodes();
1915 qSort(cnodes.begin(), cnodes.end(), compareNodes);
1917 foreach (Node* child, cnodes) {
1919 Don't generate anything for a QML property group node.
1920 It is just a place holder for a collection of QML property
1921 nodes. Recurse to its children, which are the QML property
1924 if (child->subType() == Node::QmlPropertyGroup) {
1925 const InnerNode* pgn = static_cast<const InnerNode*>(child);
1926 foreach (Node* c, pgn->childNodes()) {
1927 generateIndexSections(writer, c, generateInternalNodes);
1931 generateIndexSections(writer, child, generateInternalNodes);
1935 foreach (const Node* child, inner->relatedNodes()) {
1936 QDomElement childElement = generateIndexSections(document, child);
1937 element.appendChild(childElement);
1941 writer.writeEndElement();
1946 Outputs an index file.
1948 void Tree::generateIndex(const QString& fileName,
1950 const QString& title,
1951 bool generateInternalNodes)
1953 QFile file(fileName);
1954 if (!file.open(QFile::WriteOnly | QFile::Text))
1957 QXmlStreamWriter writer(&file);
1958 writer.setAutoFormatting(true);
1959 writer.writeStartDocument();
1960 writer.writeDTD("<!DOCTYPE QDOCINDEX>");
1962 writer.writeStartElement("INDEX");
1963 writer.writeAttribute("url", url);
1964 writer.writeAttribute("title", title);
1965 writer.writeAttribute("version", version());
1967 generateIndexSections(writer, root(), generateInternalNodes);
1969 writer.writeEndElement(); // INDEX
1970 writer.writeEndElement(); // QDOCINDEX
1971 writer.writeEndDocument();
1976 Generate the tag file section with the given \a writer for the \a node
1977 specified, returning true if an element was written; otherwise returns
1980 void Tree::generateTagFileCompounds(QXmlStreamWriter& writer, const InnerNode* inner)
1982 foreach (const Node* node, inner->childNodes()) {
1984 if (!node->url().isEmpty())
1988 switch (node->type()) {
1989 case Node::Namespace:
1997 case Node::Property:
1998 case Node::Function:
1999 case Node::Variable:
2005 switch (node->access()) {
2009 case Node::Protected:
2010 access = "protected";
2017 QString objName = node->name();
2019 // Special case: only the root node should have an empty name.
2020 if (objName.isEmpty() && node != root())
2023 // *** Write the starting tag for the element here. ***
2024 writer.writeStartElement("compound");
2025 writer.writeAttribute("kind", kind);
2027 if (node->type() == Node::Class) {
2028 writer.writeTextElement("name", node->fullDocumentName());
2029 writer.writeTextElement("filename", HtmlGenerator::fullDocumentLocation(node,true));
2031 // Classes contain information about their base classes.
2032 const ClassNode* classNode = static_cast<const ClassNode*>(node);
2033 QList<RelatedClass> bases = classNode->baseClasses();
2034 foreach (const RelatedClass& related, bases) {
2035 ClassNode* baseClassNode = related.node;
2036 writer.writeTextElement("base", baseClassNode->name());
2039 // Recurse to write all members.
2040 generateTagFileMembers(writer, static_cast<const InnerNode*>(node));
2041 writer.writeEndElement();
2043 // Recurse to write all compounds.
2044 generateTagFileCompounds(writer, static_cast<const InnerNode*>(node));
2046 writer.writeTextElement("name", node->fullDocumentName());
2047 writer.writeTextElement("filename", HtmlGenerator::fullDocumentLocation(node,true));
2049 // Recurse to write all members.
2050 generateTagFileMembers(writer, static_cast<const InnerNode*>(node));
2051 writer.writeEndElement();
2053 // Recurse to write all compounds.
2054 generateTagFileCompounds(writer, static_cast<const InnerNode*>(node));
2061 void Tree::generateTagFileMembers(QXmlStreamWriter& writer, const InnerNode* inner)
2063 foreach (const Node* node, inner->childNodes()) {
2065 if (!node->url().isEmpty())
2070 switch (node->type()) {
2072 nodeName = "member";
2076 nodeName = "member";
2079 case Node::Property:
2080 nodeName = "member";
2083 case Node::Function:
2084 nodeName = "member";
2087 case Node::Namespace:
2088 nodeName = "namespace";
2093 case Node::Variable:
2099 switch (node->access()) {
2103 case Node::Protected:
2104 access = "protected";
2111 QString objName = node->name();
2113 // Special case: only the root node should have an empty name.
2114 if (objName.isEmpty() && node != root())
2117 // *** Write the starting tag for the element here. ***
2118 writer.writeStartElement(nodeName);
2119 if (!kind.isEmpty())
2120 writer.writeAttribute("kind", kind);
2122 switch (node->type()) {
2125 writer.writeCharacters(node->fullDocumentName());
2126 writer.writeEndElement();
2128 case Node::Namespace:
2129 writer.writeCharacters(node->fullDocumentName());
2130 writer.writeEndElement();
2132 case Node::Function:
2135 Function nodes contain information about
2136 the type of function being described.
2139 const FunctionNode* functionNode =
2140 static_cast<const FunctionNode*>(node);
2141 writer.writeAttribute("protection", access);
2143 switch (functionNode->virtualness()) {
2144 case FunctionNode::NonVirtual:
2145 writer.writeAttribute("virtualness", "non");
2147 case FunctionNode::ImpureVirtual:
2148 writer.writeAttribute("virtualness", "virtual");
2150 case FunctionNode::PureVirtual:
2151 writer.writeAttribute("virtual", "pure");
2156 writer.writeAttribute("static",
2157 functionNode->isStatic() ? "yes" : "no");
2159 if (functionNode->virtualness() == FunctionNode::NonVirtual)
2160 writer.writeTextElement("type", functionNode->returnType());
2162 writer.writeTextElement("type",
2163 "virtual " + functionNode->returnType());
2165 writer.writeTextElement("name", objName);
2166 QStringList pieces = HtmlGenerator::fullDocumentLocation(node,true).split(QLatin1Char('#'));
2167 writer.writeTextElement("anchorfile", pieces[0]);
2168 writer.writeTextElement("anchor", pieces[1]);
2170 // Write a signature attribute for convenience.
2171 QStringList signatureList;
2173 foreach (const Parameter& parameter, functionNode->parameters()) {
2174 QString leftType = parameter.leftType();
2175 const Node* leftNode = const_cast<Tree*>(this)->findNode(parameter.leftType().split("::"),
2176 0, SearchBaseClasses|NonFunction);
2177 if (!leftNode || leftNode->type() != Node::Typedef) {
2178 leftNode = const_cast<Tree*>(this)->findNode(parameter.leftType().split("::"),
2179 node->parent(), SearchBaseClasses|NonFunction);
2181 if (leftNode && leftNode->type() == Node::Typedef) {
2182 const TypedefNode* typedefNode = static_cast<const TypedefNode*>(leftNode);
2183 if (typedefNode->associatedEnum()) {
2184 leftType = "QFlags<" + typedefNode->associatedEnum()->fullDocumentName() + QLatin1Char('>');
2187 signatureList.append(leftType + QLatin1Char(' ') + parameter.name());
2190 QString signature = QLatin1Char('(')+signatureList.join(", ")+QLatin1Char(')');
2191 if (functionNode->isConst())
2192 signature += " const";
2193 if (functionNode->virtualness() == FunctionNode::PureVirtual)
2194 signature += " = 0";
2195 writer.writeTextElement("arglist", signature);
2197 writer.writeEndElement(); // member
2200 case Node::Property:
2202 const PropertyNode* propertyNode = static_cast<const PropertyNode*>(node);
2203 writer.writeAttribute("type", propertyNode->dataType());
2204 writer.writeTextElement("name", objName);
2205 QStringList pieces = HtmlGenerator::fullDocumentLocation(node,true).split(QLatin1Char('#'));
2206 writer.writeTextElement("anchorfile", pieces[0]);
2207 writer.writeTextElement("anchor", pieces[1]);
2208 writer.writeTextElement("arglist", "");
2210 writer.writeEndElement(); // member
2215 const EnumNode* enumNode = static_cast<const EnumNode*>(node);
2216 writer.writeTextElement("name", objName);
2217 QStringList pieces = HtmlGenerator::fullDocumentLocation(node).split(QLatin1Char('#'));
2218 writer.writeTextElement("anchor", pieces[1]);
2219 writer.writeTextElement("arglist", "");
2220 writer.writeEndElement(); // member
2222 for (int i = 0; i < enumNode->items().size(); ++i) {
2223 EnumItem item = enumNode->items().value(i);
2224 writer.writeStartElement("member");
2225 writer.writeAttribute("name", item.name());
2226 writer.writeTextElement("anchor", pieces[1]);
2227 writer.writeTextElement("arglist", "");
2228 writer.writeEndElement(); // member
2235 const TypedefNode* typedefNode = static_cast<const TypedefNode*>(node);
2236 if (typedefNode->associatedEnum())
2237 writer.writeAttribute("type", typedefNode->associatedEnum()->fullDocumentName());
2239 writer.writeAttribute("type", "");
2240 writer.writeTextElement("name", objName);
2241 QStringList pieces = HtmlGenerator::fullDocumentLocation(node,true).split(QLatin1Char('#'));
2242 writer.writeTextElement("anchorfile", pieces[0]);
2243 writer.writeTextElement("anchor", pieces[1]);
2244 writer.writeTextElement("arglist", "");
2246 writer.writeEndElement(); // member
2249 case Node::Variable:
2257 Writes a tag file named \a fileName.
2259 void Tree::generateTagFile(const QString& fileName)
2261 QFile file(fileName);
2262 if (!file.open(QFile::WriteOnly | QFile::Text))
2265 QXmlStreamWriter writer(&file);
2266 writer.setAutoFormatting(true);
2267 writer.writeStartDocument();
2269 writer.writeStartElement("tagfile");
2271 generateTagFileCompounds(writer, root());
2273 writer.writeEndElement(); // tagfile
2274 writer.writeEndDocument();
2280 void Tree::addExternalLink(const QString& url, const Node* relative)
2282 FakeNode* fakeNode = new FakeNode(root(), url, Node::ExternalPage, Node::ArticlePage);
2283 fakeNode->setAccess(Node::Public);
2285 // Create some content for the node.
2286 QSet<QString> emptySet;
2287 Location location(relative->doc().location());
2288 Doc doc(location, location, " ", emptySet); // placeholder
2289 fakeNode->setDoc(doc);
2293 Find the node with the specified \a path name that is of
2294 the specified \a type and \a subtype. Begin the search at
2295 the \a start node. If the \a start node is 0, begin the
2296 search at the tree root. \a subtype is not used unless
2297 \a type is \c{Fake}.
2299 Node* Tree::findNodeByNameAndType(const QStringList& path,
2301 Node::SubType subtype,
2303 bool acceptCollision)
2306 start = const_cast<NamespaceNode*>(root());
2307 Node* result = findNodeRecursive(path, 0, start, type, subtype, acceptCollision);
2313 qDebug() << "FOUND:" << path << Node::nodeTypeString(type)
2314 << Node::nodeSubtypeString(subtype);
2316 qDebug() << "NOT FOUND:" << path << Node::nodeTypeString(type)
2317 << Node::nodeSubtypeString(subtype);
2321 Recursive search for a node identified by \a path. Each
2322 path element is a name. \a pathIndex specifies the index
2323 of the name in \a path to try to match. \a start is the
2324 node whose children shoulod be searched for one that has
2325 that name. Each time a match is found, increment the
2326 \a pathIndex and call this function recursively.
2328 If the end of the path is reached (i.e. if a matching
2329 node is found for each name in the \a path), the \a type
2330 must match the type of the last matching node, and if the
2331 type is \e{Fake}, the \a subtype must match as well.
2333 If the algorithm is successful, the pointer to the final
2334 node is returned. Otherwise 0 is returned.
2336 Node* Tree::findNodeRecursive(const QStringList& path,
2340 Node::SubType subtype,
2341 bool acceptCollision)
2343 if (!start || path.isEmpty())
2344 return 0; // no place to start, or nothing to search for.
2345 if (start->isLeaf()) {
2346 if (pathIndex >= path.size())
2347 return start; // found a match.
2348 return 0; // premature leaf
2350 if (pathIndex >= path.size())
2351 return 0; // end of search path.
2353 InnerNode* current = static_cast<InnerNode*>(start);
2354 const NodeList& children = current->childNodes();
2355 const QString& name = path.at(pathIndex);
2356 for (int i=0; i<children.size(); ++i) {
2357 Node* n = children.at(i);
2360 if (n->isQmlPropertyGroup()) {
2361 if (type == Node::QmlProperty) {
2362 n = findNodeRecursive(path, pathIndex, n, type, subtype);
2367 else if (n->name() == name) {
2368 if (pathIndex+1 >= path.size()) {
2369 if (n->type() == type) {
2370 if (type == Node::Fake) {
2371 if (n->subType() == subtype)
2373 else if (n->subType() == Node::Collision && acceptCollision)
2375 else if (subtype == Node::NoSubType)
2376 return n; // don't care what subtype is.
2382 else if (n->isCollisionNode()) {
2383 if (acceptCollision)
2385 return n = findNodeRecursive(path, pathIndex, n, type, subtype);
2392 else { // Not at the end of the path.
2393 n = findNodeRecursive(path, pathIndex+1, n, type, subtype);
2403 Find the Enum type node named \a path. Begin the search at the
2404 \a start node. If the \a start node is 0, begin the search
2405 at the root of the tree. Only an Enum type node named \a path is
2406 acceptible. If one is not found, 0 is returned.
2408 EnumNode* Tree::findEnumNode(const QStringList& path, Node* start)
2411 start = const_cast<NamespaceNode*>(root());
2412 return static_cast<EnumNode*>(findNodeRecursive(path, 0, start, Node::Enum, Node::NoSubType));
2416 Find the C++ class node named \a path. Begin the search at the
2417 \a start node. If the \a start node is 0, begin the search
2418 at the root of the tree. Only a C++ class node named \a path is
2419 acceptible. If one is not found, 0 is returned.
2421 ClassNode* Tree::findClassNode(const QStringList& path, Node* start)
2424 start = const_cast<NamespaceNode*>(root());
2425 return static_cast<ClassNode*>(findNodeRecursive(path, 0, start, Node::Class, Node::NoSubType));
2429 Find the Qml class node named \a path. Begin the search at the
2430 \a start node. If the \a start node is 0, begin the search
2431 at the root of the tree. Only a Qml class node named \a path is
2432 acceptible. If one is not found, 0 is returned.
2434 QmlClassNode* Tree::findQmlClassNode(const QStringList& path, Node* start)
2437 If the path contains one or two double colons ("::"),
2438 check first to see if the first two path strings refer
2439 to a QML element. If yes, that reference identifies a
2442 if (path.size() >= 2) {
2443 QmlClassNode* qcn = QmlClassNode::moduleMap.value(path[0]+ "::" +path[1]);
2449 start = const_cast<NamespaceNode*>(root());
2450 return static_cast<QmlClassNode*>(findNodeRecursive(path, 0, start, Node::Fake, Node::QmlClass));
2454 Find the Namespace node named \a path. Begin the search at the
2455 \a start node. If the \a start node is 0, begin the search
2456 at the root of the tree. Only a Namespace node named \a path is
2457 acceptible. If one is not found, 0 is returned.
2459 NamespaceNode* Tree::findNamespaceNode(const QStringList& path, Node* start)
2462 start = const_cast<NamespaceNode*>(root());
2463 return static_cast<NamespaceNode*>(findNodeRecursive(path, 0, start, Node::Namespace, Node::NoSubType));
2467 Find the Group node named \a path. Begin the search at the
2468 \a start node. If the \a start node is 0, begin the search
2469 at the root of the tree. Only a Group node named \a path is
2470 acceptible. If one is not found, 0 is returned.
2472 FakeNode* Tree::findGroupNode(const QStringList& path, Node* start)
2475 start = const_cast<NamespaceNode*>(root());
2476 return static_cast<FakeNode*>(findNodeRecursive(path, 0, start, Node::Fake, Node::Group));
2480 Find the Qml module node named \a path. Begin the search at the
2481 \a start node. If the \a start node is 0, begin the search
2482 at the root of the tree. Only a Qml module node named \a path is
2483 acceptible. If one is not found, 0 is returned.
2485 FakeNode* Tree::findQmlModuleNode(const QStringList& path, Node* start)
2488 start = const_cast<NamespaceNode*>(root());
2489 return static_cast<FakeNode*>(findNodeRecursive(path, 0, start, Node::Fake, Node::QmlModule));
2495 const Node* Tree::findNodeXXX(const QStringList& path, bool qml) const
2497 const Node* current = root();
2499 const Node* node = current;
2504 If the path contains one or two double colons ("::"),
2505 check first to see if the first two path strings refer
2506 to a QML element. If yes, that reference identifies a
2509 if (qml && path.size() >= 2) {
2510 QmlClassNode* qcn = QmlClassNode::moduleMap.value(path[0]+ "::" +path[1]);
2513 if (path.size() == 2)
2519 for (i = start_idx; i < path.size(); ++i) {
2520 if (node == 0 || !node->isInnerNode())
2523 const Node* next = static_cast<const InnerNode*>(node)->findChildNodeByName(path.at(i), qml);
2526 if (node && i == path.size()) {
2527 if (node->subType() != Node::QmlPropertyGroup) {
2528 if (node->subType() == Node::Collision) {
2529 node = node->applyModuleIdentifier(start);
2534 current = current->parent();