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