Forráskód Böngészése

Implement map fields generation

- Add map fields detection
- Add generation of map fiels
- Add metatypes registraion for map fields
TODO: Map tests failed because of hidden reasons
      Will fix ASAP.
Alexey Edelev 6 éve
szülő
commit
8e5cb78a11

+ 1 - 1
examples/addressbook/proto/addressbook.proto

@@ -56,7 +56,7 @@ message Contact {
     string firstName = 1;
     string lastName = 2;
     string middleName = 3;
-
+    map<int32, PhoneNumber> phones = 4;
     Address address = 5;
     Job job = 6;
 }

+ 12 - 1
src/generator/generator.cpp

@@ -87,7 +87,18 @@ bool QtGenerator::Generate(const FileDescriptor *file,
 
     for (int i = 0; i < file->message_type_count(); i++) {
         const Descriptor *message = file->message_type(i);
-        if (message->nested_type_count() > 0) {
+
+        //Detect nested fields and filter maps fields
+        int mapsFieldsCount = 0;
+        for (int j = 0; j < message->nested_type_count(); j++) {
+            for (int k = 0; k < message->field_count(); k++) {
+                if (message->field(k)->is_map() && message->field(k)->message_type() == message->nested_type(j)) {
+                    ++mapsFieldsCount;
+                }
+            }
+        }
+
+        if (message->nested_type_count() > 0 && message->nested_type_count() > mapsFieldsCount) {
             std::cerr << file->name() << ":" << (message->index() + 1) << ": " << " Error: Meta object features not supported for nested classes in " << message->full_name() << std::endl;
             continue;
         }

+ 81 - 36
src/generator/protobufclassgenerator.cpp

@@ -157,51 +157,64 @@ void ProtobufClassGenerator::printIncludes()
     mPrinter.Print(Templates::DefaultProtobufIncludesTemplate);
 
     std::set<std::string> existingIncludes;
+    for (int i = 0; i < mMessage->field_count(); i++) {
+        printInclude(mMessage->field(i), existingIncludes);
+    }
+}
+
+void ProtobufClassGenerator::printInclude(const FieldDescriptor *field, std::set<std::string> &existingIncludes)
+{
+    assert(field != nullptr);
     std::string newInclude;
     const char* includeTemplate;
-    for (int i = 0; i < mMessage->field_count(); i++) {
-        const FieldDescriptor* field = mMessage->field(i);
-        switch (field->type()) {
-        case FieldDescriptor::TYPE_MESSAGE: {
+    switch (field->type()) {
+    case FieldDescriptor::TYPE_MESSAGE:
+        if ( field->is_map() ) {
+            newInclude = "QMap";
+            assert(field->message_type() != nullptr);
+            assert(field->message_type()->field_count() == 2);
+            printInclude(field->message_type()->field(0), existingIncludes);
+            printInclude(field->message_type()->field(1), existingIncludes);
+            includeTemplate = Templates::ExternalIncludeTemplate;
+        } else {
             std::string typeName = field->message_type()->name();
             utils::tolower(typeName);
             newInclude = typeName;
             includeTemplate = Templates::InternalIncludeTemplate;
         }
-            break;
-        case FieldDescriptor::TYPE_BYTES:
-            newInclude = "QByteArray";
-            includeTemplate = Templates::ExternalIncludeTemplate;
-            break;
-        case FieldDescriptor::TYPE_STRING:
-            newInclude = "QString";
-            includeTemplate = Templates::ExternalIncludeTemplate;
-            break;
-        case FieldDescriptor::TYPE_ENUM: {
-            EnumVisibility enumVisibily = getEnumVisibility(field);
-            if (enumVisibily == GLOBAL_ENUM) {
-                includeTemplate = Templates::GlobalEnumIncludeTemplate;
-            } else if (enumVisibily == NEIGHBOUR_ENUM){
-                includeTemplate = Templates::InternalIncludeTemplate;
-                std::string fullEnumName = field->enum_type()->full_name();
-                std::vector<std::string> fullEnumNameParts;
-                utils::split(fullEnumName, fullEnumNameParts, '.');
-                std::string enumTypeOwner = fullEnumNameParts.at(fullEnumNameParts.size() - 2);
-                utils::tolower(enumTypeOwner);
-                newInclude = enumTypeOwner;
-            } else {
-                continue;
-            }
-        }
-            break;
-        default:
-            continue;
+        break;
+    case FieldDescriptor::TYPE_BYTES:
+        newInclude = "QByteArray";
+        includeTemplate = Templates::ExternalIncludeTemplate;
+        break;
+    case FieldDescriptor::TYPE_STRING:
+        newInclude = "QString";
+        includeTemplate = Templates::ExternalIncludeTemplate;
+        break;
+    case FieldDescriptor::TYPE_ENUM: {
+        EnumVisibility enumVisibily = getEnumVisibility(field);
+        if (enumVisibily == GLOBAL_ENUM) {
+            includeTemplate = Templates::GlobalEnumIncludeTemplate;
+        } else if (enumVisibily == NEIGHBOUR_ENUM){
+            includeTemplate = Templates::InternalIncludeTemplate;
+            std::string fullEnumName = field->enum_type()->full_name();
+            std::vector<std::string> fullEnumNameParts;
+            utils::split(fullEnumName, fullEnumNameParts, '.');
+            std::string enumTypeOwner = fullEnumNameParts.at(fullEnumNameParts.size() - 2);
+            utils::tolower(enumTypeOwner);
+            newInclude = enumTypeOwner;
+        } else {
+            return;
         }
+    }
+        break;
+    default:
+        return;
+    }
 
-        if (existingIncludes.find(newInclude) == std::end(existingIncludes)) {
-            mPrinter.Print({{"include", newInclude}}, includeTemplate);
-            existingIncludes.insert(newInclude);
-        }
+    if (existingIncludes.find(newInclude) == std::end(existingIncludes)) {
+        mPrinter.Print({{"include", newInclude}}, includeTemplate);
+        existingIncludes.insert(newInclude);
     }
 }
 
@@ -256,6 +269,9 @@ std::string ProtobufClassGenerator::getTypeName(const FieldDescriptor *field)
         namespaceTypeName = getNamespacesList(msg, typeNamespace);
         typeName = namespaceTypeName.append(msg->name());
 
+        if ( field->is_map() ) {
+            return field->message_type()->name();
+        }
         if (field->is_repeated()) {
             return namespaceTypeName.append("List");
         }
@@ -348,6 +364,33 @@ void ProtobufClassGenerator::printConstructor()
     mPrinter.Print(Templates::ConstructorContentTemplate);
 }
 
+void ProtobufClassGenerator::printMaps()
+{
+    Indent();
+    for (int i = 0; i < mMessage->field_count(); i++) {
+        const FieldDescriptor* field = mMessage->field(i);
+        if (field->is_map()) {
+            std::string keyType = getTypeName(field->message_type()->field(0));
+            std::string valueType = getTypeName(field->message_type()->field(1));
+             mPrinter.Print({{"classname",field->message_type()->name()},
+                             {"key", keyType},
+                             {"value", valueType}}, Templates::MapTypeUsingTemplate);
+        }
+    }
+    Outdent();
+}
+
+void ProtobufClassGenerator::printMapsMetaTypesDeclaration()
+{
+    for (int i = 0; i < mMessage->field_count(); i++) {
+        const FieldDescriptor* field = mMessage->field(i);
+        if (field->is_map()) {
+             mPrinter.Print({{"classname", field->message_type()->name()},
+                             {"namespaces", mNamespacesColonDelimited + "::" + mClassName}}, Templates::DeclareMetaTypeTemplate);
+        }
+    }
+}
+
 void ProtobufClassGenerator::printProperties()
 {
     assert(mMessage != nullptr);
@@ -366,6 +409,7 @@ void ProtobufClassGenerator::printProperties()
 
     //public section
     printPublic();
+    printMaps();
 
     //Body
     Indent();
@@ -491,4 +535,5 @@ void ProtobufClassGenerator::run()
     printListType();
     encloseNamespaces();
     printMetaTypeDeclaration();
+    printMapsMetaTypesDeclaration();
 }

+ 3 - 0
src/generator/protobufclassgenerator.h

@@ -68,6 +68,9 @@ public:
     void printRegisterTypes();
     void printConstructor();
     void printListType();
+    void printInclude(const google::protobuf::FieldDescriptor *field, std::set<std::string> &existingIncludes);
+    void printMaps();
+    void printMapsMetaTypesDeclaration();
 
     std::set<std::string> extractModels() const;
 

+ 14 - 1
src/generator/protobufsourcegenerator.cpp

@@ -43,7 +43,20 @@ ProtobufSourceGenerator::ProtobufSourceGenerator(const google::protobuf::Descrip
 
 void ProtobufSourceGenerator::printRegisterBody()
 {
-    mPrinter.Print({{"classname", mClassName}, {"namespaces", mNamespacesColonDelimited}}, Templates::ComplexTypeRegistrationTemplate);
+    mPrinter.Print({{"classname", mClassName}, {"namespaces", mNamespacesColonDelimited}},
+                   Templates::ComplexTypeRegistrationTemplate);
+
+    for (int i = 0; i < mMessage->field_count(); i++) {
+        const FieldDescriptor* field = mMessage->field(i);
+        if (field->is_map()) {
+            mPrinter.Print({{"classname", field->message_type()->name()},
+                            {"namespaces", mNamespacesColonDelimited + "::" + mClassName}},
+                           Templates::RegisterMetaTypeTemplate);
+        }
+    }
+
+    mPrinter.Print(Templates::SimpleBlockEnclosureTemplate);
+    mPrinter.Print(Templates::SimpleBlockEnclosureTemplate);
 }
 
 void ProtobufSourceGenerator::printFieldsOrdering() {

+ 3 - 2
src/generator/templates.cpp

@@ -51,9 +51,9 @@ const char *Templates::ComplexTypeRegistrationTemplate = "void $classname$::regi
                                                          "        int listMetaTypeId = qRegisterMetaType<$classname$List>(\"$classname$List\");\n"
                                                          "        qRegisterMetaType<$namespaces$::$classname$>(\"$namespaces$::$classname$\");\n"
                                                          "        qRegisterMetaType<$namespaces$::$classname$List>(\"$namespaces$::$classname$List\");\n"
-                                                         "        registerSerializers(metaTypeId, listMetaTypeId);\n"
-                                                         "    }\n}\n";
+                                                         "        registerSerializers(metaTypeId, listMetaTypeId);\n";
 const char *Templates::ComplexListTypeUsingTemplate = "using $classname$List = QList<$classname$>;\n";
+const char *Templates::MapTypeUsingTemplate = "using $classname$ = QMap<$key$, $value$>;\n";
 
 const char *Templates::EnumTypeUsingTemplate = "using $enum$List = QList<$enum$>;\n";
 
@@ -127,6 +127,7 @@ const char *Templates::ConstructorContentTemplate = "{\n    registerTypes();\n}\
 
 const char *Templates::DeclareMetaTypeTemplate = "Q_DECLARE_METATYPE($namespaces$::$classname$)\n";
 const char *Templates::DeclareComplexListTypeTemplate = "Q_DECLARE_METATYPE($namespaces$::$classname$List)\n";
+const char *Templates::RegisterMetaTypeTemplate = "        qRegisterMetaType<$namespaces$::$classname$>(\"$namespaces$::$classname$\");\n";
 
 const char *Templates::QEnumTemplate = "Q_ENUM($type$)\n";
 

+ 2 - 0
src/generator/templates.h

@@ -44,6 +44,7 @@ public:
     static const char *ComplexTypeRegistrationMethodTemplate;
     static const char *ComplexTypeRegistrationTemplate;
     static const char *ComplexListTypeUsingTemplate;
+    static const char *MapTypeUsingTemplate;
     static const char *EnumTypeUsingTemplate;
     static const char *NamespaceTemplate;
     static const char *UsingNamespaceTemplate;
@@ -87,6 +88,7 @@ public:
     static const char *ConstructorContentTemplate;
     static const char *DeclareMetaTypeTemplate;
     static const char *DeclareComplexListTypeTemplate;
+    static const char *RegisterMetaTypeTemplate;
     static const char *QEnumTemplate;
 
     //Service templates

+ 2 - 0
tests/CMakeLists.txt

@@ -76,6 +76,8 @@ set(EXPECTED_GENERATED_HEADERS
     testserviceclient.h
     testserviceserver.h
     simpleenumlistmessage.h
+    simplesint32mapmessage.h
+    simplestringmapmessage.h
 )
 
 foreach(EXPECTED_GENERATED_HEADER ${EXPECTED_GENERATED_HEADERS})

+ 8 - 0
tests/proto/simpletest.proto

@@ -169,6 +169,14 @@ message RepeatedSFixedInt64Message {
     repeated sfixed64 testRepeatedInt = 1;
 }
 
+message SimpleSInt32MapMessage {
+    map<sint32, SimpleStringMessage> mapField = 1;
+}
+
+message SimpleStringMapMessage {
+    map<string, SimpleStringMessage> mapField = 1;
+}
+
 enum TestEnum {
     TEST_ENUM_VALUE0 = 0;
     TEST_ENUM_VALUE1 = 1;

+ 26 - 0
tests/simpletest.cpp

@@ -63,6 +63,9 @@
 #include "repeatedfixedint64message.h"
 #include "repeatedsfixedint64message.h"
 #include "repeatedexternalcomplexmessage.h"
+#include "simplestringmapmessage.h"
+#include "simplesint32mapmessage.h"
+
 #include "globalenums.h"
 #include "qtprotobuf.h"
 #include <QVariantList>
@@ -641,3 +644,26 @@ TEST_F(SimpleTest, StepChildEnumListMessageTest)
                                                                                SimpleEnumMessage::LOCAL_ENUM_VALUE1,
                                                                                SimpleEnumMessage::LOCAL_ENUM_VALUE3}));
 }
+
+
+TEST_F(SimpleTest, SimpleSInt32MapMessage)
+{
+    const char* propertyName = "mapField";
+    SimpleSInt32MapMessage::registerTypes();
+    ASSERT_TRUE(QMetaType::isRegistered(qMetaTypeId<SimpleSInt32MapMessage::MapFieldEntry>()));
+    int propertyNumber = SimpleSInt32MapMessage::propertyOrdering.at(1); //See simpletest.proto
+    ASSERT_STREQ(SimpleSInt32MapMessage::staticMetaObject.property(propertyNumber).typeName(), "MapFieldEntry");
+    ASSERT_EQ(SimpleSInt32MapMessage::staticMetaObject.property(propertyNumber).userType(), qMetaTypeId<SimpleSInt32MapMessage::MapFieldEntry>());
+    ASSERT_STREQ(SimpleSInt32MapMessage::staticMetaObject.property(propertyNumber).name(), propertyName);
+}
+
+TEST_F(SimpleTest, SimpleStringMapMessage)
+{
+    const char* propertyName = "mapField";
+    SimpleStringMapMessage::registerTypes();
+    ASSERT_TRUE(QMetaType::isRegistered(qMetaTypeId<SimpleStringMapMessage::MapFieldEntry>()));
+    int propertyNumber = SimpleStringMapMessage::propertyOrdering.at(1); //See simpletest.proto
+    ASSERT_STREQ(SimpleStringMapMessage::staticMetaObject.property(propertyNumber).typeName(), "MapFieldEntry");
+    ASSERT_EQ(SimpleStringMapMessage::staticMetaObject.property(propertyNumber).userType(), qMetaTypeId<SimpleStringMapMessage::MapFieldEntry>());
+    ASSERT_STREQ(SimpleStringMapMessage::staticMetaObject.property(propertyNumber).name(), propertyName);
+}