summaryrefslogtreecommitdiff
path: root/src/qdoc/qdocdatabase.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qdoc/qdocdatabase.cpp')
-rw-r--r--src/qdoc/qdocdatabase.cpp1744
1 files changed, 1744 insertions, 0 deletions
diff --git a/src/qdoc/qdocdatabase.cpp b/src/qdoc/qdocdatabase.cpp
new file mode 100644
index 000000000..28373bd3b
--- /dev/null
+++ b/src/qdoc/qdocdatabase.cpp
@@ -0,0 +1,1744 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** 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 http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "generator.h"
+#include "atom.h"
+#include "tree.h"
+#include "qdocdatabase.h"
+#include "qdoctagfiles.h"
+#include "qdocindexfiles.h"
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+static NodeMap emptyNodeMap_;
+static NodeMultiMap emptyNodeMultiMap_;
+bool QDocDatabase::debug = false;
+
+/*! \class QDocForest
+ This class manages a collection of trees. Each tree is an
+ instance of class Tree, which is a private class.
+
+ The forest is populated as each index file is loaded.
+ Each index file adds a tree to the forest. Each tree
+ is named with the name of the module it represents.
+
+ The search order is created by searchOrder(), if it has
+ not already been created. The search order and module
+ names arrays have parallel structure, i.e. modulNames_[i]
+ is the module name of the Tree at searchOrder_[i].
+ */
+
+/*!
+ Destroys the qdoc forest. This requires deleting
+ each Tree in the forest. Note that the forest has
+ been transferred into the search order array, so
+ what is really being used to destroy the forest
+ is the search order array.
+ */
+QDocForest::~QDocForest()
+{
+ for (int i=0; i<searchOrder_.size(); ++i)
+ delete searchOrder_.at(i);
+ forest_.clear();
+ searchOrder_.clear();
+ indexSearchOrder_.clear();
+ moduleNames_.clear();
+ primaryTree_ = 0;
+}
+
+/*!
+ Initializes the forest prior to a traversal and
+ returns a pointer to the root node of the primary
+ tree. If the forest is empty, it return 0
+ */
+NamespaceNode* QDocForest::firstRoot()
+{
+ currentIndex_ = 0;
+ return (!searchOrder().isEmpty() ? searchOrder()[0]->root() : 0);
+}
+
+/*!
+ Increments the forest's current tree index. If the current
+ tree index is still within the forest, the function returns
+ the root node of the current tree. Otherwise it returns 0.
+ */
+NamespaceNode* QDocForest::nextRoot()
+{
+ ++currentIndex_;
+ return (currentIndex_ < searchOrder().size() ? searchOrder()[currentIndex_]->root() : 0);
+}
+
+/*!
+ Initializes the forest prior to a traversal and
+ returns a pointer to the primary tree. If the
+ forest is empty, it returns 0.
+ */
+Tree* QDocForest::firstTree()
+{
+ currentIndex_ = 0;
+ return (!searchOrder().isEmpty() ? searchOrder()[0] : 0);
+}
+
+/*!
+ Increments the forest's current tree index. If the current
+ tree index is still within the forest, the function returns
+ the pointer to the current tree. Otherwise it returns 0.
+ */
+Tree* QDocForest::nextTree()
+{
+ ++currentIndex_;
+ return (currentIndex_ < searchOrder().size() ? searchOrder()[currentIndex_] : 0);
+}
+
+/*!
+ \fn Tree* QDocForest::primaryTree()
+
+ Returns the pointer to the primary tree.
+ */
+
+/*!
+ Finds the tree for module \a t in the forest and
+ sets the primary tree to be that tree. After the
+ primary tree is set, that tree is removed from the
+ forest.
+
+ \node It gets re-inserted into the forest after the
+ search order is built.
+ */
+void QDocForest::setPrimaryTree(const QString& t)
+{
+ QString T = t.toLower();
+ primaryTree_ = findTree(T);
+ forest_.remove(T);
+ if (!primaryTree_)
+ qDebug() << "ERROR: Could not set primary tree to:" << t;
+}
+
+/*!
+ If the search order array is empty, create the search order.
+ If the search order array is not empty, do nothing.
+ */
+void QDocForest::setSearchOrder(QStringList& t)
+{
+ if (!searchOrder_.isEmpty())
+ return;
+
+ /* Allocate space for the search order. */
+ searchOrder_.reserve(forest_.size()+1);
+ searchOrder_.clear();
+ moduleNames_.reserve(forest_.size()+1);
+ moduleNames_.clear();
+
+ /* The primary tree is always first in the search order. */
+ QString primaryName = primaryTree()->physicalModuleName();
+ searchOrder_.append(primaryTree_);
+ moduleNames_.append(primaryName);
+ forest_.remove(primaryName);
+
+ QMap<QString, Tree*>::iterator i;
+ foreach (const QString &m, t) {
+ if (primaryName != m) {
+ i = forest_.find(m);
+ if (i != forest_.end()) {
+ searchOrder_.append(i.value());
+ moduleNames_.append(m);
+ forest_.remove(m);
+ }
+ }
+ }
+ /*
+ If any trees remain in the forest, just add them
+ to the search order sequentially, because we don't
+ know any better at this point.
+ */
+ if (!forest_.isEmpty()) {
+ i = forest_.begin();
+ while (i != forest_.end()) {
+ searchOrder_.append(i.value());
+ moduleNames_.append(i.key());
+ ++i;
+ }
+ forest_.clear();
+ }
+
+ /*
+ Rebuild the forest after constructing the search order.
+ It was destroyed during construction of the search order,
+ but it is needed for module-specific searches.
+
+ Note that this loop also inserts the primary tree into the
+ forrest. That is a requirement.
+ */
+ for (int i=0; i<searchOrder_.size(); ++i) {
+ if (!forest_.contains(moduleNames_.at(i))) {
+ forest_.insert(moduleNames_.at(i), searchOrder_.at(i));
+ }
+ }
+#if 0
+ qDebug() << " SEARCH ORDER:";
+ for (int i=0; i<moduleNames_.size(); ++i)
+ qDebug() << " " << i+1 << "." << moduleNames_.at(i);
+ qDebug() << " FOREST:" << forest_.keys();
+ qDebug() << "SEARCH ORDER:" << moduleNames_;
+#endif
+}
+
+/*!
+ Returns an ordered array of Tree pointers that represents
+ the order in which the trees should be searched. The first
+ Tree in the array is the tree for the current module, i.e.
+ the module for which qdoc is generating documentation.
+
+ The other Tree pointers in the array represent the index
+ files that were loaded in preparation for generating this
+ module's documentation. Each Tree pointer represents one
+ index file. The index file Tree points have been ordered
+ heuristically to, hopefully, minimize searching. Thr order
+ will probably be changed.
+
+ If the search order array is empty, this function calls
+ indexSearchOrder(). The search order array is empty while
+ the index files are being loaded, but some searches must
+ be performed during this time, notably searches for base
+ class nodes. These searches require a temporary search
+ order. The temporary order changes throughout the loading
+ of the index files, but it is always the tree for the
+ current index file first, followed by the trees for the
+ index files that have already been loaded. The only
+ ordering required in this temporary search order is that
+ the current tree must be searched first.
+ */
+const QVector<Tree*>& QDocForest::searchOrder()
+{
+ if (searchOrder_.isEmpty())
+ return indexSearchOrder();
+ return searchOrder_;
+}
+
+/*!
+ There are two search orders used by qdoc when searching for
+ things. The normal search order is returned by searchOrder(),
+ but this normal search order is not known until all the index
+ files have been read. At that point, setSearchOrder() is
+ called.
+
+ During the reading of the index files, the vector holding
+ the normal search order remains empty. Whenever the search
+ order is requested, if that vector is empty, this function
+ is called to return a temporary search order, which includes
+ all the index files that have been read so far, plus the
+ one being read now. That one is prepended to the front of
+ the vector.
+ */
+const QVector<Tree*>& QDocForest::indexSearchOrder()
+{
+ if (forest_.size() > indexSearchOrder_.size())
+ indexSearchOrder_.prepend(primaryTree_);
+ return indexSearchOrder_;
+}
+
+/*!
+ Create a new Tree for the index file for the specified
+ \a module and add it to the forest. Return the pointer
+ to its root.
+ */
+NamespaceNode* QDocForest::newIndexTree(const QString& module)
+{
+ primaryTree_ = new Tree(module, qdb_);
+ forest_.insert(module.toLower(), primaryTree_);
+ return primaryTree_->root();
+}
+
+/*!
+ Create a new Tree for use as the primary tree. This tree
+ will represent the primary module. \a module is camel case.
+ */
+void QDocForest::newPrimaryTree(const QString& module)
+{
+ primaryTree_ = new Tree(module, qdb_);
+}
+
+/*!
+ Searches through the forest for a node named \a targetPath
+ and returns a pointer to it if found. The \a relative node
+ is the starting point. It only makes sense for the primary
+ tree, which is searched first. After the primary tree has
+ been searched, \a relative is set to 0 for searching the
+ other trees, which are all index trees. With relative set
+ to 0, the starting point for each index tree is the root
+ of the index tree.
+ */
+const Node* QDocForest::findNodeForTarget(QStringList& targetPath,
+ const Node* relative,
+ Node::Genus genus,
+ QString& ref)
+{
+ int flags = SearchBaseClasses | SearchEnumValues;
+
+ QString entity = targetPath.takeFirst();
+ QStringList entityPath = entity.split("::");
+
+ QString target;
+ if (!targetPath.isEmpty())
+ target = targetPath.takeFirst();
+
+ foreach (Tree* t, searchOrder()) {
+ const Node* n = t->findNodeForTarget(entityPath, target, relative, flags, genus, ref);
+ if (n)
+ return n;
+ relative = 0;
+ }
+ return 0;
+}
+
+/*!
+ Print the list of module names ordered according
+ to how many successful searches each tree had.
+ */
+void QDocForest::printLinkCounts(const QString& project)
+{
+ Location::null.report(QString("%1: Link Counts").arg(project));
+ QMultiMap<int, QString> m;
+ foreach (Tree* t, searchOrder()) {
+ if (t->linkCount() < 0)
+ m.insert(t->linkCount(), t->physicalModuleName());
+ }
+ QString depends = "depends +=";
+ QString module = project.toLower();
+ QMultiMap<int, QString>::iterator i = m.begin();
+ while (i != m.end()) {
+ QString line = " " + i.value();
+ if (i.value() != module)
+ depends += QLatin1Char(' ') + i.value();
+ int pad = 30 - line.length();
+ for (int k=0; k<pad; ++k)
+ line += QLatin1Char(' ');
+ line += "%1";
+ Location::null.report(line.arg(-(i.key())));
+ ++i;
+ }
+ Location::null.report("Optimal depends variable:");
+ Location::null.report(depends);
+}
+
+/*!
+ Print the list of module names ordered according
+ to how many successful searches each tree had.
+ */
+QString QDocForest::getLinkCounts(QStringList& strings, QVector<int>& counts)
+{
+ QMultiMap<int, QString> m;
+ foreach (Tree* t, searchOrder()) {
+ if (t->linkCount() < 0)
+ m.insert(t->linkCount(), t->physicalModuleName());
+ }
+ QString depends = "depends +=";
+ QString module = Generator::defaultModuleName().toLower();
+ QMultiMap<int, QString>::iterator i = m.begin();
+ while (i != m.end()) {
+ if (i.value() != module) {
+ counts.append(-(i.key()));
+ strings.append(i.value());
+ depends += QLatin1Char(' ') + i.value();
+ }
+ ++i;
+ }
+ return depends;
+}
+
+/*!
+ */
+const Node* QDocForest::findFunctionNode(const QString& target,
+ const Node* relative,
+ Node::Genus genus)
+{
+ QString function, params;
+ int length = target.length();
+ if (target.endsWith(QChar(')'))) {
+ int position = target.lastIndexOf(QChar('('));
+ params = target.mid(position+1, length-position-2);
+ function = target.left(position);
+ }
+ else
+ function = target;
+ foreach (Tree* t, searchOrder()) {
+ const Node* n = t->findFunctionNode(function, params, relative, genus);
+ if (n)
+ return n;
+ relative = 0;
+ }
+ return 0;
+}
+
+/*! \class QDocDatabase
+ This class provides exclusive access to the qdoc database,
+ which consists of a forrest of trees and a lot of maps and
+ other useful data structures.
+ */
+
+QDocDatabase* QDocDatabase::qdocDB_ = NULL;
+NodeMap QDocDatabase::typeNodeMap_;
+
+/*!
+ Constructs the singleton qdoc database object. The singleton
+ constructs the \a forest_ object, which is also a singleton.
+ \a showInternal_ is normally false. If it is true, qdoc will
+ write documentation for nodes marked \c internal.
+
+ \a singleExec_ is false when qdoc is being used in the standard
+ way of running qdoc twices for each module, first with -prepare
+ and then with -generate. First the -prepare phase is run for
+ each module, then the -generate phase is run for each module.
+
+ When \a singleExec_ is true, qdoc is run only once. During the
+ single execution, qdoc processes the qdocconf files for all the
+ modules sequentially in a loop. Each source file for each module
+ is read exactly once.
+ */
+QDocDatabase::QDocDatabase()
+ : showInternal_(false), singleExec_(false), forest_(this)
+{
+ // nothing
+}
+
+/*!
+ Destroys the qdoc database object. This requires destroying
+ the forest object, which contains an array of tree pointers.
+ Each tree is deleted.
+ */
+QDocDatabase::~QDocDatabase()
+{
+ // nothing.
+}
+
+/*!
+ Creates the singleton. Allows only one instance of the class
+ to be created. Returns a pointer to the singleton.
+*/
+QDocDatabase* QDocDatabase::qdocDB()
+{
+ if (!qdocDB_) {
+ qdocDB_ = new QDocDatabase;
+ initializeDB();
+ }
+ return qdocDB_;
+}
+
+/*!
+ Destroys the singleton.
+ */
+void QDocDatabase::destroyQdocDB()
+{
+ if (qdocDB_) {
+ delete qdocDB_;
+ qdocDB_ = 0;
+ }
+}
+
+/*!
+ Initialize data structures in the singleton qdoc database.
+
+ In particular, the type node map is initialized with a lot
+ type names that don't refer to documented types. For example,
+ the C++ standard types are included. These might be documented
+ here at some point, but for now they are not. Other examples
+ include \c array and \c data, which are just generic names
+ used as place holders in function signatures that appear in
+ the documentation.
+
+ Also calls Node::initialize() to initialize the search goal map.
+ */
+void QDocDatabase::initializeDB()
+{
+ Node::initialize();
+ typeNodeMap_.insert( "accepted", 0);
+ typeNodeMap_.insert( "actionPerformed", 0);
+ typeNodeMap_.insert( "activated", 0);
+ typeNodeMap_.insert( "alias", 0);
+ typeNodeMap_.insert( "anchors", 0);
+ typeNodeMap_.insert( "any", 0);
+ typeNodeMap_.insert( "array", 0);
+ typeNodeMap_.insert( "autoSearch", 0);
+ typeNodeMap_.insert( "axis", 0);
+ typeNodeMap_.insert( "backClicked", 0);
+ typeNodeMap_.insert( "bool", 0);
+ typeNodeMap_.insert( "boomTime", 0);
+ typeNodeMap_.insert( "border", 0);
+ typeNodeMap_.insert( "buttonClicked", 0);
+ typeNodeMap_.insert( "callback", 0);
+ typeNodeMap_.insert( "char", 0);
+ typeNodeMap_.insert( "clicked", 0);
+ typeNodeMap_.insert( "close", 0);
+ typeNodeMap_.insert( "closed", 0);
+ typeNodeMap_.insert( "color", 0);
+ typeNodeMap_.insert( "cond", 0);
+ typeNodeMap_.insert( "data", 0);
+ typeNodeMap_.insert( "dataReady", 0);
+ typeNodeMap_.insert( "dateString", 0);
+ typeNodeMap_.insert( "dateTimeString", 0);
+ typeNodeMap_.insert( "datetime", 0);
+ typeNodeMap_.insert( "day", 0);
+ typeNodeMap_.insert( "deactivated", 0);
+ typeNodeMap_.insert( "double", 0);
+ typeNodeMap_.insert( "drag", 0);
+ typeNodeMap_.insert( "easing", 0);
+ typeNodeMap_.insert( "enumeration", 0);
+ typeNodeMap_.insert( "error", 0);
+ typeNodeMap_.insert( "exposure", 0);
+ typeNodeMap_.insert( "fatalError", 0);
+ typeNodeMap_.insert( "fileSelected", 0);
+ typeNodeMap_.insert( "flags", 0);
+ typeNodeMap_.insert( "float", 0);
+ typeNodeMap_.insert( "focus", 0);
+ typeNodeMap_.insert( "focusZone", 0);
+ typeNodeMap_.insert( "format", 0);
+ typeNodeMap_.insert( "framePainted", 0);
+ typeNodeMap_.insert( "from", 0);
+ typeNodeMap_.insert( "frontClicked", 0);
+ typeNodeMap_.insert( "function", 0);
+ typeNodeMap_.insert( "hasOpened", 0);
+ typeNodeMap_.insert( "hovered", 0);
+ typeNodeMap_.insert( "hoveredTitle", 0);
+ typeNodeMap_.insert( "hoveredUrl", 0);
+ typeNodeMap_.insert( "imageCapture", 0);
+ typeNodeMap_.insert( "imageProcessing", 0);
+ typeNodeMap_.insert( "index", 0);
+ typeNodeMap_.insert( "initialized", 0);
+ typeNodeMap_.insert( "int", 0);
+ typeNodeMap_.insert( "isLoaded", 0);
+ typeNodeMap_.insert( "item", 0);
+ typeNodeMap_.insert( "jsdict", 0);
+ typeNodeMap_.insert( "jsobject", 0);
+ typeNodeMap_.insert( "key", 0);
+ typeNodeMap_.insert( "keysequence", 0);
+ typeNodeMap_.insert( "list", 0);
+ typeNodeMap_.insert( "listViewClicked", 0);
+ typeNodeMap_.insert( "loadRequest", 0);
+ typeNodeMap_.insert( "locale", 0);
+ typeNodeMap_.insert( "location", 0);
+ typeNodeMap_.insert( "long", 0);
+ typeNodeMap_.insert( "message", 0);
+ typeNodeMap_.insert( "messageReceived", 0);
+ typeNodeMap_.insert( "mode", 0);
+ typeNodeMap_.insert( "month", 0);
+ typeNodeMap_.insert( "name", 0);
+ typeNodeMap_.insert( "number", 0);
+ typeNodeMap_.insert( "object", 0);
+ typeNodeMap_.insert( "offset", 0);
+ typeNodeMap_.insert( "ok", 0);
+ typeNodeMap_.insert( "openCamera", 0);
+ typeNodeMap_.insert( "openImage", 0);
+ typeNodeMap_.insert( "openVideo", 0);
+ typeNodeMap_.insert( "padding", 0);
+ typeNodeMap_.insert( "parent", 0);
+ typeNodeMap_.insert( "path", 0);
+ typeNodeMap_.insert( "photoModeSelected", 0);
+ typeNodeMap_.insert( "position", 0);
+ typeNodeMap_.insert( "precision", 0);
+ typeNodeMap_.insert( "presetClicked", 0);
+ typeNodeMap_.insert( "preview", 0);
+ typeNodeMap_.insert( "previewSelected", 0);
+ typeNodeMap_.insert( "progress", 0);
+ typeNodeMap_.insert( "puzzleLost", 0);
+ typeNodeMap_.insert( "qmlSignal", 0);
+ typeNodeMap_.insert( "real", 0);
+ typeNodeMap_.insert( "rectangle", 0);
+ typeNodeMap_.insert( "request", 0);
+ typeNodeMap_.insert( "requestId", 0);
+ typeNodeMap_.insert( "section", 0);
+ typeNodeMap_.insert( "selected", 0);
+ typeNodeMap_.insert( "send", 0);
+ typeNodeMap_.insert( "settingsClicked", 0);
+ typeNodeMap_.insert( "shoe", 0);
+ typeNodeMap_.insert( "short", 0);
+ typeNodeMap_.insert( "signed", 0);
+ typeNodeMap_.insert( "sizeChanged", 0);
+ typeNodeMap_.insert( "size_t", 0);
+ typeNodeMap_.insert( "sockaddr", 0);
+ typeNodeMap_.insert( "someOtherSignal", 0);
+ typeNodeMap_.insert( "sourceSize", 0);
+ typeNodeMap_.insert( "startButtonClicked", 0);
+ typeNodeMap_.insert( "state", 0);
+ typeNodeMap_.insert( "std::initializer_list", 0);
+ typeNodeMap_.insert( "std::list", 0);
+ typeNodeMap_.insert( "std::map", 0);
+ typeNodeMap_.insert( "std::pair", 0);
+ typeNodeMap_.insert( "std::string", 0);
+ typeNodeMap_.insert( "std::vector", 0);
+ typeNodeMap_.insert( "string", 0);
+ typeNodeMap_.insert( "stringlist", 0);
+ typeNodeMap_.insert( "swapPlayers", 0);
+ typeNodeMap_.insert( "symbol", 0);
+ typeNodeMap_.insert( "t", 0);
+ typeNodeMap_.insert( "T", 0);
+ typeNodeMap_.insert( "tagChanged", 0);
+ typeNodeMap_.insert( "timeString", 0);
+ typeNodeMap_.insert( "timeout", 0);
+ typeNodeMap_.insert( "to", 0);
+ typeNodeMap_.insert( "toggled", 0);
+ typeNodeMap_.insert( "type", 0);
+ typeNodeMap_.insert( "unsigned", 0);
+ typeNodeMap_.insert( "urllist", 0);
+ typeNodeMap_.insert( "va_list", 0);
+ typeNodeMap_.insert( "value", 0);
+ typeNodeMap_.insert( "valueEmitted", 0);
+ typeNodeMap_.insert( "videoFramePainted", 0);
+ typeNodeMap_.insert( "videoModeSelected", 0);
+ typeNodeMap_.insert( "videoRecorder", 0);
+ typeNodeMap_.insert( "void", 0);
+ typeNodeMap_.insert( "volatile", 0);
+ typeNodeMap_.insert( "wchar_t", 0);
+ typeNodeMap_.insert( "x", 0);
+ typeNodeMap_.insert( "y", 0);
+ typeNodeMap_.insert( "zoom", 0);
+ typeNodeMap_.insert( "zoomTo", 0);
+}
+
+/*! \fn NamespaceNode* QDocDatabase::primaryTreeRoot()
+ Returns a pointer to the root node of the primary tree.
+ */
+
+/*!
+ \fn const CNMap& QDocDatabase::groups()
+ Returns a const reference to the collection of all
+ group nodes in the primary tree.
+*/
+
+/*!
+ \fn const CNMap& QDocDatabase::modules()
+ Returns a const reference to the collection of all
+ module nodes in the primary tree.
+*/
+
+/*!
+ \fn const CNMap& QDocDatabase::qmlModules()
+ Returns a const reference to the collection of all
+ QML module nodes in the primary tree.
+*/
+
+/*!
+ \fn const CNMap& QDocDatabase::jsModules()
+ Returns a const reference to the collection of all
+ JovaScript module nodes in the primary tree.
+*/
+
+/*! \fn CollectionNode* QDocDatabase::findGroup(const QString& name)
+ Find the group node named \a name and return a pointer
+ to it. If a matching node is not found, add a new group
+ node named \a name and return a pointer to that 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* QDocDatabase::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* QDocDatabase::findQmlModule(const QString& name, bool javaScript)
+ 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 javaScript is set, the return collection must be a
+ JavaScript module.
+
+ If a new QML or JavaScript module node is added, its parent
+ is the tree root, and the new node is marked \e{not seen}.
+ */
+
+/*! \fn CollectionNode* QDocDatabase::addGroup(const QString& name)
+ Looks up the group named \a name in the primary tree. 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* QDocDatabase::addModule(const QString& name)
+ Looks up the module named \a name in the primary tree. 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* QDocDatabase::addQmlModule(const QString& name)
+ Looks up the QML module named \a name in the primary tree.
+ 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* QDocDatabase::addJsModule(const QString& name)
+ Looks up the JavaScript module named \a name in the primary
+ tree. If a match is found, a pointer to the node is returned.
+ Otherwise, a new JavaScript module node named \a name is
+ created and inserted into the collection, and the pointer to
+ that node is returned.
+ */
+
+/*! \fn CollectionNode* QDocDatabase::addToGroup(const QString& name, Node* node)
+ 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 node to the member list of the \a node. The parent of the
+ \a node is not changed by this function. Returns a pointer to
+ the group node.
+ */
+
+/*! \fn CollectionNode* QDocDatabase::addToModule(const QString& name, Node* node)
+ 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.
+ */
+
+/*! \fn Collection* QDocDatabase::addToQmlModule(const QString& name, Node* node)
+ 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.
+ */
+
+/*! \fn Collection* QDocDatabase::addToJsModule(const QString& name, Node* node)
+ Looks up the JavaScript module named \a name. If it isn't there,
+ create it. Then append \a node to the JavaScript module's member
+ list. The parent of \a node is not changed by this function.
+ */
+
+/*!
+ Looks up the QML type node identified by the qualified Qml
+ type \a name and returns a pointer to the QML type node.
+ */
+QmlTypeNode* QDocDatabase::findQmlType(const QString& name)
+{
+ QmlTypeNode* qcn = forest_.lookupQmlType(name);
+ if (qcn)
+ return qcn;
+ return 0;
+}
+
+/*!
+ Looks up the QML type node identified by the Qml module id
+ \a qmid and QML type \a name and returns a pointer to the
+ QML type node. The key is \a qmid + "::" + \a name.
+
+ If the QML module id is empty, it looks up the QML type by
+ \a name only.
+ */
+QmlTypeNode* QDocDatabase::findQmlType(const QString& qmid, const QString& name)
+{
+ if (!qmid.isEmpty()) {
+ QString t = qmid + "::" + name;
+ QmlTypeNode* qcn = forest_.lookupQmlType(t);
+ if (qcn)
+ return qcn;
+ }
+
+ QStringList path(name);
+ Node* n = forest_.findNodeByNameAndType(path, Node::QmlType);
+ if (n && (n->isQmlType() || n->isJsType()))
+ return static_cast<QmlTypeNode*>(n);
+ return 0;
+}
+
+/*!
+ Looks up the QML type node identified by the Qml module id
+ constructed from the strings in the \a import record and the
+ QML type \a name and returns a pointer to the QML type node.
+ If a QML type node is not found, 0 is returned.
+ */
+QmlTypeNode* QDocDatabase::findQmlType(const ImportRec& import, const QString& name)
+{
+ if (!import.isEmpty()) {
+ QStringList dotSplit;
+ dotSplit = name.split(QLatin1Char('.'));
+ QString qmName;
+ if (import.importUri_.isEmpty())
+ qmName = import.name_;
+ else
+ qmName = import.importUri_;
+ for (int i=0; i<dotSplit.size(); ++i) {
+ QString qualifiedName = qmName + "::" + dotSplit[i];
+ QmlTypeNode* qcn = forest_.lookupQmlType(qualifiedName);
+ if (qcn)
+ return qcn;
+ }
+ }
+ return 0;
+}
+
+/*!
+ This function calls a set of functions for each tree in the
+ forest that has not already been analyzed. In this way, when
+ running qdoc in \e singleExec mode, each tree is analyzed in
+ turn, and its classes and types are added to the appropriate
+ node maps.
+ */
+void QDocDatabase::processForest()
+{
+ Tree* t = forest_.firstTree();
+ while (t) {
+ findAllClasses(t->root());
+ findAllFunctions(t->root());
+ findAllObsoleteThings(t->root());
+ findAllLegaleseTexts(t->root());
+ findAllSince(t->root());
+ t->setTreeHasBeenAnalyzed();
+ t = forest_.nextTree();
+ }
+ resolveNamespaces();
+}
+
+/*!
+ This function calls \a func for each tree in the forest,
+ but only if Tree::treeHasBeenAnalyzed() returns false for
+ the tree. In this way, when running qdoc in \e singleExec
+ mode, each tree is analyzed in turn, and its classes and
+ types are added to the appropriate node maps.
+ */
+void QDocDatabase::processForest(void (QDocDatabase::*func) (Aggregate*))
+{
+ Tree* t = forest_.firstTree();
+ while (t) {
+ if (!t->treeHasBeenAnalyzed()) {
+ (this->*(func))(t->root());
+ }
+ t = forest_.nextTree();
+ }
+}
+
+/*!
+ Constructs the collection of legalese texts, if it has not
+ already been constructed and returns a reference to it.
+ */
+TextToNodeMap& QDocDatabase::getLegaleseTexts()
+{
+ if (legaleseTexts_.isEmpty())
+ processForest(&QDocDatabase::findAllLegaleseTexts);
+ return legaleseTexts_;
+}
+
+/*!
+ Construct the data structures for obsolete things, if they
+ have not already been constructed. Returns a reference to
+ the map of C++ classes with obsolete members.
+ */
+NodeMultiMap& QDocDatabase::getClassesWithObsoleteMembers()
+{
+ if (obsoleteClasses_.isEmpty() && obsoleteQmlTypes_.isEmpty())
+ processForest(&QDocDatabase::findAllObsoleteThings);
+ return classesWithObsoleteMembers_;
+}
+
+/*!
+ Construct the data structures for obsolete things, if they
+ have not already been constructed. Returns a reference to
+ the map of obsolete QML types.
+ */
+NodeMultiMap& QDocDatabase::getObsoleteQmlTypes()
+{
+ if (obsoleteClasses_.isEmpty() && obsoleteQmlTypes_.isEmpty())
+ processForest(&QDocDatabase::findAllObsoleteThings);
+ return obsoleteQmlTypes_;
+}
+
+/*!
+ Construct the data structures for obsolete things, if they
+ have not already been constructed. Returns a reference to
+ the map of QML types with obsolete members.
+ */
+NodeMultiMap& QDocDatabase::getQmlTypesWithObsoleteMembers()
+{
+ if (obsoleteClasses_.isEmpty() && obsoleteQmlTypes_.isEmpty())
+ processForest(&QDocDatabase::findAllObsoleteThings);
+ return qmlTypesWithObsoleteMembers_;
+}
+
+/*! \fn NodeMultiMap& QDocDatabase::getNamespaces()
+ Returns a reference to the map of all namespace nodes.
+ This function must not be called in the -prepare phase.
+ */
+
+/*!
+ Construct the data structures for QML basic types, if they
+ have not already been constructed. Returns a reference to
+ the map of QML basic types.
+ */
+NodeMultiMap& QDocDatabase::getQmlBasicTypes()
+{
+ if (cppClasses_.isEmpty() && qmlBasicTypes_.isEmpty())
+ processForest(&QDocDatabase::findAllClasses);
+ return qmlBasicTypes_;
+}
+
+/*!
+ Construct the data structures for obsolete things, if they
+ have not already been constructed. Returns a reference to
+ the multimap of QML types.
+ */
+NodeMultiMap& QDocDatabase::getQmlTypes()
+{
+ if (cppClasses_.isEmpty() && qmlTypes_.isEmpty())
+ processForest(&QDocDatabase::findAllClasses);
+ return qmlTypes_;
+}
+
+/*!
+ Construct the data structures for obsolete things, if they
+ have not already been constructed. Returns a reference to
+ the map of obsolete C++ clases.
+ */
+NodeMultiMap& QDocDatabase::getObsoleteClasses()
+{
+ if (obsoleteClasses_.isEmpty() && obsoleteQmlTypes_.isEmpty())
+ processForest(&QDocDatabase::findAllObsoleteThings);
+ return obsoleteClasses_;
+}
+
+/*!
+ Construct the C++ class data structures, if they have not
+ already been constructed. Returns a reference to the map
+ of all C++ classes.
+ */
+NodeMultiMap& QDocDatabase::getCppClasses()
+{
+ if (cppClasses_.isEmpty() && qmlTypes_.isEmpty())
+ processForest(&QDocDatabase::findAllClasses);
+ return cppClasses_;
+}
+
+/*!
+ Finds all the C++ class nodes and QML type nodes and
+ sorts them into maps.
+ */
+void QDocDatabase::findAllClasses(Aggregate* node)
+{
+ NodeList::const_iterator c = node->childNodes().constBegin();
+ while (c != node->childNodes().constEnd()) {
+ if ((*c)->access() != Node::Private && (!(*c)->isInternal() || showInternal_) &&
+ (*c)->tree()->camelCaseModuleName() != QString("QDoc")) {
+ if ((*c)->type() == Node::Class && !(*c)->doc().isEmpty()) {
+ QString className = (*c)->name();
+ if ((*c)->parent() &&
+ (*c)->parent()->type() == Node::Namespace &&
+ !(*c)->parent()->name().isEmpty())
+ className = (*c)->parent()->name()+"::"+className;
+
+ cppClasses_.insert(className, *c);
+ }
+ else if (((*c)->isQmlType() || (*c)->isQmlBasicType() ||
+ (*c)->isJsType() || (*c)->isJsBasicType()) && !(*c)->doc().isEmpty()) {
+ QString qmlTypeName = (*c)->name();
+ if (qmlTypeName.startsWith(QLatin1String("QML:")))
+ qmlTypes_.insert(qmlTypeName.mid(4),*c);
+ else
+ qmlTypes_.insert(qmlTypeName,*c);
+
+ //also add to the QML basic type map
+ if ((*c)->isQmlBasicType() || (*c)->isJsType())
+ qmlBasicTypes_.insert(qmlTypeName,*c);
+ }
+ else if ((*c)->isAggregate()) {
+ findAllClasses(static_cast<Aggregate*>(*c));
+ }
+ }
+ ++c;
+ }
+}
+
+/*!
+ Construct the function index data structure and return it.
+ This data structure is used to output the function index page.
+ */
+NodeMapMap& QDocDatabase::getFunctionIndex()
+{
+ processForest(&QDocDatabase::findAllFunctions);
+ return funcIndex_;
+}
+
+/*!
+ Finds all the function nodes
+ */
+void QDocDatabase::findAllFunctions(Aggregate* node)
+{
+ NodeList::ConstIterator c = node->childNodes().constBegin();
+ while (c != node->childNodes().constEnd()) {
+ if ((*c)->access() != Node::Private) {
+ if ((*c)->isAggregate()) {
+ findAllFunctions(static_cast<Aggregate*>(*c));
+ }
+ else if ((*c)->type() == Node::Function) {
+ const FunctionNode* func = static_cast<const FunctionNode*>(*c);
+ if ((func->status() > Node::Obsolete) &&
+ !func->isInternal() &&
+ (func->metaness() != FunctionNode::Ctor) &&
+ (func->metaness() != FunctionNode::Dtor)) {
+ funcIndex_[(*c)->name()].insert((*c)->parent()->fullDocumentName(), *c);
+ }
+ }
+ }
+ ++c;
+ }
+}
+
+/*!
+ Finds all the nodes containing legalese text and puts them
+ in a map.
+ */
+void QDocDatabase::findAllLegaleseTexts(Aggregate* node)
+{
+ NodeList::ConstIterator c = node->childNodes().constBegin();
+ while (c != node->childNodes().constEnd()) {
+ if ((*c)->access() != Node::Private) {
+ if (!(*c)->doc().legaleseText().isEmpty())
+ legaleseTexts_.insertMulti((*c)->doc().legaleseText(), *c);
+ if ((*c)->isAggregate())
+ findAllLegaleseTexts(static_cast<Aggregate *>(*c));
+ }
+ ++c;
+ }
+}
+
+/*!
+ Finds all the namespace nodes and puts them in an index.
+ */
+void QDocDatabase::findAllNamespaces(Aggregate* node)
+{
+ NodeList::ConstIterator c = node->childNodes().constBegin();
+ while (c != node->childNodes().constEnd()) {
+ if ((*c)->access() != Node::Private || (*c)->isNamespace()) {
+ if ((*c)->isAggregate()) {
+ findAllNamespaces(static_cast<Aggregate *>(*c));
+ if ((*c)->isNamespace()) {
+ // Ensure that the namespace's name is not empty (the root
+ // namespace has no name).
+ if (!(*c)->name().isEmpty()) {
+ nmm_.insert((*c)->name(), *c);
+ }
+ }
+ }
+ }
+ ++c;
+ }
+}
+
+/*!
+ Finds all nodes with status = Obsolete and sorts them into
+ maps. They can be C++ classes, QML types, or they can be
+ functions, enum types, typedefs, methods, etc.
+ */
+void QDocDatabase::findAllObsoleteThings(Aggregate* node)
+{
+ NodeList::const_iterator c = node->childNodes().constBegin();
+ while (c != node->childNodes().constEnd()) {
+ if ((*c)->access() != Node::Private) {
+ QString name = (*c)->name();
+ if ((*c)->status() == Node::Obsolete) {
+ if ((*c)->type() == Node::Class) {
+ if ((*c)->parent() && (*c)->parent()->type() == Node::Namespace &&
+ !(*c)->parent()->name().isEmpty())
+ name = (*c)->parent()->name() + "::" + name;
+ obsoleteClasses_.insert(name, *c);
+ }
+ else if ((*c)->isQmlType() || (*c)->isJsType()) {
+ if (name.startsWith(QLatin1String("QML:")))
+ name = name.mid(4);
+ name = (*c)->logicalModuleName() + "::" + name;
+ obsoleteQmlTypes_.insert(name,*c);
+ }
+ }
+ else if ((*c)->type() == Node::Class) {
+ Aggregate* n = static_cast<Aggregate*>(*c);
+ bool inserted = false;
+ NodeList::const_iterator p = n->childNodes().constBegin();
+ while (p != n->childNodes().constEnd()) {
+ if ((*p)->access() != Node::Private) {
+ switch ((*p)->type()) {
+ case Node::Enum:
+ case Node::Typedef:
+ case Node::Function:
+ case Node::Property:
+ case Node::Variable:
+ if ((*p)->status() == Node::Obsolete) {
+ if ((*c)->parent() && (*c)->parent()->type() == Node::Namespace &&
+ !(*c)->parent()->name().isEmpty())
+ name = (*c)->parent()->name() + "::" + name;
+ classesWithObsoleteMembers_.insert(name, *c);
+ inserted = true;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ if (inserted)
+ break;
+ ++p;
+ }
+ }
+ else if ((*c)->isQmlType() || (*c)->isJsType()) {
+ Aggregate* n = static_cast<Aggregate*>(*c);
+ bool inserted = false;
+ NodeList::const_iterator p = n->childNodes().constBegin();
+ while (p != n->childNodes().constEnd()) {
+ if ((*p)->access() != Node::Private) {
+ switch ((*c)->type()) {
+ case Node::QmlProperty:
+ case Node::QmlSignal:
+ case Node::QmlSignalHandler:
+ case Node::QmlMethod:
+ if ((*c)->parent()) {
+ Node* parent = (*c)->parent();
+ if ((parent->isQmlPropertyGroup() ||
+ parent->isJsPropertyGroup()) && parent->parent())
+ parent = parent->parent();
+ if (parent && (parent->isQmlType() || parent->isJsType()) &&
+ !parent->name().isEmpty())
+ name = parent->name() + "::" + name;
+ }
+ qmlTypesWithObsoleteMembers_.insert(name,*c);
+ inserted = true;
+ break;
+ default:
+ break;
+ }
+ }
+ if (inserted)
+ break;
+ ++p;
+ }
+ }
+ else if ((*c)->isAggregate()) {
+ findAllObsoleteThings(static_cast<Aggregate*>(*c));
+ }
+ }
+ ++c;
+ }
+}
+
+/*!
+ Finds all the nodes 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 QDocDatabase::findAllSince(Aggregate* node)
+{
+ NodeList::const_iterator child = node->childNodes().constBegin();
+ while (child != node->childNodes().constEnd()) {
+ QString sinceString = (*child)->since();
+ // Insert a new entry into each map for each new since string found.
+ if (((*child)->access() != Node::Private) && !sinceString.isEmpty()) {
+ NodeMultiMapMap::iterator nsmap = newSinceMaps_.find(sinceString);
+ if (nsmap == newSinceMaps_.end())
+ nsmap = newSinceMaps_.insert(sinceString,NodeMultiMap());
+
+ NodeMapMap::iterator ncmap = newClassMaps_.find(sinceString);
+ if (ncmap == newClassMaps_.end())
+ ncmap = newClassMaps_.insert(sinceString,NodeMap());
+
+ NodeMapMap::iterator nqcmap = newQmlTypeMaps_.find(sinceString);
+ if (nqcmap == newQmlTypeMaps_.end())
+ nqcmap = newQmlTypeMaps_.insert(sinceString,NodeMap());
+
+ if ((*child)->type() == Node::Function) {
+ // Insert functions into the general since map.
+ FunctionNode *func = static_cast<FunctionNode *>(*child);
+ if ((func->status() > Node::Obsolete) &&
+ (func->metaness() != FunctionNode::Ctor) &&
+ (func->metaness() != FunctionNode::Dtor)) {
+ nsmap.value().insert(func->name(),(*child));
+ }
+ }
+ else {
+ if ((*child)->type() == Node::Class) {
+ // Insert classes into the since and class maps.
+ QString className = (*child)->name();
+ if ((*child)->parent() && !(*child)->parent()->name().isEmpty()) {
+ className = (*child)->parent()->name()+"::"+className;
+ }
+ nsmap.value().insert(className,(*child));
+ ncmap.value().insert(className,(*child));
+ }
+ else if ((*child)->isQmlType() || (*child)->isJsType()) {
+ // Insert QML elements into the since and element maps.
+ QString className = (*child)->name();
+ if ((*child)->parent() && !(*child)->parent()->name().isEmpty()) {
+ className = (*child)->parent()->name()+"::"+className;
+ }
+ nsmap.value().insert(className,(*child));
+ nqcmap.value().insert(className,(*child));
+ }
+ else if ((*child)->isQmlProperty() || (*child)->isJsProperty()) {
+ // Insert QML properties into the since map.
+ QString propertyName = (*child)->name();
+ nsmap.value().insert(propertyName,(*child));
+ }
+ else {
+ // Insert external documents into the general since map.
+ QString name = (*child)->name();
+ if ((*child)->parent() && !(*child)->parent()->name().isEmpty()) {
+ name = (*child)->parent()->name()+"::"+name;
+ }
+ nsmap.value().insert(name,(*child));
+ }
+ }
+ }
+ // Recursively find child nodes with since commands.
+ if ((*child)->isAggregate())
+ findAllSince(static_cast<Aggregate *>(*child));
+
+ ++child;
+ }
+}
+
+/*!
+ Find the \a key in the map of new class maps, and return a
+ reference to the value, which is a NodeMap. If \a key is not
+ found, return a reference to an empty NodeMap.
+ */
+const NodeMap& QDocDatabase::getClassMap(const QString& key)
+{
+ if (newSinceMaps_.isEmpty() && newClassMaps_.isEmpty() && newQmlTypeMaps_.isEmpty())
+ processForest(&QDocDatabase::findAllSince);
+ NodeMapMap::const_iterator i = newClassMaps_.constFind(key);
+ if (i != newClassMaps_.constEnd())
+ return i.value();
+ return emptyNodeMap_;
+}
+
+/*!
+ Find the \a key in the map of new QML type maps, and return a
+ reference to the value, which is a NodeMap. If the \a key is not
+ found, return a reference to an empty NodeMap.
+ */
+const NodeMap& QDocDatabase::getQmlTypeMap(const QString& key)
+{
+ if (newSinceMaps_.isEmpty() && newClassMaps_.isEmpty() && newQmlTypeMaps_.isEmpty())
+ processForest(&QDocDatabase::findAllSince);
+ NodeMapMap::const_iterator i = newQmlTypeMaps_.constFind(key);
+ if (i != newQmlTypeMaps_.constEnd())
+ return i.value();
+ return emptyNodeMap_;
+}
+
+/*!
+ Find the \a key in the map of new \e {since} maps, and return
+ a reference to the value, which is a NodeMultiMap. If \a key
+ is not found, return a reference to an empty NodeMultiMap.
+ */
+const NodeMap& QDocDatabase::getSinceMap(const QString& key)
+{
+ if (newSinceMaps_.isEmpty() && newClassMaps_.isEmpty() && newQmlTypeMaps_.isEmpty())
+ processForest(&QDocDatabase::findAllSince);
+ NodeMultiMapMap::const_iterator i = newSinceMaps_.constFind(key);
+ if (i != newSinceMaps_.constEnd())
+ return i.value();
+ return emptyNodeMultiMap_;
+}
+
+/*!
+ Performs several housekeeping algorithms that create
+ certain data structures and resolve lots of links, prior
+ to generating documentation.
+ */
+void QDocDatabase::resolveIssues() {
+ primaryTreeRoot()->normalizeOverloads();
+ fixInheritance();
+ resolveProperties();
+ primaryTreeRoot()->makeUndocumentedChildrenInternal();
+ resolveQmlInheritance(primaryTreeRoot());
+ primaryTree()->resolveTargets(primaryTreeRoot());
+ primaryTree()->resolveCppToQmlLinks();
+ if (!Generator::singleExec()) {
+ QDocIndexFiles::qdocIndexFiles()->resolveRelates();
+ QDocIndexFiles::destroyQDocIndexFiles();
+ }
+ if (Generator::generating())
+ resolveNamespaces();
+}
+
+void QDocDatabase::resolveStuff()
+{
+ primaryTree()->resolveInheritance();
+ resolveQmlInheritance(primaryTreeRoot());
+ //primaryTree()->resolveTargets(primaryTreeRoot());
+ primaryTree()->resolveCppToQmlLinks();
+ primaryTree()->resolveUsingClauses();
+ resolveNamespaces();
+}
+
+/*!
+ */
+void QDocDatabase::resolveNamespaces()
+{
+ if (!namespaceIndex_.isEmpty())
+ return;
+ Tree* t = forest_.firstTree();
+ while (t) {
+ findAllNamespaces(t->root());
+ t = forest_.nextTree();
+ }
+ QList<QString> keys = nmm_.uniqueKeys();
+ foreach (const QString &s, keys) {
+ NamespaceNode* ns = 0;
+ QList<Node*> nodes = nmm_.values(s);
+ int count = nmm_.remove(s);
+ if (count > 1) {
+ foreach (Node* n, nodes) {
+ // Treat public namespaces from index trees as 'seen'
+ if (n->isNamespace() && (n->wasSeen() || (n->isIndexNode() && n->access() == Node::Public))) {
+ ns = static_cast<NamespaceNode*>(n);
+ ns->markSeen();
+ break;
+ }
+ }
+ }
+ else if (count == 1)
+ ns = static_cast<NamespaceNode*>(nodes.at(0));
+ if (ns && ns->wasSeen()) {
+ if (count >1) {
+ foreach (Node* n, nodes) {
+ if (n->isNamespace()) {
+ NamespaceNode* NS = static_cast<NamespaceNode*>(n);
+ if ((NS != ns) && !NS->childNodes().isEmpty()) {
+ const NodeList& children = NS->childNodes();
+ int i = children.size() - 1;
+ while (i >= 0) {
+ Node* child = children.at(i--);
+ if (!child)
+ continue;
+ if (!child->isClass()
+ && !child->isQmlType()
+ && !child->isNamespace()) {
+ NS->removeChild(child);
+ ns->addChild(child);
+ }
+ else {
+ NS->setStatus(Node::Intermediate);
+ NS->setAccess(Node::Public);
+ ns->addOrphan(child);
+ }
+ }
+ }
+ }
+ }
+ }
+ namespaceIndex_.insert(ns->name(), ns);
+ }
+ }
+}
+#if 0
+/*!
+ */
+const Node* QDocDatabase::findFunctionNode(const QString& target,
+ const Node* relative,
+ Node::Genus genus)
+{
+ return forest_.findFunctionNode(target, relative, genus);
+}
+#endif
+/*!
+ This function is called for autolinking to a \a type,
+ which could be a function return type or a parameter
+ type. The tree node that represents the \a type is
+ returned. All the trees are searched until a match is
+ found. When searching the primary tree, the search
+ begins at \a relative and proceeds up the parent chain.
+ When searching the index trees, the search begins at the
+ root.
+ */
+const Node* QDocDatabase::findTypeNode(const QString& type, const Node* relative)
+{
+ QStringList path = type.split("::");
+ if ((path.size() == 1) && (path.at(0)[0].isLower() || path.at(0) == QString("T"))) {
+ NodeMap::iterator i = typeNodeMap_.find(path.at(0));
+ if (i != typeNodeMap_.end())
+ return i.value();
+ }
+ return forest_.findTypeNode(path, relative);
+}
+
+/*!
+ Finds the node that will generate the documentation that
+ contains the \a target and returns a pointer to it.
+
+ Can this be improved by using the target map in Tree?
+ */
+const Node* QDocDatabase::findNodeForTarget(const QString& target, const Node* relative)
+{
+ const Node* node = 0;
+ if (target.isEmpty())
+ node = relative;
+ else if (target.endsWith(".html"))
+ node = findNodeByNameAndType(QStringList(target), Node::Document);
+ else {
+ QStringList path = target.split("::");
+ int flags = SearchBaseClasses | SearchEnumValues; // | NonFunction;
+ foreach (Tree* t, searchOrder()) {
+ const Node* n = t->findNode(path, relative, flags, Node::DontCare);
+ if (n)
+ return n;
+ relative = 0;
+ }
+ node = findDocumentNodeByTitle(target);
+ }
+ return node;
+}
+
+/*!
+ For each QML Type node in the tree beginning at \a root,
+ if it has a QML base type name but its QML base type node
+ pointer is 0, use the QML base type name to look up the
+ base type node. If the node is found in the tree, set the
+ node's QML base type node pointer.
+ */
+void QDocDatabase::resolveQmlInheritance(Aggregate* root)
+{
+ NodeMap previousSearches;
+ // Do we need recursion?
+ foreach (Node* child, root->childNodes()) {
+ if (child->isQmlType() || child->isJsType()) {
+ QmlTypeNode* qcn = static_cast<QmlTypeNode*>(child);
+ if (qcn->qmlBaseNodeNotSet() && !qcn->qmlBaseName().isEmpty()) {
+ QmlTypeNode* bqcn = static_cast<QmlTypeNode*>(previousSearches.value(qcn->qmlBaseName()));
+ if (bqcn)
+ qcn->setQmlBaseNode(bqcn);
+ else {
+ if (!qcn->importList().isEmpty()) {
+ const ImportList& imports = qcn->importList();
+ for (int i=0; i<imports.size(); ++i) {
+ bqcn = findQmlType(imports[i], qcn->qmlBaseName());
+ if (bqcn)
+ break;
+ }
+ }
+ if (bqcn == 0) {
+ bqcn = findQmlType(QString(), qcn->qmlBaseName());
+ }
+ if (bqcn) {
+ qcn->setQmlBaseNode(bqcn);
+ previousSearches.insert(qcn->qmlBaseName(), bqcn);
+ }
+#if 0
+ else {
+ qDebug() << "Temporary error message (ignore): UNABLE to resolve QML base type:"
+ << qcn->qmlBaseName() << "for QML type:" << qcn->name();
+ }
+#endif
+ }
+ }
+ }
+ }
+}
+
+/*!
+ Generates a tag file and writes it to \a name.
+ */
+void QDocDatabase::generateTagFile(const QString& name, Generator* g)
+{
+ if (!name.isEmpty()) {
+ QDocTagFiles::qdocTagFiles()->generateTagFile(name, g);
+ QDocTagFiles::destroyQDocTagFiles();
+ }
+}
+
+/*!
+ Reads and parses the qdoc index files listed in \a t.
+ */
+void QDocDatabase::readIndexes(const QStringList& t)
+{
+ QStringList indexFiles;
+ foreach (const QString& f, t) {
+ QString fn = f.mid(f.lastIndexOf(QChar('/'))+1);
+ if (!isLoaded(fn))
+ indexFiles << f;
+ else
+ qDebug() << "This index file is already in memory:" << f;
+ }
+ QDocIndexFiles::qdocIndexFiles()->readIndexes(indexFiles);
+}
+
+/*!
+ Generates a qdoc index file and write it to \a fileName. The
+ index file is generated with the parameters \a url, \a title,
+ \a g, and \a generateInternalNodes.
+ */
+void QDocDatabase::generateIndex(const QString& fileName,
+ const QString& url,
+ const QString& title,
+ Generator* g,
+ bool generateInternalNodes)
+{
+ QString t = fileName.mid(fileName.lastIndexOf(QChar('/'))+1);
+ primaryTree()->setIndexFileName(t);
+ QDocIndexFiles::qdocIndexFiles()->generateIndex(fileName, url, title, g, generateInternalNodes);
+ QDocIndexFiles::destroyQDocIndexFiles();
+}
+
+/*!
+ If there are open namespaces, search for the function node
+ having the same function name as the \a clone node in each
+ open namespace. The \a parentPath is a portion of the path
+ name provided with the function name at the point of
+ reference. \a parentPath is usually a class name. Return
+ the pointer to the function node if one is found in an
+ open namespace. Otherwise return 0.
+
+ This open namespace concept is of dubious value and might
+ be removed.
+ */
+FunctionNode* QDocDatabase::findNodeInOpenNamespace(const QStringList& parentPath,
+ const FunctionNode* clone)
+{
+ FunctionNode* fn = 0;
+ if (!openNamespaces_.isEmpty()) {
+ foreach (const QString& t, openNamespaces_) {
+ QStringList path = t.split("::") + parentPath;
+ fn = findFunctionNode(path, clone);
+ if (fn)
+ break;
+ }
+ }
+ return fn;
+}
+
+/*!
+ Find a node of the specified \a type that is reached with
+ the specified \a path qualified with the name of one of the
+ open namespaces (might not be any open ones). If the node
+ is found in an open namespace, prefix \a path with the name
+ of the open namespace and "::" and return a pointer to the
+ node. Othewrwise return 0.
+
+ This function only searches in the current primary tree.
+ */
+Node* QDocDatabase::findNodeInOpenNamespace(QStringList& path, Node::NodeType type)
+{
+ if (path.isEmpty())
+ return 0;
+ Node* n = 0;
+ if (!openNamespaces_.isEmpty()) {
+ foreach (const QString& t, openNamespaces_) {
+ QStringList p;
+ if (t != path[0])
+ p = t.split("::") + path;
+ else
+ p = path;
+ n = primaryTree()->findNodeByNameAndType(p, type);
+ if (n) {
+ path = p;
+ break;
+ }
+ }
+ }
+ return n;
+}
+
+/*!
+ Finds all the collection nodes of the specified \a genus
+ into the collection node map \a cnm. Nodes that match the
+ \a relative node are not included.
+ */
+void QDocDatabase::mergeCollections(Node::Genus genus, CNMap& cnm, const Node* relative)
+{
+ cnm.clear();
+ CNMultiMap cnmm;
+ foreach (Tree* t, searchOrder()) {
+ CNMap* m = t->getCollectionMap(genus);
+ if (m && !m->isEmpty()) {
+ CNMap::const_iterator i = m->cbegin();
+ while (i != m->cend()) {
+ if (!i.value()->isInternal())
+ cnmm.insert(i.key(), i.value());
+ ++i;
+ }
+ }
+ }
+ if (cnmm.isEmpty())
+ return;
+ QRegExp singleDigit("\\b([0-9])\\b");
+ QStringList keys = cnmm.uniqueKeys();
+ foreach (const QString &key, keys) {
+ QList<CollectionNode*> values = cnmm.values(key);
+ CollectionNode* n = 0;
+ foreach (CollectionNode* v, values) {
+ if (v && v->wasSeen() && (v != relative)) {
+ n = v;
+ break;
+ }
+ }
+ if (n) {
+ if (values.size() > 1) {
+ foreach (CollectionNode* v, values) {
+ if (v != n) {
+ // Allow multiple (major) versions of QML/JS modules
+ if (n->type() == Node::QmlModule
+ && n->logicalModuleIdentifier() != v->logicalModuleIdentifier()) {
+ if (v->wasSeen() && v != relative && !v->members().isEmpty())
+ cnm.insert(v->fullTitle().toLower(), v);
+ continue;
+ }
+ foreach (Node* t, v->members())
+ n->addMember(t);
+ }
+ }
+ }
+ if (!n->members().isEmpty()) {
+ QString sortKey = n->fullTitle().toLower();
+ if (sortKey.startsWith("the "))
+ sortKey.remove(0, 4);
+ sortKey.replace(singleDigit, "0\\1");
+ cnm.insert(sortKey, n);
+ }
+ }
+ }
+}
+
+/*!
+ Finds all the collection nodes with the same name
+ and genus as \a c and merges their members into the
+ members list of \a c.
+
+ For QML and JS modules, the merge is done only if
+ the module identifier matches between the nodes, to avoid
+ merging modules with different (major) versions.
+ */
+void QDocDatabase::mergeCollections(CollectionNode* c)
+{
+ foreach (Tree* t, searchOrder()) {
+ CollectionNode* cn = t->getCollection(c->name(), c->genus());
+ if (cn && cn != c) {
+ if (cn->type() == Node::QmlModule
+ && cn->logicalModuleIdentifier() != c->logicalModuleIdentifier())
+ continue;
+ foreach (Node* n, cn->members())
+ c->addMember(n);
+ }
+ }
+}
+
+/*!
+ Searches for the node that matches the path in \a atom. The
+ \a relative node is used if the first leg of the path is
+ empty, i.e. if the path begins with a hashtag. The function
+ also sets \a ref if there remains an unused leg in the path
+ after the node is found. The node is returned as well as the
+ \a ref. If the returned node pointer is null, \a ref is not
+ valid.
+ */
+const Node* QDocDatabase::findNodeForAtom(const Atom* a, const Node* relative, QString& ref)
+{
+ const Node* node = 0;
+
+ Atom* atom = const_cast<Atom*>(a);
+ QStringList targetPath = atom->string().split(QLatin1Char('#'));
+ QString first = targetPath.first().trimmed();
+ if (Generator::debugging())
+ qDebug() << " first:" << first;
+
+ Tree* domain = 0;
+ Node::Genus genus = Node::DontCare;
+ // Reserved for future use
+ //Node::NodeType goal = Node::NoType;
+
+ if (atom->isLinkAtom()) {
+ domain = atom->domain();
+ genus = atom->genus();
+ // Reserved for future use
+ //goal = atom->goal();
+ }
+
+ if (first.isEmpty())
+ node = relative; // search for a target on the current page.
+ else if (domain) {
+ if (first.endsWith(".html"))
+ node = domain->findNodeByNameAndType(QStringList(first), Node::Document);
+ else if (first.endsWith(QChar(')'))) {
+ QString function, params;
+ int length = first.length();
+ int position = first.lastIndexOf(QChar('('));
+ params = first.mid(position+1, length-position-2);
+ function = first.left(position);
+ node = domain->findFunctionNode(function, params, 0, genus);
+ }
+ if (!node) {
+ int flags = SearchBaseClasses | SearchEnumValues;
+ QStringList nodePath = first.split("::");
+ QString target;
+ targetPath.removeFirst();
+ if (!targetPath.isEmpty())
+ target = targetPath.takeFirst();
+ if (relative && relative->tree()->physicalModuleName() != domain->physicalModuleName())
+ relative = 0;
+ return domain->findNodeForTarget(nodePath, target, relative, flags, genus, ref);
+ }
+ }
+ else {
+ if (first.endsWith(".html"))
+ node = findNodeByNameAndType(QStringList(first), Node::Document);
+ else if (first.endsWith(QChar(')'))) {
+ node = findFunctionNode(first, relative, genus);
+ if (Generator::debugging())
+ qDebug() << " node:" << node;
+ }
+ if (!node)
+ return findNodeForTarget(targetPath, relative, genus, ref);
+ }
+
+ if (node && ref.isEmpty()) {
+ if (!node->url().isEmpty())
+ return node;
+ targetPath.removeFirst();
+ if (!targetPath.isEmpty()) {
+ ref = node->root()->tree()->getRef(targetPath.first(), node);
+ if (ref.isEmpty())
+ node = 0;
+ }
+ }
+ return node;
+}
+
+QT_END_NAMESPACE