1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#include "tagfilewriter.h"
5
6#include "access.h"
7#include "aggregate.h"
8#include "classnode.h"
9#include "enumnode.h"
10#include "functionnode.h"
11#include "htmlgenerator.h"
12#include "location.h"
13#include "node.h"
14#include "propertynode.h"
15#include "qdocdatabase.h"
16#include "typedefnode.h"
17
18QT_BEGIN_NAMESPACE
19
20/*!
21 \class TagFileWriter
22
23 This class handles the generation of the QDoc tag files.
24 */
25
26/*!
27 Default constructor. \a qdb is the pointer to the
28 qdoc database that is used when reading and writing the
29 index files.
30 */
31TagFileWriter::TagFileWriter() : m_qdb(QDocDatabase::qdocDB()) { }
32
33/*!
34 Generate the tag file section with the given \a writer for the \a parent
35 node.
36 */
37void TagFileWriter::generateTagFileCompounds(QXmlStreamWriter &writer, const Aggregate *parent)
38{
39 const auto &nonFunctionList = const_cast<Aggregate *>(parent)->nonfunctionList();
40 for (const auto *node : nonFunctionList) {
41 if (!node->url().isNull() || node->isPrivate())
42 continue;
43
44 QString kind;
45 switch (node->nodeType()) {
46 case Node::Namespace:
47 kind = "namespace";
48 break;
49 case Node::Class:
50 case Node::Struct:
51 case Node::Union:
52 case Node::QmlType:
53 kind = "class";
54 break;
55 default:
56 continue;
57 }
58 const auto *aggregate = static_cast<const Aggregate *>(node);
59
60 QString access = "public";
61 if (node->isProtected())
62 access = "protected";
63
64 QString objName = node->name();
65
66 // Special case: only the root node should have an empty name.
67 if (objName.isEmpty() && node != m_qdb->primaryTreeRoot())
68 continue;
69
70 // *** Write the starting tag for the element here. ***
71 writer.writeStartElement(qualifiedName: "compound");
72 writer.writeAttribute(qualifiedName: "kind", value: kind);
73
74 if (node->isClassNode()) {
75 writer.writeTextElement(qualifiedName: "name", text: node->fullDocumentName());
76 writer.writeTextElement(qualifiedName: "filename", text: m_generator->fullDocumentLocation(node, useSubdir: false));
77
78 // Classes contain information about their base classes.
79 const auto *classNode = static_cast<const ClassNode *>(node);
80 const QList<RelatedClass> &bases = classNode->baseClasses();
81 for (const auto &related : bases) {
82 ClassNode *n = related.m_node;
83 if (n)
84 writer.writeTextElement(qualifiedName: "base", text: n->name());
85 }
86
87 // Recurse to write all members.
88 generateTagFileMembers(writer, inner: aggregate);
89 writer.writeEndElement();
90
91 // Recurse to write all compounds.
92 generateTagFileCompounds(writer, parent: aggregate);
93 } else {
94 writer.writeTextElement(qualifiedName: "name", text: node->fullDocumentName());
95 writer.writeTextElement(qualifiedName: "filename", text: m_generator->fullDocumentLocation(node, useSubdir: false));
96
97 // Recurse to write all members.
98 generateTagFileMembers(writer, inner: aggregate);
99 writer.writeEndElement();
100
101 // Recurse to write all compounds.
102 generateTagFileCompounds(writer, parent: aggregate);
103 }
104 }
105}
106
107/*!
108 Writes all the members of the \a parent node with the \a writer.
109 The node represents a C++ class, namespace, etc.
110 */
111void TagFileWriter::generateTagFileMembers(QXmlStreamWriter &writer, const Aggregate *parent)
112{
113 auto childNodes = parent->childNodes();
114 std::sort(first: childNodes.begin(), last: childNodes.end(), comp: Node::nodeNameLessThan);
115 for (const auto *node : childNodes) {
116 if (!node->url().isNull())
117 continue;
118
119 QString nodeName;
120 QString kind;
121 switch (node->nodeType()) {
122 case Node::Enum:
123 nodeName = "member";
124 kind = "enumeration";
125 break;
126 case Node::TypeAlias: // Treated as typedef
127 case Node::Typedef:
128 nodeName = "member";
129 kind = "typedef";
130 break;
131 case Node::Property:
132 nodeName = "member";
133 kind = "property";
134 break;
135 case Node::Function:
136 nodeName = "member";
137 kind = "function";
138 break;
139 case Node::Namespace:
140 nodeName = "namespace";
141 break;
142 case Node::Class:
143 case Node::Struct:
144 case Node::Union:
145 nodeName = "class";
146 break;
147 case Node::Variable:
148 default:
149 continue;
150 }
151
152 QString access;
153 switch (node->access()) {
154 case Access::Public:
155 access = "public";
156 break;
157 case Access::Protected:
158 access = "protected";
159 break;
160 case Access::Private:
161 default:
162 continue;
163 }
164
165 QString objName = node->name();
166
167 // Special case: only the root node should have an empty name.
168 if (objName.isEmpty() && node != m_qdb->primaryTreeRoot())
169 continue;
170
171 // *** Write the starting tag for the element here. ***
172 writer.writeStartElement(qualifiedName: nodeName);
173 if (!kind.isEmpty())
174 writer.writeAttribute(qualifiedName: "kind", value: kind);
175
176 switch (node->nodeType()) {
177 case Node::Class:
178 case Node::Struct:
179 case Node::Union:
180 writer.writeCharacters(text: node->fullDocumentName());
181 writer.writeEndElement();
182 break;
183 case Node::Namespace:
184 writer.writeCharacters(text: node->fullDocumentName());
185 writer.writeEndElement();
186 break;
187 case Node::Function: {
188 /*
189 Function nodes contain information about
190 the type of function being described.
191 */
192
193 const auto *functionNode = static_cast<const FunctionNode *>(node);
194 writer.writeAttribute(qualifiedName: "protection", value: access);
195 writer.writeAttribute(qualifiedName: "virtualness", value: functionNode->virtualness());
196 writer.writeAttribute(qualifiedName: "static", value: functionNode->isStatic() ? "yes" : "no");
197
198 if (functionNode->isNonvirtual())
199 writer.writeTextElement(qualifiedName: "type", text: functionNode->returnType());
200 else
201 writer.writeTextElement(qualifiedName: "type", text: "virtual " + functionNode->returnType());
202
203 writer.writeTextElement(qualifiedName: "name", text: objName);
204 const QStringList pieces =
205 m_generator->fullDocumentLocation(node, useSubdir: false).split(sep: QLatin1Char('#'));
206 writer.writeTextElement(qualifiedName: "anchorfile", text: pieces[0]);
207 writer.writeTextElement(qualifiedName: "anchor", text: pieces[1]);
208 QString signature = functionNode->signature(options: Node::SignatureReturnType);
209 signature = signature.mid(position: signature.indexOf(c: QChar('('))).trimmed();
210 if (functionNode->isConst())
211 signature += " const";
212 if (functionNode->isFinal())
213 signature += " final";
214 if (functionNode->isOverride())
215 signature += " override";
216 if (functionNode->isPureVirtual())
217 signature += " = 0";
218 writer.writeTextElement(qualifiedName: "arglist", text: signature);
219 }
220 writer.writeEndElement(); // member
221 break;
222 case Node::Property: {
223 const auto *propertyNode = static_cast<const PropertyNode *>(node);
224 writer.writeAttribute(qualifiedName: "type", value: propertyNode->dataType());
225 writer.writeTextElement(qualifiedName: "name", text: objName);
226 const QStringList pieces =
227 m_generator->fullDocumentLocation(node, useSubdir: false).split(sep: QLatin1Char('#'));
228 writer.writeTextElement(qualifiedName: "anchorfile", text: pieces[0]);
229 writer.writeTextElement(qualifiedName: "anchor", text: pieces[1]);
230 writer.writeTextElement(qualifiedName: "arglist", text: QString());
231 }
232 writer.writeEndElement(); // member
233 break;
234 case Node::Enum: {
235 const auto *enumNode = static_cast<const EnumNode *>(node);
236 writer.writeTextElement(qualifiedName: "name", text: objName);
237 const QStringList pieces =
238 m_generator->fullDocumentLocation(node, useSubdir: false).split(sep: QLatin1Char('#'));
239 writer.writeTextElement(qualifiedName: "anchorfile", text: pieces[0]);
240 writer.writeTextElement(qualifiedName: "anchor", text: pieces[1]);
241 writer.writeEndElement(); // member
242
243 for (const auto &item : enumNode->items()) {
244 writer.writeStartElement(qualifiedName: "member");
245 writer.writeAttribute(qualifiedName: "kind", value: "enumvalue");
246 writer.writeTextElement(qualifiedName: "name", text: item.name());
247 writer.writeTextElement(qualifiedName: "anchorfile", text: pieces[0]);
248 writer.writeTextElement(qualifiedName: "anchor", text: pieces[1]);
249 writer.writeTextElement(qualifiedName: "arglist", text: QString());
250 writer.writeEndElement(); // member
251 }
252 } break;
253 case Node::TypeAlias: // Treated as typedef
254 case Node::Typedef: {
255 const auto *typedefNode = static_cast<const TypedefNode *>(node);
256 if (typedefNode->associatedEnum())
257 writer.writeAttribute(qualifiedName: "type", value: typedefNode->associatedEnum()->fullDocumentName());
258 else
259 writer.writeAttribute(qualifiedName: "type", value: QString());
260 writer.writeTextElement(qualifiedName: "name", text: objName);
261 const QStringList pieces =
262 m_generator->fullDocumentLocation(node, useSubdir: false).split(sep: QLatin1Char('#'));
263 writer.writeTextElement(qualifiedName: "anchorfile", text: pieces[0]);
264 writer.writeTextElement(qualifiedName: "anchor", text: pieces[1]);
265 writer.writeTextElement(qualifiedName: "arglist", text: QString());
266 }
267 writer.writeEndElement(); // member
268 break;
269
270 case Node::Variable:
271 default:
272 break;
273 }
274 }
275}
276
277/*!
278 Writes a tag file named \a fileName.
279 */
280void TagFileWriter::generateTagFile(const QString &fileName, Generator *g)
281{
282 QFile file(fileName);
283 QFileInfo fileInfo(fileName);
284
285 // If no path was specified or it doesn't exist,
286 // default to the output directory
287 if (fileInfo.fileName() == fileName || !fileInfo.dir().exists())
288 file.setFileName(m_generator->outputDir() + QLatin1Char('/') + fileInfo.fileName());
289
290 if (!file.open(flags: QFile::WriteOnly | QFile::Text)) {
291 Location().warning(message: QString("Failed to open %1 for writing.").arg(a: file.fileName()));
292 return;
293 }
294
295 m_generator = g;
296 QXmlStreamWriter writer(&file);
297 writer.setAutoFormatting(true);
298 writer.writeStartDocument();
299 writer.writeStartElement(qualifiedName: "tagfile");
300 generateTagFileCompounds(writer, parent: m_qdb->primaryTreeRoot());
301 writer.writeEndElement(); // tagfile
302 writer.writeEndDocument();
303 file.close();
304}
305
306QT_END_NAMESPACE
307

source code of qttools/src/qdoc/qdoc/tagfilewriter.cpp