summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Wicking <paul.wicking@qt.io>2020-06-01 07:22:16 +0200
committerPaul Wicking <paul.wicking@qt.io>2020-06-04 13:09:25 +0200
commit04355fd6a33b4b0757ed167e6687560b705a2356 (patch)
treed39a8d3fac8ed324ee61bce5fc29c7e24d8f6027
parentc00431dd94cc069cd30af437e5546f157d9bfe10 (diff)
QDoc: Extract Aggregate from Node
Task-number: QTBUG-84578 Change-Id: If28cc8b41401c90dfa48b613211b3d0a4dcd815a Reviewed-by: Topi Reiniƶ <topi.reinio@qt.io>
-rw-r--r--src/qdoc/.prev_CMakeLists.txt1
-rw-r--r--src/qdoc/CMakeLists.txt1
-rw-r--r--src/qdoc/aggregate.cpp1037
-rw-r--r--src/qdoc/aggregate.h121
-rw-r--r--src/qdoc/classnode.h2
-rw-r--r--src/qdoc/docbookgenerator.cpp1
-rw-r--r--src/qdoc/docbookgenerator.h1
-rw-r--r--src/qdoc/enumnode.cpp1
-rw-r--r--src/qdoc/functionnode.h1
-rw-r--r--src/qdoc/generator.cpp1
-rw-r--r--src/qdoc/generator.h1
-rw-r--r--src/qdoc/headernode.cpp2
-rw-r--r--src/qdoc/headernode.h2
-rw-r--r--src/qdoc/helpprojectwriter.cpp1
-rw-r--r--src/qdoc/htmlgenerator.cpp1
-rw-r--r--src/qdoc/htmlgenerator.h1
-rw-r--r--src/qdoc/namespacenode.cpp10
-rw-r--r--src/qdoc/namespacenode.h2
-rw-r--r--src/qdoc/node.cpp997
-rw-r--r--src/qdoc/node.h78
-rw-r--r--src/qdoc/propertynode.cpp2
-rw-r--r--src/qdoc/propertynode.h2
-rw-r--r--src/qdoc/proxynode.h2
-rw-r--r--src/qdoc/qdoc.pro4
-rw-r--r--src/qdoc/qdoctagfiles.cpp1
-rw-r--r--src/qdoc/qmlpropertynode.h1
-rw-r--r--src/qdoc/qmltypenode.h2
-rw-r--r--src/qdoc/qmlvisitor.cpp1
-rw-r--r--src/qdoc/qmlvisitor.h2
-rw-r--r--src/qdoc/sections.cpp1
-rw-r--r--src/qdoc/sections.h2
-rw-r--r--src/qdoc/sharedcommentnode.cpp1
-rw-r--r--src/qdoc/sharedcommentnode.h1
-rw-r--r--src/qdoc/typedefnode.cpp2
-rw-r--r--src/qdoc/typedefnode.h2
-rw-r--r--src/qdoc/variablenode.h1
-rw-r--r--src/qdoc/webxmlgenerator.cpp1
-rw-r--r--src/qdoc/webxmlgenerator.h2
38 files changed, 1206 insertions, 1086 deletions
diff --git a/src/qdoc/.prev_CMakeLists.txt b/src/qdoc/.prev_CMakeLists.txt
index 9f55b0e81..cb2056a2e 100644
--- a/src/qdoc/.prev_CMakeLists.txt
+++ b/src/qdoc/.prev_CMakeLists.txt
@@ -8,6 +8,7 @@ qt_add_tool(qdoc
TARGET_DESCRIPTION "Qt Documentation Compiler"
SOURCES
access.h
+ aggregate.cpp aggregate.h
atom.cpp atom.h
clangcodeparser.cpp clangcodeparser.h
classnode.cpp classnode.h
diff --git a/src/qdoc/CMakeLists.txt b/src/qdoc/CMakeLists.txt
index 9a199bcdc..0776e2c9e 100644
--- a/src/qdoc/CMakeLists.txt
+++ b/src/qdoc/CMakeLists.txt
@@ -9,6 +9,7 @@ qt_add_tool(qdoc
TOOLS_TARGET Tools # special case
SOURCES
access.h
+ aggregate.cpp aggregate.h
atom.cpp atom.h
clangcodeparser.cpp clangcodeparser.h
classnode.cpp classnode.h
diff --git a/src/qdoc/aggregate.cpp b/src/qdoc/aggregate.cpp
new file mode 100644
index 000000000..cac53cd1f
--- /dev/null
+++ b/src/qdoc/aggregate.cpp
@@ -0,0 +1,1037 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 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 "aggregate.h"
+
+#include "functionnode.h"
+#include "parameters.h"
+#include "typedefnode.h"
+#include "qdocdatabase.h"
+#include "qmlpropertynode.h"
+#include "qmltypenode.h"
+#include "sharedcommentnode.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class Aggregate
+ */
+
+/*! \fn Aggregate::Aggregate(NodeType type, Aggregate *parent, const QString &name)
+ The constructor should never be called directly. It is only called
+ by the constructors of subclasses of Aggregate. Those constructors
+ pass the node \a type they want to create, the \a parent of the new
+ node, and its \a name.
+ */
+
+/*!
+ The destructor calls delete for each child of this Aggregate
+ that has this Aggregate as its parent. A child node that has
+ some other Aggregate as its parent is deleted by that
+ Aggregate's destructor. This is a fail-safe test.
+
+ The destructor no longer deletes the collection of children
+ by calling qDeleteAll() because the child list can contain
+ pointers to children that have some other Aggregate as their
+ parent. This is because of how the \e{\\relates} command is
+ processed. An Aggregate can have a pointer to, for example,
+ a FunctionNode in its child list, but that FunctionNode has
+ a differen Aggregate as its parent because a \e{\\relates}
+ command was used to relate it to that parent. In that case,
+ the other Aggregate's destructor must delete that node.
+
+ \note This function is the \b only place where delete is
+ called to delete any subclass of Node.
+
+ \note This strategy depends on the node tree being destroyed
+ by calling delete on the root node of the tree. This happens
+ in the destructor of class Tree.
+ */
+Aggregate::~Aggregate()
+{
+ m_enumChildren.clear();
+ m_nonfunctionMap.clear();
+ m_functionMap.clear();
+ for (int i = 0; i < m_children.size(); ++i) {
+ if ((m_children[i] != nullptr) && (m_children[i]->parent() == this))
+ delete m_children[i];
+ m_children[i] = nullptr;
+ }
+ m_children.clear();
+}
+
+/*!
+ If \a genus is \c{Node::DontCare}, find the first node in
+ this node's child list that has the given \a name. If this
+ node is a QML type, be sure to also look in the children
+ of its property group nodes. Return the matching node or 0.
+
+ If \a genus is either \c{Node::CPP} or \c {Node::QML}, then
+ find all this node's children that have the given \a name,
+ and return the one that satisfies the \a genus requirement.
+ */
+Node *Aggregate::findChildNode(const QString &name, Node::Genus genus, int findFlags) const
+{
+ if (genus == Node::DontCare) {
+ Node *node = m_nonfunctionMap.value(name);
+ if (node)
+ return node;
+ } else {
+ NodeList nodes = m_nonfunctionMap.values(name);
+ if (!nodes.isEmpty()) {
+ for (int i = 0; i < nodes.size(); ++i) {
+ Node *node = nodes.at(i);
+ if (genus == node->genus()) {
+ if (findFlags & TypesOnly) {
+ if (!node->isTypedef() && !node->isClassNode() && !node->isQmlType()
+ && !node->isQmlBasicType() && !node->isJsType()
+ && !node->isJsBasicType() && !node->isEnumType())
+ continue;
+ } else if (findFlags & IgnoreModules && node->isModule())
+ continue;
+ return node;
+ }
+ }
+ }
+ }
+ if (genus != Node::DontCare && this->genus() != genus)
+ return nullptr;
+ return m_functionMap.value(name);
+}
+
+/*!
+ Find all the child nodes of this node that are named
+ \a name and return them in \a nodes.
+ */
+void Aggregate::findChildren(const QString &name, NodeVector &nodes) const
+{
+ nodes.clear();
+ int nonfunctionCount = m_nonfunctionMap.count(name);
+ auto it = m_functionMap.find(name);
+ if (it != m_functionMap.end()) {
+ int functionCount = 0;
+ FunctionNode *fn = it.value();
+ while (fn != nullptr) {
+ ++functionCount;
+ fn = fn->nextOverload();
+ }
+ nodes.reserve(nonfunctionCount + functionCount);
+ fn = it.value();
+ while (fn != nullptr) {
+ nodes.append(fn);
+ fn = fn->nextOverload();
+ }
+ } else {
+ nodes.reserve(nonfunctionCount);
+ }
+ if (nonfunctionCount > 0) {
+ for (auto it = m_nonfunctionMap.find(name);
+ it != m_nonfunctionMap.end() && it.key() == name; ++it) {
+ nodes.append(it.value());
+ }
+ }
+}
+
+/*!
+ This function searches for a child node of this Aggregate,
+ such that the child node has the spacified \a name and the
+ function \a isMatch returns true for the node. The function
+ passed must be one of the isXxx() functions in class Node
+ that tests the node type.
+ */
+Node *Aggregate::findNonfunctionChild(const QString &name, bool (Node::*isMatch)() const)
+{
+ NodeList nodes = m_nonfunctionMap.values(name);
+ for (int i = 0; i < nodes.size(); ++i) {
+ Node *node = nodes.at(i);
+ if ((node->*(isMatch))())
+ return node;
+ }
+ return nullptr;
+}
+
+/*!
+ Find a function node that is a child of this node, such that
+ the function node has the specified \a name and \a parameters.
+ If \a parameters is empty but no matching function is found
+ that has no parameters, return the first non-internal primary
+ function or overload, whether it has parameters or not.
+ */
+FunctionNode *Aggregate::findFunctionChild(const QString &name, const Parameters &parameters)
+{
+ auto it = m_functionMap.find(name);
+ if (it == m_functionMap.end())
+ return nullptr;
+ FunctionNode *fn = it.value();
+
+ if (parameters.isEmpty() && fn->parameters().isEmpty() && !fn->isInternal())
+ return fn;
+
+ while (fn != nullptr) {
+ if (parameters.count() == fn->parameters().count() && !fn->isInternal()) {
+ if (parameters.isEmpty())
+ return fn;
+ bool matched = true;
+ for (int i = 0; i < parameters.count(); i++) {
+ if (parameters.at(i).type() != fn->parameters().at(i).type()) {
+ matched = false;
+ break;
+ }
+ }
+ if (matched)
+ return fn;
+ }
+ fn = fn->nextOverload();
+ }
+
+ if (parameters.isEmpty()) {
+ for (fn = it.value(); fn != nullptr; fn = fn->nextOverload())
+ if (!fn->isInternal())
+ return fn;
+ return it.value();
+ }
+ return nullptr;
+}
+
+/*!
+ Find the function node that is a child of this node, such
+ that the function described has the same name and signature
+ as the function described by the function node \a clone.
+ */
+FunctionNode *Aggregate::findFunctionChild(const FunctionNode *clone)
+{
+ FunctionNode *fn = m_functionMap.value(clone->name());
+ while (fn != nullptr) {
+ if (isSameSignature(clone, fn))
+ return fn;
+ fn = fn->nextOverload();
+ }
+ return nullptr;
+}
+
+/*!
+ Returns the list of keys from the primary function map.
+ */
+QStringList Aggregate::primaryKeys()
+{
+ return m_functionMap.keys();
+}
+
+/*!
+ Mark all child nodes that have no documentation as having
+ private access and internal status. qdoc will then ignore
+ them for documentation purposes.
+ */
+void Aggregate::markUndocumentedChildrenInternal()
+{
+ for (auto *child : qAsConst(m_children)) {
+ if (!child->isSharingComment() && !child->hasDoc() && !child->isDontDocument()) {
+ if (!child->docMustBeGenerated()) {
+ if (child->isFunction()) {
+ if (static_cast<FunctionNode *>(child)->hasAssociatedProperties())
+ continue;
+ } else if (child->isTypedef()) {
+ if (static_cast<TypedefNode *>(child)->hasAssociatedEnum())
+ continue;
+ }
+ child->setAccess(Access::Private);
+ child->setStatus(Node::Internal);
+ }
+ }
+ if (child->isAggregate()) {
+ static_cast<Aggregate *>(child)->markUndocumentedChildrenInternal();
+ }
+ }
+}
+
+/*!
+ This is where we set the overload numbers for function nodes.
+ \note Overload numbers for related non-members are handled
+ separately.
+ */
+void Aggregate::normalizeOverloads()
+{
+ /*
+ Ensure that none of the primary functions is inactive, private,
+ or marked \e {overload}.
+ */
+ for (auto it = m_functionMap.begin(); it != m_functionMap.end(); ++it) {
+ FunctionNode *fn = it.value();
+ if (fn->isOverload()) {
+ FunctionNode *primary = fn->findPrimaryFunction();
+ if (primary) {
+ primary->setNextOverload(fn);
+ it.value() = primary;
+ fn = primary;
+ } else {
+ fn->clearOverloadFlag();
+ }
+ }
+ int count = 0;
+ fn->setOverloadNumber(0);
+ FunctionNode *internalFn = nullptr;
+ while (fn != nullptr) {
+ FunctionNode *next = fn->nextOverload();
+ if (next) {
+ if (next->isInternal()) {
+ // internal overloads are moved to a separate list
+ // and processed last
+ fn->setNextOverload(next->nextOverload());
+ next->setNextOverload(internalFn);
+ internalFn = next;
+ } else {
+ next->setOverloadNumber(++count);
+ }
+ fn = fn->nextOverload();
+ } else {
+ fn->setNextOverload(internalFn);
+ break;
+ }
+ }
+ while (internalFn) {
+ internalFn->setOverloadNumber(++count);
+ internalFn = internalFn->nextOverload();
+ }
+ // process next function in function map.
+ }
+ /*
+ Recursive part.
+ */
+ for (auto *node : qAsConst(m_children)) {
+ if (node->isAggregate())
+ static_cast<Aggregate *>(node)->normalizeOverloads();
+ }
+}
+
+/*!
+ Returns a const reference to the list of child nodes of this
+ aggregate that are not function nodes. Duplicate nodes are
+ removed from the list.
+ */
+const NodeList &Aggregate::nonfunctionList()
+{
+ m_nonfunctionList = m_nonfunctionMap.values();
+ std::sort(m_nonfunctionList.begin(), m_nonfunctionList.end(), Node::nodeNameLessThan);
+ m_nonfunctionList.erase(std::unique(m_nonfunctionList.begin(), m_nonfunctionList.end()),
+ m_nonfunctionList.end());
+ return m_nonfunctionList;
+}
+
+/*! \fn bool Aggregate::isAggregate() const
+ Returns \c true because this node is an instance of Aggregate,
+ which means it can have children.
+ */
+
+/*!
+ Finds the enum type node that has \a enumValue as one of
+ its enum values and returns a pointer to it. Returns 0 if
+ no enum type node is found that has \a enumValue as one
+ of its values.
+ */
+const EnumNode *Aggregate::findEnumNodeForValue(const QString &enumValue) const
+{
+ for (const auto *node : m_enumChildren) {
+ const EnumNode *en = static_cast<const EnumNode *>(node);
+ if (en->hasItem(enumValue))
+ return en;
+ }
+ return nullptr;
+}
+
+/*!
+ Appends \a includeFile file to the list of include files.
+ */
+void Aggregate::addIncludeFile(const QString &includeFile)
+{
+ m_includeFiles.append(includeFile);
+}
+
+/*!
+ Sets the list of include files to \a includeFiles.
+ */
+void Aggregate::setIncludeFiles(const QStringList &includeFiles)
+{
+ m_includeFiles = includeFiles;
+}
+
+/*!
+ Compare \a f1 to \a f2 and return \c true if they have the same
+ signature. Otherwise return \c false. They must have the same
+ number of parameters, and all the parameter types must be the
+ same. The functions must have the same constness and refness.
+ This is a private function.
+ */
+bool Aggregate::isSameSignature(const FunctionNode *f1, const FunctionNode *f2)
+{
+ if (f1->parameters().count() != f2->parameters().count())
+ return false;
+ if (f1->isConst() != f2->isConst())
+ return false;
+ if (f1->isRef() != f2->isRef())
+ return false;
+ if (f1->isRefRef() != f2->isRefRef())
+ return false;
+
+ const Parameters &p1 = f1->parameters();
+ const Parameters &p2 = f2->parameters();
+ for (int i = 0; i < p1.count(); i++) {
+ if (p1.at(i).hasType() && p2.at(i).hasType()) {
+ QString t1 = p1.at(i).type();
+ QString t2 = p2.at(i).type();
+
+ if (t1.length() < t2.length())
+ qSwap(t1, t2);
+
+ /*
+ ### hack for C++ to handle superfluous
+ "Foo::" prefixes gracefully
+ */
+ if (t1 != t2 && t1 != (f2->parent()->name() + "::" + t2)) {
+ // Accept a difference in the template parametters of the type if one
+ // is omited (eg. "QAtomicInteger" == "QAtomicInteger<T>")
+ auto ltLoc = t1.indexOf('<');
+ auto gtLoc = t1.indexOf('>', ltLoc);
+ if (ltLoc < 0 || gtLoc < ltLoc)
+ return false;
+ t1.remove(ltLoc, gtLoc - ltLoc + 1);
+ if (t1 != t2)
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+/*!
+ This function is only called by addChild(), when the child is a
+ FunctionNode. If the function map does not contain a function with
+ the name in \a fn, \a fn is inserted into the function map. If the
+ map already contains a function by that name, \a fn is appended to
+ that function's linked list of overloads.
+
+ \note A function's overloads appear in the linked list in the same
+ order they were created. The first overload in the list is the first
+ overload created. This order is maintained in the numbering of
+ overloads. In other words, the first overload in the linked list has
+ overload number 1, and the last overload in the list has overload
+ number n, where n is the number of overloads not including the
+ function in the function map.
+
+ \not Adding a function increments the aggregate's function count,
+ which is the total number of function nodes in the function map,
+ including the overloads. The overloads are not inserted into the map
+ but are in a linked list using the FunctionNode's m_nextOverload
+ pointer.
+
+ \note The function's overload number and overload flag are set in
+ normalizeOverloads().
+
+ \note This is a private function.
+
+ \sa normalizeOverloads()
+ */
+void Aggregate::addFunction(FunctionNode *fn)
+{
+ auto it = m_functionMap.find(fn->name());
+ if (it == m_functionMap.end())
+ m_functionMap.insert(fn->name(), fn);
+ else
+ it.value()->appendOverload(fn);
+ m_functionCount++;
+}
+
+/*!
+ When an Aggregate adopts a function that is a child of
+ another Aggregate, the function is inserted into this
+ Aggregate's function map, if the function's name is not
+ already in the function map. If the function's name is
+ already in the function map, do nothing. The overload
+ link is already set correctly.
+
+ \note This is a private function.
+ */
+void Aggregate::adoptFunction(FunctionNode *fn)
+{
+ auto it = m_functionMap.find(fn->name());
+ if (it == m_functionMap.end())
+ m_functionMap.insert(fn->name(), fn);
+ ++m_functionCount;
+}
+
+/*!
+ Adds the \a child to this node's child map using \a title
+ as the key. The \a child is not added to the child list
+ again, because it is presumed to already be there. We just
+ want to be able to find the child by its \a title.
+ */
+void Aggregate::addChildByTitle(Node *child, const QString &title)
+{
+ m_nonfunctionMap.insert(title, child);
+}
+
+/*!
+ Adds the \a child to this node's child list and sets the child's
+ parent pointer to this Aggregate. It then mounts the child with
+ mountChild().
+
+ The \a child is then added to this Aggregate's searchable maps
+ and lists.
+
+ \note This function does not test the child's parent pointer
+ for null before changing it. If the child's parent pointer
+ is not null, then it is being reparented. The child becomes
+ a child of this Aggregate, but it also remains a child of
+ the Aggregate that is it's old parent. But the child will
+ only have one parent, and it will be this Aggregate. The is
+ because of the \c relates command.
+
+ \sa mountChild(), dismountChild()
+ */
+void Aggregate::addChild(Node *child)
+{
+ m_children.append(child);
+ child->setParent(this);
+ child->setOutputSubdirectory(this->outputSubdirectory());
+ child->setUrl(QString());
+ child->setIndexNodeFlag(isIndexNode());
+
+ if (child->isFunction()) {
+ addFunction(static_cast<FunctionNode *>(child));
+ } else if (!child->name().isEmpty()) {
+ m_nonfunctionMap.insert(child->name(), child);
+ if (child->isEnumType())
+ m_enumChildren.append(child);
+ }
+}
+
+/*!
+ This Aggregate becomes the adoptive parent of \a child. The
+ \a child knows this Aggregate as its parent, but its former
+ parent continues to have pointers to the child in its child
+ list and in its searchable data structures. But the child is
+ also added to the child list and searchable data structures
+ of this Aggregate.
+
+ The one caveat is that if the child being adopted is a function
+ node, it's next overload pointer is not altered.
+ */
+void Aggregate::adoptChild(Node *child)
+{
+ if (child->parent() != this) {
+ m_children.append(child);
+ child->setParent(this);
+ if (child->isFunction()) {
+ adoptFunction(static_cast<FunctionNode *>(child));
+ } else if (!child->name().isEmpty()) {
+ m_nonfunctionMap.insert(child->name(), child);
+ if (child->isEnumType())
+ m_enumChildren.append(child);
+ }
+ if (child->isSharedCommentNode()) {
+ SharedCommentNode *scn = static_cast<SharedCommentNode *>(child);
+ for (Node *n : scn->collective())
+ adoptChild(n);
+ }
+ }
+}
+
+/*!
+ Recursively sets the output subdirectory for children
+ */
+void Aggregate::setOutputSubdirectory(const QString &t)
+{
+ Node::setOutputSubdirectory(t);
+ for (auto *node : qAsConst(m_children))
+ node->setOutputSubdirectory(t);
+}
+
+/*!
+ If this node has a child that is a QML property or JS property
+ named \a n, return a pointer to that child. Otherwise, return \nullptr.
+ */
+QmlPropertyNode *Aggregate::hasQmlProperty(const QString &n) const
+{
+ NodeType goal = Node::QmlProperty;
+ if (isJsNode())
+ goal = Node::JsProperty;
+ for (auto *child : qAsConst(m_children)) {
+ if (child->nodeType() == goal) {
+ if (child->name() == n)
+ return static_cast<QmlPropertyNode *>(child);
+ }
+ }
+ return nullptr;
+}
+
+/*!
+ If this node has a child that is a QML property or JS property
+ named \a n and that also matches \a attached, return a pointer
+ to that child.
+ */
+QmlPropertyNode *Aggregate::hasQmlProperty(const QString &n, bool attached) const
+{
+ NodeType goal = Node::QmlProperty;
+ if (isJsNode())
+ goal = Node::JsProperty;
+ for (auto *child : qAsConst(m_children)) {
+ if (child->nodeType() == goal) {
+ if (child->name() == n && child->isAttached() == attached)
+ return static_cast<QmlPropertyNode *>(child);
+ }
+ }
+ return nullptr;
+}
+
+/*!
+ The FunctionNode \a fn is assumed to be a member function
+ of this Aggregate. The function's name is looked up in the
+ Aggregate's function map. It should be found because it is
+ assumed that \a fn is in this Aggregate's function map. But
+ in case it is not found, \c false is returned.
+
+ Normally, the name will be found in the function map, and
+ the value of the iterator is used to get the value, which
+ is a pointer to another FunctionNode, which is not itself
+ an overload. If that function has a non-null overload
+ pointer, true is returned. Otherwise false is returned.
+
+ This is a convenience function that you should not need to
+ use.
+ */
+bool Aggregate::hasOverloads(const FunctionNode *fn) const
+{
+ auto it = m_functionMap.find(fn->name());
+ return (it == m_functionMap.end() ? false : (it.value()->nextOverload() != nullptr));
+}
+
+/*!
+ Prints the inner node's list of children.
+ For debugging only.
+ */
+void Aggregate::printChildren(const QString &title)
+{
+ qDebug() << title << name() << m_children.size();
+ if (m_children.size() > 0) {
+ for (int i = 0; i < m_children.size(); ++i) {
+ Node *n = m_children.at(i);
+ qDebug() << " CHILD:" << n->name() << n->nodeTypeString();
+ }
+ }
+}
+
+/*!
+ Removes \a fn from this aggregate's function map. That's
+ all it does. If \a fn is in the function map index and it
+ has an overload, the value pointer in the function map
+ index is set to the the overload pointer. If the function
+ has no overload pointer, the function map entry is erased.
+
+ \note When removing a function node from the function map,
+ it is important to set the removed function node's next
+ overload pointer to null because the function node might
+ be added as a child to some other aggregate.
+
+ \note This is a protected function.
+ */
+void Aggregate::removeFunctionNode(FunctionNode *fn)
+{
+ auto it = m_functionMap.find(fn->name());
+ if (it != m_functionMap.end()) {
+ if (it.value() == fn) {
+ if (fn->nextOverload() != nullptr) {
+ it.value() = fn->nextOverload();
+ fn->setNextOverload(nullptr);
+ fn->setOverloadNumber(0);
+ } else {
+ m_functionMap.erase(it);
+ }
+ } else {
+ FunctionNode *current = it.value();
+ while (current != nullptr) {
+ if (current->nextOverload() == fn) {
+ current->setNextOverload(fn->nextOverload());
+ fn->setNextOverload(nullptr);
+ fn->setOverloadNumber(0);
+ break;
+ }
+ current = current->nextOverload();
+ }
+ }
+ }
+}
+
+/*
+ When deciding whether to include a function in the function
+ index, if the function is marked private, don't include it.
+ If the function is marked obsolete, don't include it. If the
+ function is marked internal, don't include it. Or if the
+ function is a destructor or any kind of constructor, don't
+ include it. Otherwise include it.
+ */
+static bool keep(FunctionNode *fn)
+{
+ if (fn->isPrivate() || fn->isObsolete() || fn->isInternal() || fn->isSomeCtor() || fn->isDtor())
+ return false;
+ return true;
+}
+
+/*!
+ Insert all functions declared in this aggregate into the
+ \a functionIndex. Call the function recursively for each
+ child that is an aggregate.
+
+ Only include functions that are in the public API and
+ that are not constructors or destructors.
+ */
+void Aggregate::findAllFunctions(NodeMapMap &functionIndex)
+{
+ for (auto it = m_functionMap.constBegin(); it != m_functionMap.constEnd(); ++it) {
+ FunctionNode *fn = it.value();
+ if (keep(fn))
+ functionIndex[fn->name()].insert(fn->parent()->fullDocumentName(), fn);
+ fn = fn->nextOverload();
+ while (fn != nullptr) {
+ if (keep(fn))
+ functionIndex[fn->name()].insert(fn->parent()->fullDocumentName(), fn);
+ fn = fn->nextOverload();
+ }
+ }
+ for (Node *node : qAsConst(m_children)) {
+ if (node->isAggregate() && !node->isPrivate())
+ static_cast<Aggregate *>(node)->findAllFunctions(functionIndex);
+ }
+}
+
+/*!
+ For each child of this node, if the child is a namespace node,
+ insert the child into the \a namespaces multimap. If the child
+ is an aggregate, call this function recursively for that child.
+
+ When the function called with the root node of a tree, it finds
+ all the namespace nodes in that tree and inserts them into the
+ \a namespaces multimap.
+
+ The root node of a tree is a namespace, but it has no name, so
+ it is not inserted into the map. So, if this function is called
+ for each tree in the qdoc database, it finds all the namespace
+ nodes in the database.
+ */
+void Aggregate::findAllNamespaces(NodeMultiMap &namespaces)
+{
+ for (auto *node : qAsConst(m_children)) {
+ if (node->isAggregate() && !node->isPrivate()) {
+ if (node->isNamespace() && !node->name().isEmpty())
+ namespaces.insert(node->name(), node);
+ static_cast<Aggregate *>(node)->findAllNamespaces(namespaces);
+ }
+ }
+}
+
+/*!
+ Returns true if this aggregate contains at least one child
+ that is marked obsolete. Otherwise returns false.
+ */
+bool Aggregate::hasObsoleteMembers() const
+{
+ for (const auto *node : m_children) {
+ if (!node->isPrivate() && node->isObsolete()) {
+ if (node->isFunction() || node->isProperty() || node->isEnumType() || node->isTypedef()
+ || node->isTypeAlias() || node->isVariable() || node->isQmlProperty()
+ || node->isJsProperty())
+ return true;
+ }
+ }
+ return false;
+}
+
+/*!
+ Finds all the obsolete C++ classes and QML types in this
+ aggregate and all the C++ classes and QML types with obsolete
+ members, and inserts them into maps used elsewhere for
+ generating documentation.
+ */
+void Aggregate::findAllObsoleteThings()
+{
+ for (auto *node : qAsConst(m_children)) {
+ if (!node->isPrivate()) {
+ QString name = node->name();
+ if (node->isObsolete()) {
+ if (node->isClassNode())
+ QDocDatabase::obsoleteClasses().insert(node->qualifyCppName(), node);
+ else if (node->isQmlType() || node->isJsType())
+ QDocDatabase::obsoleteQmlTypes().insert(node->qualifyQmlName(), node);
+ } else if (node->isClassNode()) {
+ Aggregate *a = static_cast<Aggregate *>(node);
+ if (a->hasObsoleteMembers())
+ QDocDatabase::classesWithObsoleteMembers().insert(node->qualifyCppName(), node);
+ } else if (node->isQmlType() || node->isJsType()) {
+ Aggregate *a = static_cast<Aggregate *>(node);
+ if (a->hasObsoleteMembers())
+ QDocDatabase::qmlTypesWithObsoleteMembers().insert(node->qualifyQmlName(),
+ node);
+ } else if (node->isAggregate()) {
+ static_cast<Aggregate *>(node)->findAllObsoleteThings();
+ }
+ }
+ }
+}
+
+/*!
+ Finds all the C++ classes, QML types, JS types, QML and JS
+ basic types, and examples in this aggregate and inserts them
+ into appropriate maps for later use in generating documentation.
+ */
+void Aggregate::findAllClasses()
+{
+ for (auto *node : qAsConst(m_children)) {
+ if (!node->isPrivate() && !node->isInternal() && !node->isDontDocument()
+ && node->tree()->camelCaseModuleName() != QString("QDoc")) {
+ if (node->isClassNode()) {
+ QDocDatabase::cppClasses().insert(node->qualifyCppName().toLower(), node);
+ } else if (node->isQmlType() || node->isQmlBasicType() || node->isJsType()
+ || node->isJsBasicType()) {
+ QString name = node->unqualifyQmlName();
+ QDocDatabase::qmlTypes().insert(name, node);
+ // also add to the QML basic type map
+ if (node->isQmlBasicType() || node->isJsBasicType())
+ QDocDatabase::qmlBasicTypes().insert(name, node);
+ } else if (node->isExample()) {
+ // use the module index title as key for the example map
+ QString title = node->tree()->indexTitle();
+ if (!QDocDatabase::examples().contains(title, node))
+ QDocDatabase::examples().insert(title, node);
+ } else if (node->isAggregate()) {
+ static_cast<Aggregate *>(node)->findAllClasses();
+ }
+ }
+ }
+}
+
+/*!
+ Find all the attribution pages in this node and insert them
+ into \a attributions.
+ */
+void Aggregate::findAllAttributions(NodeMultiMap &attributions)
+{
+ for (auto *node : qAsConst(m_children)) {
+ if (!node->isPrivate()) {
+ if (node->pageType() == Node::AttributionPage)
+ attributions.insert(node->tree()->indexTitle(), node);
+ else if (node->isAggregate())
+ static_cast<Aggregate *>(node)->findAllAttributions(attributions);
+ }
+ }
+}
+
+/*!
+ Finds all the nodes in this node where a \e{since} command appeared
+ in the qdoc comment and sorts them into maps according to the kind
+ of node.
+
+ This function is used for generating the "New Classes... in x.y"
+ section on the \e{What's New in Qt x.y} page.
+ */
+void Aggregate::findAllSince()
+{
+ for (auto *node : qAsConst(m_children)) {
+ QString sinceString = node->since();
+ // Insert a new entry into each map for each new since string found.
+ if (!node->isPrivate() && !sinceString.isEmpty()) {
+ auto nsmap = QDocDatabase::newSinceMaps().find(sinceString);
+ if (nsmap == QDocDatabase::newSinceMaps().end())
+ nsmap = QDocDatabase::newSinceMaps().insert(sinceString, NodeMultiMap());
+
+ auto ncmap = QDocDatabase::newClassMaps().find(sinceString);
+ if (ncmap == QDocDatabase::newClassMaps().end())
+ ncmap = QDocDatabase::newClassMaps().insert(sinceString, NodeMap());
+
+ auto nqcmap = QDocDatabase::newQmlTypeMaps().find(sinceString);
+ if (nqcmap == QDocDatabase::newQmlTypeMaps().end())
+ nqcmap = QDocDatabase::newQmlTypeMaps().insert(sinceString, NodeMap());
+
+ if (node->isFunction()) {
+ // Insert functions into the general since map.
+ FunctionNode *fn = static_cast<FunctionNode *>(node);
+ if (!fn->isObsolete() && !fn->isSomeCtor() && !fn->isDtor())
+ nsmap.value().insert(fn->name(), fn);
+ } else if (node->isClassNode()) {
+ // Insert classes into the since and class maps.
+ QString name = node->qualifyWithParentName();
+ nsmap.value().insert(name, node);
+ ncmap.value().insert(name, node);
+ } else if (node->isQmlType() || node->isJsType()) {
+ // Insert QML elements into the since and element maps.
+ QString name = node->qualifyWithParentName();
+ nsmap.value().insert(name, node);
+ nqcmap.value().insert(name, node);
+ } else if (node->isQmlProperty() || node->isJsProperty()) {
+ // Insert QML properties into the since map.
+ nsmap.value().insert(node->name(), node);
+ } else {
+ // Insert external documents into the general since map.
+ QString name = node->qualifyWithParentName();
+ nsmap.value().insert(name, node);
+ }
+ }
+ // Recursively find child nodes with since commands.
+ if (node->isAggregate())
+ static_cast<Aggregate *>(node)->findAllSince();
+ }
+}
+
+/*!
+ For each QML Type node in this aggregate's children, if the
+ QML type has a QML base type name but its QML base type node
+ pointer is nullptr, use the QML base type name to look up the
+ base type node. If the node is found, set the node's QML base
+ type node pointer to that node.
+ */
+void Aggregate::resolveQmlInheritance()
+{
+ NodeMap previousSearches;
+ // Do we need recursion?
+ for (auto *child : qAsConst(m_children)) {
+ if (!child->isQmlType() && !child->isJsType())
+ continue;
+ QmlTypeNode *type = static_cast<QmlTypeNode *>(child);
+ if (type->qmlBaseNode() != nullptr)
+ continue;
+ if (type->qmlBaseName().isEmpty())
+ continue;
+ QmlTypeNode *base = static_cast<QmlTypeNode *>(previousSearches.value(type->qmlBaseName()));
+ if (base && (base != type)) {
+ type->setQmlBaseNode(base);
+ QmlTypeNode::addInheritedBy(base, type);
+ } else {
+ if (!type->importList().isEmpty()) {
+ const ImportList &imports = type->importList();
+ for (int i = 0; i < imports.size(); ++i) {
+ base = QDocDatabase::qdocDB()->findQmlType(imports[i], type->qmlBaseName());
+ if (base && (base != type)) {
+ if (base->logicalModuleVersion()[0] != imports[i].m_majorMinorVersion[0])
+ base = nullptr; // Safeguard for QTBUG-53529
+ break;
+ }
+ }
+ }
+ if (base == nullptr) {
+ base = QDocDatabase::qdocDB()->findQmlType(QString(), type->qmlBaseName());
+ }
+ if (base && (base != type)) {
+ type->setQmlBaseNode(base);
+ QmlTypeNode::addInheritedBy(base, type);
+ previousSearches.insert(type->qmlBaseName(), base);
+ }
+ }
+ }
+}
+
+/*!
+ Returns a word representing the kind of Aggregate this node is.
+ Currently only works for class, struct, and union, but it can
+ easily be extended. If \a cap is true, the word is capitalised.
+ */
+QString Aggregate::typeWord(bool cap) const
+{
+ if (cap) {
+ switch (nodeType()) {
+ case Node::Class:
+ return QLatin1String("Class");
+ case Node::Struct:
+ return QLatin1String("Struct");
+ case Node::Union:
+ return QLatin1String("Union");
+ default:
+ break;
+ }
+ } else {
+ switch (nodeType()) {
+ case Node::Class:
+ return QLatin1String("class");
+ case Node::Struct:
+ return QLatin1String("struct");
+ case Node::Union:
+ return QLatin1String("union");
+ default:
+ break;
+ }
+ }
+ return QString();
+}
+
+/*! \fn int Aggregate::count() const
+ Returns the number of children in the child list.
+ */
+
+/*! \fn const NodeList &Aggregate::childNodes() const
+ Returns a const reference to the child list.
+ */
+
+/*! \fn NodeList::ConstIterator Aggregate::constBegin() const
+ Returns a const iterator pointing at the beginning of the child list.
+ */
+
+/*! \fn NodeList::ConstIterator Aggregate::constEnd() const
+ Returns a const iterator pointing at the end of the child list.
+ */
+
+/*! \fn const QStringList &Aggregate::includeFiles() const
+ This function returns a const reference to a list of strings, but
+ I no longer know what they are.
+ */
+
+/*! \fn QmlTypeNode *Aggregate::qmlBaseNode() const
+ If this Aggregate is a QmlTypeNode, this function returns a pointer to
+ the QmlTypeNode that is its base type. Otherwise it returns \c nullptr.
+ A QmlTypeNode doesn't always have a base type, so even when this Aggregate
+ is aQmlTypeNode, the pointer returned can be \c nullptr.
+ */
+
+/*! \fn FunctionMap &Aggregate::functionMap()
+ Returns a reference to this Aggregate's function map, which
+ is a map of all the children of this Aggregate that are
+ FunctionNodes.
+ */
+
+/*! \fn void Aggregate::appendToRelatedByProxy(const NodeList &t)
+ Appends the list of node pointers to the list of elements that are
+ related to this Aggregate but are documented in a different module.
+
+ \sa relatedByProxy()
+ */
+
+/*! \fn NodeList &Aggregate::relatedByProxy()
+ Returns a reference to a list of node pointers where each element
+ points to a node in an index file for some other module, such that
+ whatever the node represents was documented in that other module,
+ but it is related to this Aggregate, so when the documentation for
+ this Aggregate is written, it will contain links to elements in the
+ other module.
+ */
+
+QT_END_NAMESPACE
diff --git a/src/qdoc/aggregate.h b/src/qdoc/aggregate.h
new file mode 100644
index 000000000..7e0fb5fe3
--- /dev/null
+++ b/src/qdoc/aggregate.h
@@ -0,0 +1,121 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 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$
+**
+****************************************************************************/
+
+#ifndef AGGREGATE_H
+#define AGGREGATE_H
+
+#include "node.h"
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qstringlist.h>
+
+QT_BEGIN_NAMESPACE
+
+class FunctionNode;
+class QmlTypeNode;
+class QmlPropertyNode;
+
+class Aggregate : public PageNode
+{
+public:
+ Node *findChildNode(const QString &name, Node::Genus genus, int findFlags = 0) const;
+ Node *findNonfunctionChild(const QString &name, bool (Node::*)() const);
+ void findChildren(const QString &name, NodeVector &nodes) const;
+ FunctionNode *findFunctionChild(const QString &name, const Parameters &parameters);
+ FunctionNode *findFunctionChild(const FunctionNode *clone);
+
+ void normalizeOverloads();
+ void markUndocumentedChildrenInternal();
+
+ bool isAggregate() const override { return true; }
+ const EnumNode *findEnumNodeForValue(const QString &enumValue) const;
+
+ int count() const { return m_children.size(); }
+ const NodeList &childNodes() const { return m_children; }
+ const NodeList &nonfunctionList();
+ NodeList::ConstIterator constBegin() const { return m_children.constBegin(); }
+ NodeList::ConstIterator constEnd() const { return m_children.constEnd(); }
+
+ void addIncludeFile(const QString &includeFile);
+ void setIncludeFiles(const QStringList &includeFiles);
+ const QStringList &includeFiles() const { return m_includeFiles; }
+
+ QStringList primaryKeys();
+ QmlPropertyNode *hasQmlProperty(const QString &) const;
+ QmlPropertyNode *hasQmlProperty(const QString &, bool attached) const;
+ virtual QmlTypeNode *qmlBaseNode() const { return nullptr; }
+ void addChildByTitle(Node *child, const QString &title);
+ void printChildren(const QString &title);
+ void addChild(Node *child);
+ void adoptChild(Node *child);
+ void setOutputSubdirectory(const QString &t) override;
+
+ FunctionMap &functionMap() { return m_functionMap; }
+ void findAllFunctions(NodeMapMap &functionIndex);
+ void findAllNamespaces(NodeMultiMap &namespaces);
+ void findAllAttributions(NodeMultiMap &attributions);
+ bool hasObsoleteMembers() const;
+ void findAllObsoleteThings();
+ void findAllClasses();
+ void findAllSince();
+ void resolveQmlInheritance();
+ bool hasOverloads(const FunctionNode *fn) const;
+ void appendToRelatedByProxy(const NodeList &t) { m_relatedByProxy.append(t); }
+ NodeList &relatedByProxy() { return m_relatedByProxy; }
+ QString typeWord(bool cap) const;
+
+protected:
+ Aggregate(NodeType type, Aggregate *parent, const QString &name)
+ : PageNode(type, parent, name), m_functionCount(0)
+ {
+ }
+ ~Aggregate() override;
+ void removeFunctionNode(FunctionNode *fn);
+
+private:
+ friend class Node;
+ void addFunction(FunctionNode *fn);
+ void adoptFunction(FunctionNode *fn);
+ static bool isSameSignature(const FunctionNode *f1, const FunctionNode *f2);
+
+protected:
+ NodeList m_children {};
+ NodeList m_relatedByProxy {};
+ int m_functionCount {};
+ FunctionMap m_functionMap {};
+
+private:
+ QStringList m_includeFiles {};
+ NodeList m_enumChildren {};
+ NodeMultiMap m_nonfunctionMap {};
+ NodeList m_nonfunctionList {};
+};
+
+QT_END_NAMESPACE
+
+#endif // AGGREGATE_H
diff --git a/src/qdoc/classnode.h b/src/qdoc/classnode.h
index 8252f0421..345c657fa 100644
--- a/src/qdoc/classnode.h
+++ b/src/qdoc/classnode.h
@@ -29,7 +29,7 @@
#ifndef CLASSNODE_H
#define CLASSNODE_H
-#include "node.h"
+#include "aggregate.h"
#include "relatedclass.h"
#include "usingclause.h"
diff --git a/src/qdoc/docbookgenerator.cpp b/src/qdoc/docbookgenerator.cpp
index 74b1e0e34..16955b9e5 100644
--- a/src/qdoc/docbookgenerator.cpp
+++ b/src/qdoc/docbookgenerator.cpp
@@ -29,6 +29,7 @@
#include "docbookgenerator.h"
#include "access.h"
+#include "aggregate.h"
#include "classnode.h"
#include "codemarker.h"
#include "collectionnode.h"
diff --git a/src/qdoc/docbookgenerator.h b/src/qdoc/docbookgenerator.h
index 4f0143abd..69dff804a 100644
--- a/src/qdoc/docbookgenerator.h
+++ b/src/qdoc/docbookgenerator.h
@@ -38,6 +38,7 @@
QT_BEGIN_NAMESPACE
+class Aggregate;
class ExampleNode;
class FunctionNode;
diff --git a/src/qdoc/enumnode.cpp b/src/qdoc/enumnode.cpp
index ba3bb5083..2e4b82307 100644
--- a/src/qdoc/enumnode.cpp
+++ b/src/qdoc/enumnode.cpp
@@ -28,6 +28,7 @@
#include "enumnode.h"
+#include "aggregate.h"
#include "typedefnode.h"
QT_BEGIN_NAMESPACE
diff --git a/src/qdoc/functionnode.h b/src/qdoc/functionnode.h
index b3518be27..a55955d71 100644
--- a/src/qdoc/functionnode.h
+++ b/src/qdoc/functionnode.h
@@ -29,6 +29,7 @@
#ifndef FUNCTIONNODE_H
#define FUNCTIONNODE_H
+#include "aggregate.h"
#include "node.h"
#include "parameters.h"
diff --git a/src/qdoc/generator.cpp b/src/qdoc/generator.cpp
index e39b576f4..a99d632c2 100644
--- a/src/qdoc/generator.cpp
+++ b/src/qdoc/generator.cpp
@@ -32,6 +32,7 @@
#include "generator.h"
#include "access.h"
+#include "aggregate.h"
#include "classnode.h"
#include "codemarker.h"
#include "collectionnode.h"
diff --git a/src/qdoc/generator.h b/src/qdoc/generator.h
index 1ce761829..79380d6c3 100644
--- a/src/qdoc/generator.h
+++ b/src/qdoc/generator.h
@@ -42,6 +42,7 @@ QT_BEGIN_NAMESPACE
typedef QMultiMap<QString, Node *> NodeMultiMap;
typedef QMap<Node *, NodeMultiMap> ParentMaps;
+class Aggregate;
class CodeMarker;
class ExampleNode;
class FunctionNode;
diff --git a/src/qdoc/headernode.cpp b/src/qdoc/headernode.cpp
index 32da565d0..230b562a6 100644
--- a/src/qdoc/headernode.cpp
+++ b/src/qdoc/headernode.cpp
@@ -61,7 +61,7 @@ bool HeaderNode::docMustBeGenerated() const
*/
bool HeaderNode::hasDocumentedChildren() const
{
- for (const auto *node : qAsConst(children_)) {
+ for (const auto *node : qAsConst(m_children)) {
if (node->isInAPI())
return true;
}
diff --git a/src/qdoc/headernode.h b/src/qdoc/headernode.h
index cf6fb30f2..e50e23c55 100644
--- a/src/qdoc/headernode.h
+++ b/src/qdoc/headernode.h
@@ -29,7 +29,7 @@
#ifndef HEADERNODE_H
#define HEADERNODE_H
-#include "node.h"
+#include "aggregate.h"
#include <QtCore/qglobal.h>
#include <QtCore/qstring.h>
diff --git a/src/qdoc/helpprojectwriter.cpp b/src/qdoc/helpprojectwriter.cpp
index d8e58f84d..d7861ce1b 100644
--- a/src/qdoc/helpprojectwriter.cpp
+++ b/src/qdoc/helpprojectwriter.cpp
@@ -29,6 +29,7 @@
#include "helpprojectwriter.h"
#include "access.h"
+#include "aggregate.h"
#include "atom.h"
#include "classnode.h"
#include "collectionnode.h"
diff --git a/src/qdoc/htmlgenerator.cpp b/src/qdoc/htmlgenerator.cpp
index 6a9ad978e..4189f3dab 100644
--- a/src/qdoc/htmlgenerator.cpp
+++ b/src/qdoc/htmlgenerator.cpp
@@ -29,6 +29,7 @@
#include "htmlgenerator.h"
#include "access.h"
+#include "aggregate.h"
#include "classnode.h"
#include "collectionnode.h"
#include "config.h"
diff --git a/src/qdoc/htmlgenerator.h b/src/qdoc/htmlgenerator.h
index 783a6b0ed..49748b92b 100644
--- a/src/qdoc/htmlgenerator.h
+++ b/src/qdoc/htmlgenerator.h
@@ -38,6 +38,7 @@
QT_BEGIN_NAMESPACE
+class Aggregate;
class Config;
class ExampleNode;
class HelpProjectWriter;
diff --git a/src/qdoc/namespacenode.cpp b/src/qdoc/namespacenode.cpp
index 5a3d9b205..27e342f49 100644
--- a/src/qdoc/namespacenode.cpp
+++ b/src/qdoc/namespacenode.cpp
@@ -59,9 +59,9 @@ QT_BEGIN_NAMESPACE
*/
NamespaceNode::~NamespaceNode()
{
- for (int i = 0; i < children_.size(); ++i) {
- if (children_[i]->parent() != this)
- children_[i] = nullptr;
+ for (int i = 0; i < m_children.size(); ++i) {
+ if (m_children[i]->parent() != this)
+ m_children[i] = nullptr;
}
}
@@ -84,7 +84,7 @@ bool NamespaceNode::isDocumentedHere() const
*/
bool NamespaceNode::hasDocumentedChildren() const
{
- for (const auto *node : qAsConst(children_)) {
+ for (const auto *node : qAsConst(m_children)) {
if (node->isInAPI())
return true;
}
@@ -98,7 +98,7 @@ bool NamespaceNode::hasDocumentedChildren() const
*/
void NamespaceNode::reportDocumentedChildrenInUndocumentedNamespace() const
{
- for (const auto *node : qAsConst(children_)) {
+ for (const auto *node : qAsConst(m_children)) {
if (node->isInAPI()) {
QString msg1 = node->name();
if (node->isFunction())
diff --git a/src/qdoc/namespacenode.h b/src/qdoc/namespacenode.h
index cfb7388f8..08e676339 100644
--- a/src/qdoc/namespacenode.h
+++ b/src/qdoc/namespacenode.h
@@ -29,7 +29,7 @@
#ifndef NAMESPACENODE_H
#define NAMESPACENODE_H
-#include "node.h"
+#include "aggregate.h"
#include <QtCore/qglobal.h>
#include <QtCore/qstring.h>
diff --git a/src/qdoc/node.cpp b/src/qdoc/node.cpp
index a528f0051..2d7ac4329 100644
--- a/src/qdoc/node.cpp
+++ b/src/qdoc/node.cpp
@@ -28,6 +28,7 @@
#include "node.h"
+#include "aggregate.h"
#include "codemarker.h"
#include "config.h"
#include "cppcodeparser.h"
@@ -1925,1002 +1926,6 @@ QString Node::physicalModuleName() const
*/
/*!
- \class Aggregate
- */
-
-/*! \fn Aggregate::Aggregate(NodeType type, Aggregate *parent, const QString &name)
- The constructor should never be called directly. It is only called
- by the constructors of subclasses of Aggregate. Those constructors
- pass the node \a type they want to create, the \a parent of the new
- node, and its \a name.
- */
-
-/*!
- The destructor calls delete for each child of this Aggregate
- that has this Aggregate as its parent. A child node that has
- some other Aggregate as its parent is deleted by that
- Aggregate's destructor. This is a fail-safe test.
-
- The destructor no longer deletes the collection of children
- by calling qDeleteAll() because the child list can contain
- pointers to children that have some other Aggregate as their
- parent. This is because of how the \e{\\relates} command is
- processed. An Aggregate can have a pointer to, for example,
- a FunctionNode in its child list, but that FunctionNode has
- a differen Aggregate as its parent because a \e{\\relates}
- command was used to relate it to that parent. In that case,
- the other Aggregate's destructor must delete that node.
-
- \note This function is the \b only place where delete is
- called to delete any subclass of Node.
-
- \note This strategy depends on the node tree being destroyed
- by calling delete on the root node of the tree. This happens
- in the destructor of class Tree.
- */
-Aggregate::~Aggregate()
-{
- enumChildren_.clear();
- nonfunctionMap_.clear();
- functionMap_.clear();
- for (int i = 0; i < children_.size(); ++i) {
- if ((children_[i] != nullptr) && (children_[i]->parent() == this))
- delete children_[i];
- children_[i] = nullptr;
- }
- children_.clear();
-}
-
-/*!
- If \a genus is \c{Node::DontCare}, find the first node in
- this node's child list that has the given \a name. If this
- node is a QML type, be sure to also look in the children
- of its property group nodes. Return the matching node or 0.
-
- If \a genus is either \c{Node::CPP} or \c {Node::QML}, then
- find all this node's children that have the given \a name,
- and return the one that satisfies the \a genus requirement.
- */
-Node *Aggregate::findChildNode(const QString &name, Node::Genus genus, int findFlags) const
-{
- if (genus == Node::DontCare) {
- Node *node = nonfunctionMap_.value(name);
- if (node)
- return node;
- } else {
- NodeList nodes = nonfunctionMap_.values(name);
- if (!nodes.isEmpty()) {
- for (int i = 0; i < nodes.size(); ++i) {
- Node *node = nodes.at(i);
- if (genus == node->genus()) {
- if (findFlags & TypesOnly) {
- if (!node->isTypedef() && !node->isClassNode() && !node->isQmlType()
- && !node->isQmlBasicType() && !node->isJsType()
- && !node->isJsBasicType() && !node->isEnumType())
- continue;
- } else if (findFlags & IgnoreModules && node->isModule())
- continue;
- return node;
- }
- }
- }
- }
- if (genus != Node::DontCare && this->genus() != genus)
- return nullptr;
- return functionMap_.value(name);
-}
-
-/*!
- Find all the child nodes of this node that are named
- \a name and return them in \a nodes.
- */
-void Aggregate::findChildren(const QString &name, NodeVector &nodes) const
-{
- nodes.clear();
- int nonfunctionCount = nonfunctionMap_.count(name);
- auto it = functionMap_.find(name);
- if (it != functionMap_.end()) {
- int functionCount = 0;
- FunctionNode *fn = it.value();
- while (fn != nullptr) {
- ++functionCount;
- fn = fn->nextOverload();
- }
- nodes.reserve(nonfunctionCount + functionCount);
- fn = it.value();
- while (fn != nullptr) {
- nodes.append(fn);
- fn = fn->nextOverload();
- }
- } else {
- nodes.reserve(nonfunctionCount);
- }
- if (nonfunctionCount > 0) {
- for (auto it = nonfunctionMap_.find(name); it != nonfunctionMap_.end() && it.key() == name;
- ++it) {
- nodes.append(it.value());
- }
- }
-}
-
-/*!
- This function searches for a child node of this Aggregate,
- such that the child node has the spacified \a name and the
- function \a isMatch returns true for the node. The function
- passed must be one of the isXxx() functions in class Node
- that tests the node type.
- */
-Node *Aggregate::findNonfunctionChild(const QString &name, bool (Node::*isMatch)() const)
-{
- NodeList nodes = nonfunctionMap_.values(name);
- for (int i = 0; i < nodes.size(); ++i) {
- Node *node = nodes.at(i);
- if ((node->*(isMatch))())
- return node;
- }
- return nullptr;
-}
-
-/*!
- Find a function node that is a child of this node, such that
- the function node has the specified \a name and \a parameters.
- If \a parameters is empty but no matching function is found
- that has no parameters, return the first non-internal primary
- function or overload, whether it has parameters or not.
- */
-FunctionNode *Aggregate::findFunctionChild(const QString &name, const Parameters &parameters)
-{
- auto it = functionMap_.find(name);
- if (it == functionMap_.end())
- return nullptr;
- FunctionNode *fn = it.value();
-
- if (parameters.isEmpty() && fn->parameters().isEmpty() && !fn->isInternal())
- return fn;
-
- while (fn != nullptr) {
- if (parameters.count() == fn->parameters().count() && !fn->isInternal()) {
- if (parameters.isEmpty())
- return fn;
- bool matched = true;
- for (int i = 0; i < parameters.count(); i++) {
- if (parameters.at(i).type() != fn->parameters().at(i).type()) {
- matched = false;
- break;
- }
- }
- if (matched)
- return fn;
- }
- fn = fn->nextOverload();
- }
-
- if (parameters.isEmpty()) {
- for (fn = it.value(); fn != nullptr; fn = fn->nextOverload())
- if (!fn->isInternal())
- return fn;
- return it.value();
- }
- return nullptr;
-}
-
-/*!
- Find the function node that is a child of this node, such
- that the function described has the same name and signature
- as the function described by the function node \a clone.
- */
-FunctionNode *Aggregate::findFunctionChild(const FunctionNode *clone)
-{
- FunctionNode *fn = functionMap_.value(clone->name());
- while (fn != nullptr) {
- if (isSameSignature(clone, fn))
- return fn;
- fn = fn->nextOverload();
- }
- return nullptr;
-}
-
-/*!
- Returns the list of keys from the primary function map.
- */
-QStringList Aggregate::primaryKeys()
-{
- return functionMap_.keys();
-}
-
-/*!
- Mark all child nodes that have no documentation as having
- private access and internal status. qdoc will then ignore
- them for documentation purposes.
- */
-void Aggregate::markUndocumentedChildrenInternal()
-{
- for (auto *child : qAsConst(children_)) {
- if (!child->isSharingComment() && !child->hasDoc() && !child->isDontDocument()) {
- if (!child->docMustBeGenerated()) {
- if (child->isFunction()) {
- if (static_cast<FunctionNode *>(child)->hasAssociatedProperties())
- continue;
- } else if (child->isTypedef()) {
- if (static_cast<TypedefNode *>(child)->hasAssociatedEnum())
- continue;
- }
- child->setAccess(Access::Private);
- child->setStatus(Node::Internal);
- }
- }
- if (child->isAggregate()) {
- static_cast<Aggregate *>(child)->markUndocumentedChildrenInternal();
- }
- }
-}
-
-/*!
- This is where we set the overload numbers for function nodes.
- \note Overload numbers for related non-members are handled
- separately.
- */
-void Aggregate::normalizeOverloads()
-{
- /*
- Ensure that none of the primary functions is inactive, private,
- or marked \e {overload}.
- */
- for (auto it = functionMap_.begin(); it != functionMap_.end(); ++it) {
- FunctionNode *fn = it.value();
- if (fn->isOverload()) {
- FunctionNode *primary = fn->findPrimaryFunction();
- if (primary) {
- primary->setNextOverload(fn);
- it.value() = primary;
- fn = primary;
- } else {
- fn->clearOverloadFlag();
- }
- }
- int count = 0;
- fn->setOverloadNumber(0);
- FunctionNode *internalFn = nullptr;
- while (fn != nullptr) {
- FunctionNode *next = fn->nextOverload();
- if (next) {
- if (next->isInternal()) {
- // internal overloads are moved to a separate list
- // and processed last
- fn->setNextOverload(next->nextOverload());
- next->setNextOverload(internalFn);
- internalFn = next;
- } else {
- next->setOverloadNumber(++count);
- }
- fn = fn->nextOverload();
- } else {
- fn->setNextOverload(internalFn);
- break;
- }
- }
- while (internalFn) {
- internalFn->setOverloadNumber(++count);
- internalFn = internalFn->nextOverload();
- }
- // process next function in function map.
- }
- /*
- Recursive part.
- */
- for (auto *node : qAsConst(children_)) {
- if (node->isAggregate())
- static_cast<Aggregate *>(node)->normalizeOverloads();
- }
-}
-
-/*!
- Returns a const reference to the list of child nodes of this
- aggregate that are not function nodes. Duplicate nodes are
- removed from the list.
- */
-const NodeList &Aggregate::nonfunctionList()
-{
- nonfunctionList_ = nonfunctionMap_.values();
- std::sort(nonfunctionList_.begin(), nonfunctionList_.end(), Node::nodeNameLessThan);
- nonfunctionList_.erase(std::unique(nonfunctionList_.begin(), nonfunctionList_.end()),
- nonfunctionList_.end());
- return nonfunctionList_;
-}
-
-/*! \fn bool Aggregate::isAggregate() const
- Returns \c true because this node is an instance of Aggregate,
- which means it can have children.
- */
-
-/*!
- Finds the enum type node that has \a enumValue as one of
- its enum values and returns a pointer to it. Returns 0 if
- no enum type node is found that has \a enumValue as one
- of its values.
- */
-const EnumNode *Aggregate::findEnumNodeForValue(const QString &enumValue) const
-{
- for (const auto *node : enumChildren_) {
- const EnumNode *en = static_cast<const EnumNode *>(node);
- if (en->hasItem(enumValue))
- return en;
- }
- return nullptr;
-}
-
-/*!
- Appends \a includeFile file to the list of include files.
- */
-void Aggregate::addIncludeFile(const QString &includeFile)
-{
- includeFiles_.append(includeFile);
-}
-
-/*!
- Sets the list of include files to \a includeFiles.
- */
-void Aggregate::setIncludeFiles(const QStringList &includeFiles)
-{
- includeFiles_ = includeFiles;
-}
-
-/*!
- Compare \a f1 to \a f2 and return \c true if they have the same
- signature. Otherwise return \c false. They must have the same
- number of parameters, and all the parameter types must be the
- same. The functions must have the same constness and refness.
- This is a private function.
- */
-bool Aggregate::isSameSignature(const FunctionNode *f1, const FunctionNode *f2)
-{
- if (f1->parameters().count() != f2->parameters().count())
- return false;
- if (f1->isConst() != f2->isConst())
- return false;
- if (f1->isRef() != f2->isRef())
- return false;
- if (f1->isRefRef() != f2->isRefRef())
- return false;
-
- const Parameters &p1 = f1->parameters();
- const Parameters &p2 = f2->parameters();
- for (int i = 0; i < p1.count(); i++) {
- if (p1.at(i).hasType() && p2.at(i).hasType()) {
- QString t1 = p1.at(i).type();
- QString t2 = p2.at(i).type();
-
- if (t1.length() < t2.length())
- qSwap(t1, t2);
-
- /*
- ### hack for C++ to handle superfluous
- "Foo::" prefixes gracefully
- */
- if (t1 != t2 && t1 != (f2->parent()->name() + "::" + t2)) {
- // Accept a difference in the template parametters of the type if one
- // is omited (eg. "QAtomicInteger" == "QAtomicInteger<T>")
- auto ltLoc = t1.indexOf('<');
- auto gtLoc = t1.indexOf('>', ltLoc);
- if (ltLoc < 0 || gtLoc < ltLoc)
- return false;
- t1.remove(ltLoc, gtLoc - ltLoc + 1);
- if (t1 != t2)
- return false;
- }
- }
- }
- return true;
-}
-
-/*!
- This function is only called by addChild(), when the child is a
- FunctionNode. If the function map does not contain a function with
- the name in \a fn, \a fn is inserted into the function map. If the
- map already contains a function by that name, \a fn is appended to
- that function's linked list of overloads.
-
- \note A function's overloads appear in the linked list in the same
- order they were created. The first overload in the list is the first
- overload created. This order is maintained in the numbering of
- overloads. In other words, the first overload in the linked list has
- overload number 1, and the last overload in the list has overload
- number n, where n is the number of overloads not including the
- function in the function map.
-
- \not Adding a function increments the aggregate's function count,
- which is the total number of function nodes in the function map,
- including the overloads. The overloads are not inserted into the map
- but are in a linked list using the FunctionNode's m_nextOverload
- pointer.
-
- \note The function's overload number and overload flag are set in
- normalizeOverloads().
-
- \note This is a private function.
-
- \sa normalizeOverloads()
- */
-void Aggregate::addFunction(FunctionNode *fn)
-{
- auto it = functionMap_.find(fn->name());
- if (it == functionMap_.end())
- functionMap_.insert(fn->name(), fn);
- else
- it.value()->appendOverload(fn);
- functionCount_++;
-}
-
-/*!
- When an Aggregate adopts a function that is a child of
- another Aggregate, the function is inserted into this
- Aggregate's function map, if the function's name is not
- already in the function map. If the function's name is
- already in the function map, do nothing. The overload
- link is already set correctly.
-
- \note This is a private function.
- */
-void Aggregate::adoptFunction(FunctionNode *fn)
-{
- auto it = functionMap_.find(fn->name());
- if (it == functionMap_.end())
- functionMap_.insert(fn->name(), fn);
- ++functionCount_;
-}
-
-/*!
- Adds the \a child to this node's child map using \a title
- as the key. The \a child is not added to the child list
- again, because it is presumed to already be there. We just
- want to be able to find the child by its \a title.
- */
-void Aggregate::addChildByTitle(Node *child, const QString &title)
-{
- nonfunctionMap_.insert(title, child);
-}
-
-/*!
- Adds the \a child to this node's child list and sets the child's
- parent pointer to this Aggregate. It then mounts the child with
- mountChild().
-
- The \a child is then added to this Aggregate's searchable maps
- and lists.
-
- \note This function does not test the child's parent pointer
- for null before changing it. If the child's parent pointer
- is not null, then it is being reparented. The child becomes
- a child of this Aggregate, but it also remains a child of
- the Aggregate that is it's old parent. But the child will
- only have one parent, and it will be this Aggregate. The is
- because of the \c relates command.
-
- \sa mountChild(), dismountChild()
- */
-void Aggregate::addChild(Node *child)
-{
- children_.append(child);
- child->setParent(this);
- child->setOutputSubdirectory(this->outputSubdirectory());
- child->setUrl(QString());
- child->setIndexNodeFlag(isIndexNode());
-
- if (child->isFunction()) {
- addFunction(static_cast<FunctionNode *>(child));
- } else if (!child->name().isEmpty()) {
- nonfunctionMap_.insert(child->name(), child);
- if (child->isEnumType())
- enumChildren_.append(child);
- }
-}
-
-/*!
- This Aggregate becomes the adoptive parent of \a child. The
- \a child knows this Aggregate as its parent, but its former
- parent continues to have pointers to the child in its child
- list and in its searchable data structures. But the child is
- also added to the child list and searchable data structures
- of this Aggregate.
-
- The one caveat is that if the child being adopted is a function
- node, it's next overload pointer is not altered.
- */
-void Aggregate::adoptChild(Node *child)
-{
- if (child->parent() != this) {
- children_.append(child);
- child->setParent(this);
- if (child->isFunction()) {
- adoptFunction(static_cast<FunctionNode *>(child));
- } else if (!child->name().isEmpty()) {
- nonfunctionMap_.insert(child->name(), child);
- if (child->isEnumType())
- enumChildren_.append(child);
- }
- if (child->isSharedCommentNode()) {
- SharedCommentNode *scn = static_cast<SharedCommentNode *>(child);
- for (Node *n : scn->collective())
- adoptChild(n);
- }
- }
-}
-
-/*!
- Recursively sets the output subdirectory for children
- */
-void Aggregate::setOutputSubdirectory(const QString &t)
-{
- Node::setOutputSubdirectory(t);
- for (auto *node : qAsConst(children_))
- node->setOutputSubdirectory(t);
-}
-
-/*!
- If this node has a child that is a QML property or JS property
- named \a n, return a pointer to that child. Otherwise, return \nullptr.
- */
-QmlPropertyNode *Aggregate::hasQmlProperty(const QString &n) const
-{
- NodeType goal = Node::QmlProperty;
- if (isJsNode())
- goal = Node::JsProperty;
- for (auto *child : qAsConst(children_)) {
- if (child->nodeType() == goal) {
- if (child->name() == n)
- return static_cast<QmlPropertyNode *>(child);
- }
- }
- return nullptr;
-}
-
-/*!
- If this node has a child that is a QML property or JS property
- named \a n and that also matches \a attached, return a pointer
- to that child.
- */
-QmlPropertyNode *Aggregate::hasQmlProperty(const QString &n, bool attached) const
-{
- NodeType goal = Node::QmlProperty;
- if (isJsNode())
- goal = Node::JsProperty;
- for (auto *child : qAsConst(children_)) {
- if (child->nodeType() == goal) {
- if (child->name() == n && child->isAttached() == attached)
- return static_cast<QmlPropertyNode *>(child);
- }
- }
- return nullptr;
-}
-
-/*!
- The FunctionNode \a fn is assumed to be a member function
- of this Aggregate. The function's name is looked up in the
- Aggregate's function map. It should be found because it is
- assumed that \a fn is in this Aggregate's function map. But
- in case it is not found, \c false is returned.
-
- Normally, the name will be found in the function map, and
- the value of the iterator is used to get the value, which
- is a pointer to another FunctionNode, which is not itself
- an overload. If that function has a non-null overload
- pointer, true is returned. Otherwise false is returned.
-
- This is a convenience function that you should not need to
- use.
- */
-bool Aggregate::hasOverloads(const FunctionNode *fn) const
-{
- auto it = functionMap_.find(fn->name());
- return (it == functionMap_.end() ? false : (it.value()->nextOverload() != nullptr));
-}
-
-/*!
- Prints the inner node's list of children.
- For debugging only.
- */
-void Aggregate::printChildren(const QString &title)
-{
- qDebug() << title << name() << children_.size();
- if (children_.size() > 0) {
- for (int i = 0; i < children_.size(); ++i) {
- Node *n = children_.at(i);
- qDebug() << " CHILD:" << n->name() << n->nodeTypeString();
- }
- }
-}
-
-/*!
- Removes \a fn from this aggregate's function map. That's
- all it does. If \a fn is in the function map index and it
- has an overload, the value pointer in the function map
- index is set to the the overload pointer. If the function
- has no overload pointer, the function map entry is erased.
-
- \note When removing a function node from the function map,
- it is important to set the removed function node's next
- overload pointer to null because the function node might
- be added as a child to some other aggregate.
-
- \note This is a protected function.
- */
-void Aggregate::removeFunctionNode(FunctionNode *fn)
-{
- auto it = functionMap_.find(fn->name());
- if (it != functionMap_.end()) {
- if (it.value() == fn) {
- if (fn->nextOverload() != nullptr) {
- it.value() = fn->nextOverload();
- fn->setNextOverload(nullptr);
- fn->setOverloadNumber(0);
- } else {
- functionMap_.erase(it);
- }
- } else {
- FunctionNode *current = it.value();
- while (current != nullptr) {
- if (current->nextOverload() == fn) {
- current->setNextOverload(fn->nextOverload());
- fn->setNextOverload(nullptr);
- fn->setOverloadNumber(0);
- break;
- }
- current = current->nextOverload();
- }
- }
- }
-}
-
-/*
- When deciding whether to include a function in the function
- index, if the function is marked private, don't include it.
- If the function is marked obsolete, don't include it. If the
- function is marked internal, don't include it. Or if the
- function is a destructor or any kind of constructor, don't
- include it. Otherwise include it.
- */
-static bool keep(FunctionNode *fn)
-{
- if (fn->isPrivate() || fn->isObsolete() || fn->isInternal() || fn->isSomeCtor() || fn->isDtor())
- return false;
- return true;
-}
-
-/*!
- Insert all functions declared in this aggregate into the
- \a functionIndex. Call the function recursively for each
- child that is an aggregate.
-
- Only include functions that are in the public API and
- that are not constructors or destructors.
- */
-void Aggregate::findAllFunctions(NodeMapMap &functionIndex)
-{
- for (auto it = functionMap_.constBegin(); it != functionMap_.constEnd(); ++it) {
- FunctionNode *fn = it.value();
- if (keep(fn))
- functionIndex[fn->name()].insert(fn->parent()->fullDocumentName(), fn);
- fn = fn->nextOverload();
- while (fn != nullptr) {
- if (keep(fn))
- functionIndex[fn->name()].insert(fn->parent()->fullDocumentName(), fn);
- fn = fn->nextOverload();
- }
- }
- for (Node *node : qAsConst(children_)) {
- if (node->isAggregate() && !node->isPrivate())
- static_cast<Aggregate *>(node)->findAllFunctions(functionIndex);
- }
-}
-
-/*!
- For each child of this node, if the child is a namespace node,
- insert the child into the \a namespaces multimap. If the child
- is an aggregate, call this function recursively for that child.
-
- When the function called with the root node of a tree, it finds
- all the namespace nodes in that tree and inserts them into the
- \a namespaces multimap.
-
- The root node of a tree is a namespace, but it has no name, so
- it is not inserted into the map. So, if this function is called
- for each tree in the qdoc database, it finds all the namespace
- nodes in the database.
- */
-void Aggregate::findAllNamespaces(NodeMultiMap &namespaces)
-{
- for (auto *node : qAsConst(children_)) {
- if (node->isAggregate() && !node->isPrivate()) {
- if (node->isNamespace() && !node->name().isEmpty())
- namespaces.insert(node->name(), node);
- static_cast<Aggregate *>(node)->findAllNamespaces(namespaces);
- }
- }
-}
-
-/*!
- Returns true if this aggregate contains at least one child
- that is marked obsolete. Otherwise returns false.
- */
-bool Aggregate::hasObsoleteMembers() const
-{
- for (const auto *node : children_) {
- if (!node->isPrivate() && node->isObsolete()) {
- if (node->isFunction() || node->isProperty() || node->isEnumType() || node->isTypedef()
- || node->isTypeAlias() || node->isVariable() || node->isQmlProperty()
- || node->isJsProperty())
- return true;
- }
- }
- return false;
-}
-
-/*!
- Finds all the obsolete C++ classes and QML types in this
- aggregate and all the C++ classes and QML types with obsolete
- members, and inserts them into maps used elsewhere for
- generating documentation.
- */
-void Aggregate::findAllObsoleteThings()
-{
- for (auto *node : qAsConst(children_)) {
- if (!node->isPrivate()) {
- QString name = node->name();
- if (node->isObsolete()) {
- if (node->isClassNode())
- QDocDatabase::obsoleteClasses().insert(node->qualifyCppName(), node);
- else if (node->isQmlType() || node->isJsType())
- QDocDatabase::obsoleteQmlTypes().insert(node->qualifyQmlName(), node);
- } else if (node->isClassNode()) {
- Aggregate *a = static_cast<Aggregate *>(node);
- if (a->hasObsoleteMembers())
- QDocDatabase::classesWithObsoleteMembers().insert(node->qualifyCppName(), node);
- } else if (node->isQmlType() || node->isJsType()) {
- Aggregate *a = static_cast<Aggregate *>(node);
- if (a->hasObsoleteMembers())
- QDocDatabase::qmlTypesWithObsoleteMembers().insert(node->qualifyQmlName(),
- node);
- } else if (node->isAggregate()) {
- static_cast<Aggregate *>(node)->findAllObsoleteThings();
- }
- }
- }
-}
-
-/*!
- Finds all the C++ classes, QML types, JS types, QML and JS
- basic types, and examples in this aggregate and inserts them
- into appropriate maps for later use in generating documentation.
- */
-void Aggregate::findAllClasses()
-{
- for (auto *node : qAsConst(children_)) {
- if (!node->isPrivate() && !node->isInternal() && !node->isDontDocument()
- && node->tree()->camelCaseModuleName() != QString("QDoc")) {
- if (node->isClassNode()) {
- QDocDatabase::cppClasses().insert(node->qualifyCppName().toLower(), node);
- } else if (node->isQmlType() || node->isQmlBasicType() || node->isJsType()
- || node->isJsBasicType()) {
- QString name = node->unqualifyQmlName();
- QDocDatabase::qmlTypes().insert(name, node);
- // also add to the QML basic type map
- if (node->isQmlBasicType() || node->isJsBasicType())
- QDocDatabase::qmlBasicTypes().insert(name, node);
- } else if (node->isExample()) {
- // use the module index title as key for the example map
- QString title = node->tree()->indexTitle();
- if (!QDocDatabase::examples().contains(title, node))
- QDocDatabase::examples().insert(title, node);
- } else if (node->isAggregate()) {
- static_cast<Aggregate *>(node)->findAllClasses();
- }
- }
- }
-}
-
-/*!
- Find all the attribution pages in this node and insert them
- into \a attributions.
- */
-void Aggregate::findAllAttributions(NodeMultiMap &attributions)
-{
- for (auto *node : qAsConst(children_)) {
- if (!node->isPrivate()) {
- if (node->pageType() == Node::AttributionPage)
- attributions.insert(node->tree()->indexTitle(), node);
- else if (node->isAggregate())
- static_cast<Aggregate *>(node)->findAllAttributions(attributions);
- }
- }
-}
-
-/*!
- Finds all the nodes in this node where a \e{since} command appeared
- in the qdoc comment and sorts them into maps according to the kind
- of node.
-
- This function is used for generating the "New Classes... in x.y"
- section on the \e{What's New in Qt x.y} page.
- */
-void Aggregate::findAllSince()
-{
- for (auto *node : qAsConst(children_)) {
- QString sinceString = node->since();
- // Insert a new entry into each map for each new since string found.
- if (!node->isPrivate() && !sinceString.isEmpty()) {
- auto nsmap = QDocDatabase::newSinceMaps().find(sinceString);
- if (nsmap == QDocDatabase::newSinceMaps().end())
- nsmap = QDocDatabase::newSinceMaps().insert(sinceString, NodeMultiMap());
-
- auto ncmap = QDocDatabase::newClassMaps().find(sinceString);
- if (ncmap == QDocDatabase::newClassMaps().end())
- ncmap = QDocDatabase::newClassMaps().insert(sinceString, NodeMap());
-
- auto nqcmap = QDocDatabase::newQmlTypeMaps().find(sinceString);
- if (nqcmap == QDocDatabase::newQmlTypeMaps().end())
- nqcmap = QDocDatabase::newQmlTypeMaps().insert(sinceString, NodeMap());
-
- if (node->isFunction()) {
- // Insert functions into the general since map.
- FunctionNode *fn = static_cast<FunctionNode *>(node);
- if (!fn->isObsolete() && !fn->isSomeCtor() && !fn->isDtor())
- nsmap.value().insert(fn->name(), fn);
- } else if (node->isClassNode()) {
- // Insert classes into the since and class maps.
- QString name = node->qualifyWithParentName();
- nsmap.value().insert(name, node);
- ncmap.value().insert(name, node);
- } else if (node->isQmlType() || node->isJsType()) {
- // Insert QML elements into the since and element maps.
- QString name = node->qualifyWithParentName();
- nsmap.value().insert(name, node);
- nqcmap.value().insert(name, node);
- } else if (node->isQmlProperty() || node->isJsProperty()) {
- // Insert QML properties into the since map.
- nsmap.value().insert(node->name(), node);
- } else {
- // Insert external documents into the general since map.
- QString name = node->qualifyWithParentName();
- nsmap.value().insert(name, node);
- }
- }
- // Recursively find child nodes with since commands.
- if (node->isAggregate())
- static_cast<Aggregate *>(node)->findAllSince();
- }
-}
-
-/*!
- For each QML Type node in this aggregate's children, if the
- QML type has a QML base type name but its QML base type node
- pointer is nullptr, use the QML base type name to look up the
- base type node. If the node is found, set the node's QML base
- type node pointer to that node.
- */
-void Aggregate::resolveQmlInheritance()
-{
- NodeMap previousSearches;
- // Do we need recursion?
- for (auto *child : qAsConst(children_)) {
- if (!child->isQmlType() && !child->isJsType())
- continue;
- QmlTypeNode *type = static_cast<QmlTypeNode *>(child);
- if (type->qmlBaseNode() != nullptr)
- continue;
- if (type->qmlBaseName().isEmpty())
- continue;
- QmlTypeNode *base = static_cast<QmlTypeNode *>(previousSearches.value(type->qmlBaseName()));
- if (base && (base != type)) {
- type->setQmlBaseNode(base);
- QmlTypeNode::addInheritedBy(base, type);
- } else {
- if (!type->importList().isEmpty()) {
- const ImportList &imports = type->importList();
- for (int i = 0; i < imports.size(); ++i) {
- base = QDocDatabase::qdocDB()->findQmlType(imports[i], type->qmlBaseName());
- if (base && (base != type)) {
- if (base->logicalModuleVersion()[0] != imports[i].m_majorMinorVersion[0])
- base = nullptr; // Safeguard for QTBUG-53529
- break;
- }
- }
- }
- if (base == nullptr) {
- base = QDocDatabase::qdocDB()->findQmlType(QString(), type->qmlBaseName());
- }
- if (base && (base != type)) {
- type->setQmlBaseNode(base);
- QmlTypeNode::addInheritedBy(base, type);
- previousSearches.insert(type->qmlBaseName(), base);
- }
- }
- }
-}
-
-/*!
- Returns a word representing the kind of Aggregate this node is.
- Currently only works for class, struct, and union, but it can
- easily be extended. If \a cap is true, the word is capitalised.
- */
-QString Aggregate::typeWord(bool cap) const
-{
- if (cap) {
- switch (nodeType()) {
- case Node::Class:
- return QLatin1String("Class");
- case Node::Struct:
- return QLatin1String("Struct");
- case Node::Union:
- return QLatin1String("Union");
- default:
- break;
- }
- } else {
- switch (nodeType()) {
- case Node::Class:
- return QLatin1String("class");
- case Node::Struct:
- return QLatin1String("struct");
- case Node::Union:
- return QLatin1String("union");
- default:
- break;
- }
- }
- return QString();
-}
-
-/*! \fn int Aggregate::count() const
- Returns the number of children in the child list.
- */
-
-/*! \fn const NodeList &Aggregate::childNodes() const
- Returns a const reference to the child list.
- */
-
-/*! \fn NodeList::ConstIterator Aggregate::constBegin() const
- Returns a const iterator pointing at the beginning of the child list.
- */
-
-/*! \fn NodeList::ConstIterator Aggregate::constEnd() const
- Returns a const iterator pointing at the end of the child list.
- */
-
-/*! \fn const QStringList &Aggregate::includeFiles() const
- This function returns a const reference to a list of strings, but
- I no longer know what they are.
- */
-
-/*! \fn QmlTypeNode *Aggregate::qmlBaseNode() const
- If this Aggregate is a QmlTypeNode, this function returns a pointer to
- the QmlTypeNode that is its base type. Otherwise it returns \c nullptr.
- A QmlTypeNode doesn't always have a base type, so even when this Aggregate
- is aQmlTypeNode, the pointer returned can be \c nullptr.
- */
-
-/*! \fn FunctionMap &Aggregate::functionMap()
- Returns a reference to this Aggregate's function map, which
- is a map of all the children of this Aggregate that are
- FunctionNodes.
- */
-
-/*! \fn void Aggregate::appendToRelatedByProxy(const NodeList &t)
- Appends the list of node pointers to the list of elements that are
- related to this Aggregate but are documented in a different module.
-
- \sa relatedByProxy()
- */
-
-/*! \fn NodeList &Aggregate::relatedByProxy()
- Returns a reference to a list of node pointers where each element
- points to a node in an index file for some other module, such that
- whatever the node represents was documented in that other module,
- but it is related to this Aggregate, so when the documentation for
- this Aggregate is written, it will contain links to elements in the
- other module.
- */
-
-/*!
\class PageNode
\brief A PageNode is a Node that generates a documentation page.
diff --git a/src/qdoc/node.h b/src/qdoc/node.h
index 3d7d021a1..898a12dc2 100644
--- a/src/qdoc/node.h
+++ b/src/qdoc/node.h
@@ -451,84 +451,6 @@ protected:
QStringList groupNames_;
};
-class Aggregate : public PageNode
-{
-public:
- Node *findChildNode(const QString &name, Node::Genus genus, int findFlags = 0) const;
- Node *findNonfunctionChild(const QString &name, bool (Node::*)() const);
- void findChildren(const QString &name, NodeVector &nodes) const;
- FunctionNode *findFunctionChild(const QString &name, const Parameters &parameters);
- FunctionNode *findFunctionChild(const FunctionNode *clone);
-
- void normalizeOverloads();
- void markUndocumentedChildrenInternal();
-
- bool isAggregate() const override { return true; }
- const EnumNode *findEnumNodeForValue(const QString &enumValue) const;
-
- int count() const { return children_.size(); }
- const NodeList &childNodes() const { return children_; }
- const NodeList &nonfunctionList();
- NodeList::ConstIterator constBegin() const { return children_.constBegin(); }
- NodeList::ConstIterator constEnd() const { return children_.constEnd(); }
-
- void addIncludeFile(const QString &includeFile);
- void setIncludeFiles(const QStringList &includeFiles);
- const QStringList &includeFiles() const { return includeFiles_; }
-
- QStringList primaryKeys();
- QmlPropertyNode *hasQmlProperty(const QString &) const;
- QmlPropertyNode *hasQmlProperty(const QString &, bool attached) const;
- virtual QmlTypeNode *qmlBaseNode() const { return nullptr; }
- void addChildByTitle(Node *child, const QString &title);
- void printChildren(const QString &title);
- void addChild(Node *child);
- void adoptChild(Node *child);
- void setOutputSubdirectory(const QString &t) override;
-
- FunctionMap &functionMap() { return functionMap_; }
- void findAllFunctions(NodeMapMap &functionIndex);
- void findAllNamespaces(NodeMultiMap &namespaces);
- void findAllAttributions(NodeMultiMap &attributions);
- bool hasObsoleteMembers() const;
- void findAllObsoleteThings();
- void findAllClasses();
- void findAllSince();
- void resolveQmlInheritance();
- bool hasOverloads(const FunctionNode *fn) const;
- void appendToRelatedByProxy(const NodeList &t) { relatedByProxy_.append(t); }
- NodeList &relatedByProxy() { return relatedByProxy_; }
- QString typeWord(bool cap) const;
-
-protected:
- Aggregate(NodeType type, Aggregate *parent, const QString &name)
- : PageNode(type, parent, name), functionCount_(0)
- {
- }
- ~Aggregate() override;
- void removeFunctionNode(FunctionNode *fn);
-
-private:
- friend class Node;
- void addFunction(FunctionNode *fn);
- void adoptFunction(FunctionNode *fn);
- static bool isSameSignature(const FunctionNode *f1, const FunctionNode *f2);
-
-protected:
- NodeList children_;
- NodeList relatedByProxy_;
-
-private:
- QStringList includeFiles_;
- NodeList enumChildren_;
- NodeMultiMap nonfunctionMap_;
- NodeList nonfunctionList_;
-
-protected:
- int functionCount_;
- FunctionMap functionMap_;
-};
-
QT_END_NAMESPACE
#endif
diff --git a/src/qdoc/propertynode.cpp b/src/qdoc/propertynode.cpp
index f6667c05b..824ad851a 100644
--- a/src/qdoc/propertynode.cpp
+++ b/src/qdoc/propertynode.cpp
@@ -28,6 +28,8 @@
#include "propertynode.h"
+#include "aggregate.h"
+
QT_BEGIN_NAMESPACE
/*!
diff --git a/src/qdoc/propertynode.h b/src/qdoc/propertynode.h
index 3caf38dee..d6ee33547 100644
--- a/src/qdoc/propertynode.h
+++ b/src/qdoc/propertynode.h
@@ -37,6 +37,8 @@
QT_BEGIN_NAMESPACE
+class Aggregate;
+
class PropertyNode : public Node
{
public:
diff --git a/src/qdoc/proxynode.h b/src/qdoc/proxynode.h
index 634025598..4cfcb8d0a 100644
--- a/src/qdoc/proxynode.h
+++ b/src/qdoc/proxynode.h
@@ -29,7 +29,7 @@
#ifndef PROXYNODE_H
#define PROXYNODE_H
-#include "node.h"
+#include "aggregate.h"
#include <QtCore/qglobal.h>
diff --git a/src/qdoc/qdoc.pro b/src/qdoc/qdoc.pro
index e085b6903..9e36809bc 100644
--- a/src/qdoc/qdoc.pro
+++ b/src/qdoc/qdoc.pro
@@ -31,6 +31,7 @@ win32-icc*|win32-msvc*:{
}
HEADERS += access.h \
+ aggregate.h \
atom.h \
clangcodeparser.h \
classnode.h \
@@ -91,7 +92,8 @@ HEADERS += access.h \
webxmlgenerator.h \
xmlgenerator.h
-SOURCES += atom.cpp \
+SOURCES += aggregate.cpp \
+ atom.cpp \
clangcodeparser.cpp \
classnode.cpp \
codechunk.cpp \
diff --git a/src/qdoc/qdoctagfiles.cpp b/src/qdoc/qdoctagfiles.cpp
index 550be97a9..874fe187a 100644
--- a/src/qdoc/qdoctagfiles.cpp
+++ b/src/qdoc/qdoctagfiles.cpp
@@ -29,6 +29,7 @@
#include "qdoctagfiles.h"
#include "access.h"
+#include "aggregate.h"
#include "classnode.h"
#include "enumnode.h"
#include "functionnode.h"
diff --git a/src/qdoc/qmlpropertynode.h b/src/qdoc/qmlpropertynode.h
index 2ab9d8bc5..b86346a9c 100644
--- a/src/qdoc/qmlpropertynode.h
+++ b/src/qdoc/qmlpropertynode.h
@@ -29,6 +29,7 @@
#ifndef QMLPROPERTYNODE_H
#define QMLPROPERTYNODE_H
+#include "aggregate.h"
#include "node.h"
#include <QtCore/qglobal.h>
diff --git a/src/qdoc/qmltypenode.h b/src/qdoc/qmltypenode.h
index 8dc153ccb..ab124e1e6 100644
--- a/src/qdoc/qmltypenode.h
+++ b/src/qdoc/qmltypenode.h
@@ -30,7 +30,7 @@
#define QMLTYPENODE_H
#include "importrec.h"
-#include "node.h"
+#include "aggregate.h"
#include <QtCore/qglobal.h>
#include <QtCore/qstring.h>
diff --git a/src/qdoc/qmlvisitor.cpp b/src/qdoc/qmlvisitor.cpp
index 00aff56ab..158531444 100644
--- a/src/qdoc/qmlvisitor.cpp
+++ b/src/qdoc/qmlvisitor.cpp
@@ -28,6 +28,7 @@
#include "qmlvisitor.h"
+#include "aggregate.h"
#include "codechunk.h"
#include "codeparser.h"
#include "functionnode.h"
diff --git a/src/qdoc/qmlvisitor.h b/src/qdoc/qmlvisitor.h
index 6d24b50a3..578de7997 100644
--- a/src/qdoc/qmlvisitor.h
+++ b/src/qdoc/qmlvisitor.h
@@ -50,6 +50,8 @@ namespace QQmlJS {
# endif
#endif
+class Aggregate;
+
struct QmlPropArgs
{
QString type_;
diff --git a/src/qdoc/sections.cpp b/src/qdoc/sections.cpp
index 81a3603fb..b786f5abe 100644
--- a/src/qdoc/sections.cpp
+++ b/src/qdoc/sections.cpp
@@ -28,6 +28,7 @@
#include "sections.h"
+#include "aggregate.h"
#include "classnode.h"
#include "config.h"
#include "enumnode.h"
diff --git a/src/qdoc/sections.h b/src/qdoc/sections.h
index dfcaaed7b..82c7e3d31 100644
--- a/src/qdoc/sections.h
+++ b/src/qdoc/sections.h
@@ -35,6 +35,8 @@
QT_BEGIN_NAMESPACE
+class Aggregate;
+
typedef QMultiMap<QString, Node *> MemberMap; // the string is the member signature
typedef QPair<const QmlTypeNode *, MemberMap> ClassMap; // the node is the QML type
typedef QVector<ClassMap *> ClassMapList;
diff --git a/src/qdoc/sharedcommentnode.cpp b/src/qdoc/sharedcommentnode.cpp
index fc512fe25..e1190adaa 100644
--- a/src/qdoc/sharedcommentnode.cpp
+++ b/src/qdoc/sharedcommentnode.cpp
@@ -28,6 +28,7 @@
#include "sharedcommentnode.h"
+#include "aggregate.h"
#include "functionnode.h"
#include "qmltypenode.h"
diff --git a/src/qdoc/sharedcommentnode.h b/src/qdoc/sharedcommentnode.h
index ccb9f2c83..e7f786b4d 100644
--- a/src/qdoc/sharedcommentnode.h
+++ b/src/qdoc/sharedcommentnode.h
@@ -36,6 +36,7 @@
QT_BEGIN_NAMESPACE
+class Aggregate;
class QmlTypeNode;
class SharedCommentNode : public Node
diff --git a/src/qdoc/typedefnode.cpp b/src/qdoc/typedefnode.cpp
index e2d1eb2c2..6046a9aaa 100644
--- a/src/qdoc/typedefnode.cpp
+++ b/src/qdoc/typedefnode.cpp
@@ -28,6 +28,8 @@
#include "typedefnode.h"
+#include "aggregate.h"
+
QT_BEGIN_NAMESPACE
/*!
diff --git a/src/qdoc/typedefnode.h b/src/qdoc/typedefnode.h
index 3cebccaa1..308f62a3a 100644
--- a/src/qdoc/typedefnode.h
+++ b/src/qdoc/typedefnode.h
@@ -37,6 +37,8 @@
QT_BEGIN_NAMESPACE
+class Aggregate;
+
class TypedefNode : public Node
{
public:
diff --git a/src/qdoc/variablenode.h b/src/qdoc/variablenode.h
index bdc36bcad..a197c33a6 100644
--- a/src/qdoc/variablenode.h
+++ b/src/qdoc/variablenode.h
@@ -29,6 +29,7 @@
#ifndef VARIABLENODE_H
#define VARIABLENODE_H
+#include "aggregate.h"
#include "node.h"
#include <QtCore/qglobal.h>
diff --git a/src/qdoc/webxmlgenerator.cpp b/src/qdoc/webxmlgenerator.cpp
index b23758260..1b3e00e88 100644
--- a/src/qdoc/webxmlgenerator.cpp
+++ b/src/qdoc/webxmlgenerator.cpp
@@ -28,6 +28,7 @@
#include "webxmlgenerator.h"
+#include "aggregate.h"
#include "collectionnode.h"
#include "config.h"
#include "helpprojectwriter.h"
diff --git a/src/qdoc/webxmlgenerator.h b/src/qdoc/webxmlgenerator.h
index f8b3fa80e..9aaabf39c 100644
--- a/src/qdoc/webxmlgenerator.h
+++ b/src/qdoc/webxmlgenerator.h
@@ -38,6 +38,8 @@
QT_BEGIN_NAMESPACE
+class Aggregate;
+
class WebXMLGenerator : public HtmlGenerator, public IndexSectionWriter
{
public: