| /**************************************************************************** |
| ** |
| ** Copyright (C) 2019 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the tools applications of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "tree.h" |
| |
| #include "doc.h" |
| #include "htmlgenerator.h" |
| #include "location.h" |
| #include "node.h" |
| #include "qdocdatabase.h" |
| #include "text.h" |
| |
| #include <QtCore/qdebug.h> |
| |
| #include <limits.h> |
| |
| QT_BEGIN_NAMESPACE |
| |
| /*! |
| \class Tree |
| |
| This class constructs and maintains a tree of instances of |
| the subclasses of Node. |
| |
| 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 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. |
| |
| \a camelCaseModuleName is the project name for this tree, |
| which was obtained from the qdocconf file via the Config |
| singleton. |
| */ |
| Tree::Tree(const QString &camelCaseModuleName, QDocDatabase *qdb) |
| : treeHasBeenAnalyzed_(false), |
| docsHaveBeenGenerated_(false), |
| linkCount_(0), |
| camelCaseModuleName_(camelCaseModuleName), |
| physicalModuleName_(camelCaseModuleName.toLower()), |
| qdb_(qdb), |
| root_(nullptr, QString()), |
| targetListMap_(nullptr) |
| { |
| root_.setPhysicalModuleName(physicalModuleName_); |
| root_.setTree(this); |
| if (Generator::writeQaPages()) { |
| targetListMap_ = new TargetListMap; |
| } |
| } |
| |
| /*! |
| 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. |
| |
| There are two maps of targets, keywords, and contents. |
| One map is indexed by ref, the other by title. The ref |
| is just the canonical form of the title. Both maps |
| use the same set of TargetRec objects as the values, |
| so the destructor only deletes the values from one of |
| the maps. Then it clears both maps. |
| */ |
| Tree::~Tree() |
| { |
| for (auto i = nodesByTargetRef_.begin(); i != nodesByTargetRef_.end(); ++i) { |
| delete i.value(); |
| } |
| nodesByTargetRef_.clear(); |
| nodesByTargetTitle_.clear(); |
| if (Generator::writeQaPages() && targetListMap_) { |
| for (auto target = targetListMap_->begin(); target != targetListMap_->end(); ++target) { |
| TargetList *tlist = target.value(); |
| if (tlist) { |
| for (auto *location : qAsConst(*tlist)) |
| delete location; |
| } |
| delete tlist; |
| } |
| } |
| } |
| |
| /* API members */ |
| |
| /*! |
| Calls findClassNode() first with \a path and \a start. If |
| it finds a node, the node is returned. If not, it calls |
| findNamespaceNode() with the same parameters. The result |
| is returned. |
| */ |
| Node *Tree::findNodeForInclude(const QStringList &path) const |
| { |
| Node *n = findClassNode(path); |
| if (n == nullptr) |
| n = findNamespaceNode(path); |
| return n; |
| } |
| |
| /*! |
| This function searches this tree for an Aggregate node with |
| the specified \a name. It returns the pointer to that node |
| or nullptr. |
| |
| We might need to split the name on '::' but we assume the |
| name is a single word at the moment. |
| */ |
| Aggregate *Tree::findAggregate(const QString &name) |
| { |
| QStringList path = name.split(QLatin1String("::")); |
| return static_cast<Aggregate *>(findNodeRecursive(path, 0, const_cast<NamespaceNode *>(root()), |
| &Node::isFirstClassAggregate)); |
| } |
| |
| /*! |
| 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. |
| */ |
| ClassNode *Tree::findClassNode(const QStringList &path, const Node *start) const |
| { |
| if (start == nullptr) |
| start = const_cast<NamespaceNode *>(root()); |
| return static_cast<ClassNode *>(findNodeRecursive(path, 0, start, &Node::isClassNode)); |
| } |
| |
| /*! |
| 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 |
| { |
| Node *start = const_cast<NamespaceNode *>(root()); |
| return static_cast<NamespaceNode *>(findNodeRecursive(path, 0, start, &Node::isNamespace)); |
| } |
| |
| /*! |
| 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 |
| acceptible. If one is not found, 0 is returned. |
| */ |
| QmlTypeNode *Tree::findQmlTypeNode(const QStringList &path) |
| { |
| /* |
| 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 (path.size() >= 2 && !path[0].isEmpty()) { |
| QmlTypeNode *qcn = qdb_->findQmlType(path[0], path[1]); |
| if (qcn != nullptr) |
| return qcn; |
| } |
| return static_cast<QmlTypeNode *>(findNodeRecursive(path, 0, root(), &Node::isQmlType)); |
| } |
| |
| /*! |
| 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. |
| */ |
| Aggregate *Tree::findRelatesNode(const QStringList &path) |
| { |
| Node *n = findNodeRecursive(path, 0, root(), &Node::isRelatableType); |
| return (((n != nullptr) && n->isAggregate()) ? static_cast<Aggregate *>(n) : nullptr); |
| } |
| |
| /*! |
| Inserts function name \a funcName and function role \a funcRole into |
| the property function map for the specified \a property. |
| */ |
| void Tree::addPropertyFunction(PropertyNode *property, const QString &funcName, |
| PropertyNode::FunctionRole funcRole) |
| { |
| unresolvedPropertyMap[property].insert(funcRole, funcName); |
| } |
| |
| /*! |
| 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. |
| |
| This function does not resolve QML inheritance. |
| */ |
| void Tree::resolveBaseClasses(Aggregate *n) |
| { |
| for (auto it = n->constBegin(); it != n->constEnd(); ++it) { |
| if ((*it)->isClassNode()) { |
| ClassNode *cn = static_cast<ClassNode *>(*it); |
| QVector<RelatedClass> &bases = cn->baseClasses(); |
| for (auto &base : bases) { |
| if (base.node_ == nullptr) { |
| Node *n = qdb_->findClassNode(base.path_); |
| /* |
| 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 == nullptr) { |
| Aggregate *parent = cn->parent(); |
| if (parent != nullptr) |
| // Exclude the root namespace |
| if (parent->isNamespace() && !parent->name().isEmpty()) |
| n = findClassNode(base.path_, parent); |
| } |
| if (n != nullptr) { |
| ClassNode *bcn = static_cast<ClassNode *>(n); |
| base.node_ = bcn; |
| bcn->addDerivedClass(base.access_, cn); |
| } |
| } |
| } |
| resolveBaseClasses(cn); |
| } else if ((*it)->isNamespace()) { |
| resolveBaseClasses(static_cast<NamespaceNode *>(*it)); |
| } |
| } |
| } |
| |
| /*! |
| */ |
| void Tree::resolvePropertyOverriddenFromPtrs(Aggregate *n) |
| { |
| for (auto node = n->constBegin(); node != n->constEnd(); ++node) { |
| if ((*node)->isClassNode()) { |
| ClassNode *cn = static_cast<ClassNode *>(*node); |
| for (auto property = cn->constBegin(); property != cn->constEnd(); ++property) { |
| if ((*property)->isProperty()) |
| cn->resolvePropertyOverriddenFromPtrs(static_cast<PropertyNode *>(*property)); |
| } |
| resolvePropertyOverriddenFromPtrs(cn); |
| } else if ((*node)->isNamespace()) { |
| resolvePropertyOverriddenFromPtrs(static_cast<NamespaceNode *>(*node)); |
| } |
| } |
| } |
| |
| /*! |
| */ |
| void Tree::resolveProperties() |
| { |
| for (auto propEntry = unresolvedPropertyMap.constBegin(); |
| propEntry != unresolvedPropertyMap.constEnd(); ++propEntry) { |
| PropertyNode *property = propEntry.key(); |
| Aggregate *parent = property->parent(); |
| QString getterName = (*propEntry)[PropertyNode::Getter]; |
| QString setterName = (*propEntry)[PropertyNode::Setter]; |
| QString resetterName = (*propEntry)[PropertyNode::Resetter]; |
| QString notifierName = (*propEntry)[PropertyNode::Notifier]; |
| |
| for (auto it = parent->constBegin(); it != parent->constEnd(); ++it) { |
| if ((*it)->isFunction()) { |
| FunctionNode *function = static_cast<FunctionNode *>(*it); |
| if (function->access() == property->access() |
| && (function->status() == property->status() || function->doc().isEmpty())) { |
| if (function->name() == getterName) { |
| property->addFunction(function, PropertyNode::Getter); |
| } else if (function->name() == setterName) { |
| property->addFunction(function, PropertyNode::Setter); |
| } else if (function->name() == resetterName) { |
| property->addFunction(function, PropertyNode::Resetter); |
| } else if (function->name() == notifierName) { |
| property->addSignal(function, PropertyNode::Notifier); |
| } |
| } |
| } |
| } |
| } |
| |
| for (auto propEntry = unresolvedPropertyMap.constBegin(); |
| propEntry != unresolvedPropertyMap.constEnd(); ++propEntry) { |
| PropertyNode *property = propEntry.key(); |
| // redo it to set the property functions |
| if (property->overriddenFrom()) |
| property->setOverriddenFrom(property->overriddenFrom()); |
| } |
| |
| unresolvedPropertyMap.clear(); |
| } |
| |
| /*! |
| 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. |
| */ |
| void Tree::resolveCppToQmlLinks() |
| { |
| |
| const NodeList &children = root_.childNodes(); |
| for (auto *child : children) { |
| if (child->isQmlType() || child->isJsType()) { |
| QmlTypeNode *qcn = static_cast<QmlTypeNode *>(child); |
| ClassNode *cn = const_cast<ClassNode *>(qcn->classNode()); |
| if (cn) |
| cn->setQmlElement(qcn); |
| } |
| } |
| } |
| |
| /*! |
| For each C++ class node, resolve any \c using clauses |
| that appeared in the class declaration. |
| */ |
| void Tree::resolveUsingClauses() |
| { |
| const NodeList &children = root_.childNodes(); |
| for (auto *child : children) { |
| if (child->isClassNode()) { |
| ClassNode *cn = static_cast<ClassNode *>(child); |
| QVector<UsingClause> &usingClauses = cn->usingClauses(); |
| for (auto &usingClause : usingClauses) { |
| if (usingClause.node() == nullptr) { |
| const Node *n = qdb_->findFunctionNode(usingClause.signature(), cn, Node::CPP); |
| if (n != nullptr) |
| usingClause.setNode(n); |
| } |
| } |
| } |
| } |
| } |
| |
| /*! |
| Traverse this Tree and for each ClassNode found, remove |
| from its list of base classes any that are marked private |
| or internal. When a class is removed from a base class |
| list, promote its public pase classes to be base classes |
| of the class where the base class was removed. This is |
| done for documentation purposes. The function is recursive |
| on namespace nodes. |
| */ |
| void Tree::removePrivateAndInternalBases(NamespaceNode *rootNode) |
| { |
| if (rootNode == nullptr) |
| rootNode = root(); |
| |
| for (auto node = rootNode->constBegin(); node != rootNode->constEnd(); ++node) { |
| if ((*node)->isClassNode()) |
| static_cast<ClassNode *>(*node)->removePrivateAndInternalBases(); |
| else if ((*node)->isNamespace()) |
| removePrivateAndInternalBases(static_cast<NamespaceNode *>(*node)); |
| } |
| } |
| |
| /*! |
| */ |
| ClassList Tree::allBaseClasses(const ClassNode *classNode) const |
| { |
| ClassList result; |
| const auto &baseClasses = classNode->baseClasses(); |
| for (const auto &relatedClass : baseClasses) { |
| if (relatedClass.node_ != nullptr) { |
| result += relatedClass.node_; |
| result += allBaseClasses(relatedClass.node_); |
| } |
| } |
| return result; |
| } |
| |
| /*! |
| Find the node with the specified \a path name that is of |
| the specified \a type and \a subtype. Begin the search at |
| the \a start node. If the \a start node is 0, begin the |
| search at the tree root. \a subtype is not used unless |
| \a type is \c{Page}. |
| */ |
| Node *Tree::findNodeByNameAndType(const QStringList &path, bool (Node::*isMatch)() const) const |
| { |
| return findNodeRecursive(path, 0, root(), isMatch); |
| } |
| |
| /*! |
| 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 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), the \a type |
| must match the type of the last matching node, and if the |
| type is \e{Page}, the \a subtype must match as well. |
| |
| If the algorithm is successful, the pointer to the final |
| node is returned. Otherwise 0 is returned. |
| */ |
| Node *Tree::findNodeRecursive(const QStringList &path, int pathIndex, const Node *start, |
| bool (Node::*isMatch)() const) const |
| { |
| if (start == nullptr || path.isEmpty()) |
| return nullptr; |
| Node *node = const_cast<Node *>(start); |
| if (!node->isAggregate()) |
| return ((pathIndex >= path.size()) ? node : nullptr); |
| Aggregate *current = static_cast<Aggregate *>(node); |
| const NodeList &children = current->childNodes(); |
| const QString &name = path.at(pathIndex); |
| for (auto *node : children) { |
| if (node == nullptr) |
| continue; |
| if (node->name() == name) { |
| if (pathIndex + 1 >= path.size()) { |
| if ((node->*(isMatch))()) |
| return node; |
| continue; |
| } else { // Search the children of n for the next name in the path. |
| node = findNodeRecursive(path, pathIndex + 1, node, isMatch); |
| if (node != nullptr) |
| return node; |
| } |
| } |
| } |
| return nullptr; |
| } |
| |
| /*! |
| Searches the tree for a node that matches the \a path plus |
| the \a target. The search begins at \a start and moves up |
| the parent chain from there, or, if \a start is 0, the search |
| begins at the root. |
| |
| The \a flags can indicate whether to search base classes and/or |
| the enum values in enum types. \a genus can be a further restriction |
| on what kind of node is an acceptible match, i.e. CPP or QML. |
| |
| If a matching node is found, \a ref is an output parameter that |
| is set to the HTML reference to use for the link. |
| */ |
| const Node *Tree::findNodeForTarget(const QStringList &path, const QString &target, |
| const Node *start, int flags, Node::Genus genus, |
| QString &ref) const |
| { |
| const Node *node = nullptr; |
| if ((genus == Node::DontCare) || (genus == Node::DOC)) { |
| node = findPageNodeByTitle(path.at(0)); |
| if (node) { |
| if (!target.isEmpty()) { |
| ref = getRef(target, node); |
| if (ref.isEmpty()) |
| node = nullptr; |
| } |
| if (node) |
| return node; |
| } |
| } |
| |
| node = findUnambiguousTarget(path.join(QLatin1String("::")), genus, ref); |
| if (node) { |
| if (!target.isEmpty()) { |
| ref = getRef(target, node); |
| if (ref.isEmpty()) |
| node = nullptr; |
| } |
| if (node) |
| return node; |
| } |
| |
| const Node *current = start; |
| if (current == nullptr) |
| current = root(); |
| |
| /* |
| 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 answer is yes, the reference identifies a QML |
| type node. |
| */ |
| int path_idx = 0; |
| if (((genus == Node::QML) || (genus == Node::DontCare)) && (path.size() >= 2) |
| && !path[0].isEmpty()) { |
| QmlTypeNode *qcn = lookupQmlType(QString(path[0] + "::" + path[1])); |
| if (qcn) { |
| current = qcn; |
| if (path.size() == 2) { |
| if (!target.isEmpty()) { |
| ref = getRef(target, current); |
| if (!ref.isEmpty()) |
| return current; |
| return nullptr; |
| } else |
| return current; |
| } |
| path_idx = 2; |
| } |
| } |
| |
| while (current != nullptr) { |
| if (current->isAggregate()) { // Should this be isPageNode() ??? |
| const Node *node = |
| matchPathAndTarget(path, path_idx, target, current, flags, genus, ref); |
| if (node) |
| return node; |
| } |
| current = current->parent(); |
| path_idx = 0; |
| } |
| return nullptr; |
| } |
| |
| /*! |
| First, the \a path is used to find a node. The \a path |
| matches some part of the node's fully quallified name. |
| If the \a target is not empty, it must match a target |
| in the matching node. If the matching of the \a path |
| and the \a target (if present) is successful, \a ref |
| is set from the \a target, and the pointer to the |
| matching node is returned. \a idx is the index into the |
| \a path where to begin the matching. The function is |
| recursive with idx being incremented for each recursive |
| call. |
| |
| The matching node must be of the correct \a genus, i.e. |
| either QML or C++, but \a genus can be set to \c DontCare. |
| \a flags indicates whether to search base classes and |
| whether to search for an enum value. \a node points to |
| the node where the search should begin, assuming the |
| \a path is a not a fully-qualified name. \a node is |
| most often the root of this Tree. |
| */ |
| const Node *Tree::matchPathAndTarget(const QStringList &path, int idx, const QString &target, |
| const Node *node, int flags, Node::Genus genus, |
| QString &ref) const |
| { |
| /* |
| If the path has been matched, then if there is a target, |
| try to match the target. If there is a target, but you |
| can't match it at the end of the path, give up; return 0. |
| */ |
| if (idx == path.size()) { |
| if (!target.isEmpty()) { |
| ref = getRef(target, node); |
| if (ref.isEmpty()) |
| return nullptr; |
| } |
| if (node->isFunction() && node->name() == node->parent()->name()) |
| node = node->parent(); |
| return node; |
| } |
| |
| QString name = path.at(idx); |
| if (node->isAggregate()) { |
| NodeVector nodes; |
| static_cast<const Aggregate *>(node)->findChildren(name, nodes); |
| for (const auto *node : qAsConst(nodes)) { |
| if (genus != Node::DontCare && node->genus() != genus) |
| continue; |
| const Node *t = matchPathAndTarget(path, idx + 1, target, node, flags, genus, ref); |
| if (t && !t->isPrivate()) |
| return t; |
| } |
| } |
| if (target.isEmpty()) { |
| if ((idx) == (path.size() - 1) && node->isAggregate() && (flags & SearchEnumValues)) { |
| const Node *t = |
| static_cast<const Aggregate *>(node)->findEnumNodeForValue(path.at(idx)); |
| if (t) |
| return t; |
| } |
| } |
| if (((genus == Node::CPP) || (genus == Node::DontCare)) && node->isClassNode() |
| && (flags & SearchBaseClasses)) { |
| const ClassList bases = allBaseClasses(static_cast<const ClassNode *>(node)); |
| for (const auto *base : bases) { |
| const Node *t = matchPathAndTarget(path, idx, target, base, flags, genus, ref); |
| if (t && !t->isPrivate()) |
| return t; |
| if (target.isEmpty()) { |
| if ((idx) == (path.size() - 1) && (flags & SearchEnumValues)) { |
| t = base->findEnumNodeForValue(path.at(idx)); |
| if (t) |
| return t; |
| } |
| } |
| } |
| } |
| return nullptr; |
| } |
| |
| /*! |
| 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. The \a flags are used to |
| restrict the search. |
| */ |
| const Node *Tree::findNode(const QStringList &path, const Node *start, int flags, |
| Node::Genus genus) const |
| { |
| const Node *current = start; |
| if (current == nullptr) |
| current = root(); |
| |
| 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 answer is yes, the reference identifies a QML |
| type node. |
| */ |
| if (((genus == Node::QML) || (genus == Node::DontCare)) && (path.size() >= 2) |
| && !path[0].isEmpty()) { |
| QmlTypeNode *qcn = lookupQmlType(QString(path[0] + "::" + path[1])); |
| if (qcn != nullptr) { |
| node = qcn; |
| if (path.size() == 2) |
| return node; |
| start_idx = 2; |
| } |
| } |
| |
| for (i = start_idx; i < path.size(); ++i) { |
| if (node == nullptr || !node->isAggregate()) |
| break; |
| |
| // Clear the TypesOnly flag until the last path segment, as e.g. namespaces are not |
| // types. We also ignore module nodes as they are not aggregates and thus have no |
| // children. |
| int tmpFlags = (i < path.size() - 1) ? (flags & ~TypesOnly) | IgnoreModules : flags; |
| |
| const Node *next = static_cast<const Aggregate *>(node)->findChildNode(path.at(i), |
| genus, tmpFlags); |
| if ((next == nullptr) && (flags & SearchEnumValues) && i == path.size() - 1) { |
| next = static_cast<const Aggregate *>(node)->findEnumNodeForValue(path.at(i)); |
| } |
| if ((next == nullptr) && ((genus == Node::CPP) || (genus == Node::DontCare)) |
| && node->isClassNode() && (flags & SearchBaseClasses)) { |
| const ClassList bases = allBaseClasses(static_cast<const ClassNode *>(node)); |
| for (const auto *base : bases) { |
| next = base->findChildNode(path.at(i), genus, tmpFlags); |
| if ((next == nullptr) && (flags & SearchEnumValues) && i == path.size() - 1) |
| next = base->findEnumNodeForValue(path.at(i)); |
| if (next != nullptr) |
| break; |
| } |
| } |
| node = next; |
| } |
| if ((node != nullptr) && i == path.size()) |
| return node; |
| current = current->parent(); |
| } while (current != nullptr); |
| |
| return nullptr; |
| } |
| |
| /*! |
| 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::getRef(const QString &target, const Node *node) const |
| { |
| auto it = nodesByTargetTitle_.constFind(target); |
| if (it != nodesByTargetTitle_.constEnd()) { |
| do { |
| if (it.value()->node_ == node) |
| return it.value()->ref_; |
| ++it; |
| } while (it != nodesByTargetTitle_.constEnd() && it.key() == target); |
| } |
| QString key = Doc::canonicalTitle(target); |
| it = nodesByTargetRef_.constFind(key); |
| if (it != nodesByTargetRef_.constEnd()) { |
| do { |
| if (it.value()->node_ == node) |
| return it.value()->ref_; |
| ++it; |
| } while (it != nodesByTargetRef_.constEnd() && it.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, const QString &title, TargetRec::TargetType type, |
| Node *node, int priority) |
| { |
| TargetRec *target = new TargetRec(name, title, type, node, priority); |
| nodesByTargetRef_.insert(name, target); |
| nodesByTargetTitle_.insert(title, target); |
| } |
| |
| /*! |
| */ |
| void Tree::resolveTargets(Aggregate *root) |
| { |
| for (auto *child : root->childNodes()) { |
| if (child->isTextPageNode()) { |
| PageNode *node = static_cast<PageNode *>(child); |
| QString key = node->title(); |
| if (!key.isEmpty()) { |
| if (key.contains(QChar(' '))) |
| key = Doc::canonicalTitle(key); |
| QList<PageNode *> nodes = pageNodesByTitle_.values(key); |
| bool alreadyThere = false; |
| if (!nodes.empty()) { |
| for (const auto &node_ : nodes) { |
| if (node_->isExternalPage()) { |
| if (node->name() == node_->name()) { |
| alreadyThere = true; |
| break; |
| } |
| } |
| } |
| } |
| if (!alreadyThere) |
| pageNodesByTitle_.insert(key, node); |
| } |
| } |
| |
| if (child->doc().hasTableOfContents()) { |
| const QVector<Atom *> &toc = child->doc().tableOfContents(); |
| for (int i = 0; i < toc.size(); ++i) { |
| QString ref = refForAtom(toc.at(i)); |
| QString title = Text::sectionHeading(toc.at(i)).toString(); |
| if (!ref.isEmpty() && !title.isEmpty()) { |
| QString key = Doc::canonicalTitle(title); |
| TargetRec *target = new TargetRec(ref, title, TargetRec::Contents, child, 3); |
| nodesByTargetRef_.insert(key, target); |
| nodesByTargetTitle_.insert(title, target); |
| } |
| } |
| } |
| if (child->doc().hasKeywords()) { |
| const QVector<Atom *> &keywords = child->doc().keywords(); |
| for (int i = 0; i < keywords.size(); ++i) { |
| QString ref = refForAtom(keywords.at(i)); |
| QString title = keywords.at(i)->string(); |
| if (!ref.isEmpty() && !title.isEmpty()) { |
| TargetRec *target = new TargetRec(ref, title, TargetRec::Keyword, child, 1); |
| nodesByTargetRef_.insert(Doc::canonicalTitle(title), target); |
| nodesByTargetTitle_.insert(title, target); |
| } |
| } |
| } |
| if (child->doc().hasTargets()) { |
| const QVector<Atom *> &targets = child->doc().targets(); |
| for (int i = 0; i < targets.size(); ++i) { |
| QString ref = refForAtom(targets.at(i)); |
| QString title = targets.at(i)->string(); |
| if (!ref.isEmpty() && !title.isEmpty()) { |
| QString key = Doc::canonicalTitle(title); |
| TargetRec *target = new TargetRec(ref, title, TargetRec::Target, child, 2); |
| nodesByTargetRef_.insert(key, target); |
| nodesByTargetTitle_.insert(title, target); |
| } |
| } |
| } |
| if (child->isAggregate()) |
| resolveTargets(static_cast<Aggregate *>(child)); |
| } |
| } |
| |
| /*! |
| 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, Node::Genus genus, |
| QString &ref) const |
| { |
| int numBestTargets = 0; |
| TargetRec *bestTarget = nullptr; |
| QVector<TargetRec *> bestTargetList; |
| |
| QString key = target; |
| for (auto it = nodesByTargetTitle_.find(key); it != nodesByTargetTitle_.constEnd(); ++it) { |
| if (it.key() != key) |
| break; |
| TargetRec *candidate = it.value(); |
| if ((genus == Node::DontCare) || (genus == candidate->genus())) { |
| if (!bestTarget || (candidate->priority_ < bestTarget->priority_)) { |
| bestTarget = candidate; |
| bestTargetList.clear(); |
| bestTargetList.append(candidate); |
| numBestTargets = 1; |
| } else if (candidate->priority_ == bestTarget->priority_) { |
| bestTargetList.append(candidate); |
| ++numBestTargets; |
| } |
| } |
| } |
| if (bestTarget) { |
| ref = bestTarget->ref_; |
| return bestTarget->node_; |
| } |
| |
| numBestTargets = 0; |
| bestTarget = nullptr; |
| key = Doc::canonicalTitle(target); |
| for (auto it = nodesByTargetRef_.find(key); it != nodesByTargetRef_.constEnd(); ++it) { |
| if (it.key() != key) |
| break; |
| TargetRec *candidate = it.value(); |
| if ((genus == Node::DontCare) || (genus == candidate->genus())) { |
| if (!bestTarget || (candidate->priority_ < bestTarget->priority_)) { |
| bestTarget = candidate; |
| bestTargetList.clear(); |
| bestTargetList.append(candidate); |
| numBestTargets = 1; |
| } else if (candidate->priority_ == bestTarget->priority_) { |
| bestTargetList.append(candidate); |
| ++numBestTargets; |
| } |
| } |
| } |
| if (bestTarget) { |
| ref = bestTarget->ref_; |
| return bestTarget->node_; |
| } |
| |
| ref.clear(); |
| return nullptr; |
| } |
| |
| /*! |
| This function searches for a node with the specified \a title. |
| */ |
| const PageNode *Tree::findPageNodeByTitle(const QString &title) const |
| { |
| PageNodeMultiMap::const_iterator it; |
| if (title.contains(QChar(' '))) |
| it = pageNodesByTitle_.constFind(Doc::canonicalTitle(title)); |
| else |
| it = pageNodesByTitle_.constFind(title); |
| if (it != pageNodesByTitle_.constEnd()) { |
| /* |
| Reporting all these duplicate section titles is probably |
| overkill. We should report the duplicate file and let |
| that suffice. |
| */ |
| PageNodeMultiMap::const_iterator j = it; |
| ++j; |
| if (j != pageNodesByTitle_.constEnd() && j.key() == it.key()) { |
| while (j != pageNodesByTitle_.constEnd()) { |
| if (j.key() == it.key() && j.value()->url().isEmpty()) { |
| break; // Just report one duplicate for now. |
| } |
| ++j; |
| } |
| if (j != pageNodesByTitle_.cend()) { |
| it.value()->location().warning("This page title exists in more than one file: " |
| + title); |
| j.value()->location().warning("[It also exists here]"); |
| } |
| } |
| return it.value(); |
| } |
| return nullptr; |
| } |
| |
| /*! |
| Returns a canonical title for the \a atom, if the \a atom |
| is a SectionLeft or a Target. |
| */ |
| QString Tree::refForAtom(const Atom *atom) |
| { |
| if (atom) { |
| if (atom->type() == Atom::SectionLeft) |
| return Doc::canonicalTitle(Text::sectionHeading(atom).toString()); |
| if ((atom->type() == Atom::Target) || (atom->type() == Atom::Keyword)) |
| return Doc::canonicalTitle(atom->string()); |
| } |
| return QString(); |
| } |
| |
| /*! |
| \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 a pointer to the collection map specified by \a type. |
| Returns null if \a type is not specified. |
| */ |
| CNMap *Tree::getCollectionMap(Node::NodeType type) |
| { |
| switch (type) { |
| case Node::Group: |
| return &groups_; |
| case Node::Module: |
| return &modules_; |
| case Node::QmlModule: |
| return &qmlModules_; |
| case Node::JsModule: |
| return &jsModules_; |
| default: |
| break; |
| } |
| return nullptr; |
| } |
| |
| /*! |
| Searches this tree for a collection named \a name with the |
| specified \a type. If the collection is found, a pointer |
| to it is returned. If a collection is not found, null is |
| returned. |
| */ |
| CollectionNode *Tree::getCollection(const QString &name, Node::NodeType type) |
| { |
| CNMap *map = getCollectionMap(type); |
| if (map) { |
| auto it = map->constFind(name); |
| if (it != map->cend()) |
| return it.value(); |
| } |
| return nullptr; |
| } |
| |
| /*! |
| Find the group, module, QML module, or JavaScript module |
| named \a name and return a pointer to that collection node. |
| \a type specifies which kind of collection node you want. |
| If a collection node with the specified \a name and \a type |
| is not found, a new one is created, and the pointer to the |
| new one is returned. |
| |
| If a new collection node is created, its parent is the tree |
| root, and the new collection node is marked \e{not seen}. |
| |
| \a genus must be specified, i.e. it must not be \c{DontCare}. |
| If it is \c{DontCare}, 0 is returned, which is a programming |
| error. |
| */ |
| CollectionNode *Tree::findCollection(const QString &name, Node::NodeType type) |
| { |
| CNMap *m = getCollectionMap(type); |
| if (!m) // error |
| return nullptr; |
| auto it = m->constFind(name); |
| if (it != m->cend()) |
| return it.value(); |
| CollectionNode *cn = new CollectionNode(type, root(), name); |
| cn->markNotSeen(); |
| m->insert(name, cn); |
| return cn; |
| } |
| |
| /*! \fn CollectionNode *Tree::findGroup(const QString &name) |
| 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}. |
| */ |
| |
| /*! \fn CollectionNode *Tree::findModule(const QString &name) |
| 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}. |
| */ |
| |
| /*! \fn CollectionNode *Tree::findQmlModule(const QString &name) |
| 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 node is marked \e{not seen}. |
| */ |
| |
| /*! \fn CollectionNode *Tree::findJsModule(const QString &name) |
| Find the JavaScript module named \a name and return a pointer |
| to it. If a matching node is not found, add a new JavaScript |
| module node named \a name and return a pointer to that one. |
| |
| If a new JavaScript module node is added, its parent is the |
| tree root, and the new node is marked \e{not seen}. |
| */ |
| |
| /*! \fn CollectionNode *Tree::addGroup(const QString &name) |
| 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. |
| */ |
| |
| /*! \fn CollectionNode *Tree::addModule(const QString &name) |
| 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. |
| */ |
| |
| /*! \fn CollectionNode *Tree::addQmlModule(const QString &name) |
| 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. |
| */ |
| |
| /*! \fn CollectionNode *Tree::addJsModule(const QString &name) |
| Looks up the JavaScript module node named \a name in the collection |
| of all JavaScript module nodes. If a match is found, a pointer to the |
| node is returned. Otherwise, a new JavaScrpt module node named \a name |
| is created and inserted into the collection, and the pointer |
| to that node is returned. |
| */ |
| |
| /*! |
| 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. |
| */ |
| CollectionNode *Tree::addToGroup(const QString &name, Node *node) |
| { |
| CollectionNode *cn = findGroup(name); |
| if (!node->isInternal()) { |
| cn->addMember(node); |
| node->appendGroupName(name); |
| } |
| return cn; |
| } |
| |
| /*! |
| 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. |
| */ |
| CollectionNode *Tree::addToModule(const QString &name, Node *node) |
| { |
| CollectionNode *cn = findModule(name); |
| cn->addMember(node); |
| node->setPhysicalModuleName(name); |
| return cn; |
| } |
| |
| /*! |
| 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. |
| */ |
| CollectionNode *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]); |
| } |
| |
| CollectionNode *cn = findQmlModule(blankSplit[0]); |
| cn->addMember(node); |
| node->setQmlModule(cn); |
| if (node->isQmlType()) { |
| QmlTypeNode *n = static_cast<QmlTypeNode *>(node); |
| for (int i = 0; i < qmid.size(); ++i) { |
| QString key = qmid[i] + "::" + node->name(); |
| insertQmlType(key, n); |
| } |
| } |
| return cn; |
| } |
| |
| /*! |
| 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. |
| */ |
| CollectionNode *Tree::addToJsModule(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]); |
| } |
| |
| CollectionNode *cn = findJsModule(blankSplit[0]); |
| cn->addMember(node); |
| node->setQmlModule(cn); |
| if (node->isJsType()) { |
| QmlTypeNode *n = static_cast<QmlTypeNode *>(node); |
| for (int i = 0; i < qmid.size(); ++i) { |
| QString key = qmid[i] + "::" + node->name(); |
| insertQmlType(key, n); |
| } |
| } |
| return cn; |
| } |
| |
| /*! |
| 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, QmlTypeNode *n) |
| { |
| if (!qmlTypeMap_.contains(key)) |
| qmlTypeMap_.insert(key, n); |
| } |
| |
| /*! |
| Finds the function node with the specifried name \a path that |
| also has the specified \a parameters and returns a pointer to |
| the the first matching function node if one is found. |
| |
| This function begins searching the tree at \a relative for |
| the \l {FunctionNode} {function node} identified by \a path |
| that has the specified \a parameters. The \a flags are |
| used to restrict the search. If a matching node is found, a |
| pointer to it is returned. Otherwise, nullis returned. If |
| \a relative is ull, the search begins at the tree root. |
| */ |
| const FunctionNode *Tree::findFunctionNode(const QStringList &path, const Parameters ¶meters, |
| const Node *relative, Node::Genus genus) const |
| { |
| if (path.size() == 3 && !path[0].isEmpty() |
| && ((genus == Node::QML) || (genus == Node::DontCare))) { |
| QmlTypeNode *qcn = lookupQmlType(QString(path[0] + "::" + path[1])); |
| if (qcn == nullptr) { |
| QStringList p(path[1]); |
| Node *n = findNodeByNameAndType(p, &Node::isQmlType); |
| if ((n != nullptr) && (n->isQmlType() || n->isJsType())) |
| qcn = static_cast<QmlTypeNode *>(n); |
| } |
| if (qcn != nullptr) |
| return static_cast<const FunctionNode *>(qcn->findFunctionChild(path[2], parameters)); |
| } |
| |
| if (relative == nullptr) |
| relative = root(); |
| else if (genus != Node::DontCare) { |
| if (genus != relative->genus()) |
| relative = root(); |
| } |
| |
| do { |
| Node *node = const_cast<Node *>(relative); |
| int i; |
| |
| for (i = 0; i < path.size(); ++i) { |
| if (node == nullptr || !node->isAggregate()) |
| break; |
| |
| Aggregate *aggregate = static_cast<Aggregate *>(node); |
| Node *next = nullptr; |
| if (i == path.size() - 1) |
| next = aggregate->findFunctionChild(path.at(i), parameters); |
| else |
| next = aggregate->findChildNode(path.at(i), genus); |
| |
| if ((next == nullptr) && aggregate->isClassNode()) { |
| const ClassList bases = allBaseClasses(static_cast<const ClassNode *>(aggregate)); |
| for (auto *base : bases) { |
| if (i == path.size() - 1) |
| next = base->findFunctionChild(path.at(i), parameters); |
| else |
| next = base->findChildNode(path.at(i), genus); |
| |
| if (next != nullptr) |
| break; |
| } |
| } |
| |
| node = next; |
| } // for (i = 0; i < path.size(); ++i) |
| |
| if (node && i == path.size() && node->isFunction()) { |
| // A function node was found at the end of the path. |
| // If it is not marked private, return it. If it is |
| // marked private, then if it overrides a function, |
| // find that function instead because it might not |
| // be marked private. If all the overloads are |
| // marked private, return the original function node. |
| // This should be replace with findOverriddenFunctionNode(). |
| const FunctionNode *fn = static_cast<const FunctionNode *>(node); |
| const FunctionNode *FN = fn; |
| while (FN->isPrivate() && !FN->overridesThis().isEmpty()) { |
| QStringList path = FN->overridesThis().split("::"); |
| FN = qdb_->findFunctionNode(path, parameters, relative, genus); |
| if (FN == nullptr) |
| break; |
| if (!FN->isPrivate()) |
| return FN; |
| } |
| return fn; |
| } |
| relative = relative->parent(); |
| } while (relative); |
| return nullptr; |
| } |
| |
| /*! |
| Generate a target of the form link-nnn, where the nnn is |
| the current link count for this tree. This target string |
| is returned. It will be output as an HTML anchor just before |
| an HTML link to the node \a t. |
| |
| The node \a t |
| */ |
| QString Tree::getNewLinkTarget(const Node *locNode, const Node *t, const QString &fileName, |
| QString &text, bool broken) |
| { |
| QString physicalModuleName; |
| if (t != nullptr && !broken) { |
| Tree *tree = t->tree(); |
| if (tree != this) |
| tree->incrementLinkCount(); |
| physicalModuleName = tree->physicalModuleName(); |
| } else |
| physicalModuleName = "broken"; |
| incrementLinkCount(); |
| QString target = QString("qa-target-%1").arg(-(linkCount())); |
| TargetLoc *tloc = new TargetLoc(locNode, target, fileName, text, broken); |
| TargetList *tList = nullptr; |
| auto it = targetListMap_->find(physicalModuleName); |
| if (it == targetListMap_->end()) { |
| tList = new TargetList; |
| it = targetListMap_->insert(physicalModuleName, tList); |
| } else |
| tList = it.value(); |
| tList->append(tloc); |
| return target; |
| } |
| |
| /*! |
| Look up the target list for the specified \a module |
| and return a pointer to it. |
| */ |
| TargetList *Tree::getTargetList(const QString &module) |
| { |
| return targetListMap_->value(module); |
| } |
| |
| /*! |
| Search this tree recursively from \a parent to find a function |
| node with the specified \a tag. If no function node is found |
| with the required \a tag, return 0. |
| */ |
| FunctionNode *Tree::findFunctionNodeForTag(const QString &tag, Aggregate *parent) |
| { |
| if (parent == nullptr) |
| parent = root(); |
| const NodeList &children = parent->childNodes(); |
| for (Node *n : children) { |
| if (n != nullptr && n->isFunction() && n->hasTag(tag)) |
| return static_cast<FunctionNode *>(n); |
| } |
| for (Node *n : children) { |
| if (n != nullptr && n->isAggregate()) { |
| n = findFunctionNodeForTag(tag, static_cast<Aggregate *>(n)); |
| if (n != nullptr) |
| return static_cast<FunctionNode *>(n); |
| } |
| } |
| return nullptr; |
| } |
| |
| /*! |
| There should only be one macro node for macro name \a t. |
| The macro node is not built until the \macro command is seen. |
| */ |
| FunctionNode *Tree::findMacroNode(const QString &t, const Aggregate *parent) |
| { |
| if (parent == nullptr) |
| parent = root(); |
| const NodeList &children = parent->childNodes(); |
| for (Node *n : children) { |
| if (n != nullptr && (n->isMacro() || n->isFunction()) && n->name() == t) |
| return static_cast<FunctionNode *>(n); |
| } |
| for (Node *n : children) { |
| if (n != nullptr && n->isAggregate()) { |
| FunctionNode *fn = findMacroNode(t, static_cast<Aggregate *>(n)); |
| if (fn != nullptr) |
| return fn; |
| } |
| } |
| return nullptr; |
| } |
| |
| /*! |
| Add the class and struct names in \a arg to the \e {don't document} |
| map. |
| */ |
| void Tree::addToDontDocumentMap(QString &arg) |
| { |
| arg.remove(QChar('(')); |
| arg.remove(QChar(')')); |
| QString t = arg.simplified(); |
| QStringList sl = t.split(QChar(' ')); |
| if (sl.isEmpty()) |
| return; |
| for (const QString &s : sl) { |
| if (!dontDocumentMap_.contains(s)) |
| dontDocumentMap_.insert(s, nullptr); |
| } |
| } |
| |
| /*! |
| The \e {don't document} map has been loaded with the names |
| of classes and structs in the current module that are not |
| documented and should not be documented. Now traverse the |
| map, and for each class or struct name, find the class node |
| that represents that class or struct and mark it with the |
| \C DontDocument status. |
| |
| This results in a map of the class and struct nodes in the |
| module that are in the public API but are not meant to be |
| used by anyone. They are only used internally, but for one |
| reason or another, they must have public visibility. |
| */ |
| void Tree::markDontDocumentNodes() |
| { |
| for (auto it = dontDocumentMap_.begin(); it != dontDocumentMap_.end(); ++it) { |
| Aggregate *node = findAggregate(it.key()); |
| if (node != nullptr) |
| node->setStatus(Node::DontDocument); |
| } |
| } |
| |
| QT_END_NAMESPACE |