diff options
author | Ulf Hermann <ulf.hermann@qt.io> | 2020-11-06 12:33:51 +0100 |
---|---|---|
committer | Ulf Hermann <ulf.hermann@qt.io> | 2020-11-06 22:41:51 +0100 |
commit | 421ad80dee10e7b5eff352aecb2986c370f0e487 (patch) | |
tree | 152b155b255ac99e9dd34f36dc64d047e89ceb57 /src/qmltyperegistrar/metatypesjsonprocessor.cpp | |
parent | 68b9ab7b93320a975c2f20c09eddccf0fdb275b7 (diff) |
qmltyperegistrar: Move JSON processing into separate class
qmltyperegistrar.cpp was getting unwieldy.
Change-Id: I2172253d81c0fca724bb0fef4a96d50a93c47428
Reviewed-by: Maximilian Goldstein <max.goldstein@qt.io>
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Diffstat (limited to 'src/qmltyperegistrar/metatypesjsonprocessor.cpp')
-rw-r--r-- | src/qmltyperegistrar/metatypesjsonprocessor.cpp | 335 |
1 files changed, 335 insertions, 0 deletions
diff --git a/src/qmltyperegistrar/metatypesjsonprocessor.cpp b/src/qmltyperegistrar/metatypesjsonprocessor.cpp new file mode 100644 index 0000000000..f71a94c67d --- /dev/null +++ b/src/qmltyperegistrar/metatypesjsonprocessor.cpp @@ -0,0 +1,335 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "metatypesjsonprocessor.h" + +#include <QtCore/qfile.h> +#include <QtCore/qjsonarray.h> +#include <QtCore/qjsondocument.h> +#include <QtCore/qqueue.h> + + +bool MetaTypesJsonProcessor::processTypes(const QStringList &files) +{ + for (const QString &source: files) { + QJsonDocument metaObjects; + { + QFile f(source); + if (!f.open(QIODevice::ReadOnly)) { + fprintf(stderr, "Error opening %s for reading\n", qPrintable(source)); + return false; + } + QJsonParseError error = {0, QJsonParseError::NoError}; + metaObjects = QJsonDocument::fromJson(f.readAll(), &error); + if (error.error != QJsonParseError::NoError) { + fprintf(stderr, "Error parsing %s\n", qPrintable(source)); + return false; + } + } + + if (metaObjects.isArray()) { + const QJsonArray metaObjectsArray = metaObjects.array(); + for (const QJsonValue metaObject : metaObjectsArray) { + if (!metaObject.isObject()) { + fprintf(stderr, "Error parsing %s: JSON is not an object\n", + qPrintable(source)); + return false; + } + + processTypes(metaObject.toObject()); + } + } else if (metaObjects.isObject()) { + processTypes(metaObjects.object()); + } else { + fprintf(stderr, "Error parsing %s: JSON is not an object or an array\n", + qPrintable(source)); + return false; + } + } + + return true; +} + +bool MetaTypesJsonProcessor::processForeignTypes(const QStringList &foreignTypesFiles) +{ + bool success = true; + + for (const QString &types : foreignTypesFiles) { + QFile typesFile(types); + if (!typesFile.open(QIODevice::ReadOnly)) { + fprintf(stderr, "Cannot open foreign types file %s\n", qPrintable(types)); + success = false; + continue; + } + + QJsonParseError error = {0, QJsonParseError::NoError}; + QJsonDocument foreignMetaObjects = QJsonDocument::fromJson(typesFile.readAll(), &error); + if (error.error != QJsonParseError::NoError) { + fprintf(stderr, "Error parsing %s\n", qPrintable(types)); + success = false; + continue; + } + + const QJsonArray foreignObjectsArray = foreignMetaObjects.array(); + for (const QJsonValue metaObject : foreignObjectsArray) { + if (!metaObject.isObject()) { + fprintf(stderr, "Error parsing %s: JSON is not an object\n", + qPrintable(types)); + success = false; + continue; + } + + processForeignTypes(metaObject.toObject()); + } + } + return success; +} + +void MetaTypesJsonProcessor::postProcessTypes() +{ + sortTypes(m_types); + sortIncludes(); +} + +void MetaTypesJsonProcessor::postProcessForeignTypes() +{ + sortTypes(m_foreignTypes); + m_types += foreignRelatedTypes(); + sortTypes(m_types); +} + +MetaTypesJsonProcessor::RegistrationMode MetaTypesJsonProcessor::qmlTypeRegistrationMode( + const QJsonObject &classDef) +{ + const QJsonArray classInfos = classDef[QLatin1String("classInfos")].toArray(); + for (const QJsonValue info : classInfos) { + const QString name = info[QLatin1String("name")].toString(); + if (name == QLatin1String("QML.Element")) { + if (classDef[QLatin1String("object")].toBool()) + return ObjectRegistration; + if (classDef[QLatin1String("gadget")].toBool()) + return GadgetRegistration; + if (classDef[QLatin1String("namespace")].toBool()) + return NamespaceRegistration; + qWarning() << "Not registering classInfo which is neither an object, " + "nor a gadget, nor a namespace:" + << name; + break; + } + } + return NoRegistration; +} + +QVector<QJsonObject> MetaTypesJsonProcessor::foreignRelatedTypes() const +{ + const QLatin1String classInfosKey("classInfos"); + const QLatin1String nameKey("name"); + const QLatin1String qualifiedClassNameKey("qualifiedClassName"); + const QLatin1String qmlNamePrefix("QML."); + const QLatin1String qmlForeignName("QML.Foreign"); + const QLatin1String qmlAttachedName("QML.Attached"); + const QLatin1String valueKey("value"); + const QLatin1String superClassesKey("superClasses"); + const QLatin1String accessKey("access"); + const QLatin1String publicAccess("public"); + + QSet<QString> processedRelatedNames; + QQueue<QJsonObject> typeQueue; + typeQueue.append(m_types); + QVector<QJsonObject> relatedTypes; + + // First mark all classes registered from this module as already processed. + for (const QJsonObject &type : m_types) { + processedRelatedNames.insert(type.value(qualifiedClassNameKey).toString()); + const auto classInfos = type.value(classInfosKey).toArray(); + for (const QJsonValue classInfo : classInfos) { + const QJsonObject obj = classInfo.toObject(); + if (obj.value(nameKey).toString() == qmlForeignName) { + processedRelatedNames.insert(obj.value(valueKey).toString()); + break; + } + } + } + + // Then mark all classes registered from other modules as already processed. + // We don't want to generate them again for this module. + for (const QJsonObject &foreignType : m_foreignTypes) { + const auto classInfos = foreignType.value(classInfosKey).toArray(); + bool seenQmlPrefix = false; + for (const QJsonValue classInfo : classInfos) { + const QJsonObject obj = classInfo.toObject(); + const QString name = obj.value(nameKey).toString(); + if (!seenQmlPrefix && name.startsWith(qmlNamePrefix)) { + processedRelatedNames.insert(foreignType.value(qualifiedClassNameKey).toString()); + seenQmlPrefix = true; + } + if (name == qmlForeignName) { + processedRelatedNames.insert(obj.value(valueKey).toString()); + break; + } + } + } + + auto addType = [&](const QString &typeName) { + if (processedRelatedNames.contains(typeName)) + return; + processedRelatedNames.insert(typeName); + if (const QJsonObject *other + = QmlTypesClassDescription::findType(m_foreignTypes, typeName)) { + relatedTypes.append(*other); + typeQueue.enqueue(*other); + } + }; + + // Then recursively iterate the super types and attached types, marking the + // ones we are interested in as related. + while (!typeQueue.isEmpty()) { + const QJsonObject classDef = typeQueue.dequeue(); + + const auto classInfos = classDef.value(classInfosKey).toArray(); + for (const QJsonValue classInfo : classInfos) { + const QJsonObject obj = classInfo.toObject(); + if (obj.value(nameKey).toString() == qmlAttachedName) { + addType(obj.value(valueKey).toString()); + } else if (obj.value(nameKey).toString() == qmlForeignName) { + const QString foreignClassName = obj.value(valueKey).toString(); + if (const QJsonObject *other = QmlTypesClassDescription::findType( + m_foreignTypes, foreignClassName)) { + const auto otherSupers = other->value(superClassesKey).toArray(); + if (!otherSupers.isEmpty()) { + const QJsonObject otherSuperObject = otherSupers.first().toObject(); + if (otherSuperObject.value(accessKey).toString() == publicAccess) + addType(otherSuperObject.value(nameKey).toString()); + } + + const auto otherClassInfos = other->value(classInfosKey).toArray(); + for (const QJsonValue otherClassInfo : otherClassInfos) { + const QJsonObject obj = otherClassInfo.toObject(); + if (obj.value(nameKey).toString() == qmlAttachedName) { + addType(obj.value(valueKey).toString()); + break; + } + // No, you cannot chain QML_FOREIGN declarations. Sorry. + } + break; + } + } + } + + const auto supers = classDef.value(superClassesKey).toArray(); + if (!supers.isEmpty()) { + const QJsonObject superObject = supers.first().toObject(); + if (superObject.value(accessKey).toString() == publicAccess) + addType(superObject.value(nameKey).toString()); + } + } + + return relatedTypes; +} + +void MetaTypesJsonProcessor::sortTypes(QVector<QJsonObject> &types) +{ + const QLatin1String qualifiedClassNameKey("qualifiedClassName"); + std::sort(types.begin(), types.end(), [&](const QJsonObject &a, const QJsonObject &b) { + return a.value(qualifiedClassNameKey).toString() < + b.value(qualifiedClassNameKey).toString(); + }); +} + +void MetaTypesJsonProcessor::sortIncludes() +{ + std::sort(m_includes.begin(), m_includes.end()); + const auto newEnd = std::unique(m_includes.begin(), m_includes.end()); + m_includes.erase(newEnd, m_includes.end()); +} + +QString MetaTypesJsonProcessor::resolvedInclude(const QString &include) +{ + return (m_privateIncludes && include.endsWith(QLatin1String("_p.h"))) + ? QLatin1String("private/") + include + : include; +} + +void MetaTypesJsonProcessor::processTypes(const QJsonObject &types) +{ + const QString include = resolvedInclude(types[QLatin1String("inputFile")].toString()); + const QJsonArray classes = types[QLatin1String("classes")].toArray(); + for (const QJsonValue cls : classes) { + QJsonObject classDef = cls.toObject(); + classDef.insert(QLatin1String("inputFile"), include); + + switch (qmlTypeRegistrationMode(classDef)) { + case NamespaceRegistration: + case GadgetRegistration: + case ObjectRegistration: { + if (!include.endsWith(QLatin1String(".h")) + && !include.endsWith(QLatin1String(".hpp")) + && !include.endsWith(QLatin1String(".hxx")) + && include.contains(QLatin1Char('.'))) { + fprintf(stderr, + "Class %s is declared in %s, which appears not to be a header.\n" + "The compilation of its registration to QML may fail.\n", + qPrintable(classDef.value(QLatin1String("qualifiedClassName")) + .toString()), + qPrintable(include)); + } + m_includes.append(include); + { + bool shouldRegister = true; + for (const QJsonValue v : + classDef.value(QLatin1String("classInfos")).toArray()) { + if (v[QLatin1String("name")].toString() + == QLatin1String("QML.ManualRegistration")) { + shouldRegister = QStringView(u"true").compare( + v[QLatin1String("value")].toString(), + Qt::CaseInsensitive) != 0; + } + } + classDef.insert(QLatin1String("registerable"), shouldRegister); + } + + m_types.append(classDef); + break; + } + case NoRegistration: + m_foreignTypes.append(classDef); + break; + } + } +} + +void MetaTypesJsonProcessor::processForeignTypes(const QJsonObject &types) +{ + const QString include = types[QLatin1String("inputFile")].toString(); + const QJsonArray classes = types[QLatin1String("classes")].toArray(); + for (const QJsonValue cls : classes) { + QJsonObject classDef = cls.toObject(); + classDef.insert(QLatin1String("inputFile"), include); + m_foreignTypes.append(classDef); + } +} |