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("noexcept")) == QLatin1String("true")) {
499 fn->markNoexcept(attributes.value("noexcept_expression").toString());
500 }
501
502 qsizetype refness = attributes.value(QLatin1String("refness")).toUInt();
503 if (refness == 1)
504 fn->setRef(true);
505 else if (refness == 2)
506 fn->setRefRef(true);
507 /*
508 Theoretically, this should ensure that each function
509 node receives the same overload number and overload
510 flag it was written with, and it should be unnecessary
511 to call normalizeOverloads() for index nodes.
512 */
513 if (attributes.value(QLatin1String("overload")) == QLatin1String("true"))
514 fn->setOverloadNumber(attributes.value(QLatin1String("overload-number")).toUInt());
515 else
516 fn->setOverloadNumber(0);
517 }
518
519 /*
520 Note: The "signature" attribute was written to the
521 index file, but it is not read back in. That is ok
522 because we reconstruct the parameter list and the
523 return type, from which the signature was built in
524 the first place and from which it can be rebuilt.
525 */
526 while (reader.readNextStartElement()) {
527 QXmlStreamAttributes childAttributes = reader.attributes();
528 if (reader.name() == QLatin1String("parameter")) {
529 QString type = childAttributes.value(QLatin1String("type")).toString();
530 QString name = childAttributes.value(QLatin1String("name")).toString();
531 QString default_ = childAttributes.value(QLatin1String("default")).toString();
532 fn->parameters().append(type, name, default_);
533 } else if (reader.name() == QLatin1String("keyword")) {
534 insertTarget(TargetRec::Keyword, childAttributes, fn);
535 } else if (reader.name() == QLatin1String("target")) {
536 insertTarget(TargetRec::Target, childAttributes, fn);
537 }
538 reader.skipCurrentElement();
539 }
540
541 node = fn;
542 if (!indexUrl.isEmpty())
543 location = Location(indexUrl + QLatin1Char('/') + parent->name().toLower() + ".html");
544 else if (!indexUrl.isNull())
545 location = Location(parent->name().toLower() + ".html");
546
547 hasReadChildren = true;
548 } else if (elementName == QLatin1String("variable")) {
549 auto *varNode = new VariableNode(parent, name);
550 varNode->setLeftType(attributes.value("type").toString());
551 varNode->setStatic((attributes.value("static").toString() == "true") ? true : false);
552 node = varNode;
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 } else if (elementName == QLatin1String("keyword")) {
558 insertTarget(TargetRec::Keyword, attributes, current);
559 goto done;
560 } else if (elementName == QLatin1String("target")) {
561 insertTarget(TargetRec::Target, attributes, current);
562 goto done;
563 } else if (elementName == QLatin1String("contents")) {
564 insertTarget(TargetRec::Contents, attributes, current);
565 goto done;
566 } else if (elementName == QLatin1String("proxy")) {
567 node = new ProxyNode(parent, name);
568 if (!indexUrl.isEmpty())
569 location = Location(indexUrl + QLatin1Char('/') + name.toLower() + ".html");
570 else if (!indexUrl.isNull())
571 location = Location(name.toLower() + ".html");
572 } else {
573 goto done;
574 }
575
576 {
577 if (!href.isEmpty()) {
578 node->setUrl(href);
579 // Include the index URL if it exists
580 if (!node->isExternalPage() && !indexUrl.isEmpty())
581 node->setUrl(indexUrl + QLatin1Char('/') + href);
582 }
583
584 const QString access = attributes.value(QLatin1String("access")).toString();
585 if (access == "protected")
587 else if (access == "private")
589 else
591
592 if (attributes.hasAttribute(QLatin1String("related"))) {
594 m_relatedNodes << node;
595 }
596
597 if (attributes.hasAttribute(QLatin1String("threadsafety"))) {
598 QString threadSafety = attributes.value(QLatin1String("threadsafety")).toString();
599 if (threadSafety == QLatin1String("non-reentrant"))
601 else if (threadSafety == QLatin1String("reentrant"))
603 else if (threadSafety == QLatin1String("thread safe"))
605 else
607 } else
609
610 const QString category = attributes.value(QLatin1String("comparison_category")).toString();
611 node->setComparisonCategory(comparisonCategoryFromString(category.toStdString()));
612
613 QString status = attributes.value(QLatin1String("status")).toString();
614 // TODO: "obsolete" is kept for backward compatibility, remove in the near future
615 if (status == QLatin1String("obsolete") || status == QLatin1String("deprecated"))
617 else if (status == QLatin1String("preliminary"))
619 else if (status == QLatin1String("internal"))
621 else if (status == QLatin1String("ignored"))
623 else
625
626 QString physicalModuleName = attributes.value(QLatin1String("module")).toString();
627 if (!physicalModuleName.isEmpty())
628 m_qdb->addToModule(physicalModuleName, node);
629
630 QString since = attributes.value(QLatin1String("since")).toString();
631 if (!since.isEmpty()) {
632 node->setSince(since);
633 }
634
635 if (attributes.hasAttribute(QLatin1String("documented"))) {
636 if (attributes.value(QLatin1String("documented")) == QLatin1String("true"))
637 node->setHadDoc();
638 }
639
640 QString groupsAttr = attributes.value(QLatin1String("groups")).toString();
641 if (!groupsAttr.isEmpty()) {
642 const QStringList groupNames = groupsAttr.split(QLatin1Char(','));
643 for (const auto &group : groupNames) {
644 m_qdb->addToGroup(group, node);
645 }
646 }
647
648 // Create some content for the node.
649 QSet<QString> emptySet;
650 Location t(filePath);
651 if (!filePath.isEmpty()) {
652 t.setLineNo(lineNo);
653 node->setLocation(t);
654 location = t;
655 }
656 Doc doc(location, location, QString(), emptySet, emptySet); // placeholder
657 node->setDoc(doc);
658 node->setIndexNodeFlag(); // Important: This node came from an index file.
659 QString briefAttr = attributes.value(QLatin1String("brief")).toString();
660 if (!briefAttr.isEmpty()) {
661 node->setReconstitutedBrief(briefAttr);
662 }
663
664 if (const auto sortKey = attributes.value(QLatin1String("sortkey")).toString(); !sortKey.isEmpty()) {
666 if (auto *metaMap = node->doc().metaTagMap())
667 metaMap->insert("sortkey", sortKey);
668 }
669 if (!hasReadChildren) {
670 bool useParent = (elementName == QLatin1String("namespace") && name.isEmpty());
671 while (reader.readNextStartElement()) {
672 if (useParent)
673 readIndexSection(reader, parent, indexUrl);
674 else
675 readIndexSection(reader, node, indexUrl);
676 }
677 }
678 }
679
680done:
681 while (!reader.isEndElement()) {
682 if (reader.readNext() == QXmlStreamReader::Invalid) {
683 break;
684 }
685 }
686}
687
688void QDocIndexFiles::insertTarget(TargetRec::TargetType type,
689 const QXmlStreamAttributes &attributes, Node *node)
690{
691 int priority;
692 switch (type) {
694 priority = 1;
695 break;
697 priority = 2;
698 break;
700 priority = 3;
701 break;
702 default:
703 return;
704 }
705
706 QString name = attributes.value(QLatin1String("name")).toString();
707 QString title = attributes.value(QLatin1String("title")).toString();
708 m_qdb->insertTarget(name, title, type, node, priority);
709}
710
711/*!
712 This function tries to resolve class inheritance immediately
713 after the index file is read. It is not always possible to
714 resolve a class inheritance at this point, because the base
715 class might be in an index file that hasn't been read yet, or
716 it might be in one of the header files that will be read for
717 the current module. These cases will be resolved after all
718 the index files and header and source files have been read,
719 just prior to beginning the generate phase for the current
720 module.
721
722 I don't think this is completely correct because it always
723 sets the access to public.
724 */
725void QDocIndexFiles::resolveIndex()
726{
727 for (const auto &pair : std::as_const(m_basesList)) {
728 const QStringList bases = pair.second.split(QLatin1Char(','));
729 for (const auto &base : bases) {
730 QStringList basePath = base.split(QString("::"));
731 Node *n = m_qdb->findClassNode(basePath);
732 if (n)
733 pair.first->addResolvedBaseClass(Access::Public, static_cast<ClassNode *>(n));
734 else
735 pair.first->addUnresolvedBaseClass(Access::Public, basePath);
736 }
737 }
738 // No longer needed.
739 m_basesList.clear();
740}
741
743{
744
745 switch (t) {
746 case Access::Public:
747 return QLatin1String("public");
749 return QLatin1String("protected");
750 case Access::Private:
751 return QLatin1String("private");
752 default:
753 break;
754 }
755 return QLatin1String("public");
756}
757
759{
760 switch (t) {
761 case Node::Deprecated:
762 return QLatin1String("deprecated");
764 return QLatin1String("preliminary");
765 case Node::Active:
766 return QLatin1String("active");
767 case Node::Internal:
768 return QLatin1String("internal");
770 return QLatin1String("ignored");
771 default:
772 break;
773 }
774 return QLatin1String("active");
775}
776
778{
779 switch (t) {
781 return QLatin1String("non-reentrant");
782 case Node::Reentrant:
783 return QLatin1String("reentrant");
784 case Node::ThreadSafe:
785 return QLatin1String("thread safe");
787 default:
788 break;
789 }
790 return QLatin1String("unspecified");
791}
792
793/*!
794 Returns the index of \a node in the list of related non-member nodes.
795*/
796int QDocIndexFiles::indexForNode(Node *node)
797{
798 qsizetype i = m_relatedNodes.indexOf(node);
799 if (i == -1) {
800 i = m_relatedNodes.size();
801 m_relatedNodes << node;
802 }
803 return i;
804}
805
806/*!
807 Adopts the related non-member node identified by \a index to the
808 parent \a adoptiveParent. Returns \c true if successful.
809*/
810bool QDocIndexFiles::adoptRelatedNode(Aggregate *adoptiveParent, int index)
811{
812 Node *related = m_relatedNodes.value(index);
813
814 if (adoptiveParent && related) {
815 adoptiveParent->adoptChild(related);
816 return true;
817 }
818
819 return false;
820}
821
822/*!
823 Write canonicalized versions of \\target and \\keyword identifiers
824 that appear in the documentation of \a node into the index using
825 \a writer, so that they can be used as link targets in external
826 documentation sets.
827*/
828void QDocIndexFiles::writeTargets(QXmlStreamWriter &writer, Node *node)
829{
830 if (node->doc().hasTargets()) {
831 for (const Atom *target : std::as_const(node->doc().targets())) {
832 const QString &title = target->string();
833 const QString &name{Utilities::asAsciiPrintable(title)};
834 writer.writeStartElement("target");
835 writer.writeAttribute("name", node->isExternalPage() ? title : name);
836 if (name != title)
837 writer.writeAttribute("title", title);
838 writer.writeEndElement(); // target
839 }
840 }
841 if (node->doc().hasKeywords()) {
842 for (const Atom *keyword : std::as_const(node->doc().keywords())) {
843 const QString &title = keyword->string();
844 const QString &name{Utilities::asAsciiPrintable(title)};
845 writer.writeStartElement("keyword");
846 writer.writeAttribute("name", name);
847 if (name != title)
848 writer.writeAttribute("title", title);
849 writer.writeEndElement(); // keyword
850 }
851 }
852}
853
854/*!
855 Generate the index section with the given \a writer for the \a node
856 specified, returning true if an element was written, and returning
857 false if an element is not written.
858
859 \note Function nodes are processed in generateFunctionSection()
860 */
861bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter &writer, Node *node,
862 IndexSectionWriter *post)
863{
864 if (m_gen == nullptr)
866
867 Q_ASSERT(m_gen);
868
869 post_ = nullptr;
870 /*
871 Don't include index nodes in a new index file.
872 */
873 if (node->isIndexNode())
874 return false;
875
876 QString nodeName;
877 QString logicalModuleName;
878 QString logicalModuleVersion;
879 QString qmlFullBaseName;
880 QString baseNameAttr;
881 QString moduleNameAttr;
882 QString moduleVerAttr;
883
884 switch (node->nodeType()) {
886 nodeName = "namespace";
887 break;
888 case NodeType::Class:
889 nodeName = "class";
890 break;
891 case NodeType::Struct:
892 nodeName = "struct";
893 break;
894 case NodeType::Union:
895 nodeName = "union";
896 break;
898 nodeName = "header";
899 break;
901 nodeName = "qmlenum";
902 break;
905 nodeName = (node->nodeType() == NodeType::QmlType) ? "qmlclass" : "qmlvaluetype";
906 logicalModuleName = node->logicalModuleName();
907 baseNameAttr = "qml-base-type";
908 moduleNameAttr = "qml-module-name";
909 moduleVerAttr = "qml-module-version";
910 qmlFullBaseName = node->qmlFullBaseName();
911 break;
912 case NodeType::Page:
915 nodeName = "page";
916 break;
917 case NodeType::Group:
918 nodeName = "group";
919 break;
920 case NodeType::Module:
921 nodeName = "module";
922 break;
924 nodeName = "qmlmodule";
925 moduleNameAttr = "qml-module-name";
926 moduleVerAttr = "qml-module-version";
927 logicalModuleName = node->logicalModuleName();
928 logicalModuleVersion = node->logicalModuleVersion();
929 break;
930 case NodeType::Enum:
931 nodeName = "enum";
932 break;
935 nodeName = "typedef";
936 break;
938 nodeName = "property";
939 break;
941 nodeName = "variable";
942 break;
944 if (!node->isPropertyGroup())
945 return false;
946 // Add an entry for property groups so that they can be linked to
947 nodeName = "qmlproperty";
948 break;
950 nodeName = "qmlproperty";
951 break;
952 case NodeType::Proxy:
953 nodeName = "proxy";
954 break;
955 case NodeType::Function: // Now processed in generateFunctionSection()
956 default:
957 return false;
958 }
959
960 QString objName = node->name();
961 // Special case: only the root node should have an empty name.
962 if (objName.isEmpty() && node != m_qdb->primaryTreeRoot())
963 return false;
964
965 writer.writeStartElement(nodeName);
966
967 if (!node->isTextPageNode() && !node->isCollectionNode() && !node->isHeader()) {
968 if (node->threadSafeness() != Node::UnspecifiedSafeness)
969 writer.writeAttribute("threadsafety", getThreadSafenessString(node->threadSafeness()));
970 }
971
972 writer.writeAttribute("name", objName);
973
974 if (node->isPropertyGroup())
975 writer.writeAttribute("propertygroup", "true");
977 writer.writeAttribute("inpropertygroup", node->sharedCommentNode()->fullDocumentName());
978
979 // Write module and base type info for QML types
980 if (!moduleNameAttr.isEmpty()) {
981 if (!logicalModuleName.isEmpty())
982 writer.writeAttribute(moduleNameAttr, logicalModuleName);
983 if (!logicalModuleVersion.isEmpty())
984 writer.writeAttribute(moduleVerAttr, logicalModuleVersion);
985 }
986 if (!baseNameAttr.isEmpty() && !qmlFullBaseName.isEmpty())
987 writer.writeAttribute(baseNameAttr, qmlFullBaseName);
988 else if (!baseNameAttr.isEmpty()) {
989 auto qmlBase = static_cast<QmlTypeNode *>(node)->qmlBaseName();
990 if (!qmlBase.isEmpty())
991 writer.writeAttribute(baseNameAttr, qmlBase);
992 }
993
994 QString href;
995 if (!node->isExternalPage()) {
996 QString fullName = node->fullDocumentName();
997 if (fullName != objName)
998 writer.writeAttribute("fullname", fullName);
999 href = m_gen->fullDocumentLocation(node);
1000 } else
1001 href = node->name();
1002 if (node->isQmlNode()) {
1003 Aggregate *p = node->parent();
1004 if (p && p->isQmlType() && p->isAbstract())
1005 href.clear();
1006 }
1007 if (!href.isEmpty())
1008 writer.writeAttribute("href", href);
1009
1010 writer.writeAttribute("status", getStatusString(node->status()));
1011 if (!node->isTextPageNode() && !node->isCollectionNode() && !node->isHeader()) {
1012 writer.writeAttribute("access", getAccessString(node->access()));
1013 if (node->isAbstract())
1014 writer.writeAttribute("abstract", "true");
1015 }
1016 const Location &declLocation = node->declLocation();
1017 if (!declLocation.fileName().isEmpty())
1018 writer.writeAttribute("location", declLocation.fileName());
1019 if (m_storeLocationInfo && !declLocation.filePath().isEmpty()) {
1020 writer.writeAttribute("filepath", declLocation.filePath());
1021 writer.writeAttribute("lineno", QString("%1").arg(declLocation.lineNo()));
1022 }
1023
1024 if (node->isRelatedNonmember())
1025 writer.writeAttribute("related", QString::number(indexForNode(node)));
1026
1027 if (!node->since().isEmpty())
1028 writer.writeAttribute("since", node->since());
1029
1030 if (node->hasDoc())
1031 writer.writeAttribute("documented", "true");
1032
1033 QStringList groups = m_qdb->groupNamesForNode(node);
1034 if (!groups.isEmpty())
1035 writer.writeAttribute("groups", groups.join(QLatin1Char(',')));
1036
1037 if (const auto *metamap = node->doc().metaTagMap(); metamap)
1038 if (const auto sortKey = metamap->value("sortkey"); !sortKey.isEmpty())
1039 writer.writeAttribute("sortkey", sortKey);
1040
1041 QString brief = node->doc().trimmedBriefText(node->name()).toString();
1042 switch (node->nodeType()) {
1043 case NodeType::Class:
1044 case NodeType::Struct:
1045 case NodeType::Union: {
1046 // Classes contain information about their base classes.
1047 const auto *classNode = static_cast<const ClassNode *>(node);
1048 const QList<RelatedClass> &bases = classNode->baseClasses();
1049 QSet<QString> baseStrings;
1050 for (const auto &related : bases) {
1051 ClassNode *n = related.m_node;
1052 if (n)
1053 baseStrings.insert(n->fullName());
1054 else if (!related.m_path.isEmpty())
1055 baseStrings.insert(related.m_path.join(QLatin1String("::")));
1056 }
1057 if (!baseStrings.isEmpty()) {
1058 QStringList baseStringsAsList = baseStrings.values();
1059 baseStringsAsList.sort();
1060 writer.writeAttribute("bases", baseStringsAsList.join(QLatin1Char(',')));
1061 }
1062 if (!node->physicalModuleName().isEmpty())
1063 writer.writeAttribute("module", node->physicalModuleName());
1064 if (!brief.isEmpty())
1065 writer.writeAttribute("brief", brief);
1066 if (auto category = node->comparisonCategory(); category != ComparisonCategory::None)
1067 writer.writeAttribute("comparison_category", comparisonCategoryAsString(category));
1068 } break;
1069 case NodeType::HeaderFile: {
1070 const auto *headerNode = static_cast<const HeaderNode *>(node);
1071 if (!headerNode->physicalModuleName().isEmpty())
1072 writer.writeAttribute("module", headerNode->physicalModuleName());
1073 if (!brief.isEmpty())
1074 writer.writeAttribute("brief", brief);
1075 writer.writeAttribute("title", headerNode->title());
1076 writer.writeAttribute("fulltitle", headerNode->fullTitle());
1077 writer.writeAttribute("subtitle", headerNode->subtitle());
1078 } break;
1079 case NodeType::Namespace: {
1080 const auto *namespaceNode = static_cast<const NamespaceNode *>(node);
1081 if (!namespaceNode->physicalModuleName().isEmpty())
1082 writer.writeAttribute("module", namespaceNode->physicalModuleName());
1083 if (!brief.isEmpty())
1084 writer.writeAttribute("brief", brief);
1085 } break;
1087 case NodeType::QmlType: {
1088 const auto *qmlTypeNode = static_cast<const QmlTypeNode *>(node);
1089 if (qmlTypeNode->isSingleton())
1090 writer.writeAttribute("singleton", "true");
1091 if (!brief.isEmpty())
1092 writer.writeAttribute("brief", brief);
1093 } break;
1094 case NodeType::Page:
1095 case NodeType::Example:
1097 if (node->isExample())
1098 writer.writeAttribute("subtype", "example");
1099 else if (node->isExternalPage())
1100 writer.writeAttribute("subtype", "externalpage");
1101 else
1102 writer.writeAttribute("subtype", (static_cast<PageNode*>(node)->isAttribution() ? "attribution" : "page"));
1103
1104 const auto *pageNode = static_cast<const PageNode *>(node);
1105 writer.writeAttribute("title", pageNode->title());
1106 writer.writeAttribute("fulltitle", pageNode->fullTitle());
1107 writer.writeAttribute("subtitle", pageNode->subtitle());
1108 if (!brief.isEmpty())
1109 writer.writeAttribute("brief", brief);
1110 } break;
1111 case NodeType::Group:
1112 case NodeType::Module:
1113 case NodeType::QmlModule: {
1114 const auto *collectionNode = static_cast<const CollectionNode *>(node);
1115 writer.writeAttribute("seen", collectionNode->wasSeen() ? "true" : "false");
1116 writer.writeAttribute("title", collectionNode->title());
1117 if (!collectionNode->subtitle().isEmpty())
1118 writer.writeAttribute("subtitle", collectionNode->subtitle());
1119 if (!collectionNode->physicalModuleName().isEmpty())
1120 writer.writeAttribute("module", collectionNode->physicalModuleName());
1121 if (!brief.isEmpty())
1122 writer.writeAttribute("brief", brief);
1123 } break;
1124 case NodeType::QmlProperty: {
1125 auto *qmlPropertyNode = static_cast<QmlPropertyNode *>(node);
1126 writer.writeAttribute("type", qmlPropertyNode->dataType());
1127 writer.writeAttribute("attached", qmlPropertyNode->isAttached() ? "true" : "false");
1128 writer.writeAttribute("writable", qmlPropertyNode->isReadOnly() ? "false" : "true");
1129 if (qmlPropertyNode->isRequired())
1130 writer.writeAttribute("required", "true");
1131 if (!brief.isEmpty())
1132 writer.writeAttribute("brief", brief);
1133 } break;
1134 case NodeType::Property: {
1135 const auto *propertyNode = static_cast<const PropertyNode *>(node);
1136
1138 writer.writeAttribute("bindable", "true");
1139
1140 if (!propertyNode->isWritable())
1141 writer.writeAttribute("writable", "false");
1142
1143 if (propertyNode->isConstant())
1144 writer.writeAttribute("constant", "true");
1145
1146 writer.writeAttribute("dataType", propertyNode->dataType());
1147
1148 if (!brief.isEmpty())
1149 writer.writeAttribute("brief", brief);
1150 // Property access function names
1151 for (qsizetype i{0}; i < (qsizetype)PropertyNode::FunctionRole::NumFunctionRoles; ++i) {
1152 auto role{(PropertyNode::FunctionRole)i};
1153 for (const auto *fnNode : propertyNode->functions(role)) {
1154 writer.writeStartElement(PropertyNode::roleName(role));
1155 writer.writeAttribute("name", fnNode->name());
1156 writer.writeEndElement();
1157 }
1158 }
1159 } break;
1160 case NodeType::Variable: {
1161 const auto *variableNode = static_cast<const VariableNode *>(node);
1162 writer.writeAttribute("type", variableNode->dataType());
1163 writer.writeAttribute("static", variableNode->isStatic() ? "true" : "false");
1164 if (!brief.isEmpty())
1165 writer.writeAttribute("brief", brief);
1166 } break;
1167 case NodeType::QmlEnum:
1168 case NodeType::Enum: {
1169 const auto *enumNode = static_cast<const EnumNode *>(node);
1170 if (enumNode->isScoped())
1171 writer.writeAttribute("scoped", "true");
1172 if (enumNode->flagsType())
1173 writer.writeAttribute("typedef", enumNode->flagsType()->fullDocumentName());
1174 if (enumNode->isAnonymous())
1175 writer.writeAttribute("anonymous", "true");
1176 const auto &items = enumNode->items();
1177 for (const auto &item : items) {
1178 writer.writeStartElement("value");
1179 writer.writeAttribute("name", item.name());
1180 if (node->isEnumType(Genus::CPP))
1181 writer.writeAttribute("value", item.value());
1182 if (!item.since().isEmpty())
1183 writer.writeAttribute("since", item.since());
1184 writer.writeEndElement(); // value
1185 }
1186 } break;
1187 case NodeType::Typedef: {
1188 const auto *typedefNode = static_cast<const TypedefNode *>(node);
1189 if (typedefNode->associatedEnum())
1190 writer.writeAttribute("enum", typedefNode->associatedEnum()->fullDocumentName());
1191 } break;
1193 writer.writeAttribute("aliasedtype", static_cast<const TypeAliasNode *>(node)->aliasedType());
1194 break;
1195 case NodeType::Function: // Now processed in generateFunctionSection()
1196 default:
1197 break;
1198 }
1199
1200 writeTargets(writer, node);
1201
1202 /*
1203 Some nodes have a table of contents. For these, we close
1204 the opening tag, create sub-elements for the items in the
1205 table of contents, and then add a closing tag for the
1206 element. Elements for all other nodes are closed in the
1207 opening tag.
1208 */
1209 if (node->isPageNode() || node->isCollectionNode()) {
1211 for (int i = 0; i < node->doc().tableOfContents().size(); ++i) {
1212 Atom *item = node->doc().tableOfContents()[i];
1213 int level = node->doc().tableOfContentsLevels()[i];
1214 QString title = Text::sectionHeading(item).toString();
1215 writer.writeStartElement("contents");
1216 writer.writeAttribute("name", Tree::refForAtom(item));
1217 writer.writeAttribute("title", title);
1218 writer.writeAttribute("level", QString::number(level));
1219 writer.writeEndElement(); // contents
1220 }
1221 }
1222 }
1223 // WebXMLGenerator - skip the nested <page> elements for example
1224 // files/images, as the generator produces them separately
1225 if (node->isExample() && m_gen->format() != QLatin1String("WebXML")) {
1226 const auto *exampleNode = static_cast<const ExampleNode *>(node);
1227 const auto &files = exampleNode->files();
1228 for (const QString &file : files) {
1229 writer.writeStartElement("page");
1230 writer.writeAttribute("name", file);
1231 QString href = m_gen->linkForExampleFile(file);
1232 writer.writeAttribute("href", href);
1233 writer.writeAttribute("status", "active");
1234 writer.writeAttribute("subtype", "file");
1235 writer.writeAttribute("title", "");
1236 writer.writeAttribute("fulltitle", Generator::exampleFileTitle(exampleNode, file));
1237 writer.writeAttribute("subtitle", file);
1238 writer.writeEndElement(); // page
1239 }
1240 const auto &images = exampleNode->images();
1241 for (const QString &file : images) {
1242 writer.writeStartElement("page");
1243 writer.writeAttribute("name", file);
1244 QString href = m_gen->linkForExampleFile(file);
1245 writer.writeAttribute("href", href);
1246 writer.writeAttribute("status", "active");
1247 writer.writeAttribute("subtype", "image");
1248 writer.writeAttribute("title", "");
1249 writer.writeAttribute("fulltitle", Generator::exampleFileTitle(exampleNode, file));
1250 writer.writeAttribute("subtitle", file);
1251 writer.writeEndElement(); // page
1252 }
1253 }
1254 // Append to the section if the callback object was set
1255 if (post)
1256 post->append(writer, node);
1257
1258 post_ = post;
1259 return true;
1260}
1261
1262/*!
1263 This function writes a <function> element for \a fn to the
1264 index file using \a writer.
1265 */
1266void QDocIndexFiles::generateFunctionSection(QXmlStreamWriter &writer, FunctionNode *fn)
1267{
1268 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
1269 const NodeContext context = fn->createContext();
1270 if (!InclusionFilter::isPubliclyVisible(policy, context))
1271 return;
1272
1273 const QString objName = fn->name();
1274 writer.writeStartElement("function");
1275 writer.writeAttribute("name", objName);
1276
1277 const QString fullName = fn->fullDocumentName();
1278 if (fullName != objName)
1279 writer.writeAttribute("fullname", fullName);
1280 const QString href = m_gen->fullDocumentLocation(fn);
1281 if (!href.isEmpty())
1282 writer.writeAttribute("href", href);
1283 if (fn->threadSafeness() != Node::UnspecifiedSafeness)
1284 writer.writeAttribute("threadsafety", getThreadSafenessString(fn->threadSafeness()));
1285 writer.writeAttribute("status", getStatusString(fn->status()));
1286 writer.writeAttribute("access", getAccessString(fn->access()));
1287
1288 const Location &declLocation = fn->declLocation();
1289 if (!declLocation.fileName().isEmpty())
1290 writer.writeAttribute("location", declLocation.fileName());
1291 if (m_storeLocationInfo && !declLocation.filePath().isEmpty()) {
1292 writer.writeAttribute("filepath", declLocation.filePath());
1293 writer.writeAttribute("lineno", QString("%1").arg(declLocation.lineNo()));
1294 }
1295
1296 if (fn->hasDoc())
1297 writer.writeAttribute("documented", "true");
1298 if (fn->isRelatedNonmember())
1299 writer.writeAttribute("related", QString::number(indexForNode(fn)));
1300 if (!fn->since().isEmpty())
1301 writer.writeAttribute("since", fn->since());
1302
1303 const QString brief = fn->doc().trimmedBriefText(fn->name()).toString();
1304 writer.writeAttribute("meta", fn->metanessString());
1305 if (fn->isCppNode()) {
1306 if (!fn->isNonvirtual())
1307 writer.writeAttribute("virtual", fn->virtualness());
1308
1309 if (fn->isConst())
1310 writer.writeAttribute("const", "true");
1311 if (fn->isStatic())
1312 writer.writeAttribute("static", "true");
1313 if (fn->isFinal())
1314 writer.writeAttribute("final", "true");
1315 if (fn->isOverride())
1316 writer.writeAttribute("override", "true");
1317 if (fn->isExplicit())
1318 writer.writeAttribute("explicit", "true");
1319 if (fn->isConstexpr())
1320 writer.writeAttribute("constexpr", "true");
1321
1322 if (auto noexcept_info = fn->getNoexcept()) {
1323 writer.writeAttribute("noexcept", "true");
1324 if (!(*noexcept_info).isEmpty()) writer.writeAttribute("noexcept_expression", *noexcept_info);
1325 }
1326
1327 /*
1328 This ensures that for functions that have overloads,
1329 the first function written is the one that is not an
1330 overload, and the overloads follow it immediately in
1331 the index file numbered from 1 to n.
1332 */
1333 if (fn->isOverload() && (fn->overloadNumber() > 0)) {
1334 writer.writeAttribute("overload", "true");
1335 writer.writeAttribute("overload-number", QString::number(fn->overloadNumber()));
1336 }
1337 if (fn->isRef())
1338 writer.writeAttribute("refness", QString::number(1));
1339 else if (fn->isRefRef())
1340 writer.writeAttribute("refness", QString::number(2));
1342 QStringList associatedProperties;
1343 for (const auto *node : fn->associatedProperties()) {
1344 associatedProperties << node->name();
1345 }
1346 associatedProperties.sort();
1347 writer.writeAttribute("associated-property",
1348 associatedProperties.join(QLatin1Char(',')));
1349 }
1350 } else {
1351 if (fn->isAttached())
1352 writer.writeAttribute("attached", "true");
1353 }
1354
1355 const auto &return_type = fn->returnType();
1356 if (!return_type.isEmpty())
1357 writer.writeAttribute("type", std::move(return_type));
1358
1359 const auto &declared_return_type = fn->declaredReturnType();
1360 if (declared_return_type.has_value())
1361 writer.writeAttribute("declaredtype", declared_return_type.value());
1362
1363 if (fn->isCppNode()) {
1364 if (!brief.isEmpty())
1365 writer.writeAttribute("brief", brief);
1366
1367 /*
1368 Note: The "signature" attribute is written to the
1369 index file, but it is not read back in by qdoc. However,
1370 we need it for the webxml generator.
1371 */
1372 const QString signature = appendAttributesToSignature(fn);
1373 writer.writeAttribute("signature", signature);
1374
1375 QStringList groups = m_qdb->groupNamesForNode(fn);
1376 if (!groups.isEmpty())
1377 writer.writeAttribute("groups", groups.join(QLatin1Char(',')));
1378 }
1379
1380 for (int i = 0; i < fn->parameters().count(); ++i) {
1381 const Parameter &parameter = fn->parameters().at(i);
1382 writer.writeStartElement("parameter");
1383 writer.writeAttribute("type", parameter.type());
1384 writer.writeAttribute("name", parameter.name());
1385 writer.writeAttribute("default", parameter.defaultValue());
1386 writer.writeEndElement(); // parameter
1387 }
1388
1389 writeTargets(writer, fn);
1390
1391 // Append to the section if the callback object was set
1392 if (post_)
1393 post_->append(writer, fn);
1394
1395 writer.writeEndElement(); // function
1396}
1397
1398/*!
1399 \internal
1400
1401 Constructs the signature to be written to an index file for the function
1402 represented by FunctionNode \a fn.
1403
1404 'const' is already part of FunctionNode::signature(), which forms the basis
1405 for the signature returned by this method. The method adds, where
1406 applicable, the C++ keywords "final", "override", or "= 0", to the
1407 signature carried by the FunctionNode itself.
1408 */
1409QString QDocIndexFiles::appendAttributesToSignature(const FunctionNode *fn) const noexcept
1410{
1411 QString signature = fn->signature(Node::SignatureReturnType);
1412
1413 if (fn->isFinal())
1414 signature += " final";
1415 if (fn->isOverride())
1416 signature += " override";
1417 if (fn->isPureVirtual())
1418 signature += " = 0";
1419
1420 return signature;
1421}
1422
1423/*!
1424 Outputs a <function> element to the index for each FunctionNode in
1425 an \a aggregate, using \a writer.
1426 The \a aggregate has a function map that contains all the
1427 function nodes (a vector of overloads) indexed by function
1428 name.
1429
1430 If a function element represents an overload, it has an
1431 \c overload attribute set to \c true and an \c {overload-number}
1432 attribute set to the function's overload number.
1433 */
1434void QDocIndexFiles::generateFunctionSections(QXmlStreamWriter &writer, Aggregate *aggregate)
1435{
1436 for (auto functions : std::as_const(aggregate->functionMap())) {
1437 std::for_each(functions.begin(), functions.end(),
1438 [this,&writer](FunctionNode *fn) {
1439 generateFunctionSection(writer, fn);
1440 }
1441 );
1442 }
1443}
1444
1445/*!
1446 Generate index sections for the child nodes of the given \a node
1447 using the \a writer specified.
1448*/
1449void QDocIndexFiles::generateIndexSections(QXmlStreamWriter &writer, Node *node,
1450 IndexSectionWriter *post)
1451{
1452 /*
1453 Note that groups, modules, QML modules, and proxies are written
1454 after all the other nodes.
1455 */
1456 if (node->isCollectionNode() || node->isGroup() || node->isModule() ||
1458 return;
1459
1460 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
1461 const NodeContext context = node->createContext();
1462 if (!InclusionFilter::isPubliclyVisible(policy, context))
1463 return;
1464
1465 if (generateIndexSection(writer, node, post)) {
1466 if (node->isAggregate()) {
1467 auto *aggregate = static_cast<Aggregate *>(node);
1468 // First write the function children, then write the nonfunction children.
1469 generateFunctionSections(writer, aggregate);
1470 const auto &nonFunctionList = aggregate->nonfunctionList();
1471 for (auto *node : nonFunctionList)
1472 generateIndexSections(writer, node, post);
1473 }
1474
1475 if (node == root_) {
1476 /*
1477 We wait until the end of the index file to output the group, module,
1478 QML module, and proxy nodes. By outputting them at the end, when we read
1479 the index file back in, all the group/module/proxy member
1480 nodes will have already been created. It is then only necessary to
1481 create the collection node and add each member to its member list.
1482 */
1483 const CNMap &groups = m_qdb->groups();
1484 if (!groups.isEmpty()) {
1485 for (auto it = groups.constBegin(); it != groups.constEnd(); ++it) {
1486 if (generateIndexSection(writer, it.value(), post))
1487 writer.writeEndElement();
1488 }
1489 }
1490
1491 const CNMap &modules = m_qdb->modules();
1492 if (!modules.isEmpty()) {
1493 for (auto it = modules.constBegin(); it != modules.constEnd(); ++it) {
1494 if (generateIndexSection(writer, it.value(), post))
1495 writer.writeEndElement();
1496 }
1497 }
1498
1499 const CNMap &qmlModules = m_qdb->qmlModules();
1500 if (!qmlModules.isEmpty()) {
1501 for (auto it = qmlModules.constBegin(); it != qmlModules.constEnd(); ++it) {
1502 if (generateIndexSection(writer, it.value(), post))
1503 writer.writeEndElement();
1504 }
1505 }
1506
1507 for (auto *p : m_qdb->primaryTree()->proxies()) {
1508 if (generateIndexSection(writer, p, post)) {
1509 auto aggregate = static_cast<Aggregate *>(p);
1510 generateFunctionSections(writer, aggregate);
1511 for (auto *n : aggregate->nonfunctionList())
1512 generateIndexSections(writer, n, post);
1513 writer.writeEndElement();
1514 }
1515 }
1516 }
1517
1518 writer.writeEndElement();
1519 }
1520}
1521
1522/*!
1523 Writes a qdoc module index in XML to a file named \a fileName.
1524 \a url is the \c url attribute of the <INDEX> element.
1525 \a title is the \c title attribute of the <INDEX> element.
1526 \a g is a pointer to the current Generator in use, stored for later use.
1527 */
1528void QDocIndexFiles::generateIndex(const QString &fileName, const QString &url,
1529 const QString &title)
1530{
1531 QFile file(fileName);
1532 if (!file.open(QFile::WriteOnly | QFile::Text))
1533 return;
1534
1535 qCDebug(lcQdoc) << "Writing index file:" << fileName;
1536
1538 m_relatedNodes.clear();
1539 QXmlStreamWriter writer(&file);
1540 writer.setAutoFormatting(true);
1541 writer.writeStartDocument();
1542 writer.writeDTD("<!DOCTYPE QDOCINDEX>");
1543
1544 writer.writeStartElement("INDEX");
1545 writer.writeAttribute("url", url);
1546 writer.writeAttribute("title", title);
1547 writer.writeAttribute("version", m_qdb->version());
1548 writer.writeAttribute("project", Config::instance().get(CONFIG_PROJECT).asString());
1549
1551 if (!root_->tree()->indexTitle().isEmpty())
1552 writer.writeAttribute("indexTitle", root_->tree()->indexTitle());
1553
1554 generateIndexSections(writer, root_, nullptr);
1555
1556 writer.writeEndElement(); // INDEX
1557 writer.writeEndElement(); // QDOCINDEX
1558 writer.writeEndDocument();
1559 file.close();
1560}
1561
1562QT_END_NAMESPACE
Access
Definition access.h:11
void adoptChild(Node *child)
This Aggregate becomes the adoptive parent of child.
const NodeList & nonfunctionList()
Returns a const reference to the list of child nodes of this aggregate that are not function nodes.
The Atom class is the fundamental unit for representing documents internally.
Definition atom.h:19
The ClassNode represents a C++ class.
Definition classnode.h:23
A class for holding the members of a collection of doc pages.
bool wasSeen() const override
Returns the seen flag data member of this node if it is a NamespaceNode or a CollectionNode.
Definition doc.h:31
bool hasTableOfContents() const
Definition doc.cpp:261
bool hasKeywords() const
Definition doc.cpp:266
bool hasTargets() const
Definition doc.cpp:271
QStringMultiMap * metaTagMap() const
Definition doc.cpp:300
void constructExtra() const
Definition doc.cpp:310
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
bool isAnonymous() const
Definition enumnode.h:34
const TypedefNode * flagsType() const
Definition enumnode.h:38
bool isScoped() const
Definition enumnode.h:33
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 isNonvirtual() const
bool isExplicit() const
bool isOverload() const
bool isConst() const
bool isAttached() const override
Returns true if the QML property or QML method node is marked as attached.
bool isStatic() const override
Returns true if the FunctionNode represents a static function.
bool isFinal() const
Parameters & parameters()
bool hasAssociatedProperties() const
static Generator * currentGenerator()
Definition generator.h:71
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:75
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
bool isAttribution() const
Definition pagenode.h:51
The Parameter class describes one function parameter.
Definition parameters.h:20
This class describes one instance of using the Q_PROPERTY macro.
bool isConstant() const
PropertyType propertyType() const
bool isWritable() const
A class for representing an Aggregate that is documented in a different module.
Definition proxynode.h:14
This class provides exclusive access to the qdoc database, which consists of a forrest of trees and a...
static QDocDatabase * qdocDB()
Creates the singleton.
const CNMap & qmlModules()
Returns a const reference to the collection of all QML module nodes in the primary tree.
NamespaceNode * primaryTreeRoot()
Returns a pointer to the root node of the primary tree.
const CNMap & modules()
Returns a const reference to the collection of all module nodes in the primary tree.
const CNMap & groups()
Returns a const reference to the collection of all group nodes in the primary tree.
This class handles qdoc index files.
bool isRequired()
Returns true if this QML property is marked with \required or the corresponding C++ property uses the...
bool isReadOnly()
Returns true if this QML property or attached property is read-only.
bool isAttached() const override
Returns true if the QML property or QML method node is marked as attached.
bool isSingleton() const
Definition qmltypenode.h:31
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
const EnumNode * associatedEnum() const
Definition typedefnode.h:27
bool isStatic() const override
Returns true if the FunctionNode represents a static function.
static std::string comparisonCategoryAsString(ComparisonCategory category)
#define CONFIG_URL
Definition config.h:440
#define CONFIG_PROJECT
Definition config.h:419
#define CONFIG_LOCATIONINFO
Definition config.h:404
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:51
static QHash< QString, SharedCommentNode * > sharedDocNodes_
static QString getStatusString(Node::Status t)
static QString getAccessString(Access t)
static IndexSectionWriter * post_
static QString getThreadSafenessString(Node::ThreadSafeness t)
@ QDocAttrFile
@ QDocAttrAttribution
@ QDocAttrImage
@ QDocAttrExternalPage
@ QDocAttrExample
@ QDocAttrNone
@ QDocAttrDocument
static Node * root_
@ Public
Definition access.h:11
@ Private
Definition access.h:11
@ Protected
Definition access.h:11
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:106
const Doc & doc() const
Returns a reference to the node's Doc data member.
Definition node.h:242
bool isQmlNode() const
Returns true if this node's Genus value is QML.
Definition node.h:126
void setHadDoc()
Definition node.h:189
bool isGroup() const
Returns true if the node type is Group.
Definition node.h:111
void setAccess(Access t)
Sets the node's access type to t.
Definition node.h:177
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:188
virtual bool isAbstract() const
Returns true if the ClassNode or QmlTypeNode is marked abstract.
Definition node.h:142
SharedCommentNode * sharedCommentNode()
Definition node.h:258
ComparisonCategory comparisonCategory() const
Definition node.h:191
bool isQmlType() const
Returns true if the node type is QmlType or QmlValueType.
Definition node.h:128
bool isHeader() const
Returns true if the node type is HeaderFile.
Definition node.h:112
NodeType nodeType() const override
Returns this node's type.
Definition node.h:89
virtual bool isPageNode() const
Returns true if this node represents something that generates a documentation page.
Definition node.h:155
bool isEnumType() const
Returns true if the node type is Enum.
Definition node.h:100
virtual bool isTextPageNode() const
Returns true if the node is a PageNode but not an Aggregate.
Definition node.h:160
Aggregate * parent() const
Returns the node's parent pointer.
Definition node.h:215
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:885
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:143
virtual void setRelatedNonmember(bool b)
Sets a flag in the node indicating whether this node is a related nonmember of something.
Definition node.h:192
void setComparisonCategory(const ComparisonCategory &category)
Definition node.h:190
bool isProxyNode() const
Returns true if the node type is Proxy.
Definition node.h:120
virtual Tree * tree() const
Returns a pointer to the Tree this node is in.
Definition node.cpp:875
const Location & declLocation() const
Returns the Location where this node's declaration was seen.
Definition node.h:236
NodeContext createContext() const
Definition node.cpp:162
void setDoc(const Doc &doc, bool replace=false)
Sets this Node's Doc to doc.
Definition node.cpp:542
bool isModule() const
Returns true if the node type is Module.
Definition node.h:114
virtual bool isPropertyGroup() const
Returns true if the node is a SharedCommentNode for documenting multiple C++ properties or multiple Q...
Definition node.h:158
ThreadSafeness
An unsigned char that specifies the degree of thread-safeness of the element.
Definition node.h:65
@ ThreadSafe
Definition node.h:69
@ NonReentrant
Definition node.h:67
@ UnspecifiedSafeness
Definition node.h:66
@ Reentrant
Definition node.h:68
bool isSharingComment() const
This function returns true if the node is sharing a comment with other nodes.
Definition node.h:256
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:196
bool hasDoc() const
Returns true if this node is documented, or it represents a documented node read from the index ('had...
Definition node.cpp:905
bool isCppNode() const
Returns true if this node's Genus value is CPP.
Definition node.h:98
void setStatus(Status t)
Sets the node's status to t.
Definition node.cpp:556
virtual bool isCollectionNode() const
Returns true if this is an instance of CollectionNode.
Definition node.h:151
void setThreadSafeness(ThreadSafeness t)
Sets the node's thread safeness to t.
Definition node.h:181
Status
An unsigned char that specifies the status of the documentation element in the documentation set.
Definition node.h:57
@ Internal
Definition node.h:61
@ Active
Definition node.h:60
@ DontDocument
Definition node.h:62
@ Deprecated
Definition node.h:58
@ Preliminary
Definition node.h:59
bool isQmlModule() const
Returns true if the node type is QmlModule.
Definition node.h:125
@ SignatureReturnType
Definition node.h:75
bool isExample() const
Returns true if the node type is Example.
Definition node.h:105
bool isIndexNode() const
Returns true if this node was created from something in an index file.
Definition node.h:113
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