Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qdocindexfiles.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
5
6#include "access.h"
7#include "atom.h"
8#include "classnode.h"
11#include "config.h"
12#include "enumnode.h"
13#include "examplenode.h"
15#include "functionnode.h"
16#include "generator.h"
17#include "headernode.h"
18#include "location.h"
19#include "utilities.h"
20#include "propertynode.h"
21#include "qdocdatabase.h"
23#include "typedefnode.h"
24#include "variablenode.h"
25
26#include <QtCore/qxmlstream.h>
27
28#include <algorithm>
29
31
41
42static Node *root_ = nullptr;
43static IndexSectionWriter *post_ = nullptr;
44
45/*!
46 \class QDocIndexFiles
47
48 This class handles qdoc index files.
49 */
50
51QDocIndexFiles *QDocIndexFiles::s_qdocIndexFiles = nullptr;
52
53/*!
54 Constructs the singleton QDocIndexFiles.
55 */
56QDocIndexFiles::QDocIndexFiles() : m_gen(nullptr)
57{
59 m_storeLocationInfo = Config::instance().get(CONFIG_LOCATIONINFO).asBool();
60}
61
62/*!
63 Destroys the singleton QDocIndexFiles.
64 */
65QDocIndexFiles::~QDocIndexFiles()
66{
67 m_qdb = nullptr;
68 m_gen = 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 */
75QDocIndexFiles *QDocIndexFiles::qdocIndexFiles()
76{
77 if (s_qdocIndexFiles == nullptr)
78 s_qdocIndexFiles = new QDocIndexFiles;
79 return s_qdocIndexFiles;
80}
81
82/*!
83 Destroys the singleton.
84 */
85void QDocIndexFiles::destroyQDocIndexFiles()
86{
87 if (s_qdocIndexFiles != nullptr) {
88 delete s_qdocIndexFiles;
89 s_qdocIndexFiles = nullptr;
90 }
91}
92
93/*!
94 Reads and parses the list of index files in \a indexFiles.
95 */
96void QDocIndexFiles::readIndexes(const QStringList &indexFiles)
97{
98 for (const QString &file : indexFiles) {
99 qCDebug(lcQdoc) << "Loading index file: " << file;
100 readIndexFile(file);
101 }
102}
103
104/*!
105 Reads and parses the index file at \a path.
106 */
107void QDocIndexFiles::readIndexFile(const QString &path)
108{
109 QFile file(path);
110 if (!file.open(QFile::ReadOnly)) {
111 qWarning() << "Could not read index file" << path;
112 return;
113 }
114
115 QXmlStreamReader reader(&file);
116 reader.setNamespaceProcessing(false);
117
118 if (!reader.readNextStartElement())
119 return;
120
121 if (reader.name() != QLatin1String("INDEX"))
122 return;
123
124 QXmlStreamAttributes attrs = reader.attributes();
125
126 QString indexUrl {attrs.value(QLatin1String("url")).toString()};
127
128 // Decide how we link to nodes loaded from this index file:
129 // If building a set that will be installed AND the URL of
130 // the dependency is identical to ours, assume that also
131 // the dependent html files are available under the same
132 // directory tree. Otherwise, link using the full index URL.
133 if (!Config::installDir.isEmpty() && indexUrl == Config::instance().get(CONFIG_URL).asString()) {
134 // Generate a relative URL between the install dir and the index file
135 // when the -installdir command line option is set.
136 QDir installDir(path.section('/', 0, -3) + '/' + Generator::outputSubdir());
137 indexUrl = installDir.relativeFilePath(path).section('/', 0, -2);
138 }
139 m_project = attrs.value(QLatin1String("project")).toString();
140 QString indexTitle = attrs.value(QLatin1String("indexTitle")).toString();
141 m_basesList.clear();
142 m_relatedNodes.clear();
143
144 NamespaceNode *root = m_qdb->newIndexTree(m_project);
145 if (!root) {
146 qWarning() << "Issue parsing index tree" << path;
147 return;
148 }
149
150 root->tree()->setIndexTitle(indexTitle);
151
152 // Scan all elements in the XML file, constructing a map that contains
153 // base classes for each class found.
154 while (reader.readNextStartElement()) {
155 readIndexSection(reader, root, indexUrl);
156 }
157
158 // Now that all the base classes have been found for this index,
159 // arrange them into an inheritance hierarchy.
160 resolveIndex();
161}
162
163/*!
164 Read a <section> element from the index file and create the
165 appropriate node(s).
166 */
167void QDocIndexFiles::readIndexSection(QXmlStreamReader &reader, Node *current,
168 const QString &indexUrl)
169{
170 QXmlStreamAttributes attributes = reader.attributes();
171 QStringView elementName = reader.name();
172
173 QString name = attributes.value(QLatin1String("name")).toString();
174 QString href = attributes.value(QLatin1String("href")).toString();
175 Node *node;
176 Location location;
177 Aggregate *parent = nullptr;
178 bool hasReadChildren = false;
179
180 if (current->isAggregate())
181 parent = static_cast<Aggregate *>(current);
182
183 if (attributes.hasAttribute(QLatin1String("related"))) {
184 bool isIntTypeRelatedValue = false;
185 int relatedIndex = attributes.value(QLatin1String("related")).toInt(&isIntTypeRelatedValue);
186 if (isIntTypeRelatedValue) {
187 if (adoptRelatedNode(parent, relatedIndex)) {
188 reader.skipCurrentElement();
189 return;
190 }
191 } else {
192 QList<Node *>::iterator nodeIterator =
193 std::find_if(m_relatedNodes.begin(), m_relatedNodes.end(), [&](const Node *relatedNode) {
194 return (name == relatedNode->name() && href == relatedNode->url().section(QLatin1Char('/'), -1));
195 });
196
197 if (nodeIterator != m_relatedNodes.end() && parent) {
198 parent->adoptChild(*nodeIterator);
199 reader.skipCurrentElement();
200 return;
201 }
202 }
203 }
204
205 QString filePath;
206 int lineNo = 0;
207 if (attributes.hasAttribute(QLatin1String("filepath"))) {
208 filePath = attributes.value(QLatin1String("filepath")).toString();
209 lineNo = attributes.value("lineno").toInt();
210 }
211 if (elementName == QLatin1String("namespace")) {
212 auto *namespaceNode = new NamespaceNode(parent, name);
213 node = namespaceNode;
214 if (!indexUrl.isEmpty())
215 location = Location(indexUrl + QLatin1Char('/') + name.toLower() + ".html");
216 else if (!indexUrl.isNull())
217 location = Location(name.toLower() + ".html");
218 } else if (elementName == QLatin1String("class") || elementName == QLatin1String("struct")
219 || elementName == QLatin1String("union")) {
220 Node::NodeType type = Node::Class;
221 if (elementName == QLatin1String("class"))
222 type = Node::Class;
223 else if (elementName == QLatin1String("struct"))
224 type = Node::Struct;
225 else if (elementName == QLatin1String("union"))
226 type = Node::Union;
227 node = new ClassNode(type, parent, name);
228 if (attributes.hasAttribute(QLatin1String("bases"))) {
229 QString bases = attributes.value(QLatin1String("bases")).toString();
230 if (!bases.isEmpty())
231 m_basesList.append(
232 std::pair<ClassNode *, QString>(static_cast<ClassNode *>(node), bases));
233 }
234 if (!indexUrl.isEmpty())
235 location = Location(indexUrl + QLatin1Char('/') + name.toLower() + ".html");
236 else if (!indexUrl.isNull())
237 location = Location(name.toLower() + ".html");
238 bool abstract = false;
239 if (attributes.value(QLatin1String("abstract")) == QLatin1String("true"))
240 abstract = true;
241 node->setAbstract(abstract);
242 } else if (elementName == QLatin1String("header")) {
243 node = new HeaderNode(parent, name);
244
245 if (attributes.hasAttribute(QLatin1String("location")))
246 name = attributes.value(QLatin1String("location")).toString();
247
248 if (!indexUrl.isEmpty())
249 location = Location(indexUrl + QLatin1Char('/') + name);
250 else if (!indexUrl.isNull())
251 location = Location(name);
252 } else if (elementName == QLatin1String("qmlclass") || elementName == QLatin1String("qmlvaluetype")
253 || elementName == QLatin1String("qmlbasictype")) {
254 auto *qmlTypeNode = new QmlTypeNode(parent, name,
255 elementName == QLatin1String("qmlclass") ? Node::QmlType : Node::QmlValueType);
256 qmlTypeNode->setTitle(attributes.value(QLatin1String("title")).toString());
257 QString logicalModuleName = attributes.value(QLatin1String("qml-module-name")).toString();
258 if (!logicalModuleName.isEmpty())
259 m_qdb->addToQmlModule(logicalModuleName, qmlTypeNode);
260 bool abstract = false;
261 if (attributes.value(QLatin1String("abstract")) == QLatin1String("true"))
262 abstract = true;
263 qmlTypeNode->setAbstract(abstract);
264 QString qmlFullBaseName = attributes.value(QLatin1String("qml-base-type")).toString();
265 if (!qmlFullBaseName.isEmpty()) {
266 qmlTypeNode->setQmlBaseName(qmlFullBaseName);
267 }
268 if (attributes.hasAttribute(QLatin1String("location")))
269 name = attributes.value("location").toString();
270 if (!indexUrl.isEmpty())
271 location = Location(indexUrl + QLatin1Char('/') + name);
272 else if (!indexUrl.isNull())
273 location = Location(name);
274 node = qmlTypeNode;
275 } else if (elementName == QLatin1String("qmlproperty")) {
276 QString type = attributes.value(QLatin1String("type")).toString();
277 bool attached = false;
278 if (attributes.value(QLatin1String("attached")) == QLatin1String("true"))
279 attached = true;
280 bool readonly = false;
281 if (attributes.value(QLatin1String("writable")) == QLatin1String("false"))
282 readonly = true;
283 auto *qmlPropertyNode = new QmlPropertyNode(parent, name, type, attached);
284 qmlPropertyNode->markReadOnly(readonly);
285 if (attributes.value(QLatin1String("required")) == QLatin1String("true"))
286 qmlPropertyNode->setRequired();
287 node = qmlPropertyNode;
288 } else if (elementName == QLatin1String("group")) {
289 auto *collectionNode = m_qdb->addGroup(name);
290 collectionNode->setTitle(attributes.value(QLatin1String("title")).toString());
291 collectionNode->setSubtitle(attributes.value(QLatin1String("subtitle")).toString());
292 if (attributes.value(QLatin1String("seen")) == QLatin1String("true"))
293 collectionNode->markSeen();
294 node = collectionNode;
295 } else if (elementName == QLatin1String("module")) {
296 auto *collectionNode = m_qdb->addModule(name);
297 collectionNode->setTitle(attributes.value(QLatin1String("title")).toString());
298 collectionNode->setSubtitle(attributes.value(QLatin1String("subtitle")).toString());
299 if (attributes.value(QLatin1String("seen")) == QLatin1String("true"))
300 collectionNode->markSeen();
301 node = collectionNode;
302 } else if (elementName == QLatin1String("qmlmodule")) {
303 auto *collectionNode = m_qdb->addQmlModule(name);
304 const QStringList info = QStringList()
305 << name
306 << QString(attributes.value(QLatin1String("qml-module-version")).toString());
307 collectionNode->setLogicalModuleInfo(info);
308 collectionNode->setTitle(attributes.value(QLatin1String("title")).toString());
309 collectionNode->setSubtitle(attributes.value(QLatin1String("subtitle")).toString());
310 if (attributes.value(QLatin1String("seen")) == QLatin1String("true"))
311 collectionNode->markSeen();
312 node = collectionNode;
313 } else if (elementName == QLatin1String("page")) {
314 QDocAttr subtype = QDocAttrNone;
315 QString attr = attributes.value(QLatin1String("subtype")).toString();
316 if (attr == QLatin1String("attribution")) {
317 subtype = QDocAttrAttribution;
318 } else if (attr == QLatin1String("example")) {
319 subtype = QDocAttrExample;
320 } else if (attr == QLatin1String("file")) {
321 subtype = QDocAttrFile;
322 } else if (attr == QLatin1String("image")) {
323 subtype = QDocAttrImage;
324 } else if (attr == QLatin1String("page")) {
325 subtype = QDocAttrDocument;
326 } else if (attr == QLatin1String("externalpage")) {
327 subtype = QDocAttrExternalPage;
328 } else
329 goto done;
330
331 if (current->isExample()) {
332 auto *exampleNode = static_cast<ExampleNode *>(current);
333 if (subtype == QDocAttrFile) {
334 exampleNode->appendFile(name);
335 goto done;
336 } else if (subtype == QDocAttrImage) {
337 exampleNode->appendImage(name);
338 goto done;
339 }
340 }
341 PageNode *pageNode = nullptr;
342 if (subtype == QDocAttrExample)
343 pageNode = new ExampleNode(parent, name);
344 else if (subtype == QDocAttrExternalPage)
345 pageNode = new ExternalPageNode(parent, name);
346 else {
347 pageNode = new PageNode(parent, name);
348 if (subtype == QDocAttrAttribution) pageNode->markAttribution();
349 }
350
351 pageNode->setTitle(attributes.value(QLatin1String("title")).toString());
352
353 if (attributes.hasAttribute(QLatin1String("location")))
354 name = attributes.value(QLatin1String("location")).toString();
355
356 if (!indexUrl.isEmpty())
357 location = Location(indexUrl + QLatin1Char('/') + name);
358 else if (!indexUrl.isNull())
359 location = Location(name);
360
361 node = pageNode;
362
363 } else if (elementName == QLatin1String("enum")) {
364 auto *enumNode = new EnumNode(parent, name, attributes.hasAttribute("scoped"));
365
366 if (!indexUrl.isEmpty())
367 location = Location(indexUrl + QLatin1Char('/') + parent->name().toLower() + ".html");
368 else if (!indexUrl.isNull())
369 location = Location(parent->name().toLower() + ".html");
370
371 while (reader.readNextStartElement()) {
372 QXmlStreamAttributes childAttributes = reader.attributes();
373 if (reader.name() == QLatin1String("value")) {
374 EnumItem item(childAttributes.value(QLatin1String("name")).toString(),
375 childAttributes.value(QLatin1String("value")).toString(),
376 childAttributes.value(QLatin1String("since")).toString()
377 );
378 enumNode->addItem(item);
379 } else if (reader.name() == QLatin1String("keyword")) {
380 insertTarget(TargetRec::Keyword, childAttributes, enumNode);
381 } else if (reader.name() == QLatin1String("target")) {
382 insertTarget(TargetRec::Target, childAttributes, enumNode);
383 }
384 reader.skipCurrentElement();
385 }
386
387 node = enumNode;
388
389 hasReadChildren = true;
390 } else if (elementName == QLatin1String("typedef")) {
391 if (attributes.hasAttribute("aliasedtype"))
392 node = new TypeAliasNode(parent, name, attributes.value(QLatin1String("aliasedtype")).toString());
393 else
394 node = new TypedefNode(parent, name);
395
396 if (!indexUrl.isEmpty())
397 location = Location(indexUrl + QLatin1Char('/') + parent->name().toLower() + ".html");
398 else if (!indexUrl.isNull())
399 location = Location(parent->name().toLower() + ".html");
400 } else if (elementName == QLatin1String("property")) {
401 auto *propNode = new PropertyNode(parent, name);
402 node = propNode;
403 if (attributes.value(QLatin1String("bindable")) == QLatin1String("true"))
404 propNode->setPropertyType(PropertyNode::PropertyType::BindableProperty);
405
406 propNode->setWritable(attributes.value(QLatin1String("writable")) != QLatin1String("false"));
407
408 if (!indexUrl.isEmpty())
409 location = Location(indexUrl + QLatin1Char('/') + parent->name().toLower() + ".html");
410 else if (!indexUrl.isNull())
411 location = Location(parent->name().toLower() + ".html");
412
413 } else if (elementName == QLatin1String("function")) {
414 QString t = attributes.value(QLatin1String("meta")).toString();
415 bool attached = false;
417 if (!t.isEmpty())
418 metaness = FunctionNode::getMetaness(t);
419 if (attributes.value(QLatin1String("attached")) == QLatin1String("true"))
420 attached = true;
421 auto *fn = new FunctionNode(metaness, parent, name, attached);
422
423 fn->setReturnType(attributes.value(QLatin1String("type")).toString());
424
425 if (fn->isCppNode()) {
426 fn->setVirtualness(attributes.value(QLatin1String("virtual")).toString());
427 fn->setConst(attributes.value(QLatin1String("const")) == QLatin1String("true"));
428 fn->setStatic(attributes.value(QLatin1String("static")) == QLatin1String("true"));
429 fn->setFinal(attributes.value(QLatin1String("final")) == QLatin1String("true"));
430 fn->setOverride(attributes.value(QLatin1String("override")) == QLatin1String("true"));
431
432 if (attributes.value(QLatin1String("explicit")) == QLatin1String("true"))
433 fn->markExplicit();
434
435 if (attributes.value(QLatin1String("constexpr")) == QLatin1String("true"))
436 fn->markConstexpr();
437
438 if (attributes.value(QLatin1String("noexcept")) == QLatin1String("true")) {
439 fn->markNoexcept(attributes.value("noexcept_expression").toString());
440 }
441
442 qsizetype refness = attributes.value(QLatin1String("refness")).toUInt();
443 if (refness == 1)
444 fn->setRef(true);
445 else if (refness == 2)
446 fn->setRefRef(true);
447 /*
448 Theoretically, this should ensure that each function
449 node receives the same overload number and overload
450 flag it was written with, and it should be unnecessary
451 to call normalizeOverloads() for index nodes.
452 */
453 if (attributes.value(QLatin1String("overload")) == QLatin1String("true"))
454 fn->setOverloadNumber(attributes.value(QLatin1String("overload-number")).toUInt());
455 else
456 fn->setOverloadNumber(0);
457 }
458
459 /*
460 Note: The "signature" attribute was written to the
461 index file, but it is not read back in. That is ok
462 because we reconstruct the parameter list and the
463 return type, from which the signature was built in
464 the first place and from which it can be rebuilt.
465 */
466 while (reader.readNextStartElement()) {
467 QXmlStreamAttributes childAttributes = reader.attributes();
468 if (reader.name() == QLatin1String("parameter")) {
469 // Do not use the default value for the parameter; it is not
470 // required, and has been known to cause problems.
471 QString type = childAttributes.value(QLatin1String("type")).toString();
472 QString name = childAttributes.value(QLatin1String("name")).toString();
473 fn->parameters().append(type, name);
474 } else if (reader.name() == QLatin1String("keyword")) {
475 insertTarget(TargetRec::Keyword, childAttributes, fn);
476 } else if (reader.name() == QLatin1String("target")) {
477 insertTarget(TargetRec::Target, childAttributes, fn);
478 }
479 reader.skipCurrentElement();
480 }
481
482 node = fn;
483 if (!indexUrl.isEmpty())
484 location = Location(indexUrl + QLatin1Char('/') + parent->name().toLower() + ".html");
485 else if (!indexUrl.isNull())
486 location = Location(parent->name().toLower() + ".html");
487
488 hasReadChildren = true;
489 } else if (elementName == QLatin1String("variable")) {
490 node = new VariableNode(parent, name);
491 if (!indexUrl.isEmpty())
492 location = Location(indexUrl + QLatin1Char('/') + parent->name().toLower() + ".html");
493 else if (!indexUrl.isNull())
494 location = Location(parent->name().toLower() + ".html");
495 } else if (elementName == QLatin1String("keyword")) {
496 insertTarget(TargetRec::Keyword, attributes, current);
497 goto done;
498 } else if (elementName == QLatin1String("target")) {
499 insertTarget(TargetRec::Target, attributes, current);
500 goto done;
501 } else if (elementName == QLatin1String("contents")) {
502 insertTarget(TargetRec::Contents, attributes, current);
503 goto done;
504 } else if (elementName == QLatin1String("proxy")) {
505 node = new ProxyNode(parent, name);
506 if (!indexUrl.isEmpty())
507 location = Location(indexUrl + QLatin1Char('/') + name.toLower() + ".html");
508 else if (!indexUrl.isNull())
509 location = Location(name.toLower() + ".html");
510 } else {
511 goto done;
512 }
513
514 {
515 if (!href.isEmpty()) {
516 node->setUrl(href);
517 // Include the index URL if it exists
518 if (!node->isExternalPage() && !indexUrl.isEmpty())
519 node->setUrl(indexUrl + QLatin1Char('/') + href);
520 }
521
522 const QString access = attributes.value(QLatin1String("access")).toString();
523 if (access == "protected")
524 node->setAccess(Access::Protected);
525 else if ((access == "private") || (access == "internal"))
526 node->setAccess(Access::Private);
527 else
528 node->setAccess(Access::Public);
529
530 if (attributes.hasAttribute(QLatin1String("related"))) {
532 m_relatedNodes << node;
533 }
534
535 if (attributes.hasAttribute(QLatin1String("threadsafety"))) {
536 QString threadSafety = attributes.value(QLatin1String("threadsafety")).toString();
537 if (threadSafety == QLatin1String("non-reentrant"))
539 else if (threadSafety == QLatin1String("reentrant"))
541 else if (threadSafety == QLatin1String("thread safe"))
543 else
545 } else
547
548 const QString category = attributes.value(QLatin1String("comparison_category")).toString();
549 node->setComparisonCategory(comparisonCategoryFromString(category.toStdString()));
550
551 QString status = attributes.value(QLatin1String("status")).toString();
552 // TODO: "obsolete" is kept for backward compatibility, remove in the near future
553 if (status == QLatin1String("obsolete") || status == QLatin1String("deprecated"))
555 else if (status == QLatin1String("preliminary"))
557 else if (status == QLatin1String("internal"))
559 else if (status == QLatin1String("ignored"))
561 else
563
564 QString physicalModuleName = attributes.value(QLatin1String("module")).toString();
565 if (!physicalModuleName.isEmpty())
566 m_qdb->addToModule(physicalModuleName, node);
567
568 QString since = attributes.value(QLatin1String("since")).toString();
569 if (!since.isEmpty()) {
570 node->setSince(since);
571 }
572
573 if (attributes.hasAttribute(QLatin1String("documented"))) {
574 if (attributes.value(QLatin1String("documented")) == QLatin1String("true"))
575 node->setHadDoc();
576 }
577
578 QString groupsAttr = attributes.value(QLatin1String("groups")).toString();
579 if (!groupsAttr.isEmpty()) {
580 const QStringList groupNames = groupsAttr.split(QLatin1Char(','));
581 for (const auto &group : groupNames) {
582 m_qdb->addToGroup(group, node);
583 }
584 }
585
586 // Create some content for the node.
587 QSet<QString> emptySet;
588 Location t(filePath);
589 if (!filePath.isEmpty()) {
590 t.setLineNo(lineNo);
591 node->setLocation(t);
592 location = t;
593 }
594 Doc doc(location, location, QString(), emptySet, emptySet); // placeholder
595 node->setDoc(doc);
596 node->setIndexNodeFlag(); // Important: This node came from an index file.
597 QString briefAttr = attributes.value(QLatin1String("brief")).toString();
598 if (!briefAttr.isEmpty()) {
599 node->setReconstitutedBrief(briefAttr);
600 }
601
602 if (const auto sortKey = attributes.value(QLatin1String("sortkey")).toString(); !sortKey.isEmpty()) {
604 if (auto *metaMap = node->doc().metaTagMap())
605 metaMap->insert("sortkey", sortKey);
606 }
607 if (!hasReadChildren) {
608 bool useParent = (elementName == QLatin1String("namespace") && name.isEmpty());
609 while (reader.readNextStartElement()) {
610 if (useParent)
611 readIndexSection(reader, parent, indexUrl);
612 else
613 readIndexSection(reader, node, indexUrl);
614 }
615 }
616 }
617
618done:
619 while (!reader.isEndElement()) {
620 if (reader.readNext() == QXmlStreamReader::Invalid) {
621 break;
622 }
623 }
624}
625
626void QDocIndexFiles::insertTarget(TargetRec::TargetType type,
627 const QXmlStreamAttributes &attributes, Node *node)
628{
629 int priority;
630 switch (type) {
632 priority = 1;
633 break;
635 priority = 2;
636 break;
638 priority = 3;
639 break;
640 default:
641 return;
642 }
643
644 QString name = attributes.value(QLatin1String("name")).toString();
645 QString title = attributes.value(QLatin1String("title")).toString();
646 m_qdb->insertTarget(name, title, type, node, priority);
647}
648
649/*!
650 This function tries to resolve class inheritance immediately
651 after the index file is read. It is not always possible to
652 resolve a class inheritance at this point, because the base
653 class might be in an index file that hasn't been read yet, or
654 it might be in one of the header files that will be read for
655 the current module. These cases will be resolved after all
656 the index files and header and source files have been read,
657 just prior to beginning the generate phase for the current
658 module.
659
660 I don't think this is completely correct because it always
661 sets the access to public.
662 */
663void QDocIndexFiles::resolveIndex()
664{
665 for (const auto &pair : std::as_const(m_basesList)) {
666 const QStringList bases = pair.second.split(QLatin1Char(','));
667 for (const auto &base : bases) {
668 QStringList basePath = base.split(QString("::"));
669 Node *n = m_qdb->findClassNode(basePath);
670 if (n)
671 pair.first->addResolvedBaseClass(Access::Public, static_cast<ClassNode *>(n));
672 else
673 pair.first->addUnresolvedBaseClass(Access::Public, basePath);
674 }
675 }
676 // No longer needed.
677 m_basesList.clear();
678}
679
680static QString getAccessString(Access t)
681{
682
683 switch (t) {
684 case Access::Public:
685 return QLatin1String("public");
686 case Access::Protected:
687 return QLatin1String("protected");
688 case Access::Private:
689 return QLatin1String("private");
690 default:
691 break;
692 }
693 return QLatin1String("public");
694}
695
697{
698 switch (t) {
699 case Node::Deprecated:
700 return QLatin1String("deprecated");
702 return QLatin1String("preliminary");
703 case Node::Active:
704 return QLatin1String("active");
705 case Node::Internal:
706 return QLatin1String("internal");
708 return QLatin1String("ignored");
709 default:
710 break;
711 }
712 return QLatin1String("active");
713}
714
716{
717 switch (t) {
719 return QLatin1String("non-reentrant");
720 case Node::Reentrant:
721 return QLatin1String("reentrant");
722 case Node::ThreadSafe:
723 return QLatin1String("thread safe");
725 default:
726 break;
727 }
728 return QLatin1String("unspecified");
729}
730
731/*!
732 Returns the index of \a node in the list of related non-member nodes.
733*/
734int QDocIndexFiles::indexForNode(Node *node)
735{
736 qsizetype i = m_relatedNodes.indexOf(node);
737 if (i == -1) {
738 i = m_relatedNodes.size();
739 m_relatedNodes << node;
740 }
741 return i;
742}
743
744/*!
745 Adopts the related non-member node identified by \a index to the
746 parent \a adoptiveParent. Returns \c true if successful.
747*/
748bool QDocIndexFiles::adoptRelatedNode(Aggregate *adoptiveParent, int index)
749{
750 Node *related = m_relatedNodes.value(index);
751
752 if (adoptiveParent && related) {
753 adoptiveParent->adoptChild(related);
754 return true;
755 }
756
757 return false;
758}
759
760/*!
761 Write canonicalized versions of \\target and \\keyword identifiers
762 that appear in the documentation of \a node into the index using
763 \a writer, so that they can be used as link targets in external
764 documentation sets.
765*/
766void QDocIndexFiles::writeTargets(QXmlStreamWriter &writer, Node *node)
767{
768 if (node->doc().hasTargets()) {
769 for (const Atom *target : std::as_const(node->doc().targets())) {
770 const QString &title = target->string();
771 const QString &name{Utilities::asAsciiPrintable(title)};
772 writer.writeStartElement("target");
773 writer.writeAttribute("name", node->isExternalPage() ? title : name);
774 if (name != title)
775 writer.writeAttribute("title", title);
776 writer.writeEndElement(); // target
777 }
778 }
779 if (node->doc().hasKeywords()) {
780 for (const Atom *keyword : std::as_const(node->doc().keywords())) {
781 const QString &title = keyword->string();
782 const QString &name{Utilities::asAsciiPrintable(title)};
783 writer.writeStartElement("keyword");
784 writer.writeAttribute("name", name);
785 if (name != title)
786 writer.writeAttribute("title", title);
787 writer.writeEndElement(); // keyword
788 }
789 }
790}
791
792/*!
793 Generate the index section with the given \a writer for the \a node
794 specified, returning true if an element was written, and returning
795 false if an element is not written.
796
797 \note Function nodes are processed in generateFunctionSection()
798 */
799bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter &writer, Node *node,
800 IndexSectionWriter *post)
801{
802 if (m_gen == nullptr)
804
805 Q_ASSERT(m_gen);
806
807 post_ = nullptr;
808 /*
809 Don't include index nodes in a new index file.
810 */
811 if (node->isIndexNode())
812 return false;
813
814 QString nodeName;
815 QString logicalModuleName;
816 QString logicalModuleVersion;
817 QString qmlFullBaseName;
818 QString baseNameAttr;
819 QString moduleNameAttr;
820 QString moduleVerAttr;
821
822 switch (node->nodeType()) {
823 case Node::Namespace:
824 nodeName = "namespace";
825 break;
826 case Node::Class:
827 nodeName = "class";
828 break;
829 case Node::Struct:
830 nodeName = "struct";
831 break;
832 case Node::Union:
833 nodeName = "union";
834 break;
835 case Node::HeaderFile:
836 nodeName = "header";
837 break;
838 case Node::QmlType:
840 nodeName = (node->nodeType() == Node::QmlType) ? "qmlclass" : "qmlvaluetype";
841 logicalModuleName = node->logicalModuleName();
842 baseNameAttr = "qml-base-type";
843 moduleNameAttr = "qml-module-name";
844 moduleVerAttr = "qml-module-version";
845 qmlFullBaseName = node->qmlFullBaseName();
846 break;
847 case Node::Page:
848 case Node::Example:
850 nodeName = "page";
851 break;
852 case Node::Group:
853 nodeName = "group";
854 break;
855 case Node::Module:
856 nodeName = "module";
857 break;
858 case Node::QmlModule:
859 nodeName = "qmlmodule";
860 moduleNameAttr = "qml-module-name";
861 moduleVerAttr = "qml-module-version";
862 logicalModuleName = node->logicalModuleName();
863 logicalModuleVersion = node->logicalModuleVersion();
864 break;
865 case Node::Enum:
866 nodeName = "enum";
867 break;
868 case Node::TypeAlias:
869 case Node::Typedef:
870 nodeName = "typedef";
871 break;
872 case Node::Property:
873 nodeName = "property";
874 break;
875 case Node::Variable:
876 nodeName = "variable";
877 break;
879 if (!node->isPropertyGroup())
880 return false;
881 // Add an entry for property groups so that they can be linked to
882 nodeName = "qmlproperty";
883 break;
885 nodeName = "qmlproperty";
886 break;
887 case Node::Proxy:
888 nodeName = "proxy";
889 break;
890 case Node::Function: // Now processed in generateFunctionSection()
891 default:
892 return false;
893 }
894
895 QString objName = node->name();
896 // Special case: only the root node should have an empty name.
897 if (objName.isEmpty() && node != m_qdb->primaryTreeRoot())
898 return false;
899
900 writer.writeStartElement(nodeName);
901
902 if (!node->isTextPageNode() && !node->isCollectionNode() && !node->isHeader()) {
903 if (node->threadSafeness() != Node::UnspecifiedSafeness)
904 writer.writeAttribute("threadsafety", getThreadSafenessString(node->threadSafeness()));
905 }
906
907 writer.writeAttribute("name", objName);
908
909 // Write module and base type info for QML types
910 if (!moduleNameAttr.isEmpty()) {
911 if (!logicalModuleName.isEmpty())
912 writer.writeAttribute(moduleNameAttr, logicalModuleName);
913 if (!logicalModuleVersion.isEmpty())
914 writer.writeAttribute(moduleVerAttr, logicalModuleVersion);
915 }
916 if (!baseNameAttr.isEmpty() && !qmlFullBaseName.isEmpty())
917 writer.writeAttribute(baseNameAttr, qmlFullBaseName);
918
919 QString href;
920 if (!node->isExternalPage()) {
921 QString fullName = node->fullDocumentName();
922 if (fullName != objName)
923 writer.writeAttribute("fullname", fullName);
924 href = m_gen->fullDocumentLocation(node);
925 } else
926 href = node->name();
927 if (node->isQmlNode()) {
928 Aggregate *p = node->parent();
929 if (p && p->isQmlType() && p->isAbstract())
930 href.clear();
931 }
932 if (!href.isEmpty())
933 writer.writeAttribute("href", href);
934
935 writer.writeAttribute("status", getStatusString(node->status()));
936 if (!node->isTextPageNode() && !node->isCollectionNode() && !node->isHeader()) {
937 writer.writeAttribute("access", getAccessString(node->access()));
938 if (node->isAbstract())
939 writer.writeAttribute("abstract", "true");
940 }
941 const Location &declLocation = node->declLocation();
942 if (!declLocation.fileName().isEmpty())
943 writer.writeAttribute("location", declLocation.fileName());
944 if (m_storeLocationInfo && !declLocation.filePath().isEmpty()) {
945 writer.writeAttribute("filepath", declLocation.filePath());
946 writer.writeAttribute("lineno", QString("%1").arg(declLocation.lineNo()));
947 }
948
949 if (node->isRelatedNonmember())
950 writer.writeAttribute("related", QString::number(indexForNode(node)));
951
952 if (!node->since().isEmpty())
953 writer.writeAttribute("since", node->since());
954
955 if (node->hasDoc())
956 writer.writeAttribute("documented", "true");
957
958 QStringList groups = m_qdb->groupNamesForNode(node);
959 if (!groups.isEmpty())
960 writer.writeAttribute("groups", groups.join(QLatin1Char(',')));
961
962 if (const auto *metamap = node->doc().metaTagMap(); metamap)
963 if (const auto sortKey = metamap->value("sortkey"); !sortKey.isEmpty())
964 writer.writeAttribute("sortkey", sortKey);
965
966 QString brief = node->doc().trimmedBriefText(node->name()).toString();
967 switch (node->nodeType()) {
968 case Node::Class:
969 case Node::Struct:
970 case Node::Union: {
971 // Classes contain information about their base classes.
972 const auto *classNode = static_cast<const ClassNode *>(node);
973 const QList<RelatedClass> &bases = classNode->baseClasses();
974 QSet<QString> baseStrings;
975 for (const auto &related : bases) {
976 ClassNode *n = related.m_node;
977 if (n)
978 baseStrings.insert(n->fullName());
979 else if (!related.m_path.isEmpty())
980 baseStrings.insert(related.m_path.join(QLatin1String("::")));
981 }
982 if (!baseStrings.isEmpty()) {
983 QStringList baseStringsAsList = baseStrings.values();
984 baseStringsAsList.sort();
985 writer.writeAttribute("bases", baseStringsAsList.join(QLatin1Char(',')));
986 }
987 if (!node->physicalModuleName().isEmpty())
988 writer.writeAttribute("module", node->physicalModuleName());
989 if (!brief.isEmpty())
990 writer.writeAttribute("brief", brief);
991 if (auto category = node->comparisonCategory(); category != ComparisonCategory::None)
992 writer.writeAttribute("comparison_category", comparisonCategoryAsString(category));
993 } break;
994 case Node::HeaderFile: {
995 const auto *headerNode = static_cast<const HeaderNode *>(node);
996 if (!headerNode->physicalModuleName().isEmpty())
997 writer.writeAttribute("module", headerNode->physicalModuleName());
998 if (!brief.isEmpty())
999 writer.writeAttribute("brief", brief);
1000 writer.writeAttribute("title", headerNode->title());
1001 writer.writeAttribute("fulltitle", headerNode->fullTitle());
1002 writer.writeAttribute("subtitle", headerNode->subtitle());
1003 } break;
1004 case Node::Namespace: {
1005 const auto *namespaceNode = static_cast<const NamespaceNode *>(node);
1006 if (!namespaceNode->physicalModuleName().isEmpty())
1007 writer.writeAttribute("module", namespaceNode->physicalModuleName());
1008 if (!brief.isEmpty())
1009 writer.writeAttribute("brief", brief);
1010 } break;
1011 case Node::QmlValueType:
1012 case Node::QmlType: {
1013 const auto *qmlTypeNode = static_cast<const QmlTypeNode *>(node);
1014 writer.writeAttribute("title", qmlTypeNode->title());
1015 writer.writeAttribute("fulltitle", qmlTypeNode->fullTitle());
1016 writer.writeAttribute("subtitle", qmlTypeNode->subtitle());
1017 if (!brief.isEmpty())
1018 writer.writeAttribute("brief", brief);
1019 } break;
1020 case Node::Page:
1021 case Node::Example:
1022 case Node::ExternalPage: {
1023 if (node->isExample())
1024 writer.writeAttribute("subtype", "example");
1025 else if (node->isExternalPage())
1026 writer.writeAttribute("subtype", "externalpage");
1027 else
1028 writer.writeAttribute("subtype", (static_cast<PageNode*>(node)->isAttribution() ? "attribution" : "page"));
1029
1030 const auto *pageNode = static_cast<const PageNode *>(node);
1031 writer.writeAttribute("title", pageNode->title());
1032 writer.writeAttribute("fulltitle", pageNode->fullTitle());
1033 writer.writeAttribute("subtitle", pageNode->subtitle());
1034 if (!brief.isEmpty())
1035 writer.writeAttribute("brief", brief);
1036 } break;
1037 case Node::Group:
1038 case Node::Module:
1039 case Node::QmlModule: {
1040 const auto *collectionNode = static_cast<const CollectionNode *>(node);
1041 writer.writeAttribute("seen", collectionNode->wasSeen() ? "true" : "false");
1042 writer.writeAttribute("title", collectionNode->title());
1043 if (!collectionNode->subtitle().isEmpty())
1044 writer.writeAttribute("subtitle", collectionNode->subtitle());
1045 if (!collectionNode->physicalModuleName().isEmpty())
1046 writer.writeAttribute("module", collectionNode->physicalModuleName());
1047 if (!brief.isEmpty())
1048 writer.writeAttribute("brief", brief);
1049 } break;
1050 case Node::QmlProperty: {
1051 auto *qmlPropertyNode = static_cast<QmlPropertyNode *>(node);
1052 writer.writeAttribute("type", qmlPropertyNode->dataType());
1053 writer.writeAttribute("attached", qmlPropertyNode->isAttached() ? "true" : "false");
1054 writer.writeAttribute("writable", qmlPropertyNode->isReadOnly() ? "false" : "true");
1055 if (qmlPropertyNode->isRequired())
1056 writer.writeAttribute("required", "true");
1057 if (!brief.isEmpty())
1058 writer.writeAttribute("brief", brief);
1059 } break;
1060 case Node::Property: {
1061 const auto *propertyNode = static_cast<const PropertyNode *>(node);
1062
1064 writer.writeAttribute("bindable", "true");
1065
1066 if (!propertyNode->isWritable())
1067 writer.writeAttribute("writable", "false");
1068
1069 if (!brief.isEmpty())
1070 writer.writeAttribute("brief", brief);
1071 // Property access function names
1072 for (qsizetype i{0}; i < (qsizetype)PropertyNode::FunctionRole::NumFunctionRoles; ++i) {
1073 auto role{(PropertyNode::FunctionRole)i};
1074 for (const auto *fnNode : propertyNode->functions(role)) {
1075 writer.writeStartElement(PropertyNode::roleName(role));
1076 writer.writeAttribute("name", fnNode->name());
1077 writer.writeEndElement();
1078 }
1079 }
1080 } break;
1081 case Node::Variable: {
1082 const auto *variableNode = static_cast<const VariableNode *>(node);
1083 writer.writeAttribute("type", variableNode->dataType());
1084 writer.writeAttribute("static", variableNode->isStatic() ? "true" : "false");
1085 if (!brief.isEmpty())
1086 writer.writeAttribute("brief", brief);
1087 } break;
1088 case Node::Enum: {
1089 const auto *enumNode = static_cast<const EnumNode *>(node);
1090 if (enumNode->isScoped())
1091 writer.writeAttribute("scoped", "true");
1092 if (enumNode->flagsType())
1093 writer.writeAttribute("typedef", enumNode->flagsType()->fullDocumentName());
1094 const auto &items = enumNode->items();
1095 for (const auto &item : items) {
1096 writer.writeStartElement("value");
1097 writer.writeAttribute("name", item.name());
1098 writer.writeAttribute("value", item.value());
1099 if (!item.since().isEmpty())
1100 writer.writeAttribute("since", item.since());
1101 writer.writeEndElement(); // value
1102 }
1103 } break;
1104 case Node::Typedef: {
1105 const auto *typedefNode = static_cast<const TypedefNode *>(node);
1106 if (typedefNode->associatedEnum())
1107 writer.writeAttribute("enum", typedefNode->associatedEnum()->fullDocumentName());
1108 } break;
1109 case Node::TypeAlias:
1110 writer.writeAttribute("aliasedtype", static_cast<const TypeAliasNode *>(node)->aliasedType());
1111 break;
1112 case Node::Function: // Now processed in generateFunctionSection()
1113 default:
1114 break;
1115 }
1116
1117 writeTargets(writer, node);
1118
1119 /*
1120 Some nodes have a table of contents. For these, we close
1121 the opening tag, create sub-elements for the items in the
1122 table of contents, and then add a closing tag for the
1123 element. Elements for all other nodes are closed in the
1124 opening tag.
1125 */
1126 if (node->isPageNode() || node->isCollectionNode()) {
1128 for (int i = 0; i < node->doc().tableOfContents().size(); ++i) {
1129 Atom *item = node->doc().tableOfContents()[i];
1130 int level = node->doc().tableOfContentsLevels()[i];
1131 QString title = Text::sectionHeading(item).toString();
1132 writer.writeStartElement("contents");
1133 writer.writeAttribute("name", Tree::refForAtom(item));
1134 writer.writeAttribute("title", title);
1135 writer.writeAttribute("level", QString::number(level));
1136 writer.writeEndElement(); // contents
1137 }
1138 }
1139 }
1140 // WebXMLGenerator - skip the nested <page> elements for example
1141 // files/images, as the generator produces them separately
1142 if (node->isExample() && m_gen->format() != QLatin1String("WebXML")) {
1143 const auto *exampleNode = static_cast<const ExampleNode *>(node);
1144 const auto &files = exampleNode->files();
1145 for (const QString &file : files) {
1146 writer.writeStartElement("page");
1147 writer.writeAttribute("name", file);
1148 QString href = m_gen->linkForExampleFile(file);
1149 writer.writeAttribute("href", href);
1150 writer.writeAttribute("status", "active");
1151 writer.writeAttribute("subtype", "file");
1152 writer.writeAttribute("title", "");
1153 writer.writeAttribute("fulltitle", Generator::exampleFileTitle(exampleNode, file));
1154 writer.writeAttribute("subtitle", file);
1155 writer.writeEndElement(); // page
1156 }
1157 const auto &images = exampleNode->images();
1158 for (const QString &file : images) {
1159 writer.writeStartElement("page");
1160 writer.writeAttribute("name", file);
1161 QString href = m_gen->linkForExampleFile(file);
1162 writer.writeAttribute("href", href);
1163 writer.writeAttribute("status", "active");
1164 writer.writeAttribute("subtype", "image");
1165 writer.writeAttribute("title", "");
1166 writer.writeAttribute("fulltitle", Generator::exampleFileTitle(exampleNode, file));
1167 writer.writeAttribute("subtitle", file);
1168 writer.writeEndElement(); // page
1169 }
1170 }
1171 // Append to the section if the callback object was set
1172 if (post)
1173 post->append(writer, node);
1174
1175 post_ = post;
1176 return true;
1177}
1178
1179/*!
1180 This function writes a <function> element for \a fn to the
1181 index file using \a writer.
1182 */
1183void QDocIndexFiles::generateFunctionSection(QXmlStreamWriter &writer, FunctionNode *fn)
1184{
1185 if (fn->isInternal() && !Config::instance().showInternal())
1186 return;
1187
1188 const QString objName = fn->name();
1189 writer.writeStartElement("function");
1190 writer.writeAttribute("name", objName);
1191
1192 const QString fullName = fn->fullDocumentName();
1193 if (fullName != objName)
1194 writer.writeAttribute("fullname", fullName);
1195 const QString href = m_gen->fullDocumentLocation(fn);
1196 if (!href.isEmpty())
1197 writer.writeAttribute("href", href);
1198 if (fn->threadSafeness() != Node::UnspecifiedSafeness)
1199 writer.writeAttribute("threadsafety", getThreadSafenessString(fn->threadSafeness()));
1200 writer.writeAttribute("status", getStatusString(fn->status()));
1201 writer.writeAttribute("access", getAccessString(fn->access()));
1202
1203 const Location &declLocation = fn->declLocation();
1204 if (!declLocation.fileName().isEmpty())
1205 writer.writeAttribute("location", declLocation.fileName());
1206 if (m_storeLocationInfo && !declLocation.filePath().isEmpty()) {
1207 writer.writeAttribute("filepath", declLocation.filePath());
1208 writer.writeAttribute("lineno", QString("%1").arg(declLocation.lineNo()));
1209 }
1210
1211 if (fn->hasDoc())
1212 writer.writeAttribute("documented", "true");
1213 if (fn->isRelatedNonmember())
1214 writer.writeAttribute("related", QString::number(indexForNode(fn)));
1215 if (!fn->since().isEmpty())
1216 writer.writeAttribute("since", fn->since());
1217
1218 const QString brief = fn->doc().trimmedBriefText(fn->name()).toString();
1219 writer.writeAttribute("meta", fn->metanessString());
1220 if (fn->isCppNode()) {
1221 if (!fn->isNonvirtual())
1222 writer.writeAttribute("virtual", fn->virtualness());
1223
1224 if (fn->isConst())
1225 writer.writeAttribute("const", "true");
1226 if (fn->isStatic())
1227 writer.writeAttribute("static", "true");
1228 if (fn->isFinal())
1229 writer.writeAttribute("final", "true");
1230 if (fn->isOverride())
1231 writer.writeAttribute("override", "true");
1232 if (fn->isExplicit())
1233 writer.writeAttribute("explicit", "true");
1234 if (fn->isConstexpr())
1235 writer.writeAttribute("constexpr", "true");
1236
1237 if (auto noexcept_info = fn->getNoexcept()) {
1238 writer.writeAttribute("noexcept", "true");
1239 if (!(*noexcept_info).isEmpty()) writer.writeAttribute("noexcept_expression", *noexcept_info);
1240 }
1241
1242 /*
1243 This ensures that for functions that have overloads,
1244 the first function written is the one that is not an
1245 overload, and the overloads follow it immediately in
1246 the index file numbered from 1 to n.
1247 */
1248 if (fn->isOverload() && (fn->overloadNumber() > 0)) {
1249 writer.writeAttribute("overload", "true");
1250 writer.writeAttribute("overload-number", QString::number(fn->overloadNumber()));
1251 }
1252 if (fn->isRef())
1253 writer.writeAttribute("refness", QString::number(1));
1254 else if (fn->isRefRef())
1255 writer.writeAttribute("refness", QString::number(2));
1257 QStringList associatedProperties;
1258 for (const auto *node : fn->associatedProperties()) {
1259 associatedProperties << node->name();
1260 }
1261 associatedProperties.sort();
1262 writer.writeAttribute("associated-property",
1263 associatedProperties.join(QLatin1Char(',')));
1264 }
1265 }
1266
1267 const auto return_type = fn->returnType();
1268 if (!return_type.isEmpty())
1269 writer.writeAttribute("type", return_type);
1270
1271 if (fn->isCppNode()) {
1272 if (!brief.isEmpty())
1273 writer.writeAttribute("brief", brief);
1274
1275 /*
1276 Note: The "signature" attribute is written to the
1277 index file, but it is not read back in by qdoc. However,
1278 we need it for the webxml generator.
1279 */
1280 const QString signature = appendAttributesToSignature(fn);
1281 writer.writeAttribute("signature", signature);
1282
1283 QStringList groups = m_qdb->groupNamesForNode(fn);
1284 if (!groups.isEmpty())
1285 writer.writeAttribute("groups", groups.join(QLatin1Char(',')));
1286 }
1287
1288 for (int i = 0; i < fn->parameters().count(); ++i) {
1289 const Parameter &parameter = fn->parameters().at(i);
1290 writer.writeStartElement("parameter");
1291 writer.writeAttribute("type", parameter.type());
1292 writer.writeAttribute("name", parameter.name());
1293 writer.writeAttribute("default", parameter.defaultValue());
1294 writer.writeEndElement(); // parameter
1295 }
1296
1297 writeTargets(writer, fn);
1298
1299 // Append to the section if the callback object was set
1300 if (post_)
1301 post_->append(writer, fn);
1302
1303 writer.writeEndElement(); // function
1304}
1305
1306/*!
1307 \internal
1308
1309 Constructs the signature to be written to an index file for the function
1310 represented by FunctionNode \a fn.
1311
1312 'const' is already part of FunctionNode::signature(), which forms the basis
1313 for the signature returned by this method. The method adds, where
1314 applicable, the C++ keywords "final", "override", or "= 0", to the
1315 signature carried by the FunctionNode itself.
1316 */
1317QString QDocIndexFiles::appendAttributesToSignature(const FunctionNode *fn) const noexcept
1318{
1319 QString signature = fn->signature(Node::SignatureReturnType);
1320
1321 if (fn->isFinal())
1322 signature += " final";
1323 if (fn->isOverride())
1324 signature += " override";
1325 if (fn->isPureVirtual())
1326 signature += " = 0";
1327
1328 return signature;
1329}
1330
1331/*!
1332 Outputs a <function> element to the index for each FunctionNode in
1333 an \a aggregate, using \a writer.
1334 The \a aggregate has a function map that contains all the
1335 function nodes (a vector of overloads) indexed by function
1336 name.
1337
1338 If a function element represents an overload, it has an
1339 \c overload attribute set to \c true and an \c {overload-number}
1340 attribute set to the function's overload number.
1341 */
1342void QDocIndexFiles::generateFunctionSections(QXmlStreamWriter &writer, Aggregate *aggregate)
1343{
1344 for (auto functions : std::as_const(aggregate->functionMap())) {
1345 std::for_each(functions.begin(), functions.end(),
1346 [this,&writer](FunctionNode *fn) {
1347 generateFunctionSection(writer, fn);
1348 }
1349 );
1350 }
1351}
1352
1353/*!
1354 Generate index sections for the child nodes of the given \a node
1355 using the \a writer specified.
1356*/
1357void QDocIndexFiles::generateIndexSections(QXmlStreamWriter &writer, Node *node,
1358 IndexSectionWriter *post)
1359{
1360 /*
1361 Note that groups, modules, and QML modules are written
1362 after all the other nodes.
1363 */
1364 if (node->isCollectionNode() || node->isGroup() || node->isModule() || node->isQmlModule())
1365 return;
1366
1367 if (node->isInternal() && !Config::instance().showInternal())
1368 return;
1369
1370 if (generateIndexSection(writer, node, post)) {
1371 if (node->isAggregate()) {
1372 auto *aggregate = static_cast<Aggregate *>(node);
1373 // First write the function children, then write the nonfunction children.
1374 generateFunctionSections(writer, aggregate);
1375 const auto &nonFunctionList = aggregate->nonfunctionList();
1376 for (auto *node : nonFunctionList)
1377 generateIndexSections(writer, node, post);
1378 }
1379
1380 if (node == root_) {
1381 /*
1382 We wait until the end of the index file to output the group, module,
1383 and QML module elements. By outputting them at the end, when we read
1384 the index file back in, all the group, module, and QML module member
1385 elements will have already been created. It is then only necessary to
1386 create the group, module, or QML module element and add each member to
1387 its member list.
1388 */
1389 const CNMap &groups = m_qdb->groups();
1390 if (!groups.isEmpty()) {
1391 for (auto it = groups.constBegin(); it != groups.constEnd(); ++it) {
1392 if (generateIndexSection(writer, it.value(), post))
1393 writer.writeEndElement();
1394 }
1395 }
1396
1397 const CNMap &modules = m_qdb->modules();
1398 if (!modules.isEmpty()) {
1399 for (auto it = modules.constBegin(); it != modules.constEnd(); ++it) {
1400 if (generateIndexSection(writer, it.value(), post))
1401 writer.writeEndElement();
1402 }
1403 }
1404
1405 const CNMap &qmlModules = m_qdb->qmlModules();
1406 if (!qmlModules.isEmpty()) {
1407 for (auto it = qmlModules.constBegin(); it != qmlModules.constEnd(); ++it) {
1408 if (generateIndexSection(writer, it.value(), post))
1409 writer.writeEndElement();
1410 }
1411 }
1412 }
1413
1414 writer.writeEndElement();
1415 }
1416}
1417
1418/*!
1419 Writes a qdoc module index in XML to a file named \a fileName.
1420 \a url is the \c url attribute of the <INDEX> element.
1421 \a title is the \c title attribute of the <INDEX> element.
1422 \a g is a pointer to the current Generator in use, stored for later use.
1423 */
1424void QDocIndexFiles::generateIndex(const QString &fileName, const QString &url,
1425 const QString &title, Generator *g)
1426{
1427 QFile file(fileName);
1428 if (!file.open(QFile::WriteOnly | QFile::Text))
1429 return;
1430
1431 qCDebug(lcQdoc) << "Writing index file:" << fileName;
1432
1433 m_gen = g;
1434 m_relatedNodes.clear();
1435 QXmlStreamWriter writer(&file);
1436 writer.setAutoFormatting(true);
1437 writer.writeStartDocument();
1438 writer.writeDTD("<!DOCTYPE QDOCINDEX>");
1439
1440 writer.writeStartElement("INDEX");
1441 writer.writeAttribute("url", url);
1442 writer.writeAttribute("title", title);
1443 writer.writeAttribute("version", m_qdb->version());
1444 writer.writeAttribute("project", Config::instance().get(CONFIG_PROJECT).asString());
1445
1447 if (!root_->tree()->indexTitle().isEmpty())
1448 writer.writeAttribute("indexTitle", root_->tree()->indexTitle());
1449
1450 generateIndexSections(writer, root_, nullptr);
1451
1452 writer.writeEndElement(); // INDEX
1453 writer.writeEndElement(); // QDOCINDEX
1454 writer.writeEndDocument();
1455 file.close();
1456}
1457
1458QT_END_NAMESPACE
void adoptChild(Node *child)
This Aggregate becomes the adoptive parent of child.
const NodeList & nonfunctionList()
Returns a const reference to the list of child nodes of this aggregate that are not function nodes.
The Atom class is the fundamental unit for representing documents internally.
Definition atom.h:18
The ClassNode represents a C++ class.
Definition classnode.h:21
A class for holding the members of a collection of doc pages.
bool wasSeen() const override
Returns the seen flag data member of this node if it is a NamespaceNode or a CollectionNode.
Definition doc.h:31
bool hasTableOfContents() const
Definition doc.cpp:253
bool hasKeywords() const
Definition doc.cpp:258
bool hasTargets() const
Definition doc.cpp:263
QStringMultiMap * metaTagMap() const
Definition doc.cpp:292
void constructExtra() const
Definition doc.cpp:302
const TypedefNode * flagsType() const
Definition enumnode.h:35
bool isScoped() const
Definition enumnode.h:31
The ExternalPageNode represents an external documentation page.
This node is used to represent any kind of function being documented.
signed short overloadNumber() const
Returns the overload number for this function.
bool isOverride() const
bool isConstexpr() const
bool isNonvirtual() const
bool isPureVirtual() const
bool isExplicit() const
bool isOverload() const
bool isConst() const
bool isStatic() const override
Returns true if the FunctionNode represents a static function.
bool isFinal() const
Parameters & parameters()
bool hasAssociatedProperties() const
static Generator * currentGenerator()
Definition generator.h:57
virtual void append(QXmlStreamWriter &writer, Node *node)=0
The Location class provides a way to mark a location in a file.
Definition location.h:15
int lineNo() const
Returns the current line number.
Definition location.h:43
void setLineNo(int no)
Definition location.h:35
Location & operator=(const Location &other)
The assignment operator does a deep copy of the entire state of other into this Location.
Definition location.cpp:69
This class represents a C++ namespace.
Tree * tree() const override
Returns a pointer to the Tree that contains this NamespaceNode.
bool isExternalPage() const
Returns true if the node type is ExternalPage.
Definition node.h:134
const Doc & doc() const
Returns a reference to the node's Doc data member.
Definition node.h:271
bool isQmlNode() const
Returns true if this node's Genus value is QML.
Definition node.h:153
void setHadDoc()
Definition node.h:215
bool isGroup() const
Returns true if the node type is Group.
Definition node.h:139
void setAccess(Access t)
Sets the node's access type to t.
Definition node.h:203
void setIndexNodeFlag(bool isIndexNode=true)
Sets a flag in this Node that indicates the node was created for something in an index file.
Definition node.h:214
virtual bool isAbstract() const
Returns true if the ClassNode or QmlTypeNode is marked abstract.
Definition node.h:169
NodeType
An unsigned char value that identifies an object as a particular subclass of Node.
Definition node.h:54
@ Variable
Definition node.h:69
@ Module
Definition node.h:71
@ Struct
Definition node.h:58
@ QmlModule
Definition node.h:73
@ Typedef
Definition node.h:66
@ QmlValueType
Definition node.h:75
@ Function
Definition node.h:65
@ TypeAlias
Definition node.h:67
@ Union
Definition node.h:59
@ Page
Definition node.h:61
@ Group
Definition node.h:70
@ Enum
Definition node.h:62
@ HeaderFile
Definition node.h:60
@ QmlProperty
Definition node.h:74
@ QmlType
Definition node.h:72
@ SharedComment
Definition node.h:76
@ Namespace
Definition node.h:56
@ Proxy
Definition node.h:78
@ Property
Definition node.h:68
@ Class
Definition node.h:57
@ ExternalPage
Definition node.h:64
@ Example
Definition node.h:63
ComparisonCategory comparisonCategory() const
Definition node.h:217
bool isQmlType() const
Returns true if the node type is QmlType or QmlValueType.
Definition node.h:155
bool isHeader() const
Returns true if the node type is HeaderFile.
Definition node.h:140
virtual bool isPageNode() const
Returns true if this node represents something that generates a documentation page.
Definition node.h:182
virtual bool isTextPageNode() const
Returns true if the node is a PageNode but not an Aggregate.
Definition node.h:187
Aggregate * parent() const
Returns the node's parent pointer.
Definition node.h:244
void setLocation(const Location &t)
Sets the node's declaration location, its definition location, or both, depending on the suffix of th...
Definition node.cpp:886
virtual bool isAggregate() const
Returns true if this node is an aggregate, which means it inherits Aggregate and can therefore have c...
Definition node.h:170
NodeType nodeType() const
Returns this node's type.
Definition node.h:121
virtual void setRelatedNonmember(bool b)
Sets a flag in the node indicating whether this node is a related nonmember of something.
Definition node.h:218
void setComparisonCategory(const ComparisonCategory &category)
Definition node.h:216
virtual Tree * tree() const
Returns a pointer to the Tree this node is in.
Definition node.cpp:876
const Location & declLocation() const
Returns the Location where this node's declaration was seen.
Definition node.h:265
void setDoc(const Doc &doc, bool replace=false)
Sets this Node's Doc to doc.
Definition node.cpp:546
bool isModule() const
Returns true if the node type is Module.
Definition node.h:142
virtual bool isPropertyGroup() const
Returns true if the node is a SharedCommentNode for documenting multiple C++ properties or multiple Q...
Definition node.h:185
ThreadSafeness
An unsigned char that specifies the degree of thread-safeness of the element.
Definition node.h:97
@ ThreadSafe
Definition node.h:101
@ NonReentrant
Definition node.h:99
@ UnspecifiedSafeness
Definition node.h:98
@ Reentrant
Definition node.h:100
virtual void setAbstract(bool)
If this node is a ClassNode or a QmlTypeNode, the node's abstract flag data member is set to b.
Definition node.h:222
bool hasDoc() const
Returns true if this node is documented, or it represents a documented node read from the index ('had...
Definition node.cpp:906
LinkType
An unsigned char value that probably should be moved out of the Node base class.
Definition node.h:112
bool isCppNode() const
Returns true if this node's Genus value is CPP.
Definition node.h:130
void setStatus(Status t)
Sets the node's status to t.
Definition node.cpp:560
virtual bool isCollectionNode() const
Returns true if this is an instance of CollectionNode.
Definition node.h:178
void setThreadSafeness(ThreadSafeness t)
Sets the node's thread safeness to t.
Definition node.h:207
Status
An unsigned char that specifies the status of the documentation element in the documentation set.
Definition node.h:89
@ Internal
Definition node.h:93
@ Active
Definition node.h:92
@ DontDocument
Definition node.h:94
@ Deprecated
Definition node.h:90
@ Preliminary
Definition node.h:91
bool isQmlModule() const
Returns true if the node type is QmlModule.
Definition node.h:152
bool isExample() const
Returns true if the node type is Example.
Definition node.h:133
bool isIndexNode() const
Returns true if this node was created from something in an index file.
Definition node.h:141
A PageNode is a Node that generates a documentation page.
Definition pagenode.h:18
void markAttribution()
Definition pagenode.h:49
bool isAttribution() const
Definition pagenode.h:50
The Parameter class describes one function parameter.
Definition parameters.h:20
const Parameter & at(int i) const
Definition parameters.h:74
int count() const
Definition parameters.h:72
This class describes one instance of using the Q_PROPERTY macro.
PropertyType propertyType() const
bool isWritable() const
A class for representing an Aggregate that is documented in a different module.
Definition proxynode.h:14
This class provides exclusive access to the qdoc database, which consists of a forrest of trees and a...
static QDocDatabase * qdocDB()
Creates the singleton.
const CNMap & qmlModules()
Returns a const reference to the collection of all QML module nodes in the primary tree.
NamespaceNode * primaryTreeRoot()
Returns a pointer to the root node of the primary tree.
const CNMap & modules()
Returns a const reference to the collection of all module nodes in the primary tree.
const CNMap & groups()
Returns a const reference to the collection of all group nodes in the primary tree.
This class handles qdoc index files.
bool isRequired()
Returns true if this QML property is marked with \required or the corresponding C++ property uses the...
bool isReadOnly()
Returns true if this QML property or attached property is read-only.
bool isAttached() const override
Returns true if the QML property or QML method node is marked as attached.
const EnumNode * associatedEnum() const
Definition typedefnode.h:26
bool isStatic() const override
Returns true if the FunctionNode represents a static function.
static std::string comparisonCategoryAsString(ComparisonCategory category)
#define CONFIG_URL
Definition config.h:391
#define CONFIG_PROJECT
Definition config.h:373
#define CONFIG_LOCATIONINFO
Definition config.h:360
Combined button and popup list for selecting options.
QMap< QString, CollectionNode * > CNMap
Definition node.h:48
static QString getStatusString(Node::Status t)
static QString getAccessString(Access t)
static IndexSectionWriter * post_
static QString getThreadSafenessString(Node::ThreadSafeness t)
@ QDocAttrFile
@ QDocAttrAttribution
@ QDocAttrImage
@ QDocAttrExternalPage
@ QDocAttrExample
@ QDocAttrNone
@ QDocAttrDocument
static Node * root_
A record of a linkable target within the documentation.
Definition tree.h:25
TargetType
A type of a linkable target record.
Definition tree.h:27
@ Keyword
Definition tree.h:27
@ Target
Definition tree.h:27
@ Contents
Definition tree.h:27