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