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