| /**************************************************************************** |
| ** |
| ** 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 |