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