Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qmetatypesjsonprocessor.cpp
Go to the documentation of this file.
1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
5
9
10#include <QtCore/qcborarray.h>
11#include <QtCore/qcbormap.h>
12#include <QtCore/qfile.h>
13#include <QtCore/qjsondocument.h>
14#include <QtCore/qqueue.h>
15
17
18using namespace Qt::StringLiterals;
19using namespace Constants;
20using namespace Constants::MetatypesDotJson;
22using namespace QAnyStringViewUtils;
23
25{
26 const QJsonDocument jsonValue = QJsonDocument::fromJson(json, error);
27 if (jsonValue.isArray())
28 return QCborValue::fromJsonValue(jsonValue.array());
29 if (jsonValue.isObject())
30 return QCborValue::fromJsonValue(jsonValue.object());
31 return QCborValue();
32}
33
35{
36 const QAnyStringView unqualified = toStringView(classDef, S_CLASS_NAME);
37 const QAnyStringView qualified = toStringView(classDef, S_QUALIFIED_CLASS_NAME);
38
40 if (qualified != unqualified) {
41 namespaces = split(qualified, "::"_L1);
42 Q_ASSERT(namespaces.last() == unqualified);
44 }
45
46 return namespaces;
47}
48
50{
51 for (const QString &source: files) {
52 QCborValue metaObjects;
53 {
54 QFile f(source);
55 if (!f.open(QIODevice::ReadOnly)) {
56 fprintf(stderr, "Error opening %s for reading\n", qPrintable(source));
57 return false;
58 }
60 metaObjects = fromJson(f.readAll(), &error);
61 if (error.error != QJsonParseError::NoError) {
62 fprintf(stderr, "Error %d while parsing %s: %s\n", error.error, qPrintable(source),
63 qPrintable(error.errorString()));
64 return false;
65 }
66 }
67
68 if (metaObjects.isArray()) {
69 const QCborArray metaObjectsArray = metaObjects.toArray();
70 for (const QCborValue &metaObject : metaObjectsArray) {
71 if (!metaObject.isMap()) {
72 fprintf(stderr, "Error parsing %s: JSON is not an object\n",
74 return false;
75 }
76
77 processTypes(metaObject.toMap());
78 }
79 } else if (metaObjects.isMap()) {
80 processTypes(metaObjects.toMap());
81 } else {
82 fprintf(stderr, "Error parsing %s: JSON is not an object or an array\n",
84 return false;
85 }
86 }
87
88 return true;
89}
90
92{
93 bool success = true;
94
95 for (const QString &types : foreignTypesFiles) {
96 QFile typesFile(types);
97 if (!typesFile.open(QIODevice::ReadOnly)) {
98 fprintf(stderr, "Cannot open foreign types file %s\n", qPrintable(types));
99 success = false;
100 continue;
101 }
102
104 QCborValue foreignMetaObjects = fromJson(typesFile.readAll(), &error);
105 if (error.error != QJsonParseError::NoError) {
106 fprintf(stderr, "Error %d while parsing %s: %s\n", error.error, qPrintable(types),
107 qPrintable(error.errorString()));
108 success = false;
109 continue;
110 }
111
112 const QCborArray foreignObjectsArray = foreignMetaObjects.toArray();
113 for (const QCborValue &metaObject : foreignObjectsArray) {
114 if (!metaObject.isMap()) {
115 fprintf(stderr, "Error parsing %s: JSON is not an object\n",
117 success = false;
118 continue;
119 }
120
122 }
123 }
124 return success;
125}
126
127template<typename String>
129{
130 std::sort(list->begin(), list->end());
131 const auto newEnd = std::unique(list->begin(), list->end());
133}
134
136{
137 sortTypes(m_types);
138 sortStringList(&m_includes);
139}
140
142{
143 sortTypes(m_foreignTypes);
144 addRelatedTypes();
145 sortStringList(&m_referencedTypes);
146 sortTypes(m_types);
147}
148
150{
151 QString registrationHelper;
152 for (const auto &obj: m_types) {
153 const QString className = obj[S_CLASS_NAME].toString();
154 const QString foreignClassName = className + u"Foreign";
155 const auto classInfos = obj[S_CLASS_INFOS].toArray();
156 QString qmlElement;
157 QString qmlUncreatable;
158 QString qmlAttached;
159 bool isSingleton = false;
160 bool isExplicitlyUncreatable = false;
161 for (const QCborValue &info: classInfos) {
162 const QCborMap entry = info.toMap();
163 const auto name = toStringView(entry, S_NAME);
164 const auto value = toStringView(entry, S_VALUE);
165 if (name == S_ELEMENT) {
166 if (value == S_AUTO) {
167 qmlElement = u"QML_NAMED_ELEMENT("_s + className + u")"_s;
168 } else if (value == S_ANONYMOUS) {
169 qmlElement = u"QML_ANONYMOUS"_s;
170 } else {
171 qmlElement = u"QML_NAMED_ELEMENT("_s + value.toString() + u")";
172 }
173 } else if (name == S_CREATABLE && value == S_FALSE) {
174 isExplicitlyUncreatable = true;
175 } else if (name == S_UNCREATABLE_REASON) {
176 qmlUncreatable = u"QML_UNCREATABLE(\""_s + value.toString() + u"\")";
177 } else if (name == S_ATTACHED) {
178 qmlAttached = u"QML_ATTACHED("_s + value.toString() + u")";
179 } else if (name == S_SINGLETON) {
180 isSingleton = true;
181 }
182 }
183 if (qmlElement.isEmpty())
184 continue; // no relevant entries found
185 const QString spaces = u" "_s;
186 registrationHelper += u"\nstruct "_s + foreignClassName + u"{\n Q_GADGET\n"_s;
187 registrationHelper += spaces + u"QML_FOREIGN(" + className + u")\n"_s;
188 registrationHelper += spaces + qmlElement + u"\n"_s;
189 if (isSingleton)
190 registrationHelper += spaces + u"QML_SINGLETON\n"_s;
191 if (isExplicitlyUncreatable) {
192 if (qmlUncreatable.isEmpty())
193 registrationHelper += spaces + uR"(QML_UNCREATABLE(""))" + u"n";
194 else
195 registrationHelper += spaces + qmlUncreatable + u"\n";
196 }
197 if (!qmlAttached.isEmpty())
198 registrationHelper += spaces + qmlAttached + u"\n";
199 registrationHelper += u"};\n";
200 }
201 return registrationHelper;
202}
203
204MetaTypesJsonProcessor::RegistrationMode MetaTypesJsonProcessor::qmlTypeRegistrationMode(
205 const QCborMap &classDef)
206{
207 const QCborArray classInfos = classDef[S_CLASS_INFOS].toArray();
208 for (const QCborValue &info : classInfos) {
209 const QCborMap entry = info.toMap();
211 if (name == S_ELEMENT) {
212 if (classDef[S_OBJECT].toBool())
213 return ObjectRegistration;
214 if (classDef[S_GADGET].toBool())
215 return GadgetRegistration;
216 if (classDef[S_NAMESPACE].toBool())
217 return NamespaceRegistration;
218 qWarning() << "Not registering classInfo which is neither an object, "
219 "nor a gadget, nor a namespace:"
220 << name.toString();
221 break;
222 }
223 }
224 return NoRegistration;
225}
226
227// TODO: Remove this when QAnyStringView gets a proper qHash()
228static size_t qHash(QAnyStringView string, size_t seed = 0)
229{
230 return string.visit([seed](auto view) {
231 if constexpr (std::is_same_v<decltype(view), QStringView>)
232 return qHash(view, seed);
233 if constexpr (std::is_same_v<decltype(view), QLatin1StringView>)
234 return qHash(view, seed);
235 if constexpr (std::is_same_v<decltype(view), QUtf8StringView>)
236 return qHash(QByteArrayView(view.data(), view.length()), seed);
237 });
238}
239
240void MetaTypesJsonProcessor::addRelatedTypes()
241{
242 QSet<QAnyStringView> processedRelatedNames;
243 QQueue<QCborMap> typeQueue;
244 typeQueue.append(m_types);
245
246 const auto addRelatedName
247 = [&](QAnyStringView relatedName, const QList<QAnyStringView> &namespaces) {
248 if (const QCborMap *related = QmlTypesClassDescription::findType(
249 m_types, m_foreignTypes, relatedName, namespaces)) {
250 processedRelatedNames.insert(toStringView(*related, S_QUALIFIED_CLASS_NAME));
251 }
252 };
253
254 // First mark all classes registered from this module as already processed.
255 for (const QCborMap &type : m_types) {
256 processedRelatedNames.insert(toStringView(type, S_QUALIFIED_CLASS_NAME));
257 const auto classInfos = type.value(S_CLASS_INFOS).toArray();
258 for (const QCborValue &classInfo : classInfos) {
259 const QCborMap obj = classInfo.toMap();
260 if (obj.value(S_NAME) == S_FOREIGN) {
261 addRelatedName(toStringView(obj, S_VALUE), namespaces(type));
262 break;
263 }
264 }
265 }
266
267 // Then mark all classes registered from other modules as already processed.
268 // We don't want to generate them again for this module.
269 for (const QCborMap &foreignType : m_foreignTypes) {
270 const auto classInfos = foreignType.value(S_CLASS_INFOS).toArray();
271 bool seenQmlPrefix = false;
272 for (const QCborValue &classInfo : classInfos) {
273 const QCborMap obj = classInfo.toMap();
275 if (!seenQmlPrefix && startsWith(name, "QML."_L1)) {
276 processedRelatedNames.insert(
278 seenQmlPrefix = true;
279 }
280 if (name == S_FOREIGN) {
281 addRelatedName(toStringView(obj, S_VALUE), namespaces(foreignType));
282 break;
283 }
284 }
285 }
286
287 auto addType = [&](QAnyStringView typeName, const QList<QAnyStringView> &namespaces) {
289 m_types, m_foreignTypes, typeName, namespaces)) {
291 m_referencedTypes.append(qualifiedName);
292 if (!processedRelatedNames.contains(qualifiedName)) {
293 processedRelatedNames.insert(qualifiedName);
294 m_types.append(*other);
295 typeQueue.enqueue(*other);
296 }
297 return true;
298 }
299
300 processedRelatedNames.insert(typeName);
301 return false;
302 };
303
304 // Then recursively iterate the super types and attached types, marking the
305 // ones we are interested in as related.
306 while (!typeQueue.isEmpty()) {
307 const QCborMap classDef = typeQueue.dequeue();
309
310 const auto classInfos = classDef.value(S_CLASS_INFOS).toArray();
311 for (const QCborValue &classInfo : classInfos) {
312 const QCborMap obj = classInfo.toMap();
313 const QAnyStringView objNameValue = toStringView(obj, S_NAME);
314 if (objNameValue == S_ATTACHED || objNameValue == S_SEQUENCE
315 || objNameValue == S_EXTENDED) {
317 } else if (objNameValue == S_FOREIGN) {
318 const QAnyStringView foreignClassName = toStringView(obj, S_VALUE);
320 m_foreignTypes, {}, foreignClassName, namespaces)) {
321 const auto otherSupers = other->value(S_SUPER_CLASSES).toArray();
322 const QList<QAnyStringView> otherNamespaces
324 if (!otherSupers.isEmpty()) {
325 const QCborMap otherSuperObject = otherSupers.first().toMap();
326 if (otherSuperObject.value(S_ACCESS) == S_PUBLIC)
327 addType(toStringView(otherSuperObject, S_NAME), otherNamespaces);
328 }
329
330 const auto otherClassInfos = other->value(S_CLASS_INFOS).toArray();
331 for (const QCborValue &otherClassInfo : otherClassInfos) {
332 const QCborMap obj = otherClassInfo.toMap();
333 const QAnyStringView objNameValue = toStringView(obj, S_NAME);
334 if (objNameValue == S_ATTACHED || objNameValue == S_SEQUENCE
335 || objNameValue == S_EXTENDED) {
336 addType(toStringView(obj, S_VALUE), otherNamespaces);
337 break;
338 }
339 // No, you cannot chain S_FOREIGN declarations. Sorry.
340 }
341 }
342 }
343 }
344
345 const auto supers = classDef.value(S_SUPER_CLASSES).toArray();
346 for (const QCborValue &super : supers) {
347 const QCborMap superObject = super.toMap();
348 if (superObject.value(S_ACCESS) == S_PUBLIC)
349 addType(toStringView(superObject, S_NAME), namespaces);
350 }
351 }
352}
353
354void MetaTypesJsonProcessor::sortTypes(QVector<QCborMap> &types)
355{
356 std::sort(types.begin(), types.end(), [&](const QCborMap &a, const QCborMap &b) {
357 return toStringView(a, S_QUALIFIED_CLASS_NAME) <
358 toStringView(b, S_QUALIFIED_CLASS_NAME);
359 });
360}
361
362QString MetaTypesJsonProcessor::resolvedInclude(QAnyStringView include)
363{
364 return (m_privateIncludes && endsWith(include, "_p.h"_L1))
365 ? QLatin1String("private/") + include.toString()
366 : include.toString();
367}
368
370{
371 const QString include = resolvedInclude(toStringView(types, S_INPUT_FILE));
372 const QCborArray classes = types[S_CLASSES].toArray();
373 for (const QCborValue &cls : classes) {
374 QCborMap classDef = cls.toMap();
375 classDef.insert(S_INPUT_FILE, include);
376
377 switch (qmlTypeRegistrationMode(classDef)) {
378 case NamespaceRegistration:
379 case GadgetRegistration:
380 case ObjectRegistration: {
381 if (!endsWith(include, QLatin1String(".h"))
382 && !endsWith(include, QLatin1String(".hpp"))
383 && !endsWith(include, QLatin1String(".hxx"))
384 && !endsWith(include, QLatin1String(".hh"))
385 && !endsWith(include, QLatin1String(".py"))
386 && contains(include, QLatin1Char('.'))) {
387 fprintf(stderr,
388 "Class %s is declared in %s, which appears not to be a header.\n"
389 "The compilation of its registration to QML may fail.\n",
391 qPrintable(include));
392 }
393 m_includes.append(include);
394 m_types.append(classDef);
395 break;
396 }
397 case NoRegistration:
398 m_foreignTypes.append(classDef);
399 break;
400 }
401 }
402}
403
405{
406 const QString include = resolvedInclude(toStringView(types, S_INPUT_FILE));
407 const QCborArray classes = types[S_CLASSES].toArray();
408 for (const QCborValue &cls : classes) {
409 QCborMap classDef = cls.toMap();
410 classDef.insert(S_INPUT_FILE, include);
411 m_foreignTypes.append(classDef);
412 }
413}
414
bool processTypes(const QStringList &files)
static QList< QAnyStringView > namespaces(const QCborMap &classDef)
bool processForeignTypes(const QStringList &foreignTypesFiles)
\inmodule QtCore
QString toString() const
Returns a deep copy of this string view's data as a QString.
Definition qstring.h:1071
\inmodule QtCore
Definition qbytearray.h:57
\inmodule QtCore\reentrant
Definition qcborarray.h:20
\inmodule QtCore\reentrant
Definition qcbormap.h:21
QCborValue value(qint64 key) const
Returns the QCborValue element in this map that corresponds to key key, if there is one.
Definition qcbormap.h:169
iterator insert(qint64 key, const QCborValue &value_)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qcbormap.h:270
\inmodule QtCore\reentrant
Definition qcborvalue.h:50
QString toString(const QString &defaultValue={}) const
Returns the string value stored in this QCborValue, if it is of the string type.
QCborArray toArray() const
QCborMap toMap() const
bool isArray() const
Returns true if this QCborValue is of the array type.
Definition qcborvalue.h:161
static QCborValue fromJsonValue(const QJsonValue &v)
Converts the JSON value contained in v into its corresponding CBOR value and returns it.
bool isMap() const
Returns true if this QCborValue is of the map type.
Definition qcborvalue.h:162
\inmodule QtCore
Definition qfile.h:93
bool open(OpenMode flags) override
Opens the file using OpenMode mode, returning true if successful; otherwise false.
Definition qfile.cpp:881
QByteArray readAll()
Reads all remaining data from the device, and returns it as a byte array.
\inmodule QtCore\reentrant
bool isArray() const
Returns true if the document contains an array.
QJsonArray array() const
Returns the QJsonArray contained in the document.
QJsonObject object() const
Returns the QJsonObject contained in the document.
bool isObject() const
Returns true if the document contains an object.
static QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error=nullptr)
Parses json as a UTF-8 encoded JSON document, and creates a QJsonDocument from it.
Definition qlist.h:74
void pop_back() noexcept
Definition qlist.h:676
bool isEmpty() const noexcept
Definition qlist.h:390
T & last()
Definition qlist.h:631
iterator erase(const_iterator begin, const_iterator end)
Definition qlist.h:882
iterator end()
Definition qlist.h:609
iterator begin()
Definition qlist.h:608
void append(parameter_type t)
Definition qlist.h:441
const_iterator constEnd() const noexcept
Definition qlist.h:616
\inmodule QtCore
Definition qqueue.h:14
void enqueue(const T &t)
Adds value t to the tail of the queue.
Definition qqueue.h:18
T dequeue()
Removes the head item in the queue and returns it.
Definition qqueue.h:19
Definition qset.h:18
bool contains(const T &value) const
Definition qset.h:71
iterator insert(const T &value)
Definition qset.h:155
\inmodule QtCore
\inmodule QtCore
Definition qstringview.h:76
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
static constexpr QLatin1StringView S_UNCREATABLE_REASON
static constexpr QLatin1StringView S_ATTACHED
static constexpr QLatin1StringView S_FOREIGN
static constexpr QLatin1StringView S_SEQUENCE
static constexpr QLatin1StringView S_EXTENDED
static constexpr QLatin1StringView S_SINGLETON
static constexpr QLatin1StringView S_ELEMENT
static constexpr QLatin1StringView S_CREATABLE
static constexpr QLatin1StringView S_VALUE
static constexpr QLatin1StringView S_ANONYMOUS
static constexpr QLatin1StringView S_QUALIFIED_CLASS_NAME
static constexpr QLatin1StringView S_NAME
static constexpr QLatin1StringView S_SUPER_CLASSES
static constexpr QLatin1StringView S_INPUT_FILE
static constexpr QLatin1StringView S_ACCESS
static constexpr QLatin1StringView S_CLASS_NAME
static constexpr QLatin1StringView S_CLASS_INFOS
static constexpr QLatin1StringView S_PUBLIC
static constexpr QLatin1StringView S_FALSE
static constexpr QLatin1StringView S_GADGET
static constexpr QLatin1StringView S_CLASSES
static constexpr QLatin1StringView S_AUTO
static constexpr QLatin1StringView S_NAMESPACE
static constexpr QLatin1StringView S_OBJECT
bool startsWith(QAnyStringView whole, QAnyStringView part)
QAnyStringView toStringView(const QCborValue &value)
bool endsWith(QAnyStringView whole, QAnyStringView part)
const char * classInfo(const QMetaObject *metaObject, const char *key)
Combined button and popup list for selecting options.
DBusConnection const char DBusError * error
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define qWarning
Definition qlogging.h:162
const char * typeName
static QCborValue fromJson(const QByteArray &json, QJsonParseError *error)
static void sortStringList(QList< String > *list)
static size_t qHash(QAnyStringView string, size_t seed=0)
static bool contains(const QJsonArray &haystack, unsigned needle)
Definition qopengl.cpp:116
GLboolean GLboolean GLboolean b
GLboolean GLboolean GLboolean GLboolean a
[7]
GLsizei GLenum GLenum * types
GLfloat GLfloat f
GLenum type
GLuint name
GLsizei GLsizei GLchar * source
GLhandleARB obj
[2]
GLuint entry
static Q_CONSTINIT QBasicAtomicInteger< unsigned > seed
Definition qrandom.cpp:196
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
static void split(QT_FT_Vector *b)
#define qPrintable(string)
Definition qstring.h:1391
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
const char className[16]
[1]
Definition qwizard.cpp:100
QList< int > list
[14]
QFileInfo info(fileName)
[8]
obj metaObject() -> className()
QSharedPointer< T > other(t)
[5]
QStringList files
[8]
QQuickView * view
[0]
\inmodule QtCore\reentrant
\inmodule QtCore \reentrant
Definition qchar.h:17
static const QCborMap * findType(const QVector< QCborMap > &types, const QVector< QCborMap > &foreign, const QAnyStringView &name, const QList< QAnyStringView > &namespaces)
QT_BEGIN_NAMESPACE bool toBool(const QString &str)
Definition utils.h:14