21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
38
39
40
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
60
61
62
63
64
65
66
67Tree::Tree(
const QString &camelCaseModuleName, QDocDatabase *qdb)
68 : m_camelCaseModuleName(camelCaseModuleName),
69 m_physicalModuleName(camelCaseModuleName.toLower()),
71 m_root(
nullptr, QString())
73 m_root.setPhysicalModuleName(m_physicalModuleName);
78
79
80
81
82
83
84
85
86
87
90 qDeleteAll(m_nodesByTargetRef);
91 m_nodesByTargetRef.clear();
92 m_nodesByTargetTitle.clear();
98
99
100
101
102
103Node *
Tree::findNodeForInclude(
const QStringList &path)
const
105 Node *n = findClassNode(path);
107 n = findNamespaceNode(path);
112
113
114
115
116
117
118
121 QStringList path = name.split(QLatin1String(
"::"));
122 return static_cast<Aggregate *>(findNodeRecursive(path, 0,
const_cast<NamespaceNode *>(root()),
123 &Node::isFirstClassAggregate));
127
128
129
130
131
134 if (start ==
nullptr)
136 return static_cast<ClassNode *>(findNodeRecursive(path, 0, start, &Node::isClassNode));
140
141
142
143
147 return static_cast<NamespaceNode *>(findNodeRecursive(path, 0, start, &Node::isNamespace));
151
152
153
154
155
156
157
158
159
160
161
164 Node *n = findNodeRecursive(path, 0, root(), &Node::isRelatableType);
169
170
171
172void Tree::addPropertyFunction(
PropertyNode *property,
const QString &funcName,
175 m_unresolvedPropertyMap[property].insert(funcRole, funcName);
179
180
181
182
183
184
185
188 for (
auto it = n->constBegin(); it != n->constEnd(); ++it) {
189 if ((*it)->isClassNode()) {
190 auto *cn =
static_cast<
ClassNode *>(*it);
191 QList<RelatedClass> &bases = cn->baseClasses();
192 for (
auto &base : bases) {
193 if (base.m_node ==
nullptr) {
194 Node *n = m_qdb->findClassNode(base.m_path);
196
197
198
199
200
201
202
203
204
206 Aggregate *parent = cn->parent();
207 if (parent !=
nullptr)
209 if (parent->isNamespace() && !parent->name().isEmpty())
210 n = findClassNode(base.m_path, parent);
213 auto *bcn =
static_cast<ClassNode *>(n);
215 bcn->addDerivedClass(base.m_access, cn);
219 resolveBaseClasses(cn);
220 }
else if ((*it)->isNamespace()) {
227
230 for (
auto node = n->constBegin(); node != n->constEnd(); ++node) {
231 if ((*node)->isClassNode()) {
232 auto *cn =
static_cast<
ClassNode *>(*node);
233 for (
auto property = cn->constBegin(); property != cn->constEnd(); ++property) {
234 if ((*property)->isProperty())
235 cn->resolvePropertyOverriddenFromPtrs(
static_cast<
PropertyNode *>(*property));
237 resolvePropertyOverriddenFromPtrs(cn);
238 }
else if ((*node)->isNamespace()) {
239 resolvePropertyOverriddenFromPtrs(
static_cast<
NamespaceNode *>(*node));
245
246
247
248
249
250void Tree::resolveProperties()
252 for (
auto propEntry = m_unresolvedPropertyMap.constBegin();
253 propEntry != m_unresolvedPropertyMap.constEnd(); ++propEntry) {
256 QString getterName = (*propEntry)[PropertyNode::FunctionRole::Getter];
257 QString setterName = (*propEntry)[PropertyNode::FunctionRole::Setter];
258 QString resetterName = (*propEntry)[PropertyNode::FunctionRole::Resetter];
259 QString notifierName = (*propEntry)[PropertyNode::FunctionRole::Notifier];
260 QString bindableName = (*propEntry)[PropertyNode::FunctionRole::Bindable];
262 for (
auto it = parent->constBegin(); it != parent->constEnd(); ++it) {
263 if ((*it)->isFunction()) {
265 if (function->access() == property
->access()
266 && (function->status() == property
->status() || function->doc().isEmpty())) {
267 if (function->name() == getterName) {
269 }
else if (function->name() == setterName) {
271 }
else if (function->name() == resetterName) {
273 }
else if (function->name() == notifierName) {
275 }
else if (function->name() == bindableName) {
283 for (
auto propEntry = m_unresolvedPropertyMap.constBegin();
284 propEntry != m_unresolvedPropertyMap.constEnd(); ++propEntry) {
291 m_unresolvedPropertyMap.clear();
295
296
297
298
299void Tree::resolveCppToQmlLinks()
302 const NodeList &children = m_root.childNodes();
303 for (
auto *child : children) {
304 if (child->isQmlType()) {
305 auto *qcn =
static_cast<QmlTypeNode *>(child);
306 auto *cn =
const_cast<ClassNode *>(qcn->classNode());
308 cn->insertQmlNativeType(qcn);
314
315
316
317
318
319
320
321
324 for (
auto *child : aggregate.childNodes()) {
327 if (child->isEnumType())
328 resolveEnumValueSince(
static_cast<EnumNode&>(*child));
329 if (!child->isAggregate())
331 if (!child->since().isEmpty())
334 if (
const auto collectionNode = m_qdb->getModuleNode(child))
335 child->setSince(collectionNode->since());
337 resolveSince(
static_cast<Aggregate&>(*child));
342
343
344
345
346
347
348
349
352 const QStringList enumItems{en.doc().enumItemNames()};
353 const Atom *atom = en
.doc().body().firstAtom();
359 if (
auto val = atom->string(); enumItems.contains(val)) {
367
368
369
370
371
372
373
374
377 if (rootNode ==
nullptr)
380 for (
auto node = rootNode->constBegin(); node != rootNode->constEnd(); ++node) {
381 if ((*node)->isClassNode())
383 else if ((*node)->isNamespace())
384 removePrivateAndInternalBases(
static_cast<
NamespaceNode *>(*node));
389
393 const auto &baseClasses = classNode->baseClasses();
394 for (
const auto &relatedClass : baseClasses) {
395 if (relatedClass.m_node !=
nullptr) {
396 result += relatedClass.m_node;
397 result += allBaseClasses(relatedClass.m_node);
404
405
406
407
408
409
412 return findNodeRecursive(path, 0, root(), isMatch);
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431Node *
Tree::findNodeRecursive(
const QStringList &path,
int pathIndex,
const Node *start,
432 bool (Node::*isMatch)()
const)
const
434 if (start ==
nullptr || path.isEmpty())
436 Node *node =
const_cast<
Node *>(start);
438 return ((pathIndex >= path.size()) ? node :
nullptr);
439 auto *current =
static_cast<
Aggregate *>(node);
441 const QString &name = path.at(pathIndex);
442 for (
auto *node : children) {
445 if (node->name() == name) {
446 if (pathIndex + 1 >= path.size()) {
447 if ((node->*(isMatch))())
451 node = findNodeRecursive(path, pathIndex + 1, node, isMatch);
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476const Node *
Tree::findNodeForTarget(
const QStringList &path,
const QString &target,
480 const Node *node =
nullptr;
484 auto set_ref_from_target = [
this, &ref, &target](
const Node *n) ->
const Node* {
485 if (!target.isEmpty()) {
486 if (ref = getRef(target, n); ref.isEmpty())
493 if (node = findPageNodeByTitle(path.at(0)); node) {
494 if (node = set_ref_from_target(node); node)
499 const TargetRec *result = findUnambiguousTarget(path.join(QLatin1String(
"::")), genus);
502 if (node = set_ref_from_target(result
->m_node); node) {
514 const Node *current = start ? start : root();
516
517
518
519
520
523 && path.size() >= 2 && !path[0].isEmpty()) {
524 if (
auto *qcn = lookupQmlType(path.sliced(0, 2).join(QLatin1String(
"::"))); qcn) {
527 if (path.size() == 2)
528 return set_ref_from_target(qcn);
535 if (
const Node *match = matchPathAndTarget(
536 path, path_idx, target, current, flags, genus, ref);
544 if (node && result) {
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573const Node *
Tree::matchPathAndTarget(
const QStringList &path,
int idx,
const QString &target,
578
579
580
581
582 if (idx == path.size()) {
583 if (!target.isEmpty()) {
584 ref = getRef(target, node);
593 QString name = path.at(idx);
596 static_cast<
const Aggregate *>(node)->findChildren(name, nodes);
597 for (
const auto *child : std::as_const(nodes)) {
598 if (genus != Node::DontCare && !(genus & child->genus()))
600 const Node *t = matchPathAndTarget(path, idx + 1, target, child, flags, genus, ref);
601 if (t && !t->isPrivate())
606 const auto *enumNode = node->isAggregate() ?
607 findEnumNode(
nullptr, node, path, idx) :
608 findEnumNode(node,
nullptr, path, idx);
615 for (
const auto *base : bases) {
616 const Node *t = matchPathAndTarget(path, idx, target, base, flags, genus, ref);
617 if (t && !t->isPrivate())
619 if (target.isEmpty() && (flags & SearchEnumValues)) {
620 if ((t = findEnumNode(base->findChildNode(path.at(idx), genus, flags), base, path, idx)))
629
630
631
632
633
634const Node *
Tree::findNode(
const QStringList &path,
const Node *start,
int flags,
637 const Node *current = start;
638 if (current ==
nullptr)
642 const Node *node = current;
647
648
649
650
651
652
653
655 && !path[0].isEmpty()) {
656 QmlTypeNode *qcn = lookupQmlType(QString(path[0] +
"::" + path[1]));
657 if (qcn !=
nullptr) {
659 if (path.size() == 2)
665 for (i = start_idx; i < path.size(); ++i) {
674 const Node *next =
static_cast<
const Aggregate *>(node)->findChildNode(path.at(i),
676 const Node *enumNode = (flags & SearchEnumValues) ?
677 findEnumNode(next, node, path, i) :
nullptr;
686 for (
const auto *base : bases) {
687 next = base->findChildNode(path.at(i), genus, tmpFlags);
688 if (flags & SearchEnumValues)
689 if ((enumNode = findEnumNode(next, base, path, i)))
697 if ((node !=
nullptr) && i == path.size())
700 }
while (current !=
nullptr);
707
708
709
710
711
712
713
714const Node *
Tree::findEnumNode(
const Node *node,
const Node *aggregate,
const QStringList &path,
int offset)
const
718 const auto *en =
static_cast<
const EnumNode*>(node);
724 return (!node && aggregate && offset == path.size() - 1) ?
725 static_cast<
const Aggregate *>(aggregate)->findEnumNodeForValue(path.last()) :
730
731
732
733
734
735QString
Tree::getRef(
const QString &target,
const Node *node)
const
737 auto it = m_nodesByTargetTitle.constFind(target);
738 if (it != m_nodesByTargetTitle.constEnd()) {
740 if (it.value()->m_node == node)
741 return it.value()->m_ref;
743 }
while (it != m_nodesByTargetTitle.constEnd() && it.key() == target);
745 QString key = Utilities::asAsciiPrintable(target);
746 it = m_nodesByTargetRef.constFind(key);
747 if (it != m_nodesByTargetRef.constEnd()) {
749 if (it.value()->m_node == node)
750 return it.value()->m_ref;
752 }
while (it != m_nodesByTargetRef.constEnd() && it.key() == key);
758
759
760
761
762
764 Node *node,
int priority)
766 auto *target =
new TargetRec(name, type, node, priority);
767 m_nodesByTargetRef.insert(name, target);
768 m_nodesByTargetTitle.insert(title, target);
772
773
774
775
776
777
778
781 for (
auto *child : root->childNodes()) {
782 addToPageNodeByTitleMap(child);
783 populateTocSectionTargetMap(child);
784 addKeywordsToTargetMaps(child);
785 addTargetsToTargetMap(child);
787 if (child->isAggregate())
788 resolveTargets(
static_cast<Aggregate *>(child));
793
794
795
796
797void Tree::addTargetsToTargetMap(
Node *node) {
801 for (Atom *i : std::as_const(node->doc().targets())) {
802 const QString ref = refForAtom(i);
803 const QString title = i->string();
804 if (!ref.isEmpty() && !title.isEmpty()) {
805 QString key = Utilities::asAsciiPrintable(title);
806 auto *target =
new TargetRec(ref, TargetRec::Target, node, 2);
807 m_nodesByTargetRef.insert(key, target);
808 m_nodesByTargetTitle.insert(title, target);
814
815
816
817
826
827
828
829
830void Tree::addKeywordsToTargetMaps(
Node *node) {
834 for (Atom *i : std::as_const(node->doc().keywords())) {
835 QString ref = refForAtom(i);
836 QString title = i->string();
837 if (!ref.isEmpty() && !title.isEmpty()) {
838 auto *target =
new TargetRec(ref, nextSection(i) ? TargetRec::ContentsKeyword : TargetRec::Keyword, node, 1);
839 m_nodesByTargetRef.insert(Utilities::asAsciiPrintable(title), target);
840 m_nodesByTargetTitle.insert(title, target);
841 if (!target->isEmpty())
842 i->append(target->m_ref);
848
849
850
851
852
853void Tree::populateTocSectionTargetMap(
Node *node) {
857 QStack<Atom *> tocLevels;
858 QSet<QString> anchors;
862 for (Atom *atom: std::as_const(node->doc().tableOfContents())) {
863 while (!tocLevels.isEmpty() && tocLevels.top()->string().toInt() >= atom->string().toInt())
866 tocLevels.push(atom);
868 QString ref = refForAtom(atom);
869 const QString &title = Text::sectionHeading(atom).toString();
870 if (ref.isEmpty() || title.isEmpty())
873 if (anchors.contains(ref)) {
874 QStringList refParts;
875 for (
const auto tocLevel : tocLevels)
876 refParts << refForAtom(tocLevel);
878 refParts << QString::number(index);
879 ref = refParts.join(QLatin1Char(
'-'));
883 if (atom->next(Atom::SectionHeadingLeft))
884 atom->next()->append(ref);
887 const QString &key = Utilities::asAsciiPrintable(title);
888 auto *target =
new TargetRec(ref, TargetRec::Contents, node, 3);
889 m_nodesByTargetRef.insert(key, target);
890 m_nodesByTargetTitle.insert(title, target);
895
896
897
898
899
900void Tree::addToPageNodeByTitleMap(
Node *node) {
904 auto *pageNode =
static_cast<
PageNode *>(node);
905 QString key = pageNode->title();
909 if (key.contains(QChar(
' ')))
910 key = Utilities::asAsciiPrintable(key);
911 const QList<PageNode *> nodes = m_pageNodesByTitle.values(key);
913 bool alreadyThere =
std::any_of(nodes.cbegin(), nodes.cend(), [&](
const auto &knownNode) {
914 return knownNode->isExternalPage() && knownNode->name() == pageNode->name();
918 m_pageNodesByTitle.insert(key, pageNode);
922
923
924
927 auto findBestCandidate = [&](
const TargetMap &tgtMap,
const QString &key) {
929 auto [it, end] = tgtMap.equal_range(key);
941 TargetRec *bestTarget = findBestCandidate(m_nodesByTargetTitle, target);
943 bestTarget = findBestCandidate(m_nodesByTargetRef, Utilities::asAsciiPrintable(target));
949
950
951const PageNode *
Tree::findPageNodeByTitle(
const QString &title)
const
953 PageNodeMultiMap::const_iterator it;
954 if (title.contains(QChar(
' ')))
955 it = m_pageNodesByTitle.constFind(Utilities::asAsciiPrintable(title));
957 it = m_pageNodesByTitle.constFind(title);
958 if (it != m_pageNodesByTitle.constEnd()) {
960
961
962
963
964 PageNodeMultiMap::const_iterator j = it;
966 if (j != m_pageNodesByTitle.constEnd() && j.key() == it.key()) {
967 while (j != m_pageNodesByTitle.constEnd()) {
968 if (j.key() == it.key() && j.value()->url().isEmpty()) {
973 if (j != m_pageNodesByTitle.cend()) {
974 it.value()->location().warning(
"This page title exists in more than one file: "
976 j.value()->location().warning(
"[It also exists here]");
985
986
987
988
989
990
1000 if (atom->count() == 2)
1001 return atom->string(1);
1006 if (
const auto *section = nextSection(atom))
1007 return refForAtom(section);
1008 return Utilities::asAsciiPrintable(atom->string());
1015
1016
1017
1018
1021
1022
1023
1024
1027
1028
1029
1030
1033
1034
1035
1044 return &m_qmlModules;
1052
1053
1054
1055
1056
1059 CNMap *map = getCollectionMap(type);
1061 auto it = map->constFind(name);
1062 if (it != map->cend())
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1084 CNMap *m = getCollectionMap(type);
1087 auto it = m->constFind(name);
1088 if (it != m->cend())
1092 m->insert(name, cn);
1097
1098
1099
1100
1101
1102
1103
1106
1107
1108
1109
1110
1111
1112
1115
1116
1117
1118
1119
1120
1121
1124
1125
1126
1127
1128
1129
1132
1133
1134
1135
1136
1137
1140
1141
1142
1143
1144
1145
1148
1149
1150
1151
1152
1153
1154
1155
1161 node->appendGroupName(name);
1167
1168
1169
1170
1171
1172
1177 node->setPhysicalModuleName(name);
1182
1183
1184
1185
1186
1190 QStringList dotSplit;
1191 QStringList blankSplit = name.split(QLatin1Char(
' '));
1192 qmid.append(blankSplit[0]);
1193 if (blankSplit.size() > 1) {
1194 qmid.append(blankSplit[0] + blankSplit[1]);
1195 dotSplit = blankSplit[1].split(QLatin1Char(
'.'));
1196 qmid.append(blankSplit[0] + dotSplit[0]);
1204 for (
int i = 0; i < qmid.size(); ++i) {
1205 QString key = qmid[i] +
"::" + node->name();
1206 insertQmlType(key, n);
1213
1214
1215
1218 if (!m_qmlTypeMap.contains(key))
1219 m_qmlTypeMap.insert(key, n);
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1237 if (path.size() == 3 && !path[0].isEmpty()
1239 QmlTypeNode *qcn = lookupQmlType(QString(path[0] +
"::" + path[1]));
1240 if (qcn ==
nullptr) {
1241 QStringList p(path[1]);
1242 Node *n = findNodeByNameAndType(p, &Node::isQmlType);
1250 if (relative ==
nullptr)
1258 Node *node =
const_cast<
Node *>(relative);
1261 for (i = 0; i < path.size(); ++i) {
1266 Node *next =
nullptr;
1267 if (i == path.size() - 1)
1270 next = aggregate->findChildNode(path.at(i), genus);
1274 for (
auto *base : bases) {
1275 if (i == path.size() - 1)
1276 next = base->findFunctionChild(path.at(i), parameters);
1278 next = base->findChildNode(path.at(i), genus);
1280 if (next !=
nullptr)
1298 while (FN
->isPrivate() && !FN->overridesThis().isEmpty()) {
1299 QStringList path = FN->overridesThis().split(
"::");
1300 FN = m_qdb->findFunctionNode(path, parameters, relative, genus);
1314
1315
1316
1317
1320 if (parent ==
nullptr)
1323 for (Node *n : children) {
1324 if (n !=
nullptr && n->isFunction() && n->hasTag(tag))
1325 return static_cast<FunctionNode *>(n);
1327 for (Node *n : children) {
1328 if (n !=
nullptr && n->isAggregate()) {
1329 n = findFunctionNodeForTag(tag,
static_cast<Aggregate *>(n));
1331 return static_cast<FunctionNode *>(n);
1338
1339
1340
1343 if (parent ==
nullptr)
1346 for (Node *n : children) {
1347 if (n !=
nullptr && (n->isMacro() || n->isFunction()) && n->name() == t)
1348 return static_cast<FunctionNode *>(n);
1350 for (Node *n : children) {
1351 if (n !=
nullptr && n->isAggregate()) {
1352 FunctionNode *fn = findMacroNode(t,
static_cast<Aggregate *>(n));
1361
1362
1363
1366 arg.remove(QChar(
'('));
1367 arg.remove(QChar(
')'));
1368 QString t = arg.simplified();
1369 QStringList sl = t.split(QChar(
' '));
1372 for (
const QString &s : sl) {
1373 if (!m_dontDocumentMap.contains(s))
1374 m_dontDocumentMap.insert(s,
nullptr);
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1393 for (
auto it = m_dontDocumentMap.begin(); it != m_dontDocumentMap.end(); ++it) {
1394 Aggregate *node = findAggregate(it.key());
1395 if (node !=
nullptr)
FunctionNode * findFunctionChild(const FunctionNode *clone)
Returns the function node that is a child of this node, such that the function described has the same...
const NodeList & childNodes() const
Returns a const reference to the child list.
The Atom class is the fundamental unit for representing documents internally.
AtomType type() const
Return the type of this atom.
const Atom * next() const
Return the next atom in the atom list.
const Atom * next(AtomType t) const
Return the next Atom in the list if it is of AtomType t.
const Atom * find(AtomType t) const
Starting from this Atom, searches the linked list for the atom of specified type t and returns it.
The ClassNode represents a C++ class.
void removePrivateAndInternalBases()
Remove private and internal bases classes from this class's list of base classes.
A class for holding the members of a collection of doc pages.
void addMember(Node *node) override
Appends node to the collection node's member list, if and only if it isn't already in the member list...
bool hasTableOfContents() const
This node is used to represent any kind of function being documented.
This class represents a C++ namespace.
const Doc & doc() const
Returns a reference to the node's Doc data member.
bool isPrivate() const
Returns true if this node's access is Private.
virtual void setQmlModule(CollectionNode *)
If this is a QmlTypeNode, this function sets the QML type's QML module pointer to the CollectionNode ...
NodeType
An unsigned char value that identifies an object as a particular subclass of Node.
bool isFunction(Genus g=DontCare) const
Returns true if this is a FunctionNode and its Genus is set to g.
bool isQmlType() const
Returns true if the node type is QmlType or QmlValueType.
virtual bool isInternal() const
Returns true if the node's status is Internal, or if its parent is a class with Internal status.
bool isEnumType() const
Returns true if the node type is Enum.
virtual bool isTextPageNode() const
Returns true if the node is a PageNode but not an Aggregate.
Aggregate * parent() const
Returns the node's parent pointer.
virtual bool isAggregate() const
Returns true if this node is an aggregate, which means it inherits Aggregate and can therefore have c...
Access access() const
Returns the node's Access setting, which can be Public, Protected, or Private.
Genus
An unsigned char value that specifies whether the Node represents a C++ element, a QML element,...
Genus genus() const
Returns this node's Genus.
LinkType
An unsigned char value that probably should be moved out of the Node base class.
virtual bool isClassNode() const
Returns true if this is an instance of ClassNode.
void setStatus(Status t)
Sets the node's status to t.
Status status() const
Returns the node's status value.
A PageNode is a Node that generates a documentation page.
A class for parsing and managing a function parameter list.
This class describes one instance of using the Q_PROPERTY macro.
const PropertyNode * overriddenFrom() const
void addFunction(FunctionNode *function, FunctionRole role)
void setOverriddenFrom(const PropertyNode *baseProperty)
Sets this property's {overridden from} property to baseProperty, which indicates that this property o...
void addSignal(FunctionNode *function, FunctionRole role)
static Text sectionHeading(const Atom *sectionBegin)
This class constructs and maintains a tree of instances of the subclasses of Node.
void markDontDocumentNodes()
The {don't document} map has been loaded with the names of classes and structs in the current module ...
void addToDontDocumentMap(QString &arg)
Add the class and struct names in arg to the {don't document} map.
Node * findNodeByNameAndType(const QStringList &path, bool(Node::*isMatch)() const) const
Find the node with the specified path name that is of the specified type and subtype.
Combined button and popup list for selecting options.
QList< ClassNode * > ClassList
QList< Node * > NodeVector
QMap< QString, CollectionNode * > CNMap
A record of a linkable target within the documentation.
Node::Genus genus() const
TargetType
A type of a linkable target record.
static const Atom * nextSection(const Atom *a)
QMultiMap< QString, TargetRec * > TargetMap