6#ifdef QDOC_TEMPLATE_GENERATOR_ENABLED
9#include "collectionnode.h"
10#include "inclusionfilter.h"
11#include "outputcontext.h"
12#include "qmltypenode.h"
16#include <QtCore/qdir.h>
17#include <QtCore/qfileinfo.h>
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
40
41
42
43
44
45
46
48HrefResolver::HrefResolver(
const HrefResolverConfig &config)
54
55
56
57
58
59
60QString HrefResolver::fileBase(
const Node *node)
const
62 if (!node->isPageNode() && !node->isCollectionNode()) {
63 node = node->parent();
68 if (node->hasFileNameBase())
69 return node->fileNameBase();
71 auto it = m_fileBaseCache.constFind(node);
72 if (it != m_fileBaseCache.constEnd())
75 const OutputContext *ctx = m_config.context;
76 QString result = Utilities::computeFileBase(
78 [ctx](
const Node *n) {
return ctx->outputPrefix(n->genus()); },
79 [ctx](
const Node *n) {
return ctx->outputSuffix(n->genus()); });
81 m_fileBaseCache.insert(node, result);
86
87
88
89
90
91
92
93QString HrefResolver::fileName(
const Node *node)
const
95 if (!node->url().isEmpty())
98 const auto base = fileBase(node);
102 if (node->isTextPageNode() && !node->isCollectionNode()) {
103 QFileInfo originalName(node->name());
104 QString suffix = originalName.suffix();
105 if (!suffix.isEmpty() && suffix !=
"html"_L1)
106 return base +
'.'_L1 + suffix;
109 return base +
'.'_L1 + m_config.context->fileExtension;
113
114
115
116
117
118
119
120
121
122QString HrefResolver::anchorForNode(
const Node *node)
const
124 const QString ref = computeAnchorId(node);
127 return m_config.cleanRefFn ? m_config.cleanRefFn(ref) : ref;
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148HrefResult HrefResolver::hrefForNode(
const Node *node,
const Node *relative)
const
151 return HrefSuppressReason::NullNode;
153 const bool hasExplicitUrl = !node->url().isEmpty();
154 if (hasExplicitUrl && node->isExternalPage())
162 QString fn = hasExplicitUrl
163 ? node->url().section(
'/'_L1, -1)
170 if (hasExplicitUrl) {
171 const qsizetype hashIndex = fn.indexOf(
'#'_L1);
173 fn.truncate(hashIndex);
176 return HrefSuppressReason::NoFileBase;
184 const bool isCrossModulePlaceholder = node->isCollectionNode()
185 && !
static_cast<
const CollectionNode *>(node)
186 ->resolvedPhysicalModuleName().isEmpty();
193 if (!node->isIndexNode() && !isCrossModulePlaceholder) {
194 const NodeContext context = node->createContext();
195 if (!InclusionFilter::isIncluded(m_config.inclusionPolicy, context))
196 return HrefSuppressReason::ExcludedByPolicy;
199 if (!hasExplicitUrl && node->parent() && node->parent()->isQmlType()
200 && node->parent()->isAbstract()) {
201 const QmlTypeNode *qmlContext = m_config.qmlTypeContextFn
202 ? m_config.qmlTypeContextFn() :
nullptr;
204 if (qmlContext->inherits(node->parent())) {
205 fn = fileName(qmlContext);
206 }
else if (node->parent()->isInternal() && !m_config.context->noLinkErrors) {
207 node->doc().location().warning(
208 u"Cannot link to property in internal type '%1'"_s
209 .arg(node->parent()->name()));
210 return HrefSuppressReason::InternalAbstractQml;
217 if (!node->isPageNode() || node->isPropertyGroup()) {
218 QString ref = anchorForNode(node);
219 if (relative && fn == fileName(relative) && ref == anchorForNode(relative))
220 return HrefSuppressReason::SameFileAnchor;
226 if (relative && (node != relative) && !node->isExternalPage()
227 && (node->isIndexNode() || isCrossModulePlaceholder
228 || node->tree() != relative->tree())) {
229 link.prepend(crossModulePrefix(node, relative));
236
237
238
239
240
241
242
243
244
245
246
247
248
249QString HrefResolver::crossModulePrefix(
const Node *target,
const Node *source)
const
251 const QString &sourceDir = m_config.context->outputDir.path();
252 const QString currentSegment =
'/'_L1 + source->tree()->physicalModuleName() +
'/'_L1;
253 const qsizetype moduleIndex = sourceDir.indexOf(currentSegment);
261 QString targetModuleName;
262 if (target->isCollectionNode()) {
263 const auto *cn =
static_cast<
const CollectionNode *>(target);
264 targetModuleName = cn->resolvedPhysicalModuleName();
266 if (targetModuleName.isEmpty())
267 targetModuleName = target->tree()->physicalModuleName();
268 const QString targetSegment =
'/'_L1 + targetModuleName +
'/'_L1;
269 QString targetDir = sourceDir;
270 targetDir.replace(moduleIndex, currentSegment.size(), targetSegment);
272 const QString relPath = QDir(sourceDir).relativeFilePath(targetDir);
273 if (relPath.isEmpty() || relPath ==
"."_L1)
275 return relPath +
'/'_L1;