blob: 2d9bce00641fa2c3af2cc85dd0fc6e767cc20143 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the tools applications of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qdoctagfiles.h"
#include "atom.h"
#include "doc.h"
#include "htmlgenerator.h"
#include "location.h"
#include "node.h"
#include "qdocdatabase.h"
#include "text.h"
#include <QtCore/qdebug.h>
#include <limits.h>
QT_BEGIN_NAMESPACE
/*!
\class QDocTagFiles
This class handles the generation of the qdoc tag file.
*/
QDocTagFiles *QDocTagFiles::qdocTagFiles_ = nullptr;
/*!
Constructs the singleton. \a qdb is the pointer to the
qdoc database that is used when reading and writing the
index files.
*/
QDocTagFiles::QDocTagFiles()
{
qdb_ = QDocDatabase::qdocDB();
}
/*!
Destroys the singleton QDocTagFiles.
*/
QDocTagFiles::~QDocTagFiles()
{
qdb_ = nullptr;
}
/*!
Creates the singleton. Allows only one instance of the class
to be created. Returns a pointer to the singleton.
*/
QDocTagFiles *QDocTagFiles::qdocTagFiles()
{
if (qdocTagFiles_ == nullptr)
qdocTagFiles_ = new QDocTagFiles;
return qdocTagFiles_;
}
/*!
Destroys the singleton.
*/
void QDocTagFiles::destroyQDocTagFiles()
{
if (qdocTagFiles_ != nullptr) {
delete qdocTagFiles_;
qdocTagFiles_ = nullptr;
}
}
/*!
Generate the tag file section with the given \a writer for the \a parent
node.
*/
void QDocTagFiles::generateTagFileCompounds(QXmlStreamWriter &writer, const Aggregate *parent)
{
const auto &nonFunctionList = const_cast<Aggregate *>(parent)->nonfunctionList();
for (const auto *node : nonFunctionList) {
if (!node->url().isEmpty() || node->isPrivate())
continue;
QString kind;
switch (node->nodeType()) {
case Node::Namespace:
kind = "namespace";
break;
case Node::Class:
case Node::Struct:
case Node::Union:
case Node::QmlType:
case Node::JsType:
kind = "class";
break;
default:
continue;
}
const Aggregate *aggregate = static_cast<const Aggregate *>(node);
QString access = "public";
if (node->isProtected())
access = "protected";
QString objName = node->name();
// Special case: only the root node should have an empty name.
if (objName.isEmpty() && node != qdb_->primaryTreeRoot())
continue;
// *** Write the starting tag for the element here. ***
writer.writeStartElement("compound");
writer.writeAttribute("kind", kind);
if (node->isClassNode()) {
writer.writeTextElement("name", node->fullDocumentName());
writer.writeTextElement("filename", gen_->fullDocumentLocation(node, false));
// Classes contain information about their base classes.
const ClassNode *classNode = static_cast<const ClassNode *>(node);
const QVector<RelatedClass> bases = classNode->baseClasses();
for (const auto &related : bases) {
ClassNode *n = related.node_;
if (n)
writer.writeTextElement("base", n->name());
}
// Recurse to write all members.
generateTagFileMembers(writer, aggregate);
writer.writeEndElement();
// Recurse to write all compounds.
generateTagFileCompounds(writer, aggregate);
} else {
writer.writeTextElement("name", node->fullDocumentName());
writer.writeTextElement("filename", gen_->fullDocumentLocation(node, false));
// Recurse to write all members.
generateTagFileMembers(writer, aggregate);
writer.writeEndElement();
// Recurse to write all compounds.
generateTagFileCompounds(writer, aggregate);
}
}
}
/*!
Writes all the members of the \a parent node with the \a writer.
The node represents a C++ class, namespace, etc.
*/
void QDocTagFiles::generateTagFileMembers(QXmlStreamWriter &writer, const Aggregate *parent)
{
const auto &childNodes = parent->childNodes();
for (const auto *node : childNodes) {
if (!node->url().isEmpty())
continue;
QString nodeName;
QString kind;
switch (node->nodeType()) {
case Node::Enum:
nodeName = "member";
kind = "enumeration";
break;
case Node::TypeAlias: // Treated as typedef
case Node::Typedef:
nodeName = "member";
kind = "typedef";
break;
case Node::Property:
nodeName = "member";
kind = "property";
break;
case Node::Function:
nodeName = "member";
kind = "function";
break;
case Node::Namespace:
nodeName = "namespace";
break;
case Node::Class:
case Node::Struct:
case Node::Union:
nodeName = "class";
break;
case Node::Variable:
default:
continue;
}
QString access;
switch (node->access()) {
case Node::Public:
access = "public";
break;
case Node::Protected:
access = "protected";
break;
case Node::Private:
default:
continue;
}
QString objName = node->name();
// Special case: only the root node should have an empty name.
if (objName.isEmpty() && node != qdb_->primaryTreeRoot())
continue;
// *** Write the starting tag for the element here. ***
writer.writeStartElement(nodeName);
if (!kind.isEmpty())
writer.writeAttribute("kind", kind);
switch (node->nodeType()) {
case Node::Class:
case Node::Struct:
case Node::Union:
writer.writeCharacters(node->fullDocumentName());
writer.writeEndElement();
break;
case Node::Namespace:
writer.writeCharacters(node->fullDocumentName());
writer.writeEndElement();
break;
case Node::Function: {
/*
Function nodes contain information about
the type of function being described.
*/
const FunctionNode *functionNode = static_cast<const FunctionNode *>(node);
writer.writeAttribute("protection", access);
writer.writeAttribute("virtualness", functionNode->virtualness());
writer.writeAttribute("static", functionNode->isStatic() ? "yes" : "no");
if (functionNode->isNonvirtual())
writer.writeTextElement("type", functionNode->returnType());
else
writer.writeTextElement("type", "virtual " + functionNode->returnType());
writer.writeTextElement("name", objName);
const QStringList pieces =
gen_->fullDocumentLocation(node, false).split(QLatin1Char('#'));
writer.writeTextElement("anchorfile", pieces[0]);
writer.writeTextElement("anchor", pieces[1]);
QString signature = functionNode->signature(false, false);
signature = signature.mid(signature.indexOf(QChar('('))).trimmed();
if (functionNode->isConst())
signature += " const";
if (functionNode->isFinal())
signature += " final";
if (functionNode->isOverride())
signature += " override";
if (functionNode->isPureVirtual())
signature += " = 0";
writer.writeTextElement("arglist", signature);
}
writer.writeEndElement(); // member
break;
case Node::Property: {
const PropertyNode *propertyNode = static_cast<const PropertyNode *>(node);
writer.writeAttribute("type", propertyNode->dataType());
writer.writeTextElement("name", objName);
const QStringList pieces =
gen_->fullDocumentLocation(node, false).split(QLatin1Char('#'));
writer.writeTextElement("anchorfile", pieces[0]);
writer.writeTextElement("anchor", pieces[1]);
writer.writeTextElement("arglist", QString());
}
writer.writeEndElement(); // member
break;
case Node::Enum: {
const EnumNode *enumNode = static_cast<const EnumNode *>(node);
writer.writeTextElement("name", objName);
const QStringList pieces =
gen_->fullDocumentLocation(node, false).split(QLatin1Char('#'));
writer.writeTextElement("anchorfile", pieces[0]);
writer.writeTextElement("anchor", pieces[1]);
writer.writeEndElement(); // member
for (const auto &item : enumNode->items()) {
writer.writeStartElement("member");
writer.writeAttribute("kind", "enumvalue");
writer.writeTextElement("name", item.name());
writer.writeTextElement("anchorfile", pieces[0]);
writer.writeTextElement("anchor", pieces[1]);
writer.writeTextElement("arglist", QString());
writer.writeEndElement(); // member
}
} break;
case Node::TypeAlias: // Treated as typedef
case Node::Typedef: {
const TypedefNode *typedefNode = static_cast<const TypedefNode *>(node);
if (typedefNode->associatedEnum())
writer.writeAttribute("type", typedefNode->associatedEnum()->fullDocumentName());
else
writer.writeAttribute("type", QString());
writer.writeTextElement("name", objName);
const QStringList pieces =
gen_->fullDocumentLocation(node, false).split(QLatin1Char('#'));
writer.writeTextElement("anchorfile", pieces[0]);
writer.writeTextElement("anchor", pieces[1]);
writer.writeTextElement("arglist", QString());
}
writer.writeEndElement(); // member
break;
case Node::Variable:
default:
break;
}
}
}
/*!
Writes a tag file named \a fileName.
*/
void QDocTagFiles::generateTagFile(const QString &fileName, Generator *g)
{
QFile file(fileName);
QFileInfo fileInfo(fileName);
// If no path was specified or it doesn't exist,
// default to the output directory
if (fileInfo.fileName() == fileName || !fileInfo.dir().exists())
file.setFileName(gen_->outputDir() + QLatin1Char('/') + fileInfo.fileName());
if (!file.open(QFile::WriteOnly | QFile::Text)) {
Location().warning(QString("Failed to open %1 for writing.").arg(file.fileName()));
return;
}
gen_ = g;
QXmlStreamWriter writer(&file);
writer.setAutoFormatting(true);
writer.writeStartDocument();
writer.writeStartElement("tagfile");
generateTagFileCompounds(writer, qdb_->primaryTreeRoot());
writer.writeEndElement(); // tagfile
writer.writeEndDocument();
file.close();
}
QT_END_NAMESPACE