Revision control

++ // Copyright (C) 2023 The Qt Company Ltd.
++ // Copyright (C) 2019 Alexey Edelev <semlanik@gmail.com>, Tatyana Borisova <tanusshhka@mail.ru>
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
++ // Qt-Security score:critical reason:data-parser
++
++ #include "messagedefinitionprinter.h"
++
++ #include <google/protobuf/descriptor.h>
++ #include "utils.h"
++ #include "commontemplates.h"
++ #include "generatorcommon.h"
++
++ #include <cassert>
++
++ using namespace ::QtProtobuf;
++ using namespace ::qtprotoccommon;
++ using namespace ::google::protobuf;
++ using namespace ::google::protobuf::io;
++
++ /*!
++ \class MessageDefinitionPrinter
++ \inmodule qtprotobufgen
++ \private
++
++ \brief Generates gRPC message class definition.
++ */
++ MessageDefinitionPrinter::MessageDefinitionPrinter(const Descriptor *message,
++ std::shared_ptr<Printer> printer)
++ : DescriptorPrinterBase<Descriptor>(message,
++ std::move(printer),
++ common::produceMessageTypeMap(message, nullptr))
++ {
++ }
++
++ MessageDefinitionPrinter::~MessageDefinitionPrinter() = default;
++
++ void MessageDefinitionPrinter::printClassMembers()
++ {
++ Indent();
++ common::iterateMessageFields(
++ m_descriptor, [&](const FieldDescriptor *field, const PropertyMap &propertyMap) {
++ if (common::isOneofField(field))
++ return;
++ if (common::isOptionalField(field)) {
++ m_printer->Print(propertyMap, CommonTemplates::MemberOptionalTemplate());
++ } else if (common::isPureMessage(field)) {
++ m_printer->Print(propertyMap, CommonTemplates::MemberMessageTemplate());
++ } else if (field->is_repeated() && !field->is_map()) {
++ m_printer->Print(propertyMap, CommonTemplates::MemberRepeatedTemplate());
++ } else {
++ m_printer->Print(propertyMap, CommonTemplates::MemberTemplate());
++ }
++ });
++
++ common::iterateOneofFields(
++ m_descriptor, [&](const OneofDescriptor *, const PropertyMap &propertyMap) {
++ m_printer->Print(propertyMap, CommonTemplates::MemberOneofTemplate());
++ });
++ Outdent();
++ }
++
++ void MessageDefinitionPrinter::printDataClass()
++ {
++ m_printer->Print(m_typeMap, CommonTemplates::ClassMessageDataBeginDeclarationTemplate());
++ printPublicBlock();
++ printDataClassConstructor();
++ printDataClassCopy();
++ printClassMembers();
++ encloseClass();
++ m_printer->Print("\n");
++ }
++
++ void MessageDefinitionPrinter::printDataClassConstructor()
++ {
++ Indent();
++ m_printer->Print(m_typeMap, CommonTemplates::ConstructorMessageDataDefinitionTemplate());
++ printInitializationList();
++ m_printer->Print(CommonTemplates::EmptyBracesTemplate());
++ Outdent();
++ }
++
++ void MessageDefinitionPrinter::printDataClassCopy()
++ {
++ Indent();
++ m_printer->Print(m_typeMap, CommonTemplates::CopyConstructorMessageDataDefinitionTemplate());
++ Indent();
++ m_printer->Indent();
++ common::iterateMessageFields(
++ m_descriptor, [&](const FieldDescriptor *field, const PropertyMap &propertyMap) {
++ if (common::isOneofField(field))
++ return;
++
++ m_printer->Print(",\n");
++ m_printer->Print(propertyMap,
++ common::isPureMessage(field)
++ ? CommonTemplates::CopyInitializerMemberMessageTemplate()
++ : CommonTemplates::CopyInitializerMemberTemplate());
++ });
++ common::iterateOneofFields(
++ m_descriptor, [&](const OneofDescriptor *, const PropertyMap &propertyMap) {
++ m_printer->Print(",\n");
++ m_printer->Print(propertyMap,
++ CommonTemplates::CopyInitializerMemberOneofTemplate());
++ });
++ m_printer->Outdent();
++ Outdent();
++ m_printer->Print(CommonTemplates::EmptyBracesTemplate());
++ Outdent();
++ }
++
++ void MessageDefinitionPrinter::printClassDefinitionPrivate()
++ {
++ if (common::hasNestedMessages(m_descriptor)) {
++ auto scopeNamespaces = qtprotoccommon::common::getNestedScopeNamespace(m_typeMap["classname"]);
++ m_printer->Print(scopeNamespaces, CommonTemplates::NamespaceTemplate());
++ common::iterateNestedMessages(m_descriptor, [this](const Descriptor *nestedMessage) {
++ MessageDefinitionPrinter nestedPrinter(nestedMessage, m_printer);
++ nestedPrinter.printClassDefinitionPrivate();
++ });
++ m_printer->Print(scopeNamespaces, CommonTemplates::NamespaceClosingTemplate());
++ }
++ printPrivateDurationExtras();
++ printDataClass();
++
++ printDestructor();
++ printFieldsOrdering();
++ printRegisterBody();
++ printConstructors();
++ printCopyFunctionality();
++ printMoveSemantic();
++ printQVariantOperator();
++ printComparisonOperators();
++ printGetters();
++ printPublicExtras();
++ }
++
++ void MessageDefinitionPrinter::printClassDefinition()
++ {
++ printClassDefinitionPrivate();
++ }
++
++ void MessageDefinitionPrinter::printRegisterBody()
++ {
++ std::vector<std::string> registredMetaTypes;
++
++ m_printer->Print(m_typeMap, CommonTemplates::MetaTypeRegistrationMessageDefinition());
++ Indent();
++
++ // Special case:
++ if (m_descriptor->full_name() == "google.protobuf.Any")
++ m_printer->Print("QT_PREPEND_NAMESPACE(QtProtobuf)::Any::registerTypes();\n");
++
++ if (m_descriptor->file()->package() == "google.protobuf") {
++ if (common::hasCustomJsonCoversion(m_descriptor)) {
++ m_printer->Print({ { "type_name", m_descriptor->name() } },
++ CommonTemplates::CustomJsonHanderTemplate());
++ }
++ }
++
++ common::iterateMessageFields(
++ m_descriptor, [&](const FieldDescriptor *field, const PropertyMap &propertyMap) {
++ auto it = propertyMap.find("full_type");
++ assert(it != propertyMap.end());
++ const auto &fullTypeName = it->second;
++
++ // Check if type is already registered
++ if (utils::contains(registredMetaTypes, fullTypeName))
++ return;
++ registredMetaTypes.push_back(fullTypeName);
++ if (field->is_map()) {
++ m_printer->Print(propertyMap,
++ CommonTemplates::MetaTypeRegistrationMapTemplate());
++ }
++ });
++
++ int enumCount = m_descriptor->enum_type_count();
++ for (int i = 0; i < enumCount; ++i) {
++ m_printer->Print(common::produceEnumTypeMap(m_descriptor->enum_type(i), m_descriptor),
++ CommonTemplates::MetaTypeRegistrationLocalEnumTemplate());
++ }
++
++ Outdent();
++ m_printer->Print(CommonTemplates::SimpleBlockEnclosureTemplate());
++ }
++
++ void MessageDefinitionPrinter::printFieldsOrdering()
++ {
++ const int fieldCount = common::generatedFieldCount(m_descriptor);
++ constexpr int MetaFieldsCount = 4;
++ constexpr int Version = 0;
++ constexpr int NullTerminator = 1;
++ constexpr int FakeJsonNameOffset = 1;
++
++ int uint_dataOffset = 1;
++ size_t metadataCharSize = metaCharDataSize();
++ size_t char_dataSize = charDataSize();
++ size_t char_dataSizeTotal = metadataCharSize + NullTerminator + char_dataSize;
++ std::map<std::string, std::string> dataVariables = {
++ { "version_number", std::to_string(Version) },
++ { "classname", m_typeMap["classname"] },
++ { "num_fields", std::to_string(fieldCount) },
++ { "field_number_offset",
++ std::to_string(fieldCount * (uint_dataOffset++) + NullTerminator) },
++ { "property_index_offset",
++ std::to_string(fieldCount * (uint_dataOffset++) + NullTerminator) },
++ { "field_flags_offset", std::to_string(fieldCount * (uint_dataOffset++) + NullTerminator)},
++ { "uint_size", std::to_string(fieldCount * MetaFieldsCount + FakeJsonNameOffset) },
++ { "char_size", std::to_string(char_dataSizeTotal) },
++ { "message_full_name_size", std::to_string(metadataCharSize) },
++ };
++ assert(uint_dataOffset == MetaFieldsCount);
++
++ m_printer->Print(dataVariables, CommonTemplates::PropertyOrderingDataOpeningTemplate());
++ Indent();
++ // uint_data
++ m_printer->Print("// uint_data\n{\n");
++ Indent();
++
++ m_printer->Print("// JSON name offsets:\n");
++ printUintData(CommonTemplates::JsonNameOffsetsUintDataTemplate());
++ // Include an extra offset which points to the end-of-string, so we can efficiently get the
++ // length of all the strings by subtracting adjacent offsets:
++ m_printer->Print({ { "json_name_offset", std::to_string(char_dataSizeTotal - NullTerminator) },
++ { "json_name", "end-of-string-marker" } },
++ CommonTemplates::JsonNameOffsetsUintDataTemplate());
++
++ m_printer->Print("// Field numbers:\n");
++ printUintData(CommonTemplates::FieldNumbersUintDataTemplate());
++
++ m_printer->Print("// Property indices:\n");
++ printUintData(CommonTemplates::QtPropertyIndicesUintDataTemplate());
++
++ m_printer->Print("// Field flags:\n");
++ printUintData(CommonTemplates::FieldFlagsUintDataTemplate());
++
++ Outdent();
++ m_printer->Print("},\n");
++
++ // char_data
++ printCharData();
++ Outdent();
++
++ m_printer->Print(m_typeMap, CommonTemplates::PropertyOrderingDataClosingTemplate());
++
++ m_printer->Print(m_typeMap, CommonTemplates::PropertyOrderingDefinitionTemplate());
++ }
++
++ void MessageDefinitionPrinter::printUintData(const char *templateString)
++ {
++ constexpr size_t NullTerminator = 1;
++ // JSon data starts where string metadata ends
++ size_t jsonOffset = metaCharDataSize() + NullTerminator;
++ const int numFields = m_descriptor->field_count();
++ for (int i = 0, propertyIndex = 0; i < numFields; ++i) {
++ const FieldDescriptor *field = m_descriptor->field(i);
++ if (common::isGroupField(field))
++ continue;
++ const std::string jsonName =
++ utils::escapeBlockComment(utils::escapeCString(field->json_name()));
++ const std::map<std::string, std::string> variables = {
++ { "json_name_offset", std::to_string(jsonOffset) },
++ { "field_number", std::to_string(field->number()) },
++ { "property_index", std::to_string(propertyIndex) },
++ { "field_flags", common::collectFieldFlags(field) },
++ { "json_name", jsonName },
++ };
++
++ // Oneof and optional properties generate additional has<FieldName>
++ // property next to the field property one.
++ // The repeated message property also generates 2 properties per field:
++ // <FieldName> and <FieldName>Data (the old one).
++ // The old one is deprecated and should be removed in Qt7.
++ if (common::isOneofField(field) || common::isOptionalField(field) ||
++ common::isPureMessage(field) || common::isRepeatedMessage(field)) {
++ ++propertyIndex;
++ }
++
++ m_printer->Print(variables, templateString);
++ ++propertyIndex;
++
++ const size_t length = field->json_name().length();
++ jsonOffset += length + NullTerminator;
++ }
++ }
++
++ void MessageDefinitionPrinter::printCharData()
++ {
++ m_printer->Print("// char_data\n");
++
++ m_printer->Print("/* metadata char_data: */\n\"");
++ m_printer->Print(std::string{m_descriptor->full_name()}.c_str());
++ m_printer->Print("\\0\" /* = full message name */\n");
++
++ m_printer->Print("/* field char_data: */\n\"");
++ const int numFields = m_descriptor->field_count();
++ for (int i = 0, emitted = 0; i < numFields; ++i) {
++ const FieldDescriptor *field = m_descriptor->field(i);
++ if (common::isGroupField(field))
++ continue;
++ const std::string jsonName = utils::escapeCString(field->json_name());
++ if (emitted && emitted % 5 == 0) // add some newlines to avoid overly long lines
++ m_printer->Print("\"\n\"");
++ else if (emitted && !jsonName.empty() && jsonName[0] >= '0' && jsonName[0] <= '7')
++ m_printer->Print("\" \""); // keep the digit out of the preceding \0 escape
++ m_printer->Print({ { "json_name", jsonName } }, "$json_name$\\0");
++ ++emitted;
++ }
++ m_printer->Print("\"\n");
++ }
++
++ size_t MessageDefinitionPrinter::metaCharDataSize() const
++ {
++ return m_descriptor->full_name().size();
++ }
++
++ size_t MessageDefinitionPrinter::charDataSize() const
++ {
++ size_t size = 0;
++ const int numFields = m_descriptor->field_count();
++ for (int i = 0; i < numFields; ++i) {
++ const FieldDescriptor *field = m_descriptor->field(i);
++ if (common::isGroupField(field))
++ continue;
++ size += field->json_name().length() + 1; // +1 for the embedded null terminator
++ }
++ return size + 1; // 1 for the trailing null terminator
++ }
++
++ void MessageDefinitionPrinter::printConstructors()
++ {
++ m_printer->Print(m_typeMap, CommonTemplates::ConstructorMessageDefinitionTemplate());
++ m_printer->Print(CommonTemplates::EmptyBracesTemplate());
++ }
++
++ void MessageDefinitionPrinter::printInitializationList()
++ {
++ Indent();
++ m_printer->Indent();
++ common::iterateMessageFields(
++ m_descriptor, [&](const FieldDescriptor *field, PropertyMap propertyMap) {
++ if (field->is_repeated() || common::isOneofField(field)
++ || common::isOptionalField(field))
++ return;
++
++ if (!propertyMap["initializer"].empty()) {
++ m_printer->Print(",\n");
++ m_printer->Print(propertyMap, CommonTemplates::InitializerMemberTemplate());
++ }
++ });
++ m_printer->Outdent();
++ Outdent();
++ }
++
++ void MessageDefinitionPrinter::printCopyFunctionality()
++ {
++ assert(m_descriptor != nullptr);
++ m_printer->Print(m_typeMap, CommonTemplates::CopyConstructorDefinitionTemplate());
++ m_printer->Print(m_typeMap, CommonTemplates::AssignmentOperatorDefinitionTemplate());
++ }
++
++ void MessageDefinitionPrinter::printMoveSemantic()
++ {
++ assert(m_descriptor != nullptr);
++ m_printer->Print(m_typeMap, CommonTemplates::MoveConstructorDefinitionTemplate());
++ }
++
++ void MessageDefinitionPrinter::printQVariantOperator()
++ {
++ assert(m_descriptor != nullptr);
++ m_printer->Print(m_typeMap, CommonTemplates::QVariantOperatorDefinitionTemplate());
++ }
++
++ void MessageDefinitionPrinter::printComparisonOperators()
++ {
++ assert(m_descriptor != nullptr);
++
++ m_printer->Print(m_typeMap, CommonTemplates::ComparesEqualDefinitionTemplate());
++
++ Indent();
++ Indent();
++ common::iterateMessageFields(
++ m_descriptor, [&](const FieldDescriptor *field, PropertyMap &propertyMap) {
++ if (common::isOneofField(field))
++ return;
++ m_printer->Print("\n&& ");
++ if (common::isPureMessage(field)) {
++ m_printer->Print(propertyMap,
++ CommonTemplates::EqualOperatorMemberMessageTemplate());
++ } else if (field->type() == FieldDescriptor::TYPE_MESSAGE
++ && field->is_repeated()) {
++ m_printer->Print(propertyMap,
++ CommonTemplates::EqualOperatorMemberRepeatedTemplate());
++ } else {
++ m_printer->Print(propertyMap,
++ CommonTemplates::EqualOperatorMemberTemplate());
++ }
++ });
++
++ common::iterateOneofFields(
++ m_descriptor, [&](const OneofDescriptor *, const PropertyMap &propertyMap) {
++ m_printer->Print("\n&& ");
++ m_printer->Print(propertyMap, CommonTemplates::EqualOperatorMemberOneofTemplate());
++ });
++
++ Outdent();
++ Outdent();
++
++ m_printer->Print(";\n");
++ m_printer->Print(CommonTemplates::SimpleBlockEnclosureTemplate());
++ }
++
++ void MessageDefinitionPrinter::printGetters()
++ {
++ common::iterateMessageFields(
++ m_descriptor, [&](const FieldDescriptor *field, PropertyMap &propertyMap) {
++ if (common::isOneofField(field)) {
++ if (common::isPureMessage(field)) {
++ m_printer->Print(
++ propertyMap,
++ CommonTemplates::PrivateGetterOneofMessageDefinitionTemplate());
++ m_printer->Print(propertyMap,
++ CommonTemplates::GetterOneofMessageDefinitionTemplate());
++ } else {
++ m_printer->Print(propertyMap,
++ CommonTemplates::PrivateGetterOneofDefinitionTemplate());
++ m_printer->Print(propertyMap,
++ CommonTemplates::GetterOneofDefinitionTemplate());
++ }
++ return;
++ }
++
++ if (common::isOptionalField(field)) {
++ m_printer->Print(propertyMap,
++ CommonTemplates::PrivateGetterOptionalDefinitionTemplate());
++ m_printer->Print(propertyMap,
++ CommonTemplates::GetterOptionalDefinitionTemplate());
++ return;
++ }
++
++ switch (field->type()) {
++ case FieldDescriptor::TYPE_MESSAGE:
++ if (common::isPureMessage(field)) {
++ m_printer->Print(propertyMap,
++ CommonTemplates::PrivateGetterMessageDefinitionTemplate());
++ m_printer->Print(propertyMap,
++ CommonTemplates::GetterMessageDefinitionTemplate());
++ m_printer->Print(propertyMap,
++ CommonTemplates::GetterMessageMutableDefinitionTemplate());
++
++ m_printer->Print(propertyMap,
++ CommonTemplates::ClearMessageDefinitionTemplate());
++ } else {
++ m_printer->Print(propertyMap,
++ CommonTemplates::GetterComplexDefinitionTemplate());
++ }
++ break;
++ case FieldDescriptor::FieldDescriptor::TYPE_STRING:
++ case FieldDescriptor::FieldDescriptor::TYPE_BYTES:
++ m_printer->Print(propertyMap,
++ CommonTemplates::GetterComplexDefinitionTemplate());
++ break;
++ case FieldDescriptor::TYPE_FLOAT:
++ case FieldDescriptor::TYPE_DOUBLE:
++ default:
++ m_printer->Print(propertyMap,
++ field->is_repeated()
++ ? CommonTemplates::GetterComplexDefinitionTemplate()
++ : CommonTemplates::GetterDefinitionTemplate());
++ break;
++ }
++
++ });
++
++ common::iterateMessageFields(
++ m_descriptor, [&](const FieldDescriptor *field, PropertyMap &propertyMap) {
++ bool isTriviallyCopyable = common::isTriviallyCopyable(field);
++ if (common::isOneofField(field)) {
++ m_printer->Print(propertyMap,
++ isTriviallyCopyable
++ ? CommonTemplates::SetterOneofDefinitionTemplate()
++ : CommonTemplates::SetterComplexOneofDefinitionTemplate());
++ m_printer->Print(
++ propertyMap,
++ common::isPureMessage(field)
++ ? CommonTemplates::PrivateSetterOneofMessageDefinitionTemplate()
++ : CommonTemplates::PrivateSetterOneofDefinitionTemplate());
++ return;
++ }
++
++ if (common::isOptionalField(field)) {
++
++ m_printer->Print(propertyMap,
++ isTriviallyCopyable ?
++ CommonTemplates::SetterOptionalDefinitionTemplate() :
++ CommonTemplates::SetterComplexOptionalDefinitionTemplate());
++ m_printer->Print(propertyMap,
++ CommonTemplates::PrivateSetterOptionalDefinitionTemplate());
++ m_printer->Print(propertyMap,
++ CommonTemplates::ClearOptionalDefinitionTemplate());
++ return;
++ }
++
++ switch (field->type()) {
++ case FieldDescriptor::TYPE_MESSAGE:
++ if (common::isPureMessage(field)) {
++ m_printer->Print(propertyMap,
++ CommonTemplates::PrivateSetterMessageDefinitionTemplate());
++ m_printer->Print(propertyMap,
++ CommonTemplates::SetterMessageDefinitionTemplate());
++ } else {
++ m_printer->Print(propertyMap,
++ CommonTemplates::SetterComplexDefinitionTemplate());
++ }
++ break;
++ case FieldDescriptor::FieldDescriptor::TYPE_STRING:
++ case FieldDescriptor::FieldDescriptor::TYPE_BYTES:
++ m_printer->Print(propertyMap,
++ CommonTemplates::SetterComplexDefinitionTemplate());
++ break;
++ case FieldDescriptor::TYPE_FLOAT:
++ case FieldDescriptor::TYPE_DOUBLE:
++ if (!field->is_repeated()) {
++ m_printer->Print(propertyMap,
++ CommonTemplates::SetterFloatingPointDefinitionTemplate());
++ break;
++ }
++ // fall through
++ default:
++ m_printer->Print(propertyMap,
++ field->is_repeated()
++ ? CommonTemplates::SetterComplexDefinitionTemplate()
++ : CommonTemplates::SetterDefinitionTemplate());
++ break;
++ }
++ });
++ common::iterateOneofFields(
++ m_descriptor, [&](const OneofDescriptor *, const PropertyMap &propertyMap) {
++ m_printer->Print(propertyMap,
++ CommonTemplates::GetterOneofFieldNumberDefinitionTemplate());
++ m_printer->Print(propertyMap, CommonTemplates::ClearOneofDefinitionTemplate());
++ });
++ }
++
++ void MessageDefinitionPrinter::printDestructor()
++ {
++ m_printer->Print(m_typeMap, "$classname$::~$classname$() = default;\n\n");
++ }
++
++ void MessageDefinitionPrinter::printPrivateDurationExtras()
++ {
++ if (m_descriptor->full_name() == "google.protobuf.Duration") {
++ m_printer->Print(
++ "constexpr qint64 SecondsRange = 315'576'000'000LL;\n"
++ "constexpr qint32 NanosRange = 999'999'999;\n"
++ "static constexpr bool checkDurationRange(qint64 seconds, qint32 nanos) noexcept\n"
++ "{\n"
++ " if (seconds > SecondsRange || seconds < -SecondsRange) {\n"
++ " qWarning(\"Invalid Duration: seconds=%lld out of range [-%lld, +%lld]\",\n"
++ " seconds, SecondsRange, -SecondsRange);\n"
++ " return false;\n"
++ " }\n"
++ " if (nanos > NanosRange || nanos < -NanosRange) {\n"
++ " qWarning(\"Invalid Duration: nanos=%d out of range [-%d, +%d]\",\n"
++ " nanos, NanosRange, -NanosRange);\n"
++ " return false;\n"
++ " }\n"
++ " return true;\n"
++ "}\n"
++ "static constexpr bool checkSignMatch(qint64 seconds, qint32 nanos) noexcept\n"
++ "{\n"
++ " const bool isSignMatch = !(seconds != 0 && nanos != 0 \n"
++ " && (seconds ^ qint64(nanos)) < 0);\n"
++ " if (!isSignMatch) {\n"
++ " qWarning(\"Invalid sign between seconds = %lld and nanos = %d\",\n"
++ " seconds, nanos);\n"
++ " }\n"
++ " return isSignMatch;\n"
++ "}\n\n");
++ }
++ }
++
++ void MessageDefinitionPrinter::printPublicExtras()
++ {
++ if (m_descriptor->full_name() == "google.protobuf.Timestamp") {
++ m_printer->Print(
++ "Timestamp Timestamp::fromDateTime(const QDateTime &dateTime)\n"
++ "{\n"
++ " Timestamp ts;\n"
++ " ts.setSeconds(dateTime.toMSecsSinceEpoch() / 1000);\n"
++ " ts.setNanos((dateTime.toMSecsSinceEpoch() % 1000) * 1000000);\n"
++ " return ts;\n"
++ "}\n\n"
++ "QDateTime Timestamp::toDateTime() const\n"
++ "{\n"
++ " return QDateTime::fromMSecsSinceEpoch(\n"
++ " seconds() * 1000 + nanos() / 1000000, QTimeZone(QTimeZone::UTC));\n"
++ "}\n\n");
++ } else if (m_descriptor->full_name() == "google.protobuf.Duration") {
++ m_printer->Print(
++ "bool Duration::isValid(QtProtobuf::int64 seconds, QtProtobuf::int32 nanos)\n"
++ "{\n"
++ " return checkDurationRange(seconds, nanos) && checkSignMatch(seconds, nanos);\n"
++ "}\n\n");
++ }
++ }
++
++ void MessageDefinitionPrinter::printClassRegistration(Printer *printer)
++ {
++ if (common::hasNestedMessages(m_descriptor)) {
++ auto scopeNamespaces = common::getNestedScopeNamespace(m_typeMap["classname"]);
++ printer->Print(scopeNamespaces, CommonTemplates::NamespaceTemplate());
++ common::iterateNestedMessages(
++ m_descriptor, [this, &printer](const Descriptor *nestedMessage) {
++ MessageDefinitionPrinter nestedPrinter(nestedMessage, m_printer);
++ nestedPrinter.printClassRegistration(printer);
++ });
++ printer->Print(scopeNamespaces, CommonTemplates::NamespaceClosingTemplate());
++ }
++
++ printer->Print(m_typeMap, CommonTemplates::RegistrarTemplate());
++ }
++
++ void MessageDefinitionPrinter::printQmlPluginClassRegistration()
++ {
++ Indent();
++ m_printer->Print(m_typeMap, CommonTemplates::QmlRegisterMessageTypeTemplate());
++ Outdent();
++ }