summaryrefslogtreecommitdiffstats
path: root/src/tools/qdoc/qdocdatabase.cpp
diff options
context:
space:
mode:
authorMartin Smith <martin.smith@nokia.com>2012-09-13 11:38:45 +0200
committerQt by Nokia <qt-info@nokia.com>2012-09-14 15:23:15 +0200
commit14f7eb86ca2275d91f284279af5f77205d4ae3c0 (patch)
treeed4e91d6422dd814ac3e81739ad4a3b55bf050c7 /src/tools/qdoc/qdocdatabase.cpp
parent817a4474676b30a964de476d26bd70ddba3d379a (diff)
qdoc: Refactoring of qdoc data structures
This commit is the beginning of a significant overhaul of qdoc. A new class, QDocDatabase, is added, which will eventually encapsulate all the data structures used by qdoc. In this commit, the Tree class is made private and only accessible from QDocDatabase. Several maps structures are also moved into QDocDatabase from other classes. Much dead code and unused parameters were removed. Further simplification will follow. Change-Id: I237411c50f3ced0d2fc8d3b0fbfdf4e55880f8e9 Reviewed-by: Qt Doc Bot <qt_docbot@qt-project.org> Reviewed-by: Lars Knoll <lars.knoll@nokia.com> Reviewed-by: Jerome Pasion <jerome.pasion@nokia.com>
Diffstat (limited to 'src/tools/qdoc/qdocdatabase.cpp')
-rw-r--r--src/tools/qdoc/qdocdatabase.cpp638
1 files changed, 638 insertions, 0 deletions
diff --git a/src/tools/qdoc/qdocdatabase.cpp b/src/tools/qdoc/qdocdatabase.cpp
new file mode 100644
index 0000000000..eabab2e727
--- /dev/null
+++ b/src/tools/qdoc/qdocdatabase.cpp
@@ -0,0 +1,638 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "tree.h"
+#include "qdocdatabase.h"
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+static NodeMap emptyNodeMap_;
+static NodeMultiMap emptyNodeMultiMap_;
+
+/*! \class QDocDatabase
+ */
+
+QDocDatabase* QDocDatabase::qdocDB_ = NULL;
+
+/*!
+ Constructs the singleton qdoc database object.
+ It constructs a singleton Tree object with this
+ qdoc database pointer.
+ */
+QDocDatabase::QDocDatabase()
+{
+ tree_ = new Tree(this);
+}
+
+/*!
+ Destroys the qdoc database object. This requires deleting
+ the tree of nodes, which deletes each node.
+ */
+QDocDatabase::~QDocDatabase()
+{
+ masterMap_.clear();
+ delete tree_;
+}
+
+/*! \fn Tree* QDocDatabase::tree()
+ Returns the pointer to the tree. This function is for compatibility
+ with the current qdoc. It will be removed when the QDocDatabase class
+ replaces the current structures.
+ */
+
+/*!
+ 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;
+ return qdocDB_;
+}
+
+/*!
+ Destroys the singleton.
+ */
+void QDocDatabase::destroyQdocDB()
+{
+ if (qdocDB_) {
+ delete qdocDB_;
+ qdocDB_ = 0;
+ }
+}
+
+/*!
+ \fn const DocNodeMap& QDocDatabase::modules() const
+ Returns a const reference to the collection of all
+ module nodes.
+*/
+
+/*!
+ \fn const DocNodeMap& QDocDatabase::qmlModules() const
+ Returns a const reference to the collection of all
+ QML module nodes.
+*/
+
+/*!
+ Looks up the module node named \a name in the collection
+ of all module nodes. If a match is found, a pointer to the
+ node is returned. Otherwise, a new module node named \a name
+ is created and inserted into the collection, and the pointer
+ to that node is returned.
+ */
+DocNode* QDocDatabase::addModule(const QString& name)
+{
+ return findModule(name,true);
+}
+
+/*!
+ Looks up the QML module node named \a name in the collection
+ of all QML module nodes. If a match is found, a pointer to the
+ node is returned. Otherwise, a new QML module node named \a name
+ is created and inserted into the collection, and the pointer
+ to that node is returned.
+ */
+DocNode* QDocDatabase::addQmlModule(const QString& name)
+{
+ return findQmlModule(name,true);
+}
+
+/*!
+ Looks up the C++ module named \a moduleName. If it isn't
+ there, create it. Then append \a node to the module's child
+ list. The parent of \a node is not changed by this function.
+ Returns the module node.
+ */
+DocNode* QDocDatabase::addToModule(const QString& moduleName, Node* node)
+{
+ DocNode* dn = findModule(moduleName,true);
+ dn->addMember(node);
+ node->setModuleName(moduleName);
+ return dn;
+}
+
+/*!
+ Looks up the QML module named \a qmlModuleName. If it isn't
+ there, create it. Then append \a node to the module's child
+ list. The parent of \a node is not changed by this function.
+ Returns a pointer to the QML module node.
+ */
+DocNode* QDocDatabase::addToQmlModule(const QString& qmlModuleName, Node* node)
+{
+ DocNode* dn = findQmlModule(qmlModuleName,true);
+ dn->addMember(node);
+ node->setQmlModuleInfo(qmlModuleName);
+ if (node->subType() == Node::QmlClass) {
+ QString t = node->qmlModuleIdentifier() + "::" + node->name();
+ QmlClassNode* n = static_cast<QmlClassNode*>(node);
+ if (!qmlTypeMap_.contains(t))
+ qmlTypeMap_.insert(t,n);
+ if (!masterMap_.contains(t))
+ masterMap_.insert(t,node);
+ if (!masterMap_.contains(node->name(),node))
+ masterMap_.insert(node->name(),node);
+ }
+ return dn;
+}
+
+/*!
+ Find the module node named \a name and return a pointer
+ to it. If a matching node is not found and \a addIfNotFound
+ is true, add a new module node named \a name and return
+ a pointer to that one. Otherwise, return 0.
+
+ If a new module node is added, its parent is the tree root,
+ but the new module node is not added to the child list of the
+ tree root.
+ */
+DocNode* QDocDatabase::findModule(const QString& name, bool addIfNotFound)
+{
+ DocNodeMap::const_iterator i = modules_.find(name);
+ if (i != modules_.end()) {
+ return i.value();
+ }
+ if (addIfNotFound) {
+ DocNode* dn = new DocNode(tree_->root(), name, Node::Module, Node::OverviewPage);
+ modules_.insert(name,dn);
+ if (!masterMap_.contains(name,dn))
+ masterMap_.insert(name,dn);
+ return dn;
+ }
+ return 0;
+}
+
+/*!
+ Find the QML module node named \a name and return a pointer
+ to it. If a matching node is not found and \a addIfNotFound
+ is true, add a new QML module node named \a name and return
+ a pointer to that one. Otherwise, return 0.
+
+ If a new QML module node is added, its parent is the tree root,
+ but the new QML module node is not added to the child list of
+ the tree root.
+ */
+DocNode* QDocDatabase::findQmlModule(const QString& name, bool addIfNotFound)
+{
+ QStringList dotSplit;
+ QStringList blankSplit = name.split(QLatin1Char(' '));
+ QString qmid = blankSplit[0];
+ if (blankSplit.size() > 1) {
+ dotSplit = blankSplit[1].split(QLatin1Char('.'));
+ qmid += dotSplit[0];
+ }
+ DocNode* dn = 0;
+ if (qmlModules_.contains(qmid))
+ dn = qmlModules_.value(qmid);
+ else if (addIfNotFound) {
+ dn = new DocNode(tree_->root(), name, Node::QmlModule, Node::OverviewPage);
+ dn->setQmlModuleInfo(name);
+ qmlModules_.insert(qmid,dn);
+ masterMap_.insert(qmid,dn);
+ masterMap_.insert(dn->name(),dn);
+ }
+ return dn;
+}
+
+/*!
+ 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.
+ */
+QmlClassNode* QDocDatabase::findQmlType(const QString& qmid, const QString& name) const
+{
+ if (!qmid.isEmpty())
+ return qmlTypeMap_.value(qmid + "::" + name);
+
+ QStringList path(name);
+ Node* n = tree_->findNodeByNameAndType(path, Node::Document, Node::QmlClass, 0, true);
+ if (n) {
+ if (n->subType() == Node::QmlClass)
+ return static_cast<QmlClassNode*>(n);
+ else if (n->subType() == Node::Collision) {
+ NameCollisionNode* ncn;
+ ncn = static_cast<NameCollisionNode*>(n);
+ return static_cast<QmlClassNode*>(ncn->findAny(Node::Document,Node::QmlClass));
+ }
+ }
+ return 0;
+
+}
+
+/*!
+ For debugging only.
+ */
+void QDocDatabase::printModules() const
+{
+ DocNodeMap::const_iterator i = modules_.begin();
+ while (i != modules_.end()) {
+ qDebug() << " " << i.key();
+ ++i;
+ }
+}
+
+/*!
+ For debugging only.
+ */
+void QDocDatabase::printQmlModules() const
+{
+ DocNodeMap::const_iterator i = qmlModules_.begin();
+ while (i != qmlModules_.end()) {
+ qDebug() << " " << i.key();
+ ++i;
+ }
+}
+
+/*!
+ Traverses the database to construct useful data structures
+ for use when outputting certain significant collections of
+ things, C++ classes, QML types, "since" lists, and other
+ stuff.
+ */
+void QDocDatabase::buildCollections()
+{
+ nonCompatClasses_.clear();
+ mainClasses_.clear();
+ compatClasses_.clear();
+ obsoleteClasses_.clear();
+ funcIndex_.clear();
+ legaleseTexts_.clear();
+ serviceClasses_.clear();
+ qmlClasses_.clear();
+
+ findAllClasses(treeRoot());
+ findAllFunctions(treeRoot());
+ findAllLegaleseTexts(treeRoot());
+ findAllNamespaces(treeRoot());
+ findAllSince(treeRoot());
+}
+
+/*!
+ Finds all the C++ class nodes and QML type nodes and
+ sorts them into maps.
+ */
+void QDocDatabase::findAllClasses(const InnerNode* node)
+{
+ NodeList::const_iterator c = node->childNodes().constBegin();
+ while (c != node->childNodes().constEnd()) {
+ if ((*c)->access() != Node::Private && (*c)->url().isEmpty()) {
+ 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;
+
+ if (!(static_cast<const ClassNode *>(*c))->hideFromMainList()) {
+ if ((*c)->status() == Node::Compat) {
+ compatClasses_.insert(className, *c);
+ }
+ else if ((*c)->status() == Node::Obsolete) {
+ obsoleteClasses_.insert(className, *c);
+ }
+ else {
+ nonCompatClasses_.insert(className, *c);
+ if ((*c)->status() == Node::Main)
+ mainClasses_.insert(className, *c);
+ }
+ }
+
+ QString serviceName = (static_cast<const ClassNode *>(*c))->serviceName();
+ if (!serviceName.isEmpty())
+ serviceClasses_.insert(serviceName, *c);
+ }
+ else if ((*c)->type() == Node::Document &&
+ (*c)->subType() == Node::QmlClass &&
+ !(*c)->doc().isEmpty()) {
+ QString qmlTypeName = (*c)->name();
+ if (qmlTypeName.startsWith(QLatin1String("QML:")))
+ qmlClasses_.insert(qmlTypeName.mid(4),*c);
+ else
+ qmlClasses_.insert(qmlTypeName,*c);
+ }
+ else if ((*c)->isInnerNode()) {
+ findAllClasses(static_cast<InnerNode*>(*c));
+ }
+ }
+ ++c;
+ }
+}
+
+/*!
+ Finds all the function nodes
+ */
+void QDocDatabase::findAllFunctions(const InnerNode* node)
+{
+ NodeList::ConstIterator c = node->childNodes().constBegin();
+ while (c != node->childNodes().constEnd()) {
+ if ((*c)->access() != Node::Private) {
+ if ((*c)->isInnerNode() && (*c)->url().isEmpty()) {
+ findAllFunctions(static_cast<const InnerNode*>(*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(const InnerNode* 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)->isInnerNode())
+ findAllLegaleseTexts(static_cast<const InnerNode *>(*c));
+ }
+ ++c;
+ }
+}
+
+/*!
+ Finds all the namespace nodes and puts them in an index.
+ */
+void QDocDatabase::findAllNamespaces(const InnerNode* node)
+{
+ NodeList::ConstIterator c = node->childNodes().constBegin();
+ while (c != node->childNodes().constEnd()) {
+ if ((*c)->access() != Node::Private) {
+ if ((*c)->isInnerNode() && (*c)->url().isEmpty()) {
+ findAllNamespaces(static_cast<const InnerNode *>(*c));
+ if ((*c)->type() == Node::Namespace) {
+ const NamespaceNode* nspace = static_cast<const NamespaceNode *>(*c);
+ // Ensure that the namespace's name is not empty (the root
+ // namespace has no name).
+ if (!nspace->name().isEmpty()) {
+ namespaceIndex_.insert(nspace->name(), *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(const InnerNode* 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)->url().isEmpty()) {
+ if ((*child)->type() == Node::Class && !(*child)->doc().isEmpty()) {
+ // Insert classes into the since and class maps.
+ QString className = (*child)->name();
+ if ((*child)->parent() && (*child)->parent()->type() == Node::Namespace &&
+ !(*child)->parent()->name().isEmpty()) {
+ className = (*child)->parent()->name()+"::"+className;
+ }
+ nsmap.value().insert(className,(*child));
+ ncmap.value().insert(className,(*child));
+ }
+ else if ((*child)->subType() == Node::QmlClass) {
+ // Insert QML elements into the since and element maps.
+ QString className = (*child)->name();
+ if ((*child)->parent() && (*child)->parent()->type() == Node::Namespace &&
+ !(*child)->parent()->name().isEmpty()) {
+ className = (*child)->parent()->name()+"::"+className;
+ }
+ nsmap.value().insert(className,(*child));
+ nqcmap.value().insert(className,(*child));
+ }
+ else if ((*child)->type() == Node::QmlProperty) {
+ // 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()->type() == Node::Namespace &&
+ !(*child)->parent()->name().isEmpty()) {
+ name = (*child)->parent()->name()+"::"+name;
+ }
+ nsmap.value().insert(name,(*child));
+ }
+
+ // Recursively find child nodes with since commands.
+ if ((*child)->isInnerNode()) {
+ findAllSince(static_cast<InnerNode *>(*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) const
+{
+ 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) const
+{
+ 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 NodeMultiMap& QDocDatabase::getSinceMap(const QString& key) const
+{
+ 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() {
+ tree_->resolveGroups();
+ tree_->resolveTargets(tree_->root());
+ tree_->resolveCppToQmlLinks();
+}
+
+/*!
+ Look up group \a name in the map of groups. If found, populate
+ the node map \a group with the classes in the group that are
+ not marked internal or private.
+ */
+void QDocDatabase::getGroup(const QString& name, NodeMap& group) const
+{
+ group.clear();
+ NodeList values = tree_->groups().values(name);
+ for (int i=0; i<values.size(); ++i) {
+ const Node* n = values.at(i);
+ if ((n->status() != Node::Internal) && (n->access() != Node::Private)) {
+ group.insert(n->nameForLists(),n);
+ }
+ }
+}
+
+/*!
+ Searches the \a database for a node named \a target and returns
+ a pointer to it if found.
+ */
+const Node* QDocDatabase::resolveTarget(const QString& target,
+ const Node* relative,
+ const Node* self)
+{
+ const Node* node = 0;
+ if (target.endsWith("()")) {
+ QString funcName = target;
+ funcName.chop(2);
+ QStringList path = funcName.split("::");
+ const FunctionNode* fn = tree_->findFunctionNode(path, relative, Tree::SearchBaseClasses);
+ if (fn) {
+ /*
+ Why is this case not accepted?
+ */
+ if (fn->metaness() != FunctionNode::MacroWithoutParams)
+ node = fn;
+ }
+ }
+ else if (target.contains(QLatin1Char('#'))) {
+ // This error message is never printed; I think we can remove the case.
+ qDebug() << "qdoc: target case not handled:" << target;
+ }
+ else {
+ QStringList path = target.split("::");
+ int flags = Tree::SearchBaseClasses | Tree::SearchEnumValues | Tree::NonFunction;
+ node = tree_->findNode(path, relative, flags, self);
+ }
+ return node;
+}
+
+/*!
+ zzz
+ Is the atom necessary?
+ */
+const Node* QDocDatabase::findNodeForTarget(const QString& target,
+ const Node* relative,
+ const Atom* atom)
+{
+ const Node* node = 0;
+ if (target.isEmpty())
+ node = relative;
+ else if (target.endsWith(".html"))
+ node = tree_->root()->findChildNodeByNameAndType(target, Node::Document);
+ else {
+ node = resolveTarget(target, relative);
+ if (!node)
+ node = tree_->findDocNodeByTitle(target, relative);
+ if (!node && atom) {
+ qDebug() << "USING ATOM!";
+ node = tree_->findUnambiguousTarget(target, *const_cast<Atom**>(&atom), relative);
+ }
+ }
+ return node;
+}
+
+QT_END_NAMESPACE