mirror of https://github.com/qt/qtbase.git
1042 lines
35 KiB
C++
1042 lines
35 KiB
C++
/****************************************************************************
|
|
**
|
|
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
|
** Contact: http://www.qt-project.org/legal
|
|
**
|
|
** This file is part of the tools applications of the Qt Toolkit.
|
|
**
|
|
** $QT_BEGIN_LICENSE:LGPL$
|
|
** 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 Digia. For licensing terms and
|
|
** conditions see http://qt.digia.com/licensing. For further information
|
|
** use the contact form at http://qt.digia.com/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 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, Digia gives you certain additional
|
|
** rights. These rights are described in the Digia 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.
|
|
**
|
|
**
|
|
** $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_;
|
|
|
|
/*! \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::groups() const
|
|
Returns a const reference to the collection of all
|
|
group nodes.
|
|
*/
|
|
|
|
/*!
|
|
\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.
|
|
*/
|
|
|
|
/*!
|
|
Find the group node named \a name and return a pointer
|
|
to it. If a matching node is not found, return 0.
|
|
*/
|
|
DocNode* QDocDatabase::getGroup(const QString& name)
|
|
{
|
|
DocNodeMap::const_iterator i = groups_.find(name);
|
|
if (i != groups_.end())
|
|
return i.value();
|
|
return 0;
|
|
}
|
|
|
|
/*!
|
|
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}.
|
|
*/
|
|
DocNode* QDocDatabase::findGroup(const QString& name)
|
|
{
|
|
DocNodeMap::const_iterator i = groups_.find(name);
|
|
if (i != groups_.end())
|
|
return i.value();
|
|
DocNode* dn = new DocNode(tree_->root(), name, Node::Group, Node::OverviewPage);
|
|
dn->markNotSeen();
|
|
groups_.insert(name,dn);
|
|
if (!masterMap_.contains(name,dn))
|
|
masterMap_.insert(name,dn);
|
|
return dn;
|
|
}
|
|
|
|
/*!
|
|
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}.
|
|
*/
|
|
DocNode* QDocDatabase::findModule(const QString& name)
|
|
{
|
|
DocNodeMap::const_iterator i = modules_.find(name);
|
|
if (i != modules_.end())
|
|
return i.value();
|
|
DocNode* dn = new DocNode(tree_->root(), name, Node::Module, Node::OverviewPage);
|
|
dn->markNotSeen();
|
|
modules_.insert(name,dn);
|
|
if (!masterMap_.contains(name,dn))
|
|
masterMap_.insert(name,dn);
|
|
return dn;
|
|
}
|
|
|
|
/*!
|
|
Find the QML module node named \a name and return a pointer
|
|
to it. If a matching node is not found, add a new QML module
|
|
node named \a name and return a pointer to that one.
|
|
|
|
If a new QML module node is added, its parent is the tree root,
|
|
and the new QML module node is marked \e{not seen}.
|
|
*/
|
|
DocNode* QDocDatabase::findQmlModule(const QString& name)
|
|
{
|
|
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))
|
|
return qmlModules_.value(qmid);
|
|
dn = new DocNode(tree_->root(), name, Node::QmlModule, Node::OverviewPage);
|
|
dn->markNotSeen();
|
|
dn->setQmlModuleInfo(name);
|
|
qmlModules_.insert(qmid,dn);
|
|
masterMap_.insert(qmid,dn);
|
|
masterMap_.insert(dn->name(),dn);
|
|
return dn;
|
|
}
|
|
|
|
/*!
|
|
Looks up the group node named \a name in the collection
|
|
of all group nodes. If a match is found, a pointer to the
|
|
node is returned. Otherwise, a new group node named \a name
|
|
is created and inserted into the collection, and the pointer
|
|
to that node is returned. The group node is marked \e{seen}
|
|
in either case.
|
|
*/
|
|
DocNode* QDocDatabase::addGroup(const QString& name)
|
|
{
|
|
DocNode* group = findGroup(name);
|
|
group->markSeen();
|
|
return group;
|
|
}
|
|
|
|
/*!
|
|
Looks up the module node named \a name in the collection
|
|
of all module nodes. If a match is found, a pointer to the
|
|
node is returned. Otherwise, a new module node named \a name
|
|
is created and inserted into the collection, and the pointer
|
|
to that node is returned. The module node is marked \e{seen}
|
|
in either case.
|
|
*/
|
|
DocNode* QDocDatabase::addModule(const QString& name)
|
|
{
|
|
DocNode* module = findModule(name);
|
|
module->markSeen();
|
|
return module;
|
|
}
|
|
|
|
/*!
|
|
Looks up the QML module node named \a name in the collection
|
|
of all QML module nodes. If a match is found, a pointer to the
|
|
node is returned. Otherwise, a new QML module node named \a name
|
|
is created and inserted into the collection, and the pointer
|
|
to that node is returned. The QML module node is marked \e{seen}
|
|
in either case.
|
|
*/
|
|
DocNode* QDocDatabase::addQmlModule(const QString& name)
|
|
{
|
|
DocNode* qmlModule = findQmlModule(name);
|
|
qmlModule->markSeen();
|
|
return qmlModule;
|
|
}
|
|
|
|
/*!
|
|
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.
|
|
*/
|
|
DocNode* QDocDatabase::addToGroup(const QString& name, Node* node)
|
|
{
|
|
DocNode* dn = findGroup(name);
|
|
dn->addMember(node);
|
|
node->addMember(dn);
|
|
return dn;
|
|
}
|
|
|
|
/*!
|
|
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.
|
|
*/
|
|
DocNode* QDocDatabase::addToModule(const QString& name, Node* node)
|
|
{
|
|
DocNode* dn = findModule(name);
|
|
dn->addMember(node);
|
|
node->setModuleName(name);
|
|
return dn;
|
|
}
|
|
|
|
/*!
|
|
Looks up the QML module named \a name. If it isn't there,
|
|
create it. Then append \a node to the QML module's member
|
|
list. The parent of \a node is not changed by this function.
|
|
Returns a pointer to the QML module node.
|
|
*/
|
|
DocNode* QDocDatabase::addToQmlModule(const QString& name, Node* node)
|
|
{
|
|
QString longQmid, shortQmid;
|
|
QStringList dotSplit;
|
|
QStringList blankSplit = name.split(QLatin1Char(' '));
|
|
if (blankSplit.size() > 1) {
|
|
longQmid = blankSplit[0] + blankSplit[1];
|
|
dotSplit = blankSplit[1].split(QLatin1Char('.'));
|
|
shortQmid = blankSplit[0] + dotSplit[0];
|
|
}
|
|
DocNode* dn = findQmlModule(name);
|
|
dn->addMember(node);
|
|
node->setQmlModuleInfo(name);
|
|
if (node->subType() == Node::QmlClass) {
|
|
QmlClassNode* n = static_cast<QmlClassNode*>(node);
|
|
QString key = longQmid + "::" + node->name();
|
|
for (int i=0; i<2; ++i) {
|
|
if (!qmlTypeMap_.contains(key))
|
|
qmlTypeMap_.insert(key,n);
|
|
if (!masterMap_.contains(key))
|
|
masterMap_.insert(key,node);
|
|
if (!masterMap_.contains(node->name(),node))
|
|
masterMap_.insert(node->name(),node);
|
|
key = shortQmid + "::" + node->name();
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
|
|
/*!
|
|
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.
|
|
*/
|
|
QmlClassNode* QDocDatabase::findQmlType(const ImportRec& import, const QString& name) const
|
|
{
|
|
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 qmid = qmName + import.version_;
|
|
QString qualifiedName = qmid + "::" + dotSplit[i];
|
|
QmlClassNode* qcn = qmlTypeMap_.value(qualifiedName);
|
|
if (qcn) {
|
|
return qcn;
|
|
}
|
|
if (import.version_.size() > 1) {
|
|
int dot = import.version_.lastIndexOf(QChar('.'));
|
|
if (dot > 0) {
|
|
qmid = import.name_ + import.version_.left(dot);
|
|
qualifiedName = qmid + "::" + dotSplit[i];
|
|
qcn = qmlTypeMap_.value(qualifiedName);
|
|
if (qcn) {
|
|
return qcn;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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) {
|
|
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()) {
|
|
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()) {
|
|
findAllNamespaces(static_cast<const InnerNode *>(*c));
|
|
if ((*c)->type() == Node::Namespace) {
|
|
// Ensure that the namespace's name is not empty (the root
|
|
// namespace has no name).
|
|
if (!(*c)->name().isEmpty())
|
|
namespaceIndex_.insert((*c)->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)->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)->subType() == Node::QmlClass) {
|
|
// 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)->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()->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() {
|
|
resolveQmlInheritance(treeRoot());
|
|
resolveTargets(treeRoot());
|
|
tree_->resolveCppToQmlLinks();
|
|
}
|
|
|
|
/*!
|
|
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, 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 = SearchBaseClasses | SearchEnumValues | NonFunction;
|
|
node = tree_->findNode(path, relative, flags, self);
|
|
}
|
|
return node;
|
|
}
|
|
|
|
/*!
|
|
Finds the node that will generate the documentation that
|
|
contains the \a target and returns a pointer to it.
|
|
*/
|
|
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 = tree_->root()->findChildNodeByNameAndType(target, Node::Document);
|
|
else {
|
|
node = resolveTarget(target, relative);
|
|
if (!node)
|
|
node = findDocNodeByTitle(target, relative);
|
|
}
|
|
return node;
|
|
}
|
|
|
|
/*!
|
|
Inserts a new target into the target table with the specified
|
|
\a name, \a node, and \a priority.
|
|
*/
|
|
void QDocDatabase::insertTarget(const QString& name, TargetRec::Type type, Node* node, int priority)
|
|
{
|
|
TargetRec target;
|
|
target.type_ = type;
|
|
target.node_ = node;
|
|
target.priority_ = priority;
|
|
Atom a = Atom(Atom::Target, name);
|
|
target.ref_ = refForAtom(&a);
|
|
targetRecMultiMap_.insert(name, target);
|
|
}
|
|
|
|
/*!
|
|
This function searches for a \a target anchor node. If it
|
|
finds one, it sets \a ref and returns the found node.
|
|
*/
|
|
const Node*
|
|
QDocDatabase::findUnambiguousTarget(const QString& target, QString& ref, const Node* relative)
|
|
{
|
|
TargetRec bestTarget;
|
|
int numBestTargets = 0;
|
|
QList<TargetRec> bestTargetList;
|
|
|
|
QString key = Doc::canonicalTitle(target);
|
|
TargetRecMultiMap::iterator i = targetRecMultiMap_.find(key);
|
|
while (i != targetRecMultiMap_.end()) {
|
|
if (i.key() != key)
|
|
break;
|
|
const TargetRec& candidate = i.value();
|
|
if (candidate.priority_ < bestTarget.priority_) {
|
|
bestTarget = candidate;
|
|
bestTargetList.clear();
|
|
bestTargetList.append(candidate);
|
|
numBestTargets = 1;
|
|
} else if (candidate.priority_ == bestTarget.priority_) {
|
|
bestTargetList.append(candidate);
|
|
++numBestTargets;
|
|
}
|
|
++i;
|
|
}
|
|
if (numBestTargets > 0) {
|
|
if (numBestTargets == 1) {
|
|
ref = bestTarget.ref_;
|
|
return bestTarget.node_;
|
|
}
|
|
else if (bestTargetList.size() > 1) {
|
|
if (relative && !relative->qmlModuleIdentifier().isEmpty()) {
|
|
for (int i=0; i<bestTargetList.size(); ++i) {
|
|
const Node* n = bestTargetList.at(i).node_;
|
|
if (n && relative->qmlModuleIdentifier() == n->qmlModuleIdentifier()) {
|
|
ref = bestTargetList.at(i).ref_;
|
|
return n;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ref.clear();
|
|
return 0;
|
|
}
|
|
|
|
/*!
|
|
This function searches for a node with the specified \a title.
|
|
If \a relative node is provided, it is used to disambiguate if
|
|
it has a QML module identifier.
|
|
*/
|
|
const DocNode* QDocDatabase::findDocNodeByTitle(const QString& title, const Node* relative) const
|
|
{
|
|
QString key = Doc::canonicalTitle(title);
|
|
DocNodeMultiMap::const_iterator i = docNodesByTitle_.constFind(key);
|
|
if (i != docNodesByTitle_.constEnd()) {
|
|
if (relative && !relative->qmlModuleIdentifier().isEmpty()) {
|
|
const DocNode* dn = i.value();
|
|
InnerNode* parent = dn->parent();
|
|
if (parent && parent->type() == Node::Document && parent->subType() == Node::Collision) {
|
|
const NodeList& nl = parent->childNodes();
|
|
NodeList::ConstIterator it = nl.constBegin();
|
|
while (it != nl.constEnd()) {
|
|
if ((*it)->qmlModuleIdentifier() == relative->qmlModuleIdentifier()) {
|
|
/*
|
|
By returning here, we avoid printing all the duplicate
|
|
header warnings, which are not really duplicates now,
|
|
because of the QML module identifier being used as a
|
|
namespace qualifier.
|
|
*/
|
|
dn = static_cast<const DocNode*>(*it);
|
|
return dn;
|
|
}
|
|
++it;
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
Reporting all these duplicate section titles is probably
|
|
overkill. We should report the duplicate file and let
|
|
that suffice.
|
|
*/
|
|
DocNodeMultiMap::const_iterator j = i;
|
|
++j;
|
|
if (j != docNodesByTitle_.constEnd() && j.key() == i.key()) {
|
|
QList<Location> internalLocations;
|
|
while (j != docNodesByTitle_.constEnd()) {
|
|
if (j.key() == i.key() && j.value()->url().isEmpty())
|
|
internalLocations.append(j.value()->location());
|
|
++j;
|
|
}
|
|
if (internalLocations.size() > 0) {
|
|
i.value()->location().warning(tr("This page exists in more than one file: \"%1\"").arg(title));
|
|
foreach (const Location &location, internalLocations)
|
|
location.warning(tr("[It also exists here]"));
|
|
}
|
|
}
|
|
return i.value();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*!
|
|
This function searches for a node with a canonical title
|
|
constructed from \a target. If the node it finds is \a node,
|
|
it returns the ref from that node. Otherwise it returns an
|
|
empty string.
|
|
*/
|
|
QString QDocDatabase::findTarget(const QString& target, const Node* node) const
|
|
{
|
|
QString key = Doc::canonicalTitle(target);
|
|
TargetRecMultiMap::const_iterator i = targetRecMultiMap_.constFind(key);
|
|
|
|
if (i != targetRecMultiMap_.constEnd()) {
|
|
do {
|
|
if (i.value().node_ == node)
|
|
return i.value().ref_;
|
|
++i;
|
|
} while (i != targetRecMultiMap_.constEnd() && i.key() == key);
|
|
}
|
|
return QString();
|
|
}
|
|
|
|
/*!
|
|
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(InnerNode* root)
|
|
{
|
|
// Dop we need recursion?
|
|
foreach (Node* child, root->childNodes()) {
|
|
if (child->type() == Node::Document && child->subType() == Node::QmlClass) {
|
|
QmlClassNode* qcn = static_cast<QmlClassNode*>(child);
|
|
if ((qcn->qmlBaseNode() == 0) && !qcn->qmlBaseName().isEmpty()) {
|
|
QmlClassNode* bqcn = 0;
|
|
if (qcn->qmlBaseName().contains("::")) {
|
|
bqcn = qmlTypeMap_.value(qcn->qmlBaseName());
|
|
}
|
|
else {
|
|
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);
|
|
}
|
|
#if 0
|
|
else {
|
|
qDebug() << "Temporary error message (ignore): UNABLE to resolve QML base type:"
|
|
<< qcn->qmlBaseName() << "for QML type:" << qcn->name();
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
void QDocDatabase::resolveQmlInheritance(InnerNode* root)
|
|
{
|
|
// Dop we need recursion?
|
|
foreach (Node* child, root->childNodes()) {
|
|
if (child->type() == Node::Document && child->subType() == Node::QmlClass) {
|
|
QmlClassNode* qcn = static_cast<QmlClassNode*>(child);
|
|
if ((qcn->qmlBaseNode() == 0) && !qcn->qmlBaseName().isEmpty()) {
|
|
QmlClassNode* bqcn = findQmlType(QString(), qcn->qmlBaseName());
|
|
if (bqcn) {
|
|
qcn->setQmlBaseNode(bqcn);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*!
|
|
*/
|
|
void QDocDatabase::resolveTargets(InnerNode* root)
|
|
{
|
|
// need recursion
|
|
|
|
foreach (Node* child, root->childNodes()) {
|
|
if (child->type() == Node::Document) {
|
|
DocNode* node = static_cast<DocNode*>(child);
|
|
if (!node->title().isEmpty()) {
|
|
QString key = Doc::canonicalTitle(node->title());
|
|
docNodesByTitle_.insert(key, node);
|
|
}
|
|
if (node->subType() == Node::Collision) {
|
|
resolveTargets(node);
|
|
}
|
|
}
|
|
|
|
if (child->doc().hasTableOfContents()) {
|
|
const QList<Atom*>& toc = child->doc().tableOfContents();
|
|
TargetRec target;
|
|
target.node_ = child;
|
|
target.priority_ = 3;
|
|
|
|
for (int i = 0; i < toc.size(); ++i) {
|
|
target.ref_ = refForAtom(toc.at(i));
|
|
QString title = Text::sectionHeading(toc.at(i)).toString();
|
|
if (!title.isEmpty()) {
|
|
QString key = Doc::canonicalTitle(title);
|
|
targetRecMultiMap_.insert(key, target);
|
|
}
|
|
}
|
|
}
|
|
if (child->doc().hasKeywords()) {
|
|
const QList<Atom*>& keywords = child->doc().keywords();
|
|
TargetRec target;
|
|
target.node_ = child;
|
|
target.priority_ = 1;
|
|
|
|
for (int i = 0; i < keywords.size(); ++i) {
|
|
target.ref_ = refForAtom(keywords.at(i));
|
|
QString key = Doc::canonicalTitle(keywords.at(i)->string());
|
|
targetRecMultiMap_.insert(key, target);
|
|
}
|
|
}
|
|
if (child->doc().hasTargets()) {
|
|
const QList<Atom*>& toc = child->doc().targets();
|
|
TargetRec target;
|
|
target.node_ = child;
|
|
target.priority_ = 2;
|
|
|
|
for (int i = 0; i < toc.size(); ++i) {
|
|
target.ref_ = refForAtom(toc.at(i));
|
|
QString key = Doc::canonicalTitle(toc.at(i)->string());
|
|
targetRecMultiMap_.insert(key, target);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*!
|
|
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 indexFiles.
|
|
*/
|
|
void QDocDatabase::readIndexes(const QStringList& indexFiles)
|
|
{
|
|
QDocIndexFiles::qdocIndexFiles()->readIndexes(indexFiles);
|
|
QDocIndexFiles::destroyQDocIndexFiles();
|
|
}
|
|
|
|
/*!
|
|
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)
|
|
{
|
|
QDocIndexFiles::qdocIndexFiles()->generateIndex(fileName, url, title, g, generateInternalNodes);
|
|
QDocIndexFiles::destroyQDocIndexFiles();
|
|
}
|
|
|
|
QString QDocDatabase::refForAtom(const Atom* atom)
|
|
{
|
|
if (atom) {
|
|
if (atom->type() == Atom::SectionLeft)
|
|
return Doc::canonicalTitle(Text::sectionHeading(atom).toString());
|
|
if (atom->type() == Atom::Target)
|
|
return Doc::canonicalTitle(atom->string());
|
|
}
|
|
return QString();
|
|
}
|
|
|
|
QT_END_NAMESPACE
|