diff options
Diffstat (limited to 'src/tools/qdoc/tree.cpp')
-rw-r--r-- | src/tools/qdoc/tree.cpp | 1137 |
1 files changed, 816 insertions, 321 deletions
diff --git a/src/tools/qdoc/tree.cpp b/src/tools/qdoc/tree.cpp index 113220059c..e689227bf1 100644 --- a/src/tools/qdoc/tree.cpp +++ b/src/tools/qdoc/tree.cpp @@ -60,132 +60,91 @@ QT_BEGIN_NAMESPACE This class is now private. Only class QDocDatabase has access. Please don't change this. If you must access class Tree, do it though the pointer to the singleton QDocDatabase. + + Tree is being converted to a forest. A static member provides a + map of Tree* values with the module names as the keys. There is + one Tree in the map for each index file read, and there is one + tree that is not in the map for the module whose documentation + is being generated. */ /*! - Constructs the singleton tree. \a qdb is the pointer to the + Constructs a Tree. \a qdb is the pointer to the singleton qdoc database that is constructing the tree. This might not be necessary, and it might be removed later. */ -Tree::Tree(QDocDatabase* qdb) - : qdb_(qdb), root_(0, QString()) +Tree::Tree(const QString& module, QDocDatabase* qdb) + : module_(module), qdb_(qdb), root_(0, QString()) { + root_.setModuleName(module_); + root_.setTree(this); } /*! - Destroys the singleton Tree. + Destroys the Tree. The root node is a data member + of this object, so its destructor is called. The + destructor of each child node is called, and these + destructors are recursive. Thus the entire tree is + destroyed. */ Tree::~Tree() { + // nothing } -// 1 calls 2 +/* API members */ + /*! - Searches the tree for a node that matches the \a path. The - search begins at \a start but can move up the parent chain - recursively if no match is found. + Find the C++ class node named \a path. Begin the search at the + \a start node. If the \a start node is 0, begin the search + at the root of the tree. Only a C++ class node named \a path is + acceptible. If one is not found, 0 is returned. */ -const Node* Tree::findNode(const QStringList& path, - const Node* start, - int findFlags, - const Node* self) const +ClassNode* Tree::findClassNode(const QStringList& path, Node* start) const { - const Node* current = start; - if (!current) - current = root(); - - /* - First, search for a node assuming we don't want a QML node. - If that search fails, search again assuming we do want a - QML node. - */ - const Node* n = findNode(path,current,findFlags,self,false); - if (!n) { - n = findNode(path,current,findFlags,self,true); - } - return n; + if (!start) + start = const_cast<NamespaceNode*>(root()); + return static_cast<ClassNode*>(findNodeRecursive(path, 0, start, Node::Class)); } -// 2 is private; it is only called by 1. /*! - This overload function was extracted from the one above that has the - same signature without the last bool parameter, \a qml. This version - is called only by that other one. It is therefore private. It can - be called a second time by that other version, if the first call - returns null. If \a qml is false, the search will only match a node - that is not a QML node. If \a qml is true, the search will only - match a node that is a QML node. -*/ -const Node* Tree::findNode(const QStringList& path, - const Node* start, - int findFlags, - const Node* self, - bool qml) const + Find the Namespace node named \a path. Begin the search at + the root of the tree. Only a Namespace node named \a path + is acceptible. If one is not found, 0 is returned. + */ +NamespaceNode* Tree::findNamespaceNode(const QStringList& path) const { - const Node* current = start; - do { - const Node* node = current; - int i; - int start_idx = 0; - - /* - If the path contains one or two double colons ("::"), - check first to see if the first two path strings refer - to a QML element. If they do, path[0] will be the QML - module identifier, and path[1] will be the QML type. - If the anser is yes, the reference identifies a QML - class node. - */ - if (qml && path.size() >= 2 && !path[0].isEmpty()) { - QmlClassNode* qcn = qdb_->findQmlType(path[0], path[1]); - if (qcn) { - node = qcn; - if (path.size() == 2) - return node; - start_idx = 2; - } - } - - for (i = start_idx; i < path.size(); ++i) { - if (node == 0 || !node->isInnerNode()) - break; - - const Node* next = static_cast<const InnerNode*>(node)->findChildNodeByName(path.at(i), qml); - if (!next && (findFlags & SearchEnumValues) && i == path.size()-1) - next = static_cast<const InnerNode*>(node)->findEnumNodeForValue(path.at(i)); + Node* start = const_cast<NamespaceNode*>(root()); + return static_cast<NamespaceNode*>(findNodeRecursive(path, 0, start, Node::Namespace)); +} - if (!next && !qml && node->type() == Node::Class && (findFlags & SearchBaseClasses)) { - NodeList baseClasses = allBaseClasses(static_cast<const ClassNode*>(node)); - foreach (const Node* baseClass, baseClasses) { - next = static_cast<const InnerNode*>(baseClass)->findChildNodeByName(path.at(i)); - if (!next && (findFlags & SearchEnumValues) && i == path.size() - 1) - next = static_cast<const InnerNode*>(baseClass)->findEnumNodeForValue(path.at(i)); - if (next) - break; - } - } - node = next; - } - if (node && i == path.size() - && (!(findFlags & NonFunction) || node->type() != Node::Function - || ((FunctionNode*)node)->metaness() == FunctionNode::MacroWithoutParams)) { - if ((node != self) && (node->type() != Node::QmlPropertyGroup)) { - if (node->subType() == Node::Collision) { - node = node->applyModuleName(start); - } - return node; - } - } - current = current->parent(); - } while (current); +/*! + This function first ignores the \a clone node and searches + for the parent node with \a parentPath. If that search is + successful, it searches for a child node of the parent that + matches the \a clone node. If it finds a node that is just + like the \a clone, it returns a pointer to the found node. - return 0; + There should be a way to avoid creating the clone in the + first place. Investigate when time allows. + */ +FunctionNode* Tree::findFunctionNode(const QStringList& parentPath, const FunctionNode* clone) +{ + const Node* parent = findNamespaceNode(parentPath); + if (parent == 0) + parent = findClassNode(parentPath, 0); + if (parent == 0) + parent = findNode(parentPath); + if (parent == 0 || !parent->isInnerNode()) + return 0; + return ((InnerNode*)parent)->findFunctionNode(clone); } + /*! Find the Qml type node named \a path. Begin the search at the \a start node. If the \a start node is 0, begin the search - at the root of the tree. Only a Qml type node named \a path is + at the root of the tree. Only a Qml type node named <\a path is acceptible. If one is not found, 0 is returned. */ QmlClassNode* Tree::findQmlTypeNode(const QStringList& path) @@ -203,7 +162,7 @@ QmlClassNode* Tree::findQmlTypeNode(const QStringList& path) if (qcn) return qcn; } - return static_cast<QmlClassNode*>(findNodeRecursive(path, 0, root(), Node::Document, Node::QmlClass)); + return static_cast<QmlClassNode*>(findNodeRecursive(path, 0, root(), Node::QmlType)); } /*! @@ -215,7 +174,7 @@ QmlClassNode* Tree::findQmlTypeNode(const QStringList& path) node as its first child, and return a pointer to the new NameCollisionNode. Otherwise return 0. */ -NameCollisionNode* Tree::checkForCollision(const QString& name) const +NameCollisionNode* Tree::checkForCollision(const QString& name) { Node* n = const_cast<Node*>(findNode(QStringList(name))); if (n) { @@ -248,18 +207,6 @@ NameCollisionNode* Tree::findCollisionNode(const QString& name) const } /*! - This function just calls the const version of the same function - and returns the function node. - */ -FunctionNode* Tree::findFunctionNode(const QStringList& path, - Node* relative, - int findFlags) -{ - return const_cast<FunctionNode*> - (const_cast<const Tree*>(this)->findFunctionNode(path,relative,findFlags)); -} - -/*! This function begins searching the tree at \a relative for the \l {FunctionNode} {function node} identified by \a path. The \a findFlags are used to restrict the search. If a node @@ -274,17 +221,23 @@ const FunctionNode* Tree::findFunctionNode(const QStringList& path, if (!relative) relative = root(); - /* - If the path contains two double colons ("::"), check - first to see if it is a reference to a QML method. If - it is a reference to a QML method, first look up the - QML class node in the QML module map. - */ if (path.size() == 3 && !path[0].isEmpty()) { - QmlClassNode* qcn = qdb_->findQmlType(path[0], path[1]); - if (qcn) { - return static_cast<const FunctionNode*>(qcn->findFunctionNode(path[2])); + QmlClassNode* qcn = lookupQmlType(QString(path[0] + "::" + path[1])); + if (!qcn) { + QStringList p(path[1]); + Node* n = findNodeByNameAndType(p, Node::QmlType); + if (n) { + if (n->isQmlType()) + qcn = static_cast<QmlClassNode*>(n); + else if (n->subType() == Node::Collision) { + NameCollisionNode* ncn; + ncn = static_cast<NameCollisionNode*>(n); + qcn = static_cast<QmlClassNode*>(ncn->findAny(Node::QmlType, Node::NoSubType)); + } + } } + if (qcn) + return static_cast<const FunctionNode*>(qcn->findFunctionNode(path[2])); } do { @@ -299,7 +252,7 @@ const FunctionNode* Tree::findFunctionNode(const QStringList& path, if (i == path.size() - 1) next = ((InnerNode*) node)->findFunctionNode(path.at(i)); else - next = ((InnerNode*) node)->findChildNodeByName(path.at(i)); + next = ((InnerNode*) node)->findChildNode(path.at(i)); if (!next && node->type() == Node::Class && (findFlags & SearchBaseClasses)) { NodeList baseClasses = allBaseClasses(static_cast<const ClassNode*>(node)); @@ -307,7 +260,7 @@ const FunctionNode* Tree::findFunctionNode(const QStringList& path, if (i == path.size() - 1) next = static_cast<const InnerNode*>(baseClass)->findFunctionNode(path.at(i)); else - next = static_cast<const InnerNode*>(baseClass)->findChildNodeByName(path.at(i)); + next = static_cast<const InnerNode*>(baseClass)->findChildNode(path.at(i)); if (next) break; @@ -339,57 +292,34 @@ const FunctionNode* Tree::findFunctionNode(const QStringList& path, return 0; } -/*! - This function just calls the const version of itself and - returns the result. - */ -FunctionNode* Tree::findFunctionNode(const QStringList& parentPath, - const FunctionNode* clone, - Node* relative, - int findFlags) +static NodeTypeList t; +static const NodeTypeList& relatesTypes() { - return const_cast<FunctionNode*>( - const_cast<const Tree*>(this)->findFunctionNode(parentPath, - clone, - relative, - findFlags)); + if (t.isEmpty()) { + t.reserve(3); + t.append(NodeTypePair(Node::Class, Node::NoSubType)); + t.append(NodeTypePair(Node::Namespace, Node::NoSubType)); + t.append(NodeTypePair(Node::Document, Node::HeaderFile)); + } + return t; } /*! - This function first ignores the \a clone node and searches - for the node having the \a parentPath by calling the main - findFunction(\a {parentPath}, \a {relative}, \a {findFlags}). - If that search is successful, then it searches for the \a clone - in the found parent node. - */ -const FunctionNode* Tree::findFunctionNode(const QStringList& parentPath, - const FunctionNode* clone, - const Node* relative, - int findFlags) const -{ - const Node* parent = findNamespaceNode(parentPath); - if (parent == 0) - parent = findClassNode(parentPath, 0); - if (parent == 0) - parent = findNode(parentPath, relative, findFlags); - if (parent == 0 || !parent->isInnerNode()) - return 0; - return ((InnerNode*)parent)->findFunctionNode(clone); -} + This function searches for the node specified by \a path. + The matching node can be one of several different types + including a C++ class, a C++ namespace, or a C++ header + file. -/*! + I'm not sure if it can be a QML type, but if that is a + possibility, the code can easily accommodate it. + + If a matching node is found, a pointer to it is returned. + Otherwise 0 is returned. */ -void Tree::addBaseClass(ClassNode* subclass, Node::Access access, - const QStringList& basePath, - const QString& dataTypeWithTemplateArgs, - InnerNode* parent) +InnerNode* Tree::findRelatesNode(const QStringList& path) { - unresolvedInheritanceMap[subclass].append( - InheritanceBound(access, - basePath, - dataTypeWithTemplateArgs, - parent) - ); + Node* n = findNodeRecursive(path, 0, root(), relatesTypes()); + return ((n && n->isInnerNode()) ? static_cast<InnerNode*>(n) : 0); } /*! @@ -402,24 +332,25 @@ void Tree::addPropertyFunction(PropertyNode* property, } /*! - This function resolves inheritance and reimplementation settings - for each C++ class node found in the namspace beginning at \a rootNode. - If it finds another namespace node in the child list of \a rootNode, - it calls itself recursively. For each child of \a rootNode that is a - class node, it calls the other resolveInheritance() function. + This function resolves C++ inheritance and reimplementation + settings for each C++ class node found in the tree beginning + at \a n. It also calls itself recursively for each C++ class + node or namespace node it encounters. For each child of \a n + that is a class node, it calls resolveInheritanceHelper(). This function does not resolve QML inheritance. */ -void Tree::resolveInheritance(NamespaceNode* rootNode) +void Tree::resolveInheritance(InnerNode* n) { - if (!rootNode) - rootNode = root(); + if (!n) + n = root(); for (int pass = 0; pass < 2; pass++) { - NodeList::ConstIterator c = rootNode->childNodes().constBegin(); - while (c != rootNode->childNodes().constEnd()) { + NodeList::ConstIterator c = n->childNodes().constBegin(); + while (c != n->childNodes().constEnd()) { if ((*c)->type() == Node::Class) { - resolveInheritance(pass, (ClassNode*)* c); + resolveInheritanceHelper(pass, (ClassNode*)*c); + resolveInheritance((ClassNode*)*c); } else if ((*c)->type() == Node::Namespace) { NamespaceNode* ns = static_cast<NamespaceNode*>(*c); @@ -427,8 +358,73 @@ void Tree::resolveInheritance(NamespaceNode* rootNode) } ++c; } - if (rootNode == root()) - unresolvedInheritanceMap.clear(); + } +} + +/*! + This function is run twice for eachclass node \a cn in the + tree. First it is run with \a pass set to 0 for each + class node \a cn. Then it is run with \a pass set to 1 for + eachclass node \a cn. + + In \a pass 0, all the base classes ofclass node \a cn are + found and added to the base class list forclass node \a cn. + + In \a pass 1, each child ofclass node \a cn that is a function + that is reimplemented from one of the base classes is marked + as being reimplemented from that class. + + Some property node fixing up is also done in \a pass 1. + */ +void Tree::resolveInheritanceHelper(int pass, ClassNode* cn) +{ + if (pass == 0) { + QList<RelatedClass>& bases = cn->baseClasses(); + QList<RelatedClass>::iterator b = bases.begin(); + while (b != bases.end()) { + if (!(*b).node_) { + Node* n = qdb_->findClassNode((*b).path_); +#if 0 + /* + If the node for the base class was not found, + the reason might be that the subclass is in a + namespace and the base class is in the same + namespace, but the base class name was not + qualified with the namespace name. That is the + case most of the time. Then restart the search + at the parent of the subclass node (the namespace + node) using the unqualified base class name. + */ + if (!n) { + InnerNode* parent = cn->parent(); + n = findClassNode((*b).path_, parent); + } +#endif + if (n) { + ClassNode* bcn = static_cast<ClassNode*>(n); + (*b).node_ = bcn; + bcn->addDerivedClass((*b).access_, cn); + } + } + ++b; + } + } + else { + NodeList::ConstIterator c = cn->childNodes().constBegin(); + while (c != cn->childNodes().constEnd()) { + if ((*c)->type() == Node::Function) { + FunctionNode* func = (FunctionNode*)* c; + FunctionNode* from = findVirtualFunctionInBaseClasses(cn, func); + if (from != 0) { + if (func->virtualness() == FunctionNode::NonVirtual) + func->setVirtualness(FunctionNode::ImpureVirtual); + func->setReimplementedFrom(from); + } + } + else if ((*c)->type() == Node::Property) + cn->fixPropertyUsingBaseClasses(static_cast<PropertyNode*>(*c)); + ++c; + } } } @@ -486,57 +482,6 @@ void Tree::resolveProperties() } /*! - This function is run twice for each \a classNode in the - tree. First it is run with \a pass set to 0 for each - \a classNode. Then it is run with \a pass set to 1 for - each \a classNode. - - In \a pass 0, all the base classes of \a classNode are - found and added to the base class list for \a classNode. - - In \a pass 1, each child of \a classNode that is a function - that is reimplemented from one of the base classes is marked - as being reimplemented from that class. - - Some property node fixing up is also done in \a pass 1. - */ -void Tree::resolveInheritance(int pass, ClassNode* classNode) -{ - if (pass == 0) { - QList<InheritanceBound> bounds = unresolvedInheritanceMap[classNode]; - QList<InheritanceBound>::ConstIterator b = bounds.constBegin(); - while (b != bounds.constEnd()) { - Node* n = findClassNode((*b).basePath); - if (!n && (*b).parent) { - n = findClassNode((*b).basePath, (*b).parent); - } - if (n) { - classNode->addBaseClass((*b).access, static_cast<ClassNode*>(n), (*b).dataTypeWithTemplateArgs); - } - ++b; - } - } - else { - NodeList::ConstIterator c = classNode->childNodes().constBegin(); - while (c != classNode->childNodes().constEnd()) { - if ((*c)->type() == Node::Function) { - FunctionNode* func = (FunctionNode*)* c; - FunctionNode* from = findVirtualFunctionInBaseClasses(classNode, func); - if (from != 0) { - if (func->virtualness() == FunctionNode::NonVirtual) - func->setVirtualness(FunctionNode::ImpureVirtual); - func->setReimplementedFrom(from); - } - } - else if ((*c)->type() == Node::Property) { - fixPropertyUsingBaseClasses(classNode, static_cast<PropertyNode*>(*c)); - } - ++c; - } - } -} - -/*! For each QML class node that points to a C++ class node, follow its C++ class node pointer and set the C++ class node's QML class node pointer back to the QML class node. @@ -545,7 +490,7 @@ void Tree::resolveCppToQmlLinks() { foreach (Node* child, root_.childNodes()) { - if (child->type() == Node::Document && child->subType() == Node::QmlClass) { + if (child->isQmlType()) { QmlClassNode* qcn = static_cast<QmlClassNode*>(child); ClassNode* cn = const_cast<ClassNode*>(qcn->classNode()); if (cn) @@ -575,16 +520,18 @@ void Tree::fixInheritance(NamespaceNode* rootNode) /*! */ -FunctionNode* Tree::findVirtualFunctionInBaseClasses(ClassNode* classNode, - FunctionNode* clone) +FunctionNode* Tree::findVirtualFunctionInBaseClasses(ClassNode* cn, FunctionNode* clone) { - QList<RelatedClass>::ConstIterator r = classNode->baseClasses().constBegin(); - while (r != classNode->baseClasses().constEnd()) { + const QList<RelatedClass>& rc = cn->baseClasses(); + QList<RelatedClass>::ConstIterator r = rc.constBegin(); + while (r != rc.constEnd()) { FunctionNode* func; - if (((func = findVirtualFunctionInBaseClasses((*r).node, clone)) != 0 || - (func = (*r).node->findFunctionNode(clone)) != 0)) { - if (func->virtualness() != FunctionNode::NonVirtual) - return func; + if ((*r).node_) { + if (((func = findVirtualFunctionInBaseClasses((*r).node_, clone)) != 0 || + (func = (*r).node_->findFunctionNode(clone)) != 0)) { + if (func->virtualness() != FunctionNode::NonVirtual) + return func; + } } ++r; } @@ -593,31 +540,14 @@ FunctionNode* Tree::findVirtualFunctionInBaseClasses(ClassNode* classNode, /*! */ -void Tree::fixPropertyUsingBaseClasses(ClassNode* classNode, PropertyNode* property) -{ - QList<RelatedClass>::const_iterator r = classNode->baseClasses().constBegin(); - while (r != classNode->baseClasses().constEnd()) { - Node* n = r->node->findChildNodeByNameAndType(property->name(), Node::Property); - if (n) { - PropertyNode* baseProperty = static_cast<PropertyNode*>(n); - fixPropertyUsingBaseClasses(r->node, baseProperty); - property->setOverriddenFrom(baseProperty); - } - else { - fixPropertyUsingBaseClasses(r->node, property); - } - ++r; - } -} - -/*! - */ NodeList Tree::allBaseClasses(const ClassNode* classNode) const { NodeList result; foreach (const RelatedClass& r, classNode->baseClasses()) { - result += r.node; - result += allBaseClasses(r.node); + if (r.node_) { + result += r.node_; + result += allBaseClasses(r.node_); + } } return result; } @@ -629,16 +559,9 @@ NodeList Tree::allBaseClasses(const ClassNode* classNode) const search at the tree root. \a subtype is not used unless \a type is \c{Document}. */ -Node* Tree::findNodeByNameAndType(const QStringList& path, - Node::Type type, - Node::SubType subtype, - Node* start, - bool acceptCollision) +Node* Tree::findNodeByNameAndType(const QStringList& path, Node::Type type) const { - if (!start) - start = const_cast<NamespaceNode*>(root()); - Node* result = findNodeRecursive(path, 0, start, type, subtype, acceptCollision); - return result; + return findNodeRecursive(path, 0, root(), type); } /*! @@ -659,22 +582,19 @@ Node* Tree::findNodeByNameAndType(const QStringList& path, */ Node* Tree::findNodeRecursive(const QStringList& path, int pathIndex, - Node* start, - Node::Type type, - Node::SubType subtype, - bool acceptCollision) const + const Node* start, + Node::Type type) const { if (!start || path.isEmpty()) return 0; // no place to start, or nothing to search for. + Node* node = const_cast<Node*>(start); if (start->isLeaf()) { if (pathIndex >= path.size()) - return start; // found a match. + return node; // found a match. return 0; // premature leaf } - if (pathIndex >= path.size()) - return 0; // end of search path. - InnerNode* current = static_cast<InnerNode*>(start); + InnerNode* current = static_cast<InnerNode*>(node); const NodeList& children = current->childNodes(); const QString& name = path.at(pathIndex); for (int i=0; i<children.size(); ++i) { @@ -683,79 +603,654 @@ Node* Tree::findNodeRecursive(const QStringList& path, continue; if (n->isQmlPropertyGroup()) { if (type == Node::QmlProperty) { - n = findNodeRecursive(path, pathIndex, n, type, subtype); + n = findNodeRecursive(path, pathIndex, n, type); if (n) return n; } } else if (n->name() == name) { if (pathIndex+1 >= path.size()) { - if (n->type() == type) { - if (type == Node::Document) { - if (n->subType() == subtype) - return n; - else if (n->subType() == Node::Collision && acceptCollision) - return n; - else if (subtype == Node::NoSubType) - return n; // don't care what subtype is. - return 0; + if ((n->type() == type) || (type == Node::NoType)) + return n; + continue; + } + else { // Search the children of n for the next name in the path. + n = findNodeRecursive(path, pathIndex+1, n, type); + if (n) + return n; + } + } + } + return 0; +} + +/*! + Recursive search for a node identified by \a path. Each + path element is a name. \a pathIndex specifies the index + of the name in \a path to try to match. \a start is the + node whose children shoulod be searched for one that has + that name. Each time a name match is found, increment the + \a pathIndex and call this function recursively. + + If the end of the path is reached (i.e. if a matching + node is found for each name in the \a path), test the + matching node's type and subtype values against the ones + listed in \a types. If a match is found there, return the + pointer to the final node. Otherwise return 0. + */ +Node* Tree::findNodeRecursive(const QStringList& path, + int pathIndex, + Node* start, + const NodeTypeList& types) const +{ + if (!start || path.isEmpty()) + return 0; + if (start->isLeaf()) + return ((pathIndex >= path.size()) ? start : 0); + if (pathIndex >= path.size()) + return 0; + + InnerNode* current = static_cast<InnerNode*>(start); + const NodeList& children = current->childNodes(); + for (int i=0; i<children.size(); ++i) { + Node* n = children.at(i); + if (n && n->name() == path.at(pathIndex)) { + if (pathIndex+1 >= path.size()) { + if (n->match(types)) + return n; + } + else if (!n->isLeaf()) { + n = findNodeRecursive(path, pathIndex+1, n, types); + if (n) + return n; + } + } + } + return 0; +} + +/*! + Searches the tree for a node that matches the \a path. The + search begins at \a start but can move up the parent chain + recursively if no match is found. + + This findNode() callse the other findNode(), which is not + called anywhere else. + */ +const Node* Tree::findNode(const QStringList& path, const Node* start, int findFlags) const +{ + const Node* current = start; + if (!current) + current = root(); + + /* + First, search for a node assuming we don't want a QML node. + If that search fails, search again assuming we do want a + QML node. + */ + const Node* n = findNode(path, current, findFlags, false); + if (n) + return n; + return findNode(path, current, findFlags, true); +} + +/*! + This overload function was extracted from the one above that has the + same signature without the last bool parameter, \a qml. This version + is called only by that other one. It is therefore private. It can + be called a second time by that other version, if the first call + returns null. If \a qml is false, the search will only match a node + that is not a QML node. If \a qml is true, the search will only + match a node that is a QML node. + + This findNode() is only called by the other findNode(). +*/ +const Node* Tree::findNode(const QStringList& path, const Node* start, int findFlags, bool qml) const +{ + const Node* current = start; + do { + const Node* node = current; + int i; + int start_idx = 0; + + /* + If the path contains one or two double colons ("::"), + check first to see if the first two path strings refer + to a QML element. If they do, path[0] will be the QML + module identifier, and path[1] will be the QML type. + If the anser is yes, the reference identifies a QML + class node. + */ + if (qml && path.size() >= 2 && !path[0].isEmpty()) { + QmlClassNode* qcn = lookupQmlType(QString(path[0] + "::" + path[1])); + if (qcn) { + node = qcn; + if (path.size() == 2) + return node; + start_idx = 2; + } + } + + for (i = start_idx; i < path.size(); ++i) { + if (node == 0 || !node->isInnerNode()) + break; + + const Node* next = static_cast<const InnerNode*>(node)->findChildNode(path.at(i), qml); + if (!next && (findFlags & SearchEnumValues) && i == path.size()-1) { + next = static_cast<const InnerNode*>(node)->findEnumNodeForValue(path.at(i)); + } + if (!next && !qml && node->type() == Node::Class && (findFlags & SearchBaseClasses)) { + NodeList baseClasses = allBaseClasses(static_cast<const ClassNode*>(node)); + foreach (const Node* baseClass, baseClasses) { + next = static_cast<const InnerNode*>(baseClass)->findChildNode(path.at(i)); + if (!next && (findFlags & SearchEnumValues) && i == path.size() - 1) + next = static_cast<const InnerNode*>(baseClass)->findEnumNodeForValue(path.at(i)); + if (next) { + break; + } + } + } + node = next; + } + if (node && i == path.size() + && (!(findFlags & NonFunction) || node->type() != Node::Function + || ((FunctionNode*)node)->metaness() == FunctionNode::MacroWithoutParams)) { + if (node->isCollisionNode()) + node = node->applyModuleName(start); + return node; + } + current = current->parent(); + } while (current); + + return 0; +} + +/*! + This function searches for a node with a canonical title + constructed from \a target. If the node it finds is \a node, + it returns the ref from that node. Otherwise it returns an + empty string. + */ +QString Tree::findTarget(const QString& target, const Node* node) const +{ + QString key = Doc::canonicalTitle(target); + TargetMap::const_iterator i = nodesByTarget_.constFind(key); + if (i != nodesByTarget_.constEnd()) { + do { + if (i.value().node_ == node) + return i.value().ref_; + ++i; + } while (i != nodesByTarget_.constEnd() && i.key() == key); + } + return QString(); +} + +/*! + Inserts a new target into the target table. \a name is the + key. The target record contains the \a type, a pointer to + the \a node, the \a priority. and a canonicalized form of + the \a name, which is later used. + */ +void Tree::insertTarget(const QString& name, TargetRec::Type type, Node* node, int priority) +{ + TargetRec target; + target.type_ = type; + target.node_ = node; + target.priority_ = priority; + target.ref_ = Doc::canonicalTitle(name); + nodesByTarget_.insert(name, target); +} + +/*! + Searches this tree for a node named \a target and returns + a pointer to it if found. The \a start node is the starting + point, but it only makes sense if \a start is in this tree. + If \a start is not in this tree, \a start is set to 0 before + beginning the search to ensure that the search starts at the + root. + */ +const Node* Tree::resolveTarget(const QString& target, const Node* start) +{ + QStringList path = target.split("::"); + int flags = SearchBaseClasses | SearchEnumValues | NonFunction; + if (start && start->tree() != this) + start = 0; + return findNode(path, start, flags); +} + +/*! + */ +void Tree::resolveTargets(InnerNode* root) +{ + // need recursion + foreach (Node* child, root->childNodes()) { + if (child->type() == Node::Document) { + DocNode* node = static_cast<DocNode*>(child); + if (!node->title().isEmpty()) { + QString key = Doc::canonicalTitle(node->title()); + QList<DocNode*> nodes = docNodesByTitle_.values(key); + bool alreadyThere = false; + if (!nodes.empty()) { + for (int i=0; i< nodes.size(); ++i) { + if (nodes[i]->subType() == Node::ExternalPage) { + if (node->name() == nodes[i]->name()) { + alreadyThere = true; + break; + } + } } - else - return n; } - else if (n->isCollisionNode()) { - if (acceptCollision) - return n; - return findNodeRecursive(path, pathIndex, n, type, subtype); + if (!alreadyThere) { + docNodesByTitle_.insert(key, node); } - else - return 0; } - else { // Not at the end of the path. - n = findNodeRecursive(path, pathIndex+1, n, type, subtype); - if (n) - return n; + if (node->subType() == Node::Collision) { + resolveTargets(node); + } + } + + if (child->doc().hasTableOfContents()) { + const QList<Atom*>& toc = child->doc().tableOfContents(); + TargetRec target; + target.node_ = child; + target.priority_ = 3; + + for (int i = 0; i < toc.size(); ++i) { + target.ref_ = refForAtom(toc.at(i)); + QString title = Text::sectionHeading(toc.at(i)).toString(); + if (!title.isEmpty()) { + QString key = Doc::canonicalTitle(title); + nodesByTarget_.insert(key, target); + } + } + } + if (child->doc().hasKeywords()) { + const QList<Atom*>& keywords = child->doc().keywords(); + TargetRec target; + target.node_ = child; + target.priority_ = 1; + + for (int i = 0; i < keywords.size(); ++i) { + target.ref_ = refForAtom(keywords.at(i)); + QString key = Doc::canonicalTitle(keywords.at(i)->string()); + nodesByTarget_.insert(key, target); + } + } + if (child->doc().hasTargets()) { + const QList<Atom*>& toc = child->doc().targets(); + TargetRec target; + target.node_ = child; + target.priority_ = 2; + + for (int i = 0; i < toc.size(); ++i) { + target.ref_ = refForAtom(toc.at(i)); + QString key = Doc::canonicalTitle(toc.at(i)->string()); + nodesByTarget_.insert(key, target); + } + } + } +} + +/*! + This function searches for a \a target anchor node. If it + finds one, it sets \a ref and returns the found node. + */ +const Node* +Tree::findUnambiguousTarget(const QString& target, QString& ref) +{ + TargetRec bestTarget; + int numBestTargets = 0; + QList<TargetRec> bestTargetList; + + QString key = Doc::canonicalTitle(target); + TargetMap::iterator i = nodesByTarget_.find(key); + while (i != nodesByTarget_.end()) { + if (i.key() != key) + break; + const TargetRec& candidate = i.value(); + if (candidate.priority_ < bestTarget.priority_) { + bestTarget = candidate; + bestTargetList.clear(); + bestTargetList.append(candidate); + numBestTargets = 1; + } else if (candidate.priority_ == bestTarget.priority_) { + bestTargetList.append(candidate); + ++numBestTargets; + } + ++i; + } + if (numBestTargets > 0) { + if (numBestTargets == 1) { + ref = bestTarget.ref_; + return bestTarget.node_; + } + else if (bestTargetList.size() > 1) { +#if 0 + qDebug() << "TARGET:" << target << numBestTargets; + for (int i=0; i<bestTargetList.size(); ++i) { + const Node* n = bestTargetList.at(i).node_; + qDebug() << " " << n->name() << n->title(); } +#endif + ref = bestTargetList.at(0).ref_; + return bestTargetList.at(0).node_; } } + ref.clear(); return 0; } /*! - Find the Enum type node named \a path. Begin the search at the - \a start node. If the \a start node is 0, begin the search - at the root of the tree. Only an Enum type node named \a path is - acceptible. If one is not found, 0 is returned. + This function searches for a node with the specified \a title. */ -EnumNode* Tree::findEnumNode(const QStringList& path, Node* start) +const DocNode* Tree::findDocNodeByTitle(const QString& title) const { - if (!start) - start = const_cast<NamespaceNode*>(root()); - return static_cast<EnumNode*>(findNodeRecursive(path, 0, start, Node::Enum, Node::NoSubType)); + QString key = Doc::canonicalTitle(title); + DocNodeMultiMap::const_iterator i = docNodesByTitle_.constFind(key); + if (i != docNodesByTitle_.constEnd()) { + /* + Reporting all these duplicate section titles is probably + overkill. We should report the duplicate file and let + that suffice. + */ + DocNodeMultiMap::const_iterator j = i; + ++j; + if (j != docNodesByTitle_.constEnd() && j.key() == i.key()) { + QList<Location> internalLocations; + while (j != docNodesByTitle_.constEnd()) { + if (j.key() == i.key() && j.value()->url().isEmpty()) { + internalLocations.append(j.value()->location()); + break; // Just report one duplicate for now. + } + ++j; + } + if (internalLocations.size() > 0) { + i.value()->location().warning("This page title exists in more than one file: " + title); + foreach (const Location &location, internalLocations) + location.warning("[It also exists here]"); + } + } + return i.value(); + } + return 0; } /*! - Find the C++ class node named \a path. Begin the search at the - \a start node. If the \a start node is 0, begin the search - at the root of the tree. Only a C++ class node named \a path is - acceptible. If one is not found, 0 is returned. + Returns a canonical title for the \a atom, if the \a atom + is a SectionLeft or a Target. */ -ClassNode* Tree::findClassNode(const QStringList& path, Node* start) const +QString Tree::refForAtom(const Atom* atom) { - if (!start) - start = const_cast<NamespaceNode*>(root()); - return static_cast<ClassNode*>(findNodeRecursive(path, 0, start, Node::Class, Node::NoSubType)); + if (atom) { + if (atom->type() == Atom::SectionLeft) + return Doc::canonicalTitle(Text::sectionHeading(atom).toString()); + if (atom->type() == Atom::Target) + return Doc::canonicalTitle(atom->string()); + } + return QString(); } /*! - Find the Namespace node named \a path. Begin the search at - the root of the tree. Only a Namespace node named \a path - is acceptible. If one is not found, 0 is returned. + \fn const CNMap& Tree::groups() const + Returns a const reference to the collection of all + group nodes. +*/ + +/*! + \fn const ModuleMap& Tree::modules() const + Returns a const reference to the collection of all + module nodes. +*/ + +/*! + \fn const QmlModuleMap& Tree::qmlModules() const + Returns a const reference to the collection of all + QML module nodes. +*/ + +/*! + Returns the collection node in this tree that has the same + name and type as \a cn. Returns 0 if no match is found. + + If the matching node is \a cn, return 0. */ -NamespaceNode* Tree::findNamespaceNode(const QStringList& path) const +CollectionNode* Tree::getCorrespondingCollection(CollectionNode* cn) { - Node* start = const_cast<NamespaceNode*>(root()); - return static_cast<NamespaceNode*>(findNodeRecursive(path, 0, start, Node::Namespace, Node::NoSubType)); + CollectionNode* ccn = 0; + if (cn->isGroup()) + ccn = getGroup(cn->name()); + else if (cn->isModule()) + ccn = getModule(cn->name()); + else if (cn->isQmlModule()) + ccn = getQmlModule(cn->name()); + if (ccn == cn) + ccn = 0; + return ccn; +} + +/*! + Find the group node named \a name and return a pointer + to it. If a matching node is not found, return 0. + */ +GroupNode* Tree::getGroup(const QString& name) +{ + CNMap::const_iterator i = groups_.find(name); + if (i != groups_.end()) + return static_cast<GroupNode*>(i.value()); + return 0; +} + +/*! + Find the module node named \a name and return a pointer + to it. If a matching node is not found, return 0. + */ +ModuleNode* Tree::getModule(const QString& name) +{ + CNMap::const_iterator i = modules_.find(name); + if (i != modules_.end()) + return static_cast<ModuleNode*>(i.value()); + return 0; +} + +/*! + Find the QML module node named \a name and return a pointer + to it. If a matching node is not found, return 0. + */ +QmlModuleNode* Tree::getQmlModule(const QString& name) +{ + CNMap::const_iterator i = qmlModules_.find(name); + if (i != qmlModules_.end()) + return static_cast<QmlModuleNode*>(i.value()); + return 0; +} + +/*! + Find the group node named \a name and return a pointer + to it. If the group node is not found, add a new group + node named \a name and return a pointer to the new one. + + If a new group node is added, its parent is the tree root, + and the new group node is marked \e{not seen}. + */ +GroupNode* Tree::findGroup(const QString& name) +{ + CNMap::const_iterator i = groups_.find(name); + if (i != groups_.end()) + return static_cast<GroupNode*>(i.value());; + GroupNode* gn = new GroupNode(root(), name); + gn->markNotSeen(); + groups_.insert(name, gn); + return gn; +} + +/*! + Find the module node named \a name and return a pointer + to it. If a matching node is not found, add a new module + node named \a name and return a pointer to that one. + + If a new module node is added, its parent is the tree root, + and the new module node is marked \e{not seen}. + */ +ModuleNode* Tree::findModule(const QString& name) +{ + CNMap::const_iterator i = modules_.find(name); + if (i != modules_.end()) + return static_cast<ModuleNode*>(i.value()); + ModuleNode* mn = new ModuleNode(root(), name); + mn->markNotSeen(); + modules_.insert(name, mn); + return mn; +} + +/*! + Find the QML module node named \a name and return a pointer + to it. If a matching node is not found, add a new QML module + node named \a name and return a pointer to that one. + + If a new QML module node is added, its parent is the tree root, + and the new QML module node is marked \e{not seen}. + */ +QmlModuleNode* Tree::findQmlModule(const QString& name) +{ + CNMap::const_iterator i = qmlModules_.find(name); + if (i != qmlModules_.end()) + return static_cast<QmlModuleNode*>(i.value()); + QmlModuleNode* qmn = new QmlModuleNode(root(), name); + qmn->markNotSeen(); + qmn->setQmlModuleInfo(name); + qmlModules_.insert(name, qmn); + return qmn; +} + +/*! + Looks up the group node named \a name in the collection + of all group nodes. If a match is found, a pointer to the + node is returned. Otherwise, a new group node named \a name + is created and inserted into the collection, and the pointer + to that node is returned. + */ +GroupNode* Tree::addGroup(const QString& name) +{ + GroupNode* group = findGroup(name); + return group; +} + +/*! + Looks up the module node named \a name in the collection + of all module nodes. If a match is found, a pointer to the + node is returned. Otherwise, a new module node named \a name + is created and inserted into the collection, and the pointer + to that node is returned. + */ +ModuleNode* Tree::addModule(const QString& name) +{ + ModuleNode* module = findModule(name); + return module; +} + +/*! + Looks up the QML module node named \a name in the collection + of all QML module nodes. If a match is found, a pointer to the + node is returned. Otherwise, a new QML module node named \a name + is created and inserted into the collection, and the pointer + to that node is returned. + */ +QmlModuleNode* Tree::addQmlModule(const QString& name) +{ + QStringList blankSplit = name.split(QLatin1Char(' ')); + QmlModuleNode* qmn = findQmlModule(blankSplit[0]); + qmn->setQmlModuleInfo(name); + return qmn; +} + +/*! + Looks up the group node named \a name in the collection + of all group nodes. If a match is not found, a new group + node named \a name is created and inserted into the collection. + Then append \a node to the group's members list, and append the + group name to the list of group names in \a node. The parent of + \a node is not changed by this function. Returns a pointer to + the group node. + */ +GroupNode* Tree::addToGroup(const QString& name, Node* node) +{ + GroupNode* gn = findGroup(name); + if (!node->isInternal()) { + gn->addMember(node); + node->appendGroupName(name); + } + return gn; +} + +/*! + Looks up the module node named \a name in the collection + of all module nodes. If a match is not found, a new module + node named \a name is created and inserted into the collection. + Then append \a node to the module's members list. The parent of + \a node is not changed by this function. Returns the module node. + */ +ModuleNode* Tree::addToModule(const QString& name, Node* node) +{ + ModuleNode* mn = findModule(name); + mn->addMember(node); + node->setModuleName(name); + return mn; +} + +/*! + Looks up the QML module named \a name. If it isn't there, + create it. Then append \a node to the QML module's member + list. The parent of \a node is not changed by this function. + Returns the pointer to the QML module node. + */ +QmlModuleNode* Tree::addToQmlModule(const QString& name, Node* node) +{ + QStringList qmid; + QStringList dotSplit; + QStringList blankSplit = name.split(QLatin1Char(' ')); + qmid.append(blankSplit[0]); + if (blankSplit.size() > 1) { + qmid.append(blankSplit[0] + blankSplit[1]); + dotSplit = blankSplit[1].split(QLatin1Char('.')); + qmid.append(blankSplit[0] + dotSplit[0]); + } + + QmlModuleNode* qmn = findQmlModule(blankSplit[0]); + qmn->addMember(node); + node->setQmlModule(qmn); + if (node->isQmlType()) { + QmlClassNode* n = static_cast<QmlClassNode*>(node); + for (int i=0; i<qmid.size(); ++i) { + QString key = qmid[i] + "::" + node->name(); + insertQmlType(key, n); + } + } + return qmn; +} + +/*! + If the QML type map does not contain \a key, insert node + \a n with the specified \a key. + */ +void Tree::insertQmlType(const QString& key, QmlClassNode* n) +{ + if (!qmlTypeMap_.contains(key)) + qmlTypeMap_.insert(key,n); +} + +/*! + Split \a target on "::" and find the function node with that + path. + */ +const Node* Tree::resolveFunctionTarget(const QString& target, const Node* relative) +{ + QString t = target; + t.chop(2); + QStringList path = t.split("::"); + const FunctionNode* fn = findFunctionNode(path, relative, SearchBaseClasses); + if (fn && fn->metaness() != FunctionNode::MacroWithoutParams) + return fn; + return 0; } QT_END_NAMESPACE |