1 /****************************************************************************
2 **
3 ** Copyright (C) 2019 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the tools applications of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21 ** included in the packaging of this file. Please review the following
22 ** information to ensure the GNU General Public License requirements will
23 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24 **
25 ** $QT_END_LICENSE$
26 **
27 ****************************************************************************/
28
29 #include "qdoctagfiles.h"
30
31 #include "atom.h"
32 #include "doc.h"
33 #include "htmlgenerator.h"
34 #include "location.h"
35 #include "node.h"
36 #include "qdocdatabase.h"
37 #include "text.h"
38
39 #include <QtCore/qdebug.h>
40
41 #include <limits.h>
42
43 QT_BEGIN_NAMESPACE
44
45 /*!
46 \class QDocTagFiles
47
48 This class handles the generation of the qdoc tag file.
49 */
50
51 QDocTagFiles *QDocTagFiles::qdocTagFiles_ = nullptr;
52
53 /*!
54 Constructs the singleton. \a qdb is the pointer to the
55 qdoc database that is used when reading and writing the
56 index files.
57 */
QDocTagFiles()58 QDocTagFiles::QDocTagFiles()
59 {
60 qdb_ = QDocDatabase::qdocDB();
61 }
62
63 /*!
64 Destroys the singleton QDocTagFiles.
65 */
~QDocTagFiles()66 QDocTagFiles::~QDocTagFiles()
67 {
68 qdb_ = nullptr;
69 }
70
71 /*!
72 Creates the singleton. Allows only one instance of the class
73 to be created. Returns a pointer to the singleton.
74 */
qdocTagFiles()75 QDocTagFiles *QDocTagFiles::qdocTagFiles()
76 {
77 if (qdocTagFiles_ == nullptr)
78 qdocTagFiles_ = new QDocTagFiles;
79 return qdocTagFiles_;
80 }
81
82 /*!
83 Destroys the singleton.
84 */
destroyQDocTagFiles()85 void QDocTagFiles::destroyQDocTagFiles()
86 {
87 if (qdocTagFiles_ != nullptr) {
88 delete qdocTagFiles_;
89 qdocTagFiles_ = nullptr;
90 }
91 }
92
93 /*!
94 Generate the tag file section with the given \a writer for the \a parent
95 node.
96 */
generateTagFileCompounds(QXmlStreamWriter & writer,const Aggregate * parent)97 void QDocTagFiles::generateTagFileCompounds(QXmlStreamWriter &writer, const Aggregate *parent)
98 {
99 const auto &nonFunctionList = const_cast<Aggregate *>(parent)->nonfunctionList();
100 for (const auto *node : nonFunctionList) {
101 if (!node->url().isEmpty() || node->isPrivate())
102 continue;
103
104 QString kind;
105 switch (node->nodeType()) {
106 case Node::Namespace:
107 kind = "namespace";
108 break;
109 case Node::Class:
110 case Node::Struct:
111 case Node::Union:
112 case Node::QmlType:
113 case Node::JsType:
114 kind = "class";
115 break;
116 default:
117 continue;
118 }
119 const Aggregate *aggregate = static_cast<const Aggregate *>(node);
120
121 QString access = "public";
122 if (node->isProtected())
123 access = "protected";
124
125 QString objName = node->name();
126
127 // Special case: only the root node should have an empty name.
128 if (objName.isEmpty() && node != qdb_->primaryTreeRoot())
129 continue;
130
131 // *** Write the starting tag for the element here. ***
132 writer.writeStartElement("compound");
133 writer.writeAttribute("kind", kind);
134
135 if (node->isClassNode()) {
136 writer.writeTextElement("name", node->fullDocumentName());
137 writer.writeTextElement("filename", gen_->fullDocumentLocation(node, false));
138
139 // Classes contain information about their base classes.
140 const ClassNode *classNode = static_cast<const ClassNode *>(node);
141 const QVector<RelatedClass> bases = classNode->baseClasses();
142 for (const auto &related : bases) {
143 ClassNode *n = related.node_;
144 if (n)
145 writer.writeTextElement("base", n->name());
146 }
147
148 // Recurse to write all members.
149 generateTagFileMembers(writer, aggregate);
150 writer.writeEndElement();
151
152 // Recurse to write all compounds.
153 generateTagFileCompounds(writer, aggregate);
154 } else {
155 writer.writeTextElement("name", node->fullDocumentName());
156 writer.writeTextElement("filename", gen_->fullDocumentLocation(node, false));
157
158 // Recurse to write all members.
159 generateTagFileMembers(writer, aggregate);
160 writer.writeEndElement();
161
162 // Recurse to write all compounds.
163 generateTagFileCompounds(writer, aggregate);
164 }
165 }
166 }
167
168 /*!
169 Writes all the members of the \a parent node with the \a writer.
170 The node represents a C++ class, namespace, etc.
171 */
generateTagFileMembers(QXmlStreamWriter & writer,const Aggregate * parent)172 void QDocTagFiles::generateTagFileMembers(QXmlStreamWriter &writer, const Aggregate *parent)
173 {
174 const auto &childNodes = parent->childNodes();
175 for (const auto *node : childNodes) {
176 if (!node->url().isEmpty())
177 continue;
178
179 QString nodeName;
180 QString kind;
181 switch (node->nodeType()) {
182 case Node::Enum:
183 nodeName = "member";
184 kind = "enumeration";
185 break;
186 case Node::TypeAlias: // Treated as typedef
187 case Node::Typedef:
188 nodeName = "member";
189 kind = "typedef";
190 break;
191 case Node::Property:
192 nodeName = "member";
193 kind = "property";
194 break;
195 case Node::Function:
196 nodeName = "member";
197 kind = "function";
198 break;
199 case Node::Namespace:
200 nodeName = "namespace";
201 break;
202 case Node::Class:
203 case Node::Struct:
204 case Node::Union:
205 nodeName = "class";
206 break;
207 case Node::Variable:
208 default:
209 continue;
210 }
211
212 QString access;
213 switch (node->access()) {
214 case Node::Public:
215 access = "public";
216 break;
217 case Node::Protected:
218 access = "protected";
219 break;
220 case Node::Private:
221 default:
222 continue;
223 }
224
225 QString objName = node->name();
226
227 // Special case: only the root node should have an empty name.
228 if (objName.isEmpty() && node != qdb_->primaryTreeRoot())
229 continue;
230
231 // *** Write the starting tag for the element here. ***
232 writer.writeStartElement(nodeName);
233 if (!kind.isEmpty())
234 writer.writeAttribute("kind", kind);
235
236 switch (node->nodeType()) {
237 case Node::Class:
238 case Node::Struct:
239 case Node::Union:
240 writer.writeCharacters(node->fullDocumentName());
241 writer.writeEndElement();
242 break;
243 case Node::Namespace:
244 writer.writeCharacters(node->fullDocumentName());
245 writer.writeEndElement();
246 break;
247 case Node::Function: {
248 /*
249 Function nodes contain information about
250 the type of function being described.
251 */
252
253 const FunctionNode *functionNode = static_cast<const FunctionNode *>(node);
254 writer.writeAttribute("protection", access);
255 writer.writeAttribute("virtualness", functionNode->virtualness());
256 writer.writeAttribute("static", functionNode->isStatic() ? "yes" : "no");
257
258 if (functionNode->isNonvirtual())
259 writer.writeTextElement("type", functionNode->returnType());
260 else
261 writer.writeTextElement("type", "virtual " + functionNode->returnType());
262
263 writer.writeTextElement("name", objName);
264 const QStringList pieces =
265 gen_->fullDocumentLocation(node, false).split(QLatin1Char('#'));
266 writer.writeTextElement("anchorfile", pieces[0]);
267 writer.writeTextElement("anchor", pieces[1]);
268 QString signature = functionNode->signature(false, false);
269 signature = signature.mid(signature.indexOf(QChar('('))).trimmed();
270 if (functionNode->isConst())
271 signature += " const";
272 if (functionNode->isFinal())
273 signature += " final";
274 if (functionNode->isOverride())
275 signature += " override";
276 if (functionNode->isPureVirtual())
277 signature += " = 0";
278 writer.writeTextElement("arglist", signature);
279 }
280 writer.writeEndElement(); // member
281 break;
282 case Node::Property: {
283 const PropertyNode *propertyNode = static_cast<const PropertyNode *>(node);
284 writer.writeAttribute("type", propertyNode->dataType());
285 writer.writeTextElement("name", objName);
286 const QStringList pieces =
287 gen_->fullDocumentLocation(node, false).split(QLatin1Char('#'));
288 writer.writeTextElement("anchorfile", pieces[0]);
289 writer.writeTextElement("anchor", pieces[1]);
290 writer.writeTextElement("arglist", QString());
291 }
292 writer.writeEndElement(); // member
293 break;
294 case Node::Enum: {
295 const EnumNode *enumNode = static_cast<const EnumNode *>(node);
296 writer.writeTextElement("name", objName);
297 const QStringList pieces =
298 gen_->fullDocumentLocation(node, false).split(QLatin1Char('#'));
299 writer.writeTextElement("anchorfile", pieces[0]);
300 writer.writeTextElement("anchor", pieces[1]);
301 writer.writeEndElement(); // member
302
303 for (const auto &item : enumNode->items()) {
304 writer.writeStartElement("member");
305 writer.writeAttribute("kind", "enumvalue");
306 writer.writeTextElement("name", item.name());
307 writer.writeTextElement("anchorfile", pieces[0]);
308 writer.writeTextElement("anchor", pieces[1]);
309 writer.writeTextElement("arglist", QString());
310 writer.writeEndElement(); // member
311 }
312 } break;
313 case Node::TypeAlias: // Treated as typedef
314 case Node::Typedef: {
315 const TypedefNode *typedefNode = static_cast<const TypedefNode *>(node);
316 if (typedefNode->associatedEnum())
317 writer.writeAttribute("type", typedefNode->associatedEnum()->fullDocumentName());
318 else
319 writer.writeAttribute("type", QString());
320 writer.writeTextElement("name", objName);
321 const QStringList pieces =
322 gen_->fullDocumentLocation(node, false).split(QLatin1Char('#'));
323 writer.writeTextElement("anchorfile", pieces[0]);
324 writer.writeTextElement("anchor", pieces[1]);
325 writer.writeTextElement("arglist", QString());
326 }
327 writer.writeEndElement(); // member
328 break;
329
330 case Node::Variable:
331 default:
332 break;
333 }
334 }
335 }
336
337 /*!
338 Writes a tag file named \a fileName.
339 */
generateTagFile(const QString & fileName,Generator * g)340 void QDocTagFiles::generateTagFile(const QString &fileName, Generator *g)
341 {
342 QFile file(fileName);
343 QFileInfo fileInfo(fileName);
344
345 // If no path was specified or it doesn't exist,
346 // default to the output directory
347 if (fileInfo.fileName() == fileName || !fileInfo.dir().exists())
348 file.setFileName(gen_->outputDir() + QLatin1Char('/') + fileInfo.fileName());
349
350 if (!file.open(QFile::WriteOnly | QFile::Text)) {
351 Location().warning(QString("Failed to open %1 for writing.").arg(file.fileName()));
352 return;
353 }
354
355 gen_ = g;
356 QXmlStreamWriter writer(&file);
357 writer.setAutoFormatting(true);
358 writer.writeStartDocument();
359 writer.writeStartElement("tagfile");
360 generateTagFileCompounds(writer, qdb_->primaryTreeRoot());
361 writer.writeEndElement(); // tagfile
362 writer.writeEndDocument();
363 file.close();
364 }
365
366 QT_END_NAMESPACE
367