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