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
38
39
40
43
44
45
46
47
48
49
51HrefResolver::HrefResolver(
const HrefResolverConfig &config)
57
58
59
60
61
62
63
64
65
66
67
68
69QString HrefResolver::fileBase(
const Node *node)
const
71 const bool obsoleteFileBase = !node->isAggregate() && node->isDeprecated();
73 if (!node->isPageNode() && !node->isCollectionNode()) {
74 node = node->parent();
80 if (node->hasFileNameBase()) {
81 result = node->fileNameBase();
83 auto it = m_fileBaseCache.constFind(node);
84 if (it != m_fileBaseCache.constEnd()) {
87 const OutputContext *ctx = m_config.context;
88 result = Utilities::computeFileBase(
90 [ctx](
const Node *n) {
return ctx->outputPrefix(n->genus()); },
91 [ctx](
const Node *n) {
return ctx->outputSuffix(n->genus()); });
92 m_fileBaseCache.insert(node, result);
97 result +=
"-obsolete"_L1;
102
103
104
105
106
107
108
109QString HrefResolver::fileName(
const Node *node)
const
111 if (!node->url().isEmpty())
114 const auto base = fileBase(node);
118 if (node->isTextPageNode() && !node->isCollectionNode()) {
119 QFileInfo originalName(node->name());
120 QString suffix = originalName.suffix();
121 if (!suffix.isEmpty() && suffix !=
"html"_L1)
122 return base +
'.'_L1 + suffix;
125 return base +
'.'_L1 + m_config.context->fileExtension;
129
130
131
132
133
134
135
136
137
138QString HrefResolver::anchorForNode(
const Node *node)
const
140 const QString ref = computeAnchorId(node);
143 return m_config.cleanRefFn ? m_config.cleanRefFn(ref) : ref;
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164HrefResult HrefResolver::hrefForNode(
const Node *node,
const Node *relative)
const
167 return HrefSuppressReason::NullNode;
169 const bool hasExplicitUrl = !node->url().isEmpty();
170 if (hasExplicitUrl && node->isExternalPage())
178 QString fn = hasExplicitUrl
179 ? node->url().section(
'/'_L1, -1)
186 if (hasExplicitUrl) {
187 const qsizetype hashIndex = fn.indexOf(
'#'_L1);
189 fn.truncate(hashIndex);
192 return HrefSuppressReason::NoFileBase;
200 const bool isCrossModulePlaceholder = node->isCollectionNode()
201 && !
static_cast<
const CollectionNode *>(node)
202 ->resolvedPhysicalModuleName().isEmpty();
209 if (!node->isIndexNode() && !isCrossModulePlaceholder) {
210 const NodeContext context = node->createContext();
211 if (!InclusionFilter::isIncluded(m_config.inclusionPolicy, context))
212 return HrefSuppressReason::ExcludedByPolicy;
215 if (!hasExplicitUrl && node->parent() && node->parent()->isQmlType()
216 && node->parent()->isAbstract()) {
223 const QmlTypeNode *qmlContext =
nullptr;
224 if (relative && relative->isQmlType())
225 qmlContext =
static_cast<
const QmlTypeNode *>(relative);
226 else if (m_config.qmlTypeContextFn)
227 qmlContext = m_config.qmlTypeContextFn();
229 if (qmlContext->inherits(node->parent())) {
230 fn = fileName(qmlContext);
237 if (!node->isAggregate() && node->isDeprecated()
238 && qmlContext->url().isEmpty()) {
239 const QString ext =
'.'_L1 + m_config.context->fileExtension;
240 fn.chop(ext.length());
241 fn +=
"-obsolete"_L1 + ext;
243 }
else if (node->parent()->isInternal() && !m_config.context->noLinkErrors) {
244 node->doc().location().warning(
245 u"Cannot link to property in internal type '%1'"_s
246 .arg(node->parent()->name()));
247 return HrefSuppressReason::InternalAbstractQml;
254 if (!node->isPageNode() || node->isPropertyGroup()) {
255 QString ref = anchorForNode(node);
256 if (relative && fn == fileName(relative) && ref == anchorForNode(relative))
257 return HrefSuppressReason::SameFileAnchor;
263 if (relative && (node != relative) && !node->isExternalPage()
264 && (node->isIndexNode() || isCrossModulePlaceholder
265 || node->tree() != relative->tree())) {
266 link.prepend(crossModulePrefix(node, relative));
273
274
275
276
277
278
279
280
281
282
283
284
285
286QString HrefResolver::crossModulePrefix(
const Node *target,
const Node *source)
const
288 const QString &sourceDir = m_config.context->outputDir.path();
289 const QString currentSegment =
'/'_L1 + source->tree()->physicalModuleName() +
'/'_L1;
290 const qsizetype moduleIndex = sourceDir.indexOf(currentSegment);
298 QString targetModuleName;
299 if (target->isCollectionNode()) {
300 const auto *cn =
static_cast<
const CollectionNode *>(target);
301 targetModuleName = cn->resolvedPhysicalModuleName();
303 if (targetModuleName.isEmpty())
304 targetModuleName = target->tree()->physicalModuleName();
305 const QString targetSegment =
'/'_L1 + targetModuleName +
'/'_L1;
306 QString targetDir = sourceDir;
307 targetDir.replace(moduleIndex, currentSegment.size(), targetSegment);
309 const QString relPath = QDir(sourceDir).relativeFilePath(targetDir);
310 if (relPath.isEmpty() || relPath ==
"."_L1)
312 return relPath +
'/'_L1;