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
4#include "qmlpropertynode.h"
5
6#include "classnode.h"
7#include "enumnode.h"
8#include "genustypes.h"
9#include "propertynode.h"
10#include "qdocdatabase.h"
11#include "utilities.h"
12
13#include <utility>
14
15
16QT_BEGIN_NAMESPACE
17
18using namespace Qt::StringLiterals;
19
20/*!
21 Constructor for the QML property node.
22 */
23QmlPropertyNode::QmlPropertyNode(Aggregate *parent, const QString &name, QString type,
24 bool attached)
25 : Node(NodeType::QmlProperty, parent, name),
26 m_type(std::move(type)),
27 m_attached(attached)
28{
29 if (m_type == "alias")
30 m_isAlias = true;
31 if (name.startsWith(s: "__"))
32 setStatus(Internal);
33 // Set the default prefix for enumerators
34 m_nativeEnum.setPrefix(parent->name());
35}
36
37/*!
38 Sets the data type of this property to \a dataType,
39 preserving the list property modifier if one is set
40 already.
41*/
42void QmlPropertyNode::setDataType(const QString &dataType)
43{
44 m_type = dataType;
45 // Re-apply list modifier if needed
46 if (auto is_list = m_isList; is_list == FlagValueTrue) {
47 m_isList = FlagValueDefault;
48 setIsList(true);
49 }
50}
51
52/*!
53 \fn bool QmlPropertyNode::isReadOnly() const
54
55 Returns \c true if this QML property node is marked as a
56 read-only property.
57*/
58
59/*!
60 Marks this property as a list if \a isList is \c true.
61 The \c m_type member of a list property is wrapped with
62 \c {list<>}.
63*/
64void QmlPropertyNode::setIsList(bool isList)
65{
66 if (m_isList != FlagValueDefault)
67 return;
68
69 if ((m_isList = toFlagValue(b: isList)))
70 m_type = "list<%1>"_L1.arg(args&: m_type);
71}
72
73/*!
74 Returns \c true if this QML property or attached property is
75 read-only. If the read-only status is not set explicitly
76 using the \\readonly command, QDoc attempts to resolve it
77 from the associated C++ class instantiated by the QML type
78 that this property belongs to.
79
80 \note Depending on how the QML type is implemented, this
81 information may not be available to QDoc. If so, add a debug
82 line but do not treat it as a warning.
83 */
84bool QmlPropertyNode::isReadOnly()
85{
86 if (m_readOnly != FlagValueDefault)
87 return fromFlagValue(fv: m_readOnly, defaultValue: false);
88
89 // Find the parent QML type node
90 auto *parent{this->parent()};
91 while (parent && !(parent->isQmlType()))
92 parent = parent->parent();
93
94 bool readonly{false};
95 if (auto qcn = static_cast<QmlTypeNode *>(parent); qcn && qcn->classNode()) {
96 if (auto propertyNode = findCorrespondingCppProperty(); propertyNode)
97 readonly = !propertyNode->isWritable();
98 else
99 qCDebug(lcQdoc).nospace()
100 << qPrintable(defLocation().toString())
101 << ": Automatic resolution of QML property attributes failed for "
102 << name()
103 << " (Q_PROPERTY not found in the C++ class hierarchy known to QDoc. "
104 << "Likely, the type is replaced with a private implementation.)";
105 }
106 markReadOnly(flag: readonly);
107 return readonly;
108}
109
110/*!
111 Returns \c true if this QML property is marked with \required or the
112 corresponding C++ property uses the REQUIRED keyword.
113*/
114bool QmlPropertyNode::isRequired()
115{
116 if (m_required != FlagValueDefault)
117 return fromFlagValue(fv: m_required, defaultValue: false);
118
119 PropertyNode *pn = findCorrespondingCppProperty();
120 return pn != nullptr && pn->isRequired();
121}
122
123/*!
124 Returns a pointer this QML property's corresponding C++
125 property, if it has one.
126 */
127PropertyNode *QmlPropertyNode::findCorrespondingCppProperty()
128{
129 PropertyNode *pn;
130 Node *n = parent();
131 while (n && !(n->isQmlType()))
132 n = n->parent();
133 if (n) {
134 auto *qcn = static_cast<QmlTypeNode *>(n);
135 ClassNode *cn = qcn->classNode();
136 if (cn) {
137 /*
138 If there is a dot in the property name, first
139 find the C++ property corresponding to the QML
140 property group.
141 */
142 QStringList dotSplit = name().split(sep: QChar('.'));
143 pn = cn->findPropertyNode(name: dotSplit[0]);
144 if (pn) {
145 /*
146 Now find the C++ property corresponding to
147 the QML property in the QML property group,
148 <group>.<property>.
149 */
150 if (dotSplit.size() > 1) {
151 QStringList path(extractClassName(string: pn->qualifiedDataType()));
152 Node *nn = QDocDatabase::qdocDB()->findClassNode(path);
153 if (nn) {
154 auto *cn = static_cast<ClassNode *>(nn);
155 PropertyNode *pn2 = cn->findPropertyNode(name: dotSplit[1]);
156 /*
157 If found, return the C++ property
158 corresponding to the QML property.
159 Otherwise, return the C++ property
160 corresponding to the QML property
161 group.
162 */
163 return (pn2 ? pn2 : pn);
164 }
165 } else
166 return pn;
167 }
168 }
169 }
170 return nullptr;
171}
172
173// Only define a mapping between C++ and QML value types with different names.
174QSet<QString> QmlPropertyNode::cppQmlValueTypes = {
175 "float",
176 "QColor",
177 "QDateTime",
178 "QFont",
179 "QMatrix4x4",
180 "QPoint",
181 "QPointF",
182 "QQuaternion",
183 "qreal",
184 "QRect",
185 "QRectF",
186 "QSize",
187 "QSizeF",
188 "QString",
189 "QUrl",
190 "QVector2D",
191 "QVector3D",
192 "QVector4D",
193 "unsigned int",
194};
195
196QRegularExpression QmlPropertyNode::qmlBasicList("^list<([^>]+)>$");
197QRegularExpression QmlPropertyNode::cppBasicList("^(Q[A-Za-z0-9]+)List$");
198
199/*!
200 Validates a QML property type for the property, returning true if the type
201 is a QML type or QML list type, returning false if the type is a Qt value
202 type or Qt list type.
203
204 Specifically, if the type name matches a known value or object type in
205 qdoc's database, true is returned immediately.
206
207 If the type name matches the syntax for a non-nested QML list of types,
208 true is returned if the item type of the list is valid; otherwise false is
209 returned.
210
211 If the type name is a C or C++ type with a corresponding QML type, or if it
212 matches the syntax of a Qt list type, such as QStringList, false is
213 returned.
214
215 If none of the above applied, the type name is assumed to be valid and true
216 is returned.
217*/
218bool QmlPropertyNode::validateDataType(const QString &type) const
219{
220 QString qmlType = type;
221 if (qmlType.isNull())
222 qmlType = dataType();
223
224 if (QDocDatabase::qdocDB()->getQmlValueTypes().contains(key: qmlType) ||
225 QDocDatabase::qdocDB()->findQmlType(name: qmlType))
226 return true;
227
228 auto match = qmlBasicList.match(subject: qmlType);
229 if (match.hasMatch())
230 return validateDataType(type: match.captured(nth: 1));
231
232 if (cppQmlValueTypes.contains(value: qmlType) ||
233 cppBasicList.match(subject: qmlType).hasMatch())
234 return false;
235
236 return true;
237}
238
239QT_END_NAMESPACE
240

source code of qttools/src/qdoc/qdoc/src/qdoc/qmlpropertynode.cpp