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