1/****************************************************************************
2**
3** Copyright (C) 2019 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtQml module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include "qmltypescreator.h"
30#include "qmlstreamwriter.h"
31#include "qmltypesclassdescription.h"
32
33#include <QtCore/qset.h>
34#include <QtCore/qjsonarray.h>
35#include <QtCore/qsavefile.h>
36#include <QtCore/qfile.h>
37#include <QtCore/qjsondocument.h>
38
39static QString enquote(const QString &string)
40{
41 QString s = string;
42 return QString::fromLatin1(str: "\"%1\"").arg(a: s.replace(c: QLatin1Char('\\'), after: QLatin1String("\\\\"))
43 .replace(c: QLatin1Char('"'),after: QLatin1String("\\\"")));
44}
45
46void QmlTypesCreator::writeClassProperties(const QmlTypesClassDescription &collector)
47{
48 if (!collector.file.isEmpty())
49 m_qml.writeScriptBinding(name: QLatin1String("file"), rhs: enquote(string: collector.file));
50 m_qml.writeScriptBinding(
51 name: QLatin1String("name"),
52 rhs: enquote(string: collector.resolvedClass->value(
53 key: QLatin1String("qualifiedClassName")).toString()));
54
55 if (!collector.defaultProp.isEmpty())
56 m_qml.writeScriptBinding(name: QLatin1String("defaultProperty"), rhs: enquote(string: collector.defaultProp));
57
58 if (!collector.superClass.isEmpty())
59 m_qml.writeScriptBinding(name: QLatin1String("prototype"), rhs: enquote(string: collector.superClass));
60
61 if (collector.elementName.isEmpty())
62 return;
63
64 QStringList exports;
65 QStringList metaObjects;
66
67 for (auto it = collector.revisions.begin(), end = collector.revisions.end(); it != end; ++it) {
68 const int revision = *it;
69 if (revision < collector.addedInRevision)
70 continue;
71 if (collector.removedInRevision > collector.addedInRevision
72 && revision >= collector.removedInRevision) {
73 break;
74 }
75
76 if (collector.isBuiltin) {
77 exports.append(t: enquote(string: QString::fromLatin1(str: "QML/%1 1.0").arg(a: collector.elementName)));
78 metaObjects.append(t: QLatin1String("0"));
79 }
80
81 exports.append(t: enquote(string: QString::fromLatin1(str: "%1/%2 %3.%4")
82 .arg(a: m_module).arg(a: collector.elementName)
83 .arg(a: m_majorVersion).arg(a: revision)));
84 metaObjects.append(t: QString::number(revision));
85 }
86
87 m_qml.writeArrayBinding(name: QLatin1String("exports"), elements: exports);
88
89 if (!collector.isCreatable || collector.isSingleton)
90 m_qml.writeScriptBinding(name: QLatin1String("isCreatable"), rhs: QLatin1String("false"));
91
92 if (collector.isSingleton)
93 m_qml.writeScriptBinding(name: QLatin1String("isSingleton"), rhs: QLatin1String("true"));
94
95 m_qml.writeArrayBinding(name: QLatin1String("exportMetaObjectRevisions"), elements: metaObjects);
96
97 if (!collector.attachedType.isEmpty())
98 m_qml.writeScriptBinding(name: QLatin1String("attachedType"), rhs: enquote(string: collector.attachedType));
99}
100
101void QmlTypesCreator::writeType(const QJsonObject &property, const QString &key, bool isReadonly,
102 bool parsePointer)
103{
104 auto it = property.find(key);
105 if (it == property.end())
106 return;
107
108 QString type = (*it).toString();
109 if (type.isEmpty() || type == QLatin1String("void"))
110 return;
111
112 const QLatin1String typeKey("type");
113
114 bool isList = false;
115 bool isPointer = false;
116
117 if (type == QLatin1String("QString")) {
118 type = QLatin1String("string");
119 } else if (type == QLatin1String("qreal")) {
120 type = QLatin1String("double");
121 } else if (type == QLatin1String("qint32")) {
122 type = QLatin1String("int");
123 } else if (type == QLatin1String("quint32")) {
124 type = QLatin1String("uint");
125 } else if (type == QLatin1String("qint64")) {
126 type = QLatin1String("qlonglong");
127 } else if (type == QLatin1String("quint64")) {
128 type = QLatin1String("qulonglong");
129 } else {
130
131 const QLatin1String listProperty("QQmlListProperty<");
132 if (type.startsWith(s: listProperty)) {
133 isList = true;
134 const int listPropertySize = listProperty.size();
135 type = type.mid(position: listPropertySize, n: type.size() - listPropertySize - 1);
136 }
137
138 if (parsePointer && type.endsWith(c: QLatin1Char('*'))) {
139 isPointer = true;
140 type = type.left(n: type.size() - 1);
141 }
142 }
143
144 m_qml.writeScriptBinding(name: typeKey, rhs: enquote(string: type));
145 const QLatin1String trueString("true");
146 if (isList)
147 m_qml.writeScriptBinding(name: QLatin1String("isList"), rhs: trueString);
148 if (isReadonly)
149 m_qml.writeScriptBinding(name: QLatin1String("isReadonly"), rhs: trueString);
150 if (isPointer)
151 m_qml.writeScriptBinding(name: QLatin1String("isPointer"), rhs: trueString);
152}
153
154void QmlTypesCreator::writeProperties(const QJsonArray &properties, QSet<QString> &notifySignals)
155{
156 for (const QJsonValue &property : properties) {
157 const QJsonObject obj = property.toObject();
158 const QString name = obj[QLatin1String("name")].toString();
159 m_qml.writeStartObject(component: QLatin1String("Property"));
160 m_qml.writeScriptBinding(name: QLatin1String("name"), rhs: enquote(string: name));
161 const auto it = obj.find(key: QLatin1String("revision"));
162 if (it != obj.end())
163 m_qml.writeScriptBinding(name: QLatin1String("revision"), rhs: QString::number(it.value().toInt()));
164 writeType(property: obj, key: QLatin1String("type"), isReadonly: !obj.contains(key: QLatin1String("write")), parsePointer: true);
165 m_qml.writeEndObject();
166
167 const QString notify = obj[QLatin1String("notify")].toString();
168 if (notify == name + QLatin1String("Changed"))
169 notifySignals.insert(value: notify);
170 }
171}
172
173void QmlTypesCreator::writeMethods(const QJsonArray &methods, const QString &type,
174 const QSet<QString> &notifySignals)
175{
176 for (const QJsonValue &method : methods) {
177 const QJsonObject obj = method.toObject();
178 const QString name = obj[QLatin1String("name")].toString();
179 if (name.isEmpty())
180 continue;
181 const QJsonArray arguments = method[QLatin1String("arguments")].toArray();
182 const auto revision = obj.find(key: QLatin1String("revision"));
183 if (notifySignals.contains(value: name) && arguments.isEmpty() && revision == obj.end())
184 continue;
185 m_qml.writeStartObject(component: type);
186 m_qml.writeScriptBinding(name: QLatin1String("name"), rhs: enquote(string: name));
187 if (revision != obj.end())
188 m_qml.writeScriptBinding(name: QLatin1String("revision"), rhs: QString::number(revision.value().toInt()));
189 writeType(property: obj, key: QLatin1String("returnType"), isReadonly: false, parsePointer: false);
190 for (const QJsonValue &argument : arguments) {
191 const QJsonObject obj = argument.toObject();
192 m_qml.writeStartObject(component: QLatin1String("Parameter"));
193 const QString name = obj[QLatin1String("name")].toString();
194 if (!name.isEmpty())
195 m_qml.writeScriptBinding(name: QLatin1String("name"), rhs: enquote(string: name));
196 writeType(property: obj, key: QLatin1String("type"), isReadonly: false, parsePointer: true);
197 m_qml.writeEndObject();
198 }
199 m_qml.writeEndObject();
200 }
201}
202
203void QmlTypesCreator::writeEnums(const QJsonArray &enums)
204{
205 for (const auto &item : enums) {
206 const QJsonObject obj = item.toObject();
207 const QJsonArray values = obj.value(key: QLatin1String("values")).toArray();
208 QStringList valueList;
209
210 for (const QJsonValue &value : values)
211 valueList.append(t: enquote(string: value.toString()));
212
213 m_qml.writeStartObject(component: QLatin1String("Enum"));
214 m_qml.writeScriptBinding(name: QLatin1String("name"),
215 rhs: enquote(string: obj.value(key: QLatin1String("name")).toString()));
216 auto alias = obj.find(key: QLatin1String("alias"));
217 if (alias != obj.end())
218 m_qml.writeScriptBinding(name: alias.key(), rhs: enquote(string: alias->toString()));
219 auto isFlag = obj.find(key: QLatin1String("isFlag"));
220 if (isFlag != obj.end() && isFlag->toBool())
221 m_qml.writeBooleanBinding(name: isFlag.key(), value: true);
222 m_qml.writeArrayBinding(name: QLatin1String("values"), elements: valueList);
223 m_qml.writeEndObject();
224 }
225}
226
227static QJsonArray members(const QJsonObject *classDef, const QJsonObject *origClassDef, const QString &key)
228{
229 QJsonArray classDefMembers = classDef->value(key).toArray();
230
231 if (classDef != origClassDef) {
232 const QJsonArray origClassDefMembers = origClassDef->value(key).toArray();
233 for (const auto &member : origClassDefMembers)
234 classDefMembers.append(value: member);
235 }
236
237 return classDefMembers;
238}
239
240void QmlTypesCreator::writeComponents()
241{
242 const QLatin1String nameKey("name");
243 const QLatin1String signalsKey("signals");
244 const QLatin1String enumsKey("enums");
245 const QLatin1String propertiesKey("properties");
246 const QLatin1String slotsKey("slots");
247 const QLatin1String methodsKey("methods");
248 const QLatin1String accessKey("access");
249 const QLatin1String typeKey("type");
250 const QLatin1String argumentsKey("arguments");
251
252 const QLatin1String destroyedName("destroyed");
253 const QLatin1String deleteLaterName("deleteLater");
254 const QLatin1String toStringName("toString");
255 const QLatin1String destroyName("destroy");
256 const QLatin1String delayName("delay");
257
258 const QLatin1String signalElement("Signal");
259 const QLatin1String componentElement("Component");
260 const QLatin1String methodElement("Method");
261
262 const QLatin1String publicAccess("public");
263 const QLatin1String intType("int");
264
265 for (const QJsonObject &component : m_ownTypes) {
266 m_qml.writeStartObject(component: componentElement);
267
268 QmlTypesClassDescription collector;
269 collector.collect(classDef: &component, types: m_ownTypes, foreign: m_foreignTypes,
270 mode: QmlTypesClassDescription::TopLevel);
271
272 writeClassProperties(collector);
273
274 const QJsonObject *classDef = collector.resolvedClass;
275 writeEnums(enums: members(classDef, origClassDef: &component, key: enumsKey));
276
277 QSet<QString> notifySignals;
278 writeProperties(properties: members(classDef, origClassDef: &component, key: propertiesKey), notifySignals);
279
280 if (collector.isRootClass) {
281
282 // Hide destroyed() signals
283 QJsonArray componentSignals = members(classDef, origClassDef: &component, key: signalsKey);
284 for (auto it = componentSignals.begin(); it != componentSignals.end();) {
285 if (it->toObject().value(key: nameKey).toString() == destroyedName)
286 it = componentSignals.erase(it);
287 else
288 ++it;
289 }
290 writeMethods(methods: componentSignals, type: signalElement, notifySignals);
291
292 // Hide deleteLater() methods
293 QJsonArray componentMethods = members(classDef, origClassDef: &component, key: methodsKey);
294 const QJsonArray componentSlots = members(classDef, origClassDef: &component, key: slotsKey);
295 for (const QJsonValue &componentSlot : componentSlots)
296 componentMethods.append(value: componentSlot);
297 for (auto it = componentMethods.begin(); it != componentMethods.end();) {
298 if (it->toObject().value(key: nameKey).toString() == deleteLaterName)
299 it = componentMethods.erase(it);
300 else
301 ++it;
302 }
303
304 // Add toString()
305 QJsonObject toStringMethod;
306 toStringMethod.insert(key: nameKey, value: toStringName);
307 toStringMethod.insert(key: accessKey, value: publicAccess);
308 componentMethods.append(value: toStringMethod);
309
310 // Add destroy()
311 QJsonObject destroyMethod;
312 destroyMethod.insert(key: nameKey, value: destroyName);
313 destroyMethod.insert(key: accessKey, value: publicAccess);
314 componentMethods.append(value: destroyMethod);
315
316 // Add destroy(int)
317 QJsonObject destroyMethodWithArgument;
318 destroyMethodWithArgument.insert(key: nameKey, value: destroyName);
319 destroyMethodWithArgument.insert(key: accessKey, value: publicAccess);
320 QJsonObject delayArgument;
321 delayArgument.insert(key: nameKey, value: delayName);
322 delayArgument.insert(key: typeKey, value: intType);
323 QJsonArray destroyArguments;
324 destroyArguments.append(value: delayArgument);
325 destroyMethodWithArgument.insert(key: argumentsKey, value: destroyArguments);
326 componentMethods.append(value: destroyMethodWithArgument);
327
328 writeMethods(methods: componentMethods, type: methodElement);
329 } else {
330 writeMethods(methods: members(classDef, origClassDef: &component, key: signalsKey), type: signalElement, notifySignals);
331 writeMethods(methods: members(classDef, origClassDef: &component, key: slotsKey), type: methodElement);
332 writeMethods(methods: members(classDef, origClassDef: &component, key: methodsKey), type: methodElement);
333 }
334 m_qml.writeEndObject();
335 }
336}
337
338void QmlTypesCreator::generate(const QString &outFileName, const QString &dependenciesFileName)
339{
340 m_qml.writeStartDocument();
341 m_qml.writeLibraryImport(uri: QLatin1String("QtQuick.tooling"), majorVersion: 1, minorVersion: 2);
342 m_qml.write(data: QString::fromLatin1(
343 str: "\n// This file describes the plugin-supplied types contained in the library."
344 "\n// It is used for QML tooling purposes only."
345 "\n//"
346 "\n// This file was auto-generated by qmltyperegistrar.\n\n"));
347 m_qml.writeStartObject(component: QLatin1String("Module"));
348
349 QStringList dependencies;
350 if (!dependenciesFileName.isEmpty()) {
351 QFile file(dependenciesFileName);
352 if (!file.open(flags: QIODevice::ReadOnly)) {
353 fprintf(stderr, format: "Failed to open %s\n", qPrintable(dependenciesFileName));
354 } else {
355 QJsonParseError error { .offset: -1, .error: QJsonParseError::NoError };
356 QJsonDocument doc = QJsonDocument::fromJson(json: file.readAll(), error: &error);
357 if (error.error != QJsonParseError::NoError) {
358 fprintf(stderr, format: "Failed to parse %s\n", qPrintable(dependenciesFileName));
359 } else {
360 const QJsonArray array = doc.array();
361 for (const QJsonValue &value : array)
362 dependencies.append(t: enquote(string: value.toString()));
363 }
364 }
365 } else {
366 // Default dependency is QtQuick 2.0
367 dependencies.append(t: enquote(string: QLatin1String("QtQuick 2.0")));
368 }
369
370 m_qml.writeArrayBinding(name: QLatin1String("dependencies"), elements: dependencies);
371
372 writeComponents();
373
374 m_qml.writeEndObject();
375
376 QSaveFile file(outFileName);
377 file.open(flags: QIODevice::WriteOnly);
378 file.write(data: m_output);
379 file.commit();
380}
381
382

source code of qtdeclarative/src/qmltyperegistrar/qmltypescreator.cpp